| // Copyright 2015 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 "extensions/browser/guest_view/extensions_guest_view_message_filter.h" |
| |
| #include "base/guid.h" |
| #include "base/no_destructor.h" |
| #include "base/stl_util.h" |
| #include "base/task/post_task.h" |
| #include "base/time/time.h" |
| #include "components/guest_view/browser/bad_message.h" |
| #include "components/guest_view/browser/guest_view_base.h" |
| #include "components/guest_view/browser/guest_view_manager.h" |
| #include "components/guest_view/browser/guest_view_manager_delegate.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/navigation_controller.h" |
| #include "content/public/browser/navigation_handle.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/stream_info.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/mime_handler_view_mode.h" |
| #include "extensions/browser/api/extensions_api_client.h" |
| #include "extensions/browser/bad_message.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h" |
| #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_constants.h" |
| #include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" |
| #include "extensions/browser/guest_view/web_view/web_view_content_script_manager.h" |
| #include "extensions/browser/guest_view/web_view/web_view_guest.h" |
| #include "extensions/browser/guest_view/web_view/web_view_renderer_state.h" |
| #include "extensions/common/guest_view/extensions_guest_view_messages.h" |
| #include "extensions/common/manifest_handlers/mime_types_handler.h" |
| #include "ipc/ipc_message.h" |
| #include "ipc/ipc_message_macros.h" |
| #include "url/gurl.h" |
| |
| using content::BrowserContext; |
| using content::BrowserThread; |
| using content::NavigationHandle; |
| using content::NavigationThrottle; |
| using content::RenderFrameHost; |
| using content::SiteInstance; |
| using content::WebContents; |
| using guest_view::GuestViewManager; |
| using guest_view::GuestViewManagerDelegate; |
| using guest_view::GuestViewMessageFilter; |
| |
| namespace extensions { |
| |
| namespace { |
| |
| // Arbitrary delay to quit attaching the MimeHandlerViewGuest's WebContents to |
| // the outer WebContents if no about:blank navigation is committed. The reason |
| // for this delay is to allow user to decide on the outcome of 'beforeunload'. |
| const int64_t kAttachFailureDelayMS = 30000; |
| |
| // Cancels the given navigation handle unconditionally. |
| class CancelAndIgnoreNavigationForPluginFrameThrottle |
| : public NavigationThrottle { |
| public: |
| explicit CancelAndIgnoreNavigationForPluginFrameThrottle( |
| NavigationHandle* handle) |
| : NavigationThrottle(handle) {} |
| ~CancelAndIgnoreNavigationForPluginFrameThrottle() override {} |
| |
| const char* GetNameForLogging() override { |
| return "CancelAndIgnoreNavigationForPluginFrameThrottle"; |
| } |
| ThrottleCheckResult WillStartRequest() override { return CANCEL_AND_IGNORE; } |
| ThrottleCheckResult WillProcessResponse() override { return BLOCK_RESPONSE; } |
| }; |
| |
| // TODO(ekaramad): Remove this once MimeHandlerViewGuest has fully migrated to |
| // using cross-process-frames. |
| // Returns true if |child_routing_id| corresponds to a frame which is a direct |
| // child of |parent_rfh|. |
| bool AreRoutingIDsConsistent(RenderFrameHost* parent_rfh, |
| int32_t child_routing_id) { |
| const bool uses_cross_process_frame = |
| content::MimeHandlerViewMode::UsesCrossProcessFrame(); |
| const bool is_child_routing_id_none = (child_routing_id == MSG_ROUTING_NONE); |
| |
| // For cross-process-frame MimeHandlerView, |child_routing_id| cannot be none. |
| bool should_shutdown_process = |
| (is_child_routing_id_none == uses_cross_process_frame); |
| |
| if (!should_shutdown_process && uses_cross_process_frame) { |
| // The |child_routing_id| is the routing ID of either a RenderFrame or a |
| // proxy in the |parent_rfh|. Therefore, to get the associated RFH we need |
| // to go through the FTN first. |
| int32_t child_ftn_id = RenderFrameHost::GetFrameTreeNodeIdForRoutingId( |
| parent_rfh->GetProcess()->GetID(), child_routing_id); |
| // The |child_rfh| is not really used; it is retrieved to verify whether or |
| // not what the renderer process says makes any sense. |
| auto* child_rfh = content::WebContents::FromRenderFrameHost(parent_rfh) |
| ->UnsafeFindFrameByFrameTreeNodeId(child_ftn_id); |
| should_shutdown_process = |
| child_rfh && (child_rfh->GetParent() != parent_rfh); |
| } |
| return !should_shutdown_process; |
| } |
| |
| using ProcessIdToFilterMap = |
| base::flat_map<int32_t, ExtensionsGuestViewMessageFilter*>; |
| ProcessIdToFilterMap* GetProcessIdToFilterMap() { |
| static base::NoDestructor<ProcessIdToFilterMap> instance; |
| return instance.get(); |
| } |
| |
| // Called on UI thread to remove the entry for a process. |
| void RemoveProcessIdFromGlobalMap(int32_t process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetProcessIdToFilterMap()->erase(process_id); |
| } |
| |
| } // namespace |
| |
| const uint32_t ExtensionsGuestViewMessageFilter::kFilteredMessageClasses[] = { |
| GuestViewMsgStart, ExtensionsGuestViewMsgStart}; |
| |
| // Helper class which navigates a given FrameTreeNode to "about:blank". This is |
| // used for scenarios where the plugin element's content frame has a different |
| // SiteInstance from its parent frame, or, the frame's origin is not |
| // "about:blank". Since this class triggers a navigation, all the document |
| // unload events will be dispatched and handled. During the lifetime of this |
| // helper class, all other navigations for the corresponding FrameTreeNode will |
| // be throttled and ignored. |
| class ExtensionsGuestViewMessageFilter::FrameNavigationHelper |
| : public content::WebContentsObserver { |
| public: |
| FrameNavigationHelper(RenderFrameHost* plugin_rfh, |
| int32_t guest_instance_id, |
| int32_t element_instance_id, |
| bool is_full_page_plugin, |
| ExtensionsGuestViewMessageFilter* filter); |
| ~FrameNavigationHelper() override; |
| |
| void FrameDeleted(RenderFrameHost* render_frame_host) override; |
| void DidFinishNavigation(NavigationHandle* handle) override; |
| // During attaching, we should ignore any navigation which is not a navigation |
| // to "about:blank" from the parent frame's SiteInstance. |
| bool ShouldCancelAndIgnore(NavigationHandle* handle); |
| |
| MimeHandlerViewGuest* GetGuestView() const; |
| |
| int32_t guest_instance_id() const { return guest_instance_id_; } |
| bool is_full_page_plugin() const { return is_full_page_plugin_; } |
| SiteInstance* parent_site_instance() const { |
| return parent_site_instance_.get(); |
| } |
| |
| private: |
| void NavigateToAboutBlank(); |
| void CancelPendingTask(); |
| |
| int32_t frame_tree_node_id_; |
| const int32_t guest_instance_id_; |
| const int32_t element_instance_id_; |
| const bool is_full_page_plugin_; |
| ExtensionsGuestViewMessageFilter* const filter_; |
| scoped_refptr<SiteInstance> parent_site_instance_; |
| |
| base::WeakPtrFactory<FrameNavigationHelper> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FrameNavigationHelper); |
| }; |
| |
| ExtensionsGuestViewMessageFilter::FrameNavigationHelper::FrameNavigationHelper( |
| RenderFrameHost* plugin_rfh, |
| int32_t guest_instance_id, |
| int32_t element_instance_id, |
| bool is_full_page_plugin, |
| ExtensionsGuestViewMessageFilter* filter) |
| : content::WebContentsObserver( |
| content::WebContents::FromRenderFrameHost(plugin_rfh)), |
| frame_tree_node_id_(plugin_rfh->GetFrameTreeNodeId()), |
| guest_instance_id_(guest_instance_id), |
| element_instance_id_(element_instance_id), |
| is_full_page_plugin_(is_full_page_plugin), |
| filter_(filter), |
| parent_site_instance_(plugin_rfh->GetParent()->GetSiteInstance()), |
| weak_factory_(this) { |
| DCHECK(GetGuestView()); |
| NavigateToAboutBlank(); |
| base::PostDelayedTaskWithTraits( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&ExtensionsGuestViewMessageFilter::FrameNavigationHelper:: |
| CancelPendingTask, |
| weak_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(kAttachFailureDelayMS)); |
| } |
| |
| ExtensionsGuestViewMessageFilter::FrameNavigationHelper:: |
| ~FrameNavigationHelper() {} |
| |
| void ExtensionsGuestViewMessageFilter::FrameNavigationHelper::FrameDeleted( |
| RenderFrameHost* render_frame_host) { |
| if (render_frame_host->GetFrameTreeNodeId() != frame_tree_node_id_) |
| return; |
| // It is possible that the plugin frame is deleted before a NavigationHandle |
| // is created; one such case is to immediately delete the plugin element right |
| // after MimeHandlerViewFrameContainer requests to create the |
| // MimeHandlerViewGuest on the browser side. |
| filter_->ResumeAttachOrDestroy(element_instance_id_, |
| MSG_ROUTING_NONE /* no plugin frame */); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::FrameNavigationHelper:: |
| DidFinishNavigation(NavigationHandle* handle) { |
| if (handle->GetFrameTreeNodeId() != frame_tree_node_id_) |
| return; |
| if (!handle->HasCommitted()) |
| return; |
| if (handle->GetRenderFrameHost()->GetSiteInstance() != parent_site_instance_) |
| return; |
| if (!handle->GetURL().IsAboutBlank()) |
| return; |
| if (!handle->GetRenderFrameHost()->PrepareForInnerWebContentsAttach()) { |
| filter_->ResumeAttachOrDestroy(element_instance_id_, |
| MSG_ROUTING_NONE /* no plugin frame */); |
| } |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&ExtensionsGuestViewMessageFilter::ResumeAttachOrDestroy, |
| filter_, element_instance_id_, |
| handle->GetRenderFrameHost()->GetRoutingID())); |
| } |
| |
| bool ExtensionsGuestViewMessageFilter::FrameNavigationHelper:: |
| ShouldCancelAndIgnore(NavigationHandle* handle) { |
| return handle->GetFrameTreeNodeId() == frame_tree_node_id_; |
| } |
| |
| void ExtensionsGuestViewMessageFilter::FrameNavigationHelper:: |
| NavigateToAboutBlank() { |
| // Immediately start a navigation to "about:blank". |
| GURL about_blank(url::kAboutBlankURL); |
| content::NavigationController::LoadURLParams params(about_blank); |
| params.frame_tree_node_id = frame_tree_node_id_; |
| // The goal is to have a plugin frame which is same-origin with parent, i.e., |
| // 'about:blank' and share the same SiteInstance. |
| params.source_site_instance = parent_site_instance_; |
| // The renderer (parent of the plugin frame) tries to load a MimeHandlerView |
| // and therefore this navigation should be treated as renderer initiated. |
| params.is_renderer_initiated = true; |
| web_contents()->GetController().LoadURLWithParams(params); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::FrameNavigationHelper:: |
| CancelPendingTask() { |
| filter_->ResumeAttachOrDestroy(element_instance_id_, |
| MSG_ROUTING_NONE /* no plugin frame */); |
| } |
| |
| MimeHandlerViewGuest* |
| ExtensionsGuestViewMessageFilter::FrameNavigationHelper::GetGuestView() const { |
| return MimeHandlerViewGuest::From( |
| parent_site_instance_->GetProcess()->GetID(), guest_instance_id_) |
| ->As<MimeHandlerViewGuest>(); |
| } |
| |
| // static |
| std::unique_ptr<NavigationThrottle> |
| ExtensionsGuestViewMessageFilter::MaybeCreateThrottle( |
| NavigationHandle* handle) { |
| DCHECK(content::MimeHandlerViewMode::UsesCrossProcessFrame()); |
| if (!handle->GetParentFrame()) { |
| // A plugin element cannot be the FrameOwner to a main frame. |
| return nullptr; |
| } |
| int32_t parent_process_id = handle->GetParentFrame()->GetProcess()->GetID(); |
| auto& map = *GetProcessIdToFilterMap(); |
| if (!base::ContainsKey(map, parent_process_id) || !map[parent_process_id]) { |
| // This happens if the RenderProcessHost has not been initialized yet. |
| return nullptr; |
| } |
| for (auto& pair : map[parent_process_id]->frame_navigation_helpers_) { |
| if (!pair.second->ShouldCancelAndIgnore(handle)) |
| continue; |
| // Any navigation of the corresponding FrameTreeNode which is not to |
| // "about:blank" or is not initiated by parent SiteInstance should be |
| // ignored. |
| return std::make_unique<CancelAndIgnoreNavigationForPluginFrameThrottle>( |
| handle); |
| } |
| return nullptr; |
| } |
| |
| ExtensionsGuestViewMessageFilter::ExtensionsGuestViewMessageFilter( |
| int render_process_id, |
| BrowserContext* context) |
| : GuestViewMessageFilter(kFilteredMessageClasses, |
| base::size(kFilteredMessageClasses), |
| render_process_id, |
| context), |
| content::BrowserAssociatedInterface<mojom::GuestView>(this, this) { |
| GetProcessIdToFilterMap()->insert_or_assign(render_process_id_, this); |
| } |
| |
| ExtensionsGuestViewMessageFilter::~ExtensionsGuestViewMessageFilter() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| // This map is created and accessed on the UI thread. Remove the reference to |
| // |this| here so that it will not be accessed again; but leave erasing the |
| // key from the global map to UI thread to avoid races when accessing the |
| // underlying data structure (https:/crbug.com/869791). |
| (*GetProcessIdToFilterMap())[render_process_id_] = nullptr; |
| base::PostTaskWithTraits( |
| FROM_HERE, BrowserThread::UI, |
| base::BindOnce(RemoveProcessIdFromGlobalMap, render_process_id_)); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::OverrideThreadForMessage( |
| const IPC::Message& message, |
| BrowserThread::ID* thread) { |
| switch (message.type()) { |
| case ExtensionsGuestViewHostMsg_ResizeGuest::ID: |
| *thread = BrowserThread::UI; |
| break; |
| default: |
| GuestViewMessageFilter::OverrideThreadForMessage(message, thread); |
| } |
| } |
| |
| bool ExtensionsGuestViewMessageFilter::OnMessageReceived( |
| const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(ExtensionsGuestViewMessageFilter, message) |
| IPC_MESSAGE_HANDLER(ExtensionsGuestViewHostMsg_CanExecuteContentScriptSync, |
| OnCanExecuteContentScript) |
| IPC_MESSAGE_HANDLER(ExtensionsGuestViewHostMsg_ResizeGuest, OnResizeGuest) |
| IPC_MESSAGE_UNHANDLED( |
| handled = GuestViewMessageFilter::OnMessageReceived(message)) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| GuestViewManager* ExtensionsGuestViewMessageFilter:: |
| GetOrCreateGuestViewManager() { |
| auto* manager = GuestViewManager::FromBrowserContext(browser_context_); |
| if (!manager) { |
| manager = GuestViewManager::CreateWithDelegate( |
| browser_context_, |
| ExtensionsAPIClient::Get()->CreateGuestViewManagerDelegate( |
| browser_context_)); |
| } |
| return manager; |
| } |
| |
| void ExtensionsGuestViewMessageFilter::OnCanExecuteContentScript( |
| int render_view_id, |
| int script_id, |
| bool* allowed) { |
| WebViewRendererState::WebViewInfo info; |
| WebViewRendererState::GetInstance()->GetInfo(render_process_id_, |
| render_view_id, &info); |
| |
| *allowed = |
| info.content_script_ids.find(script_id) != info.content_script_ids.end(); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::CreateMimeHandlerViewGuest( |
| int32_t render_frame_id, |
| const std::string& view_id, |
| int32_t element_instance_id, |
| const gfx::Size& element_size, |
| mime_handler::BeforeUnloadControlPtr before_unload_control, |
| int32_t plugin_frame_routing_id) { |
| base::PostTaskWithTraits( |
| FROM_HERE, {content::BrowserThread::UI}, |
| base::BindOnce(&ExtensionsGuestViewMessageFilter:: |
| CreateMimeHandlerViewGuestOnUIThread, |
| this, render_frame_id, view_id, element_instance_id, |
| element_size, before_unload_control.PassInterface(), |
| plugin_frame_routing_id, false)); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::CreateMimeHandlerViewGuestOnUIThread( |
| int render_frame_id, |
| const std::string& view_id, |
| int element_instance_id, |
| const gfx::Size& element_size, |
| mime_handler::BeforeUnloadControlPtrInfo before_unload_control, |
| int32_t plugin_frame_routing_id, |
| bool is_full_page_plugin) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| auto* manager = GetOrCreateGuestViewManager(); |
| |
| auto* rfh = RenderFrameHost::FromID(render_process_id_, render_frame_id); |
| auto* embedder_web_contents = WebContents::FromRenderFrameHost(rfh); |
| if (!embedder_web_contents) |
| return; |
| |
| if (!AreRoutingIDsConsistent(rfh, plugin_frame_routing_id)) { |
| bad_message::ReceivedBadMessage(rfh->GetProcess(), |
| bad_message::MHVG_INVALID_PLUGIN_FRAME_ID); |
| return; |
| } |
| |
| GuestViewManager::WebContentsCreatedCallback callback = base::BindOnce( |
| &ExtensionsGuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback, |
| this, element_instance_id, render_process_id_, render_frame_id, |
| plugin_frame_routing_id, element_size, std::move(before_unload_control), |
| is_full_page_plugin); |
| |
| base::DictionaryValue create_params; |
| create_params.SetString(mime_handler_view::kViewId, view_id); |
| create_params.SetInteger(guest_view::kElementWidth, element_size.width()); |
| create_params.SetInteger(guest_view::kElementHeight, element_size.height()); |
| manager->CreateGuest(MimeHandlerViewGuest::Type, embedder_web_contents, |
| create_params, std::move(callback)); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::OnResizeGuest( |
| int render_frame_id, |
| int element_instance_id, |
| const gfx::Size& new_size) { |
| // We should have a GuestViewManager at this point. If we don't then the |
| // embedder is misbehaving. |
| auto* manager = GetGuestViewManagerOrKill(); |
| if (!manager) |
| return; |
| |
| auto* guest_web_contents = |
| manager->GetGuestByInstanceID(render_process_id_, element_instance_id); |
| auto* mhvg = MimeHandlerViewGuest::FromWebContents(guest_web_contents); |
| if (!mhvg) |
| return; |
| |
| guest_view::SetSizeParams set_size_params; |
| set_size_params.enable_auto_size.reset(new bool(false)); |
| set_size_params.normal_size.reset(new gfx::Size(new_size)); |
| mhvg->SetSize(set_size_params); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::CreateEmbeddedMimeHandlerViewGuest( |
| int32_t render_frame_id, |
| int32_t tab_id, |
| const GURL& original_url, |
| int32_t element_instance_id, |
| const gfx::Size& element_size, |
| content::mojom::TransferrableURLLoaderPtr transferrable_url_loader, |
| int32_t plugin_frame_routing_id) { |
| if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { |
| base::PostTaskWithTraits( |
| FROM_HERE, {content::BrowserThread::UI}, |
| base::BindOnce(&ExtensionsGuestViewMessageFilter:: |
| CreateEmbeddedMimeHandlerViewGuest, |
| this, render_frame_id, tab_id, original_url, |
| element_instance_id, element_size, |
| base::Passed(&transferrable_url_loader), |
| plugin_frame_routing_id)); |
| return; |
| } |
| |
| content::WebContents* web_contents = |
| content::WebContents::FromRenderFrameHost( |
| RenderFrameHost::FromID(render_process_id_, render_frame_id)); |
| if (!web_contents) |
| return; |
| |
| auto* browser_context = web_contents->GetBrowserContext(); |
| std::string extension_id = transferrable_url_loader->url.host(); |
| const Extension* extension = ExtensionRegistry::Get(browser_context) |
| ->enabled_extensions() |
| .GetByID(extension_id); |
| if (!extension) |
| return; |
| |
| MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension); |
| if (!handler || !handler->HasPlugin()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| GURL handler_url(Extension::GetBaseURLFromExtensionId(extension_id).spec() + |
| handler->handler_url()); |
| |
| std::string view_id = base::GenerateGUID(); |
| std::unique_ptr<StreamContainer> stream_container(new StreamContainer( |
| nullptr, tab_id, true /* embedded */, handler_url, extension_id, |
| std::move(transferrable_url_loader), original_url)); |
| MimeHandlerStreamManager::Get(browser_context) |
| ->AddStream(view_id, std::move(stream_container), |
| -1 /* frame_tree_node_id*/, render_process_id_, |
| render_frame_id); |
| |
| CreateMimeHandlerViewGuestOnUIThread(render_frame_id, view_id, |
| element_instance_id, element_size, |
| nullptr, plugin_frame_routing_id, false); |
| } |
| |
| void ExtensionsGuestViewMessageFilter::MimeHandlerViewGuestCreatedCallback( |
| int element_instance_id, |
| int embedder_render_process_id, |
| int embedder_render_frame_id, |
| int32_t plugin_frame_routing_id, |
| const gfx::Size& element_size, |
| mime_handler::BeforeUnloadControlPtrInfo before_unload_control, |
| bool is_full_page_plugin, |
| WebContents* web_contents) { |
| auto* guest_view = MimeHandlerViewGuest::FromWebContents(web_contents); |
| if (!guest_view) |
| return; |
| |
| guest_view->SetBeforeUnloadController(std::move(before_unload_control)); |
| int guest_instance_id = guest_view->guest_instance_id(); |
| auto* rfh = RenderFrameHost::FromID(embedder_render_process_id, |
| embedder_render_frame_id); |
| if (!rfh) |
| return; |
| |
| guest_view->SetEmbedderFrame(embedder_render_process_id, |
| embedder_render_frame_id); |
| |
| base::DictionaryValue attach_params; |
| attach_params.SetInteger(guest_view::kElementWidth, element_size.width()); |
| attach_params.SetInteger(guest_view::kElementHeight, element_size.height()); |
| auto* manager = GuestViewManager::FromBrowserContext(browser_context_); |
| if (!manager) { |
| guest_view::bad_message::ReceivedBadMessage( |
| this, |
| guest_view::bad_message::GVMF_UNEXPECTED_MESSAGE_BEFORE_GVM_CREATION); |
| guest_view->Destroy(true); |
| return; |
| } |
| manager->AttachGuest(embedder_render_process_id, element_instance_id, |
| guest_instance_id, attach_params); |
| |
| if (!content::MimeHandlerViewMode::UsesCrossProcessFrame()) { |
| rfh->Send(new ExtensionsGuestViewMsg_CreateMimeHandlerViewGuestACK( |
| element_instance_id)); |
| return; |
| } |
| auto* plugin_rfh = RenderFrameHost::FromID(embedder_render_process_id, |
| plugin_frame_routing_id); |
| if (!plugin_rfh) { |
| // The plugin element has a proxy instead. |
| plugin_rfh = RenderFrameHost::FromPlaceholderId(embedder_render_process_id, |
| plugin_frame_routing_id); |
| } |
| if (!plugin_rfh) { |
| // This should only happen if the original plugin frame was cross-process |
| // and a concurrent navigation in its process won the race and ended up |
| // destroying the proxy whose routing ID was sent here by the |
| // MimeHandlerViewFrameContainer. We should ask the embedder to retry |
| // creating the guest. |
| guest_view->GetEmbedderFrame()->Send( |
| new ExtensionsGuestViewMsg_RetryCreatingMimeHandlerViewGuest( |
| element_instance_id)); |
| guest_view->Destroy(true); |
| return; |
| } |
| |
| if (guest_view->web_contents()->CanAttachToOuterContentsFrame(plugin_rfh)) { |
| guest_view->AttachToOuterWebContentsFrame(plugin_rfh, element_instance_id, |
| is_full_page_plugin); |
| |
| } else { |
| // TODO(ekaramad): Replace this navigation logic with an asynchronous |
| // attach API in content layer (https://crbug.com/911161). |
| // The current API for attaching guests requires the frame in outer |
| // WebContents to be same-origin with parent. The current frame could also |
| // have beforeunload handlers. Considering these issues, we should first |
| // navigate the frame to "about:blank" and put it in the same SiteInstance |
| // as parent before using it for attach API. |
| frame_navigation_helpers_[element_instance_id] = |
| std::make_unique<FrameNavigationHelper>( |
| plugin_rfh, guest_view->guest_instance_id(), element_instance_id, |
| is_full_page_plugin, this); |
| } |
| } |
| |
| void ExtensionsGuestViewMessageFilter::ResumeAttachOrDestroy( |
| int32_t element_instance_id, |
| int32_t plugin_frame_routing_id) { |
| auto it = frame_navigation_helpers_.find(element_instance_id); |
| if (it == frame_navigation_helpers_.end()) { |
| // This is the timeout callback. The guest is either attached or destroyed. |
| return; |
| } |
| auto* plugin_rfh = content::RenderFrameHost::FromID(render_process_id_, |
| plugin_frame_routing_id); |
| auto* helper = it->second.get(); |
| auto* guest_view = helper->GetGuestView(); |
| if (!guest_view) |
| return; |
| |
| if (plugin_rfh) { |
| DCHECK( |
| guest_view->web_contents()->CanAttachToOuterContentsFrame(plugin_rfh)); |
| guest_view->AttachToOuterWebContentsFrame(plugin_rfh, element_instance_id, |
| helper->is_full_page_plugin()); |
| } else { |
| guest_view->GetEmbedderFrame()->Send( |
| new ExtensionsGuestViewMsg_DestroyFrameContainer(element_instance_id)); |
| guest_view->Destroy(true); |
| } |
| frame_navigation_helpers_.erase(element_instance_id); |
| } |
| |
| } // namespace extensions |