diff options
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/browser/extension_web_contents_observer.cc | 111 | ||||
-rw-r--r-- | extensions/browser/extension_web_contents_observer.h | 30 | ||||
-rw-r--r-- | extensions/browser/guest_view/extension_options/extension_options_guest.cc | 5 | ||||
-rw-r--r-- | extensions/browser/process_manager.cc | 31 | ||||
-rw-r--r-- | extensions/browser/process_manager.h | 3 | ||||
-rw-r--r-- | extensions/browser/process_manager_observer.h | 8 | ||||
-rw-r--r-- | extensions/common/view_type.h | 1 | ||||
-rw-r--r-- | extensions/renderer/extension_frame_helper.cc | 19 |
8 files changed, 172 insertions, 36 deletions
diff --git a/extensions/browser/extension_web_contents_observer.cc b/extensions/browser/extension_web_contents_observer.cc index 665fc08..ea1104a 100644 --- a/extensions/browser/extension_web_contents_observer.cc +++ b/extensions/browser/extension_web_contents_observer.cc @@ -5,6 +5,7 @@ #include "extensions/browser/extension_web_contents_observer.h" #include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/navigation_details.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" @@ -50,18 +51,25 @@ void ExtensionWebContentsObserver::InitializeRenderFrame( DCHECK(render_frame_host); DCHECK(render_frame_host->IsRenderFrameLive()); + // At the initialization of the render frame, the last committed URL is not + // reliable, so do not take it into account in determining whether it is an + // extension frame. + const Extension* frame_extension = + GetExtensionFromFrame(render_frame_host, false); + // This observer is attached to every WebContents, so we are also notified of + // frames that are not in an extension process. + if (!frame_extension) + return; + // Notify the render frame of the view type. render_frame_host->Send(new ExtensionMsg_NotifyRenderViewType( render_frame_host->GetRoutingID(), GetViewType(web_contents()))); - const Extension* frame_extension = GetExtensionFromFrame(render_frame_host); - if (frame_extension) { - ExtensionsBrowserClient::Get()->RegisterMojoServices(render_frame_host, - frame_extension); - ProcessManager::Get(browser_context_) - ->RegisterRenderFrameHost(web_contents(), render_frame_host, - frame_extension); - } + ExtensionsBrowserClient::Get()->RegisterMojoServices(render_frame_host, + frame_extension); + ProcessManager::Get(browser_context_) + ->RegisterRenderFrameHost(web_contents(), render_frame_host, + frame_extension); } content::WebContents* ExtensionWebContentsObserver::GetAssociatedWebContents() @@ -110,6 +118,49 @@ void ExtensionWebContentsObserver::RenderFrameDeleted( ->UnregisterRenderFrameHost(render_frame_host); } +void ExtensionWebContentsObserver::DidCommitProvisionalLoadForFrame( + content::RenderFrameHost* render_frame_host, + const GURL& url, + ui::PageTransition transition_type) { + ProcessManager* pm = ProcessManager::Get(browser_context_); + + if (pm->IsRenderFrameHostRegistered(render_frame_host)) { + const Extension* frame_extension = + GetExtensionFromFrame(render_frame_host, true); + + if (!frame_extension) + pm->UnregisterRenderFrameHost(render_frame_host); + } +} + +void ExtensionWebContentsObserver::DidNavigateAnyFrame( + content::RenderFrameHost* render_frame_host, + const content::LoadCommittedDetails& details, + const content::FrameNavigateParams& params) { + if (details.is_in_page) + return; + + const Extension* frame_extension = + GetExtensionFromFrame(render_frame_host, true); + ProcessManager* pm = ProcessManager::Get(browser_context_); + + if (!frame_extension) { + // Should have been unregistered by DidCommitProvisionalLoadForFrame. + DCHECK(!pm->IsRenderFrameHostRegistered(render_frame_host)); + return; + } + + if (pm->IsRenderFrameHostRegistered(render_frame_host)) { + // Notify ProcessManager, because some clients do not just want to know + // whether the frame is in an extension process, but also whether the frame + // was navigated. + pm->DidNavigateRenderFrameHost(render_frame_host); + } else { + pm->RegisterRenderFrameHost(web_contents(), render_frame_host, + frame_extension); + } +} + bool ExtensionWebContentsObserver::OnMessageReceived( const IPC::Message& message, content::RenderFrameHost* render_frame_host) { @@ -146,24 +197,38 @@ void ExtensionWebContentsObserver::PepperInstanceDeleted() { std::string ExtensionWebContentsObserver::GetExtensionIdFromFrame( content::RenderFrameHost* render_frame_host) const { - content::SiteInstance* site_instance = render_frame_host->GetSiteInstance(); - GURL url = render_frame_host->GetLastCommittedURL(); - if (!url.is_empty()) { - if (site_instance->GetSiteURL().GetOrigin() != url.GetOrigin()) - return std::string(); - } else { - url = site_instance->GetSiteURL(); - } - - return url.SchemeIs(kExtensionScheme) ? url.host() : std::string(); + const Extension* extension = GetExtensionFromFrame(render_frame_host, true); + return extension ? extension->id() : std::string(); } const Extension* ExtensionWebContentsObserver::GetExtensionFromFrame( - content::RenderFrameHost* render_frame_host) const { - return ExtensionRegistry::Get( - render_frame_host->GetProcess()->GetBrowserContext()) - ->enabled_extensions() - .GetByID(GetExtensionIdFromFrame(render_frame_host)); + content::RenderFrameHost* render_frame_host, + bool verify_url) const { + const GURL site_url(render_frame_host->GetSiteInstance()->GetSiteURL()); + if (!site_url.SchemeIs(kExtensionScheme)) + return nullptr; + + const std::string& extension_id = site_url.host(); + content::BrowserContext* browser_context = + render_frame_host->GetProcess()->GetBrowserContext(); + const Extension* extension = ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetByID(extension_id); + if (!extension) + return nullptr; + + if (verify_url) { + const url::Origin& origin(render_frame_host->GetLastCommittedOrigin()); + // Without site isolation, this check is needed to eliminate non-extension + // schemes. With site isolation, this is still needed to exclude sandboxed + // extension frames with a unique origin. + if (origin.unique() || + site_url != content::SiteInstance::GetSiteForURL( + browser_context, GURL(origin.Serialize()))) + return nullptr; + } + + return extension; } const Extension* ExtensionWebContentsObserver::GetExtension( diff --git a/extensions/browser/extension_web_contents_observer.h b/extensions/browser/extension_web_contents_observer.h index c92ad9c..5b86dc0 100644 --- a/extensions/browser/extension_web_contents_observer.h +++ b/extensions/browser/extension_web_contents_observer.h @@ -29,6 +29,23 @@ class Extension; // WebContents. It must be a subclass so that creating an instance via // content::WebContentsUserData::CreateForWebContents() provides an object of // the correct type. For an example, see ChromeExtensionWebContentsObserver. +// +// This class is responsible for maintaining the registrations of extension +// frames with the ProcessManager. Only frames in an extension process are +// registered. If out-of-process frames are enabled, every frame hosts a +// chrome-extension: page. Otherwise non-extension frames may erroneously be +// registered, but only briefly until they are correctly classified. This is +// achieved using the following notifications: +// 1. RenderFrameCreated - registers all new frames in extension processes. +// 2. DidCommitProvisionalLoadForFrame - unregisters non-extension frames. +// 3. DidNavigateAnyFrame - registers extension frames if they had been +// unregistered. +// +// Without OOPIF, non-extension frames created by the Chrome extension are also +// registered at RenderFrameCreated. When the non-extension page is committed, +// we detect that the unexpected URL and unregister the frame. +// With OOPIF only the first notification is sufficient in most cases, except +// for sandboxed frames with a unique origin. class ExtensionWebContentsObserver : public content::WebContentsObserver, public ExtensionFunctionDispatcher::Delegate { @@ -60,6 +77,13 @@ class ExtensionWebContentsObserver void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; + void DidCommitProvisionalLoadForFrame( + content::RenderFrameHost* render_frame_host, + const GURL& url, + ui::PageTransition transition_type) override; + void DidNavigateAnyFrame(content::RenderFrameHost* render_frame_host, + const content::LoadCommittedDetails& details, + const content::FrameNavigateParams& params) override; // Subclasses should call this first before doing their own message handling. bool OnMessageReceived(const IPC::Message& message, @@ -77,8 +101,12 @@ class ExtensionWebContentsObserver // Returns the extension associated with the given |render_frame_host|, or // null if there is none. + // If |verify_url| is false, only the SiteInstance is taken into account. + // If |verify_url| is true, the frame's last committed URL is also used to + // improve the classification of the frame. const Extension* GetExtensionFromFrame( - content::RenderFrameHost* render_frame_host) const; + content::RenderFrameHost* render_frame_host, + bool verify_url) const; // TODO(devlin): Remove these once callers are updated to use the FromFrame // equivalents. diff --git a/extensions/browser/guest_view/extension_options/extension_options_guest.cc b/extensions/browser/guest_view/extension_options/extension_options_guest.cc index 6a34aa0..5806540 100644 --- a/extensions/browser/guest_view/extension_options/extension_options_guest.cc +++ b/extensions/browser/guest_view/extension_options/extension_options_guest.cc @@ -19,6 +19,7 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/guest_view/extension_options/extension_options_constants.h" #include "extensions/browser/guest_view/extension_options/extension_options_guest_delegate.h" +#include "extensions/browser/view_type_utils.h" #include "extensions/common/api/extension_options_internal.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" @@ -108,7 +109,9 @@ void ExtensionOptionsGuest::CreateWebContents( content::SiteInstance::CreateForURL(browser_context(), extension_url); WebContents::CreateParams params(browser_context(), options_site_instance); params.guest_delegate = this; - callback.Run(WebContents::Create(params)); + WebContents* wc = WebContents::Create(params); + SetViewType(wc, VIEW_TYPE_EXTENSION_GUEST); + callback.Run(wc); } void ExtensionOptionsGuest::DidInitialize( diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc index 8b2b4b5..581219b 100644 --- a/extensions/browser/process_manager.cc +++ b/extensions/browser/process_manager.cc @@ -166,6 +166,7 @@ struct ProcessManager::ExtensionRenderFrameData { case VIEW_TYPE_APP_WINDOW: case VIEW_TYPE_BACKGROUND_CONTENTS: case VIEW_TYPE_EXTENSION_DIALOG: + case VIEW_TYPE_EXTENSION_GUEST: case VIEW_TYPE_EXTENSION_POPUP: case VIEW_TYPE_LAUNCHER_PAGE: case VIEW_TYPE_PANEL: @@ -309,6 +310,21 @@ void ProcessManager::UnregisterRenderFrameHost( } } +void ProcessManager::DidNavigateRenderFrameHost( + content::RenderFrameHost* render_frame_host) { + ExtensionRenderFrames::iterator frame = + all_extension_frames_.find(render_frame_host); + + if (frame != all_extension_frames_.end()) { + std::string extension_id = GetExtensionID(render_frame_host); + + FOR_EACH_OBSERVER(ProcessManagerObserver, + observer_list_, + OnExtensionFrameNavigated(extension_id, + render_frame_host)); + } +} + scoped_refptr<content::SiteInstance> ProcessManager::GetSiteInstanceForURL( const GURL& url) { return make_scoped_refptr(site_instance_->GetRelatedSiteInstance(url)); @@ -324,20 +340,19 @@ const ProcessManager::FrameSet ProcessManager::GetAllFrames() const { ProcessManager::FrameSet ProcessManager::GetRenderFrameHostsForExtension( const std::string& extension_id) { FrameSet result; - scoped_refptr<content::SiteInstance> site_instance(GetSiteInstanceForURL( - Extension::GetBaseURLFromExtensionId(extension_id))); - if (!site_instance.get()) - return result; - - // Gather up all the frames for that site. for (const auto& key_value : all_extension_frames_) { - if (key_value.first->GetSiteInstance() == site_instance) + if (GetExtensionID(key_value.first) == extension_id) result.insert(key_value.first); } - return result; } +bool ProcessManager::IsRenderFrameHostRegistered( + content::RenderFrameHost* render_frame_host) { + return all_extension_frames_.find(render_frame_host) != + all_extension_frames_.end(); +} + void ProcessManager::AddObserver(ProcessManagerObserver* observer) { observer_list_.AddObserver(observer); } diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h index 9d44453..c93bd80 100644 --- a/extensions/browser/process_manager.h +++ b/extensions/browser/process_manager.h @@ -56,6 +56,7 @@ class ProcessManager : public KeyedService, content::RenderFrameHost* render_frame_host, const Extension* extension); void UnregisterRenderFrameHost(content::RenderFrameHost* render_frame_host); + void DidNavigateRenderFrameHost(content::RenderFrameHost* render_frame_host); // Returns the SiteInstance that the given URL belongs to. // TODO(aa): This only returns correct results for extensions and packaged @@ -71,6 +72,8 @@ class ProcessManager : public KeyedService, ProcessManager::FrameSet GetRenderFrameHostsForExtension( const std::string& extension_id); + bool IsRenderFrameHostRegistered(content::RenderFrameHost* render_frame_host); + void AddObserver(ProcessManagerObserver* observer); void RemoveObserver(ProcessManagerObserver* observer); diff --git a/extensions/browser/process_manager_observer.h b/extensions/browser/process_manager_observer.h index 668b3df..47883ef 100644 --- a/extensions/browser/process_manager_observer.h +++ b/extensions/browser/process_manager_observer.h @@ -30,13 +30,21 @@ class ProcessManagerObserver { // Called immediately after the extension background host is destroyed. virtual void OnBackgroundHostClose(const std::string& extension_id) {} + // Called when a RenderFrameHost has been registered in an extension process. virtual void OnExtensionFrameRegistered( const std::string& extension_id, content::RenderFrameHost* render_frame_host) {} + // Called when a RenderFrameHost is no longer part of an extension process. virtual void OnExtensionFrameUnregistered( const std::string& extension_id, content::RenderFrameHost* render_frame_host) {} + + // Called when a RenderFrameHost was navigated to another page within the + // extension process. + virtual void OnExtensionFrameNavigated( + const std::string& extension_id, + content::RenderFrameHost* render_frame_host) {} }; } // namespace extensions diff --git a/extensions/common/view_type.h b/extensions/common/view_type.h index 82c906b..bbe498d 100644 --- a/extensions/common/view_type.h +++ b/extensions/common/view_type.h @@ -18,6 +18,7 @@ enum ViewType { VIEW_TYPE_BACKGROUND_CONTENTS, VIEW_TYPE_EXTENSION_BACKGROUND_PAGE, VIEW_TYPE_EXTENSION_DIALOG, + VIEW_TYPE_EXTENSION_GUEST, VIEW_TYPE_EXTENSION_POPUP, VIEW_TYPE_LAUNCHER_PAGE, VIEW_TYPE_PANEL, diff --git a/extensions/renderer/extension_frame_helper.cc b/extensions/renderer/extension_frame_helper.cc index fe581c3..c063f84a 100644 --- a/extensions/renderer/extension_frame_helper.cc +++ b/extensions/renderer/extension_frame_helper.cc @@ -4,6 +4,7 @@ #include "extensions/renderer/extension_frame_helper.h" +#include "base/strings/string_util.h" #include "content/public/renderer/render_frame.h" #include "extensions/common/api/messaging/message.h" #include "extensions/common/constants.h" @@ -14,6 +15,7 @@ #include "extensions/renderer/dispatcher.h" #include "extensions/renderer/messaging_bindings.h" #include "extensions/renderer/script_context.h" +#include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebConsoleMessage.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" @@ -34,11 +36,22 @@ bool RenderFrameMatches(const ExtensionFrameHelper* frame_helper, if (match_view_type != VIEW_TYPE_INVALID && frame_helper->view_type() != match_view_type) return false; - GURL url = frame_helper->render_frame()->GetWebFrame()->document().url(); - if (!url.SchemeIs(kExtensionScheme)) + + // Not all frames have a valid ViewType, e.g. devtools, most GuestViews, and + // unclassified detached WebContents. + if (frame_helper->view_type() == VIEW_TYPE_INVALID) return false; - if (url.host() != match_extension_id) + + // This logic matches ExtensionWebContentsObserver::GetExtensionFromFrame. + blink::WebSecurityOrigin origin = + frame_helper->render_frame()->GetWebFrame()->securityOrigin(); + if (origin.isUnique() || + !base::EqualsASCII(base::StringPiece16(origin.protocol()), + kExtensionScheme) || + !base::EqualsASCII(base::StringPiece16(origin.host()), + match_extension_id.c_str())) return false; + if (match_window_id != extension_misc::kUnknownWindowId && frame_helper->browser_window_id() != match_window_id) return false; |