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..6af0cf3d6811f92aa7ed4ed1525ea10d10990ccb 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" |
@@ -42,6 +43,7 @@ |
#include "content/common/quota_dispatcher.h" |
#include "content/common/request_extra_data.h" |
#include "content/common/socket_stream_handle_data.h" |
+#include "content/common/string_constants.h" |
#include "content/common/view_messages.h" |
#include "content/common/webmessageportchannel_impl.h" |
#include "content/public/common/bindings_policy.h" |
@@ -345,6 +347,13 @@ 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 +458,36 @@ static bool IsNonLocalTopLevelNavigation(const GURL& url, |
return false; |
} |
+// The frame tree for a renderer is serialized to JSON, so it can be sent to |
+// the browser process. Each node in the tree is a DictionaryValue with three |
+// properties: |
+// * id - the frame identifier in this renderer |
+// * name - the name of the frame, if one has been assigned |
+// * subtree - a list of DictionaryValue objects for each frame that is |
+// direct child of the current frame. This property can be omitted if |
+// there are no direct child frames, so less data is transferred. |
+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 +597,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 +695,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 +704,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(), NULL); |
} |
RenderViewImpl::~RenderViewImpl() { |
@@ -965,6 +1007,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 +1911,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 +2500,24 @@ 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 +3670,24 @@ 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. |
+// This function sends a 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) { |
+ 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::EnsureMediaStreamImpl() { |
if (!RenderThreadImpl::current()) // Will be NULL during unit tests. |
return; |
@@ -3820,7 +3903,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 +3919,24 @@ 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 = frames_map_.find(targetFrame->identifier()); |
+ if (it != frames_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 +4666,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 frame, which is a target of this message. The source tags the |
+ // message with |remote_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->GetFrameByRemoteID(params.source_frame_id); |
} |
// Create an event with the message. The final parameter to initMessageEvent |
@@ -4927,7 +5027,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(), NULL); |
// Let WebKit know that this view is hidden so it can drop resources and |
// stop compositing. |
@@ -4938,14 +5038,47 @@ 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, |
+ base::DictionaryValue* frame_tree) { |
// 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); |
+ |
+ // If a frame tree is specified, recursively recreate it using the data |
+ // in the current DictionaryValue. |
+ if (frame_tree == NULL) |
+ return; |
+ |
+ 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)) |
+ frames_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)) { |
+ WebFrame* subframe = WebFrame::fromFrameOwnerElement(element); |
+ if (subframe) { |
+ NavigateToSwappedOutURL(subframe, child); |
+ } |
+ } |
+ } |
} |
void RenderViewImpl::OnClosePage() { |
@@ -5805,3 +5938,36 @@ 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() != NULL && tree->IsType(base::Value::TYPE_DICTIONARY)) |
+ tree->GetAsDictionary(&frames); |
+ |
+ updating_frame_tree_ = true; |
+ frames_map_.clear(); |
+ |
+ target_process_id_ = process_id; |
+ target_routing_id_ = route_id; |
+ NavigateToSwappedOutURL(webview()->mainFrame(), frames); |
+ |
+ updating_frame_tree_ = false; |
+} |
+ |
+WebKit::WebFrame* RenderViewImpl::GetFrameByRemoteID(int remote_frame_id) { |
+ WebKit::WebFrame* frame = NULL; |
+ |
+ std::map<int, int>::iterator it = frames_map_.find(remote_frame_id); |
+ if (it == frames_map_.end()) |
+ return NULL; |
+ |
+ for (WebFrame* f = webview()->mainFrame(); f; f = f->traverseNext(false)) |
+ if (f->identifier() == it->second) |
+ frame = f; |
+ |
+ return frame; |
+} |