| Index: chrome/renderer/extensions/chrome_extensions_renderer_client.cc
|
| diff --git a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
|
| index 15278e78cb9137358bc8bace77060190de2ef057..f0ffe3720a84ab16515cafc920cdc35ad5699c5c 100644
|
| --- a/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
|
| +++ b/chrome/renderer/extensions/chrome_extensions_renderer_client.cc
|
| @@ -4,9 +4,113 @@
|
|
|
| #include "chrome/renderer/extensions/chrome_extensions_renderer_client.h"
|
|
|
| +#include "base/command_line.h"
|
| #include "base/lazy_instance.h"
|
| #include "chrome/common/chrome_isolated_world_ids.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +#include "chrome/common/extensions/extension_constants.h"
|
| +#include "chrome/common/extensions/extension_metrics.h"
|
| +#include "chrome/common/extensions/extension_process_policy.h"
|
| +#include "chrome/common/url_constants.h"
|
| #include "chrome/renderer/chrome_render_process_observer.h"
|
| +#include "chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.h"
|
| +#include "chrome/renderer/extensions/renderer_permissions_policy_delegate.h"
|
| +#include "chrome/renderer/extensions/resource_request_policy.h"
|
| +#include "chrome/renderer/media/cast_ipc_dispatcher.h"
|
| +#include "content/public/common/content_constants.h"
|
| +#include "content/public/common/content_switches.h"
|
| +#include "content/public/renderer/render_thread.h"
|
| +#include "extensions/common/constants.h"
|
| +#include "extensions/common/extension.h"
|
| +#include "extensions/common/extension_set.h"
|
| +#include "extensions/common/switches.h"
|
| +#include "extensions/renderer/dispatcher.h"
|
| +#include "extensions/renderer/extension_frame_helper.h"
|
| +#include "extensions/renderer/extension_helper.h"
|
| +#include "extensions/renderer/extensions_render_frame_observer.h"
|
| +#include "extensions/renderer/guest_view/extensions_guest_view_container.h"
|
| +#include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h"
|
| +#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h"
|
| +#include "extensions/renderer/script_context.h"
|
| +#include "third_party/WebKit/public/platform/WebURL.h"
|
| +#include "third_party/WebKit/public/web/WebDocument.h"
|
| +#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
| +#include "third_party/WebKit/public/web/WebPluginParams.h"
|
| +
|
| +using extensions::Extension;
|
| +
|
| +namespace {
|
| +
|
| +bool IsStandaloneExtensionProcess() {
|
| + return base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + extensions::switches::kExtensionProcess);
|
| +}
|
| +
|
| +void IsGuestViewApiAvailableToScriptContext(
|
| + bool* api_is_available,
|
| + extensions::ScriptContext* context) {
|
| + if (context->GetAvailability("guestViewInternal").is_available()) {
|
| + *api_is_available = true;
|
| + }
|
| +}
|
| +
|
| +// Returns true if the frame is navigating to an URL either into or out of an
|
| +// extension app's extent.
|
| +bool CrossesExtensionExtents(blink::WebLocalFrame* frame,
|
| + const GURL& new_url,
|
| + bool is_extension_url,
|
| + bool is_initial_navigation) {
|
| + DCHECK(!frame->parent());
|
| + GURL old_url(frame->document().url());
|
| +
|
| + extensions::RendererExtensionRegistry* extension_registry =
|
| + extensions::RendererExtensionRegistry::Get();
|
| +
|
| + // If old_url is still empty and this is an initial navigation, then this is
|
| + // a window.open operation. We should look at the opener URL. Note that the
|
| + // opener is a local frame in this case.
|
| + if (is_initial_navigation && old_url.is_empty() && frame->opener()) {
|
| + blink::WebLocalFrame* opener_frame = frame->opener()->toWebLocalFrame();
|
| +
|
| + // If we're about to open a normal web page from a same-origin opener stuck
|
| + // in an extension process, we want to keep it in process to allow the
|
| + // opener to script it.
|
| + blink::WebDocument opener_document = opener_frame->document();
|
| + blink::WebSecurityOrigin opener_origin = opener_document.securityOrigin();
|
| + bool opener_is_extension_url = !opener_origin.isUnique() &&
|
| + extension_registry->GetExtensionOrAppByURL(
|
| + opener_document.url()) != nullptr;
|
| + if (!is_extension_url && !opener_is_extension_url &&
|
| + IsStandaloneExtensionProcess() &&
|
| + opener_origin.canRequest(blink::WebURL(new_url)))
|
| + return false;
|
| +
|
| + // In all other cases, we want to compare against the URL that determines
|
| + // the type of process. In default Chrome, that's the URL of the opener's
|
| + // top frame and not the opener frame itself. In --site-per-process, we
|
| + // can use the opener frame itself.
|
| + // TODO(nick): Either wire this up to SiteIsolationPolicy, or to state on
|
| + // |opener_frame|/its ancestors.
|
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kSitePerProcess) ||
|
| + base::CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kIsolateExtensions))
|
| + old_url = opener_frame->document().url();
|
| + else
|
| + old_url = opener_frame->top()->document().url();
|
| + }
|
| +
|
| + // Only consider keeping non-app URLs in an app process if this window
|
| + // has an opener (in which case it might be an OAuth popup that tries to
|
| + // script an iframe within the app).
|
| + bool should_consider_workaround = !!frame->opener();
|
| +
|
| + return extensions::CrossesExtensionProcessBoundary(
|
| + *extension_registry->GetMainThreadExtensionSet(), old_url, new_url,
|
| + should_consider_workaround);
|
| +}
|
| +
|
| +} // namespace
|
|
|
| ChromeExtensionsRendererClient::ChromeExtensionsRendererClient() {}
|
|
|
| @@ -26,3 +130,176 @@ bool ChromeExtensionsRendererClient::IsIncognitoProcess() const {
|
| int ChromeExtensionsRendererClient::GetLowestIsolatedWorldId() const {
|
| return chrome::ISOLATED_WORLD_ID_EXTENSIONS;
|
| }
|
| +
|
| +void ChromeExtensionsRendererClient::RenderThreadStarted() {
|
| + content::RenderThread* thread = content::RenderThread::Get();
|
| + extension_dispatcher_delegate_.reset(
|
| + new ChromeExtensionsDispatcherDelegate());
|
| + // ChromeRenderViewTest::SetUp() creates its own ExtensionDispatcher and
|
| + // injects it using SetExtensionDispatcher(). Don't overwrite it.
|
| + if (!extension_dispatcher_) {
|
| + extension_dispatcher_.reset(
|
| + new extensions::Dispatcher(extension_dispatcher_delegate_.get()));
|
| + }
|
| + permissions_policy_delegate_.reset(
|
| + new extensions::RendererPermissionsPolicyDelegate(
|
| + extension_dispatcher_.get()));
|
| + resource_request_policy_.reset(
|
| + new extensions::ResourceRequestPolicy(extension_dispatcher_.get()));
|
| + guest_view_container_dispatcher_.reset(
|
| + new extensions::ExtensionsGuestViewContainerDispatcher());
|
| +
|
| + thread->AddObserver(extension_dispatcher_.get());
|
| + thread->AddObserver(guest_view_container_dispatcher_.get());
|
| + thread->AddFilter(new CastIPCDispatcher(thread->GetIOMessageLoopProxy()));
|
| +}
|
| +
|
| +void ChromeExtensionsRendererClient::RenderFrameCreated(
|
| + content::RenderFrame* render_frame) {
|
| + new extensions::ExtensionsRenderFrameObserver(render_frame);
|
| + new extensions::ExtensionFrameHelper(render_frame,
|
| + extension_dispatcher_.get());
|
| + extension_dispatcher_->OnRenderFrameCreated(render_frame);
|
| +}
|
| +
|
| +void ChromeExtensionsRendererClient::RenderViewCreated(
|
| + content::RenderView* render_view) {
|
| + new extensions::ExtensionHelper(render_view, extension_dispatcher_.get());
|
| +}
|
| +
|
| +bool ChromeExtensionsRendererClient::OverrideCreatePlugin(
|
| + content::RenderFrame* render_frame,
|
| + const blink::WebPluginParams& params) {
|
| + if (params.mimeType.utf8() != content::kBrowserPluginMimeType)
|
| + return true;
|
| +
|
| + bool guest_view_api_available = false;
|
| + extension_dispatcher_->script_context_set().ForEach(
|
| + render_frame, base::Bind(&IsGuestViewApiAvailableToScriptContext,
|
| + &guest_view_api_available));
|
| + return !guest_view_api_available;
|
| +}
|
| +
|
| +bool ChromeExtensionsRendererClient::AllowPopup() {
|
| + extensions::ScriptContext* current_context =
|
| + extension_dispatcher_->script_context_set().GetCurrent();
|
| + if (!current_context || !current_context->extension())
|
| + return false;
|
| +
|
| + // See http://crbug.com/117446 for the subtlety of this check.
|
| + switch (current_context->context_type()) {
|
| + case extensions::Feature::UNSPECIFIED_CONTEXT:
|
| + case extensions::Feature::WEB_PAGE_CONTEXT:
|
| + case extensions::Feature::UNBLESSED_EXTENSION_CONTEXT:
|
| + case extensions::Feature::WEBUI_CONTEXT:
|
| + case extensions::Feature::SERVICE_WORKER_CONTEXT:
|
| + return false;
|
| + case extensions::Feature::BLESSED_EXTENSION_CONTEXT:
|
| + case extensions::Feature::CONTENT_SCRIPT_CONTEXT:
|
| + return true;
|
| + case extensions::Feature::BLESSED_WEB_PAGE_CONTEXT:
|
| + return !current_context->web_frame()->parent();
|
| + default:
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +bool ChromeExtensionsRendererClient::WillSendRequest(
|
| + blink::WebFrame* frame,
|
| + ui::PageTransition transition_type,
|
| + const GURL& url,
|
| + GURL* new_url) {
|
| + if (url.SchemeIs(extensions::kExtensionScheme) &&
|
| + !resource_request_policy_->CanRequestResource(url, frame,
|
| + transition_type)) {
|
| + *new_url = GURL(chrome::kExtensionInvalidRequestURL);
|
| + return true;
|
| + }
|
| +
|
| + if (url.SchemeIs(extensions::kExtensionResourceScheme) &&
|
| + !resource_request_policy_->CanRequestExtensionResourceScheme(url,
|
| + frame)) {
|
| + *new_url = GURL(chrome::kExtensionResourceInvalidRequestURL);
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void ChromeExtensionsRendererClient::SetExtensionDispatcherForTest(
|
| + scoped_ptr<extensions::Dispatcher> extension_dispatcher) {
|
| + extension_dispatcher_ = extension_dispatcher.Pass();
|
| + permissions_policy_delegate_.reset(
|
| + new extensions::RendererPermissionsPolicyDelegate(
|
| + extension_dispatcher_.get()));
|
| + content::RenderThread::Get()->RegisterExtension(
|
| + extensions::SafeBuiltins::CreateV8Extension());
|
| +}
|
| +
|
| +extensions::Dispatcher*
|
| +ChromeExtensionsRendererClient::GetExtensionDispatcherForTest() {
|
| + return extension_dispatcher();
|
| +}
|
| +
|
| +// static
|
| +bool ChromeExtensionsRendererClient::ShouldFork(blink::WebLocalFrame* frame,
|
| + const GURL& url,
|
| + bool is_initial_navigation,
|
| + bool is_server_redirect,
|
| + bool* send_referrer) {
|
| + const extensions::RendererExtensionRegistry* extension_registry =
|
| + extensions::RendererExtensionRegistry::Get();
|
| +
|
| + // Determine if the new URL is an extension (excluding bookmark apps).
|
| + const Extension* new_url_extension = extensions::GetNonBookmarkAppExtension(
|
| + *extension_registry->GetMainThreadExtensionSet(), url);
|
| + bool is_extension_url = !!new_url_extension;
|
| +
|
| + // If the navigation would cross an app extent boundary, we also need
|
| + // to defer to the browser to ensure process isolation. This is not necessary
|
| + // for server redirects, which will be transferred to a new process by the
|
| + // browser process when they are ready to commit. It is necessary for client
|
| + // redirects, which won't be transferred in the same way.
|
| + if (!is_server_redirect &&
|
| + CrossesExtensionExtents(frame, url, is_extension_url,
|
| + is_initial_navigation)) {
|
| + // Include the referrer in this case since we're going from a hosted web
|
| + // page. (the packaged case is handled previously by the extension
|
| + // navigation test)
|
| + *send_referrer = true;
|
| +
|
| + const Extension* extension =
|
| + extension_registry->GetExtensionOrAppByURL(url);
|
| + if (extension && extension->is_app()) {
|
| + extensions::RecordAppLaunchType(
|
| + extension_misc::APP_LAUNCH_CONTENT_NAVIGATION, extension->GetType());
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + // If this is a reload, check whether it has the wrong process type. We
|
| + // should send it to the browser if it's an extension URL (e.g., hosted app)
|
| + // in a normal process, or if it's a process for an extension that has been
|
| + // uninstalled. Without --site-per-process mode, we never fork processes for
|
| + // subframes, so this check only makes sense for top-level frames.
|
| + // TODO(alexmos,nasko): Figure out how this check should work when reloading
|
| + // subframes in --site-per-process mode.
|
| + if (!frame->parent() && frame->document().url() == url) {
|
| + if (is_extension_url != IsStandaloneExtensionProcess())
|
| + return true;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| +// static
|
| +content::BrowserPluginDelegate*
|
| +ChromeExtensionsRendererClient::CreateBrowserPluginDelegate(
|
| + content::RenderFrame* render_frame,
|
| + const std::string& mime_type,
|
| + const GURL& original_url) {
|
| + if (mime_type == content::kBrowserPluginMimeType)
|
| + return new extensions::ExtensionsGuestViewContainer(render_frame);
|
| + return new extensions::MimeHandlerViewContainer(render_frame, mime_type,
|
| + original_url);
|
| +}
|
|
|