Chromium Code Reviews| 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..7e29f6edd9a863f11fe522ef360bbd1c42429dcf 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/renderer_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)) |
|
Charlie Reis
2012/08/22 22:08:34
nit: This for loop needs braces.
nasko
2012/08/23 21:55:53
Done.
|
| + 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,30 @@ 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. |
| +static void ConstructFrameTree(WebKit::WebFrame* frame, |
| + WebKit::WebFrame* exclude_subtree, |
|
Charlie Reis
2012/08/22 22:08:34
nit: Wrong indent.
Also, what is this parameter f
nasko
2012/08/23 21:55:53
Done.
|
| + 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) { |
|
Charlie Reis
2012/08/22 22:08:34
nit: No braces on one-line if.
nasko
2012/08/23 21:55:53
Done.
|
| + dict->Set(content::kFrameTreeNodeSubtreeKey, children); |
| + } |
| +} |
| + |
| /////////////////////////////////////////////////////////////////////////////// |
| struct RenderViewImpl::PendingFileChooser { |
| @@ -558,6 +591,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 +689,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 +698,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 +1001,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 +1905,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 +2494,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_) { |
|
Charlie Reis
2012/08/22 22:08:34
nit: No braces.
nasko
2012/08/23 21:55:53
Done.
|
| + 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 +3664,25 @@ 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 a those updates to the browser and updates the RVH |
|
Charlie Reis
2012/08/22 22:08:34
nit: sends those
nasko
2012/08/23 21:55:53
Done.
|
| +// 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 +3898,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 +3914,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()) { |
|
Charlie Reis
2012/08/22 22:08:34
nit: no braces
nasko
2012/08/23 21:55:53
Done.
|
| + params.target_frame_id = it->second; |
| + } else { |
| + params.target_frame_id = 0; |
| + } |
| Send(new ViewHostMsg_RouteMessageEvent(routing_id_, params)); |
| return true; |
| @@ -4569,16 +4661,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 |
|
Charlie Reis
2012/08/22 22:08:34
nit: Find the target frame of this message.
nasko
2012/08/23 21:55:53
Done.
|
| + // 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 +5022,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 +5033,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 |
|
Charlie Reis
2012/08/22 22:08:34
I feel weird about putting this logic in this func
nasko
2012/08/23 21:55:53
Separated it into a separate function. Fits much b
|
| + // in the current DictionaryValue. |
| + if (frame_tree == NULL) |
| + return; |
| + |
| + string16 name; |
| + if (frame_tree->GetString(content::kFrameTreeNodeNameKey, &name) && |
| + name != string16()) |
|
Charlie Reis
2012/08/22 22:08:34
nit: Needs braces.
nasko
2012/08/23 21:55:53
Done.
|
| + 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) { |
|
Charlie Reis
2012/08/22 22:08:34
nit: No braces.
nasko
2012/08/23 21:55:53
Done.
|
| + NavigateToSwappedOutURL(subframe, child); |
| + } |
| + } |
| + } |
| } |
| void RenderViewImpl::OnClosePage() { |
| @@ -5805,3 +5933,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)) |
|
Charlie Reis
2012/08/22 22:08:34
nit: You don't need "!= NULL"
nasko
2012/08/23 21:55:53
Done.
|
| + 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)) |
|
Charlie Reis
2012/08/22 22:08:34
This part is just FindFrameByID, right? No sense
nasko
2012/08/23 21:55:53
Yes, indeed.
|
| + if (f->identifier() == it->second) |
| + frame = f; |
| + |
| + return frame; |
| +} |