1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
|
// Copyright 2014 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/test/content_browser_test_utils_internal.h"
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include "base/strings/stringprintf.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/render_frame_proxy_host.h"
#include "content/test/test_frame_navigation_observer.h"
#include "url/gurl.h"
namespace content {
void NavigateFrameToURL(FrameTreeNode* node, const GURL& url) {
TestFrameNavigationObserver observer(node);
NavigationController::LoadURLParams params(url);
params.transition_type = ui::PAGE_TRANSITION_LINK;
params.frame_tree_node_id = node->frame_tree_node_id();
node->navigator()->GetController()->LoadURLWithParams(params);
observer.Wait();
}
FrameTreeVisualizer::FrameTreeVisualizer() {
}
FrameTreeVisualizer::~FrameTreeVisualizer() {
}
std::string FrameTreeVisualizer::DepictFrameTree(FrameTreeNode* root) {
// Tracks the sites actually used in this depiction.
std::map<std::string, SiteInstance*> legend;
// Traversal 1: Assign names to current frames. This ensures that the first
// call to the pretty-printer will result in a naming of the site instances
// that feels natural and stable.
std::stack<FrameTreeNode*> to_explore;
for (to_explore.push(root); !to_explore.empty();) {
FrameTreeNode* node = to_explore.top();
to_explore.pop();
for (size_t i = node->child_count(); i-- != 0;) {
to_explore.push(node->child_at(i));
}
RenderFrameHost* current = node->render_manager()->current_frame_host();
legend[GetName(current->GetSiteInstance())] = current->GetSiteInstance();
}
// Traversal 2: Assign names to the pending/speculative frames. For stability
// of assigned names it's important to do this before trying to name the
// proxies, which have a less well defined order.
for (to_explore.push(root); !to_explore.empty();) {
FrameTreeNode* node = to_explore.top();
to_explore.pop();
for (size_t i = node->child_count(); i-- != 0;) {
to_explore.push(node->child_at(i));
}
RenderFrameHost* pending = node->render_manager()->pending_frame_host();
RenderFrameHost* spec =
node->render_manager()->speculative_render_frame_host_.get();
if (pending)
legend[GetName(pending->GetSiteInstance())] = pending->GetSiteInstance();
if (spec)
legend[GetName(spec->GetSiteInstance())] = spec->GetSiteInstance();
}
// Traversal 3: Assign names to the proxies and add them to |legend| too.
// Typically, only openers should have their names assigned this way.
for (to_explore.push(root); !to_explore.empty();) {
FrameTreeNode* node = to_explore.top();
to_explore.pop();
for (size_t i = node->child_count(); i-- != 0;) {
to_explore.push(node->child_at(i));
}
// Sort the proxies by SiteInstance ID to avoid hash_map ordering.
std::map<int, RenderFrameProxyHost*> sorted_proxy_hosts;
for (auto& proxy_pair : node->render_manager()->proxy_hosts_) {
sorted_proxy_hosts.insert(proxy_pair);
}
for (auto& proxy_pair : sorted_proxy_hosts) {
RenderFrameProxyHost* proxy = proxy_pair.second;
legend[GetName(proxy->GetSiteInstance())] = proxy->GetSiteInstance();
}
}
// Traversal 4: Now that all names are assigned, make a big loop to pretty-
// print the tree. Each iteration produces exactly one line of format.
std::string result;
for (to_explore.push(root); !to_explore.empty();) {
FrameTreeNode* node = to_explore.top();
to_explore.pop();
for (size_t i = node->child_count(); i-- != 0;) {
to_explore.push(node->child_at(i));
}
// Draw the feeler line tree graphics by walking up to the root. A feeler
// line is needed for each ancestor that is the last child of its parent.
// This creates the ASCII art that looks like:
// Foo
// |--Foo
// |--Foo
// | |--Foo
// | +--Foo
// | +--Foo
// +--Foo
// +--Foo
//
// TODO(nick): Make this more elegant.
std::string line;
if (node != root) {
if (node->parent()->child_at(node->parent()->child_count() - 1) != node)
line = " |--";
else
line = " +--";
for (FrameTreeNode* up = node->parent(); up != root; up = up->parent()) {
if (up->parent()->child_at(up->parent()->child_count() - 1) != up)
line = " | " + line;
else
line = " " + line;
}
}
// Prefix one extra space of padding for two reasons. First, this helps the
// diagram aligns nicely with the legend. Second, this makes it easier to
// read the diffs that gtest spits out on EXPECT_EQ failure.
line = " " + line;
// Summarize the FrameTreeNode's state. Always show the site of the current
// RenderFrameHost, and show any exceptional state of the node, like a
// pending or speculative RenderFrameHost.
RenderFrameHost* current = node->render_manager()->current_frame_host();
RenderFrameHost* pending = node->render_manager()->pending_frame_host();
RenderFrameHost* spec =
node->render_manager()->speculative_render_frame_host_.get();
base::StringAppendF(&line, "Site %s",
GetName(current->GetSiteInstance()).c_str());
if (pending) {
base::StringAppendF(&line, " (%s pending)",
GetName(pending->GetSiteInstance()).c_str());
}
if (spec) {
base::StringAppendF(&line, " (%s speculative)",
GetName(spec->GetSiteInstance()).c_str());
}
// Show the SiteInstances of the RenderFrameProxyHosts of this node.
if (!node->render_manager()->proxy_hosts_.empty()) {
// Show a dashed line of variable length before the proxy list. Always at
// least two dashes.
line.append(" --");
// To make proxy lists align vertically for the first three tree levels,
// pad with dashes up to a first tab stop at column 19 (which works out to
// text editor column 28 in the typical diagram fed to EXPECT_EQ as a
// string literal). Lining the lists up vertically makes differences in
// the proxy sets easier to spot visually. We choose not to use the
// *actual* tree height here, because that would make the diagram's
// appearance less stable as the tree's shape evolves.
while (line.length() < 20) {
line.append("-");
}
line.append(" proxies for");
// Sort these alphabetically, to avoid hash_map ordering dependency.
std::vector<std::string> sorted_proxy_hosts;
for (auto& proxy_pair : node->render_manager()->proxy_hosts_) {
sorted_proxy_hosts.push_back(
GetName(proxy_pair.second->GetSiteInstance()));
}
std::sort(sorted_proxy_hosts.begin(), sorted_proxy_hosts.end());
for (std::string& proxy_name : sorted_proxy_hosts) {
base::StringAppendF(&line, " %s", proxy_name.c_str());
}
}
if (node != root)
result.append("\n");
result.append(line);
}
// Finally, show a legend with details of the site instances.
const char* prefix = "Where ";
for (auto& legend_entry : legend) {
SiteInstanceImpl* site_instance =
static_cast<SiteInstanceImpl*>(legend_entry.second);
base::StringAppendF(&result, "\n%s%s = %s", prefix,
legend_entry.first.c_str(),
site_instance->GetSiteURL().spec().c_str());
// Highlight some exceptionable conditions.
if (site_instance->active_frame_count() == 0)
result.append(" (active_frame_count == 0)");
if (!site_instance->GetProcess()->HasConnection())
result.append(" (no process)");
prefix = " ";
}
return result;
}
std::string FrameTreeVisualizer::GetName(SiteInstance* site_instance) {
// Indices into the vector correspond to letters of the alphabet.
size_t index =
std::find(seen_site_instance_ids_.begin(), seen_site_instance_ids_.end(),
site_instance->GetId()) -
seen_site_instance_ids_.begin();
if (index == seen_site_instance_ids_.size())
seen_site_instance_ids_.push_back(site_instance->GetId());
// Whosoever writes a test using >=26 site instances shall be a lucky ducky.
if (index < 25)
return base::StringPrintf("%c", 'A' + static_cast<char>(index));
else
return base::StringPrintf("Z%d", static_cast<int>(index - 25));
}
} // namespace content
|