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() {
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 {
......
......@@ -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_; }
......
......@@ -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.
......
......@@ -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,
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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,
......
......@@ -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
......
......@@ -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_);
......
......@@ -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_;
......
......@@ -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>
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