Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Side by Side Diff: content/browser/iframe_zoom_browsertest.cc

Issue 1804023002: Fix page zoom to be frame-centric for out-of-process frames. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address comments, exclude part of test since not supported on Android. Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 both (1) a change in their frame rect, and (2) a change in their
alexmos 2016/04/27 23:39:51 nit: "have" before both? (seems like a word is mis
wjmaclean 2016/04/28 13:20:11 Done.
41 // 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
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)
alexmos 2016/04/27 23:39:51 So does this mean that on Android, despite having
wjmaclean 2016/04/28 13:20:11 I didn't end up trying it in the simulator, but ye
alexmos 2016/04/28 17:32:12 Acknowledged.
259 // When we navigate so that b.com is the top-level site, then it has the
260 // expected zoom.
261 TestNavigationObserver observer(web_contents());
alexmos 2016/04/27 23:39:51 NavigateToURL uses a TestNavigationObserver intern
wjmaclean 2016/04/28 13:20:11 I guess I didn't realize NavigateToURL had one int
262 GURL new_url = embedded_test_server()->GetURL("b.com", "/title1.html");
263 EXPECT_TRUE(NavigateToURL(shell(), new_url));
264 EXPECT_TRUE(observer.last_navigation_succeeded());
265 EXPECT_EQ(new_url, observer.last_navigation_url());
266 EXPECT_DOUBLE_EQ(
267 new_zoom_factor,
268 GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
269 #endif
270 }
271
272 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, AllFramesGetDefaultZoom) {
273 std::string top_level_host("a.com");
274 GURL main_url(embedded_test_server()->GetURL(
275 top_level_host, "/cross_site_iframe_factory.html?a(b(a))"));
276 EXPECT_TRUE(NavigateToURL(shell(), main_url));
277 NavigationEntry* entry =
278 web_contents()->GetController().GetLastCommittedEntry();
279 ASSERT_TRUE(entry);
280 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
281 EXPECT_EQ(top_level_host, loaded_url.host());
282
283 FrameTreeNode* root =
284 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
285 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
286 RenderFrameHostImpl* grandchild =
287 root->child_at(0)->child_at(0)->current_frame_host();
288
289 // The following calls must be made when the page's scale factor = 1.0.
290 double scale_one_child_width = GetSubframeWidth(child);
291 double scale_one_grandchild_width = GetSubframeWidth(grandchild);
292 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
293
294 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
295 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
296 EXPECT_EQ(0.0, default_zoom_level);
297
298 EXPECT_DOUBLE_EQ(
299 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
300
301 const double new_default_zoom_factor = 2.0;
302 {
303 DOMMessageQueue msg_queue;
304
305 std::vector<FrameResizeObserver> frame_observers;
306 frame_observers.emplace_back(child, "child",
307 scale_one_child_width, kTolerance);
308 frame_observers.emplace_back(grandchild, "grandchild",
309 scale_one_grandchild_width, kTolerance);
310
311 const double new_default_zoom_level =
312 default_zoom_level + ZoomFactorToZoomLevel(new_default_zoom_factor);
313
314 host_zoom_map->SetZoomLevelForHost("b.com", new_default_zoom_level + 1.0);
315 host_zoom_map->SetDefaultZoomLevel(new_default_zoom_level);
316
317 WaitAndCheckFrameZoom(msg_queue, frame_observers);
318 }
319 EXPECT_DOUBLE_EQ(
320 new_default_zoom_factor,
321 GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
322 }
323
324 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SiblingFramesZoom) {
325 std::string top_level_host("a.com");
326 GURL main_url(embedded_test_server()->GetURL(
327 top_level_host, "/cross_site_iframe_factory.html?a(b,b)"));
328 EXPECT_TRUE(NavigateToURL(shell(), main_url));
329 NavigationEntry* entry =
330 web_contents()->GetController().GetLastCommittedEntry();
331 ASSERT_TRUE(entry);
332 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
333 EXPECT_EQ(top_level_host, loaded_url.host());
334
335 FrameTreeNode* root =
336 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
337 RenderFrameHostImpl* child1 = root->child_at(0)->current_frame_host();
338 RenderFrameHostImpl* child2 = root->child_at(1)->current_frame_host();
339
340 // The following calls must be made when the page's scale factor = 1.0.
341 double scale_one_child1_width = GetSubframeWidth(child1);
342 double scale_one_child2_width = GetSubframeWidth(child2);
343 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
344
345 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
346 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
347 EXPECT_EQ(0.0, default_zoom_level);
348
349 EXPECT_DOUBLE_EQ(
350 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
351
352 const double new_zoom_factor = 2.5;
353 {
354 DOMMessageQueue msg_queue;
355
356 std::vector<FrameResizeObserver> frame_observers;
357 frame_observers.emplace_back(child1, "child1",
358 scale_one_child1_width, kTolerance);
359 frame_observers.emplace_back(child2, "child2",
360 scale_one_child2_width, kTolerance);
361
362 const double new_zoom_level =
363 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
364 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
365
366 WaitAndCheckFrameZoom(msg_queue, frame_observers);
367 }
368
369 EXPECT_DOUBLE_EQ(
370 new_zoom_factor,
371 GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
372 }
373
374 IN_PROC_BROWSER_TEST_F(IFrameZoomBrowserTest, SubframeRetainsZoomOnNavigation) {
375 std::string top_level_host("a.com");
376 GURL main_url(embedded_test_server()->GetURL(
377 top_level_host, "/cross_site_iframe_factory.html?a(b)"));
378 EXPECT_TRUE(NavigateToURL(shell(), main_url));
379 NavigationEntry* entry =
380 web_contents()->GetController().GetLastCommittedEntry();
381 ASSERT_TRUE(entry);
382 GURL loaded_url = HostZoomMap::GetURLFromEntry(entry);
383 EXPECT_EQ(top_level_host, loaded_url.host());
384
385 FrameTreeNode* root =
386 static_cast<WebContentsImpl*>(web_contents())->GetFrameTree()->root();
387 RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
388
389 // The following calls must be made when the page's scale factor = 1.0.
390 double scale_one_child_width = GetSubframeWidth(child);
391 double main_frame_window_border = GetMainframeWindowBorder(web_contents());
392
393 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents());
394 double default_zoom_level = host_zoom_map->GetDefaultZoomLevel();
395 EXPECT_EQ(0.0, default_zoom_level);
396
397 EXPECT_DOUBLE_EQ(
398 1.0, GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
399
400 const double new_zoom_factor = 0.5;
401 {
402 DOMMessageQueue msg_queue;
403
404 std::vector<FrameResizeObserver> frame_observers;
405 frame_observers.emplace_back(child, "child",
406 scale_one_child_width, kTolerance);
407
408 const double new_zoom_level =
409 default_zoom_level + ZoomFactorToZoomLevel(new_zoom_factor);
410 host_zoom_map->SetZoomLevelForHost(top_level_host, new_zoom_level);
411
412 WaitAndCheckFrameZoom(msg_queue, frame_observers);
413 }
414
415 EXPECT_DOUBLE_EQ(
416 new_zoom_factor,
417 GetMainFrameZoomFactor(web_contents(), main_frame_window_border));
418
419 // Navigate child frame cross site, and make sure zoom is the same.
420 TestNavigationObserver observer(web_contents());
421 GURL url = embedded_test_server()->GetURL("c.com", "/title1.html");
422 NavigateFrameToURL(root->child_at(0), url);
423 EXPECT_TRUE(observer.last_navigation_succeeded());
424 EXPECT_EQ(url, observer.last_navigation_url());
425
426 // Check that the child frame maintained the same scale after navigating
427 // cross-site.
428 double new_child_width =
429 GetSubframeWidth(root->child_at(0)->current_frame_host());
430 EXPECT_EQ(scale_one_child_width, new_child_width);
431 }
432
433 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698