Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6044)

Unified Diff: content/renderer/render_view_impl.cc

Issue 10827078: Support frame tree propagation between renderers in the same browsing instance. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixes based on feedback from Charlie. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/renderer/render_view_impl.cc
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index daf58486c6501a8c3c1c1b2194342698377f3a49..1c56019c9ffaffaaecc2eaf8b4e22a3c307684d8 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -13,6 +13,7 @@
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
+#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/message_loop_proxy.h"
@@ -29,6 +30,7 @@
#include "content/common/appcache/appcache_dispatcher.h"
#include "content/common/child_thread.h"
#include "content/common/clipboard_messages.h"
+#include "content/common/content_constants_internal.h"
#include "content/common/database_messages.h"
#include "content/common/drag_messages.h"
#include "content/common/fileapi/file_system_dispatcher.h"
@@ -345,6 +347,14 @@ static RenderViewImpl* FromRoutingID(int32 routing_id) {
ChildThread::current()->ResolveRoute(routing_id));
}
+static WebKit::WebFrame* FindFrameByID(WebKit::WebFrame* root, int frame_id) {
+ for (WebFrame* frame = root; frame; frame = frame->traverseNext(false)) {
+ if (frame->identifier() == frame_id)
+ return frame;
+ }
+ return NULL;
+}
+
static void GetRedirectChain(WebDataSource* ds, std::vector<GURL>* result) {
WebVector<WebURL> urls;
ds->redirectChain(urls);
@@ -449,6 +459,31 @@ static bool IsNonLocalTopLevelNavigation(const GURL& url,
return false;
}
+// Recursively walks the frame tree and serializes it to JSON as described in
+// the comment for ViewMsg_UpdateFrameTree. If |exclude_subtree| is set to
+// true, the subtree for the |frame| is not included in the serialized form.
+// This is used when a frame is going to be removed from the tree.
+static void ConstructFrameTree(WebKit::WebFrame* frame,
+ WebKit::WebFrame* exclude_subtree,
+ base::DictionaryValue* dict) {
+ dict->SetString(content::kFrameTreeNodeNameKey,
+ UTF16ToUTF8(frame->assignedName()).c_str());
+ dict->SetInteger(content::kFrameTreeNodeIdKey, frame->identifier());
+
+ WebFrame* child = frame->firstChild();
+ ListValue* children = new ListValue();
+ for (; child; child = child->nextSibling()) {
+ if (child == exclude_subtree)
+ continue;
+
+ base::DictionaryValue* d = new base::DictionaryValue();
+ ConstructFrameTree(child, exclude_subtree, d);
+ children->Append(d);
+ }
+ if (children->GetSize() > 0)
+ dict->Set(content::kFrameTreeNodeSubtreeKey, children);
+}
+
///////////////////////////////////////////////////////////////////////////////
struct RenderViewImpl::PendingFileChooser {
@@ -558,6 +593,10 @@ RenderViewImpl::RenderViewImpl(
guest_to_embedder_channel_(guest_to_embedder_channel),
guest_pp_instance_(0),
guest_uninitialized_context_(NULL),
+ updating_frame_tree_(false),
+ pending_frame_tree_update_(false),
+ target_process_id_(0),
+ target_routing_id_(0),
ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)) {
set_throttle_input_events(renderer_prefs.throttle_input_events);
routing_id_ = routing_id;
@@ -652,7 +691,6 @@ RenderViewImpl::RenderViewImpl(
// If we have an opener_id but we weren't created by a renderer, then
// it's the browser asking us to set our opener to another RenderView.
- // TODO(creis): This doesn't yet handle openers that are subframes.
if (opener_id != MSG_ROUTING_NONE && !is_renderer_created) {
RenderViewImpl* opener_view = FromRoutingID(opener_id);
if (opener_view)
@@ -662,7 +700,7 @@ RenderViewImpl::RenderViewImpl(
// If we are initially swapped out, navigate to kSwappedOutURL.
// This ensures we are in a unique origin that others cannot script.
if (is_swapped_out_)
- NavigateToSwappedOutURL();
+ NavigateToSwappedOutURL(webview()->mainFrame());
}
RenderViewImpl::~RenderViewImpl() {
@@ -965,6 +1003,7 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode)
IPC_MESSAGE_HANDLER(JavaBridgeMsg_Init, OnJavaBridgeInit)
IPC_MESSAGE_HANDLER(ViewMsg_SetAccessibilityMode, OnSetAccessibilityMode)
+ IPC_MESSAGE_HANDLER(ViewMsg_UpdateFrameTree, OnUpdatedFrameTree)
// Have the super handle all other messages.
IPC_MESSAGE_UNHANDLED(handled = RenderWidget::OnMessageReceived(message))
@@ -1868,6 +1907,11 @@ void RenderViewImpl::didStopLoading() {
if (load_progress_tracker_ != NULL)
load_progress_tracker_->DidStopLoading();
+ if (pending_frame_tree_update_) {
+ pending_frame_tree_update_ = false;
+ SendUpdatedFrameTree(webview()->mainFrame(), false);
+ }
+
FOR_EACH_OBSERVER(RenderViewObserver, observers_, DidStopLoading());
}
@@ -2452,7 +2496,23 @@ WebCookieJar* RenderViewImpl::cookieJar(WebFrame* frame) {
return &cookie_jar_;
}
+void RenderViewImpl::didCreateFrame(WebFrame* parent, WebFrame* child) {
+ if (is_loading_) {
+ pending_frame_tree_update_ = true;
+ return;
+ }
+ if (!updating_frame_tree_)
+ SendUpdatedFrameTree(child, false);
+}
+
void RenderViewImpl::frameDetached(WebFrame* frame) {
+ if (is_loading_) {
+ pending_frame_tree_update_ = true;
+ return;
+ }
+ if (!updating_frame_tree_)
+ SendUpdatedFrameTree(frame, true);
+
FOR_EACH_OBSERVER(RenderViewObserver, observers_, FrameDetached(frame));
}
@@ -3605,6 +3665,65 @@ WebGraphicsContext3D* RenderViewImpl::CreateGraphicsContext3D(
}
}
+// The browser process needs to know the shape of the tree, as well as the names
+// and ids of all frames. This allows it to properly route JavaScript messages
+// across processes and frames. The serialization format is described in the
+// comments of the ViewMsg_FrameTreeUpdated message.
+// This function sends those updates to the browser and updates the RVH
+// corresponding to this object. It must be called on any events that modify
+// the tree structure or the names of any frames.
+void RenderViewImpl::SendUpdatedFrameTree(
+ WebKit::WebFrame* frame, bool exclude_subtree) {
Charlie Reis 2012/08/24 23:26:07 Hmm, "frame" makes it sound like we're sending the
nasko 2012/08/27 18:53:37 Done.
+ std::string json;
+ WebFrame* f = frame->top();
+ base::DictionaryValue tree;
+
+ ConstructFrameTree(f, exclude_subtree ? frame : NULL, &tree);
+ base::JSONWriter::Write(&tree, &json);
+
+ Send(new ViewHostMsg_FrameTreeUpdated(routing_id_, json));
+}
+
+void RenderViewImpl::CreateFrameTree(WebKit::WebFrame* frame,
+ DictionaryValue* frame_tree) {
+ NavigateToSwappedOutURL(frame);
+
+ string16 name;
+ if (frame_tree->GetString(content::kFrameTreeNodeNameKey, &name) &&
+ name != string16()) {
+ frame->setName(name);
+ }
+
+ int remote_id;
+ if (frame_tree->GetInteger(content::kFrameTreeNodeIdKey, &remote_id))
+ active_frame_id_map_.insert(std::pair<int, int>(frame->identifier(),
+ remote_id));
+
+ ListValue* children;
+ if (!frame_tree->GetList(content::kFrameTreeNodeSubtreeKey, &children))
+ return;
+
+ base::DictionaryValue* child;
+ for (size_t i = 0; i < children->GetSize(); ++i) {
+ if (!children->GetDictionary(i, &child))
+ continue;
+ WebElement element = frame->document().createElement("iframe");
+ if (frame->document().body().appendChild(element)) {
Charlie Reis 2012/08/24 23:26:07 I wonder if we should do something if this fails?
nasko 2012/08/27 18:53:37 Done.
+ WebFrame* subframe = WebFrame::fromFrameOwnerElement(element);
+ if (subframe)
+ CreateFrameTree(subframe, child);
+ }
+ }
+}
+
+WebKit::WebFrame* RenderViewImpl::GetFrameByMappedID(int frame_id) {
+ std::map<int, int>::iterator it = active_frame_id_map_.find(frame_id);
+ if (it == active_frame_id_map_.end())
+ return NULL;
+
+ return FindFrameByID(webview()->mainFrame(), it->second);
+}
+
void RenderViewImpl::EnsureMediaStreamImpl() {
if (!RenderThreadImpl::current()) // Will be NULL during unit tests.
return;
@@ -3820,7 +3939,8 @@ void RenderViewImpl::dispatchIntent(
}
bool RenderViewImpl::willCheckAndDispatchMessageEvent(
- WebKit::WebFrame* source,
+ WebKit::WebFrame* sourceFrame,
+ WebKit::WebFrame* targetFrame,
WebKit::WebSecurityOrigin target_origin,
WebKit::WebDOMMessageEvent event) {
if (!is_swapped_out_)
@@ -3835,11 +3955,25 @@ bool RenderViewImpl::willCheckAndDispatchMessageEvent(
// Include the routing ID for the source frame, which the browser process
// will translate into the routing ID for the equivalent frame in the target
// process.
- // TODO(creis): Support source subframes.
params.source_routing_id = MSG_ROUTING_NONE;
- RenderViewImpl* source_view = FromWebView(source->view());
+ RenderViewImpl* source_view = FromWebView(sourceFrame->view());
if (source_view)
params.source_routing_id = source_view->routing_id();
+ params.source_frame_id = sourceFrame->identifier();
+
+ // Include the process, route, and frame IDs of the target frame. This allows
+ // the browser to detect races between this message being sent and the target
+ // frame no longer being valid.
+ params.target_process_id = target_process_id_;
+ params.target_routing_id = target_routing_id_;
+
+ std::map<int,int>::iterator it = active_frame_id_map_.find(
+ targetFrame->identifier());
+ if (it != active_frame_id_map_.end()) {
+ params.target_frame_id = it->second;
+ } else {
+ params.target_frame_id = 0;
+ }
Send(new ViewHostMsg_RouteMessageEvent(routing_id_, params));
return true;
@@ -4569,16 +4703,19 @@ void RenderViewImpl::OnScriptEvalRequest(const string16& frame_xpath,
void RenderViewImpl::OnPostMessageEvent(
const ViewMsg_PostMessage_Params& params) {
- // TODO(creis): Support sending to subframes.
- WebFrame* frame = webview()->mainFrame();
+ // Find the target frame of this message. The source tags the message with
+ // |target_frame_id|, so use it to locate the frame.
+ WebFrame* frame = FindFrameByID(webview()->mainFrame(),
+ params.target_frame_id);
+ if (!frame)
+ return;
// Find the source frame if it exists.
- // TODO(creis): Support source subframes.
WebFrame* source_frame = NULL;
if (params.source_routing_id != MSG_ROUTING_NONE) {
RenderViewImpl* source_view = FromRoutingID(params.source_routing_id);
if (source_view)
- source_frame = source_view->webview()->mainFrame();
+ source_frame = source_view->GetFrameByMappedID(params.source_frame_id);
}
// Create an event with the message. The final parameter to initMessageEvent
@@ -4927,7 +5064,7 @@ void RenderViewImpl::OnSwapOut(const ViewMsg_SwapOut_Params& params) {
// run a second time, thanks to a check in FrameLoader::stopLoading.
// TODO(creis): Need to add a better way to do this that avoids running the
// beforeunload handler. For now, we just run it a second time silently.
- NavigateToSwappedOutURL();
+ NavigateToSwappedOutURL(webview()->mainFrame());
// Let WebKit know that this view is hidden so it can drop resources and
// stop compositing.
@@ -4938,14 +5075,14 @@ void RenderViewImpl::OnSwapOut(const ViewMsg_SwapOut_Params& params) {
Send(new ViewHostMsg_SwapOut_ACK(routing_id_, params));
}
-void RenderViewImpl::NavigateToSwappedOutURL() {
+void RenderViewImpl::NavigateToSwappedOutURL(WebKit::WebFrame* frame) {
// We use loadRequest instead of loadHTMLString because the former commits
// synchronously. Otherwise a new navigation can interrupt the navigation
// to content::kSwappedOutURL. If that happens to be to the page we had been
// showing, then WebKit will never send a commit and we'll be left spinning.
GURL swappedOutURL(content::kSwappedOutURL);
WebURLRequest request(swappedOutURL);
- webview()->mainFrame()->loadRequest(request);
+ frame->loadRequest(request);
}
void RenderViewImpl::OnClosePage() {
@@ -5805,3 +5942,22 @@ void RenderViewImpl::OnJavaBridgeInit() {
java_bridge_dispatcher_ = new JavaBridgeDispatcher(this);
#endif
}
+
+void RenderViewImpl::OnUpdatedFrameTree(
+ int process_id,
+ int route_id,
+ const std::string& frame_tree) {
+ base::DictionaryValue* frames = NULL;
+ scoped_ptr<base::Value> tree(base::JSONReader::Read(frame_tree));
+ if (tree.get() && tree->IsType(base::Value::TYPE_DICTIONARY))
+ tree->GetAsDictionary(&frames);
+
+ updating_frame_tree_ = true;
+ active_frame_id_map_.clear();
+
+ target_process_id_ = process_id;
+ target_routing_id_ = route_id;
+ CreateFrameTree(webview()->mainFrame(), frames);
+
+ updating_frame_tree_ = false;
+}

Powered by Google App Engine
This is Rietveld 408576698