OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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. | |
4 | |
5 #include <vector> | |
6 | |
7 #include "content/browser/frame_host/frame_tree_node.h" | |
8 #include "content/browser/frame_host/render_frame_host_impl.h" | |
9 #include "content/browser/web_contents/web_contents_impl.h" | |
10 #include "content/public/browser/host_zoom_map.h" | |
11 #include "content/public/browser/navigation_entry.h" | |
12 #include "content/public/common/page_zoom.h" | |
13 #include "content/public/test/browser_test_utils.h" | |
14 #include "content/public/test/content_browser_test.h" | |
15 #include "content/public/test/content_browser_test_utils.h" | |
16 #include "content/public/test/test_navigation_observer.h" | |
17 #include "content/shell/browser/shell.h" | |
18 #include "content/test/content_browser_test_utils_internal.h" | |
19 #include "net/dns/mock_host_resolver.h" | |
20 #include "testing/gtest/include/gtest/gtest.h" | |
21 #include "url/gurl.h" | |
22 | |
23 namespace content { | |
24 | |
25 // This file contains tests to make sure that subframes zoom in a manner | |
26 // consistent with the top-level frame, even when the subframes are cross-site. | |
27 // Particular things we want to make sure of: | |
28 // | |
29 // * Subframes should always have the same zoom level as their main frame, even | |
30 // if the subframe's domain has a different zoom level stored in HostZoomMap. | |
31 // | |
32 // * The condition above should continue to hold after a navigation of the | |
33 // subframe. | |
34 // | |
35 // * Zoom changes applied to the mainframe should propagate to all subframes, | |
36 // regardless of whether they are same site or cross-site to the frame they are | |
37 // children of. | |
38 // | |
39 // The tests in this file rely on the notion that, when a page zooms, that | |
40 // subframes have both (1) a change in their frame rect, and (2) a change in | |
41 // their frame's scale. Since the page should scale as a unit, this means the | |
42 // innerWidth value of any subframe should be the same before and after the | |
43 // zoom (though it may transiently take on a different value). The | |
44 // FrameSizeObserver serves to watch for onresize events, and observes when | |
45 // the innerWidth is correctly set. | |
46 class IFrameZoomBrowserTest : public ContentBrowserTest { | |
47 public: | |
48 IFrameZoomBrowserTest() {} | |
49 | |
50 protected: | |
51 void SetUpOnMainThread() override { | |
52 host_resolver()->AddRule("*", "127.0.0.1"); | |
53 ASSERT_TRUE(embedded_test_server()->Start()); | |
54 SetupCrossSiteRedirector(embedded_test_server()); | |
55 } | |
56 | |
57 WebContentsImpl* web_contents() { | |
58 return static_cast<WebContentsImpl*>(shell()->web_contents()); | |
59 } | |
60 }; | |
61 | |
62 namespace { | |
63 | |
64 const double kTolerance = 0.1; // In CSS pixels. | |
65 | |
66 double GetMainframeWindowBorder(const ToRenderFrameHost& adapter) { | |
67 double border; | |
68 const char kGetMainframeBorder[] = "window.domAutomationController.send(" | |
69 "window.outerWidth - window.innerWidth" | |
70 ");"; | |
71 EXPECT_TRUE( | |
72 ExecuteScriptAndExtractDouble(adapter, kGetMainframeBorder, &border)); | |
73 return border; | |
74 } | |
75 | |
76 double GetMainFrameZoomFactor(const ToRenderFrameHost& adapter, double border) { | |
77 const char kGetMainFrameZoomLevel[] = | |
78 "window.domAutomationController.send(" | |
79 "(window.outerWidth - %f)/window.innerWidth" | |
80 ");"; | |
81 double zoom_factor; | |
82 EXPECT_TRUE(ExecuteScriptAndExtractDouble( | |
83 adapter, base::StringPrintf(kGetMainFrameZoomLevel, border), | |
84 &zoom_factor)); | |
85 return zoom_factor; | |
86 } | |
87 | |
88 double GetSubframeWidth(const ToRenderFrameHost& adapter) { | |
89 double width; | |
90 EXPECT_TRUE(ExecuteScriptAndExtractDouble( | |
91 adapter, "window.domAutomationController.send(window.innerWidth);", | |
92 &width)); | |
93 return width; | |
94 } | |
95 | |
96 // This struct is used to track changes to subframes after a main frame zoom | |
97 // change, so that we can test subframe inner widths with assurance that all the | |
98 // changes have finished propagating. | |
99 struct FrameResizeObserver { | |
100 FrameResizeObserver(RenderFrameHost* host, | |
101 std::string label, | |
102 double inner_width, | |
103 double tolerance) | |
104 : frame_host(host), | |
105 msg_label(std::move(label)), | |
106 zoomed_correctly(false), | |
107 expected_inner_width(inner_width), | |
108 tolerance(tolerance) { | |
109 SetupOnResizeCallback(host, msg_label); | |
110 } | |
111 | |
112 void SetupOnResizeCallback(const ToRenderFrameHost& adapter, | |
113 const std::string& label) { | |
114 const char kOnResizeCallbackSetup[] = | |
115 "document.body.onresize = function(){" | |
116 " window.domAutomationController.setAutomationId(0);" | |
117 " window.domAutomationController.send('%s ' + window.innerWidth);" | |
118 "};"; | |
119 EXPECT_TRUE(ExecuteScript( | |
120 adapter, base::StringPrintf(kOnResizeCallbackSetup, label.c_str()))); | |
121 } | |
122 | |
123 void Check(const std::string& status_msg) { | |
124 if (status_msg.find(msg_label) != 0) | |
125 return; | |
126 | |
127 double inner_width = std::stod(status_msg.substr(msg_label.length() + 1)); | |
128 zoomed_correctly = std::abs(expected_inner_width - inner_width) < tolerance; | |
129 } | |
130 | |
131 FrameResizeObserver* toThis() {return this;} | |
132 | |
133 RenderFrameHost* frame_host; | |
134 std::string msg_label; | |
135 bool zoomed_correctly; | |
136 double expected_inner_width; | |
137 double tolerance; | |
138 }; | |
139 | |
140 void WaitAndCheckFrameZoom( | |
141 DOMMessageQueue& msg_queue, | |
142 std::vector<FrameResizeObserver>& frame_observers) { | |
143 std::string status; | |
144 while (msg_queue.WaitForMessage(&status)) { | |
145 // Strip the double quotes from the message. | |
146 status = status.substr(1, status.length() -2); | |
147 | |
148 bool all_zoomed_correctly = true; | |
149 | |
150 // Use auto& to operate on a reference, and not a copy. | |
151 for (auto& observer : frame_observers) { | |
152 observer.Check(status); | |
153 all_zoomed_correctly = all_zoomed_correctly && observer.zoomed_correctly; | |
154 } | |
155 | |
156 if (all_zoomed_correctly) | |
157 break; | |
158 } | |
159 } | |
160 | |
161 } // namespace anonymous | |
ncarter (slow)
2016/04/28 22:30:14
} // namespace
(no 'anonymous') https://google.g
wjmaclean
2016/04/29 13:40:41
Actually, is that right? The link shows a formatti
ncarter (slow)
2016/04/29 19:01:56
Because over the over-arching rule about "be consi
| |
162 | |
163 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesZoomProperly) { | |
164 std::string top_level_host("a.com"); | |
165 GURL main_url(embedded_test_server()->GetURL( | |
166 top_level_host, "/cross_site_iframe_factory.html?a(b(a))")); | |
167 EXPECT_TRUE(NavigateToURL(shell(), main_url)); | |
168 NavigationEntry* entry = | |
169 web_contents()->GetController().GetLastCommittedEntry(); | |
170 ASSERT_TRUE(entry); | |
171 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); | |
172 EXPECT_EQ(top_level_host, loaded_url.host()); | |
173 | |
174 FrameTreeNode* root = | |
175 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); | |
176 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host(); | |
177 RenderFrameHostImpl* grandchild = | |
178 root->child_at(0)->child_at(0)->current_frame_host(); | |
179 | |
180 // The following calls must be made when the page's scale factor = 1.0. | |
181 double scale_one_child_width = GetSubframeWidth(child); | |
182 double scale_one_grandchild_width = GetSubframeWidth(grandchild); | |
183 double main_frame_window_border = GetMainframeWindowBorder(web_contents()); | |
184 | |
185 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); | |
186 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); | |
187 EXPECT_EQ(0.0, default_zoom_level); | |
188 | |
189 EXPECT_DOUBLE_EQ( | |
190 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
191 | |
192 const double new_zoom_factor = 2.5; | |
193 { | |
194 DOMMessageQueue msg_queue; | |
195 | |
196 std::vector<FrameResizeObserver> frame_observers; | |
197 frame_observers.emplace_back(child, "child", | |
198 scale_one_child_width, kTolerance); | |
199 frame_observers.emplace_back(grandchild, "grandchild", | |
200 scale_one_grandchild_width, kTolerance); | |
201 | |
202 const double new_zoom_level = | |
203 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); | |
204 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level); | |
205 | |
206 WaitAndCheckFrameZoom(msg_queue, frame_observers); | |
207 } | |
208 | |
209 EXPECT_DOUBLE_EQ( | |
210 new_zoom_factor, | |
211 GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
212 } | |
213 | |
214 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframesDontZoomIndependently) { | |
215 std::string top_level_host("a.com"); | |
216 GURL main_url(embedded_test_server()->GetURL( | |
217 top_level_host, "/cross_site_iframe_factory.html?a(b(a))")); | |
218 EXPECT_TRUE(NavigateToURL(shell(), main_url)); | |
219 NavigationEntry* entry = | |
220 web_contents()->GetController().GetLastCommittedEntry(); | |
221 ASSERT_TRUE(entry); | |
222 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); | |
223 EXPECT_EQ(top_level_host, loaded_url.host()); | |
224 | |
225 FrameTreeNode* root = | |
226 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); | |
227 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host(); | |
228 RenderFrameHostImpl* grandchild = | |
229 root->child_at(0)->child_at(0)->current_frame_host(); | |
230 | |
231 // The following calls must be made when the page's scale factor = 1.0. | |
232 double scale_one_child_width = GetSubframeWidth(child); | |
233 double scale_one_grandchild_width = GetSubframeWidth(grandchild); | |
234 double main_frame_window_border = GetMainframeWindowBorder(web_contents()); | |
235 | |
236 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); | |
237 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); | |
238 EXPECT_EQ(0.0, default_zoom_level); | |
239 | |
240 EXPECT_DOUBLE_EQ( | |
241 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
242 | |
243 const double new_zoom_factor = 2.0; | |
244 const double new_zoom_level = | |
245 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); | |
246 | |
247 // This should not cause the nested iframe to change its zoom. | |
248 host_zoom_map->SetZoomLevelForHost("b.com", new_zoom_level); | |
249 | |
250 EXPECT_DOUBLE_EQ( | |
251 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
252 EXPECT_EQ(scale_one_child_width, GetSubframeWidth(child)); | |
253 EXPECT_EQ(scale_one_grandchild_width, GetSubframeWidth(grandchild)); | |
254 | |
255 // We exclude the remainder of this test on Android since Android does not | |
256 // set page zoom levels for loading pages. | |
257 // See RenderViewImpl::OnSetZoomLevelForLoadingURL(). | |
258 #if !defined(OS_ANDROID) | |
259 // When we navigate so that b.com is the top-level site, then it has the | |
260 // expected zoom. | |
261 GURL new_url = embedded_test_server()->GetURL("b.com", "/title1.html"); | |
262 EXPECT_TRUE(NavigateToURL(shell(), new_url)); | |
263 EXPECT_DOUBLE_EQ( | |
264 new_zoom_factor, | |
265 GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
266 #endif | |
267 } | |
268 | |
269 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, AllFramesGetDefaultZoom) { | |
270 std::string top_level_host("a.com"); | |
271 GURL main_url(embedded_test_server()->GetURL( | |
272 top_level_host, "/cross_site_iframe_factory.html?a(b(a))")); | |
273 EXPECT_TRUE(NavigateToURL(shell(), main_url)); | |
274 NavigationEntry* entry = | |
275 web_contents()->GetController().GetLastCommittedEntry(); | |
276 ASSERT_TRUE(entry); | |
277 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); | |
278 EXPECT_EQ(top_level_host, loaded_url.host()); | |
279 | |
280 FrameTreeNode* root = | |
281 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); | |
282 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host(); | |
283 RenderFrameHostImpl* grandchild = | |
284 root->child_at(0)->child_at(0)->current_frame_host(); | |
285 | |
286 // The following calls must be made when the page's scale factor = 1.0. | |
287 double scale_one_child_width = GetSubframeWidth(child); | |
288 double scale_one_grandchild_width = GetSubframeWidth(grandchild); | |
289 double main_frame_window_border = GetMainframeWindowBorder(web_contents()); | |
290 | |
291 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); | |
292 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); | |
293 EXPECT_EQ(0.0, default_zoom_level); | |
294 | |
295 EXPECT_DOUBLE_EQ( | |
296 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
297 | |
298 const double new_default_zoom_factor = 2.0; | |
299 { | |
300 DOMMessageQueue msg_queue; | |
301 | |
302 std::vector<FrameResizeObserver> frame_observers; | |
303 frame_observers.emplace_back(child, "child", | |
304 scale_one_child_width, kTolerance); | |
305 frame_observers.emplace_back(grandchild, "grandchild", | |
306 scale_one_grandchild_width, kTolerance); | |
307 | |
308 const double new_default_zoom_level = | |
309 default_zoom_level + ZoomFactorToZoomLevel(new_default_zoom_factor); | |
310 | |
311 host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0); | |
312 host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level); | |
313 | |
314 WaitAndCheckFrameZoom(msg_queue, frame_observers); | |
315 } | |
316 EXPECT_DOUBLE_EQ( | |
317 new_default_zoom_factor, | |
318 GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
319 } | |
320 | |
321 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SiblingFramesZoom) { | |
322 std::string top_level_host("a.com"); | |
323 GURL main_url(embedded_test_server()->GetURL( | |
324 top_level_host, "/cross_site_iframe_factory.html?a(b,b)")); | |
325 EXPECT_TRUE(NavigateToURL(shell(), main_url)); | |
326 NavigationEntry* entry = | |
327 web_contents()->GetController().GetLastCommittedEntry(); | |
328 ASSERT_TRUE(entry); | |
329 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); | |
330 EXPECT_EQ(top_level_host, loaded_url.host()); | |
331 | |
332 FrameTreeNode* root = | |
333 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); | |
334 RenderFrameHostImpl* child1 = root->child_at(0)->current_frame_host(); | |
335 RenderFrameHostImpl* child2 = root->child_at(1)->current_frame_host(); | |
336 | |
337 // The following calls must be made when the page's scale factor = 1.0. | |
338 double scale_one_child1_width = GetSubframeWidth(child1); | |
339 double scale_one_child2_width = GetSubframeWidth(child2); | |
340 double main_frame_window_border = GetMainframeWindowBorder(web_contents()); | |
341 | |
342 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); | |
343 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); | |
344 EXPECT_EQ(0.0, default_zoom_level); | |
345 | |
346 EXPECT_DOUBLE_EQ( | |
347 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
348 | |
349 const double new_zoom_factor = 2.5; | |
350 { | |
351 DOMMessageQueue msg_queue; | |
352 | |
353 std::vector<FrameResizeObserver> frame_observers; | |
354 frame_observers.emplace_back(child1, "child1", | |
355 scale_one_child1_width, kTolerance); | |
356 frame_observers.emplace_back(child2, "child2", | |
357 scale_one_child2_width, kTolerance); | |
358 | |
359 const double new_zoom_level = | |
360 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); | |
361 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level); | |
362 | |
363 WaitAndCheckFrameZoom(msg_queue, frame_observers); | |
364 } | |
365 | |
366 EXPECT_DOUBLE_EQ( | |
367 new_zoom_factor, | |
368 GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
369 } | |
370 | |
371 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframeRetainsZoomOnNavigation) { | |
372 std::string top_level_host("a.com"); | |
373 GURL main_url(embedded_test_server()->GetURL( | |
374 top_level_host, "/cross_site_iframe_factory.html?a(b)")); | |
375 EXPECT_TRUE(NavigateToURL(shell(), main_url)); | |
376 NavigationEntry* entry = | |
377 web_contents()->GetController().GetLastCommittedEntry(); | |
378 ASSERT_TRUE(entry); | |
379 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry); | |
380 EXPECT_EQ(top_level_host, loaded_url.host()); | |
381 | |
382 FrameTreeNode* root = | |
383 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root(); | |
384 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host(); | |
385 | |
386 // The following calls must be made when the page's scale factor = 1.0. | |
387 double scale_one_child_width = GetSubframeWidth(child); | |
388 double main_frame_window_border = GetMainframeWindowBorder(web_contents()); | |
389 | |
390 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents()); | |
391 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel(); | |
392 EXPECT_EQ(0.0, default_zoom_level); | |
393 | |
394 EXPECT_DOUBLE_EQ( | |
395 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
396 | |
397 const double new_zoom_factor = 0.5; | |
398 { | |
399 DOMMessageQueue msg_queue; | |
400 | |
401 std::vector<FrameResizeObserver> frame_observers; | |
402 frame_observers.emplace_back(child, "child", | |
403 scale_one_child_width, kTolerance); | |
404 | |
405 const double new_zoom_level = | |
406 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor); | |
407 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level); | |
408 | |
409 WaitAndCheckFrameZoom(msg_queue, frame_observers); | |
410 } | |
411 | |
412 EXPECT_DOUBLE_EQ( | |
413 new_zoom_factor, | |
414 GetMainFrameZoomFactor(web_contents(), main_frame_window_border)); | |
415 | |
416 // Navigate child frame cross site, and make sure zoom is the same. | |
417 TestNavigationObserver observer(web_contents()); | |
418 GURL url = embedded_test_server()->GetURL("c.com", "/title1.html"); | |
419 NavigateFrameToURL(root->child_at(0), url); | |
420 EXPECT_TRUE(observer.last_navigation_succeeded()); | |
421 EXPECT_EQ(url, observer.last_navigation_url()); | |
422 | |
423 // Check that the child frame maintained the same scale after navigating | |
424 // cross-site. | |
425 double new_child_width = | |
426 GetSubframeWidth(root->child_at(0)->current_frame_host()); | |
427 EXPECT_EQ(scale_one_child_width, new_child_width); | |
428 } | |
429 | |
430 } // namespace content | |
OLD | NEW |