| // 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/renderer/render_view_impl.h" |
| |
| #include <map> |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/lazy_instance.h" |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_piece.h" |
| #include "cc/trees/ukm_manager.h" |
| #include "content/common/agent_scheduling_group.mojom.h" |
| #include "content/public/common/content_client.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/renderer/content_renderer_client.h" |
| #include "content/public/renderer/render_view_visitor.h" |
| #include "content/public/renderer/window_features_converter.h" |
| #include "content/renderer/agent_scheduling_group.h" |
| #include "content/renderer/render_frame_impl.h" |
| #include "content/renderer/render_frame_proxy.h" |
| #include "content/renderer/render_thread_impl.h" |
| #include "third_party/blink/public/mojom/page/page.mojom.h" |
| #include "third_party/blink/public/platform/impression_conversions.h" |
| #include "third_party/blink/public/platform/modules/video_capture/web_video_capture_impl_manager.h" |
| #include "third_party/blink/public/platform/url_conversion.h" |
| #include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.h" |
| #include "third_party/blink/public/web/web_frame_widget.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_page_popup.h" |
| #include "third_party/blink/public/web/web_view.h" |
| #include "third_party/blink/public/web/web_window_features.h" |
| #include "ui/base/ui_base_features.h" |
| |
| using blink::WebFrame; |
| using blink::WebLocalFrame; |
| using blink::WebNavigationPolicy; |
| using blink::WebString; |
| using blink::WebURLRequest; |
| using blink::WebView; |
| using blink::WebWindowFeatures; |
| |
| namespace content { |
| |
| //----------------------------------------------------------------------------- |
| |
| typedef std::map<blink::WebView*, RenderViewImpl*> ViewMap; |
| static base::LazyInstance<ViewMap>::Leaky g_view_map = |
| LAZY_INSTANCE_INITIALIZER; |
| typedef std::map<int32_t, RenderViewImpl*> RoutingIDViewMap; |
| static base::LazyInstance<RoutingIDViewMap>::Leaky g_routing_id_view_map = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| // static |
| WindowOpenDisposition RenderViewImpl::NavigationPolicyToDisposition( |
| WebNavigationPolicy policy) { |
| switch (policy) { |
| case blink::kWebNavigationPolicyDownload: |
| return WindowOpenDisposition::SAVE_TO_DISK; |
| case blink::kWebNavigationPolicyCurrentTab: |
| return WindowOpenDisposition::CURRENT_TAB; |
| case blink::kWebNavigationPolicyNewBackgroundTab: |
| return WindowOpenDisposition::NEW_BACKGROUND_TAB; |
| case blink::kWebNavigationPolicyNewForegroundTab: |
| return WindowOpenDisposition::NEW_FOREGROUND_TAB; |
| case blink::kWebNavigationPolicyNewWindow: |
| return WindowOpenDisposition::NEW_WINDOW; |
| case blink::kWebNavigationPolicyNewPopup: |
| return WindowOpenDisposition::NEW_POPUP; |
| default: |
| NOTREACHED() << "Unexpected WebNavigationPolicy"; |
| return WindowOpenDisposition::IGNORE_ACTION; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| namespace { |
| |
| content::mojom::WindowContainerType WindowFeaturesToContainerType( |
| const blink::WebWindowFeatures& window_features) { |
| if (window_features.background) { |
| if (window_features.persistent) |
| return content::mojom::WindowContainerType::PERSISTENT; |
| else |
| return content::mojom::WindowContainerType::BACKGROUND; |
| } else { |
| return content::mojom::WindowContainerType::NORMAL; |
| } |
| } |
| |
| } // namespace |
| |
| RenderViewImpl::RenderViewImpl(AgentSchedulingGroup& agent_scheduling_group, |
| const mojom::CreateViewParams& params) |
| : routing_id_(params.view_id), |
| renderer_wide_named_frame_lookup_( |
| params.renderer_wide_named_frame_lookup), |
| agent_scheduling_group_(agent_scheduling_group) { |
| // Please put all logic in RenderViewImpl::Initialize(). |
| } |
| |
| void RenderViewImpl::Initialize( |
| mojom::CreateViewParamsPtr params, |
| bool was_created_by_renderer, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| DCHECK(RenderThread::IsMainThread()); |
| |
| WebFrame* opener_frame = nullptr; |
| if (params->opener_frame_token) |
| opener_frame = WebFrame::FromFrameToken(params->opener_frame_token.value()); |
| |
| // The newly created webview_ is owned by this instance. |
| webview_ = WebView::Create( |
| this, params->hidden, params->is_prerendering, |
| params->type == mojom::ViewWidgetType::kPortal ? true : false, |
| /*compositing_enabled=*/true, params->never_composited, |
| opener_frame ? opener_frame->View() : nullptr, |
| std::move(params->blink_page_broadcast), |
| agent_scheduling_group_.agent_group_scheduler(), |
| params->session_storage_namespace_id, params->base_background_color); |
| |
| g_view_map.Get().insert(std::make_pair(GetWebView(), this)); |
| g_routing_id_view_map.Get().insert(std::make_pair(GetRoutingID(), this)); |
| |
| bool local_main_frame = params->main_frame->is_local_params(); |
| |
| webview_->SetWebPreferences(params->web_preferences); |
| |
| if (local_main_frame) { |
| RenderFrameImpl::CreateMainFrame( |
| agent_scheduling_group_, this, opener_frame, |
| params->type != mojom::ViewWidgetType::kTopLevel, |
| std::move(params->replication_state), params->devtools_main_frame_token, |
| std::move(params->main_frame->get_local_params())); |
| } else { |
| RenderFrameProxy::CreateFrameProxy( |
| agent_scheduling_group_, params->main_frame->get_remote_params()->token, |
| params->main_frame->get_remote_params()->routing_id, |
| params->opener_frame_token, GetRoutingID(), MSG_ROUTING_NONE, |
| blink::mojom::TreeScopeType::kDocument /* ignored for main frames */, |
| std::move(params->replication_state), params->devtools_main_frame_token, |
| std::move( |
| params->main_frame->get_remote_params()->main_frame_interfaces)); |
| } |
| |
| // TODO(davidben): Move this state from Blink into content. |
| if (params->window_was_opened_by_another_window) |
| GetWebView()->SetOpenedByDOM(); |
| |
| webview_->SetRendererPreferences(params->renderer_preferences); |
| |
| GetContentClient()->renderer()->WebViewCreated(webview_); |
| |
| #if defined(OS_ANDROID) |
| // TODO(sgurun): crbug.com/325351 Needed only for android webview's deprecated |
| // HandleNavigation codepath. |
| was_created_by_renderer_ = was_created_by_renderer; |
| #endif |
| } |
| |
| RenderViewImpl::~RenderViewImpl() { |
| DCHECK(destroying_); // Always deleted through Destroy(). |
| |
| g_routing_id_view_map.Get().erase(routing_id_); |
| |
| #ifndef NDEBUG |
| // Make sure we are no longer referenced by the ViewMap or RoutingIDViewMap. |
| ViewMap* views = g_view_map.Pointer(); |
| for (ViewMap::iterator it = views->begin(); it != views->end(); ++it) |
| DCHECK_NE(this, it->second) << "Failed to call Close?"; |
| RoutingIDViewMap* routing_id_views = g_routing_id_view_map.Pointer(); |
| for (RoutingIDViewMap::iterator it = routing_id_views->begin(); |
| it != routing_id_views->end(); ++it) |
| DCHECK_NE(this, it->second) << "Failed to call Close?"; |
| #endif |
| } |
| |
| /*static*/ |
| RenderView* RenderView::FromWebView(blink::WebView* webview) { |
| DCHECK(RenderThread::IsMainThread()); |
| ViewMap* views = g_view_map.Pointer(); |
| auto it = views->find(webview); |
| return it == views->end() ? NULL : it->second; |
| } |
| |
| /*static*/ |
| RenderViewImpl* RenderViewImpl::FromRoutingID(int32_t routing_id) { |
| DCHECK(RenderThread::IsMainThread()); |
| RoutingIDViewMap* views = g_routing_id_view_map.Pointer(); |
| auto it = views->find(routing_id); |
| return it == views->end() ? NULL : it->second; |
| } |
| |
| /*static*/ |
| void RenderView::ForEach(RenderViewVisitor* visitor) { |
| DCHECK(RenderThread::IsMainThread()); |
| ViewMap* views = g_view_map.Pointer(); |
| for (auto it = views->begin(); it != views->end(); ++it) { |
| if (!visitor->Visit(it->second)) |
| return; |
| } |
| } |
| |
| /*static*/ |
| RenderViewImpl* RenderViewImpl::Create( |
| AgentSchedulingGroup& agent_scheduling_group, |
| mojom::CreateViewParamsPtr params, |
| bool was_created_by_renderer, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) { |
| DCHECK(params->view_id != MSG_ROUTING_NONE); |
| DCHECK(!params->session_storage_namespace_id.empty()) |
| << "Session storage namespace must be populated."; |
| |
| RenderViewImpl* render_view = |
| new RenderViewImpl(agent_scheduling_group, *params); |
| render_view->Initialize(std::move(params), was_created_by_renderer, |
| std::move(task_runner)); |
| return render_view; |
| } |
| |
| void RenderViewImpl::Destroy() { |
| destroying_ = true; |
| |
| webview_->Close(); |
| // The webview_ is already destroyed by the time we get here, remove any |
| // references to it. |
| g_view_map.Get().erase(webview_); |
| webview_ = nullptr; |
| |
| delete this; |
| } |
| |
| // blink::WebViewClient ------------------------------------------------------ |
| |
| // TODO(csharrison): Migrate this method to WebLocalFrameClient / |
| // RenderFrameImpl, as it is now serviced by a mojo interface scoped to the |
| // opener frame. |
| WebView* RenderViewImpl::CreateView( |
| WebLocalFrame* creator, |
| const WebURLRequest& request, |
| const WebWindowFeatures& features, |
| const WebString& frame_name, |
| WebNavigationPolicy policy, |
| network::mojom::WebSandboxFlags sandbox_flags, |
| const blink::SessionStorageNamespaceId& session_storage_namespace_id, |
| bool& consumed_user_gesture, |
| const absl::optional<blink::WebImpression>& impression) { |
| consumed_user_gesture = false; |
| RenderFrameImpl* creator_frame = RenderFrameImpl::FromWebFrame(creator); |
| mojom::CreateNewWindowParamsPtr params = mojom::CreateNewWindowParams::New(); |
| |
| // The user activation check is done at the browser process through |
| // |frame_host->CreateNewWindow()| call below. But the extensions case |
| // handled through the following |if| is an exception. |
| params->allow_popup = false; |
| if (GetContentClient()->renderer()->AllowPopup()) |
| params->allow_popup = true; |
| |
| params->window_container_type = WindowFeaturesToContainerType(features); |
| |
| params->session_storage_namespace_id = session_storage_namespace_id; |
| if (!features.noopener) { |
| params->clone_from_session_storage_namespace_id = |
| GetWebView()->GetSessionStorageNamespaceId(); |
| } |
| |
| const std::string& frame_name_utf8 = frame_name.Utf8( |
| WebString::UTF8ConversionMode::kStrictReplacingErrorsWithFFFD); |
| params->frame_name = frame_name_utf8; |
| params->opener_suppressed = features.noopener; |
| params->disposition = NavigationPolicyToDisposition(policy); |
| if (!request.IsNull()) { |
| params->target_url = request.Url(); |
| params->referrer = blink::mojom::Referrer::New( |
| blink::WebStringToGURL(request.ReferrerString()), |
| request.GetReferrerPolicy()); |
| } |
| params->features = ConvertWebWindowFeaturesToMojoWindowFeatures(features); |
| |
| if (impression) { |
| params->impression = blink::ConvertWebImpressionToImpression(*impression); |
| } |
| |
| params->download_policy.ApplyDownloadFramePolicy( |
| /*is_opener_navigation=*/false, request.HasUserGesture(), |
| // `openee_can_access_opener_origin` only matters for opener navigations, |
| // so its value here is irrelevant. |
| /*openee_can_access_opener_origin=*/true, !creator->IsAllowedToDownload(), |
| creator->IsAdSubframe()); |
| |
| // We preserve this information before sending the message since |params| is |
| // moved on send. |
| bool is_background_tab = |
| params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB; |
| |
| mojom::CreateNewWindowStatus status; |
| mojom::CreateNewWindowReplyPtr reply; |
| auto* frame_host = creator_frame->GetFrameHost(); |
| bool err = !frame_host->CreateNewWindow(std::move(params), &status, &reply); |
| if (err || status == mojom::CreateNewWindowStatus::kIgnore) |
| return nullptr; |
| |
| // For Android WebView, we support a pop-up like behavior for window.open() |
| // even if the embedding app doesn't support multiple windows. In this case, |
| // window.open() will return "window" and navigate it to whatever URL was |
| // passed. We also don't need to consume user gestures to protect against |
| // multiple windows being opened, because, well, the app doesn't support |
| // multiple windows. |
| // TODO(dcheng): It's awkward that this is plumbed into Blink but not really |
| // used much in Blink, except to enable web testing... perhaps this should |
| // be checked directly in the browser side. |
| if (status == mojom::CreateNewWindowStatus::kReuse) |
| return GetWebView(); |
| |
| DCHECK(reply); |
| DCHECK_NE(MSG_ROUTING_NONE, reply->route_id); |
| DCHECK_NE(MSG_ROUTING_NONE, reply->main_frame_route_id); |
| DCHECK_NE(MSG_ROUTING_NONE, reply->widget_params->routing_id); |
| |
| // The browser allowed creation of a new window and consumed the user |
| // activation. |
| consumed_user_gesture = creator->ConsumeTransientUserActivation( |
| blink::UserActivationUpdateSource::kBrowser); |
| |
| // While this view may be a background extension page, it can spawn a visible |
| // render view. So we just assume that the new one is not another background |
| // page instead of passing on our own value. |
| // TODO(vangelis): Can we tell if the new view will be a background page? |
| bool never_composited = false; |
| |
| // The initial hidden state for the RenderViewImpl here has to match what the |
| // browser will eventually decide for the given disposition. Since we have to |
| // return from this call synchronously, we just have to make our best guess |
| // and rely on the browser sending a WasHidden / WasShown message if it |
| // disagrees. |
| mojom::CreateViewParamsPtr view_params = mojom::CreateViewParams::New(); |
| |
| view_params->opener_frame_token = creator->GetFrameToken(); |
| DCHECK_EQ(GetRoutingID(), creator_frame->render_view()->GetRoutingID()); |
| |
| view_params->window_was_opened_by_another_window = true; |
| view_params->renderer_preferences = webview_->GetRendererPreferences(); |
| view_params->web_preferences = webview_->GetWebPreferences(); |
| view_params->view_id = reply->route_id; |
| |
| view_params->replication_state = blink::mojom::FrameReplicationState::New(); |
| view_params->replication_state->frame_policy.sandbox_flags = sandbox_flags; |
| view_params->replication_state->name = frame_name_utf8; |
| view_params->devtools_main_frame_token = reply->devtools_main_frame_token; |
| |
| auto main_frame_params = mojom::CreateLocalMainFrameParams::New(); |
| main_frame_params->token = reply->main_frame_token; |
| main_frame_params->routing_id = reply->main_frame_route_id; |
| main_frame_params->frame = std::move(reply->frame); |
| main_frame_params->interface_broker = |
| std::move(reply->main_frame_interface_broker); |
| main_frame_params->policy_container = std::move(reply->policy_container); |
| main_frame_params->widget_params = std::move(reply->widget_params); |
| main_frame_params->subresource_loader_factories = |
| base::WrapUnique(static_cast<blink::PendingURLLoaderFactoryBundle*>( |
| creator_frame->CloneLoaderFactories()->Clone().release())); |
| |
| view_params->main_frame = |
| mojom::CreateMainFrameUnion::NewLocalParams(std::move(main_frame_params)); |
| view_params->blink_page_broadcast = std::move(reply->page_broadcast); |
| view_params->session_storage_namespace_id = |
| reply->cloned_session_storage_namespace_id; |
| DCHECK(!view_params->session_storage_namespace_id.empty()) |
| << "Session storage namespace must be populated."; |
| view_params->hidden = is_background_tab; |
| view_params->never_composited = never_composited; |
| |
| RenderViewImpl* view = RenderViewImpl::Create( |
| agent_scheduling_group_, std::move(view_params), |
| /*was_created_by_renderer=*/true, |
| creator->GetTaskRunner(blink::TaskType::kInternalDefault)); |
| |
| if (reply->wait_for_debugger) { |
| blink::WebFrameWidget* frame_widget = view->GetWebView() |
| ->MainFrame() |
| ->ToWebLocalFrame() |
| ->LocalRoot() |
| ->FrameWidget(); |
| frame_widget->WaitForDebuggerWhenShown(); |
| } |
| |
| return view->GetWebView(); |
| } |
| |
| // RenderView implementation --------------------------------------------------- |
| |
| int RenderViewImpl::GetRoutingID() { |
| return routing_id_; |
| } |
| |
| blink::WebView* RenderViewImpl::GetWebView() { |
| return webview_; |
| } |
| |
| } // namespace content |