| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/web_contents/web_contents_impl.h" |
| |
| #include <stddef.h> |
| |
| #include <cmath> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/debug/dump_without_crashing.h" |
| #include "base/feature_list.h" |
| #include "base/i18n/character_encoding.h" |
| #include "base/lazy_instance.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/no_destructor.h" |
| #include "base/process/process.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "components/download/public/common/download_stats.h" |
| #include "components/rappor/public/rappor_utils.h" |
| #include "components/url_formatter/url_formatter.h" |
| #include "content/browser/accessibility/accessibility_tree_formatter.h" |
| #include "content/browser/accessibility/accessibility_tree_formatter_blink.h" |
| #include "content/browser/accessibility/browser_accessibility_state_impl.h" |
| #include "content/browser/bad_message.h" |
| #include "content/browser/browser_main_loop.h" |
| #include "content/browser/browser_plugin/browser_plugin_embedder.h" |
| #include "content/browser/browser_plugin/browser_plugin_guest.h" |
| #include "content/browser/child_process_security_policy_impl.h" |
| #include "content/browser/devtools/protocol/page_handler.h" |
| #include "content/browser/devtools/render_frame_devtools_agent_host.h" |
| #include "content/browser/display_cutout/display_cutout_host_impl.h" |
| #include "content/browser/dom_storage/dom_storage_context_wrapper.h" |
| #include "content/browser/dom_storage/session_storage_namespace_impl.h" |
| #include "content/browser/download/mhtml_generation_manager.h" |
| #include "content/browser/download/save_package.h" |
| #include "content/browser/find_request_manager.h" |
| #include "content/browser/frame_host/cross_process_frame_connector.h" |
| #include "content/browser/frame_host/frame_tree_node.h" |
| #include "content/browser/frame_host/interstitial_page_impl.h" |
| #include "content/browser/frame_host/navigation_entry_impl.h" |
| #include "content/browser/frame_host/navigation_handle_impl.h" |
| #include "content/browser/frame_host/navigation_request.h" |
| #include "content/browser/frame_host/navigator_impl.h" |
| #include "content/browser/frame_host/render_frame_host_impl.h" |
| #include "content/browser/frame_host/render_frame_proxy_host.h" |
| #include "content/browser/gpu/gpu_data_manager_impl.h" |
| #include "content/browser/loader/loader_io_thread_notifier.h" |
| #include "content/browser/loader/resource_dispatcher_host_impl.h" |
| #include "content/browser/manifest/manifest_manager_host.h" |
| #include "content/browser/media/audio_stream_broker.h" |
| #include "content/browser/media/audio_stream_monitor.h" |
| #include "content/browser/media/capture/web_contents_audio_muter.h" |
| #include "content/browser/media/media_web_contents_observer.h" |
| #include "content/browser/media/session/media_session_impl.h" |
| #include "content/browser/plugin_content_origin_whitelist.h" |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| #include "content/browser/renderer_host/render_view_host_delegate_view.h" |
| #include "content/browser/renderer_host/render_view_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #include "content/browser/renderer_host/render_widget_host_input_event_router.h" |
| #include "content/browser/renderer_host/render_widget_host_view_base.h" |
| #include "content/browser/renderer_host/render_widget_host_view_child_frame.h" |
| #include "content/browser/renderer_host/text_input_manager.h" |
| #include "content/browser/screen_orientation/screen_orientation_provider.h" |
| #include "content/browser/site_instance_impl.h" |
| #include "content/browser/web_contents/web_contents_view_child_frame.h" |
| #include "content/browser/web_contents/web_contents_view_guest.h" |
| #include "content/browser/webui/generic_handler.h" |
| #include "content/browser/webui/web_ui_controller_factory_registry.h" |
| #include "content/browser/webui/web_ui_impl.h" |
| #include "content/common/browser_plugin/browser_plugin_constants.h" |
| #include "content/common/browser_plugin/browser_plugin_messages.h" |
| #include "content/common/drag_messages.h" |
| #include "content/common/frame_messages.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/page_messages.h" |
| #include "content/common/page_state_serialization.h" |
| #include "content/common/render_message_filter.mojom.h" |
| #include "content/common/view_messages.h" |
| #include "content/common/widget_messages.h" |
| #include "content/public/browser/ax_event_notification_details.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_plugin_guest_manager.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/content_browser_client.h" |
| #include "content/public/browser/download_manager.h" |
| #include "content/public/browser/file_select_listener.h" |
| #include "content/public/browser/focused_node_details.h" |
| #include "content/public/browser/guest_mode.h" |
| #include "content/public/browser/invalidate_type.h" |
| #include "content/public/browser/javascript_dialog_manager.h" |
| #include "content/public/browser/keyboard_event_processing_result.h" |
| #include "content/public/browser/load_notification_details.h" |
| #include "content/public/browser/navigation_details.h" |
| #include "content/public/browser/notification_details.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_widget_host_iterator.h" |
| #include "content/public/browser/restore_type.h" |
| #include "content/public/browser/security_style_explanations.h" |
| #include "content/public/browser/ssl_status.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/web_contents_binding_set.h" |
| #include "content/public/browser/web_contents_delegate.h" |
| #include "content/public/browser/web_ui_controller.h" |
| #include "content/public/common/bindings_policy.h" |
| #include "content/public/common/child_process_host.h" |
| #include "content/public/common/content_constants.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/page_state.h" |
| #include "content/public/common/page_zoom.h" |
| #include "content/public/common/referrer_type_converters.h" |
| #include "content/public/common/result_codes.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/common/url_utils.h" |
| #include "content/public/common/web_preferences.h" |
| #include "media/base/user_input_monitor.h" |
| #include "net/base/url_util.h" |
| #include "net/http/http_cache.h" |
| #include "net/http/http_transaction_factory.h" |
| #include "net/traffic_annotation/network_traffic_annotation.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "ppapi/buildflags/buildflags.h" |
| #include "services/device/public/mojom/constants.mojom.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/cpp/interface_provider.h" |
| #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" |
| #include "third_party/blink/public/common/frame/sandbox_flags.h" |
| #include "third_party/blink/public/common/mime_util/mime_util.h" |
| #include "third_party/blink/public/platform/web_security_style.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/accessibility/ax_tree_combiner.h" |
| #include "ui/base/layout.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/blink/web_input_event_traits.h" |
| #include "ui/gl/gl_switches.h" |
| |
| #if defined(OS_WIN) |
| #include "content/browser/renderer_host/dip_util.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| #endif |
| |
| #if defined(OS_ANDROID) |
| #include "content/browser/android/date_time_chooser_android.h" |
| #include "content/browser/android/java_interfaces_impl.h" |
| #include "content/browser/media/android/media_web_contents_observer_android.h" |
| #include "content/browser/web_contents/web_contents_android.h" |
| #include "services/device/public/mojom/nfc.mojom.h" |
| #else // !OS_ANDROID |
| #include "content/browser/host_zoom_map_impl.h" |
| #include "content/browser/host_zoom_map_observer.h" |
| #endif // OS_ANDROID |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| #include "content/browser/media/session/pepper_playback_observer.h" |
| #endif |
| |
| namespace content { |
| namespace { |
| |
| const int kMinimumDelayBetweenLoadingUpdatesMS = 100; |
| const char kDotGoogleDotCom[] = ".google.com"; |
| |
| #if defined(OS_ANDROID) |
| const void* const kWebContentsAndroidKey = &kWebContentsAndroidKey; |
| #endif |
| |
| base::LazyInstance<std::vector< |
| WebContentsImpl::FriendWrapper::CreatedCallback>>::DestructorAtExit |
| g_created_callbacks = LAZY_INSTANCE_INITIALIZER; |
| |
| void NotifyCacheOnIO( |
| scoped_refptr<net::URLRequestContextGetter> request_context, |
| const GURL& url, |
| const std::string& http_method) { |
| net::HttpCache* cache = request_context->GetURLRequestContext()-> |
| http_transaction_factory()->GetCache(); |
| if (cache) |
| cache->OnExternalCacheHit(url, http_method); |
| } |
| |
| bool HasMatchingProcess(FrameTree* tree, int render_process_id) { |
| for (FrameTreeNode* node : tree->Nodes()) { |
| if (node->current_frame_host()->GetProcess()->GetID() == render_process_id) |
| return true; |
| } |
| return false; |
| } |
| |
| bool HasMatchingWidgetHost(FrameTree* tree, RenderWidgetHost* host) { |
| // This method scans the frame tree rather than checking whether |
| // host->delegate() == this, which allows it to return false when the host |
| // for a frame that is pending or pending deletion. |
| if (!host) |
| return false; |
| |
| for (FrameTreeNode* node : tree->Nodes()) { |
| if (node->current_frame_host()->GetRenderWidgetHost() == host) |
| return true; |
| } |
| return false; |
| } |
| |
| void UpdateAccessibilityModeOnFrame(RenderFrameHost* frame_host) { |
| static_cast<RenderFrameHostImpl*>(frame_host)->UpdateAccessibilityMode(); |
| } |
| |
| void ResetAccessibility(RenderFrameHost* rfh) { |
| static_cast<RenderFrameHostImpl*>(rfh)->AccessibilityReset(); |
| } |
| |
| // Helper for GetInnerWebContents(). |
| bool GetInnerWebContentsHelper( |
| std::vector<WebContentsImpl*>* all_guest_contents, |
| WebContents* guest_contents) { |
| all_guest_contents->push_back(static_cast<WebContentsImpl*>(guest_contents)); |
| return false; |
| } |
| |
| RenderFrameHostImpl* FindOpenerRFH(const WebContents::CreateParams& params) { |
| RenderFrameHostImpl* opener_rfh = nullptr; |
| if (params.opener_render_frame_id != MSG_ROUTING_NONE) { |
| opener_rfh = RenderFrameHostImpl::FromID(params.opener_render_process_id, |
| params.opener_render_frame_id); |
| } |
| return opener_rfh; |
| } |
| |
| // Returns |true| if |type| is the kind of user input that should trigger the |
| // user interaction observers. |
| bool IsUserInteractionInputType(blink::WebInputEvent::Type type) { |
| // Ideally, this list would be based more off of |
| // https://whatwg.org/C/interaction.html#triggered-by-user-activation. |
| return type == blink::WebInputEvent::kMouseDown || |
| type == blink::WebInputEvent::kGestureScrollBegin || |
| type == blink::WebInputEvent::kTouchStart || |
| type == blink::WebInputEvent::kRawKeyDown; |
| } |
| |
| // Returns |true| if |type| is the kind of user input that should be used as |
| // a user gesture signal for resource load dispatches. |
| bool IsResourceLoadUserInteractionInputType(blink::WebInputEvent::Type type) { |
| return type == blink::WebInputEvent::kMouseDown || |
| type == blink::WebInputEvent::kTouchStart || |
| type == blink::WebInputEvent::kRawKeyDown; |
| } |
| |
| // Ensures that OnDialogClosed is only called once. |
| class CloseDialogCallbackWrapper |
| : public base::RefCountedThreadSafe<CloseDialogCallbackWrapper> { |
| public: |
| using CloseCallback = |
| base::OnceCallback<void(bool, bool, const base::string16&)>; |
| |
| explicit CloseDialogCallbackWrapper(CloseCallback callback) |
| : callback_(std::move(callback)) {} |
| |
| void Run(bool dialog_was_suppressed, |
| bool success, |
| const base::string16& user_input) { |
| if (callback_.is_null()) |
| return; |
| std::move(callback_).Run(dialog_was_suppressed, success, user_input); |
| } |
| |
| private: |
| friend class base::RefCountedThreadSafe<CloseDialogCallbackWrapper>; |
| ~CloseDialogCallbackWrapper() {} |
| |
| CloseCallback callback_; |
| }; |
| |
| bool FrameCompareDepth(RenderFrameHostImpl* a, RenderFrameHostImpl* b) { |
| return a->frame_tree_node()->depth() < b->frame_tree_node()->depth(); |
| } |
| |
| } // namespace |
| |
| std::unique_ptr<WebContents> WebContents::Create( |
| const WebContents::CreateParams& params) { |
| return WebContentsImpl::CreateWithOpener(params, FindOpenerRFH(params)); |
| } |
| |
| std::unique_ptr<WebContents> WebContents::CreateWithSessionStorage( |
| const WebContents::CreateParams& params, |
| const SessionStorageNamespaceMap& session_storage_namespace_map) { |
| std::unique_ptr<WebContentsImpl> new_contents( |
| new WebContentsImpl(params.browser_context)); |
| RenderFrameHostImpl* opener_rfh = FindOpenerRFH(params); |
| FrameTreeNode* opener = nullptr; |
| if (opener_rfh) |
| opener = opener_rfh->frame_tree_node(); |
| new_contents->SetOpenerForNewContents(opener, params.opener_suppressed); |
| |
| for (auto it = session_storage_namespace_map.begin(); |
| it != session_storage_namespace_map.end(); ++it) { |
| new_contents->GetController() |
| .SetSessionStorageNamespace(it->first, it->second.get()); |
| } |
| |
| if (params.guest_delegate) { |
| // This makes |new_contents| act as a guest. |
| // For more info, see comment above class BrowserPluginGuest. |
| BrowserPluginGuest::CreateInWebContents(new_contents.get(), |
| params.guest_delegate); |
| } |
| |
| new_contents->Init(params); |
| return new_contents; |
| } |
| |
| void WebContentsImpl::FriendWrapper::AddCreatedCallbackForTesting( |
| const CreatedCallback& callback) { |
| g_created_callbacks.Get().push_back(callback); |
| } |
| |
| void WebContentsImpl::FriendWrapper::RemoveCreatedCallbackForTesting( |
| const CreatedCallback& callback) { |
| for (size_t i = 0; i < g_created_callbacks.Get().size(); ++i) { |
| if (g_created_callbacks.Get().at(i).Equals(callback)) { |
| g_created_callbacks.Get().erase(g_created_callbacks.Get().begin() + i); |
| return; |
| } |
| } |
| } |
| |
| WebContents* WebContents::FromRenderViewHost(RenderViewHost* rvh) { |
| if (!rvh) |
| return nullptr; |
| return rvh->GetDelegate()->GetAsWebContents(); |
| } |
| |
| WebContents* WebContents::FromRenderFrameHost(RenderFrameHost* rfh) { |
| if (!rfh) |
| return nullptr; |
| return static_cast<RenderFrameHostImpl*>(rfh)->delegate()->GetAsWebContents(); |
| } |
| |
| WebContents* WebContents::FromFrameTreeNodeId(int frame_tree_node_id) { |
| FrameTreeNode* frame_tree_node = |
| FrameTreeNode::GloballyFindByID(frame_tree_node_id); |
| if (!frame_tree_node) |
| return nullptr; |
| return WebContentsImpl::FromFrameTreeNode(frame_tree_node); |
| } |
| |
| void WebContents::SetScreenOrientationDelegate( |
| ScreenOrientationDelegate* delegate) { |
| ScreenOrientationProvider::SetDelegate(delegate); |
| } |
| |
| // WebContentsImpl::DestructionObserver ---------------------------------------- |
| |
| class WebContentsImpl::DestructionObserver : public WebContentsObserver { |
| public: |
| DestructionObserver(WebContentsImpl* owner, WebContents* watched_contents) |
| : WebContentsObserver(watched_contents), |
| owner_(owner) { |
| } |
| |
| // WebContentsObserver: |
| void WebContentsDestroyed() override { |
| owner_->OnWebContentsDestroyed( |
| static_cast<WebContentsImpl*>(web_contents())); |
| } |
| |
| private: |
| WebContentsImpl* owner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DestructionObserver); |
| }; |
| |
| // WebContentsImpl::ColorChooser ---------------------------------------------- |
| class WebContentsImpl::ColorChooser : public blink::mojom::ColorChooser { |
| public: |
| ColorChooser(content::ColorChooser* chooser, |
| blink::mojom::ColorChooserRequest request, |
| blink::mojom::ColorChooserClientPtr client) |
| : chooser_(chooser), |
| binding_(this, std::move(request)), |
| client_(std::move(client)) { |
| binding_.set_connection_error_handler( |
| base::BindOnce([](content::ColorChooser* chooser) { chooser->End(); }, |
| base::Unretained(chooser))); |
| } |
| |
| ~ColorChooser() override { chooser_->End(); } |
| |
| void SetSelectedColor(SkColor color) override { |
| chooser_->SetSelectedColor(color); |
| } |
| |
| void DidChooseColorInColorChooser(SkColor color) { |
| client_->DidChooseColor(color); |
| } |
| |
| private: |
| // Color chooser that was opened by this tab. |
| std::unique_ptr<content::ColorChooser> chooser_; |
| |
| // mojo bindings. |
| mojo::Binding<blink::mojom::ColorChooser> binding_; |
| |
| // mojo renderer client. |
| blink::mojom::ColorChooserClientPtr client_; |
| }; |
| |
| // WebContentsImpl::WebContentsTreeNode ---------------------------------------- |
| WebContentsImpl::WebContentsTreeNode::WebContentsTreeNode( |
| WebContentsImpl* current_web_contents) |
| : current_web_contents_(current_web_contents), |
| outer_web_contents_(nullptr), |
| outer_contents_frame_tree_node_id_( |
| FrameTreeNode::kFrameTreeNodeInvalidId), |
| focused_web_contents_(current_web_contents) {} |
| |
| WebContentsImpl::WebContentsTreeNode::~WebContentsTreeNode() {} |
| |
| void WebContentsImpl::WebContentsTreeNode::ConnectToOuterWebContents( |
| std::unique_ptr<WebContents> current_web_contents, |
| RenderFrameHostImpl* outer_contents_frame) { |
| DCHECK_EQ(current_web_contents.get(), current_web_contents_); |
| auto* outer_web_contents = |
| static_cast<WebContentsImpl*>(FromRenderFrameHost(outer_contents_frame)); |
| |
| focused_web_contents_ = nullptr; |
| outer_web_contents_ = outer_web_contents; |
| outer_contents_frame_tree_node_id_ = |
| outer_contents_frame->frame_tree_node()->frame_tree_node_id(); |
| |
| outer_web_contents_->node_.AttachInnerWebContents( |
| std::move(current_web_contents)); |
| outer_contents_frame->frame_tree_node()->AddObserver(this); |
| } |
| |
| void WebContentsImpl::WebContentsTreeNode::AttachInnerWebContents( |
| std::unique_ptr<WebContents> inner_web_contents) { |
| inner_web_contents_.push_back(std::move(inner_web_contents)); |
| } |
| |
| std::unique_ptr<WebContents> |
| WebContentsImpl::WebContentsTreeNode::DetachInnerWebContents( |
| WebContentsImpl* inner_web_contents) { |
| std::unique_ptr<WebContents> detached_contents; |
| for (std::unique_ptr<WebContents>& web_contents : inner_web_contents_) { |
| if (web_contents.get() == inner_web_contents) { |
| detached_contents = std::move(web_contents); |
| std::swap(web_contents, inner_web_contents_.back()); |
| inner_web_contents_.pop_back(); |
| return detached_contents; |
| } |
| } |
| |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| FrameTreeNode* |
| WebContentsImpl::WebContentsTreeNode::OuterContentsFrameTreeNode() const { |
| return FrameTreeNode::GloballyFindByID(outer_contents_frame_tree_node_id_); |
| } |
| |
| void WebContentsImpl::WebContentsTreeNode::OnFrameTreeNodeDestroyed( |
| FrameTreeNode* node) { |
| DCHECK_EQ(outer_contents_frame_tree_node_id_, node->frame_tree_node_id()) |
| << "WebContentsTreeNode should only receive notifications for the " |
| "FrameTreeNode in its outer WebContents that hosts it."; |
| |
| // Deletes |this| too. |
| outer_web_contents_->node_.DetachInnerWebContents(current_web_contents_); |
| } |
| |
| void WebContentsImpl::WebContentsTreeNode::SetFocusedWebContents( |
| WebContentsImpl* web_contents) { |
| DCHECK(!outer_web_contents()) |
| << "Only the outermost WebContents tracks focus."; |
| focused_web_contents_ = web_contents; |
| } |
| |
| WebContentsImpl* |
| WebContentsImpl::WebContentsTreeNode::GetInnerWebContentsInFrame( |
| const FrameTreeNode* frame) { |
| auto ftn_id = frame->frame_tree_node_id(); |
| for (auto& contents : inner_web_contents_) { |
| WebContentsImpl* impl = static_cast<WebContentsImpl*>(contents.get()); |
| if (impl->node_.outer_contents_frame_tree_node_id() == ftn_id) { |
| return impl; |
| } |
| } |
| return nullptr; |
| } |
| |
| std::vector<WebContentsImpl*> |
| WebContentsImpl::WebContentsTreeNode::GetInnerWebContents() const { |
| std::vector<WebContentsImpl*> inner_web_contents; |
| for (auto& contents : inner_web_contents_) |
| inner_web_contents.push_back(static_cast<WebContentsImpl*>(contents.get())); |
| |
| return inner_web_contents; |
| } |
| |
| // WebContentsImpl ------------------------------------------------------------- |
| |
| WebContentsImpl::WebContentsImpl(BrowserContext* browser_context) |
| : delegate_(nullptr), |
| controller_(this, browser_context), |
| render_view_host_delegate_view_(nullptr), |
| created_with_opener_(false), |
| frame_tree_(new NavigatorImpl(&controller_, this), |
| this, |
| this, |
| this, |
| this), |
| node_(this), |
| is_load_to_different_document_(false), |
| crashed_status_(base::TERMINATION_STATUS_STILL_RUNNING), |
| crashed_error_code_(0), |
| waiting_for_response_(false), |
| load_state_(net::LOAD_STATE_IDLE, base::string16()), |
| upload_size_(0), |
| upload_position_(0), |
| is_resume_pending_(false), |
| interstitial_page_(nullptr), |
| has_accessed_initial_document_(false), |
| theme_color_(SK_ColorTRANSPARENT), |
| last_sent_theme_color_(SK_ColorTRANSPARENT), |
| did_first_visually_non_empty_paint_(false), |
| capturer_count_(0), |
| is_being_destroyed_(false), |
| is_notifying_observers_(false), |
| notify_disconnection_(false), |
| dialog_manager_(nullptr), |
| is_showing_before_unload_dialog_(false), |
| last_active_time_(base::TimeTicks::Now()), |
| closed_by_user_gesture_(false), |
| minimum_zoom_percent_(static_cast<int>(kMinimumZoomFactor * 100)), |
| maximum_zoom_percent_(static_cast<int>(kMaximumZoomFactor * 100)), |
| zoom_scroll_remainder_(0), |
| fullscreen_widget_process_id_(ChildProcessHost::kInvalidUniqueID), |
| fullscreen_widget_routing_id_(MSG_ROUTING_NONE), |
| fullscreen_widget_had_focus_at_shutdown_(false), |
| force_disable_overscroll_content_(false), |
| last_dialog_suppressed_(false), |
| accessibility_mode_( |
| BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode()), |
| audio_stream_monitor_(this), |
| bluetooth_connected_device_count_(0), |
| media_device_group_id_salt_base_( |
| BrowserContext::CreateRandomMediaDeviceIDSalt()), |
| #if !defined(OS_ANDROID) |
| page_scale_factor_is_one_(true), |
| #endif // !defined(OS_ANDROID) |
| is_overlay_content_(false), |
| showing_context_menu_(false), |
| loading_weak_factory_(this), |
| weak_factory_(this) { |
| frame_tree_.SetFrameRemoveListener( |
| base::Bind(&WebContentsImpl::OnFrameRemoved, |
| base::Unretained(this))); |
| #if defined(OS_ANDROID) |
| media_web_contents_observer_.reset(new MediaWebContentsObserverAndroid(this)); |
| #else |
| media_web_contents_observer_.reset(new MediaWebContentsObserver(this)); |
| #endif |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| pepper_playback_observer_.reset(new PepperPlaybackObserver(this)); |
| #endif |
| |
| loader_io_thread_notifier_.reset(new LoaderIOThreadNotifier(this)); |
| #if !defined(OS_ANDROID) |
| host_zoom_map_observer_.reset(new HostZoomMapObserver(this)); |
| #endif // !defined(OS_ANDROID) |
| |
| #if defined(OS_ANDROID) |
| if (base::FeatureList::IsEnabled(features::kDisplayCutoutAPI)) |
| display_cutout_host_impl_ = std::make_unique<DisplayCutoutHostImpl>(this); |
| #endif |
| |
| registry_.AddInterface(base::BindRepeating( |
| &WebContentsImpl::OnColorChooserFactoryRequest, base::Unretained(this))); |
| } |
| |
| WebContentsImpl::~WebContentsImpl() { |
| // Imperfect sanity check against double free, given some crashes unexpectedly |
| // observed in the wild. |
| CHECK(!is_being_destroyed_); |
| |
| // We generally keep track of is_being_destroyed_ to let other features know |
| // to avoid certain actions during destruction. |
| is_being_destroyed_ = true; |
| |
| // A WebContents should never be deleted while it is notifying observers, |
| // since this will lead to a use-after-free as it continues to notify later |
| // observers. |
| CHECK(!is_notifying_observers_); |
| |
| rwh_input_event_router_.reset(); |
| |
| for (auto& entry : binding_sets_) |
| entry.second->CloseAllBindings(); |
| |
| WebContentsImpl* outermost = GetOutermostWebContents(); |
| if (this != outermost && ContainsOrIsFocusedWebContents()) { |
| // If the current WebContents is in focus, unset it. |
| outermost->SetAsFocusedWebContentsIfNecessary(); |
| } |
| |
| if (mouse_lock_widget_) |
| mouse_lock_widget_->RejectMouseLockOrUnlockIfNecessary(); |
| |
| for (FrameTreeNode* node : frame_tree_.Nodes()) { |
| // Delete all RFHs pending shutdown, which will lead the corresponding RVHs |
| // to be shutdown and be deleted as well. |
| node->render_manager()->ClearRFHsPendingShutdown(); |
| node->render_manager()->ClearWebUIInstances(); |
| } |
| |
| for (RenderWidgetHostImpl* widget : created_widgets_) |
| widget->DetachDelegate(); |
| created_widgets_.clear(); |
| |
| // Clear out any JavaScript state. |
| if (dialog_manager_) { |
| dialog_manager_->CancelDialogs(this, /*reset_state=*/true); |
| } |
| |
| color_chooser_.reset(); |
| find_request_manager_.reset(); |
| |
| NotifyDisconnected(); |
| |
| // Notify any observer that have a reference on this WebContents. |
| NotificationService::current()->Notify( |
| NOTIFICATION_WEB_CONTENTS_DESTROYED, |
| Source<WebContents>(this), |
| NotificationService::NoDetails()); |
| |
| // Destroy all subframes now. This notifies observers. |
| GetMainFrame()->ResetChildren(); |
| GetRenderManager()->ResetProxyHosts(); |
| |
| // Manually call the observer methods for the root frame tree node. It is |
| // necessary to manually delete all objects tracking navigations |
| // (NavigationHandle, NavigationRequest) for observers to be properly |
| // notified of these navigations stopping before the WebContents is |
| // destroyed. |
| RenderFrameHostManager* root = GetRenderManager(); |
| |
| root->current_frame_host()->SetRenderFrameCreated(false); |
| root->current_frame_host()->ResetNavigationRequests(); |
| |
| // Do not update state as the WebContents is being destroyed. |
| frame_tree_.root()->ResetNavigationRequest(true, true); |
| if (root->speculative_frame_host()) { |
| root->speculative_frame_host()->SetRenderFrameCreated(false); |
| root->speculative_frame_host()->ResetNavigationRequests(); |
| } |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| // Call this before WebContentsDestroyed() is broadcasted since |
| // AudioFocusManager will be destroyed after that. |
| pepper_playback_observer_.reset(); |
| #endif // defined(ENABLED_PLUGINS) |
| |
| // If audio is playing then notify external observers of the audio stream |
| // disappearing. |
| if (is_currently_audible_) { |
| is_currently_audible_ = false; |
| for (auto& observer : observers_) |
| observer.OnAudioStateChanged(false); |
| |
| if (GetOuterWebContents()) |
| GetOuterWebContents()->OnAudioStateChanged(); |
| } |
| |
| for (auto& observer : observers_) |
| observer.FrameDeleted(root->current_frame_host()); |
| |
| for (auto& observer : observers_) |
| observer.RenderViewDeleted(root->current_host()); |
| |
| for (auto& observer : observers_) |
| observer.WebContentsDestroyed(); |
| |
| if (display_cutout_host_impl_) |
| display_cutout_host_impl_->WebContentsDestroyed(); |
| |
| for (auto& observer : observers_) |
| observer.ResetWebContents(); |
| |
| SetDelegate(nullptr); |
| } |
| |
| std::unique_ptr<WebContentsImpl> WebContentsImpl::CreateWithOpener( |
| const WebContents::CreateParams& params, |
| RenderFrameHostImpl* opener_rfh) { |
| TRACE_EVENT0("browser", "WebContentsImpl::CreateWithOpener"); |
| FrameTreeNode* opener = nullptr; |
| if (opener_rfh) |
| opener = opener_rfh->frame_tree_node(); |
| std::unique_ptr<WebContentsImpl> new_contents( |
| new WebContentsImpl(params.browser_context)); |
| new_contents->SetOpenerForNewContents(opener, params.opener_suppressed); |
| |
| // If the opener is sandboxed, a new popup must inherit the opener's sandbox |
| // flags, and these flags take effect immediately. An exception is if the |
| // opener's sandbox flags lack the PropagatesToAuxiliaryBrowsingContexts |
| // bit (which is controlled by the "allow-popups-to-escape-sandbox" token). |
| // See https://html.spec.whatwg.org/#attr-iframe-sandbox. |
| FrameTreeNode* new_root = new_contents->GetFrameTree()->root(); |
| if (opener) { |
| blink::WebSandboxFlags opener_flags = opener_rfh->active_sandbox_flags(); |
| const blink::WebSandboxFlags inherit_flag = |
| blink::WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts; |
| if ((opener_flags & inherit_flag) == inherit_flag) { |
| // TODO(iclelland): Transfer correct container policy from opener as well. |
| // https://crbug.com/774620 |
| new_root->SetPendingFramePolicy({opener_flags, {}}); |
| } |
| } |
| |
| // Apply starting sandbox flags. |
| blink::FramePolicy frame_policy(new_root->pending_frame_policy()); |
| frame_policy.sandbox_flags |= params.starting_sandbox_flags; |
| new_root->SetPendingFramePolicy(frame_policy); |
| new_root->CommitPendingFramePolicy(); |
| |
| // This may be true even when opener is null, such as when opening blocked |
| // popups. |
| if (params.created_with_opener) |
| new_contents->created_with_opener_ = true; |
| |
| if (params.guest_delegate) { |
| // This makes |new_contents| act as a guest. |
| // For more info, see comment above class BrowserPluginGuest. |
| BrowserPluginGuest::CreateInWebContents(new_contents.get(), |
| params.guest_delegate); |
| } |
| |
| new_contents->Init(params); |
| return new_contents; |
| } |
| |
| // static |
| std::vector<WebContentsImpl*> WebContentsImpl::GetAllWebContents() { |
| std::vector<WebContentsImpl*> result; |
| std::unique_ptr<RenderWidgetHostIterator> widgets( |
| RenderWidgetHostImpl::GetRenderWidgetHosts()); |
| while (RenderWidgetHost* rwh = widgets->GetNextHost()) { |
| RenderViewHost* rvh = RenderViewHost::From(rwh); |
| if (!rvh) |
| continue; |
| WebContents* web_contents = WebContents::FromRenderViewHost(rvh); |
| if (!web_contents) |
| continue; |
| if (web_contents->GetRenderViewHost() != rvh) |
| continue; |
| // Because a WebContents can only have one current RVH at a time, there will |
| // be no duplicate WebContents here. |
| result.push_back(static_cast<WebContentsImpl*>(web_contents)); |
| } |
| return result; |
| } |
| |
| // static |
| WebContentsImpl* WebContentsImpl::FromFrameTreeNode( |
| const FrameTreeNode* frame_tree_node) { |
| return static_cast<WebContentsImpl*>( |
| WebContents::FromRenderFrameHost(frame_tree_node->current_frame_host())); |
| } |
| |
| // static |
| WebContents* WebContentsImpl::FromRenderFrameHostID(int render_process_host_id, |
| int render_frame_host_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| RenderFrameHost* render_frame_host = |
| RenderFrameHost::FromID(render_process_host_id, render_frame_host_id); |
| if (!render_frame_host) |
| return nullptr; |
| |
| return WebContents::FromRenderFrameHost(render_frame_host); |
| } |
| |
| // static |
| WebContentsImpl* WebContentsImpl::FromOuterFrameTreeNode( |
| const FrameTreeNode* frame_tree_node) { |
| return WebContentsImpl::FromFrameTreeNode(frame_tree_node) |
| ->node_.GetInnerWebContentsInFrame(frame_tree_node); |
| } |
| |
| RenderFrameHostManager* WebContentsImpl::GetRenderManagerForTesting() { |
| return GetRenderManager(); |
| } |
| |
| bool WebContentsImpl::OnMessageReceived(RenderViewHostImpl* render_view_host, |
| const IPC::Message& message) { |
| for (auto& observer : observers_) { |
| // TODO(nick, creis): https://crbug.com/758026: Replace all uses of this |
| // variant of OnMessageReceived with the version that takes a |
| // RenderFrameHost, and then delete it. |
| if (observer.OnMessageReceived(message)) |
| return true; |
| } |
| |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(WebContentsImpl, message, render_view_host) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_GoToEntryAtOffset, OnGoToEntryAtOffset) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateZoomLimits, OnUpdateZoomLimits) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_PageScaleFactorChanged, |
| OnPageScaleFactorChanged) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_AppCacheAccessed, OnAppCacheAccessed) |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_RequestPpapiBrokerPermission, |
| OnRequestPpapiBrokerPermission) |
| #endif |
| #if defined(OS_ANDROID) |
| IPC_MESSAGE_HANDLER(ViewHostMsg_OpenDateTimeDialog, OnOpenDateTimeDialog) |
| #endif |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| bool WebContentsImpl::OnMessageReceived(RenderFrameHostImpl* render_frame_host, |
| const IPC::Message& message) { |
| { |
| WebUIImpl* web_ui = render_frame_host->web_ui(); |
| if (web_ui && web_ui->OnMessageReceived(message, render_frame_host)) |
| return true; |
| } |
| |
| for (auto& observer : observers_) { |
| if (observer.OnMessageReceived(message, render_frame_host)) |
| return true; |
| } |
| |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(WebContentsImpl, message, render_frame_host) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DomOperationResponse, |
| OnDomOperationResponse) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidChangeThemeColor, |
| OnThemeColorChanged) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidFinishDocumentLoad, |
| OnDocumentLoadedInFrame) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidFinishLoad, OnDidFinishLoad) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidLoadResourceFromMemoryCache, |
| OnDidLoadResourceFromMemoryCache) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidDisplayInsecureContent, |
| OnDidDisplayInsecureContent) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidContainInsecureFormAction, |
| OnDidContainInsecureFormAction) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidRunInsecureContent, |
| OnDidRunInsecureContent) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidDisplayContentWithCertificateErrors, |
| OnDidDisplayContentWithCertificateErrors) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_DidRunContentWithCertificateErrors, |
| OnDidRunContentWithCertificateErrors) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_RegisterProtocolHandler, |
| OnRegisterProtocolHandler) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_UnregisterProtocolHandler, |
| OnUnregisterProtocolHandler) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_UpdatePageImportanceSignals, |
| OnUpdatePageImportanceSignals) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateFaviconURL, OnUpdateFaviconURL) |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_PepperInstanceCreated, |
| OnPepperInstanceCreated) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_PepperInstanceDeleted, |
| OnPepperInstanceDeleted) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_PepperPluginHung, OnPepperPluginHung) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_PepperStartsPlayback, |
| OnPepperStartsPlayback) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_PepperStopsPlayback, |
| OnPepperStopsPlayback) |
| IPC_MESSAGE_HANDLER(FrameHostMsg_PluginCrashed, OnPluginCrashed) |
| IPC_MESSAGE_HANDLER_GENERIC(BrowserPluginHostMsg_Attach, |
| OnBrowserPluginMessage(render_frame_host, |
| message)) |
| #endif |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| |
| return handled; |
| } |
| |
| NavigationControllerImpl& WebContentsImpl::GetController() { |
| return controller_; |
| } |
| |
| const NavigationControllerImpl& WebContentsImpl::GetController() const { |
| return controller_; |
| } |
| |
| BrowserContext* WebContentsImpl::GetBrowserContext() const { |
| return controller_.GetBrowserContext(); |
| } |
| |
| const GURL& WebContentsImpl::GetURL() const { |
| // We may not have a navigation entry yet. |
| NavigationEntry* entry = controller_.GetVisibleEntry(); |
| return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); |
| } |
| |
| const GURL& WebContentsImpl::GetVisibleURL() const { |
| // We may not have a navigation entry yet. |
| NavigationEntry* entry = controller_.GetVisibleEntry(); |
| return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); |
| } |
| |
| const GURL& WebContentsImpl::GetLastCommittedURL() const { |
| // We may not have a navigation entry yet. |
| NavigationEntry* entry = controller_.GetLastCommittedEntry(); |
| return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); |
| } |
| |
| WebContentsDelegate* WebContentsImpl::GetDelegate() { |
| return delegate_; |
| } |
| |
| void WebContentsImpl::SetDelegate(WebContentsDelegate* delegate) { |
| // TODO(cbentzel): remove this debugging code? |
| if (delegate == delegate_) |
| return; |
| if (delegate_) |
| delegate_->Detach(this); |
| delegate_ = delegate; |
| if (delegate_) { |
| delegate_->Attach(this); |
| // Ensure the visible RVH reflects the new delegate's preferences. |
| if (view_) |
| view_->SetOverscrollControllerEnabled(CanOverscrollContent()); |
| if (GetRenderViewHost()) |
| RenderFrameDevToolsAgentHost::WebContentsCreated(this); |
| } |
| } |
| |
| RenderFrameHostImpl* WebContentsImpl::GetMainFrame() const { |
| return frame_tree_.root()->current_frame_host(); |
| } |
| |
| RenderFrameHostImpl* WebContentsImpl::GetFocusedFrame() { |
| FrameTreeNode* focused_node = frame_tree_.GetFocusedFrame(); |
| if (!focused_node) |
| return nullptr; |
| return focused_node->current_frame_host(); |
| } |
| |
| RenderFrameHostImpl* WebContentsImpl::FindFrameByFrameTreeNodeId( |
| int frame_tree_node_id, |
| int process_id) { |
| FrameTreeNode* frame = frame_tree_.FindByID(frame_tree_node_id); |
| |
| // Sanity check that this is in the caller's expected process. Otherwise a |
| // recent cross-process navigation may have led to a privilege change that the |
| // caller is not expecting. |
| if (!frame || |
| frame->current_frame_host()->GetProcess()->GetID() != process_id) |
| return nullptr; |
| |
| return frame->current_frame_host(); |
| } |
| |
| RenderFrameHostImpl* WebContentsImpl::UnsafeFindFrameByFrameTreeNodeId( |
| int frame_tree_node_id) { |
| // Beware using this! The RenderFrameHost may have changed since the caller |
| // obtained frame_tree_node_id. |
| FrameTreeNode* frame = frame_tree_.FindByID(frame_tree_node_id); |
| return frame ? frame->current_frame_host() : nullptr; |
| } |
| |
| void WebContentsImpl::ForEachFrame( |
| const base::RepeatingCallback<void(RenderFrameHost*)>& on_frame) { |
| for (FrameTreeNode* node : frame_tree_.Nodes()) { |
| on_frame.Run(node->current_frame_host()); |
| } |
| } |
| |
| std::vector<RenderFrameHost*> WebContentsImpl::GetAllFrames() { |
| std::vector<RenderFrameHost*> frame_hosts; |
| for (FrameTreeNode* node : frame_tree_.Nodes()) |
| frame_hosts.push_back(node->current_frame_host()); |
| return frame_hosts; |
| } |
| |
| int WebContentsImpl::SendToAllFrames(IPC::Message* message) { |
| int number_of_messages = 0; |
| for (RenderFrameHost* rfh : GetAllFrames()) { |
| if (!rfh->IsRenderFrameLive()) |
| continue; |
| |
| ++number_of_messages; |
| IPC::Message* message_copy = new IPC::Message(*message); |
| message_copy->set_routing_id(rfh->GetRoutingID()); |
| rfh->Send(message_copy); |
| } |
| delete message; |
| return number_of_messages; |
| } |
| |
| void WebContentsImpl::SendPageMessage(IPC::Message* msg) { |
| frame_tree_.root()->render_manager()->SendPageMessage(msg, nullptr); |
| } |
| |
| RenderViewHostImpl* WebContentsImpl::GetRenderViewHost() const { |
| return GetRenderManager()->current_host(); |
| } |
| |
| void WebContentsImpl::CancelActiveAndPendingDialogs() { |
| if (dialog_manager_) { |
| dialog_manager_->CancelDialogs(this, /*reset_state=*/false); |
| } |
| if (browser_plugin_embedder_) |
| browser_plugin_embedder_->CancelGuestDialogs(); |
| } |
| |
| void WebContentsImpl::ClosePage() { |
| GetRenderViewHost()->ClosePage(); |
| } |
| |
| RenderWidgetHostView* WebContentsImpl::GetRenderWidgetHostView() const { |
| return GetRenderManager()->GetRenderWidgetHostView(); |
| } |
| |
| RenderWidgetHostView* WebContentsImpl::GetTopLevelRenderWidgetHostView() { |
| if (GetOuterWebContents()) |
| return GetOuterWebContents()->GetTopLevelRenderWidgetHostView(); |
| return GetRenderManager()->GetRenderWidgetHostView(); |
| } |
| |
| RenderWidgetHostView* WebContentsImpl::GetFullscreenRenderWidgetHostView() |
| const { |
| if (auto* widget_host = GetFullscreenRenderWidgetHost()) |
| return widget_host->GetView(); |
| return nullptr; |
| } |
| |
| WebContentsView* WebContentsImpl::GetView() const { |
| return view_.get(); |
| } |
| |
| void WebContentsImpl::OnScreenOrientationChange() { |
| DCHECK(screen_orientation_provider_); |
| screen_orientation_provider_->OnOrientationChange(); |
| } |
| |
| SkColor WebContentsImpl::GetThemeColor() const { |
| return theme_color_; |
| } |
| |
| void WebContentsImpl::SetAccessibilityMode(ui::AXMode mode) { |
| if (mode == accessibility_mode_) |
| return; |
| |
| // Don't allow accessibility to be enabled for WebContents that are never |
| // visible, like background pages. |
| if (IsNeverVisible()) |
| return; |
| |
| accessibility_mode_ = mode; |
| |
| for (FrameTreeNode* node : frame_tree_.Nodes()) { |
| UpdateAccessibilityModeOnFrame(node->current_frame_host()); |
| // Also update accessibility mode on the speculative RenderFrameHost for |
| // this FrameTreeNode, if one exists. |
| RenderFrameHost* speculative_frame_host = |
| node->render_manager()->speculative_frame_host(); |
| if (speculative_frame_host) |
| UpdateAccessibilityModeOnFrame(speculative_frame_host); |
| } |
| } |
| |
| void WebContentsImpl::AddAccessibilityMode(ui::AXMode mode) { |
| ui::AXMode new_mode(accessibility_mode_); |
| new_mode |= mode; |
| SetAccessibilityMode(new_mode); |
| } |
| |
| // Helper class used by WebContentsImpl::RequestAXTreeSnapshot. |
| // Handles the callbacks from parallel snapshot requests to each frame, |
| // and feeds the results to an AXTreeCombiner, which converts them into a |
| // single combined accessibility tree. |
| class WebContentsImpl::AXTreeSnapshotCombiner |
| : public base::RefCounted<AXTreeSnapshotCombiner> { |
| public: |
| explicit AXTreeSnapshotCombiner(AXTreeSnapshotCallback callback) |
| : callback_(std::move(callback)) {} |
| |
| AXTreeSnapshotCallback AddFrame(bool is_root) { |
| // Adds a reference to |this|. |
| return base::BindOnce(&AXTreeSnapshotCombiner::ReceiveSnapshot, this, |
| is_root); |
| } |
| |
| void ReceiveSnapshot(bool is_root, const ui::AXTreeUpdate& snapshot) { |
| combiner_.AddTree(snapshot, is_root); |
| } |
| |
| private: |
| friend class base::RefCounted<AXTreeSnapshotCombiner>; |
| |
| // This is called automatically after the last call to ReceiveSnapshot |
| // when there are no more references to this object. |
| ~AXTreeSnapshotCombiner() { |
| combiner_.Combine(); |
| std::move(callback_).Run(combiner_.combined()); |
| } |
| |
| ui::AXTreeCombiner combiner_; |
| AXTreeSnapshotCallback callback_; |
| }; |
| |
| void WebContentsImpl::RequestAXTreeSnapshot(AXTreeSnapshotCallback callback, |
| ui::AXMode ax_mode) { |
| // Send a request to each of the frames in parallel. Each one will return |
| // an accessibility tree snapshot, and AXTreeSnapshotCombiner will combine |
| // them into a single tree and call |callback| with that result, then |
| // delete |combiner|. |
| FrameTreeNode* root_node = frame_tree_.root(); |
| auto combiner = |
| base::MakeRefCounted<AXTreeSnapshotCombiner>(std::move(callback)); |
| |
| RecursiveRequestAXTreeSnapshotOnFrame(root_node, combiner.get(), ax_mode); |
| } |
| |
| void WebContentsImpl::RecursiveRequestAXTreeSnapshotOnFrame( |
| FrameTreeNode* root_node, |
| AXTreeSnapshotCombiner* combiner, |
| ui::AXMode ax_mode) { |
| for (FrameTreeNode* frame_tree_node : frame_tree_.Nodes()) { |
| WebContentsImpl* inner_contents = |
| node_.GetInnerWebContentsInFrame(frame_tree_node); |
| if (inner_contents) { |
| inner_contents->RecursiveRequestAXTreeSnapshotOnFrame(root_node, combiner, |
| ax_mode); |
| } else { |
| bool is_root = frame_tree_node == root_node; |
| frame_tree_node->current_frame_host()->RequestAXTreeSnapshot( |
| combiner->AddFrame(is_root), ax_mode); |
| } |
| } |
| } |
| |
| void WebContentsImpl::NotifyViewportFitChanged( |
| blink::mojom::ViewportFit value) { |
| for (auto& observer : observers_) |
| observer.ViewportFitChanged(value); |
| } |
| |
| #if !defined(OS_ANDROID) |
| void WebContentsImpl::UpdateZoom() { |
| RenderWidgetHostImpl* rwh = GetRenderViewHost()->GetWidget(); |
| if (rwh->GetView()) |
| rwh->SynchronizeVisualProperties(); |
| } |
| |
| |
| void WebContentsImpl::UpdateZoomIfNecessary(const std::string& scheme, |
| const std::string& host) { |
| NavigationEntry* entry = GetController().GetLastCommittedEntry(); |
| if (!entry) |
| return; |
| |
| GURL url = HostZoomMap::GetURLFromEntry(entry); |
| if (host != net::GetHostOrSpecFromURL(url) || |
| (!scheme.empty() && !url.SchemeIs(scheme))) { |
| return; |
| } |
| |
| UpdateZoom(); |
| } |
| #endif // !defined(OS_ANDROID) |
| |
| base::Closure WebContentsImpl::AddBindingSet( |
| const std::string& interface_name, |
| WebContentsBindingSet* binding_set) { |
| auto result = |
| binding_sets_.insert(std::make_pair(interface_name, binding_set)); |
| DCHECK(result.second); |
| return base::Bind(&WebContentsImpl::RemoveBindingSet, |
| weak_factory_.GetWeakPtr(), interface_name); |
| } |
| |
| WebContentsBindingSet* WebContentsImpl::GetBindingSet( |
| const std::string& interface_name) { |
| auto it = binding_sets_.find(interface_name); |
| if (it == binding_sets_.end()) |
| return nullptr; |
| return it->second; |
| } |
| |
| std::vector<WebContentsImpl*> WebContentsImpl::GetInnerWebContents() { |
| if (browser_plugin_embedder_) { |
| std::vector<WebContentsImpl*> inner_contents; |
| GetBrowserContext()->GetGuestManager()->ForEachGuest( |
| this, base::BindRepeating(&GetInnerWebContentsHelper, &inner_contents)); |
| return inner_contents; |
| } |
| |
| return node_.GetInnerWebContents(); |
| } |
| |
| std::vector<WebContentsImpl*> WebContentsImpl::GetWebContentsAndAllInner() { |
| std::vector<WebContentsImpl*> all_contents(1, this); |
| |
| for (size_t i = 0; i != all_contents.size(); ++i) { |
| for (auto* inner_contents : all_contents[i]->GetInnerWebContents()) { |
| all_contents.push_back(inner_contents); |
| } |
| } |
| |
| return all_contents; |
| } |
| |
| void WebContentsImpl::NotifyManifestUrlChanged( |
| const base::Optional<GURL>& manifest_url) { |
| for (auto& observer : observers_) |
| observer.DidUpdateWebManifestURL(manifest_url); |
| } |
| |
| WebUI* WebContentsImpl::GetWebUI() const { |
| WebUI* commited_web_ui = GetCommittedWebUI(); |
| return commited_web_ui ? commited_web_ui |
| : GetRenderManager()->GetNavigatingWebUI(); |
| } |
| |
| WebUI* WebContentsImpl::GetCommittedWebUI() const { |
| return frame_tree_.root()->current_frame_host()->web_ui(); |
| } |
| |
| void WebContentsImpl::SetUserAgentOverride(const std::string& override, |
| bool override_in_new_tabs) { |
| if (GetUserAgentOverride() == override) |
| return; |
| |
| should_override_user_agent_in_new_tabs_ = override_in_new_tabs; |
| |
| renderer_preferences_.user_agent_override = override; |
| |
| // Send the new override string to the renderer. |
| RenderViewHost* host = GetRenderViewHost(); |
| if (host) |
| host->SyncRendererPrefs(); |
| |
| // Reload the page if a load is currently in progress to avoid having |
| // different parts of the page loaded using different user agents. |
| NavigationEntry* entry = controller_.GetVisibleEntry(); |
| if (IsLoading() && entry != nullptr && entry->GetIsOverridingUserAgent()) |
| controller_.Reload(ReloadType::BYPASSING_CACHE, true); |
| |
| for (auto& observer : observers_) |
| observer.UserAgentOverrideSet(override); |
| } |
| |
| const std::string& WebContentsImpl::GetUserAgentOverride() const { |
| return renderer_preferences_.user_agent_override; |
| } |
| |
| bool WebContentsImpl::ShouldOverrideUserAgentInNewTabs() { |
| return should_override_user_agent_in_new_tabs_; |
| } |
| |
| void WebContentsImpl::EnableWebContentsOnlyAccessibilityMode() { |
| if (!GetAccessibilityMode().is_mode_off()) { |
| for (RenderFrameHost* rfh : GetAllFrames()) |
| ResetAccessibility(rfh); |
| } else { |
| AddAccessibilityMode(ui::kAXModeWebContentsOnly); |
| } |
| } |
| |
| bool WebContentsImpl::IsWebContentsOnlyAccessibilityModeForTesting() const { |
| return accessibility_mode_ == ui::kAXModeWebContentsOnly; |
| } |
| |
| bool WebContentsImpl::IsFullAccessibilityModeForTesting() const { |
| return accessibility_mode_ == ui::kAXModeComplete; |
| } |
| |
| const PageImportanceSignals& WebContentsImpl::GetPageImportanceSignals() const { |
| return page_importance_signals_; |
| } |
| |
| const std::string& WebContentsImpl::GetMediaDeviceGroupIDSaltBase() const { |
| return media_device_group_id_salt_base_; |
| } |
| |
| #if defined(OS_ANDROID) |
| |
| void WebContentsImpl::SetDisplayCutoutSafeArea(gfx::Insets insets) { |
| if (display_cutout_host_impl_) |
| display_cutout_host_impl_->SetDisplayCutoutSafeArea(insets); |
| } |
| |
| #endif |
| |
| const base::string16& WebContentsImpl::GetTitle() const { |
| // Transient entries take precedence. They are used for interstitial pages |
| // that are shown on top of existing pages. |
| NavigationEntry* entry = controller_.GetTransientEntry(); |
| if (entry) { |
| return entry->GetTitleForDisplay(); |
| } |
| |
| WebUI* navigating_web_ui = GetRenderManager()->GetNavigatingWebUI(); |
| WebUI* our_web_ui = navigating_web_ui |
| ? navigating_web_ui |
| : GetRenderManager()->current_frame_host()->web_ui(); |
| |
| if (our_web_ui) { |
| // Don't override the title in view source mode. |
| entry = controller_.GetVisibleEntry(); |
| if (!(entry && entry->IsViewSourceMode())) { |
| // Give the Web UI the chance to override our title. |
| const base::string16& title = our_web_ui->GetOverriddenTitle(); |
| if (!title.empty()) |
| return title; |
| } |
| } |
| |
| // We use the title for the last committed entry rather than a pending |
| // navigation entry. For example, when the user types in a URL, we want to |
| // keep the old page's title until the new load has committed and we get a new |
| // title. |
| entry = controller_.GetLastCommittedEntry(); |
| |
| // We make an exception for initial navigations. We only want to use the title |
| // from the visible entry if: |
| // 1. The pending entry has been explicitly assigned a title to display. |
| // 2. The user is doing a history navigation in a new tab (e.g., Ctrl+Back), |
| // which case there is a pending entry index other than -1. |
| // |
| // Otherwise, we want to stick with the last committed entry's title during |
| // new navigations, which have pending entries at index -1 with no title. |
| if (controller_.IsInitialNavigation() && |
| ((controller_.GetVisibleEntry() && |
| !controller_.GetVisibleEntry()->GetTitle().empty()) || |
| controller_.GetPendingEntryIndex() != -1)) { |
| entry = controller_.GetVisibleEntry(); |
| } |
| |
| if (entry) { |
| return entry->GetTitleForDisplay(); |
| } |
| |
| // |page_title_when_no_navigation_entry_| is finally used |
| // if no title cannot be retrieved. |
| return page_title_when_no_navigation_entry_; |
| } |
| |
| SiteInstanceImpl* WebContentsImpl::GetSiteInstance() const { |
| return GetRenderManager()->current_host()->GetSiteInstance(); |
| } |
| |
| bool WebContentsImpl::IsLoading() const { |
| return frame_tree_.IsLoading() && |
| !(ShowingInterstitialPage() && interstitial_page_->pause_throbber()); |
| } |
| |
| double WebContentsImpl::GetLoadProgress() const { |
| return frame_tree_.load_progress(); |
| } |
| |
| bool WebContentsImpl::IsLoadingToDifferentDocument() const { |
| return IsLoading() && is_load_to_different_document_; |
| } |
| |
| bool WebContentsImpl::IsWaitingForResponse() const { |
| return waiting_for_response_ && is_load_to_different_document_; |
| } |
| |
| const net::LoadStateWithParam& WebContentsImpl::GetLoadState() const { |
| return load_state_; |
| } |
| |
| const base::string16& WebContentsImpl::GetLoadStateHost() const { |
| return load_state_host_; |
| } |
| |
| uint64_t WebContentsImpl::GetUploadSize() const { |
| return upload_size_; |
| } |
| |
| uint64_t WebContentsImpl::GetUploadPosition() const { |
| return upload_position_; |
| } |
| |
| const std::string& WebContentsImpl::GetEncoding() const { |
| return canonical_encoding_; |
| } |
| |
| bool WebContentsImpl::WasDiscarded() { |
| return GetFrameTree()->root()->was_discarded(); |
| } |
| |
| void WebContentsImpl::SetWasDiscarded(bool was_discarded) { |
| GetFrameTree()->root()->set_was_discarded(); |
| } |
| |
| void WebContentsImpl::IncrementCapturerCount(const gfx::Size& capture_size) { |
| DCHECK(!is_being_destroyed_); |
| const bool was_captured = IsBeingCaptured(); |
| ++capturer_count_; |
| DVLOG(1) << "There are now " << capturer_count_ |
| << " capturing(s) of WebContentsImpl@" << this; |
| |
| // Note: This provides a hint to upstream code to size the views optimally |
| // for quality (e.g., to avoid scaling). |
| if (!capture_size.IsEmpty() && preferred_size_for_capture_.IsEmpty()) { |
| preferred_size_for_capture_ = capture_size; |
| OnPreferredSizeChanged(preferred_size_); |
| } |
| |
| if (GetVisibility() != Visibility::VISIBLE && !was_captured) { |
| // Ensure that all views act as if they were visible before capture begins. |
| // TODO(fdoray): Replace RenderWidgetHostView::WasUnOccluded() with a method |
| // to explicitly notify the RenderWidgetHostView that capture began. |
| // https://crbug.com/668690 |
| for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree()) |
| view->WasUnOccluded(); |
| } |
| } |
| |
| void WebContentsImpl::DecrementCapturerCount() { |
| --capturer_count_; |
| DVLOG(1) << "There are now " << capturer_count_ |
| << " capturing(s) of WebContentsImpl@" << this; |
| DCHECK_LE(0, capturer_count_); |
| |
| if (is_being_destroyed_) |
| return; |
| |
| if (!IsBeingCaptured()) { |
| const gfx::Size old_size = preferred_size_for_capture_; |
| preferred_size_for_capture_ = gfx::Size(); |
| OnPreferredSizeChanged(old_size); |
| |
| if (visibility_ == Visibility::HIDDEN) { |
| DVLOG(1) << "Executing delayed WasHidden()."; |
| WasHidden(); |
| } else if (visibility_ == Visibility::OCCLUDED) { |
| WasOccluded(); |
| } |
| } |
| } |
| |
| bool WebContentsImpl::IsBeingCaptured() const { |
| return capturer_count_ > 0; |
| } |
| |
| bool WebContentsImpl::IsAudioMuted() const { |
| if (base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) { |
| return audio_stream_factory_ && audio_stream_factory_->IsMuted(); |
| } |
| return audio_muter_.get() && audio_muter_->is_muting(); |
| } |
| |
| void WebContentsImpl::SetAudioMuted(bool mute) { |
| DVLOG(1) << "SetAudioMuted(mute=" << mute << "), was " << IsAudioMuted() |
| << " for WebContentsImpl@" << this; |
| |
| if (mute == IsAudioMuted()) |
| return; |
| |
| if (base::FeatureList::IsEnabled(features::kAudioServiceAudioStreams)) { |
| GetAudioStreamFactory()->SetMuted(mute); |
| } else { |
| if (mute) { |
| if (!audio_muter_) |
| audio_muter_.reset(new WebContentsAudioMuter(this)); |
| audio_muter_->StartMuting(); |
| } else { |
| DCHECK(audio_muter_); |
| audio_muter_->StopMuting(); |
| } |
| } |
| |
| for (auto& observer : observers_) |
| observer.DidUpdateAudioMutingState(mute); |
| |
| // Notification for UI updates in response to the changed muting state. |
| NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); |
| } |
| |
| bool WebContentsImpl::IsCurrentlyAudible() { |
| return is_currently_audible_; |
| } |
| |
| bool WebContentsImpl::IsConnectedToBluetoothDevice() const { |
| return bluetooth_connected_device_count_ > 0; |
| } |
| |
| bool WebContentsImpl::HasPictureInPictureVideo() const { |
| return has_picture_in_picture_video_; |
| } |
| |
| void WebContentsImpl::SetHasPictureInPictureVideo( |
| bool has_picture_in_picture_video) { |
| // If status of |this| is already accurate, there is no need to update. |
| if (has_picture_in_picture_video == has_picture_in_picture_video_) |
| return; |
| has_picture_in_picture_video_ = has_picture_in_picture_video; |
| NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); |
| for (auto& observer : observers_) |
| observer.MediaPictureInPictureChanged(has_picture_in_picture_video_); |
| } |
| |
| bool WebContentsImpl::IsCrashed() const { |
| switch (crashed_status_) { |
| case base::TERMINATION_STATUS_PROCESS_CRASHED: |
| case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: |
| case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: |
| case base::TERMINATION_STATUS_OOM: |
| case base::TERMINATION_STATUS_LAUNCH_FAILED: |
| #if defined(OS_CHROMEOS) |
| case base::TERMINATION_STATUS_PROCESS_WAS_KILLED_BY_OOM: |
| #endif |
| #if defined(OS_ANDROID) |
| case base::TERMINATION_STATUS_OOM_PROTECTED: |
| #endif |
| return true; |
| case base::TERMINATION_STATUS_NORMAL_TERMINATION: |
| case base::TERMINATION_STATUS_STILL_RUNNING: |
| return false; |
| case base::TERMINATION_STATUS_MAX_ENUM: |
| NOTREACHED(); |
| return false; |
| } |
| |
| NOTREACHED(); |
| return false; |
| } |
| |
| void WebContentsImpl::SetIsCrashed(base::TerminationStatus status, |
| int error_code) { |
| if (status == crashed_status_) |
| return; |
| |
| crashed_status_ = status; |
| crashed_error_code_ = error_code; |
| NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); |
| } |
| |
| base::TerminationStatus WebContentsImpl::GetCrashedStatus() const { |
| return crashed_status_; |
| } |
| |
| int WebContentsImpl::GetCrashedErrorCode() const { |
| return crashed_error_code_; |
| } |
| |
| bool WebContentsImpl::IsBeingDestroyed() const { |
| return is_being_destroyed_; |
| } |
| |
| void WebContentsImpl::NotifyNavigationStateChanged( |
| InvalidateTypes changed_flags) { |
| // Notify the media observer of potential audibility changes. |
| if (changed_flags & INVALIDATE_TYPE_TAB) { |
| media_web_contents_observer_->MaybeUpdateAudibleState(); |
| } |
| |
| if (delegate_) |
| delegate_->NavigationStateChanged(this, changed_flags); |
| |
| if (GetOuterWebContents()) |
| GetOuterWebContents()->NotifyNavigationStateChanged(changed_flags); |
| } |
| |
| void WebContentsImpl::OnAudioStateChanged() { |
| // This notification can come from any embedded contents or from this |
| // WebContents' stream monitor. Aggregate these signals to get the actual |
| // state. |
| bool is_currently_audible = |
| audio_stream_monitor_.IsCurrentlyAudible() || |
| (browser_plugin_embedder_ && |
| browser_plugin_embedder_->AreAnyGuestsCurrentlyAudible()); |
| if (is_currently_audible == is_currently_audible_) |
| return; |
| |
| // Update internal state. |
| is_currently_audible_ = is_currently_audible; |
| was_ever_audible_ = was_ever_audible_ || is_currently_audible_; |
| |
| SendPageMessage( |
| new PageMsg_AudioStateChanged(MSG_ROUTING_NONE, is_currently_audible_)); |
| |
| // Notification for UI updates in response to the changed audio state. |
| NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); |
| |
| // Ensure that audio state changes propagate from innermost to outermost |
| // WebContents. |
| if (GetOuterWebContents()) |
| GetOuterWebContents()->OnAudioStateChanged(); |
| |
| for (auto& observer : observers_) |
| observer.OnAudioStateChanged(is_currently_audible_); |
| } |
| |
| base::TimeTicks WebContentsImpl::GetLastActiveTime() const { |
| return last_active_time_; |
| } |
| |
| void WebContentsImpl::WasShown() { |
| controller_.SetActive(true); |
| |
| if (auto* view = GetRenderWidgetHostView()) { |
| view->Show(); |
| #if defined(OS_MACOSX) |
| view->SetActive(true); |
| #endif |
| } |
| |
| if (!ShowingInterstitialPage()) |
| SetVisibilityForChildViews(true); |
| |
| SendPageMessage(new PageMsg_WasShown(MSG_ROUTING_NONE)); |
| |
| last_active_time_ = base::TimeTicks::Now(); |
| SetVisibility(Visibility::VISIBLE); |
| |
| for (FrameTreeNode* node : frame_tree_.Nodes()) { |
| RenderFrameProxyHost* parent = node->render_manager()->GetProxyToParent(); |
| if (!parent) |
| continue; |
| |
| parent->cross_process_frame_connector()->DelegateWasShown(); |
| } |
| } |
| |
| void WebContentsImpl::WasHidden() { |
| // If there are entities capturing screenshots or video (e.g. mirroring), |
| // or in Picture-in-Picture mode, don't activate the "disable rendering" |
| // optimization. |
| if (!IsBeingCaptured() && !HasPictureInPictureVideo()) { |
| // |GetRenderViewHost()| can be NULL if the user middle clicks a link to |
| // open a tab in the background, then closes the tab before selecting it. |
| // This is because closing the tab calls WebContentsImpl::Destroy(), which |
| // removes the |GetRenderViewHost()|; then when we actually destroy the |
| // window, OnWindowPosChanged() notices and calls WasHidden() (which |
| // calls us). |
| if (auto* view = GetRenderWidgetHostView()) |
| view->Hide(); |
| |
| if (!ShowingInterstitialPage()) |
| SetVisibilityForChildViews(false); |
| |
| SendPageMessage(new PageMsg_WasHidden(MSG_ROUTING_NONE)); |
| } |
| |
| SetVisibility(Visibility::HIDDEN); |
| } |
| |
| bool WebContentsImpl::HasRecentInteractiveInputEvent() const { |
| static constexpr base::TimeDelta kMaxInterval = |
| base::TimeDelta::FromSeconds(5); |
| base::TimeDelta delta = |
| ui::EventTimeForNow() - last_interactive_input_event_time_; |
| // Note: the expectation is that the caller is typically expecting an input |
| // event, e.g. validating that a WebUI message that requires a gesture is |
| // actually attached to a gesture. Logging to UMA here should hopefully give |
| // sufficient data if 5 seconds is actually sufficient (or even too high a |
| // threshhold). |
| UMA_HISTOGRAM_TIMES("Tabs.TimeSinceLastInteraction", delta); |
| return delta <= kMaxInterval; |
| } |
| |
| void WebContentsImpl::SetIgnoreInputEvents(bool ignore_input_events) { |
| ignore_input_events_ = ignore_input_events; |
| } |
| |
| #if defined(OS_ANDROID) |
| void WebContentsImpl::SetMainFrameImportance( |
| ChildProcessImportance importance) { |
| GetMainFrame()->GetRenderWidgetHost()->SetImportance(importance); |
| if (ShowingInterstitialPage()) { |
| static_cast<RenderFrameHostImpl*>(interstitial_page_->GetMainFrame()) |
| ->GetRenderWidgetHost() |
| ->SetImportance(importance); |
| } |
| } |
| #endif |
| |
| void WebContentsImpl::WasOccluded() { |
| if (!IsBeingCaptured()) { |
| for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree()) |
| view->WasOccluded(); |
| } |
| SetVisibility(Visibility::OCCLUDED); |
| } |
| |
| Visibility WebContentsImpl::GetVisibility() const { |
| return visibility_; |
| } |
| |
| // TODO(alexmos): rename to NeedToFireBeforeUnloadOrUnload(). |
| bool WebContentsImpl::NeedToFireBeforeUnload() { |
| // TODO(creis): Should we fire even for interstitial pages? |
| if (ShowingInterstitialPage()) |
| return false; |
| |
| if (!WillNotifyDisconnection()) |
| return false; |
| |
| // Don't fire if the main frame's RenderViewHost indicates that beforeunload |
| // and unload have already executed (e.g., after receiving a ClosePage ACK) |
| // or should be ignored. |
| if (GetRenderViewHost()->SuddenTerminationAllowed()) |
| return false; |
| |
| // TODO(alexmos): This checks for both beforeunload and unload handlers from |
| // the whole main frame process. Remove this and explicitly check whether |
| // the main frame has each of these handlers. This can be done for |
| // beforeunload via RenderFrameHostImpl::ShouldDispatchBeforeUnload(), and |
| // something similar is needed for unload. |
| if (!GetMainFrame()->GetProcess()->SuddenTerminationAllowed()) |
| return true; |
| |
| // Check whether any subframes need to run beforeunload handlers. |
| // |
| // TODO(alexmos): Also check whether subframes need to run unload handlers in |
| // addition to beforeunload. |
| return GetMainFrame()->ShouldDispatchBeforeUnload( |
| true /* check_subframes_only */); |
| } |
| |
| void WebContentsImpl::DispatchBeforeUnload(bool auto_cancel) { |
| auto before_unload_type = |
| auto_cancel ? RenderFrameHostImpl::BeforeUnloadType::DISCARD |
| : RenderFrameHostImpl::BeforeUnloadType::TAB_CLOSE; |
| GetMainFrame()->DispatchBeforeUnload(before_unload_type, false); |
| } |
| |
| void WebContentsImpl::AttachToOuterWebContentsFrame( |
| std::unique_ptr<WebContents> current_web_contents, |
| RenderFrameHost* outer_contents_frame) { |
| DCHECK(!node_.outer_web_contents()); |
| DCHECK_EQ(current_web_contents.get(), this); |
| |
| RenderFrameHostManager* render_manager = GetRenderManager(); |
| |
| // When attaching a WebContents as an inner WebContents, we need to replace |
| // the Webcontents' view with a WebContentsViewChildFrame. |
| view_.reset(new WebContentsViewChildFrame( |
| this, GetContentClient()->browser()->GetWebContentsViewDelegate(this), |
| &render_view_host_delegate_view_)); |
| |
| // When the WebContents being initialized has an opener, the browser side |
| // Render{View,Frame}Host must be initialized and the RenderWidgetHostView |
| // created. This is needed because the usual initialization happens during |
| // the first navigation, but when attaching a new window we don't navigate |
| // before attaching. If the browser side is already initialized, the calls |
| // below will just early return. |
| render_manager->InitRenderView(GetRenderViewHost(), nullptr); |
| GetMainFrame()->Init(); |
| if (!render_manager->GetRenderWidgetHostView()) |
| CreateRenderWidgetHostViewForRenderManager(GetRenderViewHost()); |
| |
| auto* outer_contents_frame_impl = |
| static_cast<RenderFrameHostImpl*>(outer_contents_frame); |
| // Create a link to our outer WebContents. |
| node_.ConnectToOuterWebContents(std::move(current_web_contents), |
| outer_contents_frame_impl); |
| |
| // Create a proxy in top-level RenderFrameHostManager, pointing to the |
| // SiteInstance of the outer WebContents. The proxy will be used to send |
| // postMessage to the inner WebContents. |
| render_manager->CreateOuterDelegateProxy( |
| outer_contents_frame->GetSiteInstance(), outer_contents_frame_impl); |
| |
| ReattachToOuterWebContentsFrame(); |
| |
| if (node_.outer_web_contents()->frame_tree_.GetFocusedFrame() == |
| outer_contents_frame_impl->frame_tree_node()) { |
| SetFocusedFrame(frame_tree_.root(), |
| outer_contents_frame->GetSiteInstance()); |
| } |
| |
| // At this point, we should destroy the TextInputManager which will notify all |
| // the RWHV in this WebContents. The RWHV in this WebContents should use the |
| // TextInputManager owned by the outer WebContents. |
| // TODO(ekaramad): Is it possible to have TextInputState before attaching to |
| // outer WebContents? In such a case, is this still the right way to hand off |
| // state tracking from inner WebContents's TextInputManager to that of the |
| // outer WebContent (crbug.com/609846)? |
| text_input_manager_.reset(nullptr); |
| } |
| |
| void WebContentsImpl::ReattachToOuterWebContentsFrame() { |
| DCHECK(node_.outer_web_contents()); |
| auto* render_manager = GetRenderManager(); |
| auto* parent_frame = |
| node_.OuterContentsFrameTreeNode()->current_frame_host()->GetParent(); |
| render_manager->SetRWHViewForInnerContents( |
| render_manager->GetRenderWidgetHostView()); |
| |
| static_cast<RenderWidgetHostViewChildFrame*>( |
| render_manager->GetRenderWidgetHostView()) |
| ->RegisterFrameSinkId(); |
| |
| // Set up the the guest's AX tree to point back at the embedder's AX tree. |
| GetMainFrame()->set_browser_plugin_embedder_ax_tree_id( |
| parent_frame->GetAXTreeID()); |
| GetMainFrame()->UpdateAXTreeData(); |
| } |
| |
| void WebContentsImpl::DidChangeVisibleSecurityState() { |
| if (delegate_) { |
| delegate_->VisibleSecurityStateChanged(this); |
| for (auto& observer : observers_) |
| observer.DidChangeVisibleSecurityState(); |
| } |
| } |
| |
| void WebContentsImpl::NotifyPreferencesChanged() { |
| std::set<RenderViewHost*> render_view_host_set; |
| for (FrameTreeNode* node : frame_tree_.Nodes()) { |
| render_view_host_set.insert( |
| node->current_frame_host()->GetRenderViewHost()); |
| } |
| |
| for (RenderViewHost* render_view_host : render_view_host_set) |
| render_view_host->OnWebkitPreferencesChanged(); |
| } |
| |
| void WebContentsImpl::Stop() { |
| for (FrameTreeNode* node : frame_tree_.Nodes()) |
| node->StopLoading(); |
| for (auto& observer : observers_) |
| observer.NavigationStopped(); |
| } |
| |
| void WebContentsImpl::SetPageFrozen(bool frozen) { |
| // A visible page is never frozen. |
| DCHECK_NE(Visibility::VISIBLE, GetVisibility()); |
| |
| SendPageMessage(new PageMsg_SetPageFrozen(MSG_ROUTING_NONE, frozen)); |
| } |
| |
| std::unique_ptr<WebContents> WebContentsImpl::Clone() { |
| // We use our current SiteInstance since the cloned entry will use it anyway. |
| // We pass our own opener so that the cloned page can access it if it was set |
| // before. |
| CreateParams create_params(GetBrowserContext(), GetSiteInstance()); |
| create_params.initial_size = GetContainerBounds().size(); |
| FrameTreeNode* opener = frame_tree_.root()->opener(); |
| RenderFrameHostImpl* opener_rfh = nullptr; |
| if (opener) |
| opener_rfh = opener->current_frame_host(); |
| std::unique_ptr<WebContentsImpl> tc = |
| CreateWithOpener(create_params, opener_rfh); |
| tc->GetController().CopyStateFrom(controller_, true); |
| for (auto& observer : observers_) |
| observer.DidCloneToNewWebContents(this, tc.get()); |
| return tc; |
| } |
| |
| void WebContentsImpl::Observe(int type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type) { |
| case NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED: { |
| RenderWidgetHost* host = Source<RenderWidgetHost>(source).ptr(); |
| RenderWidgetHostView* view = host->GetView(); |
| if (view == GetFullscreenRenderWidgetHostView()) { |
| // We cannot just call view_->RestoreFocus() here. On some platforms, |
| // attempting to focus the currently-invisible WebContentsView will be |
| // flat-out ignored. Therefore, this boolean is used to track whether |
| // we will request focus after the fullscreen widget has been |
| // destroyed. |
| fullscreen_widget_had_focus_at_shutdown_ = (view && view->HasFocus()); |
| } else { |
| for (auto i = pending_widget_views_.begin(); |
| i != pending_widget_views_.end(); ++i) { |
| if (host->GetView() == i->second) { |
| pending_widget_views_.erase(i); |
| break; |
| } |
| } |
| } |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| WebContents* WebContentsImpl::GetWebContents() { |
| return this; |
| } |
| |
| void WebContentsImpl::Init(const WebContents::CreateParams& params) { |
| // This is set before initializing the render manager since |
| // RenderFrameHostManager::Init calls back into us via its delegate to ask if |
| // it should be hidden. |
| visibility_ = |
| params.initially_hidden ? Visibility::HIDDEN : Visibility::VISIBLE; |
| |
| if (!params.last_active_time.is_null()) |
| last_active_time_ = params.last_active_time; |
| |
| // The routing ids must either all be set or all be unset. |
| DCHECK((params.routing_id == MSG_ROUTING_NONE && |
| params.main_frame_routing_id == MSG_ROUTING_NONE && |
| params.main_frame_widget_routing_id == MSG_ROUTING_NONE) || |
| (params.routing_id != MSG_ROUTING_NONE && |
| params.main_frame_routing_id != MSG_ROUTING_NONE && |
| params.main_frame_widget_routing_id != MSG_ROUTING_NONE)); |
| |
| scoped_refptr<SiteInstance> site_instance = params.site_instance; |
| if (!site_instance) |
| site_instance = SiteInstance::Create(params.browser_context); |
| if (params.desired_renderer_state == CreateParams::kNoRendererProcess) { |
| static_cast<SiteInstanceImpl*>(site_instance.get()) |
| ->PreventAssociationWithSpareProcess(); |
| } |
| |
| // A main RenderFrameHost always has a RenderWidgetHost, since it is always a |
| // local root by definition. |
| // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, it will no |
| // longer be necessary to eagerly grab a routing ID for the view. |
| // https://crbug.com/545684 |
| int32_t view_routing_id = params.routing_id; |
| int32_t main_frame_widget_routing_id = params.main_frame_widget_routing_id; |
| if (main_frame_widget_routing_id == MSG_ROUTING_NONE) { |
| view_routing_id = site_instance->GetProcess()->GetNextRoutingID(); |
| main_frame_widget_routing_id = |
| site_instance->GetProcess()->GetNextRoutingID(); |
| } |
| |
| DCHECK_NE(view_routing_id, main_frame_widget_routing_id); |
| |
| GetRenderManager()->Init( |
| site_instance.get(), view_routing_id, params.main_frame_routing_id, |
| main_frame_widget_routing_id, params.renderer_initiated_creation); |
| |
| // blink::FrameTree::setName always keeps |unique_name| empty in case of a |
| // main frame - let's do the same thing here. |
| std::string unique_name; |
| frame_tree_.root()->SetFrameName(params.main_frame_name, unique_name); |
| |
| WebContentsViewDelegate* delegate = |
| GetContentClient()->browser()->GetWebContentsViewDelegate(this); |
| |
| if (GuestMode::IsCrossProcessFrameGuest(this)) { |
| view_.reset(new WebContentsViewChildFrame( |
| this, delegate, &render_view_host_delegate_view_)); |
| } else { |
| view_.reset(CreateWebContentsView(this, delegate, |
| &render_view_host_delegate_view_)); |
| if (browser_plugin_guest_) { |
| view_ = std::make_unique<WebContentsViewGuest>( |
| this, browser_plugin_guest_.get(), std::move(view_), |
| &render_view_host_delegate_view_); |
| } |
| } |
| CHECK(render_view_host_delegate_view_); |
| CHECK(view_.get()); |
| |
| gfx::Size initial_size = params.initial_size; |
| view_->CreateView(initial_size, params.context); |
| |
| #if BUILDFLAG(ENABLE_PLUGINS) |
| plugin_content_origin_whitelist_.reset( |
| new PluginContentOriginWhitelist(this)); |
| #endif |
| |
| registrar_.Add(this, |
| NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, |
| NotificationService::AllBrowserContextsAndSources()); |
| |
| screen_orientation_provider_.reset(new ScreenOrientationProvider(this)); |
| |
| manifest_manager_host_.reset(new ManifestManagerHost(this)); |
| |
| #if defined(OS_ANDROID) |
| date_time_chooser_.reset(new DateTimeChooserAndroid()); |
| #endif |
| |
| // BrowserPluginGuest::Init needs to be called after this WebContents has |
| // a RenderWidgetHostViewGuest. That is, |view_->CreateView| above. |
| if (browser_plugin_guest_) |
| browser_plugin_guest_->Init(); |
| |
| for (size_t i = 0; i < g_created_callbacks.Get().size(); i++) |
| g_created_callbacks.Get().at(i).Run(this); |
| |
| // If the WebContents creation was renderer-initiated, it means that the |
| // corresponding RenderView and main RenderFrame have already been created. |
| // Ensure observers are notified about this. |
| if (params.renderer_initiated_creation) { |
| GetRenderViewHost()->GetWidget()->set_renderer_initialized(true); |
| GetRenderViewHost()->DispatchRenderViewCreated(); |
| GetRenderManager()->current_frame_host()->SetRenderFrameCreated(true); |
| } |
| |
| // Create the renderer process in advance if requested. |
| if (params.desired_renderer_state == |
| CreateParams::kInitializeAndWarmupRendererProcess) { |
| if (!GetRenderManager()->current_frame_host()->IsRenderFrameLive()) { |
| GetRenderManager()->InitRenderView(GetRenderViewHost(), nullptr); |
| } |
| } |
| |
| // Ensure that observers are notified of the creation of this WebContents's |
| // main RenderFrameHost. It must be done here for main frames, since the |
| // NotifySwappedFromRenderManager expects view_ to already be created and that |
| // happens after RenderFrameHostManager::Init. |
| NotifySwappedFromRenderManager( |
| nullptr, GetRenderManager()->current_frame_host(), true); |
| } |
| |
| void WebContentsImpl::OnWebContentsDestroyed(WebContentsImpl* web_contents) { |
| RemoveDestructionObserver(web_contents); |
| |
| // Clear a pending contents that has been closed before being shown. |
| for (auto iter = pending_contents_.begin(); iter != pending_contents_.end(); |
| ++iter) { |
| if (iter->second.get() != web_contents) |
| continue; |
| |
| // Someone else has deleted the WebContents. That should never happen! |
| // TODO(erikchen): Fix semantics here. https://crbug.com/832879. |
| iter->second.release(); |
| pending_contents_.erase(iter); |
| return; |
| } |
| NOTREACHED(); |
| } |
| |
| void WebContentsImpl::AddDestructionObserver(WebContentsImpl* web_contents) { |
| if (!ContainsKey(destruction_observers_, web_contents)) { |
| destruction_observers_[web_contents] = |
| std::make_unique<DestructionObserver>(this, web_contents); |
| } |
| } |
| |
| void WebContentsImpl::RemoveDestructionObserver(WebContentsImpl* web_contents) { |
| destruction_observers_.erase(web_contents); |
| } |
| |
| void WebContentsImpl::AddObserver(WebContentsObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void WebContentsImpl::RemoveObserver(WebContentsObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| std::set<RenderWidgetHostView*> |
| WebContentsImpl::GetRenderWidgetHostViewsInTree() { |
| std::set<RenderWidgetHostView*> set; |
| if (ShowingInterstitialPage()) { |
| if (RenderWidgetHostView* rwhv = GetRenderWidgetHostView()) |
| set.insert(rwhv); |
| } else { |
| for (RenderFrameHost* rfh : GetAllFrames()) { |
| if (RenderWidgetHostView* rwhv = static_cast<RenderFrameHostImpl*>(rfh) |
| ->frame_tree_node() |
| ->render_manager() |
| ->GetRenderWidgetHostView()) { |
| set.insert(rwhv); |
| } |
| } |
| } |
| return set; |
| } |
| |
| void WebContentsImpl::Activate() { |
| if (delegate_) |
| delegate_->ActivateContents(this); |
| } |
| |
| void WebContentsImpl::LostCapture(RenderWidgetHostImpl* render_widget_host) { |
| if (!RenderViewHostImpl::From(render_widget_host)) |
| return; |
| |
| if (delegate_) |
| delegate_->LostCapture(); |
| } |
| |
| ukm::SourceId WebContentsImpl::GetUkmSourceIdForLastCommittedSource() const { |
| return last_committed_source_id_; |
| } |
| |
| void WebContentsImpl::SetTopControlsShownRatio( |
| RenderWidgetHostImpl* render_widget_host, |
| float ratio) { |
| if (!delegate_) |
| return; |
| |
| RenderFrameHostImpl* rfh = GetMainFrame(); |
| if (!rfh || render_widget_host != rfh->GetRenderWidgetHost()) |
| return; |
| |
| delegate_->SetTopControlsShownRatio(this, ratio); |
| } |
| |
| bool WebContentsImpl::DoBrowserControlsShrinkRendererSize() const { |
| return delegate_ && delegate_->DoBrowserControlsShrinkRendererSize(this); |
| } |
| |
| int WebContentsImpl::GetTopControlsHeight() const { |
| return delegate_ ? delegate_->GetTopControlsHeight() : 0; |
| } |
| |
| void WebContentsImpl::SetTopControlsGestureScrollInProgress(bool in_progress) { |
| if (delegate_) |
| delegate_->SetTopControlsGestureScrollInProgress(in_progress); |
| } |
| |
| void WebContentsImpl::RenderWidgetCreated( |
| RenderWidgetHostImpl* render_widget_host) { |
| created_widgets_.insert(render_widget_host); |
| } |
| |
| void WebContentsImpl::RenderWidgetDeleted( |
| RenderWidgetHostImpl* render_widget_host) { |
| // Note that |is_being_destroyed_| can be true at this point as |
| // ~WebContentsImpl() calls RFHM::ClearRFHsPendingShutdown(), which might lead |
| // us here. |
| created_widgets_.erase(render_widget_host); |
| |
| if (is_being_destroyed_) |
| return; |
| |
| if (render_widget_host && |
| render_widget_host->GetRoutingID() == fullscreen_widget_routing_id_ && |
| render_widget_host->GetProcess()->GetID() == |
| fullscreen_widget_process_id_) { |
| if (delegate_ && delegate_->EmbedsFullscreenWidget()) |
| delegate_->ExitFullscreenModeForTab(this); |
| for (auto& observer : observers_) |
| observer.DidDestroyFullscreenWidget(); |
| fullscreen_widget_process_id_ = ChildProcessHost::kInvalidUniqueID; |
| fullscreen_widget_routing_id_ = MSG_ROUTING_NONE; |
| if (fullscreen_widget_had_focus_at_shutdown_) |
| view_->RestoreFocus(); |
| } |
| |
| if (render_widget_host == mouse_lock_widget_) |
| LostMouseLock(mouse_lock_widget_); |
| |
| CancelKeyboardLock(render_widget_host); |
| } |
| |
| void WebContentsImpl::RenderWidgetGotFocus( |
| RenderWidgetHostImpl* render_widget_host) { |
| // Notify the observers if an embedded fullscreen widget was focused. |
| if (delegate_ && render_widget_host && delegate_->EmbedsFullscreenWidget() && |
| render_widget_host->GetView() == GetFullscreenRenderWidgetHostView()) { |
| NotifyWebContentsFocused(render_widget_host); |
| } |
| } |
| |
| void WebContentsImpl::RenderWidgetLostFocus( |
| RenderWidgetHostImpl* render_widget_host) { |
| // Notify the observers if an embedded fullscreen widget lost focus. |
| if (delegate_ && render_widget_host && delegate_->EmbedsFullscreenWidget() && |
| render_widget_host->GetView() == GetFullscreenRenderWidgetHostView()) { |
| NotifyWebContentsLostFocus(render_widget_host); |
| } |
| } |
| |
| void WebContentsImpl::RenderWidgetWasResized( |
| RenderWidgetHostImpl* render_widget_host, |
| const ScreenInfo& screen_info, |
| bool width_changed) { |
| RenderFrameHostImpl* rfh = GetMainFrame(); |
| if (!rfh || render_widget_host != rfh->GetRenderWidgetHost()) |
| return; |
| |
| SendPageMessage(new PageMsg_UpdateScreenInfo(MSG_ROUTING_NONE, screen_info)); |
| |
| for (auto& observer : observers_) |
| observer.MainFrameWasResized(width_changed); |
| } |
| |
| KeyboardEventProcessingResult WebContentsImpl::PreHandleKeyboardEvent( |
| const NativeWebKeyboardEvent& event) { |
| return delegate_ ? delegate_->PreHandleKeyboardEvent(this, event) |
| : KeyboardEventProcessingResult::NOT_HANDLED; |
| } |
| |
| bool WebContentsImpl::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) { |
| if (browser_plugin_embedder_ && |
| browser_plugin_embedder_->HandleKeyboardEvent(event)) { |
| return true; |
| } |
| return delegate_ && delegate_->HandleKeyboardEvent(this, event); |
| } |
| |
| bool WebContentsImpl::HandleWheelEvent( |
| const blink::WebMouseWheelEvent& event) { |
| #if !defined(OS_MACOSX) |
| // On platforms other than Mac, control+mousewheel may change zoom. On Mac, |
| // this isn't done for two reasons: |
| // -the OS already has a gesture to do this through pinch-zoom |
| // -if a user starts an inertial scroll, let's go, and presses control |
| // (i.e. control+tab) then the OS's buffered scroll events will come in |
| // with control key set which isn't what the user wants |
| if (delegate_ && event.wheel_ticks_y && |
| event.event_action == blink::WebMouseWheelEvent::EventAction::kPageZoom) { |
| // Count only integer cumulative scrolls as zoom events; this handles |
| // smooth scroll and regular scroll device behavior. |
| zoom_scroll_remainder_ += event.wheel_ticks_y; |
| int whole_zoom_scroll_remainder_ = std::lround(zoom_scroll_remainder_); |
| zoom_scroll_remainder_ -= whole_zoom_scroll_remainder_; |
| if (whole_zoom_scroll_remainder_ != 0) { |
| delegate_->ContentsZoomChange(whole_zoom_scroll_remainder_ > 0); |
| } |
| return true; |
| } |
| #endif |
| return false; |
| } |
| |
| bool WebContentsImpl::PreHandleGestureEvent( |
| const blink::WebGestureEvent& event) { |
| return delegate_ && delegate_->PreHandleGestureEvent(this, event); |
| } |
| |
| RenderWidgetHostInputEventRouter* WebContentsImpl::GetInputEventRouter() { |
| if (!is_being_destroyed_ && GetOuterWebContents()) |
| return GetOuterWebContents()->GetInputEventRouter(); |
| |
| if (!rwh_input_event_router_.get() && !is_being_destroyed_) |
| rwh_input_event_router_.reset(new RenderWidgetHostInputEventRouter); |
| return rwh_input_event_router_.get(); |
| } |
| |
| void WebContentsImpl::ReplicatePageFocus(bool is_focused) { |
| // Focus loss may occur while this WebContents is being destroyed. Don't |
| // send the message in this case, as the main frame's RenderFrameHost and |
| // other state has already been cleared. |
| if (is_being_destroyed_) |
| return; |
| |
| frame_tree_.ReplicatePageFocus(is_focused); |
| } |
| |
| RenderWidgetHostImpl* WebContentsImpl::GetFocusedRenderWidgetHost( |
| RenderWidgetHostImpl* receiving_widget) { |
| // Events for widgets other than the main frame (e.g., popup menus) should be |
| // forwarded directly to the widget they arrived on. |
| if (receiving_widget != GetMainFrame()->GetRenderWidgetHost()) |
| return receiving_widget; |
| |
| WebContentsImpl* focused_contents = GetFocusedWebContents(); |
| |
| // If the focused WebContents is showing an interstitial, return the |
| // interstitial's widget. |
| if (focused_contents->ShowingInterstitialPage()) { |
| return static_cast<RenderFrameHostImpl*>( |
| focused_contents->interstitial_page_->GetMainFrame()) |
| ->GetRenderWidgetHost(); |
| } |
| |
| // If the focused WebContents is a guest WebContents, then get the focused |
| // frame in the embedder WebContents instead. |
| FrameTreeNode* focused_frame = nullptr; |
| if (focused_contents->browser_plugin_guest_ && |
| !GuestMode::IsCrossProcessFrameGuest(focused_contents)) { |
| focused_frame = frame_tree_.GetFocusedFrame(); |
| } else { |
| focused_frame = GetFocusedWebContents()->frame_tree_.GetFocusedFrame(); |
| } |
| |
| if (!focused_frame) |
| return receiving_widget; |
| |
| // The view may be null if a subframe's renderer process has crashed while |
| // the subframe has focus. Drop the event in that case. Do not give |
| // it to the main frame, so that the user doesn't unexpectedly type into the |
| // wrong frame if a focused subframe renderer crashes while they type. |
| RenderWidgetHostView* view = focused_frame->current_frame_host()->GetView(); |
| if (!view) |
| return nullptr; |
| |
| return RenderWidgetHostImpl::From(view->GetRenderWidgetHost()); |
| } |
| |
| RenderWidgetHostImpl* WebContentsImpl::GetRenderWidgetHostWithPageFocus() { |
| WebContentsImpl* focused_web_contents = GetFocusedWebContents(); |
| |
| if (focused_web_contents->ShowingInterstitialPage()) { |
| return static_cast<RenderFrameHostImpl*>( |
| focused_web_contents->interstitial_page_->GetMainFrame()) |
| ->GetRenderWidgetHost(); |
| } |
| if (!GuestMode::IsCrossProcessFrameGuest(focused_web_contents) && |
| focused_web_contents->browser_plugin_guest_) { |
| // If this is a guest, we need to be controlled by our embedder. |
| return focused_web_contents->GetOuterWebContents() |
| ->GetMainFrame() |
| ->GetRenderWidgetHost(); |
| } |
| |
| return focused_web_contents->GetMainFrame()->GetRenderWidgetHost(); |
| } |
| |
| void WebContentsImpl::EnterFullscreenMode( |
| const GURL& origin, |
| const blink::WebFullscreenOptions& options) { |
| // This method is being called to enter renderer-initiated fullscreen mode. |
| // Make sure any existing fullscreen widget is shut down first. |
| RenderWidgetHostView* const widget_view = GetFullscreenRenderWidgetHostView(); |
| if (widget_view) { |
| RenderWidgetHostImpl::From(widget_view->GetRenderWidgetHost()) |
| ->ShutdownAndDestroyWidget(true); |
| } |
| |
| if (delegate_) { |
| delegate_->EnterFullscreenModeForTab(this, origin, options); |
| |
| if (keyboard_lock_widget_) |
| delegate_->RequestKeyboardLock(this, esc_key_locked_); |
| } |
| |
| for (auto& observer : observers_) |
| observer.DidToggleFullscreenModeForTab(IsFullscreenForCurrentTab(), false); |
| } |
| |
| void WebContentsImpl::ExitFullscreenMode(bool will_cause_resize) { |
| // This method is being called to leave renderer-initiated fullscreen mode. |
| // Make sure any existing fullscreen widget is shut down first. |
| RenderWidgetHostView* const widget_view = GetFullscreenRenderWidgetHostView(); |
| if (widget_view) { |
| RenderWidgetHostImpl::From(widget_view->GetRenderWidgetHost()) |
| ->ShutdownAndDestroyWidget(true); |
| } |
| |
| if (delegate_) { |
| delegate_->ExitFullscreenModeForTab(this); |
| |
| if (keyboard_lock_widget_) |
| delegate_->CancelKeyboardLockRequest(this); |
| } |
| |
| // The fullscreen state is communicated to the renderer through a resize |
| // message. If the change in fullscreen state doesn't cause a view resize |
| // then we must ensure web contents exit the fullscreen state by explicitly |
| // sending a resize message. This is required for the situation of the browser |
| // moving the view into a "browser fullscreen" state and then the contents |
| // entering "tab fullscreen". Exiting the contents "tab fullscreen" then won't |
| // have the side effect of the view resizing, hence the explicit call here is |
| // required. |
| if (!will_cause_resize) { |
| if (RenderWidgetHostView* rwhv = GetRenderWidgetHostView()) { |
| if (RenderWidgetHost* render_widget_host = rwhv->GetRenderWidgetHost()) |
| render_widget_host->SynchronizeVisualProperties(); |
| } |
| } |
| |
| current_fullscreen_frame_ = nullptr; |
| |
| for (auto& observer : observers_) { |
| observer.DidToggleFullscreenModeForTab(IsFullscreenForCurrentTab(), |
| will_cause_resize); |
| } |
| |
| if (display_cutout_host_impl_) |
| display_cutout_host_impl_->DidExitFullscreen(); |
| } |
| |
| void WebContentsImpl::FullscreenStateChanged(RenderFrameHost* rfh, |
| bool is_fullscreen) { |
| RenderFrameHostImpl* frame = static_cast<RenderFrameHostImpl*>(rfh); |
| |
| if (is_fullscreen) { |
| if (!base::ContainsKey(fullscreen_frames_, frame)) { |
| fullscreen_frames_.insert(frame); |
| FullscreenFrameSetUpdated(); |
| } |
| return; |
| } |
| |
| // If |frame| is no longer in fullscreen, remove it and any descendants. |
| // See https://fullscreen.spec.whatwg.org. |
| size_t size_before_deletion = fullscreen_frames_.size(); |
| base::EraseIf(fullscreen_frames_, [&](RenderFrameHostImpl* current) { |
| return (current == frame || current->IsDescendantOf(frame)); |
| }); |
| |
| if (size_before_deletion != fullscreen_frames_.size()) |
| FullscreenFrameSetUpdated(); |
| } |
| |
| void WebContentsImpl::FullscreenFrameSetUpdated() { |
| if (fullscreen_frames_.empty()) { |
| current_fullscreen_frame_ = nullptr; |
| return; |
| } |
| |
| // Find the current fullscreen frame and call the observers. |
| // If frame A is fullscreen, then frame B goes into inner fullscreen, then B |
| // exits fullscreen - that will result in A being fullscreen. |
| RenderFrameHostImpl* new_fullscreen_frame = *std::max_element( |
| fullscreen_frames_.begin(), fullscreen_frames_.end(), FrameCompareDepth); |
| |
| // If we have already notified observers about this frame then we should not |
| // fire the observers again. |
| if (new_fullscreen_frame == current_fullscreen_frame_) |
| return; |
| current_fullscreen_frame_ = new_fullscreen_frame; |
| |
| for (auto& observer : observers_) |
| observer.DidAcquireFullscreen(new_fullscreen_frame); |
| |
| if (display_cutout_host_impl_) |
| display_cutout_host_impl_->DidAcquireFullscreen(new_fullscreen_frame); |
| } |
| |
| #if defined(OS_ANDROID) |
| void WebContentsImpl::UpdateUserGestureCarryoverInfo() { |
| if (delegate_) |
| delegate_->UpdateUserGestureCarryoverInfo(this); |
| } |
| #endif |
| |
| bool WebContentsImpl::IsFullscreenForCurrentTab() const { |
| return delegate_ ? delegate_->IsFullscreenForTabOrPending(this) : false; |
| } |
| |
| bool WebContentsImpl::IsFullscreen() { |
| return IsFullscreenForCurrentTab(); |
| } |
| |
| blink::WebDisplayMode WebContentsImpl::GetDisplayMode( |
| RenderWidgetHostImpl* render_widget_host) const { |
| if (!RenderViewHostImpl::From(render_widget_host)) |
| return blink::kWebDisplayModeBrowser; |
| |
| return delegate_ ? delegate_->GetDisplayMode(this) |
| : blink::kWebDisplayModeBrowser; |
| } |
| |
| void WebContentsImpl::RequestToLockMouse( |
| RenderWidgetHostImpl* render_widget_host, |
| bool user_gesture, |
| bool last_unlocked_by_target, |
| bool privileged) { |
| for (WebContentsImpl* current = this; current; |
| current = current->GetOuterWebContents()) { |
| if (current->mouse_lock_widget_) { |
| render_widget_host->GotResponseToLockMouseRequest(false); |
| return; |
| } |
| } |
| |
| if (privileged) { |
| DCHECK(!GetOuterWebContents()); |
| mouse_lock_widget_ = render_widget_host; |
| render_widget_host->GotResponseToLockMouseRequest(true); |
| return; |
| } |
| |
| bool widget_in_frame_tree = false; |
| for (FrameTreeNode* node : frame_tree_.Nodes()) { |
| if (node->current_frame_host()->GetRenderWidgetHost() == |
| render_widget_host) { |
| widget_in_frame_tree = true; |
| break; |
| } |
| } |
| |
| if (widget_in_frame_tree && delegate_) { |
| for (WebContentsImpl* current = this; current; |
| current = current->GetOuterWebContents()) { |
| current->mouse_lock_widget_ = render_widget_host; |
| } |
| |
| delegate_->RequestToLockMouse(this, user_gesture, last_unlocked_by_target); |
| } else { |
| render_widget_host->GotResponseToLockMouseRequest(false); |
| } |
| } |
| |
| void WebContentsImpl::LostMouseLock(RenderWidgetHostImpl* render_widget_host) { |
| CHECK(mouse_lock_widget_); |
| |
| if (mouse_lock_widget_->delegate()->GetAsWebContents() != this) |
| return mouse_lock_widget_->delegate()->LostMouseLock(render_widget_host); |
| |
| mouse_lock_widget_->SendMouseLockLost(); |
| for (WebContentsImpl* current = this; current; |
| current = current->GetOuterWebContents()) { |
| current->mouse_lock_widget_ = nullptr; |
| } |
| |
| if (delegate_) |
| delegate_->LostMouseLock(); |
| } |
| |
| bool WebContentsImpl::HasMouseLock(RenderWidgetHostImpl* render_widget_host) { |
| // To verify if the mouse is locked, the mouse_lock_widget_ needs to be |
| // assigned to the widget that requested the mouse lock, and the top-level |
| // platform RenderWidgetHostView needs to hold the mouse lock from the OS. |
| auto* widget_host = GetTopLevelRenderWidgetHostView(); |
| return mouse_lock_widget_ == render_widget_host && widget_host && |
| widget_host->IsMouseLocked(); |
| } |
| |
| RenderWidgetHostImpl* WebContentsImpl::GetMouseLockWidget() { |
| auto* widget_host = GetTopLevelRenderWidgetHostView(); |
| if ((widget_host && widget_host->IsMouseLocked()) || |
| (GetFullscreenRenderWidgetHostView() && |
| GetFullscreenRenderWidgetHostView()->IsMouseLocked())) { |
| return mouse_lock_widget_; |
| } |
| |
| return nullptr; |
| } |
| |
| bool WebContentsImpl::RequestKeyboardLock( |
| RenderWidgetHostImpl* render_widget_host, |
| bool esc_key_locked) { |
| DCHECK(render_widget_host); |
| if (render_widget_host->delegate()->GetAsWebContents() != this) { |
| NOTREACHED(); |
| return false; |
| } |
| |
| // KeyboardLock is only supported when called by the top-level browsing |
| // context and is not supported in embedded content scenarios. |
| if (GetOuterWebContents()) |
| return false; |
| |
| esc_key_locked_ = esc_key_locked; |
| keyboard_lock_widget_ = render_widget_host; |
| |
| if (delegate_) |
| delegate_->RequestKeyboardLock(this, esc_key_locked_); |
| return true; |
| } |
| |
| void WebContentsImpl::CancelKeyboardLock( |
| RenderWidgetHostImpl* render_widget_host) { |
| if (!keyboard_lock_widget_ || render_widget_host != keyboard_lock_widget_) |
| return; |
| |
| RenderWidgetHostImpl* old_keyboard_lock_widget = keyboard_lock_widget_; |
| keyboard_lock_widget_ = nullptr; |
| |
| if (delegate_) |
| delegate_->CancelKeyboardLockRequest(this); |
| |
| old_keyboard_lock_widget->CancelKeyboardLock(); |
| } |
| |
| RenderWidgetHostImpl* WebContentsImpl::GetKeyboardLockWidget() { |
| return keyboard_lock_widget_; |
| } |
| |
| void WebContentsImpl::OnRenderFrameProxyVisibilityChanged(bool visible) { |
| if (visible && !GetOuterWebContents()->IsHidden()) |
| WasShown(); |
| else if (!visible) |
| WasHidden(); |
| } |
| |
| void WebContentsImpl::CreateNewWindow( |
| RenderFrameHost* opener, |
| int32_t render_view_route_id, |
| int32_t main_frame_route_id, |
| int32_t main_frame_widget_route_id, |
| const mojom::CreateNewWindowParams& params, |
| SessionStorageNamespace* session_storage_namespace) { |
| // We should have zero valid routing ids, or three valid routing IDs. |
| DCHECK_EQ((render_view_route_id == MSG_ROUTING_NONE), |
| (main_frame_route_id == MSG_ROUTING_NONE)); |
| DCHECK_EQ((render_view_route_id == MSG_ROUTING_NONE), |
| (main_frame_widget_route_id == MSG_ROUTING_NONE)); |
| DCHECK(opener); |
| |
| int render_process_id = opener->GetProcess()->GetID(); |
| SiteInstance* source_site_instance = opener->GetSiteInstance(); |
| |
| // The route IDs passed into this function can be trusted not to already |
| // be in use; they were allocated by the RenderWidgetHelper by the caller. |
| DCHECK(!RenderFrameHostImpl::FromID(render_process_id, main_frame_route_id)); |
| |
| // We usually create the new window in the same BrowsingInstance (group of |
| // script-related windows), by passing in the current SiteInstance. However, |
| // if the opener is being suppressed (in a non-guest), we create a new |
| // SiteInstance in its own BrowsingInstance. |
| bool is_guest = BrowserPluginGuest::IsGuest(this); |
| |
| // If the opener is to be suppressed, the new window can be in any process. |
| // Since routing ids are process specific, we must not have one passed in |
| // as argument here. |
| DCHECK(!params.opener_suppressed || render_view_route_id == MSG_ROUTING_NONE); |
| |
| scoped_refptr<SiteInstance> site_instance = |
| params.opener_suppressed && !is_guest |
| ? SiteInstance::CreateForURL(GetBrowserContext(), params.target_url) |
| : source_site_instance; |
| |
| // We must assign the SessionStorageNamespace before calling Init(). |
| // |
| // http://crbug.com/142685 |
| const std::string& partition_id = |
| GetContentClient()->browser()-> |
| GetStoragePartitionIdForSite(GetBrowserContext(), |
| site_instance->GetSiteURL()); |
| StoragePartition* partition = BrowserContext::GetStoragePartition( |
| GetBrowserContext(), site_instance.get()); |
| DOMStorageContextWrapper* dom_storage_context = |
| static_cast<DOMStorageContextWrapper*>(partition->GetDOMStorageContext()); |
| SessionStorageNamespaceImpl* session_storage_namespace_impl = |
| static_cast<SessionStorageNamespaceImpl*>(session_storage_namespace); |
| CHECK(session_storage_namespace_impl->IsFromContext(dom_storage_context)); |
| |
| if (delegate_ && |
| !delegate_->ShouldCreateWebContents( |
| this, opener, source_site_instance, render_view_route_id, |
| main_frame_route_id, main_frame_widget_route_id, |
| params.window_container_type, opener->GetLastCommittedURL(), |
| params.frame_name, params.target_url, partition_id, |
| session_storage_namespace)) { |
| // Note: even though we're not creating a WebContents here, it could have |
| // been created by the embedder so ensure that the RenderFrameHost is |
| // properly initialized. |
| // It's safe to only target the frame because the render process will not |
| // have a chance to create more frames at this point. |
| RenderFrameHostImpl* rfh = |
| RenderFrameHostImpl::FromID(render_process_id, main_frame_route_id); |
| if (rfh) { |
| DCHECK(rfh->IsRenderFrameLive()); |
| rfh->Init(); |
| } |
| return; |
| } |
| |
| // Create the new web contents. This will automatically create the new |
| // WebContentsView. In the future, we may want to create the view separately. |
| CreateParams create_params(GetBrowserContext(), site_instance.get()); |
| create_params.routing_id = render_view_route_id; |
| create_params.main_frame_routing_id = main_frame_route_id; |
| create_params.main_frame_widget_routing_id = main_frame_widget_route_id; |
| create_params.main_frame_name = params.frame_name; |
| create_params.opener_render_process_id = render_process_id; |
| create_params.opener_render_frame_id = opener->GetRoutingID(); |
| create_params.opener_suppressed = params.opener_suppressed; |
| if (params.disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) |
| create_params.initially_hidden = true; |
| create_params.renderer_initiated_creation = |
| main_frame_route_id != MSG_ROUTING_NONE; |
| |
| std::unique_ptr<WebContents> new_contents; |
| if (!is_guest) { |
| create_params.context = view_->GetNativeView(); |
| create_params.initial_size = GetContainerBounds().size(); |
| new_contents = WebContents::Create(create_params); |
| } else { |
| new_contents = base::WrapUnique( |
| GetBrowserPluginGuest()->CreateNewGuestWindow(create_params)); |
| } |
| auto owning_contents_impl = |
| base::WrapUnique(static_cast<WebContentsImpl*>(new_contents.release())); |
| auto* new_contents_impl = owning_contents_impl.get(); |
| |
| new_contents_impl->GetController().SetSessionStorageNamespace( |
| partition_id, session_storage_namespace); |
| |
| // If the new frame has a name, make sure any SiteInstances that can find |
| // this named frame have proxies for it. Must be called after |
| // SetSessionStorageNamespace, since this calls CreateRenderView, which uses |
| // GetSessionStorageNamespace. |
| if (!params.frame_name.empty()) |
| new_contents_impl->GetRenderManager()->CreateProxiesForNewNamedFrame(); |
| |
| // Save the window for later if we're not suppressing the opener (since it |
| // will be shown immediately). |
| if (!params.opener_suppressed) { |
| if (!is_guest) { |
| WebContentsView* new_view = new_contents_impl->view_.get(); |
| |
| // TODO(brettw): It seems bogus that we have to call this function on the |
| // newly created object and give it one of its own member variables. |
| new_view->CreateViewForWidget( |
| new_contents_impl->GetRenderViewHost()->GetWidget(), false); |
| } |
| // Save the created window associated with the route so we can show it |
| // later. |
| // |
| // TODO(ajwong): This should be keyed off the RenderFrame routing id or the |
| // FrameTreeNode id instead of the routing id of the Widget for the main |
| // frame. https://crbug.com/545684 |
| DCHECK_NE(MSG_ROUTING_NONE, main_frame_widget_route_id); |
| GlobalRoutingID id(render_process_id, main_frame_widget_route_id); |
| pending_contents_[id] = std::move(owning_contents_impl); |
| AddDestructionObserver(new_contents_impl); |
| } |
| |
| if (delegate_) { |
| delegate_->WebContentsCreated(this, render_process_id, |
| opener->GetRoutingID(), params.frame_name, |
| params.target_url, new_contents_impl); |
| } |
| |
| for (auto& observer : observers_) { |
| observer.DidOpenRequestedURL(new_contents_impl, opener, params.target_url, |
| params.referrer.To<Referrer>(), |
| params.disposition, ui::PAGE_TRANSITION_LINK, |
| false, // started_from_context_menu |
| true); // renderer_initiated |
| } |
| |
| // Any new WebContents opened while this WebContents is in fullscreen can be |
| // used to confuse the user, so drop fullscreen. |
| ForSecurityDropFullscreen(); |
| |
| if (params.opener_suppressed) { |
| // When the opener is suppressed, the original renderer cannot access the |
| // new window. As a result, we need to show and navigate the window here. |
| bool was_blocked = false; |
| |
| if (delegate_) { |
| base::WeakPtr<WebContentsImpl> weak_new_contents = |
| new_contents_impl->weak_factory_.GetWeakPtr(); |
|