diff options
author | alexmos <alexmos@chromium.org> | 2015-04-27 10:59:56 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-27 18:00:52 +0000 |
commit | a7a4ff82f6f78186faf446e1db4d1443c8496371 (patch) | |
tree | d94a3a86a1c467ebabd3645724c413acde202884 /content | |
parent | 0becb33779f8678e2703d6ff1a73ce2b45571f98 (diff) | |
download | chromium_src-a7a4ff82f6f78186faf446e1db4d1443c8496371.zip chromium_src-a7a4ff82f6f78186faf446e1db4d1443c8496371.tar.gz chromium_src-a7a4ff82f6f78186faf446e1db4d1443c8496371.tar.bz2 |
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}
Diffstat (limited to 'content')
-rw-r--r-- | content/browser/frame_host/frame_tree_node.cc | 11 | ||||
-rw-r--r-- | content/browser/frame_host/frame_tree_node.h | 6 | ||||
-rw-r--r-- | content/browser/frame_host/navigator_impl.cc | 2 | ||||
-rw-r--r-- | content/browser/frame_host/render_frame_host_manager.cc | 11 | ||||
-rw-r--r-- | content/browser/frame_host/render_frame_host_manager.h | 5 | ||||
-rw-r--r-- | content/browser/site_per_process_browsertest.cc | 107 | ||||
-rw-r--r-- | content/common/frame_messages.h | 4 | ||||
-rw-r--r-- | content/common/frame_replication_state.h | 33 | ||||
-rw-r--r-- | content/renderer/render_frame_proxy.cc | 6 | ||||
-rw-r--r-- | content/renderer/render_frame_proxy.h | 2 | ||||
-rw-r--r-- | content/test/data/frame_tree/page_with_two_frames.html | 4 |
11 files changed, 179 insertions, 12 deletions
diff --git a/content/browser/frame_host/frame_tree_node.cc b/content/browser/frame_host/frame_tree_node.cc index 1567980..cabc819 100644 --- a/content/browser/frame_host/frame_tree_node.cc +++ b/content/browser/frame_host/frame_tree_node.cc @@ -135,11 +135,16 @@ void FrameTreeNode::ResetForNewProcess() { 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) { + if (name != replication_state_.name) + render_manager_.OnDidUpdateName(name); replication_state_.name = name; - - // Notify this frame's proxies about the updated name. - render_manager_.OnDidUpdateName(name); } bool FrameTreeNode::IsDescendantOf(FrameTreeNode* other) const { diff --git a/content/browser/frame_host/frame_tree_node.h b/content/browser/frame_host/frame_tree_node.h index 7e1add1..bcb7780 100644 --- a/content/browser/frame_host/frame_tree_node.h +++ b/content/browser/frame_host/frame_tree_node.h @@ -93,10 +93,10 @@ class CONTENT_EXPORT FrameTreeNode { current_url_ = url; } - void set_current_origin(const url::Origin& origin) { - replication_state_.origin = origin; - } + // Set the current origin and notify proxies about the update. + void SetCurrentOrigin(const url::Origin& origin); + // Set the current name and notify proxies about the update. void SetFrameName(const std::string& name); SandboxFlags effective_sandbox_flags() { return effective_sandbox_flags_; } diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index a00f9723d..1f9a3b3 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc @@ -414,7 +414,7 @@ void NavigatorImpl::DidNavigate( // origin because it creates a RenderFrameProxy that needs this to initialize // its security context. This origin will also be sent to RenderFrameProxies // 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, // not just main frame navigations. diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc index e4edb08..c9eadcd 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc @@ -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( int type, const NotificationSource& source, diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h index f8004d9..086dea5 100644 --- a/content/browser/frame_host/render_frame_host_manager.h +++ b/content/browser/frame_host/render_frame_host_manager.h @@ -19,6 +19,7 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/common/referrer.h" #include "ui/base/page_transition_types.h" +#include "url/origin.h" namespace content { class BrowserContext; @@ -417,6 +418,10 @@ class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver { // window.name property. 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, RenderViewHostImpl* render_view_host, SiteInstance* instance); diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc index 4bf3a50..a078c4d 100644 --- a/content/browser/site_per_process_browsertest.cc +++ b/content/browser/site_per_process_browsertest.cc @@ -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 // @@ -1786,6 +1838,61 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicWindowName) { 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 // the DidStopLoading event on WebContentsObserver. IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteDidStopLoading) { diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h index 32052df..70870ab 100644 --- a/content/common/frame_messages.h +++ b/content/common/frame_messages.h @@ -534,6 +534,10 @@ IPC_MESSAGE_ROUTED1(FrameMsg_DidUpdateSandboxFlags, content::SandboxFlags) // changed in another process. 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. // Sent for top-level frames. IPC_MESSAGE_ROUTED1(FrameMsg_SetTextTrackSettings, diff --git a/content/common/frame_replication_state.h b/content/common/frame_replication_state.h index f6cfa94..77ec859 100644 --- a/content/common/frame_replication_state.h +++ b/content/common/frame_replication_state.h @@ -48,15 +48,42 @@ struct CONTENT_EXPORT FrameReplicationState { FrameReplicationState(const std::string& name); ~FrameReplicationState(); - // Current serialized security origin of the frame. Unique origins are - // represented as the string "null" per RFC 6454. + // Current serialized security origin of the frame. Unique origins are + // 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; - // 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; // The assigned name of the frame. This name can be empty, unlike the unique // 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; // TODO(alexmos): Eventually, this structure can also hold other state that diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc index f433733..558c391 100644 --- a/content/renderer/render_frame_proxy.cc +++ b/content/renderer/render_frame_proxy.cc @@ -211,6 +211,7 @@ bool RenderFrameProxy::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateSandboxFlags, OnDidUpdateSandboxFlags) IPC_MESSAGE_HANDLER(FrameMsg_DispatchLoad, OnDispatchLoad) IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateName, OnDidUpdateName) + IPC_MESSAGE_HANDLER(FrameMsg_DidUpdateOrigin, OnDidUpdateOrigin) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -303,6 +304,11 @@ void RenderFrameProxy::OnDidUpdateName(const std::string& 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() { if (web_frame_->parent()) { web_frame_->parent()->removeChild(web_frame_); diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h index 19a94f6..fba8c9a 100644 --- a/content/renderer/render_frame_proxy.h +++ b/content/renderer/render_frame_proxy.h @@ -12,6 +12,7 @@ #include "ipc/ipc_sender.h" #include "third_party/WebKit/public/web/WebRemoteFrame.h" #include "third_party/WebKit/public/web/WebRemoteFrameClient.h" +#include "url/origin.h" struct FrameMsg_BuffersSwapped_Params; struct FrameMsg_CompositorFrameSwapped_Params; @@ -144,6 +145,7 @@ class CONTENT_EXPORT RenderFrameProxy void OnDidUpdateSandboxFlags(SandboxFlags flags); void OnDispatchLoad(); void OnDidUpdateName(const std::string& name); + void OnDidUpdateOrigin(const url::Origin& origin); // The routing ID by which this RenderFrameProxy is known. const int routing_id_; diff --git a/content/test/data/frame_tree/page_with_two_frames.html b/content/test/data/frame_tree/page_with_two_frames.html index 3313ce4..c6323ad 100644 --- a/content/test/data/frame_tree/page_with_two_frames.html +++ b/content/test/data/frame_tree/page_with_two_frames.html @@ -3,8 +3,8 @@ </head> <body> This page has two iframes: one is cross-site, the other is same-site. - <iframe src="/cross-site/bar.com/title1.html"></iframe> - <iframe src="../title1.html"></iframe> + <iframe name="frame1" src="/cross-site/bar.com/title1.html"></iframe> + <iframe name="frame2" src="../title1.html"></iframe> </body> </html> |