1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/test/content_browser_test_utils_internal.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/browser/frame_host/frame_tree_node.h"
14 #include "content/browser/frame_host/navigator.h"
15 #include "content/browser/frame_host/render_frame_proxy_host.h"
16 #include "content/test/test_frame_navigation_observer.h"
21 void NavigateFrameToURL(FrameTreeNode
* node
, const GURL
& url
) {
22 TestFrameNavigationObserver
observer(node
);
23 NavigationController::LoadURLParams
params(url
);
24 params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
25 params
.frame_tree_node_id
= node
->frame_tree_node_id();
26 node
->navigator()->GetController()->LoadURLWithParams(params
);
30 FrameTreeVisualizer::FrameTreeVisualizer() {
33 FrameTreeVisualizer::~FrameTreeVisualizer() {
36 std::string
FrameTreeVisualizer::DepictFrameTree(FrameTreeNode
* root
) {
37 // Tracks the sites actually used in this depiction.
38 std::map
<std::string
, SiteInstance
*> legend
;
40 // Traversal 1: Assign names to current frames. This ensures that the first
41 // call to the pretty-printer will result in a naming of the site instances
42 // that feels natural and stable.
43 std::stack
<FrameTreeNode
*> to_explore
;
44 for (to_explore
.push(root
); !to_explore
.empty();) {
45 FrameTreeNode
* node
= to_explore
.top();
47 for (size_t i
= node
->child_count(); i
-- != 0;) {
48 to_explore
.push(node
->child_at(i
));
51 RenderFrameHost
* current
= node
->render_manager()->current_frame_host();
52 legend
[GetName(current
->GetSiteInstance())] = current
->GetSiteInstance();
55 // Traversal 2: Assign names to the pending/speculative frames. For stability
56 // of assigned names it's important to do this before trying to name the
57 // proxies, which have a less well defined order.
58 for (to_explore
.push(root
); !to_explore
.empty();) {
59 FrameTreeNode
* node
= to_explore
.top();
61 for (size_t i
= node
->child_count(); i
-- != 0;) {
62 to_explore
.push(node
->child_at(i
));
65 RenderFrameHost
* pending
= node
->render_manager()->pending_frame_host();
66 RenderFrameHost
* spec
=
67 node
->render_manager()->speculative_render_frame_host_
.get();
69 legend
[GetName(pending
->GetSiteInstance())] = pending
->GetSiteInstance();
71 legend
[GetName(spec
->GetSiteInstance())] = spec
->GetSiteInstance();
74 // Traversal 3: Assign names to the proxies and add them to |legend| too.
75 // Typically, only openers should have their names assigned this way.
76 for (to_explore
.push(root
); !to_explore
.empty();) {
77 FrameTreeNode
* node
= to_explore
.top();
79 for (size_t i
= node
->child_count(); i
-- != 0;) {
80 to_explore
.push(node
->child_at(i
));
83 // Sort the proxies by SiteInstance ID to avoid hash_map ordering.
84 std::map
<int, RenderFrameProxyHost
*> sorted_proxy_hosts
;
85 for (auto& proxy_pair
: node
->render_manager()->proxy_hosts_
) {
86 sorted_proxy_hosts
.insert(proxy_pair
);
88 for (auto& proxy_pair
: sorted_proxy_hosts
) {
89 RenderFrameProxyHost
* proxy
= proxy_pair
.second
;
90 legend
[GetName(proxy
->GetSiteInstance())] = proxy
->GetSiteInstance();
94 // Traversal 4: Now that all names are assigned, make a big loop to pretty-
95 // print the tree. Each iteration produces exactly one line of format.
97 for (to_explore
.push(root
); !to_explore
.empty();) {
98 FrameTreeNode
* node
= to_explore
.top();
100 for (size_t i
= node
->child_count(); i
-- != 0;) {
101 to_explore
.push(node
->child_at(i
));
104 // Draw the feeler line tree graphics by walking up to the root. A feeler
105 // line is needed for each ancestor that is the last child of its parent.
106 // This creates the ASCII art that looks like:
116 // TODO(nick): Make this more elegant.
119 if (node
->parent()->child_at(node
->parent()->child_count() - 1) != node
)
123 for (FrameTreeNode
* up
= node
->parent(); up
!= root
; up
= up
->parent()) {
124 if (up
->parent()->child_at(up
->parent()->child_count() - 1) != up
)
131 // Prefix one extra space of padding for two reasons. First, this helps the
132 // diagram aligns nicely with the legend. Second, this makes it easier to
133 // read the diffs that gtest spits out on EXPECT_EQ failure.
136 // Summarize the FrameTreeNode's state. Always show the site of the current
137 // RenderFrameHost, and show any exceptional state of the node, like a
138 // pending or speculative RenderFrameHost.
139 RenderFrameHost
* current
= node
->render_manager()->current_frame_host();
140 RenderFrameHost
* pending
= node
->render_manager()->pending_frame_host();
141 RenderFrameHost
* spec
=
142 node
->render_manager()->speculative_render_frame_host_
.get();
143 base::StringAppendF(&line
, "Site %s",
144 GetName(current
->GetSiteInstance()).c_str());
146 base::StringAppendF(&line
, " (%s pending)",
147 GetName(pending
->GetSiteInstance()).c_str());
150 base::StringAppendF(&line
, " (%s speculative)",
151 GetName(spec
->GetSiteInstance()).c_str());
154 // Show the SiteInstances of the RenderFrameProxyHosts of this node.
155 if (!node
->render_manager()->proxy_hosts_
.empty()) {
156 // Show a dashed line of variable length before the proxy list. Always at
160 // To make proxy lists align vertically for the first three tree levels,
161 // pad with dashes up to a first tab stop at column 19 (which works out to
162 // text editor column 28 in the typical diagram fed to EXPECT_EQ as a
163 // string literal). Lining the lists up vertically makes differences in
164 // the proxy sets easier to spot visually. We choose not to use the
165 // *actual* tree height here, because that would make the diagram's
166 // appearance less stable as the tree's shape evolves.
167 while (line
.length() < 20) {
170 line
.append(" proxies for");
172 // Sort these alphabetically, to avoid hash_map ordering dependency.
173 std::vector
<std::string
> sorted_proxy_hosts
;
174 for (auto& proxy_pair
: node
->render_manager()->proxy_hosts_
) {
175 sorted_proxy_hosts
.push_back(
176 GetName(proxy_pair
.second
->GetSiteInstance()));
178 std::sort(sorted_proxy_hosts
.begin(), sorted_proxy_hosts
.end());
179 for (std::string
& proxy_name
: sorted_proxy_hosts
) {
180 base::StringAppendF(&line
, " %s", proxy_name
.c_str());
188 // Finally, show a legend with details of the site instances.
189 const char* prefix
= "Where ";
190 for (auto& legend_entry
: legend
) {
191 SiteInstanceImpl
* site_instance
=
192 static_cast<SiteInstanceImpl
*>(legend_entry
.second
);
193 base::StringAppendF(&result
, "\n%s%s = %s", prefix
,
194 legend_entry
.first
.c_str(),
195 site_instance
->GetSiteURL().spec().c_str());
196 // Highlight some exceptionable conditions.
197 if (site_instance
->active_frame_count() == 0)
198 result
.append(" (active_frame_count == 0)");
199 if (!site_instance
->GetProcess()->HasConnection())
200 result
.append(" (no process)");
206 std::string
FrameTreeVisualizer::GetName(SiteInstance
* site_instance
) {
207 // Indices into the vector correspond to letters of the alphabet.
209 std::find(seen_site_instance_ids_
.begin(), seen_site_instance_ids_
.end(),
210 site_instance
->GetId()) -
211 seen_site_instance_ids_
.begin();
212 if (index
== seen_site_instance_ids_
.size())
213 seen_site_instance_ids_
.push_back(site_instance
->GetId());
215 // Whosoever writes a test using >=26 site instances shall be a lucky ducky.
217 return base::StringPrintf("%c", 'A' + static_cast<char>(index
));
219 return base::StringPrintf("Z%d", static_cast<int>(index
- 25));
222 } // namespace content