Index: chrome/renderer/chrome_content_renderer_client.cc |
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc |
index 31db342e15799c533e06e4ca434d99f18d1aef76..13b70a7003ed17b4012a4ed20ce2c7343cba4c30 100644 |
--- a/chrome/renderer/chrome_content_renderer_client.cc |
+++ b/chrome/renderer/chrome_content_renderer_client.cc |
@@ -72,6 +72,7 @@ |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginParams.h" |
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityPolicy.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" |
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" |
@@ -89,8 +90,10 @@ using WebKit::WebDataSource; |
using WebKit::WebFrame; |
using WebKit::WebPlugin; |
using WebKit::WebPluginParams; |
+using WebKit::WebSecurityOrigin; |
using WebKit::WebSecurityPolicy; |
using WebKit::WebString; |
+using WebKit::WebURL; |
using WebKit::WebURLError; |
using WebKit::WebURLRequest; |
using WebKit::WebURLResponse; |
@@ -573,13 +576,14 @@ bool ChromeContentRendererClient::AllowPopup(const GURL& creator) { |
bool ChromeContentRendererClient::ShouldFork(WebFrame* frame, |
const GURL& url, |
bool is_content_initiated, |
+ bool is_initial_navigation, |
bool* send_referrer) { |
// If the navigation would cross an app extent boundary, we also need |
// to defer to the browser to ensure process isolation. |
// TODO(erikkay) This is happening inside of a check to is_content_initiated |
// which means that things like the back button won't trigger it. Is that |
// OK? |
- if (!CrossesExtensionExtents(frame, url)) |
+ if (!CrossesExtensionExtents(frame, url, is_initial_navigation)) |
return false; |
// Include the referrer in this case since we're going from a hosted web |
@@ -671,25 +675,40 @@ void ChromeContentRendererClient::SetExtensionDispatcher( |
extension_dispatcher_.reset(extension_dispatcher); |
} |
-bool ChromeContentRendererClient::CrossesExtensionExtents(WebFrame* frame, |
- const GURL& new_url) { |
+bool ChromeContentRendererClient::CrossesExtensionExtents( |
+ WebFrame* frame, |
+ const GURL& new_url, |
+ bool is_initial_navigation) { |
const ExtensionSet* extensions = extension_dispatcher_->extensions(); |
- // If the URL is still empty, this is a window.open navigation. Check the |
- // opener's URL. In all cases we use the top frame's URL (as opposed to our |
- // frame's) since that's what determines the type of process. |
- // TODO(abarth): This code is super sketchy! Are you sure looking at the |
- // opener is correct here? This appears to let me steal my opener's |
- // privileges if I can make my URL be "empty." |
+ bool is_extension_url = !!extensions->GetByURL(new_url); |
GURL old_url(frame->top()->document().url()); |
- if (old_url.is_empty() && frame->opener()) |
+ |
+ // 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. |
+ if (is_initial_navigation && old_url.is_empty() && frame->opener()) { |
+ // 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. |
+ GURL opener_url = frame->opener()->document().url(); |
+ bool opener_is_extension_url = !!extensions->GetByURL(opener_url); |
+ WebSecurityOrigin opener = frame->opener()->document().securityOrigin(); |
+ if (!is_extension_url && |
+ !opener_is_extension_url && |
+ extension_dispatcher_->is_extension_process() && |
+ opener.canRequest(WebURL(new_url))) |
+ return false; |
+ |
+ // In all other cases, we want to compare against the top frame's URL (as |
+ // opposed to the opener frame's), since that's what determines the type of |
+ // process. This allows iframes outside an app to open a popup in the app. |
old_url = frame->top()->opener()->top()->document().url(); |
+ } |
// 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. |
if (old_url == new_url) { |
- bool is_extension_url = !!extensions->GetByURL(new_url); |
if (is_extension_url != extension_dispatcher_->is_extension_process()) |
return true; |
} |