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
223
224
225
226
227
228
229
230
231
content / browser / renderer_host / render_document_host_browsertest.cc [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/test/scoped_feature_list.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/content_navigation_policy.h"
#include "content/common/features.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/render_document_feature.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace content {
namespace {
// Test with RenderDocument enabled.
class RenderDocumentHostBrowserTest : public ContentBrowserTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
InitAndEnableRenderDocumentFeature(
&feature_list_for_render_document_,
GetRenderDocumentLevelName(RenderDocumentLevel::kAllFrames));
// Disable BackForwardCache so that the RenderFrameHost changes aren't
// caused by proactive BrowsingInstance swap.
feature_list_for_back_forward_cache_.InitAndDisableFeature(
features::kBackForwardCache);
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
}
WebContentsImpl* web_contents() const {
return static_cast<WebContentsImpl*>(shell()->web_contents());
}
RenderFrameHostImpl* current_main_frame() {
return web_contents()->GetPrimaryFrameTree().root()->current_frame_host();
}
private:
base::test::ScopedFeatureList feature_list_for_render_document_;
base::test::ScopedFeatureList feature_list_for_back_forward_cache_;
};
} // namespace
// A new RenderFrameHost must be used after a same process navigation.
IN_PROC_BROWSER_TEST_F(RenderDocumentHostBrowserTest, BasicMainFrame) {
GURL url_1(embedded_test_server()->GetURL("/title1.html"));
GURL url_2(embedded_test_server()->GetURL("/title2.html"));
GURL url_3(embedded_test_server()->GetURL("/title3.html"));
// 1) Navigate to A1.
EXPECT_TRUE(NavigateToURL(shell(), url_1));
RenderFrameHostWrapper rfh_1(current_main_frame());
// 2) Navigate to A2.
EXPECT_TRUE(NavigateToURL(shell(), url_2));
RenderFrameHostWrapper rfh_2(current_main_frame());
EXPECT_TRUE(rfh_1.WaitUntilRenderFrameDeleted());
// 3) Navigate to A3.
EXPECT_TRUE(NavigateToURL(shell(), url_3));
EXPECT_TRUE(rfh_2.WaitUntilRenderFrameDeleted());
}
// A new RenderFrameHost must be used after a same process subframe navigation.
// This test two cases, when the RenderFrame is not a local root and when it is.
IN_PROC_BROWSER_TEST_F(RenderDocumentHostBrowserTest, BasicSubframe) {
GURL url(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
GURL url_subframe_2(embedded_test_server()->GetURL("a.com", "/title2.html"));
GURL url_subframe_3(embedded_test_server()->GetURL("b.com", "/title1.html"));
GURL url_subframe_4(embedded_test_server()->GetURL("b.com", "/title2.html"));
// 1) Setup a main frame with a same-process subframe
EXPECT_TRUE(NavigateToURL(shell(), url));
FrameTreeNode* subframe = current_main_frame()->child_at(0);
RenderFrameHostImpl* child_rfh_1 = subframe->current_frame_host();
RenderFrameDeletedObserver delete_child_rfh_1(child_rfh_1);
// 2) Navigate the subframe same-process. (non local root case).
NavigateIframeToURL(web_contents(), "test_iframe", url_subframe_2);
RenderFrameHostImpl* child_rfh_2 = subframe->current_frame_host();
EXPECT_TRUE(delete_child_rfh_1.deleted());
EXPECT_NE(child_rfh_1, child_rfh_2);
RenderFrameDeletedObserver delete_child_rfh_2(child_rfh_2);
// 3) Navigate the subframe cross-process.
NavigateIframeToURL(web_contents(), "test_iframe", url_subframe_3);
RenderFrameHostImpl* child_rfh_3 = subframe->current_frame_host();
EXPECT_TRUE(delete_child_rfh_1.deleted());
EXPECT_NE(child_rfh_2, child_rfh_3);
RenderFrameDeletedObserver delete_child_rfh_3(child_rfh_3);
// 4) Navigate the subframe same-process. (local root case).
NavigateIframeToURL(web_contents(), "test_iframe", url_subframe_4);
RenderFrameHostImpl* child_rfh_4 = subframe->current_frame_host();
EXPECT_TRUE(delete_child_rfh_3.deleted());
EXPECT_NE(child_rfh_3, child_rfh_4);
}
// Two windows are scriptable with each other. Test it works appropriately after
// one of them doing a same-origin navigation.
IN_PROC_BROWSER_TEST_F(RenderDocumentHostBrowserTest, PopupScriptableNavigate) {
GURL url_1(embedded_test_server()->GetURL("/title1.html"));
GURL url_2(embedded_test_server()->GetURL("/title2.html"));
// 1) Navigate and open a new window.
EXPECT_TRUE(NavigateToURL(shell(), url_1));
ShellAddedObserver shell_added_observer;
EXPECT_TRUE(ExecJs(shell(), JsReplace("w = window.open($1)", url_1)));
WebContents* new_contents = shell_added_observer.GetShell()->web_contents();
EXPECT_TRUE(WaitForLoadStop(new_contents));
// Both content have the same origin, so they are cross-scriptable.
EXPECT_EQ(new_contents->GetPrimaryMainFrame()->GetLastCommittedOrigin(),
web_contents()->GetPrimaryMainFrame()->GetLastCommittedOrigin());
// 2) Reference a variable in between the two windows.
EXPECT_TRUE(ExecJs(web_contents(),
"other_window = w.window;"
"other_window.foo = 'bar_1'"));
// The object is accessible from each side.
EXPECT_EQ("bar_1", EvalJs(web_contents(), "other_window.foo;"));
EXPECT_EQ("bar_1", EvalJs(new_contents, "window.foo;"));
// The URL is accessible from each side.
EXPECT_EQ(url_1, EvalJs(web_contents(), "other_window.location.href;"));
EXPECT_EQ(url_1, EvalJs(new_contents, "window.location.href;"));
// 3) Navigate the new window same-process.
int process_id = new_contents->GetPrimaryMainFrame()->GetProcess()->GetID();
EXPECT_TRUE(NavigateToURL(new_contents, url_2));
EXPECT_EQ(process_id,
new_contents->GetPrimaryMainFrame()->GetProcess()->GetID());
// The URL is accessible from each side and correctly reflects the current
// value.
EXPECT_EQ(url_2, EvalJs(web_contents(), "other_window.location.href;"));
EXPECT_EQ(url_2, EvalJs(new_contents, "window.location.href;"));
// The object is no longer accessible from each side.
EXPECT_EQ(nullptr, EvalJs(web_contents(), "other_window.foo;"));
EXPECT_EQ(nullptr, EvalJs(new_contents, "window.foo"));
// Define the variable again.
EXPECT_TRUE(ExecJs(web_contents(), "other_window.foo = 'bar_2';"));
// The object is accessible from each side.
EXPECT_EQ("bar_2", EvalJs(web_contents(), "other_window.foo;"));
EXPECT_EQ("bar_2", EvalJs(new_contents, "window.foo;"));
}
// Two frames are scriptable with each other. Test it works appropriately after
// one of them doing a same-origin navigation.
IN_PROC_BROWSER_TEST_F(RenderDocumentHostBrowserTest,
SubframeScriptableNavigate) {
GURL url_1(embedded_test_server()->GetURL("/page_with_iframe.html"));
GURL url_2(embedded_test_server()->GetURL("/title2.html"));
GURL url_3(embedded_test_server()->GetURL("/title3.html"));
// 1) Setup a main frame with an subframe.
EXPECT_TRUE(NavigateToURL(shell(), url_1));
RenderFrameHostImpl* main_rfh = current_main_frame();
RenderFrameHostImpl* child_rfh_1 =
main_rfh->child_at(0)->current_frame_host();
RenderFrameDeletedObserver delete_child_rfh_1(child_rfh_1);
// Both content have the same origin, so they are cross-scriptable.
EXPECT_EQ(main_rfh->GetLastCommittedOrigin(),
child_rfh_1->GetLastCommittedOrigin());
// Reference a variable in between the two windows.
EXPECT_TRUE(ExecJs(main_rfh,
"other_window = document.querySelector('iframe')"
" .contentWindow;"
"other_window.foo = 'bar_1'"));
// The object is accessible from each side.
EXPECT_EQ("bar_1", EvalJs(main_rfh, "other_window.foo;"));
EXPECT_EQ("bar_1", EvalJs(child_rfh_1, "window.foo;"));
// 2) Navigate the subframe.
NavigateIframeToURL(web_contents(), "test_iframe", url_2);
EXPECT_TRUE(delete_child_rfh_1.deleted());
RenderFrameHostImpl* subframe_rfh_2 =
main_rfh->child_at(0)->current_frame_host();
// The object is no longer accessible from each side.
EXPECT_EQ(nullptr, EvalJs(main_rfh, "other_window.foo;"));
EXPECT_EQ(nullptr, EvalJs(subframe_rfh_2, "window.foo"));
// Define the variable again.
EXPECT_TRUE(ExecJs(main_rfh, "other_window.foo = 'bar_2';"));
// The object is accessible from each side.
EXPECT_EQ("bar_2", EvalJs(main_rfh, "other_window.foo;"));
EXPECT_EQ("bar_2", EvalJs(subframe_rfh_2, "window.foo;"));
}
// A new window is created and the opener injects a new function into it before
// loading the document. This makes sure the window is not replaced and the
// function still accessible from the new document.
IN_PROC_BROWSER_TEST_F(RenderDocumentHostBrowserTest, InjectedFunction) {
GURL url(embedded_test_server()->GetURL("/title1.html"));
// 1) Navigate and open a new window with an injected function.
EXPECT_TRUE(NavigateToURL(shell(), url));
ShellAddedObserver shell_added_observer;
EXPECT_TRUE(ExecJs(
shell(), JsReplace("w = window.open($1);"
"w.injected_function = () => { return 'It works!'; };",
url)));
WebContents* new_contents = shell_added_observer.GetShell()->web_contents();
// 2) Test the function is available to the new document.
EXPECT_EQ("It works!", EvalJs(new_contents, "injected_function();"));
}
} // namespace content