summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authornasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-09 02:05:12 +0000
committernasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-09 02:05:12 +0000
commit50904457aacc2c53df5bc47465b9abd53fed1c3d (patch)
tree708cc6a8bd29937010cb6eac2469a9357e6501b7 /content
parent193b09392c0a87d682a4fff09399fd5e20acd7fb (diff)
downloadchromium_src-50904457aacc2c53df5bc47465b9abd53fed1c3d.zip
chromium_src-50904457aacc2c53df5bc47465b9abd53fed1c3d.tar.gz
chromium_src-50904457aacc2c53df5bc47465b9abd53fed1c3d.tar.bz2
Create a frame tree in WebContents on the browser side.
This is a small delta from the original patch at https://codereview.chromium.org/14374002/. BUG=235879 Review URL: https://chromiumcodereview.appspot.com/14580004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@199087 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/renderer_host/frame_tree_node.cc40
-rw-r--r--content/browser/renderer_host/frame_tree_node.h61
-rw-r--r--content/browser/renderer_host/render_view_host_impl.cc14
-rw-r--r--content/browser/renderer_host/render_view_host_impl.h9
-rw-r--r--content/browser/renderer_host/test_render_view_host.cc11
-rw-r--r--content/browser/web_contents/web_contents_impl.cc69
-rw-r--r--content/browser/web_contents/web_contents_impl.h16
-rw-r--r--content/browser/web_contents/web_contents_impl_browsertest.cc39
-rw-r--r--content/browser/web_contents/web_contents_impl_unittest.cc79
-rw-r--r--content/common/view_messages.h13
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/renderer/render_view_impl.cc9
-rw-r--r--content/test/data/frame_tree/2-4.html2
13 files changed, 355 insertions, 9 deletions
diff --git a/content/browser/renderer_host/frame_tree_node.cc b/content/browser/renderer_host/frame_tree_node.cc
new file mode 100644
index 0000000..3486a2d
--- /dev/null
+++ b/content/browser/renderer_host/frame_tree_node.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/frame_tree_node.h"
+
+#include <queue>
+
+#include "base/stl_util.h"
+
+namespace content {
+
+FrameTreeNode::FrameTreeNode(int64 frame_id, const std::string& name)
+ : frame_id_(frame_id),
+ frame_name_(name) {
+}
+
+FrameTreeNode::~FrameTreeNode() {
+ STLDeleteContainerPointers(children_.begin(), children_.end());
+}
+
+void FrameTreeNode::AddChild(FrameTreeNode* child) {
+ children_.push_back(child);
+}
+
+void FrameTreeNode::RemoveChild(int64 child_id) {
+ std::vector<FrameTreeNode*>::iterator iter;
+
+ for (iter = children_.begin(); iter != children_.end(); ++iter) {
+ if ((*iter)->frame_id() == child_id)
+ break;
+ }
+
+ if (iter != children_.end()) {
+ delete *iter;
+ children_.erase(iter);
+ }
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/frame_tree_node.h b/content/browser/renderer_host/frame_tree_node.h
new file mode 100644
index 0000000..e35f409
--- /dev/null
+++ b/content/browser/renderer_host/frame_tree_node.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_FRAME_TREE_NODE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_FRAME_TREE_NODE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "content/common/content_export.h"
+
+namespace content {
+
+// Any page that contains iframes has a tree structure of the frames in the
+// renderer process. We are mirroring this tree in the browser process. This
+// class represents a node in this tree and is a wrapper for all objects that
+// are frame-specific (as opposed to page-specific).
+class CONTENT_EXPORT FrameTreeNode {
+ public:
+ FrameTreeNode(int64 frame_id, const std::string& name);
+ ~FrameTreeNode();
+
+ // This method takes ownership of the child pointer.
+ void AddChild(FrameTreeNode* child);
+ void RemoveChild(int64 child_id);
+
+ int64 frame_id() const {
+ return frame_id_;
+ }
+
+ const std::string& frame_name() const {
+ return frame_name_;
+ }
+
+ size_t child_count() const {
+ return children_.size();
+ }
+
+ FrameTreeNode* child_at(size_t index) const {
+ return children_[index];
+ }
+
+ private:
+ // The unique identifier for the frame in the page.
+ int64 frame_id_;
+
+ // The assigned name of the frame. This name can be empty, unlike the unique
+ // name generated internally in the DOM tree.
+ std::string frame_name_;
+
+ // The immediate children of this specific frame.
+ std::vector<FrameTreeNode*> children_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameTreeNode);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_FRAME_TREE_NODE_H_
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index d3363f5..322cf35 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -171,6 +171,7 @@ RenderViewHostImpl::RenderViewHostImpl(
suspended_nav_params_(NULL),
is_swapped_out_(swapped_out),
is_subframe_(false),
+ main_frame_id_(-1),
run_modal_reply_msg_(NULL),
run_modal_opener_id_(MSG_ROUTING_NONE),
is_waiting_for_beforeunload_ack_(false),
@@ -1116,6 +1117,7 @@ void RenderViewHostImpl::OnRenderViewGone(int status, int exit_code) {
// Reset state.
ClearPowerSaveBlockers();
+ main_frame_id_ = -1;
// Our base class RenderWidgetHost needs to reset some stuff.
RendererExited(render_view_termination_status_, exit_code);
@@ -1186,6 +1188,18 @@ void RenderViewHostImpl::OnNavigate(const IPC::Message& msg) {
if (is_waiting_for_unload_ack_)
return;
+ // Cache the main frame id, so we can use it for creating the frame tree
+ // root node when needed.
+ if (PageTransitionIsMainFrame(validated_params.transition)) {
+ if (main_frame_id_ == -1) {
+ main_frame_id_ = validated_params.frame_id;
+ } else {
+ // TODO(nasko): We plan to remove the usage of frame_id in navigation
+ // and move to routing ids. This is in place to ensure that a
+ // renderer is not misbehaving and sending us incorrect data.
+ DCHECK_EQ(main_frame_id_, validated_params.frame_id);
+ }
+ }
RenderProcessHost* process = GetProcess();
// If the --site-per-process flag is passed, then the renderer process is
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index a03ba43..516af7e7 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -395,6 +395,10 @@ class CONTENT_EXPORT RenderViewHostImpl
is_subframe_ = is_subframe;
}
+ int64 main_frame_id() const {
+ return main_frame_id_;
+ }
+
// Set the opener to null in the renderer process.
void DisownOpener();
@@ -607,6 +611,11 @@ class CONTENT_EXPORT RenderViewHostImpl
// different process from its parent page.
bool is_subframe_;
+ // The frame id of the main (top level) frame. This value is set on the
+ // initial navigation of a RenderView and reset when the RenderView is
+ // terminated (in RenderViewGone).
+ int64 main_frame_id_;
+
// If we were asked to RunModal, then this will hold the reply_msg that we
// must return to the renderer to unblock it.
IPC::Message* run_modal_reply_msg_;
diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc
index 6f5310f..a84f4dc 100644
--- a/content/browser/renderer_host/test_render_view_host.cc
+++ b/content/browser/renderer_host/test_render_view_host.cc
@@ -35,6 +35,9 @@ SessionStorageNamespaceImpl* CreateSessionStorageNamespace(
return new SessionStorageNamespaceImpl(
static_cast<DOMStorageContextImpl*>(dom_storage_context));
}
+
+const int64 kFrameId = 13UL;
+
} // namespace
@@ -248,6 +251,8 @@ TestRenderViewHost::TestRenderViewHost(
// deleted in the destructor below, because
// TestRenderWidgetHostView::Destroy() doesn't |delete this|.
SetView(new TestRenderWidgetHostView(this));
+
+ main_frame_id_ = kFrameId;
}
TestRenderViewHost::~TestRenderViewHost() {
@@ -287,7 +292,7 @@ void TestRenderViewHost::SendNavigateWithTransition(
void TestRenderViewHost::SendNavigateWithOriginalRequestURL(
int page_id, const GURL& url, const GURL& original_request_url) {
- OnDidStartProvisionalLoadForFrame(0, -1, true, url);
+ OnDidStartProvisionalLoadForFrame(kFrameId, -1, true, url);
SendNavigateWithParameters(page_id, url, PAGE_TRANSITION_LINK,
original_request_url, 200, 0);
}
@@ -301,7 +306,7 @@ void TestRenderViewHost::SendNavigateWithFile(
void TestRenderViewHost::SendNavigateWithTransitionAndResponseCode(
int page_id, const GURL& url, PageTransition transition,
int response_code) {
- OnDidStartProvisionalLoadForFrame(0, -1, true, url);
+ OnDidStartProvisionalLoadForFrame(kFrameId, -1, true, url);
SendNavigateWithParameters(page_id, url, transition, url, response_code, 0);
}
@@ -311,7 +316,7 @@ void TestRenderViewHost::SendNavigateWithParameters(
const base::FilePath* file_path_for_history_item) {
ViewHostMsg_FrameNavigate_Params params;
params.page_id = page_id;
- params.frame_id = 0;
+ params.frame_id = kFrameId;
params.url = url;
params.referrer = Referrer();
params.transition = transition;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 43ec8e5..66cd29e 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -692,6 +692,7 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host,
IPC_MESSAGE_HANDLER(ViewHostMsg_OpenDateTimeDialog,
OnOpenDateTimeDialog)
#endif
+ IPC_MESSAGE_HANDLER(ViewHostMsg_FrameAttached, OnFrameAttached)
IPC_MESSAGE_HANDLER(ViewHostMsg_FrameDetached, OnFrameDetached)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP_EX()
@@ -2425,9 +2426,45 @@ void WebContentsImpl::OnUpdateFaviconURL(
DidUpdateFaviconURL(page_id, candidates));
}
-void WebContentsImpl::OnFrameDetached(int64 frame_id) {
+FrameTreeNode* WebContentsImpl::FindFrameTreeNodeByID(int64 frame_id) {
+ FrameTreeNode* node = NULL;
+ std::queue<FrameTreeNode*> queue;
+ queue.push(frame_tree_root_.get());
+
+ while (!queue.empty()) {
+ node = queue.front();
+ queue.pop();
+ if (node->frame_id() == frame_id)
+ return node;
+
+ for (size_t i = 0; i < node->child_count(); ++i)
+ queue.push(node->child_at(i));
+ }
+
+ return NULL;
+}
+
+void WebContentsImpl::OnFrameAttached(
+ int64 parent_frame_id,
+ int64 frame_id,
+ const std::string& frame_name) {
+ FrameTreeNode* parent = FindFrameTreeNodeByID(parent_frame_id);
+ if (!parent)
+ return;
+
+ FrameTreeNode* node = new FrameTreeNode(frame_id, frame_name);
+ parent->AddChild(node);
+}
+
+void WebContentsImpl::OnFrameDetached(int64 parent_frame_id, int64 frame_id) {
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
FrameDetached(message_source_, frame_id));
+
+ FrameTreeNode* parent = FindFrameTreeNodeByID(parent_frame_id);
+ if (!parent)
+ return;
+
+ parent->RemoveChild(frame_id);
}
void WebContentsImpl::DidChangeVisibleSSLState() {
@@ -2720,6 +2757,14 @@ void WebContentsImpl::RenderViewDeleted(RenderViewHost* rvh) {
void WebContentsImpl::DidNavigate(
RenderViewHost* rvh,
const ViewHostMsg_FrameNavigate_Params& params) {
+ // If we don't have a frame tree root yet, this is the first navigation in
+ // using the current RenderViewHost, so we need to create it with the proper
+ // frame id.
+ if (!frame_tree_root_.get()) {
+ DCHECK(PageTransitionIsMainFrame(params.transition));
+ frame_tree_root_.reset(new FrameTreeNode(params.frame_id, std::string()));
+ }
+
if (PageTransitionIsMainFrame(params.transition)) {
// When overscroll navigation gesture is enabled, a screenshot of the page
// in its current state is taken so that it can be used during the
@@ -2733,6 +2778,10 @@ void WebContentsImpl::DidNavigate(
render_manager_.DidNavigateMainFrame(rvh);
}
+ // We expect to have a valid frame tree root node at all times when
+ // navigating.
+ DCHECK(frame_tree_root_.get());
+
// Update the site of the SiteInstance if it doesn't have one yet, unless
// this is for about:blank. In that case, the SiteInstance can still be
// considered unused until a navigation to a real page.
@@ -3306,6 +3355,24 @@ void WebContentsImpl::NotifySwappedFromRenderManager(RenderViewHost* rvh) {
view_->SetOverscrollControllerEnabled(delegate_->CanOverscrollContent());
view_->RenderViewSwappedIn(render_manager_.current_host());
+
+ FrameTreeNode* root = NULL;
+ RenderViewHostImpl* new_rvh = static_cast<RenderViewHostImpl*>(
+ render_manager_.current_host());
+
+ // We are doing a cross-site navigation and swapping processes. Since frame
+ // ids are unique to a process, we need to recreate the frame tree with the
+ // proper main frame id.
+ // Note that it is possible for this method to be called before the new RVH
+ // has committed a navigation (if RenderViewHostManager short-circuits the
+ // CommitPending call because the current RVH is dead). In that case, we
+ // haven't heard a valid frame id to use to initialize the root node, so clear
+ // out the root node and the first subsequent navigation message will set it
+ // correctly.
+ if (new_rvh->main_frame_id() != -1)
+ root = new FrameTreeNode(new_rvh->main_frame_id(), std::string());
+
+ frame_tree_root_.reset(root);
}
int WebContentsImpl::CreateOpenerRenderViewsForRenderManager(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 39cf2ae..a7a408b 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -14,6 +14,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/process.h"
+#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_view_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/web_contents/navigation_controller_impl.h"
@@ -184,6 +185,10 @@ class CONTENT_EXPORT WebContentsImpl
void DragSourceMovedTo(int client_x, int client_y,
int screen_x, int screen_y);
+ FrameTreeNode* GetFrameTreeRootForTesting() {
+ return frame_tree_root_.get();
+ }
+
// WebContents ------------------------------------------------------
virtual WebContentsDelegate* GetDelegate() OVERRIDE;
virtual void SetDelegate(WebContentsDelegate* delegate) OVERRIDE;
@@ -488,6 +493,7 @@ class CONTENT_EXPORT WebContentsImpl
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest,
CrossSiteCantPreemptAfterUnload);
FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, PendingContents);
+ FRIEND_TEST_ALL_PREFIXES(WebContentsImplTest, FrameTreeShape);
FRIEND_TEST_ALL_PREFIXES(FormStructureBrowserTest, HTMLFiles);
FRIEND_TEST_ALL_PREFIXES(NavigationControllerTest, HistoryNavigate);
FRIEND_TEST_ALL_PREFIXES(RenderViewHostManagerTest, PageDoesBackAndReload);
@@ -588,7 +594,10 @@ class CONTENT_EXPORT WebContentsImpl
const std::vector<SkBitmap>& bitmaps);
void OnUpdateFaviconURL(int32 page_id,
const std::vector<FaviconURL>& candidates);
- void OnFrameDetached(int64 frame_id);
+ void OnFrameAttached(int64 parent_frame_id,
+ int64 frame_id,
+ const std::string& frame_name);
+ void OnFrameDetached(int64 parent_frame_id, int64 frame_id);
// Changes the IsLoading state and notifies delegate as needed
// |details| is used to provide details on the load that just finished
@@ -697,6 +706,8 @@ class CONTENT_EXPORT WebContentsImpl
RenderViewHostImpl* GetRenderViewHostImpl();
+ FrameTreeNode* FindFrameTreeNodeByID(int64 frame_id);
+
// Removes browser plugin embedder if there is one.
void RemoveBrowserPluginEmbedder();
@@ -800,6 +811,9 @@ class CONTENT_EXPORT WebContentsImpl
// True if this is a secure page which displayed insecure content.
bool displayed_insecure_content_;
+ // The frame tree structure of the current page.
+ scoped_ptr<FrameTreeNode> frame_tree_root_;
+
// Data for misc internal state ----------------------------------------------
// When > 0, the WebContents is currently being captured (e.g., for
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index cfc5e08..66e0857 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/values.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/load_notification_details.h"
#include "content/public/browser/navigation_controller.h"
@@ -121,4 +122,42 @@ IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest,
load_observer.controller_);
}
+// Test that the browser receives the proper frame attach/detach messages from
+// the renderer and builds proper frame tree.
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, FrameTree) {
+ ASSERT_TRUE(test_server()->Start());
+
+ NavigateToURL(shell(), test_server()->GetURL("files/frame_tree/top.html"));
+
+ WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
+ wc->GetRenderViewHost());
+ FrameTreeNode* root = wc->GetFrameTreeRootForTesting();
+
+ // Check that the root node is properly created with the frame id of the
+ // initial navigation.
+ EXPECT_EQ(3UL, root->child_count());
+ EXPECT_EQ(std::string(), root->frame_name());
+ EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
+
+ EXPECT_EQ(2UL, root->child_at(0)->child_count());
+ EXPECT_STREQ("1-1-name", root->child_at(0)->frame_name().c_str());
+
+ // Verify the deepest node exists and has the right name.
+ EXPECT_EQ(2UL, root->child_at(2)->child_count());
+ EXPECT_EQ(1UL, root->child_at(2)->child_at(1)->child_count());
+ EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_at(0)->child_count());
+ EXPECT_STREQ("3-1-id",
+ root->child_at(2)->child_at(1)->child_at(0)->frame_name().c_str());
+
+ // Navigate to about:blank, which should leave only the root node of the frame
+ // tree in the browser process.
+ NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
+
+ root = wc->GetFrameTreeRootForTesting();
+ EXPECT_EQ(0UL, root->child_count());
+ EXPECT_EQ(std::string(), root->frame_name());
+ EXPECT_EQ(rvh->main_frame_id(), root->frame_id());
+}
+
} // namespace content
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index cb702a4..6d79088 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -4,6 +4,7 @@
#include "base/logging.h"
#include "base/utf_string_conversions.h"
+#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/renderer_host/test_render_view_host.h"
#include "content/browser/site_instance_impl.h"
@@ -2050,4 +2051,82 @@ TEST_F(WebContentsImplTest, PendingContents) {
EXPECT_EQ(NULL, contents()->GetCreatedWindow(route_id));
}
+// This test asserts the shape of the frame tree is correct, based on incoming
+// frame attached/detached messages.
+TEST_F(WebContentsImplTest, FrameTreeShape) {
+ std::string no_children_node("no children node");
+ std::string deep_subtree("node with deep subtree");
+
+ // The initial navigation will create a frame_tree_root_ node with the top
+ // level frame id. Simulate that by just creating it here.
+ contents()->frame_tree_root_.reset(
+ new FrameTreeNode(5, std::string("top-level")));
+
+ // Let's send a series of messages for frame attached and build the
+ // frame tree.
+ contents()->OnFrameAttached(5, 14, std::string());
+ contents()->OnFrameAttached(5, 15, std::string());
+ contents()->OnFrameAttached(5, 16, std::string());
+
+ contents()->OnFrameAttached(14, 244, std::string());
+ contents()->OnFrameAttached(14, 245, std::string());
+
+ contents()->OnFrameAttached(15, 255, no_children_node);
+
+ contents()->OnFrameAttached(16, 264, std::string());
+ contents()->OnFrameAttached(16, 265, std::string());
+ contents()->OnFrameAttached(16, 266, std::string());
+ contents()->OnFrameAttached(16, 267, deep_subtree);
+ contents()->OnFrameAttached(16, 268, std::string());
+
+ contents()->OnFrameAttached(267, 365, std::string());
+ contents()->OnFrameAttached(365, 455, std::string());
+ contents()->OnFrameAttached(455, 555, std::string());
+ contents()->OnFrameAttached(555, 655, std::string());
+
+ // Now, verify the tree structure is as expected.
+ FrameTreeNode* root = contents()->frame_tree_root_.get();
+ EXPECT_EQ(5, root->frame_id());
+ EXPECT_EQ(3UL, root->child_count());
+
+ EXPECT_EQ(2UL, root->child_at(0)->child_count());
+ EXPECT_EQ(0UL, root->child_at(0)->child_at(0)->child_count());
+ EXPECT_EQ(0UL, root->child_at(0)->child_at(1)->child_count());
+
+ EXPECT_EQ(1UL, root->child_at(1)->child_count());
+ EXPECT_EQ(0UL, root->child_at(1)->child_at(0)->child_count());
+ EXPECT_STREQ(no_children_node.c_str(),
+ root->child_at(1)->child_at(0)->frame_name().c_str());
+
+ EXPECT_EQ(5UL, root->child_at(2)->child_count());
+ EXPECT_EQ(0UL, root->child_at(2)->child_at(0)->child_count());
+ EXPECT_EQ(0UL, root->child_at(2)->child_at(1)->child_count());
+ EXPECT_EQ(0UL, root->child_at(2)->child_at(2)->child_count());
+ EXPECT_EQ(1UL, root->child_at(2)->child_at(3)->child_count());
+ EXPECT_STREQ(deep_subtree.c_str(),
+ root->child_at(2)->child_at(3)->frame_name().c_str());
+ EXPECT_EQ(0UL, root->child_at(2)->child_at(4)->child_count());
+
+ FrameTreeNode* deep_tree = root->child_at(2)->child_at(3)->child_at(0);
+ EXPECT_EQ(365, deep_tree->frame_id());
+ EXPECT_EQ(1UL, deep_tree->child_count());
+ EXPECT_EQ(455, deep_tree->child_at(0)->frame_id());
+ EXPECT_EQ(1UL, deep_tree->child_at(0)->child_count());
+ EXPECT_EQ(555, deep_tree->child_at(0)->child_at(0)->frame_id());
+ EXPECT_EQ(1UL, deep_tree->child_at(0)->child_at(0)->child_count());
+ EXPECT_EQ(655, deep_tree->child_at(0)->child_at(0)->child_at(0)->frame_id());
+ EXPECT_EQ(0UL,
+ deep_tree->child_at(0)->child_at(0)->child_at(0)->child_count());
+
+ // Test removing of nodes.
+ contents()->OnFrameDetached(555, 655);
+ EXPECT_EQ(0UL, deep_tree->child_at(0)->child_at(0)->child_count());
+
+ contents()->OnFrameDetached(16, 265);
+ EXPECT_EQ(4UL, root->child_at(2)->child_count());
+
+ contents()->OnFrameDetached(5, 15);
+ EXPECT_EQ(2UL, root->child_count());
+}
+
} // namespace content
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 2f3756e..0a5cc0b 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -2172,8 +2172,17 @@ IPC_MESSAGE_CONTROL3(ViewHostMsg_DidLose3DContext,
content::ThreeDAPIType /* context_type */,
int /* arb_robustness_status_code */)
-// Notifies the browser that the frame with the given id was detached.
-IPC_MESSAGE_ROUTED1(ViewHostMsg_FrameDetached,
+// This message is sent when a frame is added to the DOM.
+IPC_MESSAGE_ROUTED3(ViewHostMsg_FrameAttached,
+ int64 /* parent_frame_id*/,
+ int64 /* frame_id */,
+ std::string /* frame_name */)
+
+// Notifies the browser that the frame with the given id was detached. The
+// |parent_frame_id| is -1 for the top level frame, otherwise the id of the
+// immediate parent of the detached frame.
+IPC_MESSAGE_ROUTED2(ViewHostMsg_FrameDetached,
+ int64 /* parent_frame_id */,
int64 /* frame_id */)
// Notifies the browser that document has parsed the body. This is used by the
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 37b5aee..fc2071b 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -682,6 +682,8 @@
'browser/renderer_host/dip_util.h',
'browser/renderer_host/file_utilities_message_filter.cc',
'browser/renderer_host/file_utilities_message_filter.h',
+ 'browser/renderer_host/frame_tree_node.cc',
+ 'browser/renderer_host/frame_tree_node.h',
'browser/renderer_host/gamepad_browser_message_filter.cc',
'browser/renderer_host/gamepad_browser_message_filter.h',
'browser/renderer_host/gesture_event_filter.cc',
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 56ff217..a24e962 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -2776,6 +2776,8 @@ WebCookieJar* RenderViewImpl::cookieJar(WebFrame* frame) {
}
void RenderViewImpl::didCreateFrame(WebFrame* parent, WebFrame* child) {
+ Send(new ViewHostMsg_FrameAttached(routing_id_, parent->identifier(),
+ child->identifier(), UTF16ToUTF8(child->assignedName())));
}
void RenderViewImpl::didDisownOpener(WebKit::WebFrame* frame) {
@@ -2791,7 +2793,12 @@ void RenderViewImpl::didDisownOpener(WebKit::WebFrame* frame) {
}
void RenderViewImpl::frameDetached(WebFrame* frame) {
- Send(new ViewHostMsg_FrameDetached(routing_id_, frame->identifier()));
+ int64 parent_frame_id = -1;
+ if (frame->parent())
+ parent_frame_id = frame->parent()->identifier();
+
+ Send(new ViewHostMsg_FrameDetached(routing_id_, parent_frame_id,
+ frame->identifier()));
FOR_EACH_OBSERVER(RenderViewObserver, observers_, FrameDetached(frame));
}
diff --git a/content/test/data/frame_tree/2-4.html b/content/test/data/frame_tree/2-4.html
index 3bdea4b..3767f39 100644
--- a/content/test/data/frame_tree/2-4.html
+++ b/content/test/data/frame_tree/2-4.html
@@ -6,7 +6,7 @@
Frame 2-4.
<br>
-<iframe src="3-1.html"></iframe>
+<iframe id='3-1-id' src="3-1.html"></iframe>
</body>
</html>