summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'extensions')
-rw-r--r--extensions/browser/extension_web_contents_observer.cc111
-rw-r--r--extensions/browser/extension_web_contents_observer.h30
-rw-r--r--extensions/browser/guest_view/extension_options/extension_options_guest.cc5
-rw-r--r--extensions/browser/process_manager.cc31
-rw-r--r--extensions/browser/process_manager.h3
-rw-r--r--extensions/browser/process_manager_observer.h8
-rw-r--r--extensions/common/view_type.h1
-rw-r--r--extensions/renderer/extension_frame_helper.cc19
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;