Commit a7a4ff82 authored by alexmos's avatar alexmos Committed by Commit bot

Send origin updates to frame proxies when a frame navigates to new origin.

One current use for this is to ensure that cross-domain access error
messages (see https://crbug.com/478254) show the latest origin for
RemoteFrames.

BUG=426512, 478254

Review URL: https://codereview.chromium.org/1098763003

Cr-Commit-Position: refs/heads/master@{#327068}
parent 0becb337
...@@ -135,11 +135,16 @@ void FrameTreeNode::ResetForNewProcess() { ...@@ -135,11 +135,16 @@ void FrameTreeNode::ResetForNewProcess() {
old_children.clear(); // May notify observers. old_children.clear(); // May notify observers.
} }
void FrameTreeNode::SetCurrentOrigin(const url::Origin& origin) {
if (!origin.IsSameAs(replication_state_.origin))
render_manager_.OnDidUpdateOrigin(origin);
replication_state_.origin = origin;
}
void FrameTreeNode::SetFrameName(const std::string& name) { void FrameTreeNode::SetFrameName(const std::string& name) {
if (name != replication_state_.name)
render_manager_.OnDidUpdateName(name);
replication_state_.name = name; replication_state_.name = name;
// Notify this frame's proxies about the updated name.
render_manager_.OnDidUpdateName(name);
} }
bool FrameTreeNode::IsDescendantOf(FrameTreeNode* other) const { bool FrameTreeNode::IsDescendantOf(FrameTreeNode* other) const {
......
...@@ -93,10 +93,10 @@ class CONTENT_EXPORT FrameTreeNode { ...@@ -93,10 +93,10 @@ class CONTENT_EXPORT FrameTreeNode {
current_url_ = url; current_url_ = url;
} }
void set_current_origin(const url::Origin& origin) { // Set the current origin and notify proxies about the update.
replication_state_.origin = origin; void SetCurrentOrigin(const url::Origin& origin);
}
// Set the current name and notify proxies about the update.
void SetFrameName(const std::string& name); void SetFrameName(const std::string& name);
SandboxFlags effective_sandbox_flags() { return effective_sandbox_flags_; } SandboxFlags effective_sandbox_flags() { return effective_sandbox_flags_; }
......
...@@ -414,7 +414,7 @@ void NavigatorImpl::DidNavigate( ...@@ -414,7 +414,7 @@ void NavigatorImpl::DidNavigate(
// origin because it creates a RenderFrameProxy that needs this to initialize // origin because it creates a RenderFrameProxy that needs this to initialize
// its security context. This origin will also be sent to RenderFrameProxies // its security context. This origin will also be sent to RenderFrameProxies
// created via ViewMsg_New and FrameMsg_NewFrameProxy. // created via ViewMsg_New and FrameMsg_NewFrameProxy.
render_frame_host->frame_tree_node()->set_current_origin(params.origin); render_frame_host->frame_tree_node()->SetCurrentOrigin(params.origin);
// When using --site-per-process, we notify the RFHM for all navigations, // When using --site-per-process, we notify the RFHM for all navigations,
// not just main frame navigations. // not just main frame navigations.
......
...@@ -889,6 +889,17 @@ void RenderFrameHostManager::OnDidUpdateName(const std::string& name) { ...@@ -889,6 +889,17 @@ void RenderFrameHostManager::OnDidUpdateName(const std::string& name) {
} }
} }
void RenderFrameHostManager::OnDidUpdateOrigin(const url::Origin& origin) {
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kSitePerProcess))
return;
for (const auto& pair : proxy_hosts_) {
pair.second->Send(
new FrameMsg_DidUpdateOrigin(pair.second->GetRoutingID(), origin));
}
}
void RenderFrameHostManager::Observe( void RenderFrameHostManager::Observe(
int type, int type,
const NotificationSource& source, const NotificationSource& source,
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_registrar.h"
#include "content/public/common/referrer.h" #include "content/public/common/referrer.h"
#include "ui/base/page_transition_types.h" #include "ui/base/page_transition_types.h"
#include "url/origin.h"
namespace content { namespace content {
class BrowserContext; class BrowserContext;
...@@ -417,6 +418,10 @@ class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver { ...@@ -417,6 +418,10 @@ class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver {
// window.name property. // window.name property.
void OnDidUpdateName(const std::string& name); void OnDidUpdateName(const std::string& name);
// Send updated origin to all frame proxies when the frame navigates to a new
// origin.
void OnDidUpdateOrigin(const url::Origin& origin);
void EnsureRenderViewInitialized(FrameTreeNode* source, void EnsureRenderViewInitialized(FrameTreeNode* source,
RenderViewHostImpl* render_view_host, RenderViewHostImpl* render_view_host,
SiteInstance* instance); SiteInstance* instance);
......
...@@ -163,6 +163,58 @@ void RenderFrameHostCreatedObserver::RenderFrameCreated( ...@@ -163,6 +163,58 @@ void RenderFrameHostCreatedObserver::RenderFrameCreated(
} }
} }
// A WebContentsDelegate that catches messages sent to the console.
class ConsoleObserverDelegate : public WebContentsDelegate {
public:
ConsoleObserverDelegate(WebContents* web_contents, const std::string& filter)
: web_contents_(web_contents),
filter_(filter),
message_(""),
message_loop_runner_(new MessageLoopRunner) {}
~ConsoleObserverDelegate() override {}
bool AddMessageToConsole(WebContents* source,
int32 level,
const base::string16& message,
int32 line_no,
const base::string16& source_id) override;
std::string message() { return message_; }
void Wait();
private:
WebContents* web_contents_;
std::string filter_;
std::string message_;
// The MessageLoopRunner used to spin the message loop.
scoped_refptr<MessageLoopRunner> message_loop_runner_;
DISALLOW_COPY_AND_ASSIGN(ConsoleObserverDelegate);
};
void ConsoleObserverDelegate::Wait() {
message_loop_runner_->Run();
}
bool ConsoleObserverDelegate::AddMessageToConsole(
WebContents* source,
int32 level,
const base::string16& message,
int32 line_no,
const base::string16& source_id) {
DCHECK(source == web_contents_);
std::string ascii_message = base::UTF16ToASCII(message);
if (MatchPattern(ascii_message, filter_)) {
message_ = ascii_message;
message_loop_runner_->Quit();
}
return false;
}
// //
// SitePerProcessBrowserTest // SitePerProcessBrowserTest
// //
...@@ -1786,6 +1838,61 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicWindowName) { ...@@ -1786,6 +1838,61 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicWindowName) {
EXPECT_EQ(foo_url, root->child_at(0)->current_url()); EXPECT_EQ(foo_url, root->child_at(0)->current_url());
} }
// Verify that when a frame is navigated to a new origin, the origin update
// propagates to the frame's proxies.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OriginUpdatesReachProxies) {
GURL main_url(
embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_url));
// It is safe to obtain the root frame tree node here, as it doesn't change.
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
TestNavigationObserver observer(shell()->web_contents());
EXPECT_EQ(
" Site A ------------ proxies for B\n"
" |--Site B ------- proxies for A\n"
" +--Site A ------- proxies for B\n"
"Where A = http://127.0.0.1/\n"
" B = http://bar.com/",
DepictFrameTree(root));
// Navigate second subframe to a baz.com. This should send an origin update
// to the frame's proxy in the bar.com (first frame's) process.
GURL frame_url = embedded_test_server()->GetURL("baz.com", "/title2.html");
NavigateFrameToURL(root->child_at(1), frame_url);
EXPECT_TRUE(observer.last_navigation_succeeded());
EXPECT_EQ(frame_url, observer.last_navigation_url());
// The first frame can't directly observe the second frame's origin with
// JavaScript. Instead, try to navigate the second frame from the first
// frame. This should fail with a console error message, which should
// contain the second frame's updated origin (see blink::Frame::canNavigate).
scoped_ptr<ConsoleObserverDelegate> console_delegate(
new ConsoleObserverDelegate(
shell()->web_contents(),
"Unsafe JavaScript attempt to initiate navigation*"));
shell()->web_contents()->SetDelegate(console_delegate.get());
// frames[1] can't be used due to a bug where RemoteFrames are created out of
// order (https://crbug.com/478792). Instead, target second frame by name.
EXPECT_TRUE(ExecuteScript(
root->child_at(0)->current_frame_host(),
"window.domAutomationController.send("
" parent.frames['frame2'].location.href = 'data:text/html,foo');"));
console_delegate->Wait();
std::string frame_origin =
root->child_at(1)->current_replication_state().origin.string();
EXPECT_EQ(frame_origin + "/", frame_url.GetOrigin().spec());
EXPECT_TRUE(
MatchPattern(console_delegate->message(), "*" + frame_origin + "*"))
<< "Error message does not contain the frame's latest origin ("
<< frame_origin << ")";
}
// Ensure that navigating subframes in --site-per-process mode properly fires // Ensure that navigating subframes in --site-per-process mode properly fires
// the DidStopLoading event on WebContentsObserver. // the DidStopLoading event on WebContentsObserver.
IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteDidStopLoading) { IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteDidStopLoading) {
......
...@@ -534,6 +534,10 @@ IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateSandboxFlags, content::SandboxFlags) ...@@ -534,6 +534,10 @@ IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateSandboxFlags, content::SandboxFlags)
// changed in another process. // changed in another process.
IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateName, std::string /* name */) IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateName, std::string /* name */)
// Update a proxy's replicated origin. Used when the frame is navigated to a
// new origin.
IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateOrigin, url::Origin /* origin */)
// Send to the RenderFrame to set text track style settings. // Send to the RenderFrame to set text track style settings.
// Sent for top-level frames. // Sent for top-level frames.
IPC_MESSAGE_ROUTED1(FrameMsg_SetTextTrackSettings, IPC_MESSAGE_ROUTED1(FrameMsg_SetTextTrackSettings,
......
...@@ -48,15 +48,42 @@ struct CONTENT_EXPORT FrameReplicationState { ...@@ -48,15 +48,42 @@ struct CONTENT_EXPORT FrameReplicationState {
FrameReplicationState(const std::string& name); FrameReplicationState(const std::string& name);
~FrameReplicationState(); ~FrameReplicationState();
// Current serialized security origin of the frame. Unique origins are // Current serialized security origin of the frame. Unique origins are
// represented as the string "null" per RFC 6454. // represented as the string "null" per RFC 6454. This field is updated
// whenever a frame navigation commits.
//
// TODO(alexmos): For now, |origin| updates are immediately sent to all frame
// proxies when in --site-per-process mode. This isn't ideal, since Blink
// typically needs a proxy's origin only when performing security checks on
// the ancestors of a local frame. So, as a future improvement, we could
// delay sending origin updates to proxies until they have a local descendant
// (if ever). This would reduce leaking a user's browsing history into a
// compromized renderer.
url::Origin origin; url::Origin origin;
// Current sandbox flags of the frame. // Current sandbox flags of the frame. |sandbox_flags| are initialized for
// new child frames using the value of the <iframe> element's "sandbox"
// attribute. They are updated dynamically whenever a parent frame updates an
// <iframe>'s sandbox attribute via JavaScript.
//
// Updates to |sandbox_flags| are sent to proxies, but only after a
// subsequent navigation of the (sandboxed) frame, since the flags only take
// effect on navigation (see also FrameTreeNode::effective_sandbox_flags_).
// The proxies need updated flags so that they can be inherited properly if a
// proxy ever becomes a parent of a local frame.
SandboxFlags sandbox_flags; SandboxFlags sandbox_flags;
// The assigned name of the frame. This name can be empty, unlike the unique // The assigned name of the frame. This name can be empty, unlike the unique
// name generated internally in the DOM tree. // name generated internally in the DOM tree.
//
// |name| is set when a new child frame is created using the value of the
// <iframe> element's "name" attribute (see
// RenderFrameHostImpl::OnCreateChildFrame), and it is updated dynamically
// whenever a frame sets its window.name.
//
// |name| updates are immediately sent to all frame proxies (when in
// --site-per-process mode), so that other frames can look up or navigate a
// frame using its updated name (e.g., using window.open(url, frame_name)).
std::string name; std::string name;
// TODO(alexmos): Eventually, this structure can also hold other state that // TODO(alexmos): Eventually, this structure can also hold other state that
......
...@@ -211,6 +211,7 @@ bool RenderFrameProxy::OnMessageReceived(const IPC::Message& msg) { ...@@ -211,6 +211,7 @@ bool RenderFrameProxy::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateSandboxFlags, OnDidUpdateSandboxFlags) IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateSandboxFlags, OnDidUpdateSandboxFlags)
IPC_MESSAGE_HANDLER(FrameMsg_DispatchLoad, OnDispatchLoad) IPC_MESSAGE_HANDLER(FrameMsg_DispatchLoad, OnDispatchLoad)
IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateName, OnDidUpdateName) IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateName, OnDidUpdateName)
IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateOrigin, OnDidUpdateOrigin)
IPC_MESSAGE_UNHANDLED(handled = false) IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP() IPC_END_MESSAGE_MAP()
...@@ -303,6 +304,11 @@ void RenderFrameProxy::OnDidUpdateName(const std::string& name) { ...@@ -303,6 +304,11 @@ void RenderFrameProxy::OnDidUpdateName(const std::string& name) {
web_frame_->setReplicatedName(blink::WebString::fromUTF8(name)); web_frame_->setReplicatedName(blink::WebString::fromUTF8(name));
} }
void RenderFrameProxy::OnDidUpdateOrigin(const url::Origin& origin) {
web_frame_->setReplicatedOrigin(blink::WebSecurityOrigin::createFromString(
blink::WebString::fromUTF8(origin.string())));
}
void RenderFrameProxy::frameDetached() { void RenderFrameProxy::frameDetached() {
if (web_frame_->parent()) { if (web_frame_->parent()) {
web_frame_->parent()->removeChild(web_frame_); web_frame_->parent()->removeChild(web_frame_);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "ipc/ipc_sender.h" #include "ipc/ipc_sender.h"
#include "third_party/WebKit/public/web/WebRemoteFrame.h" #include "third_party/WebKit/public/web/WebRemoteFrame.h"
#include "third_party/WebKit/public/web/WebRemoteFrameClient.h" #include "third_party/WebKit/public/web/WebRemoteFrameClient.h"
#include "url/origin.h"
struct FrameMsg_BuffersSwapped_Params; struct FrameMsg_BuffersSwapped_Params;
struct FrameMsg_CompositorFrameSwapped_Params; struct FrameMsg_CompositorFrameSwapped_Params;
...@@ -144,6 +145,7 @@ class CONTENT_EXPORT RenderFrameProxy ...@@ -144,6 +145,7 @@ class CONTENT_EXPORT RenderFrameProxy
void OnDidUpdateSandboxFlags(SandboxFlags flags); void OnDidUpdateSandboxFlags(SandboxFlags flags);
void OnDispatchLoad(); void OnDispatchLoad();
void OnDidUpdateName(const std::string& name); void OnDidUpdateName(const std::string& name);
void OnDidUpdateOrigin(const url::Origin& origin);
// The routing ID by which this RenderFrameProxy is known. // The routing ID by which this RenderFrameProxy is known.
const int routing_id_; const int routing_id_;
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
</head> </head>
<body> <body>
This page has two iframes: one is cross-site, the other is same-site. This page has two iframes: one is cross-site, the other is same-site.
<iframe src="/cross-site/bar.com/title1.html"></iframe> <iframe name="frame1" src="/cross-site/bar.com/title1.html"></iframe>
<iframe src="../title1.html"></iframe> <iframe name="frame2" src="../title1.html"></iframe>
</body> </body>
</html> </html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment