1 /*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "third_party/blink/public/web/web_frame.h"
32
33 #include <initializer_list>
34 #include <limits>
35 #include <memory>
36
37 #include "base/callback_helpers.h"
38 #include "base/optional.h"
39 #include "base/stl_util.h"
40 #include "base/unguessable_token.h"
41 #include "build/build_config.h"
42 #include "build/chromeos_buildflags.h"
43 #include "cc/input/overscroll_behavior.h"
44 #include "cc/layers/picture_layer.h"
45 #include "cc/paint/paint_op_buffer.h"
46 #include "cc/trees/layer_tree_host.h"
47 #include "cc/trees/scroll_node.h"
48 #include "mojo/public/cpp/bindings/pending_remote.h"
49 #include "mojo/public/cpp/bindings/receiver.h"
50 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
51 #include "mojo/public/cpp/system/data_pipe_drainer.h"
52 #include "mojo/public/cpp/system/data_pipe_utils.h"
53 #include "skia/public/mojom/skcolor.mojom-blink.h"
54 #include "testing/gmock/include/gmock/gmock.h"
55 #include "testing/gtest/include/gtest/gtest.h"
56 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
57 #include "third_party/blink/public/common/context_menu_data/edit_flags.h"
58 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
59 #include "third_party/blink/public/common/input/web_keyboard_event.h"
60 #include "third_party/blink/public/common/loader/referrer_utils.h"
61 #include "third_party/blink/public/common/messaging/transferable_message.h"
62 #include "third_party/blink/public/common/page/launching_process_state.h"
63 #include "third_party/blink/public/common/widget/device_emulation_params.h"
64 #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h"
65 #include "third_party/blink/public/mojom/blob/data_element.mojom-blink.h"
66 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
67 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
68 #include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom-blink.h"
69 #include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h"
70 #include "third_party/blink/public/mojom/page_state/page_state.mojom-blink.h"
71 #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
72 #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h"
73 #include "third_party/blink/public/platform/web_cache.h"
74 #include "third_party/blink/public/platform/web_security_origin.h"
75 #include "third_party/blink/public/platform/web_url.h"
76 #include "third_party/blink/public/platform/web_url_loader_client.h"
77 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
78 #include "third_party/blink/public/platform/web_url_response.h"
79 #include "third_party/blink/public/web/web_console_message.h"
80 #include "third_party/blink/public/web/web_context_menu_data.h"
81 #include "third_party/blink/public/web/web_document.h"
82 #include "third_party/blink/public/web/web_document_loader.h"
83 #include "third_party/blink/public/web/web_form_element.h"
84 #include "third_party/blink/public/web/web_frame_content_dumper.h"
85 #include "third_party/blink/public/web/web_frame_widget.h"
86 #include "third_party/blink/public/web/web_history_item.h"
87 #include "third_party/blink/public/web/web_local_frame.h"
88 #include "third_party/blink/public/web/web_local_frame_client.h"
89 #include "third_party/blink/public/web/web_navigation_timings.h"
90 #include "third_party/blink/public/web/web_print_page_description.h"
91 #include "third_party/blink/public/web/web_print_params.h"
92 #include "third_party/blink/public/web/web_range.h"
93 #include "third_party/blink/public/web/web_remote_frame.h"
94 #include "third_party/blink/public/web/web_script_execution_callback.h"
95 #include "third_party/blink/public/web/web_script_source.h"
96 #include "third_party/blink/public/web/web_searchable_form_data.h"
97 #include "third_party/blink/public/web/web_security_policy.h"
98 #include "third_party/blink/public/web/web_settings.h"
99 #include "third_party/blink/public/web/web_text_check_client.h"
100 #include "third_party/blink/public/web/web_text_checking_completion.h"
101 #include "third_party/blink/public/web/web_text_checking_result.h"
102 #include "third_party/blink/public/web/web_view_client.h"
103 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
104 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h"
105 #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h"
106 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
107 #include "third_party/blink/renderer/bindings/core/v8/v8_node.h"
108 #include "third_party/blink/renderer/bindings/core/v8/v8_pointer_event_init.h"
109 #include "third_party/blink/renderer/core/clipboard/data_transfer.h"
110 #include "third_party/blink/renderer/core/clipboard/system_clipboard.h"
111 #include "third_party/blink/renderer/core/css/css_page_rule.h"
112 #include "third_party/blink/renderer/core/css/media_values.h"
113 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
114 #include "third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h"
115 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
116 #include "third_party/blink/renderer/core/dom/document.h"
117 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
118 #include "third_party/blink/renderer/core/dom/range.h"
119 #include "third_party/blink/renderer/core/editing/editor.h"
120 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
121 #include "third_party/blink/renderer/core/editing/finder/text_finder.h"
122 #include "third_party/blink/renderer/core/editing/frame_selection.h"
123 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
124 #include "third_party/blink/renderer/core/editing/selection_template.h"
125 #include "third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller.h"
126 #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
127 #include "third_party/blink/renderer/core/events/message_event.h"
128 #include "third_party/blink/renderer/core/events/mouse_event.h"
129 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
130 #include "third_party/blink/renderer/core/frame/browser_controls.h"
131 #include "third_party/blink/renderer/core/frame/event_handler_registry.h"
132 #include "third_party/blink/renderer/core/frame/find_in_page.h"
133 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
134 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
135 #include "third_party/blink/renderer/core/frame/local_frame.h"
136 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
137 #include "third_party/blink/renderer/core/frame/remote_frame.h"
138 #include "third_party/blink/renderer/core/frame/settings.h"
139 #include "third_party/blink/renderer/core/frame/viewport_data.h"
140 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
141 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
142 #include "third_party/blink/renderer/core/frame/web_remote_frame_impl.h"
143 #include "third_party/blink/renderer/core/frame/web_view_frame_widget.h"
144 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
145 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
146 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
147 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
148 #include "third_party/blink/renderer/core/html/html_body_element.h"
149 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
150 #include "third_party/blink/renderer/core/html/image_document.h"
151 #include "third_party/blink/renderer/core/html/media/html_video_element.h"
152 #include "third_party/blink/renderer/core/input/event_handler.h"
153 #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
154 #include "third_party/blink/renderer/core/layout/hit_test_result.h"
155 #include "third_party/blink/renderer/core/layout/layout_view.h"
156 #include "third_party/blink/renderer/core/loader/document_loader.h"
157 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
158 #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
159 #include "third_party/blink/renderer/core/page/chrome_client.h"
160 #include "third_party/blink/renderer/core/page/drag_image.h"
161 #include "third_party/blink/renderer/core/page/page.h"
162 #include "third_party/blink/renderer/core/page/scoped_page_pauser.h"
163 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
164 #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
165 #include "third_party/blink/renderer/core/paint/paint_layer.h"
166 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
167 #include "third_party/blink/renderer/core/scroll/scrollbar.h"
168 #include "third_party/blink/renderer/core/scroll/scrollbar_test_suite.h"
169 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
170 #include "third_party/blink/renderer/core/testing/fake_local_frame_host.h"
171 #include "third_party/blink/renderer/core/testing/fake_remote_frame_host.h"
172 #include "third_party/blink/renderer/core/testing/fake_remote_main_frame_host.h"
173 #include "third_party/blink/renderer/core/testing/mock_clipboard_host.h"
174 #include "third_party/blink/renderer/core/testing/null_execution_context.h"
175 #include "third_party/blink/renderer/core/testing/page_test_base.h"
176 #include "third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.h"
177 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
178 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
179 #include "third_party/blink/renderer/platform/bindings/microtask.h"
180 #include "third_party/blink/renderer/platform/blob/testing/fake_blob.h"
181 #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
182 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
183 #include "third_party/blink/renderer/platform/keyboard_codes.h"
184 #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h"
185 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
186 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
187 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
188 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
189 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
190 #include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
191 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
192 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
193 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
194 #include "third_party/blink/renderer/platform/weborigin/kurl_hash.h"
195 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
196 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
197 #include "third_party/blink/renderer/platform/wtf/forward.h"
198 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
199 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
200 #include "ui/base/ime/mojom/text_input_state.mojom-blink.h"
201 #include "ui/base/mojom/ui_base_types.mojom-shared.h"
202 #include "ui/events/keycodes/dom/dom_key.h"
203 #include "ui/gfx/transform.h"
204 #include "v8/include/v8.h"
205
206 using blink::mojom::SelectionMenuBehavior;
207 using blink::test::RunPendingTasks;
208 using blink::url_test_helpers::ToKURL;
209 using testing::_;
210 using testing::ElementsAre;
211 using testing::Mock;
212
213 namespace blink {
214
215 namespace {
216
GetScrollNode(const cc::Layer * layer)217 const cc::ScrollNode* GetScrollNode(const cc::Layer* layer) {
218 return layer->layer_tree_host()
219 ->property_trees()
220 ->scroll_tree.FindNodeFromElementId(layer->element_id());
221 }
222
GetHTMLStringForReferrerPolicy(const std::string & meta_policy,const std::string & referrer_policy)223 std::string GetHTMLStringForReferrerPolicy(const std::string& meta_policy,
224 const std::string& referrer_policy) {
225 std::string meta_tag =
226 meta_policy.empty()
227 ? ""
228 : base::StringPrintf("<meta name='referrer' content='%s'>",
229 meta_policy.c_str());
230 std::string referrer_policy_attr =
231 referrer_policy.empty()
232 ? ""
233 : base::StringPrintf("referrerpolicy='%s'", referrer_policy.c_str());
234 return base::StringPrintf(
235 "<!DOCTYPE html>"
236 "%s"
237 "<a id='dl' href='download_test' download='foo' %s>Click me</a>"
238 "<script>"
239 "(function () {"
240 " var evt = document.createEvent('MouseEvent');"
241 " evt.initMouseEvent('click', true, true);"
242 " document.getElementById('dl').dispatchEvent(evt);"
243 "})();"
244 "</script>",
245 meta_tag.c_str(), referrer_policy_attr.c_str());
246 }
247 } // namespace
248
249 const int kTouchPointPadding = 32;
250
251 const cc::OverscrollBehavior kOverscrollBehaviorAuto =
252 cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kAuto);
253
254 const cc::OverscrollBehavior kOverscrollBehaviorContain =
255 cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kContain);
256
257 const cc::OverscrollBehavior kOverscrollBehaviorNone =
258 cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kNone);
259
260 class WebFrameTest : public testing::Test {
261 protected:
WebFrameTest()262 WebFrameTest()
263 : base_url_("http://internal.test/"),
264 not_base_url_("http://external.test/"),
265 chrome_url_("chrome://") {}
266
~WebFrameTest()267 ~WebFrameTest() override {
268 url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
269 }
270
DisableRendererSchedulerThrottling()271 void DisableRendererSchedulerThrottling() {
272 // Make sure that the RendererScheduler is foregrounded to avoid getting
273 // throttled.
274 if (kLaunchingProcessIsBackgrounded) {
275 ThreadScheduler::Current()
276 ->GetWebMainThreadSchedulerForTest()
277 ->SetRendererBackgrounded(false);
278 }
279 }
280
RegisterMockedHttpURLLoad(const std::string & file_name)281 void RegisterMockedHttpURLLoad(const std::string& file_name) {
282 // TODO(crbug.com/751425): We should use the mock functionality
283 // via the WebViewHelper instance in each test case.
284 RegisterMockedURLLoadFromBase(base_url_, file_name);
285 }
286
RegisterMockedChromeURLLoad(const std::string & file_name)287 void RegisterMockedChromeURLLoad(const std::string& file_name) {
288 // TODO(crbug.com/751425): We should use the mock functionality
289 // via the WebViewHelper instance in each test case.
290 RegisterMockedURLLoadFromBase(chrome_url_, file_name);
291 }
292
RegisterMockedURLLoadFromBase(const std::string & base_url,const std::string & file_name)293 void RegisterMockedURLLoadFromBase(const std::string& base_url,
294 const std::string& file_name) {
295 // TODO(crbug.com/751425): We should use the mock functionality
296 // via the WebViewHelper instance in each test case.
297 url_test_helpers::RegisterMockedURLLoadFromBase(
298 WebString::FromUTF8(base_url), test::CoreTestDataPath(),
299 WebString::FromUTF8(file_name));
300 }
301
RegisterMockedURLLoadWithCustomResponse(const WebURL & full_url,const WebString & file_path,WebURLResponse response)302 void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url,
303 const WebString& file_path,
304 WebURLResponse response) {
305 url_test_helpers::RegisterMockedURLLoadWithCustomResponse(
306 full_url, file_path, response);
307 }
308
RegisterMockedHttpURLLoadWithCSP(const std::string & file_name,const std::string & csp,bool report_only=false)309 void RegisterMockedHttpURLLoadWithCSP(const std::string& file_name,
310 const std::string& csp,
311 bool report_only = false) {
312 WebURLResponse response;
313 response.SetMimeType("text/html");
314 response.AddHttpHeaderField(
315 report_only ? WebString("Content-Security-Policy-Report-Only")
316 : WebString("Content-Security-Policy"),
317 WebString::FromUTF8(csp));
318 std::string full_string = base_url_ + file_name;
319 RegisterMockedURLLoadWithCustomResponse(
320 ToKURL(full_string),
321 test::CoreTestDataPath(WebString::FromUTF8(file_name)), response);
322 }
323
RegisterMockedHttpURLLoadWithMimeType(const std::string & file_name,const std::string & mime_type)324 void RegisterMockedHttpURLLoadWithMimeType(const std::string& file_name,
325 const std::string& mime_type) {
326 // TODO(crbug.com/751425): We should use the mock functionality
327 // via the WebViewHelper instance in each test case.
328 url_test_helpers::RegisterMockedURLLoadFromBase(
329 WebString::FromUTF8(base_url_), test::CoreTestDataPath(),
330 WebString::FromUTF8(file_name), WebString::FromUTF8(mime_type));
331 }
332
ConfigureCompositingWebView(WebSettings * settings)333 static void ConfigureCompositingWebView(WebSettings* settings) {
334 settings->SetPreferCompositingToLCDTextEnabled(true);
335 }
336
ConfigureAndroid(WebSettings * settings)337 static void ConfigureAndroid(WebSettings* settings) {
338 settings->SetViewportMetaEnabled(true);
339 settings->SetViewportEnabled(true);
340 settings->SetMainFrameResizesAreOrientationChanges(true);
341 settings->SetShrinksViewportContentToFit(true);
342 settings->SetViewportStyle(mojom::blink::ViewportStyle::kMobile);
343 }
344
ConfigureLoadsImagesAutomatically(WebSettings * settings)345 static void ConfigureLoadsImagesAutomatically(WebSettings* settings) {
346 settings->SetLoadsImagesAutomatically(true);
347 }
348
InitializeTextSelectionWebView(const std::string & url,frame_test_helpers::WebViewHelper * web_view_helper)349 void InitializeTextSelectionWebView(
350 const std::string& url,
351 frame_test_helpers::WebViewHelper* web_view_helper) {
352 web_view_helper->InitializeAndLoad(url);
353 web_view_helper->GetWebView()->GetSettings()->SetDefaultFontSize(12);
354 web_view_helper->Resize(gfx::Size(640, 480));
355 }
356
NodeImageTestSetup(frame_test_helpers::WebViewHelper * web_view_helper,const std::string & testcase)357 std::unique_ptr<DragImage> NodeImageTestSetup(
358 frame_test_helpers::WebViewHelper* web_view_helper,
359 const std::string& testcase) {
360 RegisterMockedHttpURLLoad("nodeimage.html");
361 web_view_helper->InitializeAndLoad(base_url_ + "nodeimage.html");
362 web_view_helper->Resize(gfx::Size(640, 480));
363 auto* frame =
364 To<LocalFrame>(web_view_helper->GetWebView()->GetPage()->MainFrame());
365 DCHECK(frame);
366 Element* element = frame->GetDocument()->getElementById(testcase.c_str());
367 return DataTransfer::NodeImage(*frame, *element);
368 }
369
RemoveElementById(WebLocalFrameImpl * frame,const AtomicString & id)370 void RemoveElementById(WebLocalFrameImpl* frame, const AtomicString& id) {
371 Element* element = frame->GetFrame()->GetDocument()->getElementById(id);
372 DCHECK(element);
373 element->remove();
374 }
375
376 // Both sets the inner html and runs the document lifecycle.
InitializeWithHTML(LocalFrame & frame,const String & html_content)377 void InitializeWithHTML(LocalFrame& frame, const String& html_content) {
378 frame.GetDocument()->body()->setInnerHTML(html_content);
379 frame.GetDocument()->View()->UpdateAllLifecyclePhasesForTest();
380 }
381
382 void SwapAndVerifyFirstChildConsistency(const char* const message,
383 WebFrame* parent,
384 WebFrame* new_child);
385 void SwapAndVerifyMiddleChildConsistency(const char* const message,
386 WebFrame* parent,
387 WebFrame* new_child);
388 void SwapAndVerifyLastChildConsistency(const char* const message,
389 WebFrame* parent,
390 WebFrame* new_child);
391 void SwapAndVerifySubframeConsistency(const char* const message,
392 WebFrame* parent,
393 WebFrame* new_child);
394
NumMarkersInRange(const Document * document,const EphemeralRange & range,DocumentMarker::MarkerTypes marker_types)395 int NumMarkersInRange(const Document* document,
396 const EphemeralRange& range,
397 DocumentMarker::MarkerTypes marker_types) {
398 Node* start_container = range.StartPosition().ComputeContainerNode();
399 unsigned start_offset = static_cast<unsigned>(
400 range.StartPosition().ComputeOffsetInContainerNode());
401
402 Node* end_container = range.EndPosition().ComputeContainerNode();
403 unsigned end_offset = static_cast<unsigned>(
404 range.EndPosition().ComputeOffsetInContainerNode());
405
406 int node_count = 0;
407 for (Node& node : range.Nodes()) {
408 const DocumentMarkerVector& markers_in_node =
409 document->Markers().MarkersFor(To<Text>(node), marker_types);
410 node_count += std::count_if(
411 markers_in_node.begin(), markers_in_node.end(),
412 [start_offset, end_offset, &node, &start_container,
413 &end_container](const DocumentMarker* marker) {
414 if (node == start_container && marker->EndOffset() <= start_offset)
415 return false;
416 if (node == end_container && marker->StartOffset() >= end_offset)
417 return false;
418 return true;
419 });
420 }
421
422 return node_count;
423 }
424
UpdateAllLifecyclePhases(WebViewImpl * web_view)425 void UpdateAllLifecyclePhases(WebViewImpl* web_view) {
426 web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
427 DocumentUpdateReason::kTest);
428 }
429
GetElementAndCaretBoundsForFocusedEditableElement(frame_test_helpers::WebViewHelper & helper,IntRect & element_bounds,IntRect & caret_bounds)430 static void GetElementAndCaretBoundsForFocusedEditableElement(
431 frame_test_helpers::WebViewHelper& helper,
432 IntRect& element_bounds,
433 IntRect& caret_bounds) {
434 Element* element = helper.GetWebView()->FocusedElement();
435 gfx::Rect caret_in_viewport, unused;
436 helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
437 caret_in_viewport, unused);
438 caret_bounds =
439 helper.GetWebView()->GetPage()->GetVisualViewport().ViewportToRootFrame(
440 IntRect(caret_in_viewport));
441 element_bounds = element->GetDocument().View()->ConvertToRootFrame(
442 PixelSnappedIntRect(element->Node::BoundingBox()));
443 }
444
445 std::string base_url_;
446 std::string not_base_url_;
447 std::string chrome_url_;
448 };
449
TEST_F(WebFrameTest,ContentText)450 TEST_F(WebFrameTest, ContentText) {
451 RegisterMockedHttpURLLoad("iframes_test.html");
452 RegisterMockedHttpURLLoad("visible_iframe.html");
453 RegisterMockedHttpURLLoad("invisible_iframe.html");
454 RegisterMockedHttpURLLoad("zero_sized_iframe.html");
455
456 frame_test_helpers::WebViewHelper web_view_helper;
457 web_view_helper.InitializeAndLoad(base_url_ + "iframes_test.html");
458
459 // Now retrieve the frames text and test it only includes visible elements.
460 std::string content = WebFrameContentDumper::DumpWebViewAsText(
461 web_view_helper.GetWebView(), 1024)
462 .Utf8();
463 EXPECT_NE(std::string::npos, content.find(" visible paragraph"));
464 EXPECT_NE(std::string::npos, content.find(" visible iframe"));
465 EXPECT_EQ(std::string::npos, content.find(" invisible pararaph"));
466 EXPECT_EQ(std::string::npos, content.find(" invisible iframe"));
467 EXPECT_EQ(std::string::npos, content.find("iframe with zero size"));
468 }
469
TEST_F(WebFrameTest,FrameForEnteredContext)470 TEST_F(WebFrameTest, FrameForEnteredContext) {
471 RegisterMockedHttpURLLoad("iframes_test.html");
472 RegisterMockedHttpURLLoad("visible_iframe.html");
473 RegisterMockedHttpURLLoad("invisible_iframe.html");
474 RegisterMockedHttpURLLoad("zero_sized_iframe.html");
475
476 frame_test_helpers::WebViewHelper web_view_helper;
477 web_view_helper.InitializeAndLoad(base_url_ + "iframes_test.html");
478
479 v8::HandleScope scope(v8::Isolate::GetCurrent());
480 EXPECT_EQ(web_view_helper.GetWebView()->MainFrame(),
481 WebLocalFrame::FrameForContext(web_view_helper.GetWebView()
482 ->MainFrameImpl()
483 ->MainWorldScriptContext()));
484 EXPECT_EQ(web_view_helper.GetWebView()->MainFrame()->FirstChild(),
485 WebLocalFrame::FrameForContext(web_view_helper.GetWebView()
486 ->MainFrame()
487 ->FirstChild()
488 ->ToWebLocalFrame()
489 ->MainWorldScriptContext()));
490 }
491
492 class ScriptExecutionCallbackHelper : public WebScriptExecutionCallback {
493 public:
ScriptExecutionCallbackHelper(v8::Local<v8::Context> context)494 explicit ScriptExecutionCallbackHelper(v8::Local<v8::Context> context)
495 : did_complete_(false), bool_value_(false), context_(context) {}
496 ~ScriptExecutionCallbackHelper() override = default;
497
DidComplete() const498 bool DidComplete() const { return did_complete_; }
StringValue() const499 const String& StringValue() const { return string_value_; }
BoolValue()500 bool BoolValue() { return bool_value_; }
501
502 private:
503 // WebScriptExecutionCallback:
Completed(const WebVector<v8::Local<v8::Value>> & values)504 void Completed(const WebVector<v8::Local<v8::Value>>& values) override {
505 did_complete_ = true;
506 if (!values.empty()) {
507 if (values[0]->IsString()) {
508 string_value_ =
509 ToCoreString(values[0]->ToString(context_).ToLocalChecked());
510 } else if (values[0]->IsBoolean()) {
511 bool_value_ = values[0].As<v8::Boolean>()->Value();
512 }
513 }
514 }
515
516 bool did_complete_;
517 String string_value_;
518 bool bool_value_;
519 v8::Local<v8::Context> context_;
520 };
521
TEST_F(WebFrameTest,RequestExecuteScript)522 TEST_F(WebFrameTest, RequestExecuteScript) {
523 RegisterMockedHttpURLLoad("foo.html");
524
525 frame_test_helpers::WebViewHelper web_view_helper;
526 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
527
528 v8::HandleScope scope(v8::Isolate::GetCurrent());
529 ScriptExecutionCallbackHelper callback_helper(
530 web_view_helper.LocalMainFrame()->MainWorldScriptContext());
531 web_view_helper.GetWebView()
532 ->MainFrameImpl()
533 ->RequestExecuteScriptAndReturnValue(
534 WebScriptSource(WebString("'hello';")), false, &callback_helper);
535 RunPendingTasks();
536 EXPECT_TRUE(callback_helper.DidComplete());
537 EXPECT_EQ("hello", callback_helper.StringValue());
538 }
539
TEST_F(WebFrameTest,SuspendedRequestExecuteScript)540 TEST_F(WebFrameTest, SuspendedRequestExecuteScript) {
541 RegisterMockedHttpURLLoad("foo.html");
542
543 frame_test_helpers::WebViewHelper web_view_helper;
544 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
545
546 v8::HandleScope scope(v8::Isolate::GetCurrent());
547 ScriptExecutionCallbackHelper callback_helper(
548 web_view_helper.LocalMainFrame()->MainWorldScriptContext());
549
550 // Suspend scheduled tasks so the script doesn't run.
551 web_view_helper.GetWebView()->GetPage()->SetPaused(true);
552 web_view_helper.GetWebView()
553 ->MainFrameImpl()
554 ->RequestExecuteScriptAndReturnValue(
555 WebScriptSource(WebString("'hello';")), false, &callback_helper);
556 RunPendingTasks();
557 EXPECT_FALSE(callback_helper.DidComplete());
558
559 web_view_helper.Reset();
560 EXPECT_TRUE(callback_helper.DidComplete());
561 EXPECT_EQ(String(), callback_helper.StringValue());
562 }
563
TEST_F(WebFrameTest,RequestExecuteV8Function)564 TEST_F(WebFrameTest, RequestExecuteV8Function) {
565 RegisterMockedHttpURLLoad("foo.html");
566
567 frame_test_helpers::WebViewHelper web_view_helper;
568 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
569
570 auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
571 EXPECT_EQ(2, info.Length());
572 EXPECT_TRUE(info[0]->IsUndefined());
573 info.GetReturnValue().Set(info[1]);
574 };
575
576 v8::Isolate* isolate = v8::Isolate::GetCurrent();
577 v8::HandleScope scope(isolate);
578 v8::Local<v8::Context> context =
579 web_view_helper.LocalMainFrame()->MainWorldScriptContext();
580 ScriptExecutionCallbackHelper callback_helper(context);
581 v8::Local<v8::Function> function =
582 v8::Function::New(context, callback).ToLocalChecked();
583 v8::Local<v8::Value> args[] = {v8::Undefined(isolate),
584 V8String(isolate, "hello")};
585 web_view_helper.GetWebView()
586 ->MainFrame()
587 ->ToWebLocalFrame()
588 ->RequestExecuteV8Function(context, function, v8::Undefined(isolate),
589 base::size(args), args, &callback_helper);
590 RunPendingTasks();
591 EXPECT_TRUE(callback_helper.DidComplete());
592 EXPECT_EQ("hello", callback_helper.StringValue());
593 }
594
TEST_F(WebFrameTest,RequestExecuteV8FunctionWhileSuspended)595 TEST_F(WebFrameTest, RequestExecuteV8FunctionWhileSuspended) {
596 DisableRendererSchedulerThrottling();
597 RegisterMockedHttpURLLoad("foo.html");
598
599 frame_test_helpers::WebViewHelper web_view_helper;
600 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
601
602 auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
603 info.GetReturnValue().Set(V8String(info.GetIsolate(), "hello"));
604 };
605
606 v8::HandleScope scope(v8::Isolate::GetCurrent());
607 v8::Local<v8::Context> context =
608 web_view_helper.LocalMainFrame()->MainWorldScriptContext();
609
610 // Suspend scheduled tasks so the script doesn't run.
611 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
612 web_view_helper.GetWebView()->GetPage()->SetPaused(true);
613
614 ScriptExecutionCallbackHelper callback_helper(context);
615 v8::Local<v8::Function> function =
616 v8::Function::New(context, callback).ToLocalChecked();
617 main_frame->RequestExecuteV8Function(context, function,
618 v8::Undefined(context->GetIsolate()), 0,
619 nullptr, &callback_helper);
620 RunPendingTasks();
621 EXPECT_FALSE(callback_helper.DidComplete());
622
623 web_view_helper.GetWebView()->GetPage()->SetPaused(false);
624 RunPendingTasks();
625 EXPECT_TRUE(callback_helper.DidComplete());
626 EXPECT_EQ("hello", callback_helper.StringValue());
627 }
628
TEST_F(WebFrameTest,RequestExecuteV8FunctionWhileSuspendedWithUserGesture)629 TEST_F(WebFrameTest, RequestExecuteV8FunctionWhileSuspendedWithUserGesture) {
630 DisableRendererSchedulerThrottling();
631 RegisterMockedHttpURLLoad("foo.html");
632
633 frame_test_helpers::WebViewHelper web_view_helper;
634 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
635
636 v8::HandleScope scope(v8::Isolate::GetCurrent());
637
638 // Suspend scheduled tasks so the script doesn't run.
639 web_view_helper.GetWebView()->GetPage()->SetPaused(true);
640 LocalFrame::NotifyUserActivation(
641 web_view_helper.LocalMainFrame()->GetFrame(),
642 mojom::UserActivationNotificationType::kTest);
643 ScriptExecutionCallbackHelper callback_helper(
644 web_view_helper.LocalMainFrame()->MainWorldScriptContext());
645 WebScriptSource script_source("navigator.userActivation.isActive;");
646 web_view_helper.GetWebView()
647 ->MainFrameImpl()
648 ->RequestExecuteScriptAndReturnValue(script_source, false,
649 &callback_helper);
650 RunPendingTasks();
651 EXPECT_FALSE(callback_helper.DidComplete());
652
653 web_view_helper.GetWebView()->GetPage()->SetPaused(false);
654 RunPendingTasks();
655 EXPECT_TRUE(callback_helper.DidComplete());
656 EXPECT_EQ(true, callback_helper.BoolValue());
657 }
658
TEST_F(WebFrameTest,IframeScriptRemovesSelf)659 TEST_F(WebFrameTest, IframeScriptRemovesSelf) {
660 RegisterMockedHttpURLLoad("single_iframe.html");
661 RegisterMockedHttpURLLoad("visible_iframe.html");
662
663 frame_test_helpers::WebViewHelper web_view_helper;
664 web_view_helper.InitializeAndLoad(base_url_ + "single_iframe.html");
665
666 v8::HandleScope scope(v8::Isolate::GetCurrent());
667 ScriptExecutionCallbackHelper callback_helper(
668 web_view_helper.LocalMainFrame()->MainWorldScriptContext());
669 web_view_helper.GetWebView()
670 ->MainFrame()
671 ->FirstChild()
672 ->ToWebLocalFrame()
673 ->RequestExecuteScriptAndReturnValue(
674 WebScriptSource(WebString(
675 "var iframe = "
676 "window.top.document.getElementsByTagName('iframe')[0]; "
677 "window.top.document.body.removeChild(iframe); 'hello';")),
678 false, &callback_helper);
679 RunPendingTasks();
680 EXPECT_TRUE(callback_helper.DidComplete());
681 EXPECT_EQ(String(), callback_helper.StringValue());
682 }
683
TEST_F(WebFrameTest,FormWithNullFrame)684 TEST_F(WebFrameTest, FormWithNullFrame) {
685 RegisterMockedHttpURLLoad("form.html");
686
687 frame_test_helpers::WebViewHelper web_view_helper;
688 web_view_helper.InitializeAndLoad(base_url_ + "form.html");
689
690 WebVector<WebFormElement> forms =
691 web_view_helper.LocalMainFrame()->GetDocument().Forms();
692 web_view_helper.Reset();
693
694 EXPECT_EQ(forms.size(), 1U);
695
696 // This test passes if this doesn't crash.
697 WebSearchableFormData searchable_data_form(forms[0]);
698 }
699
TEST_F(WebFrameTest,ChromePageJavascript)700 TEST_F(WebFrameTest, ChromePageJavascript) {
701 RegisterMockedChromeURLLoad("history.html");
702
703 frame_test_helpers::WebViewHelper web_view_helper;
704 web_view_helper.InitializeAndLoad(chrome_url_ + "history.html");
705
706 // Try to run JS against the chrome-style URL.
707 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
708 "javascript:document.body.appendChild(document."
709 "createTextNode('Clobbered'))");
710
711 // Now retrieve the frame's text and ensure it was modified by running
712 // javascript.
713 std::string content = WebFrameContentDumper::DumpWebViewAsText(
714 web_view_helper.GetWebView(), 1024)
715 .Utf8();
716 EXPECT_NE(std::string::npos, content.find("Clobbered"));
717 }
718
TEST_F(WebFrameTest,ChromePageNoJavascript)719 TEST_F(WebFrameTest, ChromePageNoJavascript) {
720 RegisterMockedChromeURLLoad("history.html");
721
722 frame_test_helpers::WebViewHelper web_view_helper;
723 web_view_helper.InitializeAndLoad(chrome_url_ + "history.html");
724
725 // Try to run JS against the chrome-style URL after prohibiting it.
726 WebSecurityPolicy::RegisterURLSchemeAsNotAllowingJavascriptURLs("chrome");
727 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
728 "javascript:document.body.appendChild(document."
729 "createTextNode('Clobbered'))");
730
731 // Now retrieve the frame's text and ensure it wasn't modified by running
732 // javascript.
733 std::string content = WebFrameContentDumper::DumpWebViewAsText(
734 web_view_helper.GetWebView(), 1024)
735 .Utf8();
736 EXPECT_EQ(std::string::npos, content.find("Clobbered"));
737 }
738
TEST_F(WebFrameTest,LocationSetHostWithMissingPort)739 TEST_F(WebFrameTest, LocationSetHostWithMissingPort) {
740 std::string file_name = "print-location-href.html";
741 RegisterMockedHttpURLLoad(file_name);
742 // TODO(crbug.com/751425): We should use the mock functionality
743 // via the WebViewHelper instance in each test case.
744 RegisterMockedURLLoadFromBase("http://internal.test:0/", file_name);
745
746 frame_test_helpers::WebViewHelper web_view_helper;
747 web_view_helper.InitializeAndLoad(base_url_ + file_name);
748
749 // Setting host to "hostname:" should be treated as "hostname:0".
750 frame_test_helpers::LoadFrame(
751 web_view_helper.GetWebView()->MainFrameImpl(),
752 "javascript:location.host = 'internal.test:'; void 0;");
753
754 frame_test_helpers::LoadFrame(
755 web_view_helper.GetWebView()->MainFrameImpl(),
756 "javascript:document.body.textContent = location.href; void 0;");
757
758 std::string content = WebFrameContentDumper::DumpWebViewAsText(
759 web_view_helper.GetWebView(), 1024)
760 .Utf8();
761 EXPECT_EQ("http://internal.test/" + file_name, content);
762 }
763
TEST_F(WebFrameTest,LocationSetEmptyPort)764 TEST_F(WebFrameTest, LocationSetEmptyPort) {
765 std::string file_name = "print-location-href.html";
766 RegisterMockedHttpURLLoad(file_name);
767 // TODO(crbug.com/751425): We should use the mock functionality
768 // via the WebViewHelper instance in each test case.
769 RegisterMockedURLLoadFromBase("http://internal.test:0/", file_name);
770
771 frame_test_helpers::WebViewHelper web_view_helper;
772 web_view_helper.InitializeAndLoad(base_url_ + file_name);
773
774 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
775 "javascript:location.port = ''; void 0;");
776
777 frame_test_helpers::LoadFrame(
778 web_view_helper.GetWebView()->MainFrameImpl(),
779 "javascript:document.body.textContent = location.href; void 0;");
780
781 std::string content = WebFrameContentDumper::DumpWebViewAsText(
782 web_view_helper.GetWebView(), 1024)
783 .Utf8();
784 EXPECT_EQ("http://internal.test/" + file_name, content);
785 }
786
787 class EvaluateOnLoadWebFrameClient
788 : public frame_test_helpers::TestWebFrameClient {
789 public:
EvaluateOnLoadWebFrameClient()790 EvaluateOnLoadWebFrameClient() : executing_(false), was_executed_(false) {}
791 ~EvaluateOnLoadWebFrameClient() override = default;
792
793 // frame_test_helpers::TestWebFrameClient:
DidClearWindowObject()794 void DidClearWindowObject() override {
795 EXPECT_FALSE(executing_);
796 was_executed_ = true;
797 executing_ = true;
798 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
799 Frame()->ExecuteScriptAndReturnValue(
800 WebScriptSource(WebString("window.someProperty = 42;")));
801 executing_ = false;
802 }
803
804 bool executing_;
805 bool was_executed_;
806 };
807
TEST_F(WebFrameTest,DidClearWindowObjectIsNotRecursive)808 TEST_F(WebFrameTest, DidClearWindowObjectIsNotRecursive) {
809 EvaluateOnLoadWebFrameClient web_frame_client;
810 frame_test_helpers::WebViewHelper web_view_helper;
811 web_view_helper.InitializeAndLoad("about:blank", &web_frame_client);
812 EXPECT_TRUE(web_frame_client.was_executed_);
813 }
814
815 class CSSCallbackWebFrameClient
816 : public frame_test_helpers::TestWebFrameClient {
817 public:
CSSCallbackWebFrameClient()818 CSSCallbackWebFrameClient() : update_count_(0) {}
819 ~CSSCallbackWebFrameClient() override = default;
820
821 // frame_test_helpers::TestWebFrameClient:
822 void DidMatchCSS(
823 const WebVector<WebString>& newly_matching_selectors,
824 const WebVector<WebString>& stopped_matching_selectors) override;
825
MatchedSelectors()826 HashSet<String>& MatchedSelectors() {
827 auto it = matched_selectors_.find(Frame());
828 if (it != matched_selectors_.end())
829 return it->value;
830
831 auto add_result = matched_selectors_.insert(Frame(), HashSet<String>());
832 return add_result.stored_value->value;
833 }
834
835 HashMap<WebLocalFrame*, HashSet<String>> matched_selectors_;
836 int update_count_;
837 };
838
DidMatchCSS(const WebVector<WebString> & newly_matching_selectors,const WebVector<WebString> & stopped_matching_selectors)839 void CSSCallbackWebFrameClient::DidMatchCSS(
840 const WebVector<WebString>& newly_matching_selectors,
841 const WebVector<WebString>& stopped_matching_selectors) {
842 ++update_count_;
843
844 HashSet<String>& frame_selectors = MatchedSelectors();
845 for (size_t i = 0; i < newly_matching_selectors.size(); ++i) {
846 String selector = newly_matching_selectors[i];
847 EXPECT_TRUE(frame_selectors.find(selector) == frame_selectors.end())
848 << selector;
849 frame_selectors.insert(selector);
850 }
851 for (size_t i = 0; i < stopped_matching_selectors.size(); ++i) {
852 String selector = stopped_matching_selectors[i];
853 EXPECT_TRUE(frame_selectors.find(selector) != frame_selectors.end())
854 << selector;
855 frame_selectors.erase(selector);
856 EXPECT_TRUE(frame_selectors.find(selector) == frame_selectors.end())
857 << selector;
858 }
859 }
860
861 class WebFrameCSSCallbackTest : public testing::Test {
862 protected:
WebFrameCSSCallbackTest()863 WebFrameCSSCallbackTest() {
864 frame_ = helper_.InitializeAndLoad("about:blank", &client_)
865 ->MainFrame()
866 ->ToWebLocalFrame();
867 }
868
~WebFrameCSSCallbackTest()869 ~WebFrameCSSCallbackTest() override {
870 EXPECT_EQ(1U, client_.matched_selectors_.size());
871 }
872
Doc() const873 WebDocument Doc() const { return frame_->GetDocument(); }
874
UpdateCount() const875 int UpdateCount() const { return client_.update_count_; }
876
MatchedSelectors()877 const HashSet<String>& MatchedSelectors() {
878 auto it = client_.matched_selectors_.find(frame_);
879 if (it != client_.matched_selectors_.end())
880 return it->value;
881
882 auto add_result =
883 client_.matched_selectors_.insert(frame_, HashSet<String>());
884 return add_result.stored_value->value;
885 }
886
LoadHTML(const std::string & html)887 void LoadHTML(const std::string& html) {
888 frame_test_helpers::LoadHTMLString(frame_, html, ToKURL("about:blank"));
889 }
890
ExecuteScript(const WebString & code)891 void ExecuteScript(const WebString& code) {
892 frame_->ExecuteScript(WebScriptSource(code));
893 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
894 DocumentUpdateReason::kTest);
895 RunPendingTasks();
896 }
897
898 CSSCallbackWebFrameClient client_;
899 frame_test_helpers::WebViewHelper helper_;
900 WebLocalFrame* frame_;
901 };
902
TEST_F(WebFrameCSSCallbackTest,AuthorStyleSheet)903 TEST_F(WebFrameCSSCallbackTest, AuthorStyleSheet) {
904 LoadHTML(
905 "<style>"
906 // This stylesheet checks that the internal property and value can't be
907 // set by a stylesheet, only WebDocument::watchCSSSelectors().
908 "div.initial_on { -internal-callback: none; }"
909 "div.initial_off { -internal-callback: -internal-presence; }"
910 "</style>"
911 "<div class=\"initial_on\"></div>"
912 "<div class=\"initial_off\"></div>");
913
914 Vector<WebString> selectors;
915 selectors.push_back(WebString::FromUTF8("div.initial_on"));
916 frame_->GetDocument().WatchCSSSelectors(WebVector<WebString>(selectors));
917 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
918 DocumentUpdateReason::kTest);
919 RunPendingTasks();
920 EXPECT_EQ(1, UpdateCount());
921 EXPECT_THAT(MatchedSelectors(), ElementsAre("div.initial_on"));
922
923 // Check that adding a watched selector calls back for already-present nodes.
924 selectors.push_back(WebString::FromUTF8("div.initial_off"));
925 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
926 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
927 DocumentUpdateReason::kTest);
928 RunPendingTasks();
929 EXPECT_EQ(2, UpdateCount());
930 EXPECT_THAT(MatchedSelectors(),
931 ElementsAre("div.initial_off", "div.initial_on"));
932
933 // Check that we can turn off callbacks for certain selectors.
934 Doc().WatchCSSSelectors(WebVector<WebString>());
935 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
936 DocumentUpdateReason::kTest);
937 RunPendingTasks();
938 EXPECT_EQ(3, UpdateCount());
939 EXPECT_THAT(MatchedSelectors(), ElementsAre());
940 }
941
TEST_F(WebFrameCSSCallbackTest,SharedComputedStyle)942 TEST_F(WebFrameCSSCallbackTest, SharedComputedStyle) {
943 // Check that adding an element calls back when it matches an existing rule.
944 Vector<WebString> selectors;
945 selectors.push_back(WebString::FromUTF8("span"));
946 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
947
948 ExecuteScript(
949 "i1 = document.createElement('span');"
950 "i1.id = 'first_span';"
951 "document.body.appendChild(i1)");
952 EXPECT_EQ(1, UpdateCount());
953 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
954
955 // Adding a second element that shares a ComputedStyle shouldn't call back.
956 // We use <span>s to avoid default style rules that can set
957 // ComputedStyle::unique().
958 ExecuteScript(
959 "i2 = document.createElement('span');"
960 "i2.id = 'second_span';"
961 "i1 = document.getElementById('first_span');"
962 "i1.parentNode.insertBefore(i2, i1.nextSibling);");
963 EXPECT_EQ(1, UpdateCount());
964 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
965
966 // Removing the first element shouldn't call back.
967 ExecuteScript(
968 "i1 = document.getElementById('first_span');"
969 "i1.parentNode.removeChild(i1);");
970 EXPECT_EQ(1, UpdateCount());
971 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
972
973 // But removing the second element *should* call back.
974 ExecuteScript(
975 "i2 = document.getElementById('second_span');"
976 "i2.parentNode.removeChild(i2);");
977 EXPECT_EQ(2, UpdateCount());
978 EXPECT_THAT(MatchedSelectors(), ElementsAre());
979 }
980
TEST_F(WebFrameCSSCallbackTest,CatchesAttributeChange)981 TEST_F(WebFrameCSSCallbackTest, CatchesAttributeChange) {
982 LoadHTML("<span></span>");
983
984 Vector<WebString> selectors;
985 selectors.push_back(WebString::FromUTF8("span[attr=\"value\"]"));
986 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
987 RunPendingTasks();
988
989 EXPECT_EQ(0, UpdateCount());
990 EXPECT_THAT(MatchedSelectors(), ElementsAre());
991
992 ExecuteScript(
993 "document.querySelector('span').setAttribute('attr', 'value');");
994 EXPECT_EQ(1, UpdateCount());
995 EXPECT_THAT(MatchedSelectors(), ElementsAre("span[attr=\"value\"]"));
996 }
997
TEST_F(WebFrameCSSCallbackTest,DisplayNone)998 TEST_F(WebFrameCSSCallbackTest, DisplayNone) {
999 LoadHTML("<div style='display:none'><span></span></div>");
1000
1001 Vector<WebString> selectors;
1002 selectors.push_back(WebString::FromUTF8("span"));
1003 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
1004 RunPendingTasks();
1005
1006 EXPECT_EQ(0, UpdateCount()) << "Don't match elements in display:none trees.";
1007
1008 ExecuteScript(
1009 "d = document.querySelector('div');"
1010 "d.style.display = 'block';");
1011 EXPECT_EQ(1, UpdateCount()) << "Match elements when they become displayed.";
1012 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1013
1014 ExecuteScript(
1015 "d = document.querySelector('div');"
1016 "d.style.display = 'none';");
1017 EXPECT_EQ(2, UpdateCount())
1018 << "Unmatch elements when they become undisplayed.";
1019 EXPECT_THAT(MatchedSelectors(), ElementsAre());
1020
1021 ExecuteScript(
1022 "s = document.querySelector('span');"
1023 "s.style.display = 'none';");
1024 EXPECT_EQ(2, UpdateCount())
1025 << "No effect from no-display'ing a span that's already undisplayed.";
1026
1027 ExecuteScript(
1028 "d = document.querySelector('div');"
1029 "d.style.display = 'block';");
1030 EXPECT_EQ(2, UpdateCount())
1031 << "No effect from displaying a div whose span is display:none.";
1032
1033 ExecuteScript(
1034 "s = document.querySelector('span');"
1035 "s.style.display = 'inline';");
1036 EXPECT_EQ(3, UpdateCount())
1037 << "Now the span is visible and produces a callback.";
1038 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1039
1040 ExecuteScript(
1041 "s = document.querySelector('span');"
1042 "s.style.display = 'none';");
1043 EXPECT_EQ(4, UpdateCount())
1044 << "Undisplaying the span directly should produce another callback.";
1045 EXPECT_THAT(MatchedSelectors(), ElementsAre());
1046 }
1047
TEST_F(WebFrameCSSCallbackTest,DisplayContents)1048 TEST_F(WebFrameCSSCallbackTest, DisplayContents) {
1049 LoadHTML("<div style='display:contents'><span></span></div>");
1050
1051 Vector<WebString> selectors(1u, WebString::FromUTF8("span"));
1052 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
1053 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
1054 DocumentUpdateReason::kTest);
1055 RunPendingTasks();
1056
1057 EXPECT_EQ(1, UpdateCount()) << "Match elements in display:contents trees.";
1058 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1059
1060 ExecuteScript(
1061 "s = document.querySelector('span');"
1062 "s.style.display = 'contents';");
1063 EXPECT_EQ(1, UpdateCount()) << "Match elements which are display:contents.";
1064 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1065
1066 ExecuteScript(
1067 "d = document.querySelector('div');"
1068 "d.style.display = 'block';");
1069 EXPECT_EQ(1, UpdateCount())
1070 << "Still match display:contents after parent becomes display:block.";
1071 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1072
1073 ExecuteScript(
1074 "d = document.querySelector('div');"
1075 "d.style.display = 'none';");
1076 EXPECT_EQ(2, UpdateCount())
1077 << "No longer matched when parent becomes display:none.";
1078 EXPECT_THAT(MatchedSelectors(), ElementsAre());
1079 }
1080
TEST_F(WebFrameCSSCallbackTest,Reparenting)1081 TEST_F(WebFrameCSSCallbackTest, Reparenting) {
1082 LoadHTML(
1083 "<div id='d1'><span></span></div>"
1084 "<div id='d2'></div>");
1085
1086 Vector<WebString> selectors;
1087 selectors.push_back(WebString::FromUTF8("span"));
1088 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
1089 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
1090 DocumentUpdateReason::kTest);
1091 RunPendingTasks();
1092
1093 EXPECT_EQ(1, UpdateCount());
1094 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1095
1096 ExecuteScript(
1097 "s = document.querySelector('span');"
1098 "d2 = document.getElementById('d2');"
1099 "d2.appendChild(s);");
1100 EXPECT_EQ(1, UpdateCount()) << "Just moving an element that continues to "
1101 "match shouldn't send a spurious callback.";
1102 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
1103 }
1104
TEST_F(WebFrameCSSCallbackTest,MultiSelector)1105 TEST_F(WebFrameCSSCallbackTest, MultiSelector) {
1106 LoadHTML("<span></span>");
1107
1108 // Check that selector lists match as the whole list, not as each element
1109 // independently.
1110 Vector<WebString> selectors;
1111 selectors.push_back(WebString::FromUTF8("span"));
1112 selectors.push_back(WebString::FromUTF8("span,p"));
1113 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
1114 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
1115 DocumentUpdateReason::kTest);
1116 RunPendingTasks();
1117
1118 EXPECT_EQ(1, UpdateCount());
1119 EXPECT_THAT(MatchedSelectors(), ElementsAre("span", "span, p"));
1120 }
1121
TEST_F(WebFrameCSSCallbackTest,InvalidSelector)1122 TEST_F(WebFrameCSSCallbackTest, InvalidSelector) {
1123 LoadHTML("<p><span></span></p>");
1124
1125 // Build a list with one valid selector and one invalid.
1126 Vector<WebString> selectors;
1127 selectors.push_back(WebString::FromUTF8("span"));
1128 selectors.push_back(WebString::FromUTF8("[")); // Invalid.
1129 selectors.push_back(WebString::FromUTF8("p span")); // Not compound.
1130 Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
1131 frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases(
1132 DocumentUpdateReason::kTest);
1133 RunPendingTasks();
1134
1135 EXPECT_EQ(1, UpdateCount());
1136 EXPECT_THAT(MatchedSelectors(), ElementsAre("span"))
1137 << "An invalid selector shouldn't prevent other selectors from matching.";
1138 }
1139
TEST_F(WebFrameTest,PostMessageEvent)1140 TEST_F(WebFrameTest, PostMessageEvent) {
1141 RegisterMockedHttpURLLoad("postmessage_test.html");
1142
1143 frame_test_helpers::WebViewHelper web_view_helper;
1144 web_view_helper.InitializeAndLoad(base_url_ + "postmessage_test.html");
1145
1146 auto* frame =
1147 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
1148
1149 scoped_refptr<SerializedScriptValue> data = SerializedScriptValue::Create();
1150 MessageEvent* message_event = MessageEvent::Create(
1151 /*ports=*/nullptr, std::move(data), "http://origin.com");
1152
1153 // Send a message with the correct origin.
1154 scoped_refptr<SecurityOrigin> correct_origin =
1155 SecurityOrigin::Create(ToKURL(base_url_));
1156 frame->PostMessageEvent(
1157 base::nullopt, g_empty_string, correct_origin->ToString(),
1158 BlinkTransferableMessage::FromMessageEvent(message_event));
1159
1160 // Send another message with incorrect origin.
1161 scoped_refptr<SecurityOrigin> incorrect_origin =
1162 SecurityOrigin::Create(ToKURL(chrome_url_));
1163 frame->PostMessageEvent(
1164 base::nullopt, g_empty_string, incorrect_origin->ToString(),
1165 BlinkTransferableMessage::FromMessageEvent(message_event));
1166
1167 // Verify that only the first addition is in the body of the page.
1168 std::string content = WebFrameContentDumper::DumpWebViewAsText(
1169 web_view_helper.GetWebView(), 1024)
1170 .Utf8();
1171 EXPECT_NE(std::string::npos, content.find("Message 1."));
1172 EXPECT_EQ(std::string::npos, content.find("Message 2."));
1173 }
1174
1175 namespace {
1176
SerializeString(const StringView & message,ScriptState * script_state)1177 scoped_refptr<SerializedScriptValue> SerializeString(
1178 const StringView& message,
1179 ScriptState* script_state) {
1180 // This is inefficient, but avoids duplicating serialization logic for the
1181 // sake of this test.
1182 NonThrowableExceptionState exception_state;
1183 ScriptState::Scope scope(script_state);
1184 V8ScriptValueSerializer serializer(script_state);
1185 return serializer.Serialize(V8String(script_state->GetIsolate(), message),
1186 exception_state);
1187 }
1188
1189 } // namespace
1190
TEST_F(WebFrameTest,PostMessageThenDetach)1191 TEST_F(WebFrameTest, PostMessageThenDetach) {
1192 frame_test_helpers::WebViewHelper web_view_helper;
1193 web_view_helper.InitializeAndLoad("about:blank");
1194
1195 auto* frame =
1196 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
1197 NonThrowableExceptionState exception_state;
1198 scoped_refptr<SerializedScriptValue> message =
1199 SerializeString("message", ToScriptStateForMainWorld(frame));
1200 MessagePortArray message_ports;
1201 frame->DomWindow()->PostMessageForTesting(
1202 message, message_ports, "*", frame->DomWindow(), exception_state);
1203 web_view_helper.Reset();
1204 EXPECT_FALSE(exception_state.HadException());
1205
1206 // Success is not crashing.
1207 RunPendingTasks();
1208 }
1209
1210 namespace {
1211
1212 class FixedLayoutTestWebWidgetClient
1213 : public frame_test_helpers::TestWebWidgetClient {
1214 public:
1215 FixedLayoutTestWebWidgetClient() = default;
1216 ~FixedLayoutTestWebWidgetClient() override = default;
1217
1218 // frame_test_helpers::TestWebWidgetClient:
GetInitialScreenInfo()1219 ScreenInfo GetInitialScreenInfo() override { return screen_info_; }
1220
1221 ScreenInfo screen_info_;
1222 };
1223
1224 // Helper function to set autosizing multipliers on a document.
SetTextAutosizingMultiplier(Document * document,float multiplier)1225 bool SetTextAutosizingMultiplier(Document* document, float multiplier) {
1226 bool multiplier_set = false;
1227 for (LayoutObject* layout_object = document->GetLayoutView(); layout_object;
1228 layout_object = layout_object->NextInPreOrder()) {
1229 if (layout_object->Style()) {
1230 scoped_refptr<ComputedStyle> modified_style =
1231 ComputedStyle::Clone(layout_object->StyleRef());
1232 modified_style->SetTextAutosizingMultiplier(multiplier);
1233 EXPECT_EQ(multiplier, modified_style->TextAutosizingMultiplier());
1234 layout_object->SetModifiedStyleOutsideStyleRecalc(
1235 std::move(modified_style), LayoutObject::ApplyStyleChanges::kNo);
1236 multiplier_set = true;
1237 }
1238 }
1239 return multiplier_set;
1240 }
1241
1242 // Helper function to check autosizing multipliers on a document.
CheckTextAutosizingMultiplier(Document * document,float multiplier)1243 bool CheckTextAutosizingMultiplier(Document* document, float multiplier) {
1244 bool multiplier_checked = false;
1245 for (LayoutObject* layout_object = document->GetLayoutView(); layout_object;
1246 layout_object = layout_object->NextInPreOrder()) {
1247 if (layout_object->Style() && layout_object->IsText()) {
1248 EXPECT_EQ(multiplier, layout_object->Style()->TextAutosizingMultiplier());
1249 multiplier_checked = true;
1250 }
1251 }
1252 return multiplier_checked;
1253 }
1254
UpdateScreenInfoAndResizeView(FixedLayoutTestWebWidgetClient * client,frame_test_helpers::WebViewHelper * web_view_helper,int viewport_width,int viewport_height)1255 void UpdateScreenInfoAndResizeView(
1256 FixedLayoutTestWebWidgetClient* client,
1257 frame_test_helpers::WebViewHelper* web_view_helper,
1258 int viewport_width,
1259 int viewport_height) {
1260 client->screen_info_.rect = gfx::Rect(viewport_width, viewport_height);
1261 web_view_helper->GetWebView()->MainFrameViewWidget()->UpdateScreenInfo(
1262 client->screen_info_);
1263 web_view_helper->Resize(gfx::Size(viewport_width, viewport_height));
1264 }
1265
1266 } // namespace
1267
1268 class WebFixedLayoutFrameTest : public WebFrameTest {
1269 protected:
1270 FixedLayoutTestWebWidgetClient widget_client_;
1271 };
1272
TEST_F(WebFrameTest,ChangeInFixedLayoutResetsTextAutosizingMultipliers)1273 TEST_F(WebFrameTest, ChangeInFixedLayoutResetsTextAutosizingMultipliers) {
1274 RegisterMockedHttpURLLoad("fixed_layout.html");
1275
1276 FixedLayoutTestWebWidgetClient client;
1277 int viewport_width = 640;
1278 int viewport_height = 480;
1279
1280 frame_test_helpers::WebViewHelper web_view_helper;
1281 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
1282 nullptr, &client, ConfigureAndroid);
1283
1284 Document* document =
1285 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
1286 ->GetDocument();
1287 document->GetSettings()->SetTextAutosizingEnabled(true);
1288 EXPECT_TRUE(document->GetSettings()->TextAutosizingEnabled());
1289 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1290
1291 EXPECT_TRUE(SetTextAutosizingMultiplier(document, 2));
1292
1293 ViewportDescription description =
1294 document->GetViewportData().GetViewportDescription();
1295 // Choose a width that's not going match the viewport width of the loaded
1296 // document.
1297 description.min_width = Length::Fixed(100);
1298 description.max_width = Length::Fixed(100);
1299 web_view_helper.GetWebView()->UpdatePageDefinedViewportConstraints(
1300 description);
1301
1302 EXPECT_TRUE(CheckTextAutosizingMultiplier(document, 1));
1303 }
1304
TEST_F(WebFrameTest,WorkingTextAutosizingMultipliers_VirtualViewport)1305 TEST_F(WebFrameTest, WorkingTextAutosizingMultipliers_VirtualViewport) {
1306 const std::string html_file = "fixed_layout.html";
1307 RegisterMockedHttpURLLoad(html_file);
1308
1309 FixedLayoutTestWebWidgetClient client;
1310
1311 frame_test_helpers::WebViewHelper web_view_helper;
1312 web_view_helper.InitializeAndLoad(base_url_ + html_file, nullptr, nullptr,
1313 &client, ConfigureAndroid);
1314
1315 Document* document =
1316 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
1317 ->GetDocument();
1318 document->GetSettings()->SetTextAutosizingEnabled(true);
1319 EXPECT_TRUE(document->GetSettings()->TextAutosizingEnabled());
1320
1321 web_view_helper.Resize(gfx::Size(490, 800));
1322
1323 // Multiplier: 980 / 490 = 2.0
1324 EXPECT_TRUE(CheckTextAutosizingMultiplier(document, 2.0));
1325 }
1326
TEST_F(WebFrameTest,VisualViewportSetSizeInvalidatesTextAutosizingMultipliers)1327 TEST_F(WebFrameTest,
1328 VisualViewportSetSizeInvalidatesTextAutosizingMultipliers) {
1329 RegisterMockedHttpURLLoad("iframe_reload.html");
1330 RegisterMockedHttpURLLoad("visible_iframe.html");
1331
1332 FixedLayoutTestWebWidgetClient client;
1333 int viewport_width = 640;
1334 int viewport_height = 480;
1335
1336 frame_test_helpers::WebViewHelper web_view_helper;
1337 web_view_helper.InitializeAndLoad(base_url_ + "iframe_reload.html", nullptr,
1338 nullptr, &client, ConfigureAndroid);
1339
1340 auto* main_frame =
1341 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
1342 Document* document = main_frame->GetDocument();
1343 LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
1344 document->GetSettings()->SetTextAutosizingEnabled(true);
1345 EXPECT_TRUE(document->GetSettings()->TextAutosizingEnabled());
1346 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1347
1348 for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) {
1349 auto* local_frame = DynamicTo<LocalFrame>(frame);
1350 if (!local_frame)
1351 continue;
1352 EXPECT_TRUE(SetTextAutosizingMultiplier(local_frame->GetDocument(), 2));
1353 for (LayoutObject* layout_object =
1354 local_frame->GetDocument()->GetLayoutView();
1355 layout_object; layout_object = layout_object->NextInPreOrder()) {
1356 if (layout_object->IsText())
1357 EXPECT_FALSE(layout_object->NeedsLayout());
1358 }
1359 }
1360
1361 frame_view->GetPage()->GetVisualViewport().SetSize(IntSize(200, 200));
1362
1363 for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) {
1364 auto* local_frame = DynamicTo<LocalFrame>(frame);
1365 if (!local_frame)
1366 continue;
1367 for (LayoutObject* layout_object =
1368 local_frame->GetDocument()->GetLayoutView();
1369 !layout_object; layout_object = layout_object->NextInPreOrder()) {
1370 if (layout_object->IsText())
1371 EXPECT_TRUE(layout_object->NeedsLayout());
1372 }
1373 }
1374 }
1375
TEST_F(WebFrameTest,ZeroHeightPositiveWidthNotIgnored)1376 TEST_F(WebFrameTest, ZeroHeightPositiveWidthNotIgnored) {
1377 FixedLayoutTestWebWidgetClient client;
1378 client.screen_info_.device_scale_factor = 1;
1379 int viewport_width = 1280;
1380 int viewport_height = 0;
1381
1382 frame_test_helpers::WebViewHelper web_view_helper;
1383 web_view_helper.Initialize(nullptr, nullptr, &client, ConfigureAndroid);
1384 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1385
1386 EXPECT_EQ(viewport_width, web_view_helper.GetWebView()
1387 ->MainFrameImpl()
1388 ->GetFrameView()
1389 ->GetLayoutSize()
1390 .Width());
1391 EXPECT_EQ(viewport_height, web_view_helper.GetWebView()
1392 ->MainFrameImpl()
1393 ->GetFrameView()
1394 ->GetLayoutSize()
1395 .Height());
1396 }
1397
TEST_F(WebFrameTest,DeviceScaleFactorUsesDefaultWithoutViewportTag)1398 TEST_F(WebFrameTest, DeviceScaleFactorUsesDefaultWithoutViewportTag) {
1399 RegisterMockedHttpURLLoad("no_viewport_tag.html");
1400
1401 int viewport_width = 640;
1402 int viewport_height = 480;
1403
1404 FixedLayoutTestWebWidgetClient client;
1405 client.screen_info_.device_scale_factor = 2;
1406
1407 frame_test_helpers::WebViewHelper web_view_helper;
1408 web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr,
1409 nullptr, &client, ConfigureAndroid);
1410
1411 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1412
1413 EXPECT_EQ(
1414 2,
1415 web_view_helper.GetWebView()->GetPage()->DeviceScaleFactorDeprecated());
1416
1417 // Device scale factor should be independent of page scale.
1418 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(1, 2);
1419 web_view_helper.GetWebView()->SetPageScaleFactor(0.5);
1420 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1421 EXPECT_EQ(1, web_view_helper.GetWebView()->PageScaleFactor());
1422
1423 // Force the layout to happen before leaving the test.
1424 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1425 }
1426
TEST_F(WebFrameTest,FixedLayoutInitializeAtMinimumScale)1427 TEST_F(WebFrameTest, FixedLayoutInitializeAtMinimumScale) {
1428 RegisterMockedHttpURLLoad("fixed_layout.html");
1429
1430 FixedLayoutTestWebWidgetClient client;
1431 client.screen_info_.device_scale_factor = 1;
1432 int viewport_width = 640;
1433 int viewport_height = 480;
1434
1435 // Make sure we initialize to minimum scale, even if the window size
1436 // only becomes available after the load begins.
1437 frame_test_helpers::WebViewHelper web_view_helper;
1438 web_view_helper.Initialize(nullptr, nullptr, &client, ConfigureAndroid);
1439 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
1440 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
1441 base_url_ + "fixed_layout.html");
1442 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1443
1444 int default_fixed_layout_width = 980;
1445 float minimum_page_scale_factor =
1446 viewport_width / (float)default_fixed_layout_width;
1447 EXPECT_EQ(minimum_page_scale_factor,
1448 web_view_helper.GetWebView()->PageScaleFactor());
1449 EXPECT_EQ(minimum_page_scale_factor,
1450 web_view_helper.GetWebView()->MinimumPageScaleFactor());
1451
1452 // Assume the user has pinch zoomed to page scale factor 2.
1453 float user_pinch_page_scale_factor = 2;
1454 web_view_helper.GetWebView()->SetPageScaleFactor(
1455 user_pinch_page_scale_factor);
1456 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1457
1458 // Make sure we don't reset to initial scale if the page continues to load.
1459 web_view_helper.GetWebView()->DidCommitLoad(false, false);
1460 web_view_helper.GetWebView()->DidChangeContentsSize();
1461 EXPECT_EQ(user_pinch_page_scale_factor,
1462 web_view_helper.GetWebView()->PageScaleFactor());
1463
1464 // Make sure we don't reset to initial scale if the viewport size changes.
1465 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height + 100));
1466 EXPECT_EQ(user_pinch_page_scale_factor,
1467 web_view_helper.GetWebView()->PageScaleFactor());
1468 }
1469
TEST_F(WebFrameTest,WideDocumentInitializeAtMinimumScale)1470 TEST_F(WebFrameTest, WideDocumentInitializeAtMinimumScale) {
1471 RegisterMockedHttpURLLoad("wide_document.html");
1472
1473 FixedLayoutTestWebWidgetClient client;
1474 client.screen_info_.device_scale_factor = 1;
1475 int viewport_width = 640;
1476 int viewport_height = 480;
1477
1478 // Make sure we initialize to minimum scale, even if the window size
1479 // only becomes available after the load begins.
1480 frame_test_helpers::WebViewHelper web_view_helper;
1481 web_view_helper.Initialize(nullptr, nullptr, &client, ConfigureAndroid);
1482 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
1483 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
1484 base_url_ + "wide_document.html");
1485 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1486
1487 int wide_document_width = 1500;
1488 float minimum_page_scale_factor = viewport_width / (float)wide_document_width;
1489 EXPECT_EQ(minimum_page_scale_factor,
1490 web_view_helper.GetWebView()->PageScaleFactor());
1491 EXPECT_EQ(minimum_page_scale_factor,
1492 web_view_helper.GetWebView()->MinimumPageScaleFactor());
1493
1494 // Assume the user has pinch zoomed to page scale factor 2.
1495 float user_pinch_page_scale_factor = 2;
1496 web_view_helper.GetWebView()->SetPageScaleFactor(
1497 user_pinch_page_scale_factor);
1498 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1499
1500 // Make sure we don't reset to initial scale if the page continues to load.
1501 web_view_helper.GetWebView()->DidCommitLoad(false, false);
1502 web_view_helper.GetWebView()->DidChangeContentsSize();
1503 EXPECT_EQ(user_pinch_page_scale_factor,
1504 web_view_helper.GetWebView()->PageScaleFactor());
1505
1506 // Make sure we don't reset to initial scale if the viewport size changes.
1507 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height + 100));
1508 EXPECT_EQ(user_pinch_page_scale_factor,
1509 web_view_helper.GetWebView()->PageScaleFactor());
1510 }
1511
TEST_F(WebFrameTest,DelayedViewportInitialScale)1512 TEST_F(WebFrameTest, DelayedViewportInitialScale) {
1513 RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
1514
1515 FixedLayoutTestWebWidgetClient client;
1516 client.screen_info_.device_scale_factor = 1;
1517 int viewport_width = 640;
1518 int viewport_height = 480;
1519
1520 frame_test_helpers::WebViewHelper web_view_helper;
1521 web_view_helper.InitializeAndLoad(
1522 base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, &client,
1523 ConfigureAndroid);
1524 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1525
1526 EXPECT_EQ(0.25f, web_view_helper.GetWebView()->PageScaleFactor());
1527
1528 ViewportData& viewport =
1529 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
1530 ->GetDocument()
1531 ->GetViewportData();
1532 ViewportDescription description = viewport.GetViewportDescription();
1533 description.zoom = 2;
1534 viewport.SetViewportDescription(description);
1535 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1536 EXPECT_EQ(2, web_view_helper.GetWebView()->PageScaleFactor());
1537 }
1538
TEST_F(WebFrameTest,setLoadWithOverviewModeToFalse)1539 TEST_F(WebFrameTest, setLoadWithOverviewModeToFalse) {
1540 RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
1541
1542 FixedLayoutTestWebWidgetClient client;
1543 client.screen_info_.device_scale_factor = 1;
1544 int viewport_width = 640;
1545 int viewport_height = 480;
1546
1547 frame_test_helpers::WebViewHelper web_view_helper;
1548 web_view_helper.InitializeAndLoad(
1549 base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, &client,
1550 ConfigureAndroid);
1551 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1552 true);
1553 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false);
1554 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1555
1556 // The page must be displayed at 100% zoom.
1557 EXPECT_EQ(1.0f, web_view_helper.GetWebView()->PageScaleFactor());
1558 }
1559
TEST_F(WebFrameTest,SetLoadWithOverviewModeToFalseAndNoWideViewport)1560 TEST_F(WebFrameTest, SetLoadWithOverviewModeToFalseAndNoWideViewport) {
1561 RegisterMockedHttpURLLoad("large-div.html");
1562
1563 FixedLayoutTestWebWidgetClient client;
1564 client.screen_info_.device_scale_factor = 1;
1565 int viewport_width = 640;
1566 int viewport_height = 480;
1567
1568 frame_test_helpers::WebViewHelper web_view_helper;
1569 web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr,
1570 nullptr, &client, ConfigureAndroid);
1571 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false);
1572 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1573 true);
1574 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
1575 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1576
1577 // The page must be displayed at 100% zoom, despite that it hosts a wide div
1578 // element.
1579 EXPECT_EQ(1.0f, web_view_helper.GetWebView()->PageScaleFactor());
1580 }
1581
TEST_F(WebFrameTest,NoWideViewportIgnoresPageViewportWidth)1582 TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidth) {
1583 RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
1584
1585 FixedLayoutTestWebWidgetClient client;
1586 client.screen_info_.device_scale_factor = 1;
1587 int viewport_width = 640;
1588 int viewport_height = 480;
1589
1590 frame_test_helpers::WebViewHelper web_view_helper;
1591 web_view_helper.InitializeAndLoad(
1592 base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, &client,
1593 ConfigureAndroid);
1594 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1595 true);
1596 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
1597 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1598
1599 // The page sets viewport width to 3000, but with UseWideViewport == false is
1600 // must be ignored.
1601 EXPECT_EQ(viewport_width, web_view_helper.GetWebView()
1602 ->MainFrameImpl()
1603 ->GetFrameView()
1604 ->Size()
1605 .Width());
1606 EXPECT_EQ(viewport_height, web_view_helper.GetWebView()
1607 ->MainFrameImpl()
1608 ->GetFrameView()
1609 ->Size()
1610 .Height());
1611 }
1612
TEST_F(WebFrameTest,NoWideViewportIgnoresPageViewportWidthButAccountsScale)1613 TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidthButAccountsScale) {
1614 RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
1615
1616 FixedLayoutTestWebWidgetClient client;
1617 client.screen_info_.device_scale_factor = 1;
1618 int viewport_width = 640;
1619 int viewport_height = 480;
1620
1621 frame_test_helpers::WebViewHelper web_view_helper;
1622 web_view_helper.InitializeAndLoad(
1623 base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, nullptr,
1624 &client, ConfigureAndroid);
1625 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1626 true);
1627 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
1628 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1629
1630 // The page sets viewport width to 3000, but with UseWideViewport == false it
1631 // must be ignored while the initial scale specified by the page must be
1632 // accounted.
1633 EXPECT_EQ(viewport_width / 2, web_view_helper.GetWebView()
1634 ->MainFrameImpl()
1635 ->GetFrameView()
1636 ->Size()
1637 .Width());
1638 EXPECT_EQ(viewport_height / 2, web_view_helper.GetWebView()
1639 ->MainFrameImpl()
1640 ->GetFrameView()
1641 ->Size()
1642 .Height());
1643 }
1644
TEST_F(WebFrameTest,WideViewportSetsTo980WithoutViewportTag)1645 TEST_F(WebFrameTest, WideViewportSetsTo980WithoutViewportTag) {
1646 RegisterMockedHttpURLLoad("no_viewport_tag.html");
1647
1648 FixedLayoutTestWebWidgetClient client;
1649 client.screen_info_.device_scale_factor = 1;
1650 int viewport_width = 640;
1651 int viewport_height = 480;
1652
1653 frame_test_helpers::WebViewHelper web_view_helper;
1654 web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr,
1655 nullptr, &client, ConfigureAndroid);
1656 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1657 true);
1658 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
1659 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1660
1661 EXPECT_EQ(980, web_view_helper.GetWebView()
1662 ->MainFrameImpl()
1663 ->GetFrameView()
1664 ->LayoutViewport()
1665 ->ContentsSize()
1666 .Width());
1667 EXPECT_EQ(980.0 / viewport_width * viewport_height,
1668 web_view_helper.GetWebView()
1669 ->MainFrameImpl()
1670 ->GetFrameView()
1671 ->LayoutViewport()
1672 ->ContentsSize()
1673 .Height());
1674 }
1675
TEST_F(WebFrameTest,WideViewportSetsTo980WithXhtmlMp)1676 TEST_F(WebFrameTest, WideViewportSetsTo980WithXhtmlMp) {
1677 RegisterMockedHttpURLLoad("viewport/viewport-legacy-xhtmlmp.html");
1678
1679 FixedLayoutTestWebWidgetClient client;
1680 client.screen_info_.device_scale_factor = 1;
1681 int viewport_width = 640;
1682 int viewport_height = 480;
1683
1684 frame_test_helpers::WebViewHelper web_view_helper;
1685 web_view_helper.Initialize(nullptr, nullptr, &client, ConfigureAndroid);
1686 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1687 true);
1688 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
1689 frame_test_helpers::LoadFrame(
1690 web_view_helper.GetWebView()->MainFrameImpl(),
1691 base_url_ + "viewport/viewport-legacy-xhtmlmp.html");
1692
1693 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1694 EXPECT_EQ(viewport_width, web_view_helper.GetWebView()
1695 ->MainFrameImpl()
1696 ->GetFrameView()
1697 ->Size()
1698 .Width());
1699 EXPECT_EQ(viewport_height, web_view_helper.GetWebView()
1700 ->MainFrameImpl()
1701 ->GetFrameView()
1702 ->Size()
1703 .Height());
1704 }
1705
TEST_F(WebFrameTest,NoWideViewportAndHeightInMeta)1706 TEST_F(WebFrameTest, NoWideViewportAndHeightInMeta) {
1707 RegisterMockedHttpURLLoad("viewport-height-1000.html");
1708
1709 FixedLayoutTestWebWidgetClient client;
1710 client.screen_info_.device_scale_factor = 1;
1711 int viewport_width = 640;
1712 int viewport_height = 480;
1713
1714 frame_test_helpers::WebViewHelper web_view_helper;
1715 web_view_helper.InitializeAndLoad(base_url_ + "viewport-height-1000.html",
1716 nullptr, nullptr, &client,
1717 ConfigureAndroid);
1718 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1719 true);
1720 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
1721 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1722
1723 EXPECT_EQ(viewport_width, web_view_helper.GetWebView()
1724 ->MainFrameImpl()
1725 ->GetFrameView()
1726 ->Size()
1727 .Width());
1728 }
1729
TEST_F(WebFrameTest,WideViewportSetsTo980WithAutoWidth)1730 TEST_F(WebFrameTest, WideViewportSetsTo980WithAutoWidth) {
1731 RegisterMockedHttpURLLoad("viewport-2x-initial-scale.html");
1732
1733 FixedLayoutTestWebWidgetClient client;
1734 client.screen_info_.device_scale_factor = 1;
1735 int viewport_width = 640;
1736 int viewport_height = 480;
1737
1738 frame_test_helpers::WebViewHelper web_view_helper;
1739 web_view_helper.InitializeAndLoad(
1740 base_url_ + "viewport-2x-initial-scale.html", nullptr, nullptr, &client,
1741 ConfigureAndroid);
1742 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1743 true);
1744 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
1745 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1746
1747 EXPECT_EQ(980, web_view_helper.GetWebView()
1748 ->MainFrameImpl()
1749 ->GetFrameView()
1750 ->Size()
1751 .Width());
1752 EXPECT_EQ(980.0 / viewport_width * viewport_height,
1753 web_view_helper.GetWebView()
1754 ->MainFrameImpl()
1755 ->GetFrameView()
1756 ->Size()
1757 .Height());
1758 }
1759
TEST_F(WebFrameTest,PageViewportInitialScaleOverridesLoadWithOverviewMode)1760 TEST_F(WebFrameTest, PageViewportInitialScaleOverridesLoadWithOverviewMode) {
1761 RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
1762
1763 FixedLayoutTestWebWidgetClient client;
1764 client.screen_info_.device_scale_factor = 1;
1765 int viewport_width = 640;
1766 int viewport_height = 480;
1767
1768 frame_test_helpers::WebViewHelper web_view_helper;
1769 web_view_helper.InitializeAndLoad(
1770 base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, nullptr,
1771 &client, ConfigureAndroid);
1772 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false);
1773 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1774
1775 // The page must be displayed at 200% zoom, as specified in its viewport meta
1776 // tag.
1777 EXPECT_EQ(2.0f, web_view_helper.GetWebView()->PageScaleFactor());
1778 }
1779
TEST_F(WebFrameTest,setInitialPageScaleFactorPermanently)1780 TEST_F(WebFrameTest, setInitialPageScaleFactorPermanently) {
1781 RegisterMockedHttpURLLoad("fixed_layout.html");
1782
1783 FixedLayoutTestWebWidgetClient client;
1784 client.screen_info_.device_scale_factor = 1;
1785 float enforced_page_scale_factor = 2.0f;
1786
1787 frame_test_helpers::WebViewHelper web_view_helper;
1788 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
1789 nullptr, &client, ConfigureAndroid);
1790 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1791 true);
1792 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false);
1793 web_view_helper.GetWebView()->SetInitialPageScaleOverride(
1794 enforced_page_scale_factor);
1795 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1796
1797 EXPECT_EQ(enforced_page_scale_factor,
1798 web_view_helper.GetWebView()->PageScaleFactor());
1799
1800 int viewport_width = 640;
1801 int viewport_height = 480;
1802 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1803
1804 EXPECT_EQ(enforced_page_scale_factor,
1805 web_view_helper.GetWebView()->PageScaleFactor());
1806
1807 web_view_helper.GetWebView()->SetInitialPageScaleOverride(-1);
1808 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1809 EXPECT_EQ(1.0, web_view_helper.GetWebView()->PageScaleFactor());
1810 }
1811
TEST_F(WebFrameTest,PermanentInitialPageScaleFactorOverridesLoadWithOverviewMode)1812 TEST_F(WebFrameTest,
1813 PermanentInitialPageScaleFactorOverridesLoadWithOverviewMode) {
1814 RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
1815
1816 FixedLayoutTestWebWidgetClient client;
1817 client.screen_info_.device_scale_factor = 1;
1818 int viewport_width = 640;
1819 int viewport_height = 480;
1820 float enforced_page_scale_factor = 0.5f;
1821
1822 frame_test_helpers::WebViewHelper web_view_helper;
1823 web_view_helper.InitializeAndLoad(
1824 base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, &client,
1825 ConfigureAndroid);
1826 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false);
1827 web_view_helper.GetWebView()->SetInitialPageScaleOverride(
1828 enforced_page_scale_factor);
1829 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1830
1831 EXPECT_EQ(enforced_page_scale_factor,
1832 web_view_helper.GetWebView()->PageScaleFactor());
1833 }
1834
TEST_F(WebFrameTest,PermanentInitialPageScaleFactorOverridesPageViewportInitialScale)1835 TEST_F(WebFrameTest,
1836 PermanentInitialPageScaleFactorOverridesPageViewportInitialScale) {
1837 RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
1838
1839 FixedLayoutTestWebWidgetClient client;
1840 client.screen_info_.device_scale_factor = 1;
1841 int viewport_width = 640;
1842 int viewport_height = 480;
1843 float enforced_page_scale_factor = 0.5f;
1844
1845 frame_test_helpers::WebViewHelper web_view_helper;
1846 web_view_helper.InitializeAndLoad(
1847 base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, nullptr,
1848 &client, ConfigureAndroid);
1849 web_view_helper.GetWebView()->SetInitialPageScaleOverride(
1850 enforced_page_scale_factor);
1851 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1852
1853 EXPECT_EQ(enforced_page_scale_factor,
1854 web_view_helper.GetWebView()->PageScaleFactor());
1855 }
1856
TEST_F(WebFrameTest,SmallPermanentInitialPageScaleFactorIsClobbered)1857 TEST_F(WebFrameTest, SmallPermanentInitialPageScaleFactorIsClobbered) {
1858 const char* pages[] = {
1859 // These pages trigger the clobbering condition. There must be a matching
1860 // item in "pageScaleFactors" array.
1861 "viewport-device-0.5x-initial-scale.html",
1862 "viewport-initial-scale-1.html",
1863 // These ones do not.
1864 "viewport-auto-initial-scale.html",
1865 "viewport-target-densitydpi-device-and-fixed-width.html"};
1866 float page_scale_factors[] = {0.5f, 1.0f};
1867 for (size_t i = 0; i < base::size(pages); ++i)
1868 RegisterMockedHttpURLLoad(pages[i]);
1869
1870 FixedLayoutTestWebWidgetClient client;
1871 client.screen_info_.device_scale_factor = 1;
1872 int viewport_width = 400;
1873 int viewport_height = 300;
1874 float enforced_page_scale_factor = 0.75f;
1875
1876 for (size_t i = 0; i < base::size(pages); ++i) {
1877 for (int quirk_enabled = 0; quirk_enabled <= 1; ++quirk_enabled) {
1878 frame_test_helpers::WebViewHelper web_view_helper;
1879 web_view_helper.InitializeAndLoad(base_url_ + pages[i], nullptr, nullptr,
1880 &client, ConfigureAndroid);
1881 web_view_helper.GetWebView()
1882 ->GetSettings()
1883 ->SetClobberUserAgentInitialScaleQuirk(quirk_enabled);
1884 web_view_helper.GetWebView()->SetInitialPageScaleOverride(
1885 enforced_page_scale_factor);
1886 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1887
1888 float expected_page_scale_factor =
1889 quirk_enabled && i < base::size(page_scale_factors)
1890 ? page_scale_factors[i]
1891 : enforced_page_scale_factor;
1892 EXPECT_EQ(expected_page_scale_factor,
1893 web_view_helper.GetWebView()->PageScaleFactor());
1894 }
1895 }
1896 }
1897
TEST_F(WebFrameTest,PermanentInitialPageScaleFactorAffectsLayoutWidth)1898 TEST_F(WebFrameTest, PermanentInitialPageScaleFactorAffectsLayoutWidth) {
1899 FixedLayoutTestWebWidgetClient client;
1900 client.screen_info_.device_scale_factor = 1;
1901 int viewport_width = 640;
1902 int viewport_height = 480;
1903 float enforced_page_scale_factor = 0.5;
1904
1905 frame_test_helpers::WebViewHelper web_view_helper;
1906 web_view_helper.InitializeAndLoad("about:blank", nullptr, nullptr, &client,
1907 ConfigureAndroid);
1908 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
1909 true);
1910 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
1911 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false);
1912 web_view_helper.GetWebView()->SetInitialPageScaleOverride(
1913 enforced_page_scale_factor);
1914 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1915
1916 EXPECT_EQ(viewport_width / enforced_page_scale_factor,
1917 web_view_helper.GetWebView()
1918 ->MainFrameImpl()
1919 ->GetFrameView()
1920 ->Size()
1921 .Width());
1922 EXPECT_EQ(enforced_page_scale_factor,
1923 web_view_helper.GetWebView()->PageScaleFactor());
1924 }
1925
TEST_F(WebFrameTest,DocumentElementClientHeightWorksWithWrapContentMode)1926 TEST_F(WebFrameTest, DocumentElementClientHeightWorksWithWrapContentMode) {
1927 RegisterMockedHttpURLLoad("0-by-0.html");
1928
1929 FixedLayoutTestWebWidgetClient client;
1930 client.screen_info_.device_scale_factor = 1;
1931 int viewport_width = 640;
1932 int viewport_height = 480;
1933
1934 frame_test_helpers::WebViewHelper web_view_helper;
1935
1936 web_view_helper.InitializeAndLoad(base_url_ + "0-by-0.html", nullptr, nullptr,
1937 &client, ConfigureAndroid);
1938 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true);
1939 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1940
1941 LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame();
1942 Document* document = frame->GetDocument();
1943 EXPECT_EQ(viewport_height, document->documentElement()->clientHeight());
1944 EXPECT_EQ(viewport_width, document->documentElement()->clientWidth());
1945 }
1946
TEST_F(WebFrameTest,SetForceZeroLayoutHeightWorksWithWrapContentMode)1947 TEST_F(WebFrameTest, SetForceZeroLayoutHeightWorksWithWrapContentMode) {
1948 RegisterMockedHttpURLLoad("0-by-0.html");
1949
1950 FixedLayoutTestWebWidgetClient client;
1951 client.screen_info_.device_scale_factor = 1;
1952 int viewport_width = 640;
1953 int viewport_height = 480;
1954
1955 frame_test_helpers::WebViewHelper web_view_helper;
1956
1957 web_view_helper.InitializeAndLoad(base_url_ + "0-by-0.html", nullptr, nullptr,
1958 &client, ConfigureAndroid);
1959 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true);
1960 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
1961
1962 LocalFrameView* frame_view =
1963 web_view_helper.GetWebView()->MainFrameImpl()->GetFrameView();
1964 GraphicsLayer* scroll_container =
1965 frame_view->GetLayoutView()->Compositor()->RootGraphicsLayer();
1966
1967 EXPECT_EQ(IntSize(), frame_view->GetLayoutSize());
1968 EXPECT_EQ(gfx::Size(), scroll_container->Size());
1969
1970 web_view_helper.Resize(gfx::Size(viewport_width, 0));
1971 EXPECT_EQ(IntSize(viewport_width, 0), frame_view->GetLayoutSize());
1972 EXPECT_EQ(gfx::Size(viewport_width, 0), scroll_container->Size());
1973
1974 // The flag ForceZeroLayoutHeight will cause the following resize of viewport
1975 // height to be ignored by the outer viewport (the container layer of
1976 // LayerCompositor). The height of the visualViewport, however, is not
1977 // affected.
1978 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
1979 EXPECT_FALSE(frame_view->NeedsLayout());
1980 EXPECT_EQ(IntSize(viewport_width, 0), frame_view->GetLayoutSize());
1981 EXPECT_EQ(gfx::Size(viewport_width, viewport_height),
1982 scroll_container->Size());
1983
1984 LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame();
1985 VisualViewport& visual_viewport = frame->GetPage()->GetVisualViewport();
1986 auto* scroll_node = visual_viewport.GetScrollTranslationNode()->ScrollNode();
1987 EXPECT_EQ(IntRect(0, 0, viewport_width, viewport_height),
1988 scroll_node->ContainerRect());
1989 EXPECT_EQ(IntSize(viewport_width, viewport_height),
1990 scroll_node->ContentsSize());
1991 }
1992
TEST_F(WebFrameTest,SetForceZeroLayoutHeight)1993 TEST_F(WebFrameTest, SetForceZeroLayoutHeight) {
1994 RegisterMockedHttpURLLoad("200-by-300.html");
1995
1996 FixedLayoutTestWebWidgetClient client;
1997 client.screen_info_.device_scale_factor = 1;
1998 int viewport_width = 640;
1999 int viewport_height = 480;
2000
2001 frame_test_helpers::WebViewHelper web_view_helper;
2002
2003 web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr,
2004 nullptr, &client, ConfigureAndroid);
2005 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2006
2007 EXPECT_LE(viewport_height, web_view_helper.GetWebView()
2008 ->MainFrameImpl()
2009 ->GetFrameView()
2010 ->GetLayoutSize()
2011 .Height());
2012 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true);
2013 EXPECT_TRUE(web_view_helper.GetWebView()
2014 ->MainFrameImpl()
2015 ->GetFrameView()
2016 ->NeedsLayout());
2017
2018 EXPECT_EQ(0, web_view_helper.GetWebView()
2019 ->MainFrameImpl()
2020 ->GetFrameView()
2021 ->GetLayoutSize()
2022 .Height());
2023
2024 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height * 2));
2025 EXPECT_FALSE(web_view_helper.GetWebView()
2026 ->MainFrameImpl()
2027 ->GetFrameView()
2028 ->NeedsLayout());
2029 EXPECT_EQ(0, web_view_helper.GetWebView()
2030 ->MainFrameImpl()
2031 ->GetFrameView()
2032 ->GetLayoutSize()
2033 .Height());
2034
2035 web_view_helper.Resize(gfx::Size(viewport_width * 2, viewport_height));
2036 EXPECT_EQ(0, web_view_helper.GetWebView()
2037 ->MainFrameImpl()
2038 ->GetFrameView()
2039 ->GetLayoutSize()
2040 .Height());
2041
2042 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(false);
2043 EXPECT_LE(viewport_height, web_view_helper.GetWebView()
2044 ->MainFrameImpl()
2045 ->GetFrameView()
2046 ->GetLayoutSize()
2047 .Height());
2048 }
2049
TEST_F(WebFrameTest,ToggleViewportMetaOnOff)2050 TEST_F(WebFrameTest, ToggleViewportMetaOnOff) {
2051 RegisterMockedHttpURLLoad("viewport-device-width.html");
2052
2053 FixedLayoutTestWebWidgetClient client;
2054 client.screen_info_.device_scale_factor = 1;
2055 int viewport_width = 640;
2056 int viewport_height = 480;
2057
2058 frame_test_helpers::WebViewHelper web_view_helper;
2059 web_view_helper.InitializeAndLoad(base_url_ + "viewport-device-width.html",
2060 nullptr, nullptr, &client);
2061 WebSettings* settings = web_view_helper.GetWebView()->GetSettings();
2062 settings->SetViewportMetaEnabled(false);
2063 settings->SetViewportEnabled(true);
2064 settings->SetMainFrameResizesAreOrientationChanges(true);
2065 settings->SetShrinksViewportContentToFit(true);
2066 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2067
2068 ViewportData& viewport =
2069 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
2070 ->GetDocument()
2071 ->GetViewportData();
2072 EXPECT_FALSE(viewport.GetViewportDescription().IsLegacyViewportType());
2073
2074 settings->SetViewportMetaEnabled(true);
2075 EXPECT_TRUE(viewport.GetViewportDescription().IsLegacyViewportType());
2076
2077 settings->SetViewportMetaEnabled(false);
2078 EXPECT_FALSE(viewport.GetViewportDescription().IsLegacyViewportType());
2079 }
2080
TEST_F(WebFrameTest,SetForceZeroLayoutHeightWorksWithRelayoutsWhenHeightChanged)2081 TEST_F(WebFrameTest,
2082 SetForceZeroLayoutHeightWorksWithRelayoutsWhenHeightChanged) {
2083 // this unit test is an attempt to target a real world case where an app could
2084 // 1. call resize(width, 0) and setForceZeroLayoutHeight(true)
2085 // 2. load content (hoping that the viewport height would increase
2086 // as more content is added)
2087 // 3. fail to register touch events aimed at the loaded content
2088 // because the layout is only updated if either width or height is changed
2089 RegisterMockedHttpURLLoad("button.html");
2090
2091 FixedLayoutTestWebWidgetClient client;
2092 client.screen_info_.device_scale_factor = 1;
2093 int viewport_width = 640;
2094 int viewport_height = 480;
2095
2096 frame_test_helpers::WebViewHelper web_view_helper;
2097
2098 web_view_helper.InitializeAndLoad(base_url_ + "button.html", nullptr, nullptr,
2099 &client, ConfigureAndroid);
2100 // set view height to zero so that if the height of the view is not
2101 // successfully updated during later resizes touch events will fail
2102 // (as in not hit content included in the view)
2103 web_view_helper.Resize(gfx::Size(viewport_width, 0));
2104
2105 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true);
2106 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2107
2108 FloatPoint hit_point = FloatPoint(30, 30); // button size is 100x100
2109
2110 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
2111 Document* document = frame->GetFrame()->GetDocument();
2112 Element* element = document->getElementById("tap_button");
2113
2114 ASSERT_NE(nullptr, element);
2115 EXPECT_EQ(String("oldValue"), element->innerText());
2116
2117 WebGestureEvent gesture_event(WebInputEvent::Type::kGestureTap,
2118 WebInputEvent::kNoModifiers,
2119 WebInputEvent::GetStaticTimeStampForTests(),
2120 WebGestureDevice::kTouchscreen);
2121 gesture_event.SetFrameScale(1);
2122 gesture_event.SetPositionInWidget(hit_point);
2123 gesture_event.SetPositionInScreen(hit_point);
2124 web_view_helper.GetWebView()
2125 ->MainFrameImpl()
2126 ->GetFrame()
2127 ->GetEventHandler()
2128 .HandleGestureEvent(gesture_event);
2129 // when pressed, the button changes its own text to "updatedValue"
2130 EXPECT_EQ(String("updatedValue"), element->innerText());
2131 }
2132
TEST_F(WebFrameTest,FrameOwnerPropertiesMargin)2133 TEST_F(WebFrameTest, FrameOwnerPropertiesMargin) {
2134 frame_test_helpers::WebViewHelper helper;
2135 helper.InitializeRemote();
2136
2137 WebFrameOwnerProperties properties;
2138 properties.margin_width = 11;
2139 properties.margin_height = 22;
2140 WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
2141 *helper.RemoteMainFrame(), "frameName", properties);
2142
2143 RegisterMockedHttpURLLoad("frame_owner_properties.html");
2144 frame_test_helpers::LoadFrame(local_frame,
2145 base_url_ + "frame_owner_properties.html");
2146
2147 // Check if the LocalFrame has seen the marginwidth and marginheight
2148 // properties.
2149 Document* child_document = local_frame->GetFrame()->GetDocument();
2150 EXPECT_EQ(11, child_document->FirstBodyElement()->GetIntegralAttribute(
2151 html_names::kMarginwidthAttr));
2152 EXPECT_EQ(22, child_document->FirstBodyElement()->GetIntegralAttribute(
2153 html_names::kMarginheightAttr));
2154
2155 LocalFrameView* frame_view = local_frame->GetFrameView();
2156 frame_view->Resize(800, 600);
2157 frame_view->SetNeedsLayout();
2158 frame_view->UpdateAllLifecyclePhasesForTest();
2159 // Expect scrollbars to be enabled by default.
2160 EXPECT_NE(nullptr, frame_view->LayoutViewport()->HorizontalScrollbar());
2161 EXPECT_NE(nullptr, frame_view->LayoutViewport()->VerticalScrollbar());
2162 }
2163
TEST_F(WebFrameTest,FrameOwnerPropertiesScrolling)2164 TEST_F(WebFrameTest, FrameOwnerPropertiesScrolling) {
2165 frame_test_helpers::WebViewHelper helper;
2166 helper.InitializeRemote();
2167
2168 WebFrameOwnerProperties properties;
2169 // Turn off scrolling in the subframe.
2170 properties.scrollbar_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
2171 WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
2172 *helper.RemoteMainFrame(), "frameName", properties);
2173
2174 RegisterMockedHttpURLLoad("frame_owner_properties.html");
2175 frame_test_helpers::LoadFrame(local_frame,
2176 base_url_ + "frame_owner_properties.html");
2177
2178 Document* child_document = local_frame->GetFrame()->GetDocument();
2179 EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute(
2180 html_names::kMarginwidthAttr));
2181 EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute(
2182 html_names::kMarginheightAttr));
2183
2184 LocalFrameView* frame_view = local_frame->GetFrameView();
2185 EXPECT_EQ(nullptr, frame_view->LayoutViewport()->HorizontalScrollbar());
2186 EXPECT_EQ(nullptr, frame_view->LayoutViewport()->VerticalScrollbar());
2187 }
2188
TEST_F(WebFrameTest,SetForceZeroLayoutHeightWorksAcrossNavigations)2189 TEST_F(WebFrameTest, SetForceZeroLayoutHeightWorksAcrossNavigations) {
2190 RegisterMockedHttpURLLoad("200-by-300.html");
2191 RegisterMockedHttpURLLoad("large-div.html");
2192
2193 FixedLayoutTestWebWidgetClient client;
2194 client.screen_info_.device_scale_factor = 1;
2195 int viewport_width = 640;
2196 int viewport_height = 480;
2197
2198 frame_test_helpers::WebViewHelper web_view_helper;
2199
2200 web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr,
2201 nullptr, &client, ConfigureAndroid);
2202 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true);
2203 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2204
2205 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2206 base_url_ + "large-div.html");
2207 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
2208
2209 EXPECT_EQ(0, web_view_helper.GetWebView()
2210 ->MainFrameImpl()
2211 ->GetFrameView()
2212 ->GetLayoutSize()
2213 .Height());
2214 }
2215
TEST_F(WebFrameTest,SetForceZeroLayoutHeightWithWideViewportQuirk)2216 TEST_F(WebFrameTest, SetForceZeroLayoutHeightWithWideViewportQuirk) {
2217 RegisterMockedHttpURLLoad("200-by-300.html");
2218
2219 FixedLayoutTestWebWidgetClient client;
2220 client.screen_info_.device_scale_factor = 1;
2221 int viewport_width = 640;
2222 int viewport_height = 480;
2223
2224 frame_test_helpers::WebViewHelper web_view_helper;
2225
2226 web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr,
2227 nullptr, &client, ConfigureAndroid);
2228 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2229 true);
2230 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
2231 web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true);
2232 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2233
2234 EXPECT_EQ(0, web_view_helper.GetWebView()
2235 ->MainFrameImpl()
2236 ->GetFrameView()
2237 ->GetLayoutSize()
2238 .Height());
2239 }
2240
TEST_F(WebFrameTest,WideViewportQuirkClobbersHeight)2241 TEST_F(WebFrameTest, WideViewportQuirkClobbersHeight) {
2242 RegisterMockedHttpURLLoad("viewport-height-1000.html");
2243
2244 FixedLayoutTestWebWidgetClient client;
2245 client.screen_info_.device_scale_factor = 1;
2246 int viewport_width = 600;
2247 int viewport_height = 800;
2248
2249 frame_test_helpers::WebViewHelper web_view_helper;
2250 web_view_helper.InitializeAndLoad("about:blank", nullptr, nullptr, &client,
2251 ConfigureAndroid);
2252 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2253 true);
2254 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
2255 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2256
2257 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2258 base_url_ + "viewport-height-1000.html");
2259 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2260
2261 EXPECT_EQ(800, web_view_helper.GetWebView()
2262 ->MainFrameImpl()
2263 ->GetFrameView()
2264 ->GetLayoutSize()
2265 .Height());
2266 EXPECT_EQ(1, web_view_helper.GetWebView()->PageScaleFactor());
2267 }
2268
TEST_F(WebFrameTest,OverflowHiddenDisablesScrolling)2269 TEST_F(WebFrameTest, OverflowHiddenDisablesScrolling) {
2270 RegisterMockedHttpURLLoad("body-overflow-hidden.html");
2271
2272 FixedLayoutTestWebWidgetClient client;
2273 client.screen_info_.device_scale_factor = 1;
2274 int viewport_width = 640;
2275 int viewport_height = 480;
2276
2277 frame_test_helpers::WebViewHelper web_view_helper;
2278 web_view_helper.Initialize(nullptr, nullptr, &client);
2279 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2280 base_url_ + "body-overflow-hidden.html");
2281 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2282
2283 LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
2284 EXPECT_FALSE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar));
2285 EXPECT_FALSE(
2286 view->LayoutViewport()->UserInputScrollable(kHorizontalScrollbar));
2287 }
2288
TEST_F(WebFrameTest,OverflowHiddenDisablesScrollingWithSetCanHaveScrollbars)2289 TEST_F(WebFrameTest, OverflowHiddenDisablesScrollingWithSetCanHaveScrollbars) {
2290 RegisterMockedHttpURLLoad("body-overflow-hidden-short.html");
2291
2292 FixedLayoutTestWebWidgetClient client;
2293 client.screen_info_.device_scale_factor = 1;
2294 int viewport_width = 640;
2295 int viewport_height = 480;
2296
2297 frame_test_helpers::WebViewHelper web_view_helper;
2298 web_view_helper.Initialize(nullptr, nullptr, &client);
2299 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2300 base_url_ + "body-overflow-hidden-short.html");
2301 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2302
2303 LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
2304 EXPECT_FALSE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar));
2305 EXPECT_FALSE(
2306 view->LayoutViewport()->UserInputScrollable(kHorizontalScrollbar));
2307
2308 web_view_helper.LocalMainFrame()->GetFrameView()->SetCanHaveScrollbars(true);
2309 EXPECT_FALSE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar));
2310 EXPECT_FALSE(
2311 view->LayoutViewport()->UserInputScrollable(kHorizontalScrollbar));
2312 }
2313
TEST_F(WebFrameTest,IgnoreOverflowHiddenQuirk)2314 TEST_F(WebFrameTest, IgnoreOverflowHiddenQuirk) {
2315 RegisterMockedHttpURLLoad("body-overflow-hidden.html");
2316
2317 FixedLayoutTestWebWidgetClient client;
2318 client.screen_info_.device_scale_factor = 1;
2319 int viewport_width = 640;
2320 int viewport_height = 480;
2321
2322 frame_test_helpers::WebViewHelper web_view_helper;
2323 web_view_helper.Initialize(nullptr, nullptr, &client);
2324 web_view_helper.GetWebView()
2325 ->GetSettings()
2326 ->SetIgnoreMainFrameOverflowHiddenQuirk(true);
2327 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2328 base_url_ + "body-overflow-hidden.html");
2329 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2330
2331 LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
2332 EXPECT_TRUE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar));
2333 }
2334
TEST_F(WebFrameTest,NonZeroValuesNoQuirk)2335 TEST_F(WebFrameTest, NonZeroValuesNoQuirk) {
2336 RegisterMockedHttpURLLoad("viewport-nonzero-values.html");
2337
2338 FixedLayoutTestWebWidgetClient client;
2339 client.screen_info_.device_scale_factor = 1;
2340 int viewport_width = 640;
2341 int viewport_height = 480;
2342 float expected_page_scale_factor = 0.5f;
2343
2344 frame_test_helpers::WebViewHelper web_view_helper;
2345 web_view_helper.Initialize(nullptr, nullptr, &client, ConfigureAndroid);
2346 web_view_helper.GetWebView()->GetSettings()->SetViewportMetaZeroValuesQuirk(
2347 true);
2348 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2349 true);
2350 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2351 base_url_ + "viewport-nonzero-values.html");
2352 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2353
2354 EXPECT_EQ(viewport_width / expected_page_scale_factor,
2355 web_view_helper.GetWebView()
2356 ->MainFrameImpl()
2357 ->GetFrameView()
2358 ->GetLayoutSize()
2359 .Width());
2360 EXPECT_EQ(expected_page_scale_factor,
2361 web_view_helper.GetWebView()->PageScaleFactor());
2362
2363 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
2364 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
2365 EXPECT_EQ(viewport_width / expected_page_scale_factor,
2366 web_view_helper.GetWebView()
2367 ->MainFrameImpl()
2368 ->GetFrameView()
2369 ->GetLayoutSize()
2370 .Width());
2371 EXPECT_EQ(expected_page_scale_factor,
2372 web_view_helper.GetWebView()->PageScaleFactor());
2373 }
2374
TEST_F(WebFrameTest,setPageScaleFactorDoesNotLayout)2375 TEST_F(WebFrameTest, setPageScaleFactorDoesNotLayout) {
2376 RegisterMockedHttpURLLoad("fixed_layout.html");
2377
2378 FixedLayoutTestWebWidgetClient client;
2379 client.screen_info_.device_scale_factor = 1;
2380 // Small viewport to ensure there are always scrollbars.
2381 int viewport_width = 64;
2382 int viewport_height = 48;
2383
2384 frame_test_helpers::WebViewHelper web_view_helper;
2385 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
2386 nullptr, &client, ConfigureAndroid);
2387 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2388
2389 unsigned prev_layout_count =
2390 web_view_helper.LocalMainFrame()->GetFrameView()->LayoutCountForTesting();
2391 web_view_helper.GetWebView()->SetPageScaleFactor(3);
2392 EXPECT_FALSE(web_view_helper.GetWebView()
2393 ->MainFrameImpl()
2394 ->GetFrameView()
2395 ->NeedsLayout());
2396 EXPECT_EQ(prev_layout_count, web_view_helper.GetWebView()
2397 ->MainFrameImpl()
2398 ->GetFrameView()
2399 ->LayoutCountForTesting());
2400 }
2401
TEST_F(WebFrameTest,setPageScaleFactorWithOverlayScrollbarsDoesNotLayout)2402 TEST_F(WebFrameTest, setPageScaleFactorWithOverlayScrollbarsDoesNotLayout) {
2403 RegisterMockedHttpURLLoad("fixed_layout.html");
2404
2405 FixedLayoutTestWebWidgetClient client;
2406 client.screen_info_.device_scale_factor = 1;
2407 int viewport_width = 640;
2408 int viewport_height = 480;
2409
2410 frame_test_helpers::WebViewHelper web_view_helper;
2411 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
2412 nullptr, &client, ConfigureAndroid);
2413 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2414
2415 unsigned prev_layout_count =
2416 web_view_helper.LocalMainFrame()->GetFrameView()->LayoutCountForTesting();
2417 web_view_helper.GetWebView()->SetPageScaleFactor(30);
2418 EXPECT_FALSE(web_view_helper.GetWebView()
2419 ->MainFrameImpl()
2420 ->GetFrameView()
2421 ->NeedsLayout());
2422 EXPECT_EQ(prev_layout_count, web_view_helper.GetWebView()
2423 ->MainFrameImpl()
2424 ->GetFrameView()
2425 ->LayoutCountForTesting());
2426 }
2427
TEST_F(WebFrameTest,pageScaleFactorWrittenToHistoryItem)2428 TEST_F(WebFrameTest, pageScaleFactorWrittenToHistoryItem) {
2429 RegisterMockedHttpURLLoad("fixed_layout.html");
2430
2431 FixedLayoutTestWebWidgetClient client;
2432 client.screen_info_.device_scale_factor = 1;
2433 int viewport_width = 640;
2434 int viewport_height = 480;
2435
2436 frame_test_helpers::WebViewHelper web_view_helper;
2437 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
2438 nullptr, &client, ConfigureAndroid);
2439 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2440
2441 web_view_helper.GetWebView()->SetPageScaleFactor(3);
2442 EXPECT_EQ(3,
2443 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
2444 ->Loader()
2445 .GetDocumentLoader()
2446 ->GetHistoryItem()
2447 ->GetViewState()
2448 ->page_scale_factor_);
2449 }
2450
TEST_F(WebFrameTest,initialScaleWrittenToHistoryItem)2451 TEST_F(WebFrameTest, initialScaleWrittenToHistoryItem) {
2452 RegisterMockedHttpURLLoad("fixed_layout.html");
2453
2454 FixedLayoutTestWebWidgetClient client;
2455 client.screen_info_.device_scale_factor = 1;
2456 int viewport_width = 640;
2457 int viewport_height = 480;
2458
2459 frame_test_helpers::WebViewHelper web_view_helper;
2460 web_view_helper.Initialize(nullptr, nullptr, &client, ConfigureAndroid);
2461 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
2462 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
2463 base_url_ + "fixed_layout.html");
2464 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2465
2466 int default_fixed_layout_width = 980;
2467 float minimum_page_scale_factor =
2468 viewport_width / (float)default_fixed_layout_width;
2469 EXPECT_EQ(minimum_page_scale_factor,
2470 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
2471 ->Loader()
2472 .GetDocumentLoader()
2473 ->GetHistoryItem()
2474 ->GetViewState()
2475 ->page_scale_factor_);
2476 }
2477
TEST_F(WebFrameTest,pageScaleFactorDoesntShrinkFrameView)2478 TEST_F(WebFrameTest, pageScaleFactorDoesntShrinkFrameView) {
2479 RegisterMockedHttpURLLoad("large-div.html");
2480
2481 FixedLayoutTestWebWidgetClient client;
2482 client.screen_info_.device_scale_factor = 1;
2483 // Small viewport to ensure there are always scrollbars.
2484 int viewport_width = 64;
2485 int viewport_height = 48;
2486
2487 frame_test_helpers::WebViewHelper web_view_helper;
2488 web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr,
2489 nullptr, &client, ConfigureAndroid);
2490 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2491
2492 LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
2493 int viewport_width_minus_scrollbar = viewport_width;
2494 int viewport_height_minus_scrollbar = viewport_height;
2495
2496 if (view->LayoutViewport()->VerticalScrollbar() &&
2497 !view->LayoutViewport()->VerticalScrollbar()->IsOverlayScrollbar())
2498 viewport_width_minus_scrollbar -= 15;
2499
2500 if (view->LayoutViewport()->HorizontalScrollbar() &&
2501 !view->LayoutViewport()->HorizontalScrollbar()->IsOverlayScrollbar())
2502 viewport_height_minus_scrollbar -= 15;
2503
2504 web_view_helper.GetWebView()->SetPageScaleFactor(2);
2505
2506 IntSize unscaled_size = view->Size();
2507 EXPECT_EQ(viewport_width, unscaled_size.Width());
2508 EXPECT_EQ(viewport_height, unscaled_size.Height());
2509
2510 IntSize unscaled_size_minus_scrollbar = view->Size();
2511 EXPECT_EQ(viewport_width_minus_scrollbar,
2512 unscaled_size_minus_scrollbar.Width());
2513 EXPECT_EQ(viewport_height_minus_scrollbar,
2514 unscaled_size_minus_scrollbar.Height());
2515
2516 IntSize frame_view_size = view->Size();
2517 EXPECT_EQ(viewport_width_minus_scrollbar, frame_view_size.Width());
2518 EXPECT_EQ(viewport_height_minus_scrollbar, frame_view_size.Height());
2519 }
2520
TEST_F(WebFrameTest,pageScaleFactorDoesNotApplyCssTransform)2521 TEST_F(WebFrameTest, pageScaleFactorDoesNotApplyCssTransform) {
2522 RegisterMockedHttpURLLoad("fixed_layout.html");
2523
2524 FixedLayoutTestWebWidgetClient client;
2525 client.screen_info_.device_scale_factor = 1;
2526 int viewport_width = 640;
2527 int viewport_height = 480;
2528
2529 frame_test_helpers::WebViewHelper web_view_helper;
2530 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
2531 nullptr, &client, ConfigureAndroid);
2532 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2533
2534 web_view_helper.GetWebView()->SetPageScaleFactor(2);
2535
2536 EXPECT_EQ(980,
2537 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame())
2538 ->ContentLayoutObject()
2539 ->DocumentRect()
2540 .Width());
2541 EXPECT_EQ(980, web_view_helper.GetWebView()
2542 ->MainFrameImpl()
2543 ->GetFrameView()
2544 ->LayoutViewport()
2545 ->ContentsSize()
2546 .Width());
2547 }
2548
TEST_F(WebFrameTest,targetDensityDpiHigh)2549 TEST_F(WebFrameTest, targetDensityDpiHigh) {
2550 RegisterMockedHttpURLLoad("viewport-target-densitydpi-high.html");
2551
2552 FixedLayoutTestWebWidgetClient client;
2553 // high-dpi = 240
2554 float target_dpi = 240.0f;
2555 float device_scale_factors[] = {1.0f, 4.0f / 3.0f, 2.0f};
2556 int viewport_width = 640;
2557 int viewport_height = 480;
2558
2559 for (size_t i = 0; i < base::size(device_scale_factors); ++i) {
2560 float device_scale_factor = device_scale_factors[i];
2561 float device_dpi = device_scale_factor * 160.0f;
2562 client.screen_info_.device_scale_factor = device_scale_factor;
2563
2564 frame_test_helpers::WebViewHelper web_view_helper;
2565 web_view_helper.InitializeAndLoad(
2566 base_url_ + "viewport-target-densitydpi-high.html", nullptr, nullptr,
2567 &client, ConfigureAndroid);
2568 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2569 true);
2570 web_view_helper.GetWebView()
2571 ->GetSettings()
2572 ->SetSupportDeprecatedTargetDensityDPI(true);
2573 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2574
2575 // We need to account for the fact that logical pixels are unconditionally
2576 // multiplied by deviceScaleFactor to produce physical pixels.
2577 float density_dpi_scale_ratio =
2578 device_scale_factor * target_dpi / device_dpi;
2579 EXPECT_NEAR(viewport_width * density_dpi_scale_ratio,
2580 web_view_helper.GetWebView()
2581 ->MainFrameImpl()
2582 ->GetFrameView()
2583 ->GetLayoutSize()
2584 .Width(),
2585 1.0f);
2586 EXPECT_NEAR(viewport_height * density_dpi_scale_ratio,
2587 web_view_helper.GetWebView()
2588 ->MainFrameImpl()
2589 ->GetFrameView()
2590 ->GetLayoutSize()
2591 .Height(),
2592 1.0f);
2593 EXPECT_NEAR(1.0f / density_dpi_scale_ratio,
2594 web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2595 }
2596 }
2597
TEST_F(WebFrameTest,targetDensityDpiDevice)2598 TEST_F(WebFrameTest, targetDensityDpiDevice) {
2599 RegisterMockedHttpURLLoad("viewport-target-densitydpi-device.html");
2600
2601 float device_scale_factors[] = {1.0f, 4.0f / 3.0f, 2.0f};
2602
2603 FixedLayoutTestWebWidgetClient client;
2604 int viewport_width = 640;
2605 int viewport_height = 480;
2606
2607 for (size_t i = 0; i < base::size(device_scale_factors); ++i) {
2608 client.screen_info_.device_scale_factor = device_scale_factors[i];
2609
2610 frame_test_helpers::WebViewHelper web_view_helper;
2611 web_view_helper.InitializeAndLoad(
2612 base_url_ + "viewport-target-densitydpi-device.html", nullptr, nullptr,
2613 &client, ConfigureAndroid);
2614 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2615 true);
2616 web_view_helper.GetWebView()
2617 ->GetSettings()
2618 ->SetSupportDeprecatedTargetDensityDPI(true);
2619 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2620
2621 EXPECT_NEAR(viewport_width * client.screen_info_.device_scale_factor,
2622 web_view_helper.GetWebView()
2623 ->MainFrameImpl()
2624 ->GetFrameView()
2625 ->GetLayoutSize()
2626 .Width(),
2627 1.0f);
2628 EXPECT_NEAR(viewport_height * client.screen_info_.device_scale_factor,
2629 web_view_helper.GetWebView()
2630 ->MainFrameImpl()
2631 ->GetFrameView()
2632 ->GetLayoutSize()
2633 .Height(),
2634 1.0f);
2635 EXPECT_NEAR(1.0f / client.screen_info_.device_scale_factor,
2636 web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2637 }
2638 }
2639
TEST_F(WebFrameTest,targetDensityDpiDeviceAndFixedWidth)2640 TEST_F(WebFrameTest, targetDensityDpiDeviceAndFixedWidth) {
2641 RegisterMockedHttpURLLoad(
2642 "viewport-target-densitydpi-device-and-fixed-width.html");
2643
2644 float device_scale_factors[] = {1.0f, 4.0f / 3.0f, 2.0f};
2645
2646 FixedLayoutTestWebWidgetClient client;
2647 int viewport_width = 640;
2648 int viewport_height = 480;
2649
2650 for (size_t i = 0; i < base::size(device_scale_factors); ++i) {
2651 client.screen_info_.device_scale_factor = device_scale_factors[i];
2652
2653 frame_test_helpers::WebViewHelper web_view_helper;
2654 web_view_helper.InitializeAndLoad(
2655 base_url_ + "viewport-target-densitydpi-device-and-fixed-width.html",
2656 nullptr, nullptr, &client, ConfigureAndroid);
2657 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2658 true);
2659 web_view_helper.GetWebView()
2660 ->GetSettings()
2661 ->SetSupportDeprecatedTargetDensityDPI(true);
2662 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
2663 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2664
2665 EXPECT_NEAR(viewport_width,
2666 web_view_helper.GetWebView()
2667 ->MainFrameImpl()
2668 ->GetFrameView()
2669 ->GetLayoutSize()
2670 .Width(),
2671 1.0f);
2672 EXPECT_NEAR(viewport_height,
2673 web_view_helper.GetWebView()
2674 ->MainFrameImpl()
2675 ->GetFrameView()
2676 ->GetLayoutSize()
2677 .Height(),
2678 1.0f);
2679 EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2680 }
2681 }
2682
TEST_F(WebFrameTest,NoWideViewportAndScaleLessThanOne)2683 TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOne) {
2684 RegisterMockedHttpURLLoad("viewport-initial-scale-less-than-1.html");
2685
2686 FixedLayoutTestWebWidgetClient client;
2687 client.screen_info_.device_scale_factor = 1.33f;
2688 int viewport_width = 640;
2689 int viewport_height = 480;
2690
2691 frame_test_helpers::WebViewHelper web_view_helper;
2692 web_view_helper.InitializeAndLoad(
2693 base_url_ + "viewport-initial-scale-less-than-1.html", nullptr, nullptr,
2694 &client, ConfigureAndroid);
2695 web_view_helper.GetWebView()
2696 ->GetSettings()
2697 ->SetSupportDeprecatedTargetDensityDPI(true);
2698 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2699 true);
2700 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
2701 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2702
2703 EXPECT_NEAR(viewport_width * client.screen_info_.device_scale_factor,
2704 web_view_helper.GetWebView()
2705 ->MainFrameImpl()
2706 ->GetFrameView()
2707 ->GetLayoutSize()
2708 .Width(),
2709 1.0f);
2710 EXPECT_NEAR(viewport_height * client.screen_info_.device_scale_factor,
2711 web_view_helper.GetWebView()
2712 ->MainFrameImpl()
2713 ->GetFrameView()
2714 ->GetLayoutSize()
2715 .Height(),
2716 1.0f);
2717 EXPECT_NEAR(1.0f / client.screen_info_.device_scale_factor,
2718 web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2719 }
2720
TEST_F(WebFrameTest,NoWideViewportAndScaleLessThanOneWithDeviceWidth)2721 TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOneWithDeviceWidth) {
2722 RegisterMockedHttpURLLoad(
2723 "viewport-initial-scale-less-than-1-device-width.html");
2724
2725 FixedLayoutTestWebWidgetClient client;
2726 client.screen_info_.device_scale_factor = 1.33f;
2727 int viewport_width = 640;
2728 int viewport_height = 480;
2729
2730 frame_test_helpers::WebViewHelper web_view_helper;
2731 web_view_helper.InitializeAndLoad(
2732 base_url_ + "viewport-initial-scale-less-than-1-device-width.html",
2733 nullptr, nullptr, &client, ConfigureAndroid);
2734 web_view_helper.GetWebView()
2735 ->GetSettings()
2736 ->SetSupportDeprecatedTargetDensityDPI(true);
2737 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2738 true);
2739 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
2740 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2741
2742 const float kPageZoom = 0.25f;
2743 EXPECT_NEAR(
2744 viewport_width * client.screen_info_.device_scale_factor / kPageZoom,
2745 web_view_helper.GetWebView()
2746 ->MainFrameImpl()
2747 ->GetFrameView()
2748 ->GetLayoutSize()
2749 .Width(),
2750 1.0f);
2751 EXPECT_NEAR(
2752 viewport_height * client.screen_info_.device_scale_factor / kPageZoom,
2753 web_view_helper.GetWebView()
2754 ->MainFrameImpl()
2755 ->GetFrameView()
2756 ->GetLayoutSize()
2757 .Height(),
2758 1.0f);
2759 EXPECT_NEAR(1.0f / client.screen_info_.device_scale_factor,
2760 web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2761 }
2762
TEST_F(WebFrameTest,NoWideViewportAndNoViewportWithInitialPageScaleOverride)2763 TEST_F(WebFrameTest, NoWideViewportAndNoViewportWithInitialPageScaleOverride) {
2764 RegisterMockedHttpURLLoad("large-div.html");
2765
2766 FixedLayoutTestWebWidgetClient client;
2767 int viewport_width = 640;
2768 int viewport_height = 480;
2769 float enforced_page_scale_factor = 5.0f;
2770
2771 frame_test_helpers::WebViewHelper web_view_helper;
2772 web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr,
2773 nullptr, &client, ConfigureAndroid);
2774 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
2775 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2776 true);
2777 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
2778 web_view_helper.GetWebView()->SetInitialPageScaleOverride(
2779 enforced_page_scale_factor);
2780 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2781
2782 EXPECT_NEAR(viewport_width / enforced_page_scale_factor,
2783 web_view_helper.GetWebView()
2784 ->MainFrameImpl()
2785 ->GetFrameView()
2786 ->GetLayoutSize()
2787 .Width(),
2788 1.0f);
2789 EXPECT_NEAR(viewport_height / enforced_page_scale_factor,
2790 web_view_helper.GetWebView()
2791 ->MainFrameImpl()
2792 ->GetFrameView()
2793 ->GetLayoutSize()
2794 .Height(),
2795 1.0f);
2796 EXPECT_NEAR(enforced_page_scale_factor,
2797 web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2798 }
2799
TEST_F(WebFrameTest,NoUserScalableQuirkIgnoresViewportScale)2800 TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScale) {
2801 RegisterMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html");
2802
2803 FixedLayoutTestWebWidgetClient client;
2804 int viewport_width = 640;
2805 int viewport_height = 480;
2806
2807 frame_test_helpers::WebViewHelper web_view_helper;
2808 web_view_helper.InitializeAndLoad(
2809 base_url_ + "viewport-initial-scale-and-user-scalable-no.html", nullptr,
2810 nullptr, &client, ConfigureAndroid);
2811 web_view_helper.GetWebView()
2812 ->GetSettings()
2813 ->SetViewportMetaNonUserScalableQuirk(true);
2814 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2815
2816 EXPECT_NEAR(viewport_width,
2817 web_view_helper.GetWebView()
2818 ->MainFrameImpl()
2819 ->GetFrameView()
2820 ->GetLayoutSize()
2821 .Width(),
2822 1.0f);
2823 EXPECT_NEAR(viewport_height,
2824 web_view_helper.GetWebView()
2825 ->MainFrameImpl()
2826 ->GetFrameView()
2827 ->GetLayoutSize()
2828 .Height(),
2829 1.0f);
2830 EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2831 }
2832
TEST_F(WebFrameTest,NoUserScalableQuirkIgnoresViewportScaleForNonWideViewport)2833 TEST_F(WebFrameTest,
2834 NoUserScalableQuirkIgnoresViewportScaleForNonWideViewport) {
2835 RegisterMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html");
2836
2837 FixedLayoutTestWebWidgetClient client;
2838 client.screen_info_.device_scale_factor = 1.33f;
2839 int viewport_width = 640;
2840 int viewport_height = 480;
2841
2842 frame_test_helpers::WebViewHelper web_view_helper;
2843 web_view_helper.InitializeAndLoad(
2844 base_url_ + "viewport-initial-scale-and-user-scalable-no.html", nullptr,
2845 nullptr, &client, ConfigureAndroid);
2846 web_view_helper.GetWebView()
2847 ->GetSettings()
2848 ->SetSupportDeprecatedTargetDensityDPI(true);
2849 web_view_helper.GetWebView()
2850 ->GetSettings()
2851 ->SetViewportMetaNonUserScalableQuirk(true);
2852 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2853 true);
2854 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
2855 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2856
2857 EXPECT_NEAR(viewport_width * client.screen_info_.device_scale_factor,
2858 web_view_helper.GetWebView()
2859 ->MainFrameImpl()
2860 ->GetFrameView()
2861 ->GetLayoutSize()
2862 .Width(),
2863 1.0f);
2864 EXPECT_NEAR(viewport_height * client.screen_info_.device_scale_factor,
2865 web_view_helper.GetWebView()
2866 ->MainFrameImpl()
2867 ->GetFrameView()
2868 ->GetLayoutSize()
2869 .Height(),
2870 1.0f);
2871 EXPECT_NEAR(1.0f / client.screen_info_.device_scale_factor,
2872 web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2873 }
2874
TEST_F(WebFrameTest,NoUserScalableQuirkIgnoresViewportScaleForWideViewport)2875 TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScaleForWideViewport) {
2876 RegisterMockedHttpURLLoad("viewport-2x-initial-scale-non-user-scalable.html");
2877
2878 FixedLayoutTestWebWidgetClient client;
2879 int viewport_width = 640;
2880 int viewport_height = 480;
2881
2882 frame_test_helpers::WebViewHelper web_view_helper;
2883 web_view_helper.InitializeAndLoad(
2884 base_url_ + "viewport-2x-initial-scale-non-user-scalable.html", nullptr,
2885 nullptr, &client, ConfigureAndroid);
2886 web_view_helper.GetWebView()
2887 ->GetSettings()
2888 ->SetViewportMetaNonUserScalableQuirk(true);
2889 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2890 true);
2891 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
2892 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2893
2894 EXPECT_NEAR(viewport_width,
2895 web_view_helper.GetWebView()
2896 ->MainFrameImpl()
2897 ->GetFrameView()
2898 ->GetLayoutSize()
2899 .Width(),
2900 1.0f);
2901 EXPECT_NEAR(viewport_height,
2902 web_view_helper.GetWebView()
2903 ->MainFrameImpl()
2904 ->GetFrameView()
2905 ->GetLayoutSize()
2906 .Height(),
2907 1.0f);
2908 EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2909 }
2910
TEST_F(WebFrameTest,DesktopPageCanBeZoomedInWhenWideViewportIsTurnedOff)2911 TEST_F(WebFrameTest, DesktopPageCanBeZoomedInWhenWideViewportIsTurnedOff) {
2912 RegisterMockedHttpURLLoad("no_viewport_tag.html");
2913
2914 FixedLayoutTestWebWidgetClient client;
2915 int viewport_width = 640;
2916 int viewport_height = 480;
2917
2918 frame_test_helpers::WebViewHelper web_view_helper;
2919 web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr,
2920 nullptr, &client, ConfigureAndroid);
2921 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
2922 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
2923 true);
2924 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false);
2925 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
2926
2927 EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f);
2928 EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->MinimumPageScaleFactor(),
2929 0.01f);
2930 EXPECT_NEAR(5.0f, web_view_helper.GetWebView()->MaximumPageScaleFactor(),
2931 0.01f);
2932 }
2933
2934 class WebFrameResizeTest : public WebFrameTest {
2935 protected:
ComputeRelativeOffset(const IntPoint & absolute_offset,const LayoutRect & rect)2936 static FloatSize ComputeRelativeOffset(const IntPoint& absolute_offset,
2937 const LayoutRect& rect) {
2938 FloatSize relative_offset =
2939 FloatPoint(absolute_offset) - FloatPoint(rect.Location());
2940 relative_offset.Scale(1.f / rect.Width(), 1.f / rect.Height());
2941 return relative_offset;
2942 }
2943
TestResizeYieldsCorrectScrollAndScale(const char * url,const float initial_page_scale_factor,const WebSize scroll_offset,const WebSize viewport_size,const bool should_scale_relative_to_viewport_width)2944 void TestResizeYieldsCorrectScrollAndScale(
2945 const char* url,
2946 const float initial_page_scale_factor,
2947 const WebSize scroll_offset,
2948 const WebSize viewport_size,
2949 const bool should_scale_relative_to_viewport_width) {
2950 RegisterMockedHttpURLLoad(url);
2951
2952 const float aspect_ratio =
2953 static_cast<float>(viewport_size.width) / viewport_size.height;
2954
2955 frame_test_helpers::WebViewHelper web_view_helper;
2956 web_view_helper.InitializeAndLoad(base_url_ + url, nullptr, nullptr,
2957 nullptr, ConfigureAndroid);
2958 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
2959
2960 // Origin scrollOffsets preserved under resize.
2961 {
2962 web_view_helper.Resize(
2963 gfx::Size(viewport_size.width, viewport_size.height));
2964 web_view_helper.GetWebView()->SetPageScaleFactor(
2965 initial_page_scale_factor);
2966 ASSERT_EQ(gfx::Size(viewport_size),
2967 web_view_helper.GetWebView()->MainFrameWidget()->Size());
2968 ASSERT_EQ(initial_page_scale_factor,
2969 web_view_helper.GetWebView()->PageScaleFactor());
2970 web_view_helper.Resize(
2971 gfx::Size(viewport_size.height, viewport_size.width));
2972 float expected_page_scale_factor =
2973 initial_page_scale_factor *
2974 (should_scale_relative_to_viewport_width ? 1 / aspect_ratio : 1);
2975 EXPECT_NEAR(expected_page_scale_factor,
2976 web_view_helper.GetWebView()->PageScaleFactor(), 0.05f);
2977 EXPECT_EQ(WebSize(), web_view_helper.LocalMainFrame()->GetScrollOffset());
2978 }
2979
2980 // Resizing just the height should not affect pageScaleFactor or
2981 // scrollOffset.
2982 {
2983 web_view_helper.Resize(
2984 gfx::Size(viewport_size.width, viewport_size.height));
2985 web_view_helper.GetWebView()->SetPageScaleFactor(
2986 initial_page_scale_factor);
2987 web_view_helper.LocalMainFrame()->SetScrollOffset(scroll_offset);
2988 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
2989 const WebSize expected_scroll_offset =
2990 web_view_helper.LocalMainFrame()->GetScrollOffset();
2991 web_view_helper.Resize(
2992 gfx::Size(viewport_size.width, viewport_size.height * 0.8f));
2993 EXPECT_EQ(initial_page_scale_factor,
2994 web_view_helper.GetWebView()->PageScaleFactor());
2995 EXPECT_EQ(expected_scroll_offset,
2996 web_view_helper.LocalMainFrame()->GetScrollOffset());
2997 web_view_helper.Resize(
2998 gfx::Size(viewport_size.width, viewport_size.height * 0.8f));
2999 EXPECT_EQ(initial_page_scale_factor,
3000 web_view_helper.GetWebView()->PageScaleFactor());
3001 EXPECT_EQ(expected_scroll_offset,
3002 web_view_helper.LocalMainFrame()->GetScrollOffset());
3003 }
3004 }
3005 };
3006
TEST_F(WebFrameResizeTest,ResizeYieldsCorrectScrollAndScaleForWidthEqualsDeviceWidth)3007 TEST_F(WebFrameResizeTest,
3008 ResizeYieldsCorrectScrollAndScaleForWidthEqualsDeviceWidth) {
3009 // With width=device-width, pageScaleFactor is preserved across resizes as
3010 // long as the content adjusts according to the device-width.
3011 const char* url = "resize_scroll_mobile.html";
3012 const float kInitialPageScaleFactor = 1;
3013 const WebSize scroll_offset(0, 50);
3014 const WebSize viewport_size(120, 160);
3015 const bool kShouldScaleRelativeToViewportWidth = true;
3016
3017 TestResizeYieldsCorrectScrollAndScale(url, kInitialPageScaleFactor,
3018 scroll_offset, viewport_size,
3019 kShouldScaleRelativeToViewportWidth);
3020 }
3021
TEST_F(WebFrameResizeTest,ResizeYieldsCorrectScrollAndScaleForMinimumScale)3022 TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForMinimumScale) {
3023 // This tests a scenario where minimum-scale is set to 1.0, but some element
3024 // on the page is slightly larger than the portrait width, so our "natural"
3025 // minimum-scale would be lower. In that case, we should stick to 1.0 scale
3026 // on rotation and not do anything strange.
3027 const char* url = "resize_scroll_minimum_scale.html";
3028 const float kInitialPageScaleFactor = 1;
3029 const WebSize scroll_offset(0, 0);
3030 const WebSize viewport_size(240, 320);
3031 const bool kShouldScaleRelativeToViewportWidth = false;
3032
3033 TestResizeYieldsCorrectScrollAndScale(url, kInitialPageScaleFactor,
3034 scroll_offset, viewport_size,
3035 kShouldScaleRelativeToViewportWidth);
3036 }
3037
TEST_F(WebFrameResizeTest,ResizeYieldsCorrectScrollAndScaleForFixedWidth)3038 TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForFixedWidth) {
3039 // With a fixed width, pageScaleFactor scales by the relative change in
3040 // viewport width.
3041 const char* url = "resize_scroll_fixed_width.html";
3042 const float kInitialPageScaleFactor = 2;
3043 const WebSize scroll_offset(0, 200);
3044 const WebSize viewport_size(240, 320);
3045 const bool kShouldScaleRelativeToViewportWidth = true;
3046
3047 TestResizeYieldsCorrectScrollAndScale(url, kInitialPageScaleFactor,
3048 scroll_offset, viewport_size,
3049 kShouldScaleRelativeToViewportWidth);
3050 }
3051
TEST_F(WebFrameResizeTest,ResizeYieldsCorrectScrollAndScaleForFixedLayout)3052 TEST_F(WebFrameResizeTest, ResizeYieldsCorrectScrollAndScaleForFixedLayout) {
3053 // With a fixed layout, pageScaleFactor scales by the relative change in
3054 // viewport width.
3055 const char* url = "resize_scroll_fixed_layout.html";
3056 const float kInitialPageScaleFactor = 2;
3057 const WebSize scroll_offset(200, 400);
3058 const WebSize viewport_size(320, 240);
3059 const bool kShouldScaleRelativeToViewportWidth = true;
3060
3061 TestResizeYieldsCorrectScrollAndScale(url, kInitialPageScaleFactor,
3062 scroll_offset, viewport_size,
3063 kShouldScaleRelativeToViewportWidth);
3064 }
3065
TEST_F(WebFrameTest,pageScaleFactorUpdatesScrollbars)3066 TEST_F(WebFrameTest, pageScaleFactorUpdatesScrollbars) {
3067 RegisterMockedHttpURLLoad("fixed_layout.html");
3068
3069 FixedLayoutTestWebWidgetClient client;
3070 client.screen_info_.device_scale_factor = 1;
3071 int viewport_width = 640;
3072 int viewport_height = 480;
3073
3074 frame_test_helpers::WebViewHelper web_view_helper;
3075 web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
3076 nullptr, &client, ConfigureAndroid);
3077 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3078
3079 LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
3080 ScrollableArea* scrollable_area = view->LayoutViewport();
3081 EXPECT_EQ(scrollable_area->ScrollSize(kHorizontalScrollbar),
3082 scrollable_area->ContentsSize().Width() - view->Width());
3083 EXPECT_EQ(scrollable_area->ScrollSize(kVerticalScrollbar),
3084 scrollable_area->ContentsSize().Height() - view->Height());
3085
3086 web_view_helper.GetWebView()->SetPageScaleFactor(10);
3087
3088 EXPECT_EQ(scrollable_area->ScrollSize(kHorizontalScrollbar),
3089 scrollable_area->ContentsSize().Width() - view->Width());
3090 EXPECT_EQ(scrollable_area->ScrollSize(kVerticalScrollbar),
3091 scrollable_area->ContentsSize().Height() - view->Height());
3092 }
3093
TEST_F(WebFrameTest,CanOverrideScaleLimits)3094 TEST_F(WebFrameTest, CanOverrideScaleLimits) {
3095 RegisterMockedHttpURLLoad("no_scale_for_you.html");
3096
3097 FixedLayoutTestWebWidgetClient client;
3098 client.screen_info_.device_scale_factor = 1;
3099 int viewport_width = 640;
3100 int viewport_height = 480;
3101
3102 frame_test_helpers::WebViewHelper web_view_helper;
3103 web_view_helper.InitializeAndLoad(base_url_ + "no_scale_for_you.html",
3104 nullptr, nullptr, &client,
3105 ConfigureAndroid);
3106 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5);
3107 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3108
3109 EXPECT_EQ(2.0f, web_view_helper.GetWebView()->MinimumPageScaleFactor());
3110 EXPECT_EQ(2.0f, web_view_helper.GetWebView()->MaximumPageScaleFactor());
3111
3112 web_view_helper.GetWebView()->SetIgnoreViewportTagScaleLimits(true);
3113 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3114
3115 EXPECT_EQ(1.0f, web_view_helper.GetWebView()->MinimumPageScaleFactor());
3116 EXPECT_EQ(5.0f, web_view_helper.GetWebView()->MaximumPageScaleFactor());
3117
3118 web_view_helper.GetWebView()->SetIgnoreViewportTagScaleLimits(false);
3119 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3120
3121 EXPECT_EQ(2.0f, web_view_helper.GetWebView()->MinimumPageScaleFactor());
3122 EXPECT_EQ(2.0f, web_view_helper.GetWebView()->MaximumPageScaleFactor());
3123 }
3124
3125 // Android doesn't have scrollbars on the main LocalFrameView
3126 #if defined(OS_ANDROID)
TEST_F(WebFrameTest,DISABLED_updateOverlayScrollbarLayers)3127 TEST_F(WebFrameTest, DISABLED_updateOverlayScrollbarLayers)
3128 #else
3129 TEST_F(WebFrameTest, updateOverlayScrollbarLayers)
3130 #endif
3131 {
3132 RegisterMockedHttpURLLoad("large-div.html");
3133
3134 int view_width = 500;
3135 int view_height = 500;
3136
3137 FixedLayoutTestWebWidgetClient client;
3138 frame_test_helpers::WebViewHelper web_view_helper;
3139 web_view_helper.Initialize(nullptr, nullptr, &client,
3140 &ConfigureCompositingWebView);
3141
3142 web_view_helper.Resize(gfx::Size(view_width, view_height));
3143 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
3144 base_url_ + "large-div.html");
3145
3146 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3147 LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
3148 EXPECT_TRUE(view->LayoutViewport()->LayerForHorizontalScrollbar());
3149 EXPECT_TRUE(view->LayoutViewport()->LayerForVerticalScrollbar());
3150
3151 web_view_helper.Resize(gfx::Size(view_width * 10, view_height * 10));
3152 EXPECT_FALSE(view->LayoutViewport()->LayerForHorizontalScrollbar());
3153 EXPECT_FALSE(view->LayoutViewport()->LayerForVerticalScrollbar());
3154 }
3155
SetScaleAndScrollAndLayout(WebViewImpl * web_view,const gfx::Point & scroll,float scale)3156 void SetScaleAndScrollAndLayout(WebViewImpl* web_view,
3157 const gfx::Point& scroll,
3158 float scale) {
3159 web_view->SetPageScaleFactor(scale);
3160 web_view->MainFrameImpl()->SetScrollOffset(WebSize(scroll.x(), scroll.y()));
3161 web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
3162 DocumentUpdateReason::kTest);
3163 }
3164
SimulatePageScale(WebViewImpl * web_view_impl,float & scale)3165 void SimulatePageScale(WebViewImpl* web_view_impl, float& scale) {
3166 float scale_delta =
3167 web_view_impl->FakePageScaleAnimationPageScaleForTesting() /
3168 web_view_impl->PageScaleFactor();
3169 web_view_impl->MainFrameWidget()->ApplyViewportChangesForTesting(
3170 {gfx::ScrollOffset(), gfx::Vector2dF(), scale_delta, false, 0, 0,
3171 cc::BrowserControlsState::kBoth});
3172 scale = web_view_impl->PageScaleFactor();
3173 }
3174
ComputeBlockBoundHelper(WebViewImpl * web_view_impl,const gfx::Point & point,bool ignore_clipping)3175 WebRect ComputeBlockBoundHelper(WebViewImpl* web_view_impl,
3176 const gfx::Point& point,
3177 bool ignore_clipping) {
3178 DCHECK(web_view_impl->MainFrameImpl());
3179 WebFrameWidgetBase* widget =
3180 web_view_impl->MainFrameImpl()->FrameWidgetImpl();
3181 DCHECK(widget);
3182 return widget->ComputeBlockBound(point, ignore_clipping);
3183 }
3184
SimulateDoubleTap(WebViewImpl * web_view_impl,gfx::Point & point,float & scale)3185 void SimulateDoubleTap(WebViewImpl* web_view_impl,
3186 gfx::Point& point,
3187 float& scale) {
3188 web_view_impl->AnimateDoubleTapZoom(
3189 IntPoint(point), ComputeBlockBoundHelper(web_view_impl, point, false));
3190 EXPECT_TRUE(web_view_impl->FakeDoubleTapAnimationPendingForTesting());
3191 SimulatePageScale(web_view_impl, scale);
3192 }
3193
TEST_F(WebFrameTest,DivAutoZoomParamsTest)3194 TEST_F(WebFrameTest, DivAutoZoomParamsTest) {
3195 RegisterMockedHttpURLLoad("get_scale_for_auto_zoom_into_div_test.html");
3196
3197 const float kDeviceScaleFactor = 2.0f;
3198 int viewport_width = 640 / kDeviceScaleFactor;
3199 int viewport_height = 1280 / kDeviceScaleFactor;
3200 float double_tap_zoom_already_legible_ratio = 1.2f;
3201 frame_test_helpers::WebViewHelper web_view_helper;
3202 web_view_helper.InitializeAndLoad(
3203 base_url_ + "get_scale_for_auto_zoom_into_div_test.html", nullptr,
3204 nullptr, nullptr, ConfigureAndroid);
3205 web_view_helper.GetWebView()->SetDeviceScaleFactor(kDeviceScaleFactor);
3206 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.01f, 4);
3207 web_view_helper.GetWebView()->SetPageScaleFactor(0.5f);
3208 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3209
3210 WebRect wide_div(200, 100, 400, 150);
3211 WebRect tall_div(200, 300, 400, 800);
3212 gfx::Point double_tap_point_wide(wide_div.x + 50, wide_div.y + 50);
3213 gfx::Point double_tap_point_tall(tall_div.x + 50, tall_div.y + 50);
3214 float scale;
3215 IntPoint scroll;
3216
3217 float double_tap_zoom_already_legible_scale =
3218 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3219 double_tap_zoom_already_legible_ratio;
3220
3221 // Test double-tap zooming into wide div.
3222 WebRect wide_block_bound = ComputeBlockBoundHelper(
3223 web_view_helper.GetWebView(), double_tap_point_wide, false);
3224 web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
3225 double_tap_point_wide, wide_block_bound, kTouchPointPadding,
3226 double_tap_zoom_already_legible_scale, scale, scroll);
3227 // The div should horizontally fill the screen (modulo margins), and
3228 // vertically centered (modulo integer rounding).
3229 EXPECT_NEAR(viewport_width / (float)wide_div.width, scale, 0.1);
3230 EXPECT_NEAR(wide_div.x, scroll.X(), 20);
3231 EXPECT_EQ(0, scroll.Y());
3232
3233 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), scroll, scale);
3234
3235 // Test zoom out back to minimum scale.
3236 wide_block_bound = ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3237 double_tap_point_wide, false);
3238 web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
3239 double_tap_point_wide, wide_block_bound, kTouchPointPadding,
3240 double_tap_zoom_already_legible_scale, scale, scroll);
3241 // FIXME: Looks like we are missing EXPECTs here.
3242
3243 scale = web_view_helper.GetWebView()->MinimumPageScaleFactor();
3244 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(), scale);
3245
3246 // Test double-tap zooming into tall div.
3247 WebRect tall_block_bound = ComputeBlockBoundHelper(
3248 web_view_helper.GetWebView(), double_tap_point_tall, false);
3249 web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
3250 double_tap_point_tall, tall_block_bound, kTouchPointPadding,
3251 double_tap_zoom_already_legible_scale, scale, scroll);
3252 // The div should start at the top left of the viewport.
3253 EXPECT_NEAR(viewport_width / (float)tall_div.width, scale, 0.1);
3254 EXPECT_NEAR(tall_div.x, scroll.X(), 20);
3255 EXPECT_NEAR(tall_div.y, scroll.Y(), 20);
3256 }
3257
TEST_F(WebFrameTest,DivAutoZoomWideDivTest)3258 TEST_F(WebFrameTest, DivAutoZoomWideDivTest) {
3259 RegisterMockedHttpURLLoad("get_wide_div_for_auto_zoom_test.html");
3260
3261 const float kDeviceScaleFactor = 2.0f;
3262 int viewport_width = 640 / kDeviceScaleFactor;
3263 int viewport_height = 1280 / kDeviceScaleFactor;
3264 float double_tap_zoom_already_legible_ratio = 1.2f;
3265 frame_test_helpers::WebViewHelper web_view_helper;
3266 web_view_helper.InitializeAndLoad(
3267 base_url_ + "get_wide_div_for_auto_zoom_test.html", nullptr, nullptr,
3268 nullptr, ConfigureAndroid);
3269 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3270 web_view_helper.GetWebView()->SetDeviceScaleFactor(kDeviceScaleFactor);
3271 web_view_helper.GetWebView()->SetPageScaleFactor(1.0f);
3272 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3273
3274 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3275
3276 float double_tap_zoom_already_legible_scale =
3277 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3278 double_tap_zoom_already_legible_ratio;
3279
3280 WebRect div(0, 100, viewport_width, 150);
3281 gfx::Point point(div.x + 50, div.y + 50);
3282 float scale;
3283 SetScaleAndScrollAndLayout(
3284 web_view_helper.GetWebView(), gfx::Point(),
3285 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3286 (1 + double_tap_zoom_already_legible_ratio) / 2);
3287
3288 SimulateDoubleTap(web_view_helper.GetWebView(), point, scale);
3289 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3290 SimulateDoubleTap(web_view_helper.GetWebView(), point, scale);
3291 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3292 scale);
3293 }
3294
TEST_F(WebFrameTest,DivAutoZoomVeryTallTest)3295 TEST_F(WebFrameTest, DivAutoZoomVeryTallTest) {
3296 // When a block is taller than the viewport and a zoom targets a lower part
3297 // of it, then we should keep the target point onscreen instead of snapping
3298 // back up the top of the block.
3299 RegisterMockedHttpURLLoad("very_tall_div.html");
3300
3301 const float kDeviceScaleFactor = 2.0f;
3302 int viewport_width = 640 / kDeviceScaleFactor;
3303 int viewport_height = 1280 / kDeviceScaleFactor;
3304 frame_test_helpers::WebViewHelper web_view_helper;
3305 web_view_helper.InitializeAndLoad(base_url_ + "very_tall_div.html", nullptr,
3306 nullptr, nullptr, ConfigureAndroid);
3307 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3308 web_view_helper.GetWebView()->SetDeviceScaleFactor(kDeviceScaleFactor);
3309 web_view_helper.GetWebView()->SetPageScaleFactor(1.0f);
3310 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3311
3312 WebRect div(200, 300, 400, 5000);
3313 gfx::Point point(div.x + 50, div.y + 3000);
3314 float scale;
3315 IntPoint scroll;
3316
3317 WebRect block_bound =
3318 ComputeBlockBoundHelper(web_view_helper.GetWebView(), point, true);
3319 web_view_helper.GetWebView()->ComputeScaleAndScrollForBlockRect(
3320 point, block_bound, 0, 1.0f, scale, scroll);
3321 EXPECT_EQ(scale, 1.0f);
3322 EXPECT_EQ(scroll.Y(), 2660);
3323 }
3324
TEST_F(WebFrameTest,DivAutoZoomMultipleDivsTest)3325 TEST_F(WebFrameTest, DivAutoZoomMultipleDivsTest) {
3326 RegisterMockedHttpURLLoad("get_multiple_divs_for_auto_zoom_test.html");
3327
3328 const float kDeviceScaleFactor = 2.0f;
3329 int viewport_width = 640 / kDeviceScaleFactor;
3330 int viewport_height = 1280 / kDeviceScaleFactor;
3331 float double_tap_zoom_already_legible_ratio = 1.2f;
3332 frame_test_helpers::WebViewHelper web_view_helper;
3333 web_view_helper.InitializeAndLoad(
3334 base_url_ + "get_multiple_divs_for_auto_zoom_test.html", nullptr, nullptr,
3335 nullptr, ConfigureAndroid);
3336 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3337 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.5f, 4);
3338 web_view_helper.GetWebView()->SetDeviceScaleFactor(kDeviceScaleFactor);
3339 web_view_helper.GetWebView()->SetPageScaleFactor(0.5f);
3340 web_view_helper.GetWebView()->SetMaximumLegibleScale(1.f);
3341 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3342
3343 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3344
3345 WebRect top_div(200, 100, 200, 150);
3346 WebRect bottom_div(200, 300, 200, 150);
3347 gfx::Point top_point(top_div.x + 50, top_div.y + 50);
3348 gfx::Point bottom_point(bottom_div.x + 50, bottom_div.y + 50);
3349 float scale;
3350 SetScaleAndScrollAndLayout(
3351 web_view_helper.GetWebView(), gfx::Point(),
3352 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3353 (1 + double_tap_zoom_already_legible_ratio) / 2);
3354
3355 // Test double tap on two different divs. After first zoom, we should go back
3356 // to minimum page scale with a second double tap.
3357 SimulateDoubleTap(web_view_helper.GetWebView(), top_point, scale);
3358 EXPECT_FLOAT_EQ(1, scale);
3359 SimulateDoubleTap(web_view_helper.GetWebView(), bottom_point, scale);
3360 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3361 scale);
3362
3363 // If the user pinch zooms after double tap, a second double tap should zoom
3364 // back to the div.
3365 SimulateDoubleTap(web_view_helper.GetWebView(), top_point, scale);
3366 EXPECT_FLOAT_EQ(1, scale);
3367 web_view_helper.GetWebView()
3368 ->MainFrameWidget()
3369 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3370 0.6f, false, 0, 0,
3371 cc::BrowserControlsState::kBoth});
3372 SimulateDoubleTap(web_view_helper.GetWebView(), bottom_point, scale);
3373 EXPECT_FLOAT_EQ(1, scale);
3374 SimulateDoubleTap(web_view_helper.GetWebView(), bottom_point, scale);
3375 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3376 scale);
3377
3378 // If we didn't yet get an auto-zoom update and a second double-tap arrives,
3379 // should go back to minimum scale.
3380 web_view_helper.GetWebView()
3381 ->MainFrameWidget()
3382 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3383 1.1f, false, 0, 0,
3384 cc::BrowserControlsState::kBoth});
3385
3386 WebRect block_bounds =
3387 ComputeBlockBoundHelper(web_view_helper.GetWebView(), top_point, false);
3388 web_view_helper.GetWebView()->AnimateDoubleTapZoom(IntPoint(top_point),
3389 block_bounds);
3390 EXPECT_TRUE(
3391 web_view_helper.GetWebView()->FakeDoubleTapAnimationPendingForTesting());
3392 SimulateDoubleTap(web_view_helper.GetWebView(), bottom_point, scale);
3393 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3394 scale);
3395 }
3396
TEST_F(WebFrameTest,DivAutoZoomScaleBoundsTest)3397 TEST_F(WebFrameTest, DivAutoZoomScaleBoundsTest) {
3398 RegisterMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
3399
3400 int viewport_width = 320;
3401 int viewport_height = 480;
3402 float double_tap_zoom_already_legible_ratio = 1.2f;
3403 frame_test_helpers::WebViewHelper web_view_helper;
3404 web_view_helper.InitializeAndLoad(
3405 base_url_ + "get_scale_bounds_check_for_auto_zoom_test.html", nullptr,
3406 nullptr, nullptr, ConfigureAndroid);
3407 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3408 web_view_helper.GetWebView()->SetDeviceScaleFactor(1.5f);
3409 web_view_helper.GetWebView()->SetMaximumLegibleScale(1.f);
3410 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3411
3412 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3413
3414 WebRect div(200, 100, 200, 150);
3415 gfx::Point double_tap_point(div.x + 50, div.y + 50);
3416 float scale;
3417
3418 // Test double tap scale bounds.
3419 // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1
3420 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.5f, 4);
3421 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3422 float double_tap_zoom_already_legible_scale =
3423 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3424 double_tap_zoom_already_legible_ratio;
3425 SetScaleAndScrollAndLayout(
3426 web_view_helper.GetWebView(), gfx::Point(),
3427 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3428 (1 + double_tap_zoom_already_legible_ratio) / 2);
3429 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3430 EXPECT_FLOAT_EQ(1, scale);
3431 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3432 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3433 scale);
3434 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3435 EXPECT_FLOAT_EQ(1, scale);
3436
3437 // Zoom in to reset double_tap_zoom_in_effect flag.
3438 web_view_helper.GetWebView()
3439 ->MainFrameWidget()
3440 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3441 1.1f, false, 0, 0,
3442 cc::BrowserControlsState::kBoth});
3443 // 1 < minimumPageScale < doubleTapZoomAlreadyLegibleScale
3444 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(1.1f, 4);
3445 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3446 double_tap_zoom_already_legible_scale =
3447 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3448 double_tap_zoom_already_legible_ratio;
3449 SetScaleAndScrollAndLayout(
3450 web_view_helper.GetWebView(), gfx::Point(),
3451 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3452 (1 + double_tap_zoom_already_legible_ratio) / 2);
3453 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3454 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3455 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3456 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3457 scale);
3458 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3459 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3460
3461 // Zoom in to reset double_tap_zoom_in_effect flag.
3462 web_view_helper.GetWebView()
3463 ->MainFrameWidget()
3464 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3465 1.1f, false, 0, 0,
3466 cc::BrowserControlsState::kBoth});
3467 // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale
3468 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.95f, 4);
3469 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3470 double_tap_zoom_already_legible_scale =
3471 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3472 double_tap_zoom_already_legible_ratio;
3473 SetScaleAndScrollAndLayout(
3474 web_view_helper.GetWebView(), gfx::Point(),
3475 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3476 (1 + double_tap_zoom_already_legible_ratio) / 2);
3477 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3478 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3479 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3480 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3481 scale);
3482 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3483 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3484 }
3485
TEST_F(WebFrameTest,DivAutoZoomScaleLegibleScaleTest)3486 TEST_F(WebFrameTest, DivAutoZoomScaleLegibleScaleTest) {
3487 RegisterMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
3488
3489 int viewport_width = 320;
3490 int viewport_height = 480;
3491 float double_tap_zoom_already_legible_ratio = 1.2f;
3492 float maximum_legible_scale_factor = 1.13f;
3493 frame_test_helpers::WebViewHelper web_view_helper;
3494 web_view_helper.InitializeAndLoad(
3495 base_url_ + "get_scale_bounds_check_for_auto_zoom_test.html", nullptr,
3496 nullptr, nullptr, ConfigureAndroid);
3497 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3498 web_view_helper.GetWebView()->SetMaximumLegibleScale(
3499 maximum_legible_scale_factor);
3500 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3501
3502 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3503 web_view_helper.GetWebView()
3504 ->GetPage()
3505 ->GetSettings()
3506 .SetTextAutosizingEnabled(true);
3507
3508 WebRect div(200, 100, 200, 150);
3509 gfx::Point double_tap_point(div.x + 50, div.y + 50);
3510 float scale;
3511
3512 // Test double tap scale bounds.
3513 // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1 <
3514 // maximumLegibleScaleFactor
3515 float legible_scale = maximum_legible_scale_factor;
3516 SetScaleAndScrollAndLayout(
3517 web_view_helper.GetWebView(), gfx::Point(),
3518 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3519 (1 + double_tap_zoom_already_legible_ratio) / 2);
3520 float double_tap_zoom_already_legible_scale =
3521 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3522 double_tap_zoom_already_legible_ratio;
3523 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.5f, 4);
3524 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3525 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3526 EXPECT_FLOAT_EQ(legible_scale, scale);
3527 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3528 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3529 scale);
3530 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3531 EXPECT_FLOAT_EQ(legible_scale, scale);
3532
3533 // Zoom in to reset double_tap_zoom_in_effect flag.
3534 web_view_helper.GetWebView()
3535 ->MainFrameWidget()
3536 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3537 1.1f, false, 0, 0,
3538 cc::BrowserControlsState::kBoth});
3539 // 1 < maximumLegibleScaleFactor < minimumPageScale <
3540 // doubleTapZoomAlreadyLegibleScale
3541 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(1.0f, 4);
3542 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3543 double_tap_zoom_already_legible_scale =
3544 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3545 double_tap_zoom_already_legible_ratio;
3546 SetScaleAndScrollAndLayout(
3547 web_view_helper.GetWebView(), gfx::Point(),
3548 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3549 (1 + double_tap_zoom_already_legible_ratio) / 2);
3550 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3551 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3552 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3553 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3554 scale);
3555 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3556 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3557
3558 // Zoom in to reset double_tap_zoom_in_effect flag.
3559 web_view_helper.GetWebView()
3560 ->MainFrameWidget()
3561 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3562 1.1f, false, 0, 0,
3563 cc::BrowserControlsState::kBoth});
3564 // minimumPageScale < 1 < maximumLegibleScaleFactor <
3565 // doubleTapZoomAlreadyLegibleScale
3566 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.95f, 4);
3567 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3568 double_tap_zoom_already_legible_scale =
3569 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3570 double_tap_zoom_already_legible_ratio;
3571 SetScaleAndScrollAndLayout(
3572 web_view_helper.GetWebView(), gfx::Point(),
3573 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3574 (1 + double_tap_zoom_already_legible_ratio) / 2);
3575 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3576 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3577 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3578 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3579 scale);
3580 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3581 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3582
3583 // Zoom in to reset double_tap_zoom_in_effect flag.
3584 web_view_helper.GetWebView()
3585 ->MainFrameWidget()
3586 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3587 1.1f, false, 0, 0,
3588 cc::BrowserControlsState::kBoth});
3589 // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale <
3590 // maximumLegibleScaleFactor
3591 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.9f, 4);
3592 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3593 double_tap_zoom_already_legible_scale =
3594 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3595 double_tap_zoom_already_legible_ratio;
3596 SetScaleAndScrollAndLayout(
3597 web_view_helper.GetWebView(), gfx::Point(),
3598 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3599 (1 + double_tap_zoom_already_legible_ratio) / 2);
3600 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3601 EXPECT_FLOAT_EQ(legible_scale, scale);
3602 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3603 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3604 scale);
3605 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3606 EXPECT_FLOAT_EQ(legible_scale, scale);
3607 }
3608
TEST_F(WebFrameTest,DivAutoZoomScaleFontScaleFactorTest)3609 TEST_F(WebFrameTest, DivAutoZoomScaleFontScaleFactorTest) {
3610 RegisterMockedHttpURLLoad("get_scale_bounds_check_for_auto_zoom_test.html");
3611
3612 int viewport_width = 320;
3613 int viewport_height = 480;
3614 float double_tap_zoom_already_legible_ratio = 1.2f;
3615 float accessibility_font_scale_factor = 1.13f;
3616 frame_test_helpers::WebViewHelper web_view_helper;
3617 web_view_helper.InitializeAndLoad(
3618 base_url_ + "get_scale_bounds_check_for_auto_zoom_test.html", nullptr,
3619 nullptr, nullptr, ConfigureAndroid);
3620 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3621 web_view_helper.GetWebView()->SetMaximumLegibleScale(1.f);
3622 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3623
3624 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3625 web_view_helper.GetWebView()
3626 ->GetPage()
3627 ->GetSettings()
3628 .SetTextAutosizingEnabled(true);
3629 web_view_helper.GetWebView()
3630 ->GetPage()
3631 ->GetSettings()
3632 .SetAccessibilityFontScaleFactor(accessibility_font_scale_factor);
3633
3634 WebRect div(200, 100, 200, 150);
3635 gfx::Point double_tap_point(div.x + 50, div.y + 50);
3636 float scale;
3637
3638 // Test double tap scale bounds.
3639 // minimumPageScale < doubleTapZoomAlreadyLegibleScale < 1 <
3640 // accessibilityFontScaleFactor
3641 float legible_scale = accessibility_font_scale_factor;
3642 SetScaleAndScrollAndLayout(
3643 web_view_helper.GetWebView(), gfx::Point(),
3644 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3645 (1 + double_tap_zoom_already_legible_ratio) / 2);
3646 float double_tap_zoom_already_legible_scale =
3647 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3648 double_tap_zoom_already_legible_ratio;
3649 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.5f, 4);
3650 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3651 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3652 EXPECT_FLOAT_EQ(legible_scale, scale);
3653 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3654 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3655 scale);
3656 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3657 EXPECT_FLOAT_EQ(legible_scale, scale);
3658
3659 // Zoom in to reset double_tap_zoom_in_effect flag.
3660 web_view_helper.GetWebView()
3661 ->MainFrameWidget()
3662 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3663 1.1f, false, 0, 0,
3664 cc::BrowserControlsState::kBoth});
3665 // 1 < accessibilityFontScaleFactor < minimumPageScale <
3666 // doubleTapZoomAlreadyLegibleScale
3667 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(1.0f, 4);
3668 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3669 double_tap_zoom_already_legible_scale =
3670 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3671 double_tap_zoom_already_legible_ratio;
3672 SetScaleAndScrollAndLayout(
3673 web_view_helper.GetWebView(), gfx::Point(),
3674 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3675 (1 + double_tap_zoom_already_legible_ratio) / 2);
3676 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3677 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3678 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3679 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3680 scale);
3681 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3682 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3683
3684 // Zoom in to reset double_tap_zoom_in_effect flag.
3685 web_view_helper.GetWebView()
3686 ->MainFrameWidget()
3687 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3688 1.1f, false, 0, 0,
3689 cc::BrowserControlsState::kBoth});
3690 // minimumPageScale < 1 < accessibilityFontScaleFactor <
3691 // doubleTapZoomAlreadyLegibleScale
3692 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.95f, 4);
3693 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3694 double_tap_zoom_already_legible_scale =
3695 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3696 double_tap_zoom_already_legible_ratio;
3697 SetScaleAndScrollAndLayout(
3698 web_view_helper.GetWebView(), gfx::Point(),
3699 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3700 (1 + double_tap_zoom_already_legible_ratio) / 2);
3701 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3702 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3703 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3704 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3705 scale);
3706 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3707 EXPECT_FLOAT_EQ(double_tap_zoom_already_legible_scale, scale);
3708
3709 // Zoom in to reset double_tap_zoom_in_effect flag.
3710 web_view_helper.GetWebView()
3711 ->MainFrameWidget()
3712 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
3713 1.1f, false, 0, 0,
3714 cc::BrowserControlsState::kBoth});
3715 // minimumPageScale < 1 < doubleTapZoomAlreadyLegibleScale <
3716 // accessibilityFontScaleFactor
3717 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.9f, 4);
3718 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
3719 double_tap_zoom_already_legible_scale =
3720 web_view_helper.GetWebView()->MinimumPageScaleFactor() *
3721 double_tap_zoom_already_legible_ratio;
3722 SetScaleAndScrollAndLayout(
3723 web_view_helper.GetWebView(), gfx::Point(),
3724 (web_view_helper.GetWebView()->MinimumPageScaleFactor()) *
3725 (1 + double_tap_zoom_already_legible_ratio) / 2);
3726 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3727 EXPECT_FLOAT_EQ(legible_scale, scale);
3728 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3729 EXPECT_FLOAT_EQ(web_view_helper.GetWebView()->MinimumPageScaleFactor(),
3730 scale);
3731 SimulateDoubleTap(web_view_helper.GetWebView(), double_tap_point, scale);
3732 EXPECT_FLOAT_EQ(legible_scale, scale);
3733 }
3734
TEST_F(WebFrameTest,BlockBoundTest)3735 TEST_F(WebFrameTest, BlockBoundTest) {
3736 RegisterMockedHttpURLLoad("block_bound.html");
3737
3738 frame_test_helpers::WebViewHelper web_view_helper;
3739 web_view_helper.InitializeAndLoad(base_url_ + "block_bound.html", nullptr,
3740 nullptr, nullptr, ConfigureAndroid);
3741 web_view_helper.Resize(gfx::Size(300, 300));
3742
3743 IntRect rect_back = IntRect(0, 0, 200, 200);
3744 IntRect rect_left_top = IntRect(10, 10, 80, 80);
3745 IntRect rect_right_bottom = IntRect(110, 110, 80, 80);
3746 IntRect block_bound;
3747
3748 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3749 gfx::Point(9, 9), true));
3750 EXPECT_EQ(rect_back, block_bound);
3751
3752 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3753 gfx::Point(10, 10), true));
3754 EXPECT_EQ(rect_left_top, block_bound);
3755
3756 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3757 gfx::Point(50, 50), true));
3758 EXPECT_EQ(rect_left_top, block_bound);
3759
3760 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3761 gfx::Point(89, 89), true));
3762 EXPECT_EQ(rect_left_top, block_bound);
3763
3764 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3765 gfx::Point(90, 90), true));
3766 EXPECT_EQ(rect_back, block_bound);
3767
3768 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3769 gfx::Point(109, 109), true));
3770 EXPECT_EQ(rect_back, block_bound);
3771
3772 block_bound = IntRect(ComputeBlockBoundHelper(web_view_helper.GetWebView(),
3773 gfx::Point(110, 110), true));
3774 EXPECT_EQ(rect_right_bottom, block_bound);
3775 }
3776
TEST_F(WebFrameTest,DontZoomInOnFocusedInTouchAction)3777 TEST_F(WebFrameTest, DontZoomInOnFocusedInTouchAction) {
3778 RegisterMockedHttpURLLoad("textbox_in_touch_action.html");
3779
3780 int viewport_width = 600;
3781 int viewport_height = 1000;
3782
3783 frame_test_helpers::WebViewHelper web_view_helper;
3784 web_view_helper.InitializeAndLoad(base_url_ + "textbox_in_touch_action.html");
3785 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 4);
3786 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3787 web_view_helper.GetWebView()
3788 ->GetPage()
3789 ->GetSettings()
3790 .SetTextAutosizingEnabled(false);
3791 web_view_helper.GetWebView()
3792 ->GetSettings()
3793 ->SetAutoZoomFocusedNodeToLegibleScale(true);
3794 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3795
3796 float initial_scale = web_view_helper.GetWebView()->PageScaleFactor();
3797
3798 // Focus the first textbox that's in a touch-action: pan-x ancestor, this
3799 // shouldn't cause an autozoom since pan-x disables pinch-zoom.
3800 web_view_helper.GetWebView()->AdvanceFocus(false);
3801 web_view_helper.GetWebView()
3802 ->MainFrameImpl()
3803 ->FrameWidget()
3804 ->ScrollFocusedEditableElementIntoView();
3805 EXPECT_EQ(
3806 web_view_helper.GetWebView()->FakePageScaleAnimationPageScaleForTesting(),
3807 0);
3808
3809 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
3810 initial_scale);
3811 ASSERT_EQ(initial_scale, web_view_helper.GetWebView()->PageScaleFactor());
3812
3813 // Focus the second textbox that's in a touch-action: manipulation ancestor,
3814 // this should cause an autozoom since it allows pinch-zoom.
3815 web_view_helper.GetWebView()->AdvanceFocus(false);
3816 web_view_helper.GetWebView()
3817 ->MainFrameImpl()
3818 ->FrameWidget()
3819 ->ScrollFocusedEditableElementIntoView();
3820 EXPECT_GT(
3821 web_view_helper.GetWebView()->FakePageScaleAnimationPageScaleForTesting(),
3822 initial_scale);
3823
3824 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
3825 initial_scale);
3826 ASSERT_EQ(initial_scale, web_view_helper.GetWebView()->PageScaleFactor());
3827
3828 // Focus the third textbox that has a touch-action: pan-x ancestor, this
3829 // should cause an autozoom since it's seperated from the node with the
3830 // touch-action by an overflow:scroll element.
3831 web_view_helper.GetWebView()->AdvanceFocus(false);
3832 web_view_helper.GetWebView()
3833 ->MainFrameImpl()
3834 ->FrameWidget()
3835 ->ScrollFocusedEditableElementIntoView();
3836 EXPECT_GT(
3837 web_view_helper.GetWebView()->FakePageScaleAnimationPageScaleForTesting(),
3838 initial_scale);
3839 }
3840
TEST_F(WebFrameTest,DivScrollIntoEditableTest)3841 TEST_F(WebFrameTest, DivScrollIntoEditableTest) {
3842 RegisterMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html");
3843
3844 const bool kAutoZoomToLegibleScale = true;
3845 int viewport_width = 450;
3846 int viewport_height = 300;
3847 float left_box_ratio = 0.3f;
3848 int caret_padding = 10;
3849 float min_readable_caret_height = 16.0f;
3850 frame_test_helpers::WebViewHelper web_view_helper;
3851 web_view_helper.InitializeAndLoad(
3852 base_url_ + "get_scale_for_zoom_into_editable_test.html");
3853 web_view_helper.GetWebView()
3854 ->GetPage()
3855 ->GetSettings()
3856 .SetTextAutosizingEnabled(false);
3857 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3858 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 4);
3859
3860 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3861
3862 WebRect edit_box_with_text(200, 200, 250, 20);
3863 WebRect edit_box_with_no_text(200, 250, 250, 20);
3864
3865 // Test scrolling the focused node
3866 // The edit box is shorter and narrower than the viewport when legible.
3867 web_view_helper.GetWebView()->AdvanceFocus(false);
3868 // Set the caret to the end of the input box.
3869 web_view_helper.GetWebView()
3870 ->MainFrameImpl()
3871 ->GetDocument()
3872 .GetElementById("EditBoxWithText")
3873 .To<WebInputElement>()
3874 .SetSelectionRange(1000, 1000);
3875 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(), 1);
3876 gfx::Rect rect, caret;
3877 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
3878 caret, rect);
3879
3880 // Set the page scale to be smaller than the minimal readable scale.
3881 float initial_scale = min_readable_caret_height / caret.height() * 0.5f;
3882 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
3883 initial_scale);
3884
3885 float scale;
3886 IntPoint scroll;
3887 bool need_animation;
3888 IntRect element_bounds, caret_bounds;
3889 GetElementAndCaretBoundsForFocusedEditableElement(
3890 web_view_helper, element_bounds, caret_bounds);
3891 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
3892 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
3893 need_animation);
3894 EXPECT_TRUE(need_animation);
3895 // The edit box should be left aligned with a margin for possible label.
3896 int h_scroll = edit_box_with_text.x - left_box_ratio * viewport_width / scale;
3897 EXPECT_NEAR(h_scroll, scroll.X(), 2);
3898 int v_scroll = edit_box_with_text.y -
3899 (viewport_height / scale - edit_box_with_text.height) / 2;
3900 EXPECT_NEAR(v_scroll, scroll.Y(), 2);
3901 EXPECT_NEAR(min_readable_caret_height / caret.height(), scale, 0.1);
3902
3903 // The edit box is wider than the viewport when legible.
3904 viewport_width = 200;
3905 viewport_height = 150;
3906 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
3907 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
3908 initial_scale);
3909 GetElementAndCaretBoundsForFocusedEditableElement(
3910 web_view_helper, element_bounds, caret_bounds);
3911 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
3912 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
3913 need_animation);
3914 EXPECT_TRUE(need_animation);
3915 // The caret should be right aligned since the caret would be offscreen when
3916 // the edit box is left aligned.
3917 h_scroll = caret.x() + caret.width() + caret_padding - viewport_width / scale;
3918 EXPECT_NEAR(h_scroll, scroll.X(), 2);
3919 EXPECT_NEAR(min_readable_caret_height / caret.height(), scale, 0.1);
3920
3921 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
3922 initial_scale);
3923 // Move focus to edit box with text.
3924 web_view_helper.GetWebView()->AdvanceFocus(false);
3925 GetElementAndCaretBoundsForFocusedEditableElement(
3926 web_view_helper, element_bounds, caret_bounds);
3927 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
3928 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
3929 need_animation);
3930 EXPECT_TRUE(need_animation);
3931 // The edit box should be left aligned.
3932 h_scroll = edit_box_with_no_text.x;
3933 EXPECT_NEAR(h_scroll, scroll.X(), 2);
3934 v_scroll = edit_box_with_no_text.y -
3935 (viewport_height / scale - edit_box_with_no_text.height) / 2;
3936 EXPECT_NEAR(v_scroll, scroll.Y(), 2);
3937 EXPECT_NEAR(min_readable_caret_height / caret.height(), scale, 0.1);
3938
3939 // Move focus back to the first edit box.
3940 web_view_helper.GetWebView()->AdvanceFocus(true);
3941 // Zoom out slightly.
3942 const float within_tolerance_scale = scale * 0.9f;
3943 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), scroll,
3944 within_tolerance_scale);
3945 // Move focus back to the second edit box.
3946 web_view_helper.GetWebView()->AdvanceFocus(false);
3947 GetElementAndCaretBoundsForFocusedEditableElement(
3948 web_view_helper, element_bounds, caret_bounds);
3949 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
3950 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
3951 need_animation);
3952 // The scale should not be adjusted as the zoomed out scale was sufficiently
3953 // close to the previously focused scale.
3954 EXPECT_FALSE(need_animation);
3955 }
3956
TEST_F(WebFrameTest,DivScrollIntoEditablePreservePageScaleTest)3957 TEST_F(WebFrameTest, DivScrollIntoEditablePreservePageScaleTest) {
3958 RegisterMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html");
3959
3960 const bool kAutoZoomToLegibleScale = true;
3961 const int kViewportWidth = 450;
3962 const int kViewportHeight = 300;
3963 const float kMinReadableCaretHeight = 16.0f;
3964 frame_test_helpers::WebViewHelper web_view_helper;
3965 web_view_helper.InitializeAndLoad(
3966 base_url_ + "get_scale_for_zoom_into_editable_test.html");
3967 web_view_helper.GetWebView()
3968 ->GetPage()
3969 ->GetSettings()
3970 .SetTextAutosizingEnabled(false);
3971 web_view_helper.Resize(gfx::Size(kViewportWidth, kViewportHeight));
3972 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
3973
3974 const WebRect edit_box_with_text(200, 200, 250, 20);
3975
3976 web_view_helper.GetWebView()->AdvanceFocus(false);
3977 // Set the caret to the begining of the input box.
3978 web_view_helper.GetWebView()
3979 ->MainFrameImpl()
3980 ->GetDocument()
3981 .GetElementById("EditBoxWithText")
3982 .To<WebInputElement>()
3983 .SetSelectionRange(0, 0);
3984 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(), 1);
3985 gfx::Rect rect, caret;
3986 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
3987 caret, rect);
3988
3989 // Set the page scale to be twice as large as the minimal readable scale.
3990 float new_scale = kMinReadableCaretHeight / caret.height() * 2.0;
3991 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
3992 new_scale);
3993
3994 float scale;
3995 IntPoint scroll;
3996 bool need_animation;
3997 IntRect element_bounds, caret_bounds;
3998 GetElementAndCaretBoundsForFocusedEditableElement(
3999 web_view_helper, element_bounds, caret_bounds);
4000 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
4001 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
4002 need_animation);
4003 EXPECT_TRUE(need_animation);
4004 // Edit box and caret should be left alinged
4005 int h_scroll = edit_box_with_text.x;
4006 EXPECT_NEAR(h_scroll, scroll.X(), 1);
4007 int v_scroll = edit_box_with_text.y -
4008 (kViewportHeight / scale - edit_box_with_text.height) / 2;
4009 EXPECT_NEAR(v_scroll, scroll.Y(), 1);
4010 // Page scale have to be unchanged
4011 EXPECT_EQ(new_scale, scale);
4012
4013 // Set page scale and scroll such that edit box will be under the screen
4014 new_scale = 3.0;
4015 h_scroll = 200;
4016 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(),
4017 gfx::Point(h_scroll, 0), new_scale);
4018 GetElementAndCaretBoundsForFocusedEditableElement(
4019 web_view_helper, element_bounds, caret_bounds);
4020 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
4021 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
4022 need_animation);
4023 EXPECT_TRUE(need_animation);
4024 // Horizontal scroll have to be the same
4025 EXPECT_NEAR(h_scroll, scroll.X(), 1);
4026 v_scroll = edit_box_with_text.y -
4027 (kViewportHeight / scale - edit_box_with_text.height) / 2;
4028 EXPECT_NEAR(v_scroll, scroll.Y(), 1);
4029 // Page scale have to be unchanged
4030 EXPECT_EQ(new_scale, scale);
4031 }
4032
4033 // Tests the scroll into view functionality when
4034 // autoZoomeFocusedNodeToLegibleScale set to false. i.e. The path non-Android
4035 // platforms take.
TEST_F(WebFrameTest,DivScrollIntoEditableTestZoomToLegibleScaleDisabled)4036 TEST_F(WebFrameTest, DivScrollIntoEditableTestZoomToLegibleScaleDisabled) {
4037 RegisterMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html");
4038
4039 const bool kAutoZoomToLegibleScale = false;
4040 int viewport_width = 100;
4041 int viewport_height = 100;
4042 float left_box_ratio = 0.3f;
4043 frame_test_helpers::WebViewHelper web_view_helper;
4044 web_view_helper.InitializeAndLoad(
4045 base_url_ + "get_scale_for_zoom_into_editable_test.html");
4046 web_view_helper.GetWebView()
4047 ->GetPage()
4048 ->GetSettings()
4049 .SetTextAutosizingEnabled(false);
4050 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
4051 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 4);
4052
4053 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
4054
4055 WebRect edit_box_with_no_text(200, 250, 250, 20);
4056
4057 // Test scrolling the focused node
4058 // Since we're zoomed out, the caret is considered too small to be legible and
4059 // so we'd normally zoom in. Make sure we don't change scale since the
4060 // auto-zoom setting is off.
4061
4062 // Focus the second empty textbox.
4063 web_view_helper.GetWebView()->AdvanceFocus(false);
4064 web_view_helper.GetWebView()->AdvanceFocus(false);
4065
4066 // Set the page scale to be smaller than the minimal readable scale.
4067 float initial_scale = 0.25f;
4068 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
4069 initial_scale);
4070
4071 float scale;
4072 IntPoint scroll;
4073 bool need_animation;
4074 IntRect element_bounds, caret_bounds;
4075 GetElementAndCaretBoundsForFocusedEditableElement(
4076 web_view_helper, element_bounds, caret_bounds);
4077 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
4078 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
4079 need_animation);
4080
4081 // There should be no change in page scale.
4082 EXPECT_EQ(initial_scale, scale);
4083 // The edit box should be left aligned with a margin for possible label.
4084 EXPECT_TRUE(need_animation);
4085 int h_scroll =
4086 edit_box_with_no_text.x - left_box_ratio * viewport_width / scale;
4087 EXPECT_NEAR(h_scroll, scroll.X(), 2);
4088 int v_scroll = edit_box_with_no_text.y -
4089 (viewport_height / scale - edit_box_with_no_text.height) / 2;
4090 EXPECT_NEAR(v_scroll, scroll.Y(), 2);
4091
4092 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), scroll, scale);
4093
4094 // Select the first textbox.
4095 web_view_helper.GetWebView()->AdvanceFocus(true);
4096 gfx::Rect rect, caret;
4097 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
4098 caret, rect);
4099 GetElementAndCaretBoundsForFocusedEditableElement(
4100 web_view_helper, element_bounds, caret_bounds);
4101 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
4102 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
4103 need_animation);
4104
4105 // There should be no change at all since the textbox is fully visible
4106 // already.
4107 EXPECT_EQ(initial_scale, scale);
4108 EXPECT_FALSE(need_animation);
4109 }
4110
4111 // Tests zoom into editable zoom and scroll correctly when zoom-for-dsf enabled.
TEST_F(WebFrameTest,DivScrollIntoEditableTestWithDeviceScaleFactor)4112 TEST_F(WebFrameTest, DivScrollIntoEditableTestWithDeviceScaleFactor) {
4113 RegisterMockedHttpURLLoad("get_scale_for_zoom_into_editable_test.html");
4114
4115 bool kAutoZoomToLegibleScale = true;
4116 const float kDeviceScaleFactor = 2.f;
4117 int viewport_width = 200 * kDeviceScaleFactor;
4118 int viewport_height = 150 * kDeviceScaleFactor;
4119 float min_readable_caret_height = 16.0f * kDeviceScaleFactor;
4120
4121 frame_test_helpers::WebViewHelper web_view_helper;
4122 web_view_helper.InitializeAndLoad(
4123 base_url_ + "get_scale_for_zoom_into_editable_test.html", nullptr,
4124 nullptr, nullptr, ConfigureAndroid);
4125 web_view_helper.GetWebView()
4126 ->GetPage()
4127 ->GetSettings()
4128 .SetTextAutosizingEnabled(false);
4129 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
4130 web_view_helper.GetWebView()->SetZoomFactorForDeviceScaleFactor(
4131 kDeviceScaleFactor);
4132 web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 4);
4133
4134 web_view_helper.GetWebView()->EnableFakePageScaleAnimationForTesting(true);
4135
4136 WebRect edit_box_with_text(200 * kDeviceScaleFactor, 200 * kDeviceScaleFactor,
4137 250 * kDeviceScaleFactor, 20 * kDeviceScaleFactor);
4138 web_view_helper.GetWebView()->AdvanceFocus(false);
4139
4140 // Set the page scale to be smaller than the minimal readable scale.
4141 float initial_scale = 0.5f;
4142 SetScaleAndScrollAndLayout(web_view_helper.GetWebView(), gfx::Point(),
4143 initial_scale);
4144 ASSERT_EQ(web_view_helper.GetWebView()->PageScaleFactor(), initial_scale);
4145
4146 float scale;
4147 IntPoint scroll;
4148 bool need_animation;
4149 IntRect element_bounds, caret_bounds;
4150 GetElementAndCaretBoundsForFocusedEditableElement(
4151 web_view_helper, element_bounds, caret_bounds);
4152 web_view_helper.GetWebView()->ComputeScaleAndScrollForEditableElementRects(
4153 element_bounds, caret_bounds, kAutoZoomToLegibleScale, scale, scroll,
4154 need_animation);
4155 EXPECT_TRUE(need_animation);
4156 // The edit box wider than the viewport when legible should be left aligned.
4157 int h_scroll = edit_box_with_text.x;
4158 EXPECT_NEAR(h_scroll, scroll.X(), 2);
4159 int v_scroll = edit_box_with_text.y -
4160 (viewport_height / scale - edit_box_with_text.height) / 2;
4161 EXPECT_NEAR(v_scroll, scroll.Y(), 2);
4162 EXPECT_NEAR(min_readable_caret_height / caret_bounds.Height(), scale, 0.1);
4163 }
4164
TEST_F(WebFrameTest,FirstRectForCharacterRangeWithPinchZoom)4165 TEST_F(WebFrameTest, FirstRectForCharacterRangeWithPinchZoom) {
4166 RegisterMockedHttpURLLoad("textbox.html");
4167
4168 frame_test_helpers::WebViewHelper web_view_helper;
4169 web_view_helper.InitializeAndLoad(base_url_ + "textbox.html");
4170 web_view_helper.Resize(gfx::Size(640, 480));
4171
4172 WebLocalFrame* main_frame = web_view_helper.LocalMainFrame();
4173 main_frame->ExecuteScript(WebScriptSource("selectRange();"));
4174
4175 WebRect old_rect;
4176 main_frame->FirstRectForCharacterRange(0, 5, old_rect);
4177
4178 gfx::PointF visual_offset(100, 130);
4179 float scale = 2;
4180 web_view_helper.GetWebView()->SetPageScaleFactor(scale);
4181 web_view_helper.GetWebView()->SetVisualViewportOffset(visual_offset);
4182
4183 WebRect rect;
4184 main_frame->FirstRectForCharacterRange(0, 5, rect);
4185
4186 EXPECT_EQ((old_rect.x - visual_offset.x()) * scale, rect.x);
4187 EXPECT_EQ((old_rect.y - visual_offset.y()) * scale, rect.y);
4188 EXPECT_EQ(old_rect.width * scale, rect.width);
4189 EXPECT_EQ(old_rect.height * scale, rect.height);
4190 }
4191 class TestReloadDoesntRedirectWebFrameClient
4192 : public frame_test_helpers::TestWebFrameClient {
4193 public:
4194 TestReloadDoesntRedirectWebFrameClient() = default;
4195 ~TestReloadDoesntRedirectWebFrameClient() override = default;
4196
4197 // frame_test_helpers::TestWebFrameClient:
BeginNavigation(std::unique_ptr<WebNavigationInfo> info)4198 void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
4199 EXPECT_FALSE(info->is_client_redirect);
4200 TestWebFrameClient::BeginNavigation(std::move(info));
4201 }
4202 };
4203
TEST_F(WebFrameTest,ReloadDoesntSetRedirect)4204 TEST_F(WebFrameTest, ReloadDoesntSetRedirect) {
4205 // Test for case in http://crbug.com/73104. Reloading a frame very quickly
4206 // would sometimes call BeginNavigation with isRedirect=true
4207 RegisterMockedHttpURLLoad("form.html");
4208
4209 TestReloadDoesntRedirectWebFrameClient web_frame_client;
4210 frame_test_helpers::WebViewHelper web_view_helper;
4211 web_view_helper.InitializeAndLoad(base_url_ + "form.html", &web_frame_client);
4212
4213 web_view_helper.GetWebView()->MainFrameImpl()->StartReload(
4214 WebFrameLoadType::kReloadBypassingCache);
4215 // start another reload before request is delivered.
4216 frame_test_helpers::ReloadFrameBypassingCache(
4217 web_view_helper.GetWebView()->MainFrameImpl());
4218 }
4219
4220 class ClearScrollStateOnCommitWebFrameClient
4221 : public frame_test_helpers::TestWebFrameClient {
4222 public:
4223 ClearScrollStateOnCommitWebFrameClient() = default;
4224 ~ClearScrollStateOnCommitWebFrameClient() override = default;
4225
4226 // frame_test_helpers::TestWebFrameClient:
DidCommitNavigation(const WebHistoryItem &,WebHistoryCommitType,bool)4227 void DidCommitNavigation(const WebHistoryItem&,
4228 WebHistoryCommitType,
4229 bool) override {
4230 Frame()->View()->ResetScrollAndScaleState();
4231 }
4232 };
4233
TEST_F(WebFrameTest,ReloadPreservesState)4234 TEST_F(WebFrameTest, ReloadPreservesState) {
4235 const std::string url = "200-by-300.html";
4236 const float kPageScaleFactor = 1.1684f;
4237 const int kPageWidth = 120;
4238 const int kPageHeight = 100;
4239
4240 RegisterMockedHttpURLLoad(url);
4241
4242 ClearScrollStateOnCommitWebFrameClient client;
4243 frame_test_helpers::WebViewHelper web_view_helper;
4244 web_view_helper.InitializeAndLoad(base_url_ + url, &client);
4245 web_view_helper.Resize(gfx::Size(kPageWidth, kPageHeight));
4246 web_view_helper.LocalMainFrame()->SetScrollOffset(
4247 WebSize(kPageWidth / 4, kPageHeight / 4));
4248 web_view_helper.GetWebView()->SetPageScaleFactor(kPageScaleFactor);
4249
4250 // Reload the page and end up at the same url. State should not be propagated.
4251 web_view_helper.GetWebView()->MainFrameImpl()->StartReload(
4252 WebFrameLoadType::kReload);
4253 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
4254 web_view_helper.LocalMainFrame());
4255 EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().width);
4256 EXPECT_EQ(0, web_view_helper.LocalMainFrame()->GetScrollOffset().height);
4257 EXPECT_EQ(1.0f, web_view_helper.GetWebView()->PageScaleFactor());
4258 }
4259
TEST_F(WebFrameTest,ReloadWhileProvisional)4260 TEST_F(WebFrameTest, ReloadWhileProvisional) {
4261 // Test that reloading while the previous load is still pending does not cause
4262 // the initial request to get lost.
4263 RegisterMockedHttpURLLoad("fixed_layout.html");
4264
4265 frame_test_helpers::WebViewHelper web_view_helper;
4266 web_view_helper.Initialize();
4267 WebURLRequest request(ToKURL(base_url_ + "fixed_layout.html"));
4268 web_view_helper.GetWebView()->MainFrameImpl()->StartNavigation(request);
4269 // start reload before first request is delivered.
4270 frame_test_helpers::ReloadFrameBypassingCache(
4271 web_view_helper.GetWebView()->MainFrameImpl());
4272
4273 WebDocumentLoader* document_loader =
4274 web_view_helper.LocalMainFrame()->GetDocumentLoader();
4275 ASSERT_TRUE(document_loader);
4276 EXPECT_EQ(ToKURL(base_url_ + "fixed_layout.html"),
4277 KURL(document_loader->GetUrl()));
4278 }
4279
TEST_F(WebFrameTest,RedirectChainContainsInitialUrl)4280 TEST_F(WebFrameTest, RedirectChainContainsInitialUrl) {
4281 const std::string first_url = "data:text/html,foo";
4282
4283 frame_test_helpers::WebViewHelper web_view_helper;
4284 web_view_helper.InitializeAndLoad(first_url);
4285
4286 WebDocumentLoader* document_loader =
4287 web_view_helper.LocalMainFrame()->GetDocumentLoader();
4288 ASSERT_TRUE(document_loader);
4289
4290 WebVector<WebURL> redirects;
4291 document_loader->RedirectChain(redirects);
4292 ASSERT_EQ(1U, redirects.size());
4293 EXPECT_EQ(ToKURL(first_url), KURL(redirects[0]));
4294 }
4295
TEST_F(WebFrameTest,IframeRedirect)4296 TEST_F(WebFrameTest, IframeRedirect) {
4297 RegisterMockedHttpURLLoad("iframe_redirect.html");
4298 RegisterMockedHttpURLLoad("visible_iframe.html");
4299
4300 frame_test_helpers::WebViewHelper web_view_helper;
4301 web_view_helper.InitializeAndLoad(base_url_ + "iframe_redirect.html");
4302 // Pump pending requests one more time. The test page loads script that
4303 // navigates.
4304 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
4305 web_view_helper.LocalMainFrame());
4306
4307 WebFrame* iframe = web_view_helper.LocalMainFrame()->FindFrameByName(
4308 WebString::FromUTF8("ifr"));
4309 ASSERT_TRUE(iframe && iframe->IsWebLocalFrame());
4310 WebDocumentLoader* iframe_document_loader =
4311 iframe->ToWebLocalFrame()->GetDocumentLoader();
4312 ASSERT_TRUE(iframe_document_loader);
4313 WebVector<WebURL> redirects;
4314 iframe_document_loader->RedirectChain(redirects);
4315 ASSERT_EQ(2U, redirects.size());
4316 EXPECT_EQ(ToKURL("about:blank"), KURL(redirects[0]));
4317 EXPECT_EQ(ToKURL("http://internal.test/visible_iframe.html"),
4318 KURL(redirects[1]));
4319 }
4320
TEST_F(WebFrameTest,ClearFocusedNodeTest)4321 TEST_F(WebFrameTest, ClearFocusedNodeTest) {
4322 RegisterMockedHttpURLLoad("iframe_clear_focused_node_test.html");
4323 RegisterMockedHttpURLLoad("autofocus_input_field_iframe.html");
4324
4325 frame_test_helpers::WebViewHelper web_view_helper;
4326 web_view_helper.InitializeAndLoad(base_url_ +
4327 "iframe_clear_focused_node_test.html");
4328
4329 // Clear the focused node.
4330 web_view_helper.GetWebView()->FocusedElement()->blur();
4331
4332 // Now retrieve the FocusedNode and test it should be null.
4333 EXPECT_EQ(nullptr, web_view_helper.GetWebView()->FocusedElement());
4334 }
4335
4336 class ChangedSelectionCounter : public frame_test_helpers::TestWebFrameClient {
4337 public:
ChangedSelectionCounter()4338 ChangedSelectionCounter() : call_count_(0) {}
DidChangeSelection(bool isSelectionEmpty)4339 void DidChangeSelection(bool isSelectionEmpty) override { ++call_count_; }
Count() const4340 int Count() const { return call_count_; }
Reset()4341 void Reset() { call_count_ = 0; }
4342
4343 private:
4344 int call_count_;
4345 };
4346
TEST_F(WebFrameTest,TabKeyCursorMoveTriggersOneSelectionChange)4347 TEST_F(WebFrameTest, TabKeyCursorMoveTriggersOneSelectionChange) {
4348 ChangedSelectionCounter counter;
4349 frame_test_helpers::WebViewHelper web_view_helper;
4350 RegisterMockedHttpURLLoad("editable_elements.html");
4351 WebViewImpl* web_view = web_view_helper.InitializeAndLoad(
4352 base_url_ + "editable_elements.html", &counter);
4353
4354 WebKeyboardEvent tab_down(WebInputEvent::Type::kKeyDown,
4355 WebInputEvent::kNoModifiers,
4356 WebInputEvent::GetStaticTimeStampForTests());
4357 WebKeyboardEvent tab_up(WebInputEvent::Type::kKeyUp,
4358 WebInputEvent::kNoModifiers,
4359 WebInputEvent::GetStaticTimeStampForTests());
4360 tab_down.dom_key = ui::DomKey::TAB;
4361 tab_up.dom_key = ui::DomKey::TAB;
4362 tab_down.windows_key_code = VKEY_TAB;
4363 tab_up.windows_key_code = VKEY_TAB;
4364
4365 // Move to the next text-field: 1 cursor change.
4366 counter.Reset();
4367 web_view->MainFrameWidget()->HandleInputEvent(
4368 WebCoalescedInputEvent(tab_down, ui::LatencyInfo()));
4369 web_view->MainFrameWidget()->HandleInputEvent(
4370 WebCoalescedInputEvent(tab_up, ui::LatencyInfo()));
4371 EXPECT_EQ(1, counter.Count());
4372
4373 // Move to another text-field: 1 cursor change.
4374 web_view->MainFrameWidget()->HandleInputEvent(
4375 WebCoalescedInputEvent(tab_down, ui::LatencyInfo()));
4376 web_view->MainFrameWidget()->HandleInputEvent(
4377 WebCoalescedInputEvent(tab_up, ui::LatencyInfo()));
4378 EXPECT_EQ(2, counter.Count());
4379
4380 // Move to a number-field: 1 cursor change.
4381 web_view->MainFrameWidget()->HandleInputEvent(
4382 WebCoalescedInputEvent(tab_down, ui::LatencyInfo()));
4383 web_view->MainFrameWidget()->HandleInputEvent(
4384 WebCoalescedInputEvent(tab_up, ui::LatencyInfo()));
4385 EXPECT_EQ(3, counter.Count());
4386
4387 // Move to an editable element: 1 cursor change.
4388 web_view->MainFrameWidget()->HandleInputEvent(
4389 WebCoalescedInputEvent(tab_down, ui::LatencyInfo()));
4390 web_view->MainFrameWidget()->HandleInputEvent(
4391 WebCoalescedInputEvent(tab_up, ui::LatencyInfo()));
4392 EXPECT_EQ(4, counter.Count());
4393
4394 // Move to a non-editable element: 0 cursor changes.
4395 web_view->MainFrameWidget()->HandleInputEvent(
4396 WebCoalescedInputEvent(tab_down, ui::LatencyInfo()));
4397 web_view->MainFrameWidget()->HandleInputEvent(
4398 WebCoalescedInputEvent(tab_up, ui::LatencyInfo()));
4399 EXPECT_EQ(4, counter.Count());
4400 }
4401
4402 // Implementation of WebLocalFrameClient that tracks the v8 contexts that are
4403 // created and destroyed for verification.
4404 class ContextLifetimeTestWebFrameClient
4405 : public frame_test_helpers::TestWebFrameClient {
4406 public:
4407 struct Notification {
4408 public:
Notificationblink::ContextLifetimeTestWebFrameClient::Notification4409 Notification(WebLocalFrame* frame,
4410 v8::Local<v8::Context> context,
4411 int32_t world_id)
4412 : frame(frame),
4413 context(context->GetIsolate(), context),
4414 world_id(world_id) {}
4415
~Notificationblink::ContextLifetimeTestWebFrameClient::Notification4416 ~Notification() { context.Reset(); }
4417
Equalsblink::ContextLifetimeTestWebFrameClient::Notification4418 bool Equals(Notification* other) {
4419 return other && frame == other->frame && context == other->context &&
4420 world_id == other->world_id;
4421 }
4422
4423 WebLocalFrame* frame;
4424 v8::Persistent<v8::Context> context;
4425 int32_t world_id;
4426 };
4427
ContextLifetimeTestWebFrameClient(Vector<std::unique_ptr<Notification>> & create_notifications,Vector<std::unique_ptr<Notification>> & release_notifications)4428 ContextLifetimeTestWebFrameClient(
4429 Vector<std::unique_ptr<Notification>>& create_notifications,
4430 Vector<std::unique_ptr<Notification>>& release_notifications)
4431 : create_notifications_(create_notifications),
4432 release_notifications_(release_notifications) {}
4433 ~ContextLifetimeTestWebFrameClient() override = default;
4434
Reset()4435 void Reset() {
4436 create_notifications_.clear();
4437 release_notifications_.clear();
4438 }
4439
4440 // WebLocalFrameClient:
CreateChildFrame(WebLocalFrame * parent,mojom::blink::TreeScopeType scope,const WebString & name,const WebString & fallback_name,const FramePolicy &,const WebFrameOwnerProperties &,mojom::blink::FrameOwnerElementType)4441 WebLocalFrame* CreateChildFrame(
4442 WebLocalFrame* parent,
4443 mojom::blink::TreeScopeType scope,
4444 const WebString& name,
4445 const WebString& fallback_name,
4446 const FramePolicy&,
4447 const WebFrameOwnerProperties&,
4448 mojom::blink::FrameOwnerElementType) override {
4449 return CreateLocalChild(*parent, scope,
4450 std::make_unique<ContextLifetimeTestWebFrameClient>(
4451 create_notifications_, release_notifications_));
4452 }
4453
DidCreateScriptContext(v8::Local<v8::Context> context,int32_t world_id)4454 void DidCreateScriptContext(v8::Local<v8::Context> context,
4455 int32_t world_id) override {
4456 create_notifications_.push_back(
4457 std::make_unique<Notification>(Frame(), context, world_id));
4458 }
4459
WillReleaseScriptContext(v8::Local<v8::Context> context,int32_t world_id)4460 void WillReleaseScriptContext(v8::Local<v8::Context> context,
4461 int32_t world_id) override {
4462 release_notifications_.push_back(
4463 std::make_unique<Notification>(Frame(), context, world_id));
4464 }
4465
4466 private:
4467 Vector<std::unique_ptr<Notification>>& create_notifications_;
4468 Vector<std::unique_ptr<Notification>>& release_notifications_;
4469 };
4470
TEST_F(WebFrameTest,ContextNotificationsLoadUnload)4471 TEST_F(WebFrameTest, ContextNotificationsLoadUnload) {
4472 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
4473
4474 RegisterMockedHttpURLLoad("context_notifications_test.html");
4475 RegisterMockedHttpURLLoad("context_notifications_test_frame.html");
4476
4477 // Load a frame with an iframe, make sure we get the right create
4478 // notifications.
4479 Vector<std::unique_ptr<ContextLifetimeTestWebFrameClient::Notification>>
4480 create_notifications;
4481 Vector<std::unique_ptr<ContextLifetimeTestWebFrameClient::Notification>>
4482 release_notifications;
4483 ContextLifetimeTestWebFrameClient web_frame_client(create_notifications,
4484 release_notifications);
4485 frame_test_helpers::WebViewHelper web_view_helper;
4486 web_view_helper.InitializeAndLoad(
4487 base_url_ + "context_notifications_test.html", &web_frame_client);
4488
4489 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
4490 WebFrame* child_frame = main_frame->FirstChild();
4491
4492 ASSERT_EQ(2u, create_notifications.size());
4493 EXPECT_EQ(0u, release_notifications.size());
4494
4495 auto& first_create_notification = create_notifications[0];
4496 auto& second_create_notification = create_notifications[1];
4497
4498 EXPECT_EQ(main_frame, first_create_notification->frame);
4499 EXPECT_EQ(main_frame->MainWorldScriptContext(),
4500 first_create_notification->context);
4501 EXPECT_EQ(0, first_create_notification->world_id);
4502
4503 EXPECT_EQ(child_frame, second_create_notification->frame);
4504 EXPECT_EQ(child_frame->ToWebLocalFrame()->MainWorldScriptContext(),
4505 second_create_notification->context);
4506 EXPECT_EQ(0, second_create_notification->world_id);
4507
4508 // Close the view. We should get two release notifications that are exactly
4509 // the same as the create ones, in reverse order.
4510 web_view_helper.Reset();
4511
4512 ASSERT_EQ(2u, release_notifications.size());
4513 auto& first_release_notification = release_notifications[0];
4514 auto& second_release_notification = release_notifications[1];
4515
4516 ASSERT_TRUE(
4517 first_create_notification->Equals(second_release_notification.get()));
4518 ASSERT_TRUE(
4519 second_create_notification->Equals(first_release_notification.get()));
4520 }
4521
TEST_F(WebFrameTest,ContextNotificationsReload)4522 TEST_F(WebFrameTest, ContextNotificationsReload) {
4523 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
4524
4525 RegisterMockedHttpURLLoad("context_notifications_test.html");
4526 RegisterMockedHttpURLLoad("context_notifications_test_frame.html");
4527
4528 Vector<std::unique_ptr<ContextLifetimeTestWebFrameClient::Notification>>
4529 create_notifications;
4530 Vector<std::unique_ptr<ContextLifetimeTestWebFrameClient::Notification>>
4531 release_notifications;
4532 ContextLifetimeTestWebFrameClient web_frame_client(create_notifications,
4533 release_notifications);
4534 frame_test_helpers::WebViewHelper web_view_helper;
4535 web_view_helper.InitializeAndLoad(
4536 base_url_ + "context_notifications_test.html", &web_frame_client);
4537
4538 // Refresh, we should get two release notifications and two more create
4539 // notifications.
4540 frame_test_helpers::ReloadFrame(
4541 web_view_helper.GetWebView()->MainFrameImpl());
4542 ASSERT_EQ(4u, create_notifications.size());
4543 ASSERT_EQ(2u, release_notifications.size());
4544
4545 // The two release notifications we got should be exactly the same as the
4546 // first two create notifications.
4547 for (size_t i = 0; i < release_notifications.size(); ++i) {
4548 EXPECT_TRUE(release_notifications[i]->Equals(
4549 create_notifications[create_notifications.size() - 3 - i].get()));
4550 }
4551
4552 // The last two create notifications should be for the current frames and
4553 // context.
4554 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
4555 WebFrame* child_frame = main_frame->FirstChild();
4556 auto& first_refresh_notification = create_notifications[2];
4557 auto& second_refresh_notification = create_notifications[3];
4558
4559 EXPECT_EQ(main_frame, first_refresh_notification->frame);
4560 EXPECT_EQ(main_frame->MainWorldScriptContext(),
4561 first_refresh_notification->context);
4562 EXPECT_EQ(0, first_refresh_notification->world_id);
4563
4564 EXPECT_EQ(child_frame, second_refresh_notification->frame);
4565 EXPECT_EQ(child_frame->ToWebLocalFrame()->MainWorldScriptContext(),
4566 second_refresh_notification->context);
4567 EXPECT_EQ(0, second_refresh_notification->world_id);
4568 }
4569
TEST_F(WebFrameTest,ContextNotificationsIsolatedWorlds)4570 TEST_F(WebFrameTest, ContextNotificationsIsolatedWorlds) {
4571 v8::Isolate* isolate = v8::Isolate::GetCurrent();
4572 v8::HandleScope handle_scope(isolate);
4573
4574 RegisterMockedHttpURLLoad("context_notifications_test.html");
4575 RegisterMockedHttpURLLoad("context_notifications_test_frame.html");
4576
4577 Vector<std::unique_ptr<ContextLifetimeTestWebFrameClient::Notification>>
4578 create_notifications;
4579 Vector<std::unique_ptr<ContextLifetimeTestWebFrameClient::Notification>>
4580 release_notifications;
4581 ContextLifetimeTestWebFrameClient web_frame_client(create_notifications,
4582 release_notifications);
4583 frame_test_helpers::WebViewHelper web_view_helper;
4584 web_view_helper.InitializeAndLoad(
4585 base_url_ + "context_notifications_test.html", &web_frame_client);
4586
4587 // Add an isolated world.
4588 web_frame_client.Reset();
4589
4590 int32_t isolated_world_id = 42;
4591 WebScriptSource script_source("hi!");
4592 web_view_helper.LocalMainFrame()->ExecuteScriptInIsolatedWorld(
4593 isolated_world_id, script_source);
4594
4595 // We should now have a new create notification.
4596 ASSERT_EQ(1u, create_notifications.size());
4597 auto& notification = create_notifications[0];
4598 ASSERT_EQ(isolated_world_id, notification->world_id);
4599 ASSERT_EQ(web_view_helper.GetWebView()->MainFrame(), notification->frame);
4600
4601 // We don't have an API to enumarate isolated worlds for a frame, but we can
4602 // at least assert that the context we got is *not* the main world's context.
4603 ASSERT_NE(web_view_helper.LocalMainFrame()->MainWorldScriptContext(),
4604 v8::Local<v8::Context>::New(isolate, notification->context));
4605
4606 // Check that the context we got has the right isolated world id.
4607 ASSERT_EQ(isolated_world_id,
4608 web_view_helper.LocalMainFrame()->GetScriptContextWorldId(
4609 v8::Local<v8::Context>::New(isolate, notification->context)));
4610
4611 web_view_helper.Reset();
4612
4613 // We should have gotten three release notifications (one for each of the
4614 // frames, plus one for the isolated context).
4615 ASSERT_EQ(3u, release_notifications.size());
4616
4617 // And one of them should be exactly the same as the create notification for
4618 // the isolated context.
4619 int match_count = 0;
4620 for (size_t i = 0; i < release_notifications.size(); ++i) {
4621 if (release_notifications[i]->Equals(create_notifications[0].get()))
4622 ++match_count;
4623 }
4624 EXPECT_EQ(1, match_count);
4625 }
4626
TEST_F(WebFrameTest,FindInPage)4627 TEST_F(WebFrameTest, FindInPage) {
4628 RegisterMockedHttpURLLoad("find.html");
4629 frame_test_helpers::WebViewHelper web_view_helper;
4630 web_view_helper.InitializeAndLoad(base_url_ + "find.html");
4631 ASSERT_TRUE(web_view_helper.LocalMainFrame());
4632 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
4633 const int kFindIdentifier = 12345;
4634 auto options = mojom::blink::FindOptions::New();
4635
4636 // Find in a <div> element.
4637 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
4638 kFindIdentifier, WebString::FromUTF8("bar1"), *options, false));
4639 frame->GetFindInPage()->StopFinding(
4640 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
4641 WebRange range = frame->SelectionRange();
4642 EXPECT_EQ(5, range.StartOffset());
4643 EXPECT_EQ(9, range.EndOffset());
4644 EXPECT_TRUE(frame->GetDocument().FocusedElement().IsNull());
4645
4646 // Find in an <input> value.
4647 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
4648 kFindIdentifier, WebString::FromUTF8("bar2"), *options, false));
4649 // Confirm stopFinding(WebLocalFrame::StopFindActionKeepSelection) sets the
4650 // selection on the found text.
4651 frame->GetFindInPage()->StopFinding(
4652 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
4653 range = frame->SelectionRange();
4654 ASSERT_FALSE(range.IsNull());
4655 EXPECT_EQ(5, range.StartOffset());
4656 EXPECT_EQ(9, range.EndOffset());
4657 EXPECT_TRUE(frame->GetDocument().FocusedElement().HasHTMLTagName("input"));
4658
4659 // Find in a <textarea> content.
4660 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
4661 kFindIdentifier, WebString::FromUTF8("bar3"), *options, false));
4662 // Confirm stopFinding(WebLocalFrame::StopFindActionKeepSelection) sets the
4663 // selection on the found text.
4664 frame->GetFindInPage()->StopFinding(
4665 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
4666 range = frame->SelectionRange();
4667 ASSERT_FALSE(range.IsNull());
4668 EXPECT_EQ(5, range.StartOffset());
4669 EXPECT_EQ(9, range.EndOffset());
4670 EXPECT_TRUE(frame->GetDocument().FocusedElement().HasHTMLTagName("textarea"));
4671
4672 // Find in a contentEditable element.
4673 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
4674 kFindIdentifier, WebString::FromUTF8("bar4"), *options, false));
4675 // Confirm stopFinding(WebLocalFrame::StopFindActionKeepSelection) sets the
4676 // selection on the found text.
4677 frame->GetFindInPage()->StopFinding(
4678 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
4679 range = frame->SelectionRange();
4680 ASSERT_FALSE(range.IsNull());
4681 EXPECT_EQ(0, range.StartOffset());
4682 EXPECT_EQ(4, range.EndOffset());
4683 // "bar4" is surrounded by <span>, but the focusable node should be the parent
4684 // <div>.
4685 EXPECT_TRUE(frame->GetDocument().FocusedElement().HasHTMLTagName("div"));
4686
4687 // Find in <select> content.
4688 EXPECT_FALSE(frame->GetFindInPage()->FindInternal(
4689 kFindIdentifier, WebString::FromUTF8("bar5"), *options, false));
4690 // If there are any matches, stopFinding will set the selection on the found
4691 // text. However, we do not expect any matches, so check that the selection
4692 // is null.
4693 frame->GetFindInPage()->StopFinding(
4694 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
4695 range = frame->SelectionRange();
4696 ASSERT_TRUE(range.IsNull());
4697 }
4698
TEST_F(WebFrameTest,GetContentAsPlainText)4699 TEST_F(WebFrameTest, GetContentAsPlainText) {
4700 frame_test_helpers::WebViewHelper web_view_helper;
4701 web_view_helper.InitializeAndLoad("about:blank");
4702 // We set the size because it impacts line wrapping, which changes the
4703 // resulting text value.
4704 web_view_helper.Resize(gfx::Size(640, 480));
4705 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
4706
4707 // Generate a simple test case.
4708 const char kSimpleSource[] = "<div>Foo bar</div><div></div>baz";
4709 KURL test_url = ToKURL("about:blank");
4710 frame_test_helpers::LoadHTMLString(frame, kSimpleSource, test_url);
4711
4712 // Make sure it comes out OK.
4713 const std::string expected("Foo bar\nbaz");
4714 WebString text = WebFrameContentDumper::DumpWebViewAsText(
4715 web_view_helper.GetWebView(), std::numeric_limits<size_t>::max());
4716 EXPECT_EQ(expected, text.Utf8());
4717
4718 // Try reading the same one with clipping of the text.
4719 const int kLength = 5;
4720 text = WebFrameContentDumper::DumpWebViewAsText(web_view_helper.GetWebView(),
4721 kLength);
4722 EXPECT_EQ(expected.substr(0, kLength), text.Utf8());
4723
4724 // Now do a new test with a subframe.
4725 const char kOuterFrameSource[] = "Hello<iframe></iframe> world";
4726 frame_test_helpers::LoadHTMLString(frame, kOuterFrameSource, test_url);
4727
4728 // Load something into the subframe.
4729 WebLocalFrame* subframe = frame->FirstChild()->ToWebLocalFrame();
4730 ASSERT_TRUE(subframe);
4731 frame_test_helpers::LoadHTMLString(subframe, "sub<p>text", test_url);
4732
4733 text = WebFrameContentDumper::DumpWebViewAsText(
4734 web_view_helper.GetWebView(), std::numeric_limits<size_t>::max());
4735 EXPECT_EQ("Hello world\n\nsub\n\ntext", text.Utf8());
4736
4737 // Get the frame text where the subframe separator falls on the boundary of
4738 // what we'll take. There used to be a crash in this case.
4739 text = WebFrameContentDumper::DumpWebViewAsText(web_view_helper.GetWebView(),
4740 12);
4741 EXPECT_EQ("Hello world", text.Utf8());
4742 }
4743
TEST_F(WebFrameTest,GetFullHtmlOfPage)4744 TEST_F(WebFrameTest, GetFullHtmlOfPage) {
4745 frame_test_helpers::WebViewHelper web_view_helper;
4746 web_view_helper.InitializeAndLoad("about:blank");
4747 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
4748
4749 // Generate a simple test case.
4750 const char kSimpleSource[] = "<p>Hello</p><p>World</p>";
4751 KURL test_url = ToKURL("about:blank");
4752 frame_test_helpers::LoadHTMLString(frame, kSimpleSource, test_url);
4753
4754 WebString text = WebFrameContentDumper::DumpWebViewAsText(
4755 web_view_helper.GetWebView(), std::numeric_limits<size_t>::max());
4756 EXPECT_EQ("Hello\n\nWorld", text.Utf8());
4757
4758 const std::string html = WebFrameContentDumper::DumpAsMarkup(frame).Utf8();
4759
4760 // Load again with the output html.
4761 frame_test_helpers::LoadHTMLString(frame, html, test_url);
4762
4763 EXPECT_EQ(html, WebFrameContentDumper::DumpAsMarkup(frame).Utf8());
4764
4765 text = WebFrameContentDumper::DumpWebViewAsText(
4766 web_view_helper.GetWebView(), std::numeric_limits<size_t>::max());
4767 EXPECT_EQ("Hello\n\nWorld", text.Utf8());
4768
4769 // Test selection check
4770 EXPECT_FALSE(frame->HasSelection());
4771 frame->ExecuteCommand(WebString::FromUTF8("SelectAll"));
4772 EXPECT_TRUE(frame->HasSelection());
4773 frame->ExecuteCommand(WebString::FromUTF8("Unselect"));
4774 EXPECT_FALSE(frame->HasSelection());
4775 WebString selection_html = frame->SelectionAsMarkup();
4776 EXPECT_TRUE(selection_html.IsEmpty());
4777 }
4778
4779 class TestExecuteScriptDuringDidCreateScriptContext
4780 : public frame_test_helpers::TestWebFrameClient {
4781 public:
4782 TestExecuteScriptDuringDidCreateScriptContext() = default;
4783 ~TestExecuteScriptDuringDidCreateScriptContext() override = default;
4784
4785 // frame_test_helpers::TestWebFrameClient:
DidCreateScriptContext(v8::Local<v8::Context> context,int32_t world_id)4786 void DidCreateScriptContext(v8::Local<v8::Context> context,
4787 int32_t world_id) override {
4788 Frame()->ExecuteScript(WebScriptSource("window.history = 'replaced';"));
4789 }
4790 };
4791
TEST_F(WebFrameTest,ExecuteScriptDuringDidCreateScriptContext)4792 TEST_F(WebFrameTest, ExecuteScriptDuringDidCreateScriptContext) {
4793 RegisterMockedHttpURLLoad("hello_world.html");
4794
4795 TestExecuteScriptDuringDidCreateScriptContext web_frame_client;
4796 frame_test_helpers::WebViewHelper web_view_helper;
4797 web_view_helper.InitializeAndLoad(base_url_ + "hello_world.html",
4798 &web_frame_client);
4799
4800 frame_test_helpers::ReloadFrame(
4801 web_view_helper.GetWebView()->MainFrameImpl());
4802 }
4803
4804 class TestFindInPageClient : public mojom::blink::FindInPageClient {
4805 public:
TestFindInPageClient()4806 TestFindInPageClient()
4807 : find_results_are_ready_(false), count_(-1), active_index_(-1) {}
4808
4809 ~TestFindInPageClient() override = default;
4810
SetFrame(WebLocalFrameImpl * frame)4811 void SetFrame(WebLocalFrameImpl* frame) {
4812 frame->GetFindInPage()->SetClient(receiver_.BindNewPipeAndPassRemote());
4813 }
4814
SetNumberOfMatches(int request_id,unsigned int current_number_of_matches,mojom::blink::FindMatchUpdateType final_update)4815 void SetNumberOfMatches(
4816 int request_id,
4817 unsigned int current_number_of_matches,
4818 mojom::blink::FindMatchUpdateType final_update) final {
4819 count_ = current_number_of_matches;
4820 find_results_are_ready_ =
4821 (final_update == mojom::blink::FindMatchUpdateType::kFinalUpdate);
4822 }
4823
SetActiveMatch(int request_id,const gfx::Rect & active_match_rect,int active_match_ordinal,mojom::blink::FindMatchUpdateType final_update)4824 void SetActiveMatch(int request_id,
4825 const gfx::Rect& active_match_rect,
4826 int active_match_ordinal,
4827 mojom::blink::FindMatchUpdateType final_update) final {
4828 active_index_ = active_match_ordinal;
4829 find_results_are_ready_ =
4830 (final_update == mojom::blink::FindMatchUpdateType::kFinalUpdate);
4831 }
4832
FindResultsAreReady() const4833 bool FindResultsAreReady() const { return find_results_are_ready_; }
Count() const4834 int Count() const { return count_; }
ActiveIndex() const4835 int ActiveIndex() const { return active_index_; }
4836
4837 private:
4838 bool find_results_are_ready_;
4839 int count_;
4840 int active_index_;
4841 mojo::Receiver<mojom::blink::FindInPageClient> receiver_{this};
4842 };
4843
TEST_F(WebFrameTest,FindInPageMatchRects)4844 TEST_F(WebFrameTest, FindInPageMatchRects) {
4845 RegisterMockedHttpURLLoad("find_in_page_frame.html");
4846
4847 frame_test_helpers::TestWebFrameClient frame_client;
4848 frame_test_helpers::WebViewHelper web_view_helper;
4849 web_view_helper.InitializeAndLoad(base_url_ + "find_in_page_frame.html",
4850 &frame_client);
4851 web_view_helper.Resize(gfx::Size(640, 480));
4852 web_view_helper.GetWebView()->SetMaximumLegibleScale(1.f);
4853 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
4854 RunPendingTasks();
4855
4856 // Note that the 'result 19' in the <select> element is not expected to
4857 // produce a match. Also, results 00 and 01 are in a different frame that is
4858 // not included in this test.
4859 const char kFindString[] = "result";
4860 const int kFindIdentifier = 12345;
4861 const int kNumResults = 17;
4862
4863 auto options = mojom::blink::FindOptions::New();
4864 options->run_synchronously_for_testing = true;
4865 WebString search_text = WebString::FromUTF8(kFindString);
4866 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
4867 TestFindInPageClient find_in_page_client;
4868 find_in_page_client.SetFrame(main_frame);
4869 EXPECT_TRUE(main_frame->GetFindInPage()->FindInternal(
4870 kFindIdentifier, search_text, *options, false));
4871
4872 main_frame->EnsureTextFinder().ResetMatchCount();
4873
4874 for (WebLocalFrameImpl* frame = main_frame; frame;
4875 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
4876 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
4877 search_text, *options);
4878 }
4879 RunPendingTasks();
4880 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
4881
4882 WebVector<gfx::RectF> web_match_rects =
4883 main_frame->EnsureTextFinder().FindMatchRects();
4884 ASSERT_EQ(static_cast<size_t>(kNumResults), web_match_rects.size());
4885 int rects_version = main_frame->GetFindInPage()->FindMatchMarkersVersion();
4886
4887 for (int result_index = 0; result_index < kNumResults; ++result_index) {
4888 FloatRect result_rect =
4889 static_cast<FloatRect>(web_match_rects[result_index]);
4890
4891 // Select the match by the center of its rect.
4892 EXPECT_EQ(main_frame->EnsureTextFinder().SelectNearestFindMatch(
4893 result_rect.Center(), nullptr),
4894 result_index + 1);
4895
4896 // Check that the find result ordering matches with our expectations.
4897 Range* result = main_frame->GetTextFinder()->ActiveMatch();
4898 ASSERT_TRUE(result);
4899 result->setEnd(result->endContainer(), result->endOffset() + 3);
4900 EXPECT_EQ(result->GetText(),
4901 String::Format("%s %02d", kFindString, result_index + 2));
4902
4903 // Verify that the expected match rect also matches the currently active
4904 // match. Compare the enclosing rects to prevent precision issues caused by
4905 // CSS transforms.
4906 gfx::RectF active_match =
4907 main_frame->GetFindInPage()->ActiveFindMatchRect();
4908 EXPECT_EQ(EnclosingIntRect(FloatRect(active_match)),
4909 EnclosingIntRect(result_rect));
4910
4911 // The rects version should not have changed.
4912 EXPECT_EQ(main_frame->GetFindInPage()->FindMatchMarkersVersion(),
4913 rects_version);
4914 }
4915
4916 // Resizing should update the rects version.
4917 web_view_helper.Resize(gfx::Size(800, 600));
4918 RunPendingTasks();
4919 EXPECT_TRUE(main_frame->GetFindInPage()->FindMatchMarkersVersion() !=
4920 rects_version);
4921 }
4922
TEST_F(WebFrameTest,FindInPageActiveIndex)4923 TEST_F(WebFrameTest, FindInPageActiveIndex) {
4924 RegisterMockedHttpURLLoad("find_match_count.html");
4925
4926 frame_test_helpers::TestWebFrameClient frame_client;
4927 frame_test_helpers::WebViewHelper web_view_helper;
4928 web_view_helper.InitializeAndLoad(base_url_ + "find_match_count.html",
4929 &frame_client);
4930 web_view_helper.GetWebView()->MainFrameViewWidget()->Resize(
4931 gfx::Size(640, 480));
4932 RunPendingTasks();
4933
4934 const char* kFindString = "a";
4935 const int kFindIdentifier = 7777;
4936 const int kActiveIndex = 1;
4937
4938 auto options = mojom::blink::FindOptions::New();
4939 options->run_synchronously_for_testing = true;
4940 WebString search_text = WebString::FromUTF8(kFindString);
4941 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
4942 TestFindInPageClient find_in_page_client;
4943 find_in_page_client.SetFrame(main_frame);
4944
4945 EXPECT_TRUE(main_frame->GetFindInPage()->FindInternal(
4946 kFindIdentifier, search_text, *options, false));
4947 main_frame->EnsureTextFinder().ResetMatchCount();
4948
4949 for (WebLocalFrameImpl* frame = main_frame; frame;
4950 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
4951 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
4952 search_text, *options);
4953 }
4954 RunPendingTasks();
4955
4956 EXPECT_TRUE(main_frame->GetFindInPage()->FindInternal(
4957 kFindIdentifier, search_text, *options, false));
4958 main_frame->GetFindInPage()->StopFinding(
4959 mojom::StopFindAction::kStopFindActionClearSelection);
4960
4961 for (WebLocalFrameImpl* frame = main_frame; frame;
4962 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
4963 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
4964 search_text, *options);
4965 }
4966
4967 RunPendingTasks();
4968 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
4969 EXPECT_EQ(kActiveIndex, find_in_page_client.ActiveIndex());
4970
4971 const char* kFindStringNew = "e";
4972 WebString search_text_new = WebString::FromUTF8(kFindStringNew);
4973
4974 EXPECT_TRUE(main_frame->GetFindInPage()->FindInternal(
4975 kFindIdentifier, search_text_new, *options, false));
4976 main_frame->EnsureTextFinder().ResetMatchCount();
4977
4978 for (WebLocalFrameImpl* frame = main_frame; frame;
4979 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
4980 frame->EnsureTextFinder().StartScopingStringMatches(
4981 kFindIdentifier, search_text_new, *options);
4982 }
4983
4984 RunPendingTasks();
4985 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
4986 EXPECT_EQ(kActiveIndex, find_in_page_client.ActiveIndex());
4987 }
4988
TEST_F(WebFrameTest,FindOnDetachedFrame)4989 TEST_F(WebFrameTest, FindOnDetachedFrame) {
4990 RegisterMockedHttpURLLoad("find_in_page.html");
4991 RegisterMockedHttpURLLoad("find_in_page_frame.html");
4992
4993 frame_test_helpers::TestWebFrameClient frame_client;
4994 frame_test_helpers::WebViewHelper web_view_helper;
4995 web_view_helper.InitializeAndLoad(base_url_ + "find_in_page.html",
4996 &frame_client);
4997 web_view_helper.Resize(gfx::Size(640, 480));
4998 RunPendingTasks();
4999
5000 const char kFindString[] = "result";
5001 const int kFindIdentifier = 12345;
5002
5003 auto options = mojom::blink::FindOptions::New();
5004 options->run_synchronously_for_testing = true;
5005 WebString search_text = WebString::FromUTF8(kFindString);
5006 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
5007 TestFindInPageClient main_find_in_page_client;
5008 main_find_in_page_client.SetFrame(main_frame);
5009
5010 auto* second_frame = To<WebLocalFrameImpl>(main_frame->TraverseNext());
5011
5012 // Detach the frame before finding.
5013 RemoveElementById(main_frame, "frame");
5014
5015 EXPECT_TRUE(main_frame->GetFindInPage()->FindInternal(
5016 kFindIdentifier, search_text, *options, false));
5017 EXPECT_FALSE(second_frame->GetFindInPage()->FindInternal(
5018 kFindIdentifier, search_text, *options, false));
5019
5020 RunPendingTasks();
5021 EXPECT_FALSE(main_find_in_page_client.FindResultsAreReady());
5022
5023 main_frame->EnsureTextFinder().ResetMatchCount();
5024
5025 for (WebLocalFrameImpl* frame = main_frame; frame;
5026 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5027 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
5028 search_text, *options);
5029 }
5030
5031 RunPendingTasks();
5032 EXPECT_TRUE(main_find_in_page_client.FindResultsAreReady());
5033 }
5034
TEST_F(WebFrameTest,FindDetachFrameBeforeScopeStrings)5035 TEST_F(WebFrameTest, FindDetachFrameBeforeScopeStrings) {
5036 RegisterMockedHttpURLLoad("find_in_page.html");
5037 RegisterMockedHttpURLLoad("find_in_page_frame.html");
5038
5039 frame_test_helpers::TestWebFrameClient frame_client;
5040 frame_test_helpers::WebViewHelper web_view_helper;
5041 web_view_helper.InitializeAndLoad(base_url_ + "find_in_page.html",
5042 &frame_client);
5043 web_view_helper.Resize(gfx::Size(640, 480));
5044 RunPendingTasks();
5045
5046 const char kFindString[] = "result";
5047 const int kFindIdentifier = 12345;
5048
5049 auto options = mojom::blink::FindOptions::New();
5050 options->run_synchronously_for_testing = true;
5051 WebString search_text = WebString::FromUTF8(kFindString);
5052 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
5053 TestFindInPageClient find_in_page_client;
5054 find_in_page_client.SetFrame(main_frame);
5055
5056 for (WebLocalFrameImpl* frame = main_frame; frame;
5057 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5058 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
5059 kFindIdentifier, search_text, *options, false));
5060 }
5061 RunPendingTasks();
5062 EXPECT_FALSE(find_in_page_client.FindResultsAreReady());
5063
5064 // Detach the frame between finding and scoping.
5065 RemoveElementById(main_frame, "frame");
5066
5067 main_frame->EnsureTextFinder().ResetMatchCount();
5068
5069 for (WebLocalFrameImpl* frame = main_frame; frame;
5070 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5071 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
5072 search_text, *options);
5073 }
5074
5075 RunPendingTasks();
5076 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
5077 }
5078
TEST_F(WebFrameTest,FindDetachFrameWhileScopingStrings)5079 TEST_F(WebFrameTest, FindDetachFrameWhileScopingStrings) {
5080 RegisterMockedHttpURLLoad("find_in_page.html");
5081 RegisterMockedHttpURLLoad("find_in_page_frame.html");
5082
5083 frame_test_helpers::TestWebFrameClient frame_client;
5084 frame_test_helpers::WebViewHelper web_view_helper;
5085 web_view_helper.InitializeAndLoad(base_url_ + "find_in_page.html",
5086 &frame_client);
5087 web_view_helper.Resize(gfx::Size(640, 480));
5088 RunPendingTasks();
5089
5090 const char kFindString[] = "result";
5091 const int kFindIdentifier = 12345;
5092
5093 auto options = mojom::blink::FindOptions::New();
5094 options->run_synchronously_for_testing = true;
5095 WebString search_text = WebString::FromUTF8(kFindString);
5096 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
5097 TestFindInPageClient find_in_page_client;
5098 find_in_page_client.SetFrame(main_frame);
5099
5100 for (WebLocalFrameImpl* frame = main_frame; frame;
5101 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5102 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
5103 kFindIdentifier, search_text, *options, false));
5104 }
5105 RunPendingTasks();
5106 EXPECT_FALSE(find_in_page_client.FindResultsAreReady());
5107
5108 main_frame->EnsureTextFinder().ResetMatchCount();
5109
5110 for (WebLocalFrameImpl* frame = main_frame; frame;
5111 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5112 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
5113 search_text, *options);
5114 }
5115
5116 // The first startScopingStringMatches will have reset the state. Detach
5117 // before it actually scopes.
5118 RemoveElementById(main_frame, "frame");
5119
5120 for (WebLocalFrameImpl* frame = main_frame; frame;
5121 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5122 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
5123 search_text, *options);
5124 }
5125 RunPendingTasks();
5126 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
5127 }
5128
TEST_F(WebFrameTest,ResetMatchCount)5129 TEST_F(WebFrameTest, ResetMatchCount) {
5130 RegisterMockedHttpURLLoad("find_in_generated_frame.html");
5131
5132 frame_test_helpers::TestWebFrameClient frame_client;
5133 frame_test_helpers::WebViewHelper web_view_helper;
5134 web_view_helper.InitializeAndLoad(base_url_ + "find_in_generated_frame.html",
5135 &frame_client);
5136 web_view_helper.Resize(gfx::Size(640, 480));
5137 RunPendingTasks();
5138
5139 const char kFindString[] = "result";
5140 const int kFindIdentifier = 12345;
5141
5142 auto options = mojom::blink::FindOptions::New();
5143 options->run_synchronously_for_testing = true;
5144 WebString search_text = WebString::FromUTF8(kFindString);
5145 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
5146 TestFindInPageClient find_in_page_client;
5147 find_in_page_client.SetFrame(main_frame);
5148
5149 // Check that child frame exists.
5150 EXPECT_TRUE(!!main_frame->TraverseNext());
5151
5152 for (WebLocalFrameImpl* frame = main_frame; frame;
5153 frame = To<WebLocalFrameImpl>(frame->TraverseNext())) {
5154 EXPECT_FALSE(frame->GetFindInPage()->FindInternal(
5155 kFindIdentifier, search_text, *options, false));
5156 }
5157
5158 RunPendingTasks();
5159 EXPECT_FALSE(find_in_page_client.FindResultsAreReady());
5160
5161 main_frame->EnsureTextFinder().ResetMatchCount();
5162 }
5163
TEST_F(WebFrameTest,SetTickmarks)5164 TEST_F(WebFrameTest, SetTickmarks) {
5165 RegisterMockedHttpURLLoad("find.html");
5166
5167 frame_test_helpers::TestWebFrameClient frame_client;
5168 frame_test_helpers::WebViewHelper web_view_helper;
5169 web_view_helper.InitializeAndLoad(base_url_ + "find.html", &frame_client);
5170 web_view_helper.Resize(gfx::Size(640, 480));
5171 RunPendingTasks();
5172
5173 const char kFindString[] = "foo";
5174 const int kFindIdentifier = 12345;
5175
5176 auto options = mojom::blink::FindOptions::New();
5177 options->run_synchronously_for_testing = true;
5178 WebString search_text = WebString::FromUTF8(kFindString);
5179 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
5180 TestFindInPageClient find_in_page_client;
5181 find_in_page_client.SetFrame(main_frame);
5182 EXPECT_TRUE(main_frame->GetFindInPage()->FindInternal(
5183 kFindIdentifier, search_text, *options, false));
5184
5185 main_frame->EnsureTextFinder().ResetMatchCount();
5186 main_frame->EnsureTextFinder().StartScopingStringMatches(
5187 kFindIdentifier, search_text, *options);
5188
5189 RunPendingTasks();
5190 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
5191
5192 const Vector<IntRect> kExpectedOverridingTickmarks = {
5193 IntRect(0, 0, 100, 100), IntRect(0, 20, 100, 100),
5194 IntRect(0, 30, 100, 100)};
5195 const Vector<IntRect> kResetTickmarks;
5196
5197 {
5198 // Test SetTickmarks() with a null target WebElement.
5199 //
5200 // Get the tickmarks for the original find request. It should have 4
5201 // tickmarks, given the search performed above.
5202 LocalFrameView* frame_view =
5203 web_view_helper.LocalMainFrame()->GetFrameView();
5204 ScrollableArea* layout_viewport = frame_view->LayoutViewport();
5205 Vector<IntRect> original_tickmarks = layout_viewport->GetTickmarks();
5206 EXPECT_EQ(4u, original_tickmarks.size());
5207
5208 // Override the tickmarks.
5209 main_frame->SetTickmarks(WebElement(), kExpectedOverridingTickmarks);
5210
5211 // Check the tickmarks are overridden correctly.
5212 Vector<IntRect> overriding_tickmarks_actual =
5213 layout_viewport->GetTickmarks();
5214 EXPECT_EQ(kExpectedOverridingTickmarks, overriding_tickmarks_actual);
5215
5216 // Reset the tickmark behavior.
5217 main_frame->SetTickmarks(WebElement(), kResetTickmarks);
5218
5219 // Check that the original tickmarks are returned
5220 Vector<IntRect> original_tickmarks_after_reset =
5221 layout_viewport->GetTickmarks();
5222 EXPECT_EQ(original_tickmarks, original_tickmarks_after_reset);
5223 }
5224
5225 {
5226 // Test SetTickmarks() with a non-null target WebElement.
5227 //
5228 // Use an element from within find.html for testing. It has no tickmarks.
5229 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5230 WebElement target = frame->GetDocument().GetElementById("textarea1");
5231 ASSERT_FALSE(target.IsNull());
5232 LayoutBox* box = target.ConstUnwrap<Element>()->GetLayoutBoxForScrolling();
5233 ASSERT_TRUE(box);
5234 ScrollableArea* scrollable_area = box->GetScrollableArea();
5235 ASSERT_TRUE(scrollable_area);
5236 Vector<IntRect> original_tickmarks = scrollable_area->GetTickmarks();
5237 EXPECT_EQ(0u, original_tickmarks.size());
5238
5239 // Override the tickmarks.
5240 main_frame->SetTickmarks(target, kExpectedOverridingTickmarks);
5241
5242 // Check the tickmarks are overridden correctly.
5243 Vector<IntRect> overriding_tickmarks_actual =
5244 scrollable_area->GetTickmarks();
5245 EXPECT_EQ(kExpectedOverridingTickmarks, overriding_tickmarks_actual);
5246
5247 // Reset the tickmark behavior.
5248 main_frame->SetTickmarks(target, kResetTickmarks);
5249
5250 // Check that the original tickmarks are returned
5251 Vector<IntRect> original_tickmarks_after_reset =
5252 scrollable_area->GetTickmarks();
5253 EXPECT_EQ(original_tickmarks, original_tickmarks_after_reset);
5254 }
5255 }
5256
TEST_F(WebFrameTest,FindInPageJavaScriptUpdatesDOM)5257 TEST_F(WebFrameTest, FindInPageJavaScriptUpdatesDOM) {
5258 RegisterMockedHttpURLLoad("find.html");
5259
5260 frame_test_helpers::TestWebFrameClient frame_client;
5261 frame_test_helpers::WebViewHelper web_view_helper;
5262 web_view_helper.InitializeAndLoad(base_url_ + "find.html", &frame_client);
5263 web_view_helper.Resize(gfx::Size(640, 480));
5264 RunPendingTasks();
5265
5266 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5267 TestFindInPageClient find_in_page_client;
5268 find_in_page_client.SetFrame(frame);
5269
5270 const int kFindIdentifier = 12345;
5271 static const char* kFindString = "foo";
5272 WebString search_text = WebString::FromUTF8(kFindString);
5273 auto options = mojom::blink::FindOptions::New();
5274 options->run_synchronously_for_testing = true;
5275 bool active_now;
5276
5277 frame->EnsureTextFinder().ResetMatchCount();
5278 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
5279 search_text, *options);
5280 RunPendingTasks();
5281 EXPECT_TRUE(find_in_page_client.FindResultsAreReady());
5282
5283 // Find in a <div> element.
5284 options->new_session = false;
5285 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
5286 kFindIdentifier, search_text, *options, false, &active_now));
5287 EXPECT_TRUE(active_now);
5288
5289 // Insert new text, which contains occurence of |searchText|.
5290 frame->ExecuteScript(WebScriptSource(
5291 "var newTextNode = document.createTextNode('bar5 foo5');"
5292 "var textArea = document.getElementsByTagName('textarea')[0];"
5293 "document.body.insertBefore(newTextNode, textArea);"));
5294
5295 // Find in a <input> element.
5296 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
5297 kFindIdentifier, search_text, *options, false, &active_now));
5298 EXPECT_TRUE(active_now);
5299
5300 // Find in the inserted text node.
5301 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(
5302 kFindIdentifier, search_text, *options, false, &active_now));
5303 frame->GetFindInPage()->StopFinding(
5304 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
5305 WebRange range = frame->SelectionRange();
5306 EXPECT_EQ(5, range.StartOffset());
5307 EXPECT_EQ(8, range.EndOffset());
5308 EXPECT_TRUE(frame->GetDocument().FocusedElement().IsNull());
5309 EXPECT_FALSE(active_now);
5310 }
5311
TEST_F(WebFrameTest,FindInPageJavaScriptUpdatesDOMProperOrdinal)5312 TEST_F(WebFrameTest, FindInPageJavaScriptUpdatesDOMProperOrdinal) {
5313 const WebString search_pattern = WebString::FromUTF8("abc");
5314 // We have 2 occurrences of the pattern in our text.
5315 const char* html =
5316 "foo bar foo bar foo abc bar foo bar foo bar foo bar foo bar foo bar foo "
5317 "bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo "
5318 "bar foo bar foo abc bar <div id='new_text'></div>";
5319
5320 frame_test_helpers::TestWebFrameClient frame_client;
5321 frame_test_helpers::WebViewHelper web_view_helper;
5322 web_view_helper.Initialize(&frame_client);
5323
5324 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5325 frame_test_helpers::LoadHTMLString(frame, html,
5326 url_test_helpers::ToKURL(base_url_));
5327 web_view_helper.Resize(gfx::Size(640, 480));
5328 web_view_helper.GetWebView()->MainFrameWidget()->SetFocus(true);
5329 RunPendingTasks();
5330
5331 TestFindInPageClient find_in_page_client;
5332 find_in_page_client.SetFrame(frame);
5333 const int kFindIdentifier = 12345;
5334
5335 auto options = mojom::blink::FindOptions::New();
5336 options->run_synchronously_for_testing = true;
5337 options->new_session = true;
5338 options->forward = true;
5339 // The first search that will start the scoping process.
5340 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5341 options->Clone());
5342 EXPECT_FALSE(find_in_page_client.FindResultsAreReady());
5343 RunPendingTasks();
5344
5345 EXPECT_EQ(2, find_in_page_client.Count());
5346 EXPECT_EQ(1, find_in_page_client.ActiveIndex());
5347
5348 options->new_session = false;
5349 // The second search will jump to the next match without any scoping.
5350 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5351 options->Clone());
5352 // Run pending tasks to make sure IncreaseMatchCount calls passes.
5353 RunPendingTasks();
5354 EXPECT_EQ(2, find_in_page_client.Count());
5355 EXPECT_EQ(2, find_in_page_client.ActiveIndex());
5356 EXPECT_FALSE(frame->EnsureTextFinder().ScopingInProgress());
5357
5358 // Insert new text, which contains occurence of |searchText|.
5359 frame->ExecuteScript(
5360 WebScriptSource("var textDiv = document.getElementById('new_text');"
5361 "textDiv.innerHTML = 'foo abc';"));
5362
5363 // The third search will find a new match and initiate a new scoping.
5364 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5365 options->Clone());
5366 RunPendingTasks();
5367
5368 EXPECT_EQ(3, find_in_page_client.Count());
5369 EXPECT_EQ(3, find_in_page_client.ActiveIndex());
5370 }
5371
TEST_F(WebFrameTest,FindInPageStopFindActionKeepSelectionInAnotherDocument)5372 TEST_F(WebFrameTest, FindInPageStopFindActionKeepSelectionInAnotherDocument) {
5373 RegisterMockedHttpURLLoad("find.html");
5374 RegisterMockedHttpURLLoad("hello_world.html");
5375 frame_test_helpers::WebViewHelper web_view_helper;
5376 web_view_helper.InitializeAndLoad(base_url_ + "find.html");
5377 ASSERT_TRUE(web_view_helper.LocalMainFrame());
5378 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5379 const int kFindIdentifier = 12345;
5380 auto options = mojom::blink::FindOptions::New();
5381
5382 // Set active match
5383 ASSERT_TRUE(frame->GetFindInPage()->FindInternal(
5384 kFindIdentifier, WebString::FromUTF8("foo"), *options, false));
5385 // Move to another page.
5386 frame_test_helpers::LoadFrame(frame, base_url_ + "hello_world.html");
5387
5388 // Stop Find-In-Page. |TextFinder::active_match_| still hold a |Range| in
5389 // "find.html".
5390 frame->GetFindInPage()->StopFinding(
5391 blink::mojom::StopFindAction::kStopFindActionKeepSelection);
5392
5393 // Pass if not crash. See http://crbug.com/719880 for details.
5394 }
5395
TEST_F(WebFrameTest,FindInPageForcedRedoOfFindInPage)5396 TEST_F(WebFrameTest, FindInPageForcedRedoOfFindInPage) {
5397 const WebString search_pattern = WebString::FromUTF8("bar");
5398 const char* html = "foo bar foo foo bar";
5399 frame_test_helpers::TestWebFrameClient frame_client;
5400 frame_test_helpers::WebViewHelper web_view_helper;
5401 web_view_helper.Initialize(&frame_client);
5402
5403 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5404 frame_test_helpers::LoadHTMLString(frame, html,
5405 url_test_helpers::ToKURL(base_url_));
5406 web_view_helper.Resize(gfx::Size(640, 480));
5407 web_view_helper.GetWebView()->MainFrameWidget()->SetFocus(true);
5408 RunPendingTasks();
5409
5410 TestFindInPageClient find_in_page_client;
5411 find_in_page_client.SetFrame(frame);
5412 const int kFindIdentifier = 12345;
5413
5414 auto options = mojom::blink::FindOptions::New();
5415 options->run_synchronously_for_testing = true;
5416 options->new_session = true;
5417 options->forward = true;
5418 // First run.
5419 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5420 options->Clone());
5421 RunPendingTasks();
5422 EXPECT_EQ(2, find_in_page_client.Count());
5423 EXPECT_EQ(1, find_in_page_client.ActiveIndex());
5424
5425 options->force = true;
5426 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5427 options->Clone());
5428 RunPendingTasks();
5429 EXPECT_EQ(2, find_in_page_client.Count());
5430 EXPECT_EQ(1, find_in_page_client.ActiveIndex());
5431
5432 options->new_session = false;
5433 options->force = false;
5434
5435 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5436 options->Clone());
5437 RunPendingTasks();
5438 EXPECT_EQ(2, find_in_page_client.Count());
5439 EXPECT_EQ(2, find_in_page_client.ActiveIndex());
5440
5441 options->new_session = true;
5442 options->force = true;
5443
5444 frame->GetFindInPage()->Find(kFindIdentifier, search_pattern,
5445 options->Clone());
5446 RunPendingTasks();
5447 EXPECT_EQ(2, find_in_page_client.Count());
5448 EXPECT_EQ(2, find_in_page_client.ActiveIndex());
5449 }
5450
BottomRightMinusOne(const gfx::Rect & rect)5451 static gfx::Point BottomRightMinusOne(const gfx::Rect& rect) {
5452 // FIXME: If we don't subtract 1 from the x- and y-coordinates of the
5453 // selection bounds, selectRange() will select the *next* element. That's
5454 // strictly correct, as hit-testing checks the pixel to the lower-right of
5455 // the input coordinate, but it's a wart on the API.
5456 if (rect.width() > 0) {
5457 return gfx::Point(rect.x() + rect.width() - 1,
5458 rect.y() + rect.height() - 1);
5459 }
5460 return gfx::Point(rect.x() + rect.width(), rect.y() + rect.height() - 1);
5461 }
5462
ElementBounds(WebLocalFrame * frame,const WebString & id)5463 static gfx::Rect ElementBounds(WebLocalFrame* frame, const WebString& id) {
5464 return gfx::Rect(frame->GetDocument().GetElementById(id).BoundsInViewport());
5465 }
5466
SelectionAsString(WebFrame * frame)5467 static std::string SelectionAsString(WebFrame* frame) {
5468 return frame->ToWebLocalFrame()->SelectionAsText().Utf8();
5469 }
5470
TEST_F(WebFrameTest,SelectRange)5471 TEST_F(WebFrameTest, SelectRange) {
5472 WebLocalFrame* frame;
5473 gfx::Rect start_rect;
5474 gfx::Rect end_rect;
5475
5476 RegisterMockedHttpURLLoad("select_range_basic.html");
5477 RegisterMockedHttpURLLoad("select_range_scroll.html");
5478
5479 frame_test_helpers::WebViewHelper web_view_helper;
5480 InitializeTextSelectionWebView(base_url_ + "select_range_basic.html",
5481 &web_view_helper);
5482 frame = web_view_helper.LocalMainFrame();
5483 EXPECT_EQ("Some test text for testing.", SelectionAsString(frame));
5484 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5485 start_rect, end_rect);
5486 frame->ExecuteCommand(WebString::FromUTF8("Unselect"));
5487 EXPECT_EQ("", SelectionAsString(frame));
5488 frame->SelectRange(start_rect.origin(), BottomRightMinusOne(end_rect));
5489 // On some devices, the above bottomRightMinusOne() causes the ending '.' not
5490 // selected.
5491 std::string selection_string = SelectionAsString(frame);
5492 EXPECT_TRUE(selection_string == "Some test text for testing." ||
5493 selection_string == "Some test text for testing");
5494
5495 InitializeTextSelectionWebView(base_url_ + "select_range_scroll.html",
5496 &web_view_helper);
5497 frame = web_view_helper.LocalMainFrame();
5498 EXPECT_EQ("Some offscreen test text for testing.", SelectionAsString(frame));
5499 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5500 start_rect, end_rect);
5501 frame->ExecuteCommand(WebString::FromUTF8("Unselect"));
5502 EXPECT_EQ("", SelectionAsString(frame));
5503 frame->SelectRange(start_rect.origin(), BottomRightMinusOne(end_rect));
5504 // On some devices, the above bottomRightMinusOne() causes the ending '.' not
5505 // selected.
5506 selection_string = SelectionAsString(frame);
5507 EXPECT_TRUE(selection_string == "Some offscreen test text for testing." ||
5508 selection_string == "Some offscreen test text for testing");
5509 }
5510
TEST_F(WebFrameTest,SelectRangeDefaultHandleVisibility)5511 TEST_F(WebFrameTest, SelectRangeDefaultHandleVisibility) {
5512 RegisterMockedHttpURLLoad("select_range_basic.html");
5513
5514 frame_test_helpers::WebViewHelper web_view_helper;
5515 InitializeTextSelectionWebView(base_url_ + "select_range_basic.html",
5516 &web_view_helper);
5517
5518 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5519 frame->SelectRange(WebRange(0, 5), WebLocalFrame::kHideSelectionHandle,
5520 SelectionMenuBehavior::kHide);
5521 EXPECT_FALSE(frame->SelectionRange().IsNull());
5522
5523 EXPECT_FALSE(frame->GetFrame()->Selection().IsHandleVisible())
5524 << "By default selection handles should not be visible";
5525 }
5526
TEST_F(WebFrameTest,SelectRangeHideHandle)5527 TEST_F(WebFrameTest, SelectRangeHideHandle) {
5528 RegisterMockedHttpURLLoad("select_range_basic.html");
5529
5530 frame_test_helpers::WebViewHelper web_view_helper;
5531 InitializeTextSelectionWebView(base_url_ + "select_range_basic.html",
5532 &web_view_helper);
5533
5534 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5535 frame->SelectRange(WebRange(0, 5), WebLocalFrame::kHideSelectionHandle,
5536 SelectionMenuBehavior::kHide);
5537
5538 EXPECT_FALSE(frame->GetFrame()->Selection().IsHandleVisible())
5539 << "Selection handle should not be visible with kHideSelectionHandle";
5540 }
5541
TEST_F(WebFrameTest,SelectRangeShowHandle)5542 TEST_F(WebFrameTest, SelectRangeShowHandle) {
5543 RegisterMockedHttpURLLoad("select_range_basic.html");
5544
5545 frame_test_helpers::WebViewHelper web_view_helper;
5546 InitializeTextSelectionWebView(base_url_ + "select_range_basic.html",
5547 &web_view_helper);
5548
5549 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5550 frame->SelectRange(WebRange(0, 5), WebLocalFrame::kShowSelectionHandle,
5551 SelectionMenuBehavior::kHide);
5552
5553 EXPECT_TRUE(frame->GetFrame()->Selection().IsHandleVisible())
5554 << "Selection handle should be visible with kShowSelectionHandle";
5555 }
5556
TEST_F(WebFrameTest,SelectRangePreserveHandleVisibility)5557 TEST_F(WebFrameTest, SelectRangePreserveHandleVisibility) {
5558 RegisterMockedHttpURLLoad("select_range_basic.html");
5559
5560 frame_test_helpers::WebViewHelper web_view_helper;
5561 InitializeTextSelectionWebView(base_url_ + "select_range_basic.html",
5562 &web_view_helper);
5563
5564 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
5565 frame->SelectRange(WebRange(0, 5), WebLocalFrame::kHideSelectionHandle,
5566 SelectionMenuBehavior::kHide);
5567 frame->SelectRange(WebRange(0, 6), WebLocalFrame::kPreserveHandleVisibility,
5568 SelectionMenuBehavior::kHide);
5569
5570 EXPECT_FALSE(frame->GetFrame()->Selection().IsHandleVisible())
5571 << "kPreserveHandleVisibility should keep handles invisible";
5572
5573 frame->SelectRange(WebRange(0, 5), WebLocalFrame::kShowSelectionHandle,
5574 SelectionMenuBehavior::kHide);
5575 frame->SelectRange(WebRange(0, 6), WebLocalFrame::kPreserveHandleVisibility,
5576 SelectionMenuBehavior::kHide);
5577
5578 EXPECT_TRUE(frame->GetFrame()->Selection().IsHandleVisible())
5579 << "kPreserveHandleVisibility should keep handles visible";
5580 }
5581
TEST_F(WebFrameTest,SelectRangeInIframe)5582 TEST_F(WebFrameTest, SelectRangeInIframe) {
5583 WebFrame* frame;
5584 gfx::Rect start_rect;
5585 gfx::Rect end_rect;
5586
5587 RegisterMockedHttpURLLoad("select_range_iframe.html");
5588 RegisterMockedHttpURLLoad("select_range_basic.html");
5589
5590 frame_test_helpers::WebViewHelper web_view_helper;
5591 InitializeTextSelectionWebView(base_url_ + "select_range_iframe.html",
5592 &web_view_helper);
5593 frame = web_view_helper.GetWebView()->MainFrame();
5594 WebLocalFrame* subframe = frame->FirstChild()->ToWebLocalFrame();
5595 EXPECT_EQ("Some test text for testing.", SelectionAsString(subframe));
5596 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5597 start_rect, end_rect);
5598 subframe->ExecuteCommand(WebString::FromUTF8("Unselect"));
5599 EXPECT_EQ("", SelectionAsString(subframe));
5600 subframe->SelectRange(start_rect.origin(), BottomRightMinusOne(end_rect));
5601 // On some devices, the above bottomRightMinusOne() causes the ending '.' not
5602 // selected.
5603 std::string selection_string = SelectionAsString(subframe);
5604 EXPECT_TRUE(selection_string == "Some test text for testing." ||
5605 selection_string == "Some test text for testing");
5606 }
5607
TEST_F(WebFrameTest,SelectRangeDivContentEditable)5608 TEST_F(WebFrameTest, SelectRangeDivContentEditable) {
5609 WebLocalFrame* frame;
5610 gfx::Rect start_rect;
5611 gfx::Rect end_rect;
5612
5613 RegisterMockedHttpURLLoad("select_range_div_editable.html");
5614
5615 // Select the middle of an editable element, then try to extend the selection
5616 // to the top of the document. The selection range should be clipped to the
5617 // bounds of the editable element.
5618 frame_test_helpers::WebViewHelper web_view_helper;
5619 InitializeTextSelectionWebView(base_url_ + "select_range_div_editable.html",
5620 &web_view_helper);
5621 frame = web_view_helper.LocalMainFrame();
5622 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5623 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5624 start_rect, end_rect);
5625
5626 frame->SelectRange(BottomRightMinusOne(end_rect), gfx::Point());
5627 EXPECT_EQ("16-char header. This text is initially selected.",
5628 SelectionAsString(frame));
5629
5630 // As above, but extending the selection to the bottom of the document.
5631 InitializeTextSelectionWebView(base_url_ + "select_range_div_editable.html",
5632 &web_view_helper);
5633 frame = web_view_helper.LocalMainFrame();
5634
5635 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5636 start_rect, end_rect);
5637 frame->SelectRange(start_rect.origin(), BottomRightMinusOne(end_rect));
5638 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5639 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5640 start_rect, end_rect);
5641
5642 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5643 start_rect, end_rect);
5644 frame->SelectRange(start_rect.origin(), gfx::Point(640, 480));
5645 EXPECT_EQ("This text is initially selected. 16-char footer.",
5646 SelectionAsString(frame));
5647 }
5648
5649 // positionForPoint returns the wrong values for contenteditable spans. See
5650 // http://crbug.com/238334.
TEST_F(WebFrameTest,DISABLED_SelectRangeSpanContentEditable)5651 TEST_F(WebFrameTest, DISABLED_SelectRangeSpanContentEditable) {
5652 WebLocalFrame* frame;
5653 gfx::Rect start_rect;
5654 gfx::Rect end_rect;
5655
5656 RegisterMockedHttpURLLoad("select_range_span_editable.html");
5657
5658 // Select the middle of an editable element, then try to extend the selection
5659 // to the top of the document.
5660 // The selection range should be clipped to the bounds of the editable
5661 // element.
5662 frame_test_helpers::WebViewHelper web_view_helper;
5663 InitializeTextSelectionWebView(base_url_ + "select_range_span_editable.html",
5664 &web_view_helper);
5665 frame = web_view_helper.LocalMainFrame();
5666 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5667 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5668 start_rect, end_rect);
5669
5670 frame->SelectRange(BottomRightMinusOne(end_rect), gfx::Point());
5671 EXPECT_EQ("16-char header. This text is initially selected.",
5672 SelectionAsString(frame));
5673
5674 // As above, but extending the selection to the bottom of the document.
5675 InitializeTextSelectionWebView(base_url_ + "select_range_span_editable.html",
5676 &web_view_helper);
5677 frame = web_view_helper.LocalMainFrame();
5678
5679 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5680 start_rect, end_rect);
5681 frame->SelectRange(start_rect.origin(), BottomRightMinusOne(end_rect));
5682 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5683 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5684 start_rect, end_rect);
5685
5686 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5687 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5688 start_rect, end_rect);
5689 frame->SelectRange(start_rect.origin(), gfx::Point(640, 480));
5690 EXPECT_EQ("This text is initially selected. 16-char footer.",
5691 SelectionAsString(frame));
5692 }
5693
TEST_F(WebFrameTest,SelectRangeCanMoveSelectionStart)5694 TEST_F(WebFrameTest, SelectRangeCanMoveSelectionStart) {
5695 RegisterMockedHttpURLLoad("text_selection.html");
5696 frame_test_helpers::WebViewHelper web_view_helper;
5697 InitializeTextSelectionWebView(base_url_ + "text_selection.html",
5698 &web_view_helper);
5699 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
5700
5701 // Select second span. We can move the start to include the first span.
5702 frame->ExecuteScript(WebScriptSource("selectElement('header_2');"));
5703 EXPECT_EQ("Header 2.", SelectionAsString(frame));
5704 frame->SelectRange(BottomRightMinusOne(ElementBounds(frame, "header_2")),
5705 ElementBounds(frame, "header_1").origin());
5706 EXPECT_EQ("Header 1. Header 2.", SelectionAsString(frame));
5707
5708 // We can move the start and end together.
5709 frame->ExecuteScript(WebScriptSource("selectElement('header_1');"));
5710 EXPECT_EQ("Header 1.", SelectionAsString(frame));
5711 frame->SelectRange(BottomRightMinusOne(ElementBounds(frame, "header_1")),
5712 BottomRightMinusOne(ElementBounds(frame, "header_1")));
5713 EXPECT_EQ("", SelectionAsString(frame));
5714 // Selection is a caret, not empty.
5715 EXPECT_FALSE(frame->SelectionRange().IsNull());
5716
5717 // We can move the start across the end.
5718 frame->ExecuteScript(WebScriptSource("selectElement('header_1');"));
5719 EXPECT_EQ("Header 1.", SelectionAsString(frame));
5720 frame->SelectRange(BottomRightMinusOne(ElementBounds(frame, "header_1")),
5721 BottomRightMinusOne(ElementBounds(frame, "header_2")));
5722 EXPECT_EQ(" Header 2.", SelectionAsString(frame));
5723
5724 // Can't extend the selection part-way into an editable element.
5725 frame->ExecuteScript(WebScriptSource("selectElement('footer_2');"));
5726 EXPECT_EQ("Footer 2.", SelectionAsString(frame));
5727 frame->SelectRange(BottomRightMinusOne(ElementBounds(frame, "footer_2")),
5728 ElementBounds(frame, "editable_2").origin());
5729 EXPECT_EQ(" [ Footer 1. Footer 2.", SelectionAsString(frame));
5730
5731 // Can extend the selection completely across editable elements.
5732 frame->ExecuteScript(WebScriptSource("selectElement('footer_2');"));
5733 EXPECT_EQ("Footer 2.", SelectionAsString(frame));
5734 frame->SelectRange(BottomRightMinusOne(ElementBounds(frame, "footer_2")),
5735 ElementBounds(frame, "header_2").origin());
5736 EXPECT_EQ("Header 2. ] [ Editable 1. Editable 2. ] [ Footer 1. Footer 2.",
5737 SelectionAsString(frame));
5738
5739 // If the selection is editable text, we can't extend it into non-editable
5740 // text.
5741 frame->ExecuteScript(WebScriptSource("selectElement('editable_2');"));
5742 EXPECT_EQ("Editable 2.", SelectionAsString(frame));
5743 frame->SelectRange(BottomRightMinusOne(ElementBounds(frame, "editable_2")),
5744 ElementBounds(frame, "header_2").origin());
5745 EXPECT_EQ("[ Editable 1. Editable 2.", SelectionAsString(frame));
5746 }
5747
TEST_F(WebFrameTest,SelectRangeCanMoveSelectionEnd)5748 TEST_F(WebFrameTest, SelectRangeCanMoveSelectionEnd) {
5749 RegisterMockedHttpURLLoad("text_selection.html");
5750 frame_test_helpers::WebViewHelper web_view_helper;
5751 InitializeTextSelectionWebView(base_url_ + "text_selection.html",
5752 &web_view_helper);
5753 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
5754
5755 // Select first span. We can move the end to include the second span.
5756 frame->ExecuteScript(WebScriptSource("selectElement('header_1');"));
5757 EXPECT_EQ("Header 1.", SelectionAsString(frame));
5758 frame->SelectRange(ElementBounds(frame, "header_1").origin(),
5759 BottomRightMinusOne(ElementBounds(frame, "header_2")));
5760 EXPECT_EQ("Header 1. Header 2.", SelectionAsString(frame));
5761
5762 // We can move the start and end together.
5763 frame->ExecuteScript(WebScriptSource("selectElement('header_2');"));
5764 EXPECT_EQ("Header 2.", SelectionAsString(frame));
5765 frame->SelectRange(ElementBounds(frame, "header_2").origin(),
5766 ElementBounds(frame, "header_2").origin());
5767 EXPECT_EQ("", SelectionAsString(frame));
5768 // Selection is a caret, not empty.
5769 EXPECT_FALSE(frame->SelectionRange().IsNull());
5770
5771 // We can move the end across the start.
5772 frame->ExecuteScript(WebScriptSource("selectElement('header_2');"));
5773 EXPECT_EQ("Header 2.", SelectionAsString(frame));
5774 frame->SelectRange(ElementBounds(frame, "header_2").origin(),
5775 ElementBounds(frame, "header_1").origin());
5776 EXPECT_EQ("Header 1. ", SelectionAsString(frame));
5777
5778 // Can't extend the selection part-way into an editable element.
5779 frame->ExecuteScript(WebScriptSource("selectElement('header_1');"));
5780 EXPECT_EQ("Header 1.", SelectionAsString(frame));
5781 frame->SelectRange(ElementBounds(frame, "header_1").origin(),
5782 BottomRightMinusOne(ElementBounds(frame, "editable_1")));
5783 EXPECT_EQ("Header 1. Header 2. ] ", SelectionAsString(frame));
5784
5785 // Can extend the selection completely across editable elements.
5786 frame->ExecuteScript(WebScriptSource("selectElement('header_1');"));
5787 EXPECT_EQ("Header 1.", SelectionAsString(frame));
5788 frame->SelectRange(ElementBounds(frame, "header_1").origin(),
5789 BottomRightMinusOne(ElementBounds(frame, "footer_1")));
5790 EXPECT_EQ("Header 1. Header 2. ] [ Editable 1. Editable 2. ] [ Footer 1.",
5791 SelectionAsString(frame));
5792
5793 // If the selection is editable text, we can't extend it into non-editable
5794 // text.
5795 frame->ExecuteScript(WebScriptSource("selectElement('editable_1');"));
5796 EXPECT_EQ("Editable 1.", SelectionAsString(frame));
5797 frame->SelectRange(ElementBounds(frame, "editable_1").origin(),
5798 BottomRightMinusOne(ElementBounds(frame, "footer_1")));
5799 EXPECT_EQ("Editable 1. Editable 2. ]", SelectionAsString(frame));
5800 }
5801
TEST_F(WebFrameTest,MoveRangeSelectionExtent)5802 TEST_F(WebFrameTest, MoveRangeSelectionExtent) {
5803 WebLocalFrameImpl* frame;
5804 gfx::Rect start_rect;
5805 gfx::Rect end_rect;
5806
5807 RegisterMockedHttpURLLoad("move_range_selection_extent.html");
5808
5809 frame_test_helpers::WebViewHelper web_view_helper;
5810 InitializeTextSelectionWebView(base_url_ + "move_range_selection_extent.html",
5811 &web_view_helper);
5812 frame = web_view_helper.LocalMainFrame();
5813 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5814 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5815 start_rect, end_rect);
5816
5817 frame->MoveRangeSelectionExtent(gfx::Point(640, 480));
5818 EXPECT_EQ("This text is initially selected. 16-char footer.",
5819 SelectionAsString(frame));
5820
5821 frame->MoveRangeSelectionExtent(gfx::Point());
5822 EXPECT_EQ("16-char header. ", SelectionAsString(frame));
5823
5824 // Reset with swapped base and extent.
5825 frame->SelectRange(end_rect.origin(), BottomRightMinusOne(start_rect));
5826 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5827
5828 frame->MoveRangeSelectionExtent(gfx::Point(640, 480));
5829 EXPECT_EQ(" 16-char footer.", SelectionAsString(frame));
5830
5831 frame->MoveRangeSelectionExtent(gfx::Point());
5832 EXPECT_EQ("16-char header. This text is initially selected.",
5833 SelectionAsString(frame));
5834
5835 frame->ExecuteCommand(WebString::FromUTF8("Unselect"));
5836 EXPECT_EQ("", SelectionAsString(frame));
5837 }
5838
TEST_F(WebFrameTest,MoveRangeSelectionExtentCannotCollapse)5839 TEST_F(WebFrameTest, MoveRangeSelectionExtentCannotCollapse) {
5840 WebLocalFrameImpl* frame;
5841 gfx::Rect start_rect;
5842 gfx::Rect end_rect;
5843
5844 RegisterMockedHttpURLLoad("move_range_selection_extent.html");
5845
5846 frame_test_helpers::WebViewHelper web_view_helper;
5847 InitializeTextSelectionWebView(base_url_ + "move_range_selection_extent.html",
5848 &web_view_helper);
5849 frame = web_view_helper.LocalMainFrame();
5850 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5851 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5852 start_rect, end_rect);
5853
5854 frame->MoveRangeSelectionExtent(BottomRightMinusOne(start_rect));
5855 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5856
5857 // Reset with swapped base and extent.
5858 frame->SelectRange(end_rect.origin(), BottomRightMinusOne(start_rect));
5859 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5860
5861 frame->MoveRangeSelectionExtent(BottomRightMinusOne(end_rect));
5862 EXPECT_EQ("This text is initially selected.", SelectionAsString(frame));
5863 }
5864
TEST_F(WebFrameTest,MoveRangeSelectionExtentScollsInputField)5865 TEST_F(WebFrameTest, MoveRangeSelectionExtentScollsInputField) {
5866 WebLocalFrameImpl* frame;
5867 gfx::Rect start_rect;
5868 gfx::Rect end_rect;
5869
5870 RegisterMockedHttpURLLoad("move_range_selection_extent_input_field.html");
5871
5872 frame_test_helpers::WebViewHelper web_view_helper;
5873 InitializeTextSelectionWebView(
5874 base_url_ + "move_range_selection_extent_input_field.html",
5875 &web_view_helper);
5876 frame = web_view_helper.LocalMainFrame();
5877 EXPECT_EQ("Length", SelectionAsString(frame));
5878 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
5879 start_rect, end_rect);
5880
5881 EXPECT_EQ(0, frame->GetFrame()
5882 ->Selection()
5883 .ComputeVisibleSelectionInDOMTree()
5884 .RootEditableElement()
5885 ->scrollLeft());
5886 frame->MoveRangeSelectionExtent(gfx::Point(end_rect.x() + 500, end_rect.y()));
5887 EXPECT_GE(frame->GetFrame()
5888 ->Selection()
5889 .ComputeVisibleSelectionInDOMTree()
5890 .RootEditableElement()
5891 ->scrollLeft(),
5892 1);
5893 EXPECT_EQ("Lengthy text goes here.", SelectionAsString(frame));
5894 }
5895
TEST_F(WebFrameTest,SmartClipData)5896 TEST_F(WebFrameTest, SmartClipData) {
5897 static const char kExpectedClipText[] = "\nPrice 10,000,000won";
5898 static const char kExpectedClipHtml[] =
5899 "<div id=\"div4\" style=\"padding: 10px; margin: 10px; border: 2px solid "
5900 "skyblue; float: left; width: 190px; height: 30px; color: rgb(0, 0, 0); "
5901 "font-family: myahem; font-size: 8px; font-style: normal; "
5902 "font-variant-ligatures: normal; font-variant-caps: normal; font-weight: "
5903 "400; letter-spacing: normal; orphans: 2; text-align: start; "
5904 "text-indent: 0px; text-transform: none; white-space: normal; widows: 2; "
5905 "word-spacing: 0px; -webkit-text-stroke-width: 0px; "
5906 "text-decoration-thickness: initial; text-decoration-style: initial; "
5907 "text-decoration-color: initial;\">Air conditioner</div><div id=\"div5\" "
5908 "style=\"padding: 10px; margin: 10px; border: 2px solid skyblue; float: "
5909 "left; width: 190px; height: 30px; color: rgb(0, 0, 0); font-family: "
5910 "myahem; font-size: 8px; font-style: normal; font-variant-ligatures: "
5911 "normal; font-variant-caps: normal; font-weight: 400; letter-spacing: "
5912 "normal; orphans: 2; text-align: start; text-indent: 0px; "
5913 "text-transform: none; white-space: normal; widows: 2; word-spacing: "
5914 "0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: "
5915 "initial; text-decoration-style: initial; text-decoration-color: "
5916 "initial;\">Price 10,000,000won</div>";
5917 WebString clip_text;
5918 WebString clip_html;
5919 WebRect clip_rect;
5920 RegisterMockedHttpURLLoad("Ahem.ttf");
5921 RegisterMockedHttpURLLoad("smartclip.html");
5922 frame_test_helpers::WebViewHelper web_view_helper;
5923 web_view_helper.InitializeAndLoad(base_url_ + "smartclip.html");
5924 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
5925 web_view_helper.Resize(gfx::Size(500, 500));
5926 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
5927 WebRect crop_rect(300, 125, 152, 50);
5928 frame->ExtractSmartClipData(crop_rect, clip_text, clip_html, clip_rect);
5929 EXPECT_EQ(kExpectedClipText, clip_text);
5930 EXPECT_EQ(kExpectedClipHtml, clip_html);
5931 }
5932
TEST_F(WebFrameTest,SmartClipDataWithPinchZoom)5933 TEST_F(WebFrameTest, SmartClipDataWithPinchZoom) {
5934 static const char kExpectedClipText[] = "\nPrice 10,000,000won";
5935 static const char kExpectedClipHtml[] =
5936 "<div id=\"div4\" style=\"padding: 10px; margin: 10px; border: 2px solid "
5937 "skyblue; float: left; width: 190px; height: 30px; color: rgb(0, 0, 0); "
5938 "font-family: myahem; font-size: 8px; font-style: normal; "
5939 "font-variant-ligatures: normal; font-variant-caps: normal; font-weight: "
5940 "400; letter-spacing: normal; orphans: 2; text-align: start; "
5941 "text-indent: 0px; text-transform: none; white-space: normal; widows: 2; "
5942 "word-spacing: 0px; -webkit-text-stroke-width: 0px; "
5943 "text-decoration-thickness: initial; text-decoration-style: initial; "
5944 "text-decoration-color: initial;\">Air conditioner</div><div id=\"div5\" "
5945 "style=\"padding: 10px; margin: 10px; border: 2px solid skyblue; float: "
5946 "left; width: 190px; height: 30px; color: rgb(0, 0, 0); font-family: "
5947 "myahem; font-size: 8px; font-style: normal; font-variant-ligatures: "
5948 "normal; font-variant-caps: normal; font-weight: 400; letter-spacing: "
5949 "normal; orphans: 2; text-align: start; text-indent: 0px; "
5950 "text-transform: none; white-space: normal; widows: 2; word-spacing: "
5951 "0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: "
5952 "initial; text-decoration-style: initial; text-decoration-color: "
5953 "initial;\">Price 10,000,000won</div>";
5954 WebString clip_text;
5955 WebString clip_html;
5956 WebRect clip_rect;
5957 RegisterMockedHttpURLLoad("Ahem.ttf");
5958 RegisterMockedHttpURLLoad("smartclip.html");
5959 frame_test_helpers::WebViewHelper web_view_helper;
5960 web_view_helper.InitializeAndLoad(base_url_ + "smartclip.html");
5961 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
5962 web_view_helper.Resize(gfx::Size(500, 500));
5963 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
5964 web_view_helper.GetWebView()->SetPageScaleFactor(1.5);
5965 web_view_helper.GetWebView()->SetVisualViewportOffset(gfx::PointF(167, 100));
5966 WebRect crop_rect(200, 38, 228, 75);
5967 frame->ExtractSmartClipData(crop_rect, clip_text, clip_html, clip_rect);
5968 EXPECT_EQ(kExpectedClipText, clip_text);
5969 EXPECT_EQ(kExpectedClipHtml, clip_html);
5970 }
5971
TEST_F(WebFrameTest,SmartClipReturnsEmptyStringsWhenUserSelectIsNone)5972 TEST_F(WebFrameTest, SmartClipReturnsEmptyStringsWhenUserSelectIsNone) {
5973 WebString clip_text;
5974 WebString clip_html;
5975 WebRect clip_rect;
5976 RegisterMockedHttpURLLoad("Ahem.ttf");
5977 RegisterMockedHttpURLLoad("smartclip_user_select_none.html");
5978 frame_test_helpers::WebViewHelper web_view_helper;
5979 web_view_helper.InitializeAndLoad(base_url_ +
5980 "smartclip_user_select_none.html");
5981 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
5982 web_view_helper.Resize(gfx::Size(500, 500));
5983 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
5984 WebRect crop_rect(0, 0, 100, 100);
5985 frame->ExtractSmartClipData(crop_rect, clip_text, clip_html, clip_rect);
5986 EXPECT_STREQ("", clip_text.Utf8().c_str());
5987 EXPECT_STREQ("", clip_html.Utf8().c_str());
5988 }
5989
TEST_F(WebFrameTest,SmartClipDoesNotCrashPositionReversed)5990 TEST_F(WebFrameTest, SmartClipDoesNotCrashPositionReversed) {
5991 WebString clip_text;
5992 WebString clip_html;
5993 WebRect clip_rect;
5994 RegisterMockedHttpURLLoad("Ahem.ttf");
5995 RegisterMockedHttpURLLoad("smartclip_reversed_positions.html");
5996 frame_test_helpers::WebViewHelper web_view_helper;
5997 web_view_helper.InitializeAndLoad(base_url_ +
5998 "smartclip_reversed_positions.html");
5999 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
6000 web_view_helper.Resize(gfx::Size(500, 500));
6001 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
6002 // Left upper corner of the rect will be end position in the DOM hierarchy.
6003 WebRect crop_rect(30, 110, 400, 250);
6004 // This should not still crash. See crbug.com/589082 for more details.
6005 frame->ExtractSmartClipData(crop_rect, clip_text, clip_html, clip_rect);
6006 }
6007
ComputeOffset(LayoutObject * layout_object,int x,int y)6008 static int ComputeOffset(LayoutObject* layout_object, int x, int y) {
6009 return layout_object->PositionForPoint(PhysicalOffset(x, y))
6010 .GetPosition()
6011 .ComputeOffsetInContainerNode();
6012 }
6013
6014 // positionForPoint returns the wrong values for contenteditable spans. See
6015 // http://crbug.com/238334.
TEST_F(WebFrameTest,DISABLED_PositionForPointTest)6016 TEST_F(WebFrameTest, DISABLED_PositionForPointTest) {
6017 RegisterMockedHttpURLLoad("select_range_span_editable.html");
6018 frame_test_helpers::WebViewHelper web_view_helper;
6019 InitializeTextSelectionWebView(base_url_ + "select_range_span_editable.html",
6020 &web_view_helper);
6021 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
6022 LayoutObject* layout_object = main_frame->GetFrame()
6023 ->Selection()
6024 .ComputeVisibleSelectionInDOMTree()
6025 .RootEditableElement()
6026 ->GetLayoutObject();
6027 EXPECT_EQ(0, ComputeOffset(layout_object, -1, -1));
6028 EXPECT_EQ(64, ComputeOffset(layout_object, 1000, 1000));
6029
6030 RegisterMockedHttpURLLoad("select_range_div_editable.html");
6031 InitializeTextSelectionWebView(base_url_ + "select_range_div_editable.html",
6032 &web_view_helper);
6033 main_frame = web_view_helper.LocalMainFrame();
6034 layout_object = main_frame->GetFrame()
6035 ->Selection()
6036 .ComputeVisibleSelectionInDOMTree()
6037 .RootEditableElement()
6038 ->GetLayoutObject();
6039 EXPECT_EQ(0, ComputeOffset(layout_object, -1, -1));
6040 EXPECT_EQ(64, ComputeOffset(layout_object, 1000, 1000));
6041 }
6042
6043 #if !defined(OS_MAC) && !defined(OS_LINUX) && !defined(OS_CHROMEOS)
TEST_F(WebFrameTest,SelectRangeStaysHorizontallyAlignedWhenMoved)6044 TEST_F(WebFrameTest, SelectRangeStaysHorizontallyAlignedWhenMoved) {
6045 RegisterMockedHttpURLLoad("move_caret.html");
6046
6047 frame_test_helpers::WebViewHelper web_view_helper;
6048 InitializeTextSelectionWebView(base_url_ + "move_caret.html",
6049 &web_view_helper);
6050 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
6051
6052 gfx::Rect initial_start_rect;
6053 gfx::Rect initial_end_rect;
6054 gfx::Rect start_rect;
6055 gfx::Rect end_rect;
6056
6057 frame->ExecuteScript(WebScriptSource("selectRange();"));
6058 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6059 initial_start_rect, initial_end_rect);
6060 gfx::Point moved_start(initial_start_rect.origin());
6061
6062 moved_start.Offset(0, 40);
6063 frame->SelectRange(moved_start, BottomRightMinusOne(initial_end_rect));
6064 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6065 start_rect, end_rect);
6066 EXPECT_EQ(start_rect, initial_start_rect);
6067 EXPECT_EQ(end_rect, initial_end_rect);
6068
6069 moved_start.Offset(0, -80);
6070 frame->SelectRange(moved_start, BottomRightMinusOne(initial_end_rect));
6071 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6072 start_rect, end_rect);
6073 EXPECT_EQ(start_rect, initial_start_rect);
6074 EXPECT_EQ(end_rect, initial_end_rect);
6075
6076 gfx::Point moved_end(BottomRightMinusOne(initial_end_rect));
6077
6078 moved_end.Offset(0, 40);
6079 frame->SelectRange(initial_start_rect.origin(), moved_end);
6080 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6081 start_rect, end_rect);
6082 EXPECT_EQ(start_rect, initial_start_rect);
6083 EXPECT_EQ(end_rect, initial_end_rect);
6084
6085 moved_end.Offset(0, -80);
6086 frame->SelectRange(initial_start_rect.origin(), moved_end);
6087 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6088 start_rect, end_rect);
6089 EXPECT_EQ(start_rect, initial_start_rect);
6090 EXPECT_EQ(end_rect, initial_end_rect);
6091 }
6092
TEST_F(WebFrameTest,MoveCaretStaysHorizontallyAlignedWhenMoved)6093 TEST_F(WebFrameTest, MoveCaretStaysHorizontallyAlignedWhenMoved) {
6094 WebLocalFrameImpl* frame;
6095 RegisterMockedHttpURLLoad("move_caret.html");
6096
6097 frame_test_helpers::WebViewHelper web_view_helper;
6098 InitializeTextSelectionWebView(base_url_ + "move_caret.html",
6099 &web_view_helper);
6100 frame = (WebLocalFrameImpl*)web_view_helper.GetWebView()->MainFrame();
6101
6102 gfx::Rect initial_start_rect;
6103 gfx::Rect initial_end_rect;
6104 gfx::Rect start_rect;
6105 gfx::Rect end_rect;
6106
6107 frame->ExecuteScript(WebScriptSource("selectCaret();"));
6108 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6109 initial_start_rect, initial_end_rect);
6110 gfx::Point move_to(initial_start_rect.origin());
6111
6112 move_to.Offset(0, 40);
6113 frame->MoveCaretSelection(move_to);
6114 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6115 start_rect, end_rect);
6116 EXPECT_EQ(start_rect, initial_start_rect);
6117 EXPECT_EQ(end_rect, initial_end_rect);
6118
6119 move_to.Offset(0, -80);
6120 frame->MoveCaretSelection(move_to);
6121 web_view_helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds(
6122 start_rect, end_rect);
6123 EXPECT_EQ(start_rect, initial_start_rect);
6124 EXPECT_EQ(end_rect, initial_end_rect);
6125 }
6126 #endif
6127
6128 class CompositedSelectionBoundsTest
6129 : public WebFrameTest,
6130 private ScopedCompositedSelectionUpdateForTest {
6131 protected:
CompositedSelectionBoundsTest()6132 CompositedSelectionBoundsTest()
6133 : ScopedCompositedSelectionUpdateForTest(true) {
6134 RegisterMockedHttpURLLoad("Ahem.ttf");
6135
6136 web_view_helper_.Initialize(nullptr, nullptr, &web_widget_client_);
6137 web_view_helper_.GetWebView()->GetSettings()->SetDefaultFontSize(12);
6138 web_view_helper_.GetWebView()->SetDefaultPageScaleLimits(1, 1);
6139 web_view_helper_.Resize(gfx::Size(640, 480));
6140 }
6141
RunTestWithNoSelection(const char * test_file)6142 void RunTestWithNoSelection(const char* test_file) {
6143 RegisterMockedHttpURLLoad(test_file);
6144 web_view_helper_.GetWebView()->MainFrameWidget()->SetFocus(true);
6145 frame_test_helpers::LoadFrame(
6146 web_view_helper_.GetWebView()->MainFrameImpl(), base_url_ + test_file);
6147 UpdateAllLifecyclePhases(web_view_helper_.GetWebView());
6148
6149 cc::LayerTreeHost* layer_tree_host = web_widget_client_.layer_tree_host();
6150 const cc::LayerSelection& selection = layer_tree_host->selection();
6151
6152 ASSERT_EQ(selection, cc::LayerSelection());
6153 ASSERT_EQ(selection.start, cc::LayerSelectionBound());
6154 ASSERT_EQ(selection.end, cc::LayerSelectionBound());
6155 }
6156
RunTest(const char * test_file)6157 void RunTest(const char* test_file) {
6158 RegisterMockedHttpURLLoad(test_file);
6159 web_view_helper_.GetWebView()->MainFrameWidget()->SetFocus(true);
6160 frame_test_helpers::LoadFrame(
6161 web_view_helper_.GetWebView()->MainFrameImpl(), base_url_ + test_file);
6162 UpdateAllLifecyclePhases(web_view_helper_.GetWebView());
6163
6164 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
6165 v8::Local<v8::Value> result =
6166 web_view_helper_.GetWebView()
6167 ->MainFrameImpl()
6168 ->ExecuteScriptAndReturnValue(WebScriptSource("expectedResult"));
6169 ASSERT_FALSE(result.IsEmpty() || (*result)->IsUndefined());
6170
6171 ASSERT_TRUE((*result)->IsArray());
6172 v8::Array& expected_result = *v8::Array::Cast(*result);
6173 ASSERT_GE(expected_result.Length(), 10u);
6174
6175 v8::Local<v8::Context> context =
6176 v8::Isolate::GetCurrent()->GetCurrentContext();
6177
6178 const int start_edge_start_in_layer_x = expected_result.Get(context, 1)
6179 .ToLocalChecked()
6180 .As<v8::Int32>()
6181 ->Value();
6182 const int start_edge_start_in_layer_y = expected_result.Get(context, 2)
6183 .ToLocalChecked()
6184 .As<v8::Int32>()
6185 ->Value();
6186 const int start_edge_end_in_layer_x = expected_result.Get(context, 3)
6187 .ToLocalChecked()
6188 .As<v8::Int32>()
6189 ->Value();
6190 const int start_edge_end_in_layer_y = expected_result.Get(context, 4)
6191 .ToLocalChecked()
6192 .As<v8::Int32>()
6193 ->Value();
6194
6195 const int end_edge_start_in_layer_x = expected_result.Get(context, 6)
6196 .ToLocalChecked()
6197 .As<v8::Int32>()
6198 ->Value();
6199 const int end_edge_start_in_layer_y = expected_result.Get(context, 7)
6200 .ToLocalChecked()
6201 .As<v8::Int32>()
6202 ->Value();
6203 const int end_edge_end_in_layer_x = expected_result.Get(context, 8)
6204 .ToLocalChecked()
6205 .As<v8::Int32>()
6206 ->Value();
6207 const int end_edge_end_in_layer_y = expected_result.Get(context, 9)
6208 .ToLocalChecked()
6209 .As<v8::Int32>()
6210 ->Value();
6211
6212 FloatPoint hit_point;
6213
6214 if (expected_result.Length() >= 17) {
6215 hit_point = FloatPoint(expected_result.Get(context, 15)
6216 .ToLocalChecked()
6217 .As<v8::Int32>()
6218 ->Value(),
6219 expected_result.Get(context, 16)
6220 .ToLocalChecked()
6221 .As<v8::Int32>()
6222 ->Value());
6223 } else {
6224 hit_point =
6225 FloatPoint((start_edge_start_in_layer_x + start_edge_end_in_layer_x +
6226 end_edge_start_in_layer_x + end_edge_end_in_layer_x) /
6227 4,
6228 (start_edge_start_in_layer_y + start_edge_end_in_layer_y +
6229 end_edge_start_in_layer_y + end_edge_end_in_layer_y) /
6230 4 +
6231 3);
6232 }
6233
6234 WebGestureEvent gesture_event(WebInputEvent::Type::kGestureTap,
6235 WebInputEvent::kNoModifiers,
6236 WebInputEvent::GetStaticTimeStampForTests(),
6237 WebGestureDevice::kTouchscreen);
6238 gesture_event.SetFrameScale(1);
6239 gesture_event.SetPositionInWidget(hit_point);
6240 gesture_event.SetPositionInScreen(hit_point);
6241
6242 web_view_helper_.GetWebView()
6243 ->MainFrameImpl()
6244 ->GetFrame()
6245 ->GetEventHandler()
6246 .HandleGestureEvent(gesture_event);
6247
6248 cc::LayerTreeHost* layer_tree_host = web_widget_client_.layer_tree_host();
6249 const cc::LayerSelection& selection = layer_tree_host->selection();
6250
6251 ASSERT_NE(selection, cc::LayerSelection());
6252 ASSERT_NE(selection.start, cc::LayerSelectionBound());
6253 ASSERT_NE(selection.end, cc::LayerSelectionBound());
6254
6255 blink::Node* layer_owner_node_for_start = V8Node::ToImplWithTypeCheck(
6256 v8::Isolate::GetCurrent(),
6257 expected_result.Get(context, 0).ToLocalChecked());
6258 ASSERT_TRUE(layer_owner_node_for_start);
6259 EXPECT_EQ(GetExpectedLayerForSelection(layer_owner_node_for_start)
6260 ->CcLayer()
6261 .id(),
6262 selection.start.layer_id);
6263
6264 EXPECT_EQ(start_edge_start_in_layer_x, selection.start.edge_start.x());
6265 EXPECT_EQ(start_edge_start_in_layer_y, selection.start.edge_start.y());
6266 EXPECT_EQ(start_edge_end_in_layer_x, selection.start.edge_end.x());
6267
6268 blink::Node* layer_owner_node_for_end = V8Node::ToImplWithTypeCheck(
6269 v8::Isolate::GetCurrent(),
6270 expected_result.Get(context, 5).ToLocalChecked());
6271
6272 ASSERT_TRUE(layer_owner_node_for_end);
6273 EXPECT_EQ(
6274 GetExpectedLayerForSelection(layer_owner_node_for_end)->CcLayer().id(),
6275 selection.end.layer_id);
6276
6277 EXPECT_EQ(end_edge_start_in_layer_x, selection.end.edge_start.x());
6278 EXPECT_EQ(end_edge_start_in_layer_y, selection.end.edge_start.y());
6279 EXPECT_EQ(end_edge_end_in_layer_x, selection.end.edge_end.x());
6280
6281 // Platform differences can introduce small stylistic deviations in
6282 // y-axis positioning, the details of which aren't relevant to
6283 // selection behavior. However, such deviations from the expected value
6284 // should be consistent for the corresponding y coordinates.
6285 int y_bottom_epsilon = 0;
6286 if (expected_result.Length() == 13) {
6287 y_bottom_epsilon = expected_result.Get(context, 12)
6288 .ToLocalChecked()
6289 .As<v8::Int32>()
6290 ->Value();
6291 }
6292
6293 int y_bottom_deviation =
6294 start_edge_end_in_layer_y - selection.start.edge_end.y();
6295 EXPECT_GE(y_bottom_epsilon, std::abs(y_bottom_deviation));
6296 EXPECT_EQ(y_bottom_deviation,
6297 end_edge_end_in_layer_y - selection.end.edge_end.y());
6298
6299 if (expected_result.Length() >= 15) {
6300 bool start_hidden = expected_result.Get(context, 13)
6301 .ToLocalChecked()
6302 .As<v8::Boolean>()
6303 ->Value();
6304 bool end_hidden = expected_result.Get(context, 14)
6305 .ToLocalChecked()
6306 .As<v8::Boolean>()
6307 ->Value();
6308
6309 EXPECT_EQ(start_hidden, selection.start.hidden);
6310 EXPECT_EQ(end_hidden, selection.end.hidden);
6311 }
6312 }
6313
RunTestWithMultipleFiles(const char * test_file,std::initializer_list<const char * > auxiliary_files)6314 void RunTestWithMultipleFiles(
6315 const char* test_file,
6316 std::initializer_list<const char*> auxiliary_files) {
6317 for (const char* auxiliary_file : auxiliary_files) {
6318 RegisterMockedHttpURLLoad(auxiliary_file);
6319 }
6320
6321 RunTest(test_file);
6322 }
6323
GetExpectedLayerForSelection(blink::Node * node) const6324 GraphicsLayer* GetExpectedLayerForSelection(blink::Node* node) const {
6325 CompositedLayerMapping* clm = node->GetLayoutObject()
6326 ->EnclosingLayer()
6327 ->EnclosingLayerForPaintInvalidation()
6328 ->GetCompositedLayerMapping();
6329
6330 // If the Node is a scroller, the selection will be relative to its
6331 // scrolling contents layer.
6332 return clm->ScrollingContentsLayer() ? clm->ScrollingContentsLayer()
6333 : clm->MainGraphicsLayer();
6334 }
6335
6336 frame_test_helpers::TestWebWidgetClient web_widget_client_;
6337 frame_test_helpers::WebViewHelper web_view_helper_;
6338 };
6339
TEST_F(CompositedSelectionBoundsTest,None)6340 TEST_F(CompositedSelectionBoundsTest, None) {
6341 RunTestWithNoSelection("composited_selection_bounds_none.html");
6342 }
TEST_F(CompositedSelectionBoundsTest,NoneReadonlyCaret)6343 TEST_F(CompositedSelectionBoundsTest, NoneReadonlyCaret) {
6344 RunTestWithNoSelection(
6345 "composited_selection_bounds_none_readonly_caret.html");
6346 }
TEST_F(CompositedSelectionBoundsTest,DetachedFrame)6347 TEST_F(CompositedSelectionBoundsTest, DetachedFrame) {
6348 RunTestWithNoSelection("composited_selection_bounds_detached_frame.html");
6349 }
6350
TEST_F(CompositedSelectionBoundsTest,Basic)6351 TEST_F(CompositedSelectionBoundsTest, Basic) {
6352 RunTest("composited_selection_bounds_basic.html");
6353 }
TEST_F(CompositedSelectionBoundsTest,Transformed)6354 TEST_F(CompositedSelectionBoundsTest, Transformed) {
6355 RunTest("composited_selection_bounds_transformed.html");
6356 }
TEST_F(CompositedSelectionBoundsTest,VerticalRightToLeft)6357 TEST_F(CompositedSelectionBoundsTest, VerticalRightToLeft) {
6358 RunTest("composited_selection_bounds_vertical_rl.html");
6359 }
TEST_F(CompositedSelectionBoundsTest,VerticalLeftToRight)6360 TEST_F(CompositedSelectionBoundsTest, VerticalLeftToRight) {
6361 RunTest("composited_selection_bounds_vertical_lr.html");
6362 }
TEST_F(CompositedSelectionBoundsTest,SplitLayer)6363 TEST_F(CompositedSelectionBoundsTest, SplitLayer) {
6364 RunTest("composited_selection_bounds_split_layer.html");
6365 }
TEST_F(CompositedSelectionBoundsTest,Iframe)6366 TEST_F(CompositedSelectionBoundsTest, Iframe) {
6367 RunTestWithMultipleFiles("composited_selection_bounds_iframe.html",
6368 {"composited_selection_bounds_basic.html"});
6369 }
TEST_F(CompositedSelectionBoundsTest,Editable)6370 TEST_F(CompositedSelectionBoundsTest, Editable) {
6371 RunTest("composited_selection_bounds_editable.html");
6372 }
TEST_F(CompositedSelectionBoundsTest,EditableDiv)6373 TEST_F(CompositedSelectionBoundsTest, EditableDiv) {
6374 RunTest("composited_selection_bounds_editable_div.html");
6375 }
6376 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
6377 #if !defined(OS_ANDROID)
TEST_F(CompositedSelectionBoundsTest,Input)6378 TEST_F(CompositedSelectionBoundsTest, Input) {
6379 RunTest("composited_selection_bounds_input.html");
6380 }
TEST_F(CompositedSelectionBoundsTest,InputScrolled)6381 TEST_F(CompositedSelectionBoundsTest, InputScrolled) {
6382 RunTest("composited_selection_bounds_input_scrolled.html");
6383 }
6384 #endif
6385 #endif
6386
6387 class TestWillInsertBodyWebFrameClient
6388 : public frame_test_helpers::TestWebFrameClient {
6389 public:
TestWillInsertBodyWebFrameClient()6390 TestWillInsertBodyWebFrameClient() : did_load_(false) {}
6391 ~TestWillInsertBodyWebFrameClient() override = default;
6392
6393 // frame_test_helpers::TestWebFrameClient:
DidCommitNavigation(const WebHistoryItem &,WebHistoryCommitType,bool)6394 void DidCommitNavigation(const WebHistoryItem&,
6395 WebHistoryCommitType,
6396 bool) override {
6397 did_load_ = true;
6398 }
6399
6400 bool did_load_;
6401 };
6402
TEST_F(WebFrameTest,HTMLDocument)6403 TEST_F(WebFrameTest, HTMLDocument) {
6404 RegisterMockedHttpURLLoad("clipped-body.html");
6405
6406 TestWillInsertBodyWebFrameClient web_frame_client;
6407 frame_test_helpers::WebViewHelper web_view_helper;
6408 web_view_helper.InitializeAndLoad(base_url_ + "clipped-body.html",
6409 &web_frame_client);
6410
6411 EXPECT_TRUE(web_frame_client.did_load_);
6412 }
6413
TEST_F(WebFrameTest,EmptyDocument)6414 TEST_F(WebFrameTest, EmptyDocument) {
6415 RegisterMockedHttpURLLoad("frameserializer/svg/green_rectangle.svg");
6416
6417 TestWillInsertBodyWebFrameClient web_frame_client;
6418 frame_test_helpers::WebViewHelper web_view_helper;
6419 web_view_helper.Initialize(&web_frame_client);
6420
6421 EXPECT_FALSE(web_frame_client.did_load_);
6422 }
6423
TEST_F(WebFrameTest,MoveCaretSelectionTowardsWindowPointWithNoSelection)6424 TEST_F(WebFrameTest, MoveCaretSelectionTowardsWindowPointWithNoSelection) {
6425 frame_test_helpers::WebViewHelper web_view_helper;
6426 web_view_helper.InitializeAndLoad("about:blank");
6427 WebFrame* frame = web_view_helper.GetWebView()->MainFrame();
6428
6429 // This test passes if this doesn't crash.
6430 frame->ToWebLocalFrame()->MoveCaretSelection(gfx::Point());
6431 }
6432
6433 class TextCheckClient : public WebTextCheckClient {
6434 public:
TextCheckClient()6435 TextCheckClient() : number_of_times_checked_(0) {}
6436 ~TextCheckClient() override = default;
6437
6438 // WebTextCheckClient:
IsSpellCheckingEnabled() const6439 bool IsSpellCheckingEnabled() const override { return true; }
RequestCheckingOfText(const WebString &,std::unique_ptr<WebTextCheckingCompletion> completion)6440 void RequestCheckingOfText(
6441 const WebString&,
6442 std::unique_ptr<WebTextCheckingCompletion> completion) override {
6443 ++number_of_times_checked_;
6444 Vector<WebTextCheckingResult> results;
6445 const int kMisspellingStartOffset = 1;
6446 const int kMisspellingLength = 8;
6447 results.push_back(WebTextCheckingResult(
6448 kWebTextDecorationTypeSpelling, kMisspellingStartOffset,
6449 kMisspellingLength, WebVector<WebString>()));
6450 completion->DidFinishCheckingText(results);
6451 }
6452
NumberOfTimesChecked() const6453 int NumberOfTimesChecked() const { return number_of_times_checked_; }
6454
6455 private:
6456 int number_of_times_checked_;
6457 };
6458
TEST_F(WebFrameTest,ReplaceMisspelledRange)6459 TEST_F(WebFrameTest, ReplaceMisspelledRange) {
6460 RegisterMockedHttpURLLoad("spell.html");
6461 frame_test_helpers::WebViewHelper web_view_helper;
6462 InitializeTextSelectionWebView(base_url_ + "spell.html", &web_view_helper);
6463
6464 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
6465 TextCheckClient textcheck;
6466 frame->SetTextCheckClient(&textcheck);
6467
6468 Document* document = frame->GetFrame()->GetDocument();
6469 Element* element = document->getElementById("data");
6470
6471 web_view_helper.GetWebView()->GetSettings()->SetEditingBehavior(
6472 mojom::EditingBehavior::kEditingWindowsBehavior);
6473
6474 element->focus();
6475 NonThrowableExceptionState exception_state;
6476 document->execCommand("InsertText", false, "_wellcome_.", exception_state);
6477 EXPECT_FALSE(exception_state.HadException());
6478
6479 document->GetFrame()
6480 ->GetSpellChecker()
6481 .GetIdleSpellCheckController()
6482 .ForceInvocationForTesting();
6483
6484 const int kAllTextBeginOffset = 0;
6485 const int kAllTextLength = 11;
6486 frame->SelectRange(WebRange(kAllTextBeginOffset, kAllTextLength),
6487 WebLocalFrame::kHideSelectionHandle,
6488 SelectionMenuBehavior::kHide);
6489 EphemeralRange selection_range = frame->GetFrame()
6490 ->Selection()
6491 .ComputeVisibleSelectionInDOMTree()
6492 .ToNormalizedEphemeralRange();
6493
6494 EXPECT_EQ(1, textcheck.NumberOfTimesChecked());
6495 EXPECT_EQ(1, NumMarkersInRange(document, selection_range,
6496 DocumentMarker::MarkerTypes::Spelling()));
6497
6498 frame->ReplaceMisspelledRange("welcome");
6499 EXPECT_EQ("_welcome_.", WebFrameContentDumper::DumpWebViewAsText(
6500 web_view_helper.GetWebView(),
6501 std::numeric_limits<size_t>::max())
6502 .Utf8());
6503 }
6504
TEST_F(WebFrameTest,RemoveSpellingMarkers)6505 TEST_F(WebFrameTest, RemoveSpellingMarkers) {
6506 RegisterMockedHttpURLLoad("spell.html");
6507 frame_test_helpers::WebViewHelper web_view_helper;
6508 InitializeTextSelectionWebView(base_url_ + "spell.html", &web_view_helper);
6509
6510 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
6511 TextCheckClient textcheck;
6512 frame->SetTextCheckClient(&textcheck);
6513
6514 Document* document = frame->GetFrame()->GetDocument();
6515 Element* element = document->getElementById("data");
6516
6517 web_view_helper.GetWebView()->GetSettings()->SetEditingBehavior(
6518 mojom::EditingBehavior::kEditingWindowsBehavior);
6519
6520 element->focus();
6521 NonThrowableExceptionState exception_state;
6522 document->execCommand("InsertText", false, "_wellcome_.", exception_state);
6523 EXPECT_FALSE(exception_state.HadException());
6524
6525 document->GetFrame()
6526 ->GetSpellChecker()
6527 .GetIdleSpellCheckController()
6528 .ForceInvocationForTesting();
6529
6530 frame->RemoveSpellingMarkers();
6531
6532 const int kAllTextBeginOffset = 0;
6533 const int kAllTextLength = 11;
6534 frame->SelectRange(WebRange(kAllTextBeginOffset, kAllTextLength),
6535 WebLocalFrame::kHideSelectionHandle,
6536 SelectionMenuBehavior::kHide);
6537 EphemeralRange selection_range = frame->GetFrame()
6538 ->Selection()
6539 .ComputeVisibleSelectionInDOMTree()
6540 .ToNormalizedEphemeralRange();
6541
6542 EXPECT_EQ(0, NumMarkersInRange(document, selection_range,
6543 DocumentMarker::MarkerTypes::Spelling()));
6544 }
6545
GetSpellingMarkerOffsets(WebVector<unsigned> * offsets,const Document & document)6546 static void GetSpellingMarkerOffsets(WebVector<unsigned>* offsets,
6547 const Document& document) {
6548 Vector<unsigned> result;
6549 const DocumentMarkerVector& document_markers = document.Markers().Markers();
6550 for (size_t i = 0; i < document_markers.size(); ++i)
6551 result.push_back(document_markers[i]->StartOffset());
6552 offsets->Assign(result);
6553 }
6554
TEST_F(WebFrameTest,RemoveSpellingMarkersUnderWords)6555 TEST_F(WebFrameTest, RemoveSpellingMarkersUnderWords) {
6556 RegisterMockedHttpURLLoad("spell.html");
6557 frame_test_helpers::WebViewHelper web_view_helper;
6558 InitializeTextSelectionWebView(base_url_ + "spell.html", &web_view_helper);
6559
6560 WebLocalFrameImpl* web_frame = web_view_helper.LocalMainFrame();
6561 TextCheckClient textcheck;
6562 web_frame->SetTextCheckClient(&textcheck);
6563
6564 LocalFrame* frame = web_frame->GetFrame();
6565 Document* document = frame->GetDocument();
6566 Element* element = document->getElementById("data");
6567
6568 web_view_helper.GetWebView()->GetSettings()->SetEditingBehavior(
6569 mojom::EditingBehavior::kEditingWindowsBehavior);
6570
6571 element->focus();
6572 NonThrowableExceptionState exception_state;
6573 document->execCommand("InsertText", false, " wellcome ", exception_state);
6574 EXPECT_FALSE(exception_state.HadException());
6575
6576 frame->GetSpellChecker()
6577 .GetIdleSpellCheckController()
6578 .ForceInvocationForTesting();
6579
6580 WebVector<unsigned> offsets1;
6581 GetSpellingMarkerOffsets(&offsets1, *frame->GetDocument());
6582 EXPECT_EQ(1U, offsets1.size());
6583
6584 Vector<String> words;
6585 words.push_back("wellcome");
6586 frame->RemoveSpellingMarkersUnderWords(words);
6587
6588 WebVector<unsigned> offsets2;
6589 GetSpellingMarkerOffsets(&offsets2, *frame->GetDocument());
6590 EXPECT_EQ(0U, offsets2.size());
6591 }
6592
6593 class StubbornTextCheckClient : public WebTextCheckClient {
6594 public:
StubbornTextCheckClient()6595 StubbornTextCheckClient() : completion_(nullptr) {}
6596 ~StubbornTextCheckClient() override = default;
6597
6598 // WebTextCheckClient:
IsSpellCheckingEnabled() const6599 bool IsSpellCheckingEnabled() const override { return true; }
RequestCheckingOfText(const WebString &,std::unique_ptr<WebTextCheckingCompletion> completion)6600 void RequestCheckingOfText(
6601 const WebString&,
6602 std::unique_ptr<WebTextCheckingCompletion> completion) override {
6603 completion_ = std::move(completion);
6604 }
6605
KickNoResults()6606 void KickNoResults() { Kick(-1, -1, kWebTextDecorationTypeSpelling); }
6607
Kick()6608 void Kick() { Kick(1, 8, kWebTextDecorationTypeSpelling); }
6609
KickGrammar()6610 void KickGrammar() { Kick(1, 8, kWebTextDecorationTypeGrammar); }
6611
6612 private:
Kick(int misspelling_start_offset,int misspelling_length,WebTextDecorationType type)6613 void Kick(int misspelling_start_offset,
6614 int misspelling_length,
6615 WebTextDecorationType type) {
6616 if (!completion_)
6617 return;
6618 Vector<WebTextCheckingResult> results;
6619 if (misspelling_start_offset >= 0 && misspelling_length > 0) {
6620 results.push_back(WebTextCheckingResult(type, misspelling_start_offset,
6621 misspelling_length));
6622 }
6623 completion_->DidFinishCheckingText(results);
6624 completion_.reset();
6625 }
6626
6627 std::unique_ptr<WebTextCheckingCompletion> completion_;
6628 };
6629
TEST_F(WebFrameTest,SlowSpellcheckMarkerPosition)6630 TEST_F(WebFrameTest, SlowSpellcheckMarkerPosition) {
6631 RegisterMockedHttpURLLoad("spell.html");
6632 frame_test_helpers::WebViewHelper web_view_helper;
6633 InitializeTextSelectionWebView(base_url_ + "spell.html", &web_view_helper);
6634
6635 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
6636 StubbornTextCheckClient textcheck;
6637 frame->SetTextCheckClient(&textcheck);
6638
6639 Document* document = frame->GetFrame()->GetDocument();
6640 Element* element = document->getElementById("data");
6641
6642 web_view_helper.GetWebView()->GetSettings()->SetEditingBehavior(
6643 mojom::EditingBehavior::kEditingWindowsBehavior);
6644
6645 element->focus();
6646 NonThrowableExceptionState exception_state;
6647 document->execCommand("InsertText", false, "wellcome ", exception_state);
6648 EXPECT_FALSE(exception_state.HadException());
6649 document->execCommand("InsertText", false, "he", exception_state);
6650 EXPECT_FALSE(exception_state.HadException());
6651
6652 document->GetFrame()
6653 ->GetSpellChecker()
6654 .GetIdleSpellCheckController()
6655 .ForceInvocationForTesting();
6656
6657 textcheck.Kick();
6658
6659 WebVector<unsigned> offsets;
6660 GetSpellingMarkerOffsets(&offsets, *frame->GetFrame()->GetDocument());
6661 EXPECT_EQ(0U, offsets.size());
6662 }
6663
TEST_F(WebFrameTest,SpellcheckResultErasesMarkers)6664 TEST_F(WebFrameTest, SpellcheckResultErasesMarkers) {
6665 RegisterMockedHttpURLLoad("spell.html");
6666 frame_test_helpers::WebViewHelper web_view_helper;
6667 InitializeTextSelectionWebView(base_url_ + "spell.html", &web_view_helper);
6668
6669 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
6670 StubbornTextCheckClient textcheck;
6671 frame->SetTextCheckClient(&textcheck);
6672
6673 Document* document = frame->GetFrame()->GetDocument();
6674 Element* element = document->getElementById("data");
6675
6676 web_view_helper.GetWebView()->GetSettings()->SetEditingBehavior(
6677 mojom::EditingBehavior::kEditingWindowsBehavior);
6678
6679 element->focus();
6680 NonThrowableExceptionState exception_state;
6681 document->execCommand("InsertText", false, "welcome ", exception_state);
6682
6683 document->GetFrame()
6684 ->GetSpellChecker()
6685 .GetIdleSpellCheckController()
6686 .ForceInvocationForTesting();
6687
6688 document->UpdateStyleAndLayout(DocumentUpdateReason::kTest);
6689
6690 EXPECT_FALSE(exception_state.HadException());
6691 auto range = EphemeralRange::RangeOfContents(*element);
6692 document->Markers().AddSpellingMarker(range);
6693 document->Markers().AddGrammarMarker(range);
6694 EXPECT_EQ(2U, document->Markers().Markers().size());
6695
6696 textcheck.KickNoResults();
6697 EXPECT_EQ(0U, document->Markers().Markers().size());
6698 }
6699
TEST_F(WebFrameTest,SpellcheckResultsSavedInDocument)6700 TEST_F(WebFrameTest, SpellcheckResultsSavedInDocument) {
6701 RegisterMockedHttpURLLoad("spell.html");
6702 frame_test_helpers::WebViewHelper web_view_helper;
6703 InitializeTextSelectionWebView(base_url_ + "spell.html", &web_view_helper);
6704
6705 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
6706 StubbornTextCheckClient textcheck;
6707 frame->SetTextCheckClient(&textcheck);
6708
6709 Document* document = frame->GetFrame()->GetDocument();
6710 Element* element = document->getElementById("data");
6711
6712 web_view_helper.GetWebView()->GetSettings()->SetEditingBehavior(
6713 mojom::EditingBehavior::kEditingWindowsBehavior);
6714
6715 element->focus();
6716 NonThrowableExceptionState exception_state;
6717 document->execCommand("InsertText", false, "wellcome ", exception_state);
6718 EXPECT_FALSE(exception_state.HadException());
6719
6720 document->GetFrame()
6721 ->GetSpellChecker()
6722 .GetIdleSpellCheckController()
6723 .ForceInvocationForTesting();
6724
6725 textcheck.Kick();
6726 ASSERT_EQ(1U, document->Markers().Markers().size());
6727 ASSERT_NE(static_cast<DocumentMarker*>(nullptr),
6728 document->Markers().Markers()[0]);
6729 EXPECT_EQ(DocumentMarker::kSpelling,
6730 document->Markers().Markers()[0]->GetType());
6731
6732 document->execCommand("InsertText", false, "wellcome ", exception_state);
6733 EXPECT_FALSE(exception_state.HadException());
6734
6735 document->GetFrame()
6736 ->GetSpellChecker()
6737 .GetIdleSpellCheckController()
6738 .ForceInvocationForTesting();
6739
6740 textcheck.KickGrammar();
6741 ASSERT_EQ(1U, document->Markers().Markers().size());
6742 ASSERT_NE(static_cast<DocumentMarker*>(nullptr),
6743 document->Markers().Markers()[0]);
6744 EXPECT_EQ(DocumentMarker::kGrammar,
6745 document->Markers().Markers()[0]->GetType());
6746 }
6747
6748 class TestAccessInitialDocumentLocalFrameHost : public FakeLocalFrameHost {
6749 public:
6750 TestAccessInitialDocumentLocalFrameHost() = default;
6751 ~TestAccessInitialDocumentLocalFrameHost() override = default;
6752
6753 // FakeLocalFrameHost:
DidAccessInitialDocument()6754 void DidAccessInitialDocument() override { ++did_access_initial_document_; }
6755
6756 // !!!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!
6757 // If the actual counts in the tests below increase, this could be an
6758 // indicator of a bug that causes DidAccessInitialDocument() to always be
6759 // invoked, regardless of whether or not the initial document is accessed.
6760 // Please do not simply increment the expected counts in the below tests
6761 // without understanding what's causing the increased count.
6762 int did_access_initial_document_ = 0;
6763 };
6764
TEST_F(WebFrameTest,DidAccessInitialDocumentBody)6765 TEST_F(WebFrameTest, DidAccessInitialDocumentBody) {
6766 TestAccessInitialDocumentLocalFrameHost frame_host;
6767 frame_test_helpers::TestWebFrameClient web_frame_client;
6768 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
6769 frame_test_helpers::WebViewHelper web_view_helper;
6770 web_view_helper.Initialize(&web_frame_client);
6771 RunPendingTasks();
6772 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6773
6774 // Create another window that will try to access it.
6775 frame_test_helpers::WebViewHelper new_web_view_helper;
6776 WebViewImpl* new_view = new_web_view_helper.InitializeWithOpener(
6777 web_view_helper.GetWebView()->MainFrame());
6778 RunPendingTasks();
6779 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6780
6781 // Access the initial document by modifying the body.
6782 new_view->MainFrameImpl()->ExecuteScript(
6783 WebScriptSource("window.opener.document.body.innerHTML += 'Modified';"));
6784 RunPendingTasks();
6785 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6786
6787 web_view_helper.Reset();
6788 }
6789
TEST_F(WebFrameTest,DidAccessInitialDocumentOpen)6790 TEST_F(WebFrameTest, DidAccessInitialDocumentOpen) {
6791 TestAccessInitialDocumentLocalFrameHost frame_host;
6792 frame_test_helpers::TestWebFrameClient web_frame_client;
6793 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
6794 frame_test_helpers::WebViewHelper web_view_helper;
6795 web_view_helper.Initialize(&web_frame_client);
6796 RunPendingTasks();
6797 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6798
6799 // Create another window that will try to access it.
6800 frame_test_helpers::WebViewHelper new_web_view_helper;
6801 WebViewImpl* new_view = new_web_view_helper.InitializeWithOpener(
6802 web_view_helper.GetWebView()->MainFrame());
6803 RunPendingTasks();
6804 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6805
6806 // Access the initial document by calling document.open(), which allows
6807 // arbitrary modification of the initial document.
6808 new_view->MainFrameImpl()->ExecuteScript(
6809 WebScriptSource("window.opener.document.open();"));
6810 RunPendingTasks();
6811 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6812
6813 web_view_helper.Reset();
6814 }
6815
TEST_F(WebFrameTest,DidAccessInitialDocumentNavigator)6816 TEST_F(WebFrameTest, DidAccessInitialDocumentNavigator) {
6817 TestAccessInitialDocumentLocalFrameHost frame_host;
6818 frame_test_helpers::TestWebFrameClient web_frame_client;
6819 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
6820 frame_test_helpers::WebViewHelper web_view_helper;
6821 web_view_helper.Initialize(&web_frame_client);
6822 RunPendingTasks();
6823 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6824
6825 // Create another window that will try to access it.
6826 frame_test_helpers::WebViewHelper new_web_view_helper;
6827 WebViewImpl* new_view = new_web_view_helper.InitializeWithOpener(
6828 web_view_helper.GetWebView()->MainFrame());
6829 RunPendingTasks();
6830 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6831
6832 // Access the initial document to get to the navigator object.
6833 new_view->MainFrameImpl()->ExecuteScript(
6834 WebScriptSource("console.log(window.opener.navigator);"));
6835 RunPendingTasks();
6836 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6837
6838 web_view_helper.Reset();
6839 }
6840
TEST_F(WebFrameTest,DidAccessInitialDocumentViaJavascriptUrl)6841 TEST_F(WebFrameTest, DidAccessInitialDocumentViaJavascriptUrl) {
6842 TestAccessInitialDocumentLocalFrameHost frame_host;
6843 frame_test_helpers::TestWebFrameClient web_frame_client;
6844 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
6845 frame_test_helpers::WebViewHelper web_view_helper;
6846 web_view_helper.Initialize(&web_frame_client);
6847 RunPendingTasks();
6848 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6849
6850 // Access the initial document from a javascript: URL.
6851 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
6852 "javascript:document.body.appendChild(document."
6853 "createTextNode('Modified'))");
6854 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6855
6856 web_view_helper.Reset();
6857 }
6858
TEST_F(WebFrameTest,DidAccessInitialDocumentBodyBeforeModalDialog)6859 TEST_F(WebFrameTest, DidAccessInitialDocumentBodyBeforeModalDialog) {
6860 TestAccessInitialDocumentLocalFrameHost frame_host;
6861 frame_test_helpers::TestWebFrameClient web_frame_client;
6862 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
6863 frame_test_helpers::WebViewHelper web_view_helper;
6864 web_view_helper.Initialize(&web_frame_client);
6865 RunPendingTasks();
6866 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6867
6868 // Create another window that will try to access it.
6869 frame_test_helpers::WebViewHelper new_web_view_helper;
6870 WebViewImpl* new_view = new_web_view_helper.InitializeWithOpener(
6871 web_view_helper.GetWebView()->MainFrame());
6872 RunPendingTasks();
6873 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6874
6875 // Access the initial document by modifying the body.
6876 new_view->MainFrameImpl()->ExecuteScript(
6877 WebScriptSource("window.opener.document.body.innerHTML += 'Modified';"));
6878 RunPendingTasks();
6879 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6880
6881 // Run a modal dialog, which used to run a nested run loop and require
6882 // a special case for notifying about the access.
6883 new_view->MainFrameImpl()->ExecuteScript(
6884 WebScriptSource("window.opener.confirm('Modal');"));
6885 RunPendingTasks();
6886 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6887
6888 // Ensure that we don't notify again later.
6889 RunPendingTasks();
6890 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6891
6892 web_view_helper.Reset();
6893 }
6894
TEST_F(WebFrameTest,DidWriteToInitialDocumentBeforeModalDialog)6895 TEST_F(WebFrameTest, DidWriteToInitialDocumentBeforeModalDialog) {
6896 TestAccessInitialDocumentLocalFrameHost frame_host;
6897 frame_test_helpers::TestWebFrameClient web_frame_client;
6898 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
6899 frame_test_helpers::WebViewHelper web_view_helper;
6900 web_view_helper.Initialize(&web_frame_client);
6901 RunPendingTasks();
6902 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6903
6904 // Create another window that will try to access it.
6905 frame_test_helpers::WebViewHelper new_web_view_helper;
6906 WebViewImpl* new_view = new_web_view_helper.InitializeWithOpener(
6907 web_view_helper.GetWebView()->MainFrame());
6908 RunPendingTasks();
6909 EXPECT_EQ(0, frame_host.did_access_initial_document_);
6910
6911 // Access the initial document with document.write, which moves us past the
6912 // initial empty document state of the state machine.
6913 new_view->MainFrameImpl()->ExecuteScript(
6914 WebScriptSource("window.opener.document.write('Modified'); "
6915 "window.opener.document.close();"));
6916 RunPendingTasks();
6917 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6918
6919 // Run a modal dialog, which used to run a nested run loop and require
6920 // a special case for notifying about the access.
6921 new_view->MainFrameImpl()->ExecuteScript(
6922 WebScriptSource("window.opener.confirm('Modal');"));
6923 RunPendingTasks();
6924 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6925
6926 // Ensure that we don't notify again later.
6927 RunPendingTasks();
6928 EXPECT_EQ(1, frame_host.did_access_initial_document_);
6929
6930 web_view_helper.Reset();
6931 }
6932
6933 class TestScrolledFrameClient : public frame_test_helpers::TestWebFrameClient {
6934 public:
TestScrolledFrameClient()6935 TestScrolledFrameClient() { Reset(); }
6936 ~TestScrolledFrameClient() override = default;
6937
Reset()6938 void Reset() { did_scroll_frame_ = false; }
WasFrameScrolled() const6939 bool WasFrameScrolled() const { return did_scroll_frame_; }
6940
6941 // WebLocalFrameClient:
DidChangeScrollOffset()6942 void DidChangeScrollOffset() override {
6943 if (Frame()->Parent())
6944 return;
6945 EXPECT_FALSE(did_scroll_frame_);
6946 LocalFrameView* view = To<WebLocalFrameImpl>(Frame())->GetFrameView();
6947 // LocalFrameView can be scrolled in
6948 // LocalFrameView::SetFixedVisibleContentRect which is called from
6949 // LocalFrame::CreateView (before the frame is associated with the the
6950 // view).
6951 if (view)
6952 did_scroll_frame_ = true;
6953 }
6954
6955 private:
6956 bool did_scroll_frame_;
6957 };
6958
TEST_F(WebFrameTest,CompositorScrollIsUserScrollLongPage)6959 TEST_F(WebFrameTest, CompositorScrollIsUserScrollLongPage) {
6960 RegisterMockedHttpURLLoad("long_scroll.html");
6961 TestScrolledFrameClient client;
6962
6963 // Make sure we initialize to minimum scale, even if the window size
6964 // only becomes available after the load begins.
6965 frame_test_helpers::WebViewHelper web_view_helper;
6966 web_view_helper.InitializeAndLoad(base_url_ + "long_scroll.html", &client);
6967 web_view_helper.Resize(gfx::Size(1000, 1000));
6968
6969 WebLocalFrameImpl* frame_impl = web_view_helper.LocalMainFrame();
6970 DocumentLoader::InitialScrollState& initial_scroll_state =
6971 frame_impl->GetFrame()
6972 ->Loader()
6973 .GetDocumentLoader()
6974 ->GetInitialScrollState();
6975
6976 EXPECT_FALSE(client.WasFrameScrolled());
6977 EXPECT_FALSE(initial_scroll_state.was_scrolled_by_user);
6978
6979 auto* scrollable_area = frame_impl->GetFrameView()->LayoutViewport();
6980
6981 // Do a compositor scroll, verify that this is counted as a user scroll.
6982 scrollable_area->DidScroll(FloatPoint(0, 1));
6983 web_view_helper.GetWebView()
6984 ->MainFrameWidget()
6985 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
6986 1.7f, false, 0, 0,
6987 cc::BrowserControlsState::kBoth});
6988 EXPECT_TRUE(client.WasFrameScrolled());
6989 EXPECT_TRUE(initial_scroll_state.was_scrolled_by_user);
6990
6991 client.Reset();
6992 initial_scroll_state.was_scrolled_by_user = false;
6993
6994 // The page scale 1.0f and scroll.
6995 scrollable_area->DidScroll(FloatPoint(0, 2));
6996 web_view_helper.GetWebView()
6997 ->MainFrameWidget()
6998 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
6999 1.0f, false, 0, 0,
7000 cc::BrowserControlsState::kBoth});
7001 EXPECT_TRUE(client.WasFrameScrolled());
7002 EXPECT_TRUE(initial_scroll_state.was_scrolled_by_user);
7003 client.Reset();
7004 initial_scroll_state.was_scrolled_by_user = false;
7005
7006 // No scroll event if there is no scroll delta.
7007 scrollable_area->DidScroll(FloatPoint(0, 2));
7008 web_view_helper.GetWebView()
7009 ->MainFrameWidget()
7010 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
7011 1.0f, false, 0, 0,
7012 cc::BrowserControlsState::kBoth});
7013 EXPECT_FALSE(client.WasFrameScrolled());
7014 EXPECT_FALSE(initial_scroll_state.was_scrolled_by_user);
7015 client.Reset();
7016
7017 // Non zero page scale and scroll.
7018 scrollable_area->DidScroll(FloatPoint(9, 15));
7019 web_view_helper.GetWebView()
7020 ->MainFrameWidget()
7021 ->ApplyViewportChangesForTesting({gfx::ScrollOffset(), gfx::Vector2dF(),
7022 0.6f, false, 0, 0,
7023 cc::BrowserControlsState::kBoth});
7024 EXPECT_TRUE(client.WasFrameScrolled());
7025 EXPECT_TRUE(initial_scroll_state.was_scrolled_by_user);
7026 client.Reset();
7027 initial_scroll_state.was_scrolled_by_user = false;
7028
7029 // Programmatic scroll.
7030 frame_impl->ExecuteScript(WebScriptSource("window.scrollTo(0, 20);"));
7031 EXPECT_TRUE(client.WasFrameScrolled());
7032 EXPECT_FALSE(initial_scroll_state.was_scrolled_by_user);
7033 client.Reset();
7034
7035 // Programmatic scroll to same offset. No scroll event should be generated.
7036 frame_impl->ExecuteScript(WebScriptSource("window.scrollTo(0, 20);"));
7037 EXPECT_FALSE(client.WasFrameScrolled());
7038 EXPECT_FALSE(initial_scroll_state.was_scrolled_by_user);
7039 client.Reset();
7040 }
7041
TEST_F(WebFrameTest,SiteForCookiesForRedirect)7042 TEST_F(WebFrameTest, SiteForCookiesForRedirect) {
7043 String file_path = test::CoreTestDataPath("first_party.html");
7044
7045 WebURL test_url(ToKURL("http://internal.test/first_party_redirect.html"));
7046 char redirect[] = "http://internal.test/first_party.html";
7047 WebURL redirect_url(ToKURL(redirect));
7048 WebURLResponse redirect_response;
7049 redirect_response.SetMimeType("text/html");
7050 redirect_response.SetHttpStatusCode(302);
7051 redirect_response.SetHttpHeaderField("Location", redirect);
7052 RegisterMockedURLLoadWithCustomResponse(test_url, file_path,
7053 redirect_response);
7054
7055 WebURLResponse final_response;
7056 final_response.SetMimeType("text/html");
7057 RegisterMockedURLLoadWithCustomResponse(redirect_url, file_path,
7058 final_response);
7059
7060 frame_test_helpers::WebViewHelper web_view_helper;
7061 web_view_helper.InitializeAndLoad(base_url_ + "first_party_redirect.html");
7062 EXPECT_TRUE(
7063 web_view_helper.GetWebView()
7064 ->MainFrameImpl()
7065 ->GetDocument()
7066 .SiteForCookies()
7067 .IsEquivalent(net::SiteForCookies::FromUrl(ToKURL(redirect))));
7068 }
7069
7070 class TestNewWindowWebViewClient
7071 : public frame_test_helpers::TestWebViewClient {
7072 public:
7073 TestNewWindowWebViewClient() = default;
7074 ~TestNewWindowWebViewClient() override = default;
7075
7076 // frame_test_helpers::TestWebFrameClient:
CreateView(WebLocalFrame *,const WebURLRequest &,const WebWindowFeatures &,const WebString &,WebNavigationPolicy,network::mojom::blink::WebSandboxFlags,const FeaturePolicyFeatureState &,const SessionStorageNamespaceId &,bool & consumed_user_gesture)7077 WebView* CreateView(WebLocalFrame*,
7078 const WebURLRequest&,
7079 const WebWindowFeatures&,
7080 const WebString&,
7081 WebNavigationPolicy,
7082 network::mojom::blink::WebSandboxFlags,
7083 const FeaturePolicyFeatureState&,
7084 const SessionStorageNamespaceId&,
7085 bool& consumed_user_gesture) override {
7086 EXPECT_TRUE(false);
7087 return nullptr;
7088 }
7089 };
7090
7091 class TestNewWindowWebFrameClient
7092 : public frame_test_helpers::TestWebFrameClient {
7093 public:
TestNewWindowWebFrameClient()7094 TestNewWindowWebFrameClient() : begin_navigation_call_count_(0) {}
7095 ~TestNewWindowWebFrameClient() override = default;
7096
7097 // frame_test_helpers::TestWebFrameClient:
BeginNavigation(std::unique_ptr<WebNavigationInfo> info)7098 void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
7099 if (ignore_navigations_) {
7100 begin_navigation_call_count_++;
7101 return;
7102 }
7103 TestWebFrameClient::BeginNavigation(std::move(info));
7104 }
7105
BeginNavigationCallCount() const7106 int BeginNavigationCallCount() const { return begin_navigation_call_count_; }
IgnoreNavigations()7107 void IgnoreNavigations() { ignore_navigations_ = true; }
7108
7109 private:
7110 bool ignore_navigations_ = false;
7111 int begin_navigation_call_count_;
7112 };
7113
TEST_F(WebFrameTest,ModifiedClickNewWindow)7114 TEST_F(WebFrameTest, ModifiedClickNewWindow) {
7115 // This test checks that ctrl+click does not just open a new window,
7116 // but instead goes to client to decide the navigation policy.
7117 RegisterMockedHttpURLLoad("ctrl_click.html");
7118 RegisterMockedHttpURLLoad("hello_world.html");
7119 TestNewWindowWebViewClient web_view_client;
7120 TestNewWindowWebFrameClient web_frame_client;
7121 frame_test_helpers::WebViewHelper web_view_helper;
7122 web_view_helper.InitializeAndLoad(base_url_ + "ctrl_click.html",
7123 &web_frame_client, &web_view_client);
7124
7125 auto* frame =
7126 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
7127 LocalDOMWindow* window = frame->DomWindow();
7128 KURL destination = ToKURL(base_url_ + "hello_world.html");
7129
7130 // ctrl+click event
7131 MouseEventInit* mouse_initializer = MouseEventInit::Create();
7132 mouse_initializer->setView(window);
7133 mouse_initializer->setButton(1);
7134 mouse_initializer->setCtrlKey(true);
7135
7136 Event* event =
7137 MouseEvent::Create(nullptr, event_type_names::kClick, mouse_initializer);
7138 FrameLoadRequest frame_request(window, ResourceRequest(destination));
7139 frame_request.SetNavigationPolicy(NavigationPolicyFromEvent(event));
7140 frame_request.SetTriggeringEventInfo(TriggeringEventInfo::kFromTrustedEvent);
7141 LocalFrame::NotifyUserActivation(
7142 frame, mojom::UserActivationNotificationType::kTest);
7143 web_frame_client.IgnoreNavigations();
7144 frame->Loader().StartNavigation(frame_request, WebFrameLoadType::kStandard);
7145 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
7146 web_view_helper.LocalMainFrame());
7147
7148 // BeginNavigation should be called for the ctrl+click.
7149 EXPECT_EQ(1, web_frame_client.BeginNavigationCallCount());
7150 }
7151
7152 class TestBeginNavigationCacheModeClient
7153 : public frame_test_helpers::TestWebFrameClient {
7154 public:
TestBeginNavigationCacheModeClient()7155 TestBeginNavigationCacheModeClient()
7156 : cache_mode_(mojom::FetchCacheMode::kDefault) {}
7157 ~TestBeginNavigationCacheModeClient() override = default;
7158
GetCacheMode() const7159 mojom::FetchCacheMode GetCacheMode() const { return cache_mode_; }
7160
BeginNavigation(std::unique_ptr<WebNavigationInfo> info)7161 void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
7162 cache_mode_ = info->url_request.GetCacheMode();
7163 TestWebFrameClient::BeginNavigation(std::move(info));
7164 }
7165
7166 private:
7167 mojom::FetchCacheMode cache_mode_;
7168 };
7169
TEST_F(WebFrameTest,BackToReload)7170 TEST_F(WebFrameTest, BackToReload) {
7171 RegisterMockedHttpURLLoad("fragment_middle_click.html");
7172 TestBeginNavigationCacheModeClient client;
7173 frame_test_helpers::WebViewHelper web_view_helper;
7174 web_view_helper.InitializeAndLoad(base_url_ + "fragment_middle_click.html",
7175 &client);
7176 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
7177 const FrameLoader& main_frame_loader =
7178 web_view_helper.LocalMainFrame()->GetFrame()->Loader();
7179 Persistent<HistoryItem> first_item =
7180 main_frame_loader.GetDocumentLoader()->GetHistoryItem();
7181 EXPECT_TRUE(first_item);
7182
7183 RegisterMockedHttpURLLoad("white-1x1.png");
7184 frame_test_helpers::LoadFrame(frame, base_url_ + "white-1x1.png");
7185 EXPECT_NE(first_item.Get(),
7186 main_frame_loader.GetDocumentLoader()->GetHistoryItem());
7187
7188 frame_test_helpers::LoadHistoryItem(frame, WebHistoryItem(first_item.Get()),
7189 mojom::FetchCacheMode::kDefault);
7190 EXPECT_EQ(first_item.Get(),
7191 main_frame_loader.GetDocumentLoader()->GetHistoryItem());
7192
7193 frame_test_helpers::ReloadFrame(frame);
7194 EXPECT_EQ(mojom::FetchCacheMode::kValidateCache, client.GetCacheMode());
7195 }
7196
TEST_F(WebFrameTest,ReloadPost)7197 TEST_F(WebFrameTest, ReloadPost) {
7198 RegisterMockedHttpURLLoad("reload_post.html");
7199 TestBeginNavigationCacheModeClient client;
7200 frame_test_helpers::WebViewHelper web_view_helper;
7201 web_view_helper.InitializeAndLoad(base_url_ + "reload_post.html", &client);
7202 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
7203
7204 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
7205 "javascript:document.forms[0].submit()");
7206 // Pump requests one more time after the javascript URL has executed to
7207 // trigger the actual POST load request.
7208 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
7209 web_view_helper.LocalMainFrame());
7210 EXPECT_EQ(WebString::FromUTF8("POST"),
7211 frame->GetDocumentLoader()->HttpMethod());
7212
7213 frame_test_helpers::ReloadFrame(frame);
7214 EXPECT_EQ(mojom::FetchCacheMode::kValidateCache, client.GetCacheMode());
7215 EXPECT_EQ(kWebNavigationTypeFormResubmitted,
7216 frame->GetDocumentLoader()->GetNavigationType());
7217 }
7218
7219 class TestCachePolicyWebFrameClient
7220 : public frame_test_helpers::TestWebFrameClient {
7221 public:
TestCachePolicyWebFrameClient()7222 TestCachePolicyWebFrameClient()
7223 : cache_mode_(mojom::FetchCacheMode::kDefault),
7224 begin_navigation_call_count_(0) {}
7225 ~TestCachePolicyWebFrameClient() override = default;
7226
GetCacheMode() const7227 mojom::FetchCacheMode GetCacheMode() const { return cache_mode_; }
BeginNavigationCallCount() const7228 int BeginNavigationCallCount() const { return begin_navigation_call_count_; }
ChildClient(size_t i)7229 TestCachePolicyWebFrameClient& ChildClient(size_t i) {
7230 return *child_clients_[i].get();
7231 }
ChildFrameCreationCount() const7232 size_t ChildFrameCreationCount() const { return child_clients_.size(); }
7233
7234 // frame_test_helpers::TestWebFrameClient:
CreateChildFrame(WebLocalFrame * parent,mojom::blink::TreeScopeType scope,const WebString &,const WebString &,const FramePolicy &,const WebFrameOwnerProperties & frame_owner_properties,mojom::blink::FrameOwnerElementType)7235 WebLocalFrame* CreateChildFrame(
7236 WebLocalFrame* parent,
7237 mojom::blink::TreeScopeType scope,
7238 const WebString&,
7239 const WebString&,
7240 const FramePolicy&,
7241 const WebFrameOwnerProperties& frame_owner_properties,
7242 mojom::blink::FrameOwnerElementType) override {
7243 auto child = std::make_unique<TestCachePolicyWebFrameClient>();
7244 auto* child_ptr = child.get();
7245 child_clients_.push_back(std::move(child));
7246 return CreateLocalChild(*parent, scope, child_ptr);
7247 }
BeginNavigation(std::unique_ptr<WebNavigationInfo> info)7248 void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
7249 cache_mode_ = info->url_request.GetCacheMode();
7250 begin_navigation_call_count_++;
7251 TestWebFrameClient::BeginNavigation(std::move(info));
7252 }
7253
7254 private:
7255 mojom::FetchCacheMode cache_mode_;
7256 Vector<std::unique_ptr<TestCachePolicyWebFrameClient>> child_clients_;
7257 int begin_navigation_call_count_;
7258 };
7259
TEST_F(WebFrameTest,ReloadIframe)7260 TEST_F(WebFrameTest, ReloadIframe) {
7261 RegisterMockedHttpURLLoad("iframe_reload.html");
7262 RegisterMockedHttpURLLoad("visible_iframe.html");
7263
7264 TestCachePolicyWebFrameClient main_frame_client;
7265 frame_test_helpers::WebViewHelper web_view_helper;
7266 web_view_helper.InitializeAndLoad(base_url_ + "iframe_reload.html",
7267 &main_frame_client);
7268 WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
7269
7270 ASSERT_EQ(1U, main_frame_client.ChildFrameCreationCount());
7271 TestCachePolicyWebFrameClient* child_client =
7272 &main_frame_client.ChildClient(0);
7273 auto* child_frame = To<WebLocalFrameImpl>(main_frame->FirstChild());
7274 EXPECT_EQ(child_client, child_frame->Client());
7275 EXPECT_EQ(1u, main_frame->GetFrame()->Tree().ScopedChildCount());
7276 EXPECT_EQ(1, child_client->BeginNavigationCallCount());
7277 EXPECT_EQ(mojom::FetchCacheMode::kDefault, child_client->GetCacheMode());
7278
7279 frame_test_helpers::ReloadFrame(main_frame);
7280
7281 // A new child WebLocalFrame should have been created with a new client.
7282 ASSERT_EQ(2U, main_frame_client.ChildFrameCreationCount());
7283 TestCachePolicyWebFrameClient* new_child_client =
7284 &main_frame_client.ChildClient(1);
7285 auto* new_child_frame = To<WebLocalFrameImpl>(main_frame->FirstChild());
7286 EXPECT_EQ(new_child_client, new_child_frame->Client());
7287 ASSERT_NE(child_client, new_child_client);
7288 ASSERT_NE(child_frame, new_child_frame);
7289 // But there should still only be one subframe.
7290 EXPECT_EQ(1u, main_frame->GetFrame()->Tree().ScopedChildCount());
7291
7292 EXPECT_EQ(1, new_child_client->BeginNavigationCallCount());
7293 // Sub-frames should not be forcibly revalidated.
7294 // TODO(toyoshim): Will consider to revalidate main resources in sub-frames
7295 // on reloads. Or will do only for bypassingCache.
7296 EXPECT_EQ(mojom::FetchCacheMode::kDefault, new_child_client->GetCacheMode());
7297 }
7298
7299 class TestSameDocumentWebFrameClient
7300 : public frame_test_helpers::TestWebFrameClient {
7301 public:
TestSameDocumentWebFrameClient()7302 TestSameDocumentWebFrameClient() : frame_load_type_reload_seen_(false) {}
7303 ~TestSameDocumentWebFrameClient() override = default;
7304
7305 // frame_test_helpers::TestWebFrameClient:
BeginNavigation(std::unique_ptr<WebNavigationInfo> info)7306 void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
7307 if (info->frame_load_type == WebFrameLoadType::kReload)
7308 frame_load_type_reload_seen_ = true;
7309 TestWebFrameClient::BeginNavigation(std::move(info));
7310 }
7311
FrameLoadTypeReloadSeen() const7312 bool FrameLoadTypeReloadSeen() const { return frame_load_type_reload_seen_; }
7313
7314 private:
7315 bool frame_load_type_reload_seen_;
7316 };
7317
TEST_F(WebFrameTest,NavigateToSame)7318 TEST_F(WebFrameTest, NavigateToSame) {
7319 RegisterMockedHttpURLLoad("navigate_to_same.html");
7320 TestSameDocumentWebFrameClient client;
7321 frame_test_helpers::WebViewHelper web_view_helper;
7322 web_view_helper.InitializeAndLoad(base_url_ + "navigate_to_same.html",
7323 &client);
7324 EXPECT_FALSE(client.FrameLoadTypeReloadSeen());
7325
7326 auto* local_frame =
7327 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
7328 FrameLoadRequest frame_request(
7329 nullptr, ResourceRequest(local_frame->GetDocument()->Url()));
7330 local_frame->Loader().StartNavigation(frame_request);
7331 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
7332 web_view_helper.LocalMainFrame());
7333
7334 EXPECT_TRUE(client.FrameLoadTypeReloadSeen());
7335 }
7336
7337 class TestMainFrameIntersectionChanged
7338 : public frame_test_helpers::TestWebFrameClient {
7339 public:
7340 TestMainFrameIntersectionChanged() = default;
7341 ~TestMainFrameIntersectionChanged() override = default;
7342
7343 // frame_test_helpers::TestWebFrameClient:
OnMainFrameIntersectionChanged(const WebRect & intersection_rect)7344 void OnMainFrameIntersectionChanged(
7345 const WebRect& intersection_rect) override {
7346 main_frame_intersection_ = intersection_rect;
7347 }
7348
MainFrameIntersection() const7349 WebRect MainFrameIntersection() const { return main_frame_intersection_; }
7350
7351 private:
7352 WebRect main_frame_intersection_;
7353 };
7354
TEST_F(WebFrameTest,MainFrameIntersectionChanged)7355 TEST_F(WebFrameTest, MainFrameIntersectionChanged) {
7356 TestMainFrameIntersectionChanged client;
7357 frame_test_helpers::WebViewHelper helper;
7358 helper.InitializeRemote();
7359
7360 WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
7361 *helper.RemoteMainFrame(), "frameName", WebFrameOwnerProperties(),
7362 nullptr, &client);
7363
7364 WebFrameWidget* widget = local_frame->FrameWidget();
7365 ASSERT_TRUE(widget);
7366
7367 gfx::Rect viewport_intersection(0, 11, 200, 89);
7368 gfx::Rect mainframe_intersection(0, 0, 200, 140);
7369 blink::mojom::FrameOcclusionState occlusion_state =
7370 blink::mojom::FrameOcclusionState::kUnknown;
7371 gfx::Transform transform;
7372 transform.Translate(100, 100);
7373
7374 auto intersection_state = blink::mojom::blink::ViewportIntersectionState(
7375 viewport_intersection, mainframe_intersection, gfx::Rect(),
7376 occlusion_state, gfx::Size(), gfx::Point(), transform);
7377 static_cast<WebFrameWidgetBase*>(widget)->SetRemoteViewportIntersection(
7378 intersection_state);
7379 EXPECT_EQ(client.MainFrameIntersection(), blink::WebRect(100, 100, 200, 140));
7380 }
7381
7382 class TestSameDocumentWithImageWebFrameClient
7383 : public frame_test_helpers::TestWebFrameClient {
7384 public:
TestSameDocumentWithImageWebFrameClient()7385 TestSameDocumentWithImageWebFrameClient() : num_of_image_requests_(0) {}
7386 ~TestSameDocumentWithImageWebFrameClient() override = default;
7387
7388 // frame_test_helpers::TestWebFrameClient:
WillSendRequest(WebURLRequest & request,ForRedirect for_redirect)7389 void WillSendRequest(WebURLRequest& request,
7390 ForRedirect for_redirect) override {
7391 if (request.GetRequestContext() ==
7392 mojom::blink::RequestContextType::IMAGE) {
7393 num_of_image_requests_++;
7394 EXPECT_EQ(mojom::FetchCacheMode::kDefault, request.GetCacheMode());
7395 }
7396 }
7397
NumOfImageRequests() const7398 int NumOfImageRequests() const { return num_of_image_requests_; }
7399
7400 private:
7401 int num_of_image_requests_;
7402 };
7403
TEST_F(WebFrameTest,NavigateToSameNoConditionalRequestForSubresource)7404 TEST_F(WebFrameTest, NavigateToSameNoConditionalRequestForSubresource) {
7405 RegisterMockedHttpURLLoad("foo_with_image.html");
7406 RegisterMockedHttpURLLoad("white-1x1.png");
7407 TestSameDocumentWithImageWebFrameClient client;
7408 frame_test_helpers::WebViewHelper web_view_helper;
7409 web_view_helper.InitializeAndLoad(base_url_ + "foo_with_image.html", &client,
7410 nullptr, nullptr,
7411 &ConfigureLoadsImagesAutomatically);
7412
7413 WebCache::Clear();
7414 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
7415 base_url_ + "foo_with_image.html");
7416
7417 // 2 images are requested, and each triggers 2 willSendRequest() calls,
7418 // once for preloading and once for the real request.
7419 EXPECT_EQ(client.NumOfImageRequests(), 4);
7420 }
7421
TEST_F(WebFrameTest,WebNodeImageContents)7422 TEST_F(WebFrameTest, WebNodeImageContents) {
7423 frame_test_helpers::WebViewHelper web_view_helper;
7424 web_view_helper.InitializeAndLoad("about:blank");
7425 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
7426
7427 static const char kBluePNG[] =
7428 "<img "
7429 "src=\"data:image/"
7430 "png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+"
7431 "9AAAAGElEQVQYV2NkYPj/n4EIwDiqEF8oUT94AFIQE/cCn90IAAAAAElFTkSuQmCC\">";
7432
7433 // Load up the image and test that we can extract the contents.
7434 KURL test_url = ToKURL("about:blank");
7435 frame_test_helpers::LoadHTMLString(frame, kBluePNG, test_url);
7436
7437 WebNode node = frame->GetDocument().Body().FirstChild();
7438 EXPECT_TRUE(node.IsElementNode());
7439 WebElement element = node.To<WebElement>();
7440 SkBitmap image = element.ImageContents();
7441 ASSERT_FALSE(image.isNull());
7442 EXPECT_EQ(image.width(), 10);
7443 EXPECT_EQ(image.height(), 10);
7444 EXPECT_EQ(image.getColor(0, 0), SK_ColorBLUE);
7445 }
7446
TEST_F(WebFrameTest,WebNodeImageContentsWithOrientation)7447 TEST_F(WebFrameTest, WebNodeImageContentsWithOrientation) {
7448 frame_test_helpers::WebViewHelper web_view_helper;
7449 web_view_helper.InitializeAndLoad("about:blank");
7450 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
7451
7452 // 4x8 jpg with orientation = 6 ( 90 degree CW rotation ).
7453 // w - white, b - blue.
7454 // raw => oriented
7455 // w w w w b b b b w w w w
7456 // w w w w b b b b w w w w
7457 // w w w w b b b b w w w w
7458 // w w w w b b b b w w w w
7459 // b b b b
7460 // b b b b
7461 // b b b b
7462 // b b b b
7463 static const char kBlueJPGWithOrientation[] =
7464 "<img "
7465 "src=\"data:image/"
7466 "jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBiRXhpZgAATU0AKgAAAAgABQESAAM"
7467 "AAAABAAYAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAAITAAMAAAABAA"
7468 "EAAAAAAAAAAABgAAAAAQAAAGAAAAAB/9sAQwACAQECAQECAgICAgICAgMFAwMDAwMGBAQDB"
7469 "QcGBwcHBgcHCAkLCQgICggHBwoNCgoLDAwMDAcJDg8NDA4LDAwM/9sAQwECAgIDAwMGAwMG"
7470 "DAgHCAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw"
7471 "M/8AAEQgACAAEAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC/"
7472 "/EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJ"
7473 "DNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3"
7474 "eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tf"
7475 "Y2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCA"
7476 "kKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM"
7477 "1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpz"
7478 "dHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytL"
7479 "T1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A7j/iMz/6tv8A/Mgf/e"
7480 "2iiiv9ff8AiVzwx/6Fn/lbEf8Ay0+A/tvG/wA/4L/I/9k=\">";
7481
7482 // Load up the image and test that we can extract the contents.
7483 KURL test_url = ToKURL("about:blank");
7484 frame_test_helpers::LoadHTMLString(frame, kBlueJPGWithOrientation, test_url);
7485
7486 WebNode node = frame->GetDocument().Body().FirstChild();
7487 EXPECT_TRUE(node.IsElementNode());
7488 WebElement element = node.To<WebElement>();
7489
7490 SkBitmap image_with_orientation = element.ImageContents();
7491 ASSERT_FALSE(image_with_orientation.isNull());
7492 EXPECT_EQ(image_with_orientation.width(), 8);
7493 EXPECT_EQ(image_with_orientation.height(), 4);
7494 // Should be almost blue.
7495 SkColor oriented_color = image_with_orientation.getColor(0, 0);
7496 EXPECT_NEAR(SkColorGetR(oriented_color), SkColorGetR(SK_ColorBLUE), 5);
7497 EXPECT_NEAR(SkColorGetG(oriented_color), SkColorGetG(SK_ColorBLUE), 5);
7498 EXPECT_NEAR(SkColorGetB(oriented_color), SkColorGetB(SK_ColorBLUE), 5);
7499 EXPECT_NEAR(SkColorGetA(oriented_color), SkColorGetA(SK_ColorBLUE), 5);
7500 }
7501
7502 class TestStartStopCallbackWebFrameClient
7503 : public frame_test_helpers::TestWebFrameClient {
7504 public:
TestStartStopCallbackWebFrameClient()7505 TestStartStopCallbackWebFrameClient()
7506 : start_loading_count_(0), stop_loading_count_(0) {}
7507 ~TestStartStopCallbackWebFrameClient() override = default;
7508
7509 // frame_test_helpers::TestWebFrameClient:
DidStartLoading()7510 void DidStartLoading() override {
7511 TestWebFrameClient::DidStartLoading();
7512 start_loading_count_++;
7513 }
DidStopLoading()7514 void DidStopLoading() override {
7515 TestWebFrameClient::DidStopLoading();
7516 stop_loading_count_++;
7517 }
7518
StartLoadingCount() const7519 int StartLoadingCount() const { return start_loading_count_; }
StopLoadingCount() const7520 int StopLoadingCount() const { return stop_loading_count_; }
7521
7522 private:
7523 int start_loading_count_;
7524 int stop_loading_count_;
7525 };
7526
TEST_F(WebFrameTest,PushStateStartsAndStops)7527 TEST_F(WebFrameTest, PushStateStartsAndStops) {
7528 RegisterMockedHttpURLLoad("push_state.html");
7529 TestStartStopCallbackWebFrameClient client;
7530 frame_test_helpers::WebViewHelper web_view_helper;
7531 web_view_helper.InitializeAndLoad(base_url_ + "push_state.html", &client);
7532
7533 // Wait for push state navigation to complete.
7534 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
7535 web_view_helper.LocalMainFrame());
7536 EXPECT_EQ(client.StartLoadingCount(), 2);
7537 EXPECT_EQ(client.StopLoadingCount(), 2);
7538 }
7539
TEST_F(WebFrameTest,IPAddressSpace)7540 TEST_F(WebFrameTest, IPAddressSpace) {
7541 frame_test_helpers::WebViewHelper web_view_helper;
7542 WebViewImpl* web_view =
7543 web_view_helper.InitializeAndLoad("data:text/html,ip_address_space");
7544
7545 network::mojom::IPAddressSpace values[] = {
7546 network::mojom::IPAddressSpace::kUnknown,
7547 network::mojom::IPAddressSpace::kLocal,
7548 network::mojom::IPAddressSpace::kPrivate,
7549 network::mojom::IPAddressSpace::kPublic};
7550
7551 for (auto value : values) {
7552 auto params = std::make_unique<WebNavigationParams>();
7553 params->url = url_test_helpers::ToKURL("about:blank");
7554 params->navigation_timings.navigation_start = base::TimeTicks::Now();
7555 params->navigation_timings.fetch_start = base::TimeTicks::Now();
7556 params->is_browser_initiated = true;
7557 params->ip_address_space = value;
7558 web_view_helper.LocalMainFrame()->CommitNavigation(std::move(params),
7559 nullptr);
7560 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
7561 web_view_helper.LocalMainFrame());
7562
7563 ExecutionContext* context =
7564 web_view->MainFrameImpl()->GetFrame()->DomWindow();
7565 EXPECT_EQ(value, context->AddressSpace());
7566 }
7567 }
7568
7569 class TestDidNavigateCommitTypeWebFrameClient
7570 : public frame_test_helpers::TestWebFrameClient {
7571 public:
TestDidNavigateCommitTypeWebFrameClient()7572 TestDidNavigateCommitTypeWebFrameClient()
7573 : last_commit_type_(kWebHistoryInertCommit) {}
7574 ~TestDidNavigateCommitTypeWebFrameClient() override = default;
7575
7576 // frame_test_helpers::TestWebFrameClient:
DidFinishSameDocumentNavigation(const WebHistoryItem &,WebHistoryCommitType type,bool content_initiated)7577 void DidFinishSameDocumentNavigation(const WebHistoryItem&,
7578 WebHistoryCommitType type,
7579 bool content_initiated) override {
7580 last_commit_type_ = type;
7581 }
7582
LastCommitType() const7583 WebHistoryCommitType LastCommitType() const { return last_commit_type_; }
7584
7585 private:
7586 WebHistoryCommitType last_commit_type_;
7587 };
7588
TEST_F(WebFrameTest,SameDocumentHistoryNavigationCommitType)7589 TEST_F(WebFrameTest, SameDocumentHistoryNavigationCommitType) {
7590 RegisterMockedHttpURLLoad("push_state.html");
7591 TestDidNavigateCommitTypeWebFrameClient client;
7592 frame_test_helpers::WebViewHelper web_view_helper;
7593 WebViewImpl* web_view_impl =
7594 web_view_helper.InitializeAndLoad(base_url_ + "push_state.html", &client);
7595 auto* local_frame = To<LocalFrame>(web_view_impl->GetPage()->MainFrame());
7596 Persistent<HistoryItem> item =
7597 local_frame->Loader().GetDocumentLoader()->GetHistoryItem();
7598 RunPendingTasks();
7599
7600 local_frame->Loader().GetDocumentLoader()->CommitSameDocumentNavigation(
7601 item->Url(), WebFrameLoadType::kBackForward, item.Get(),
7602 ClientRedirectPolicy::kNotClientRedirect, nullptr, /* origin_document */
7603 false, /* has_event */
7604 nullptr /* extra_data */);
7605 EXPECT_EQ(kWebBackForwardCommit, client.LastCommitType());
7606 }
7607
7608 // Tests that the first navigation in an initially blank subframe will result in
7609 // a history entry being replaced and not a new one being added.
TEST_F(WebFrameTest,FirstBlankSubframeNavigation)7610 TEST_F(WebFrameTest, FirstBlankSubframeNavigation) {
7611 RegisterMockedHttpURLLoad("history.html");
7612 RegisterMockedHttpURLLoad("find.html");
7613
7614 frame_test_helpers::WebViewHelper web_view_helper;
7615 web_view_helper.InitializeAndLoad("about:blank");
7616
7617 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
7618
7619 frame->ExecuteScript(WebScriptSource(WebString::FromUTF8(
7620 "document.body.appendChild(document.createElement('iframe'))")));
7621
7622 auto* iframe = To<WebLocalFrameImpl>(frame->FirstChild());
7623
7624 std::string url1 = base_url_ + "history.html";
7625 frame_test_helpers::LoadFrame(iframe, url1);
7626 EXPECT_EQ(url1, iframe->GetDocument().Url().GetString().Utf8());
7627 EXPECT_TRUE(iframe->GetDocumentLoader()->ReplacesCurrentHistoryItem());
7628
7629 std::string url2 = base_url_ + "find.html";
7630 frame_test_helpers::LoadFrame(iframe, url2);
7631 EXPECT_EQ(url2, iframe->GetDocument().Url().GetString().Utf8());
7632 EXPECT_FALSE(iframe->GetDocumentLoader()->ReplacesCurrentHistoryItem());
7633 }
7634
7635 // Tests that a navigation in a frame with a non-blank initial URL will create
7636 // a new history item, unlike the case above.
TEST_F(WebFrameTest,FirstNonBlankSubframeNavigation)7637 TEST_F(WebFrameTest, FirstNonBlankSubframeNavigation) {
7638 RegisterMockedHttpURLLoad("history.html");
7639 RegisterMockedHttpURLLoad("find.html");
7640
7641 frame_test_helpers::WebViewHelper web_view_helper;
7642 web_view_helper.InitializeAndLoad("about:blank");
7643
7644 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
7645
7646 std::string url1 = base_url_ + "history.html";
7647 std::string load_frame_js =
7648 "javascript:var f = document.createElement('iframe'); "
7649 "f.src = '";
7650 load_frame_js += url1 + "';" + "document.body.appendChild(f)";
7651 frame_test_helpers::LoadFrame(frame, load_frame_js);
7652
7653 WebLocalFrame* iframe = frame->FirstChild()->ToWebLocalFrame();
7654 EXPECT_EQ(url1, iframe->GetDocument().Url().GetString().Utf8());
7655
7656 std::string url2 = base_url_ + "find.html";
7657 frame_test_helpers::LoadFrame(iframe, url2);
7658 EXPECT_EQ(url2, iframe->GetDocument().Url().GetString().Utf8());
7659 EXPECT_FALSE(iframe->GetDocumentLoader()->ReplacesCurrentHistoryItem());
7660 }
7661
7662 // Test verifies that layout will change a layer's scrollable attibutes
TEST_F(WebFrameTest,overflowHiddenRewrite)7663 TEST_F(WebFrameTest, overflowHiddenRewrite) {
7664 RegisterMockedHttpURLLoad("non-scrollable.html");
7665 FixedLayoutTestWebWidgetClient client;
7666 frame_test_helpers::WebViewHelper web_view_helper;
7667 web_view_helper.Initialize(nullptr, nullptr, &client,
7668 &ConfigureCompositingWebView);
7669
7670 web_view_helper.Resize(gfx::Size(100, 100));
7671 frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(),
7672 base_url_ + "non-scrollable.html");
7673
7674 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7675
7676 auto* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
7677 auto* cc_scroll_layer = frame_view->GetScrollableArea()->LayerForScrolling();
7678 ASSERT_TRUE(cc_scroll_layer);
7679
7680 // Verify that the cc::Layer is not scrollable initially.
7681 auto* scroll_node = GetScrollNode(cc_scroll_layer);
7682 ASSERT_FALSE(scroll_node->user_scrollable_horizontal);
7683 ASSERT_FALSE(scroll_node->user_scrollable_vertical);
7684
7685 // Call javascript to make the layer scrollable, and verify it.
7686 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
7687 frame->ExecuteScript(WebScriptSource("allowScroll();"));
7688 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7689
7690 cc_scroll_layer = frame_view->GetScrollableArea()->LayerForScrolling();
7691 scroll_node = GetScrollNode(cc_scroll_layer);
7692 ASSERT_TRUE(scroll_node->user_scrollable_horizontal);
7693 ASSERT_TRUE(scroll_node->user_scrollable_vertical);
7694 }
7695
7696 // Test that currentHistoryItem reflects the current page, not the provisional
7697 // load.
TEST_F(WebFrameTest,CurrentHistoryItem)7698 TEST_F(WebFrameTest, CurrentHistoryItem) {
7699 RegisterMockedHttpURLLoad("fixed_layout.html");
7700 std::string url = base_url_ + "fixed_layout.html";
7701
7702 frame_test_helpers::WebViewHelper web_view_helper;
7703 web_view_helper.Initialize();
7704 WebLocalFrame* frame = web_view_helper.GetWebView()->MainFrameImpl();
7705 const FrameLoader& main_frame_loader =
7706 web_view_helper.LocalMainFrame()->GetFrame()->Loader();
7707 WebURLRequest request(ToKURL(url));
7708
7709 // Before navigation, there is no history item.
7710 EXPECT_FALSE(main_frame_loader.GetDocumentLoader()->GetHistoryItem());
7711
7712 frame->StartNavigation(request);
7713 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
7714 web_view_helper.LocalMainFrame());
7715
7716 // After navigation, there is.
7717 HistoryItem* item = main_frame_loader.GetDocumentLoader()->GetHistoryItem();
7718 ASSERT_TRUE(item);
7719 EXPECT_EQ(WTF::String(url.data()), item->UrlString());
7720 }
7721
7722 class FailCreateChildFrame : public frame_test_helpers::TestWebFrameClient {
7723 public:
FailCreateChildFrame()7724 FailCreateChildFrame() : call_count_(0) {}
7725 ~FailCreateChildFrame() override = default;
7726
7727 // frame_test_helpers::TestWebFrameClient:
CreateChildFrame(WebLocalFrame * parent,mojom::blink::TreeScopeType scope,const WebString & name,const WebString & fallback_name,const FramePolicy &,const WebFrameOwnerProperties & frame_owner_properties,mojom::blink::FrameOwnerElementType)7728 WebLocalFrame* CreateChildFrame(
7729 WebLocalFrame* parent,
7730 mojom::blink::TreeScopeType scope,
7731 const WebString& name,
7732 const WebString& fallback_name,
7733 const FramePolicy&,
7734 const WebFrameOwnerProperties& frame_owner_properties,
7735 mojom::blink::FrameOwnerElementType) override {
7736 ++call_count_;
7737 return nullptr;
7738 }
7739
CallCount() const7740 int CallCount() const { return call_count_; }
7741
7742 private:
7743 int call_count_;
7744 };
7745
7746 // Test that we don't crash if WebLocalFrameClient::createChildFrame() fails.
TEST_F(WebFrameTest,CreateChildFrameFailure)7747 TEST_F(WebFrameTest, CreateChildFrameFailure) {
7748 RegisterMockedHttpURLLoad("create_child_frame_fail.html");
7749 FailCreateChildFrame client;
7750 frame_test_helpers::WebViewHelper web_view_helper;
7751 web_view_helper.InitializeAndLoad(base_url_ + "create_child_frame_fail.html",
7752 &client);
7753
7754 EXPECT_EQ(1, client.CallCount());
7755 }
7756
TEST_F(WebFrameTest,fixedPositionInFixedViewport)7757 TEST_F(WebFrameTest, fixedPositionInFixedViewport) {
7758 RegisterMockedHttpURLLoad("fixed-position-in-fixed-viewport.html");
7759 frame_test_helpers::WebViewHelper web_view_helper;
7760 web_view_helper.InitializeAndLoad(
7761 base_url_ + "fixed-position-in-fixed-viewport.html", nullptr, nullptr,
7762 nullptr, ConfigureAndroid);
7763
7764 WebViewImpl* web_view = web_view_helper.GetWebView();
7765 web_view_helper.Resize(gfx::Size(100, 100));
7766
7767 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
7768 Element* bottom_fixed = document->getElementById("bottom-fixed");
7769 Element* top_bottom_fixed = document->getElementById("top-bottom-fixed");
7770 Element* right_fixed = document->getElementById("right-fixed");
7771 Element* left_right_fixed = document->getElementById("left-right-fixed");
7772
7773 // The layout viewport will hit the min-scale limit of 0.25, so it'll be
7774 // 400x800.
7775 web_view_helper.Resize(gfx::Size(100, 200));
7776 EXPECT_EQ(800, bottom_fixed->OffsetTop() + bottom_fixed->OffsetHeight());
7777 EXPECT_EQ(800, top_bottom_fixed->OffsetHeight());
7778
7779 // Now the layout viewport hits the content width limit of 500px so it'll be
7780 // 500x500.
7781 web_view_helper.Resize(gfx::Size(200, 200));
7782 EXPECT_EQ(500, right_fixed->OffsetLeft() + right_fixed->OffsetWidth());
7783 EXPECT_EQ(500, left_right_fixed->OffsetWidth());
7784 }
7785
TEST_F(WebFrameTest,FrameViewMoveWithSetFrameRect)7786 TEST_F(WebFrameTest, FrameViewMoveWithSetFrameRect) {
7787 frame_test_helpers::WebViewHelper web_view_helper;
7788 web_view_helper.InitializeAndLoad("about:blank");
7789 web_view_helper.Resize(gfx::Size(200, 200));
7790 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7791
7792 LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
7793 EXPECT_EQ(IntRect(0, 0, 200, 200), frame_view->FrameRect());
7794 frame_view->SetFrameRect(IntRect(100, 100, 200, 200));
7795 EXPECT_EQ(IntRect(100, 100, 200, 200), frame_view->FrameRect());
7796 }
7797
TEST_F(WebFrameTest,FrameViewScrollAccountsForBrowserControls)7798 TEST_F(WebFrameTest, FrameViewScrollAccountsForBrowserControls) {
7799 FixedLayoutTestWebWidgetClient client;
7800 RegisterMockedHttpURLLoad("long_scroll.html");
7801 frame_test_helpers::WebViewHelper web_view_helper;
7802 web_view_helper.InitializeAndLoad(base_url_ + "long_scroll.html", nullptr,
7803 nullptr, &client, ConfigureAndroid);
7804
7805 WebViewImpl* web_view = web_view_helper.GetWebView();
7806 LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
7807
7808 float browser_controls_height = 40;
7809 web_view->ResizeWithBrowserControls(gfx::Size(100, 100),
7810 browser_controls_height, 0, false);
7811 web_view->SetPageScaleFactor(2.0f);
7812 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7813
7814 web_view->MainFrameImpl()->SetScrollOffset(WebSize(0, 2000));
7815 EXPECT_EQ(ScrollOffset(0, 1900),
7816 frame_view->LayoutViewport()->GetScrollOffset());
7817
7818 // Simulate the browser controls showing by 20px, thus shrinking the viewport
7819 // and allowing it to scroll an additional 20px.
7820 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7821 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7822 20.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7823 EXPECT_EQ(ScrollOffset(0, 1920),
7824 frame_view->LayoutViewport()->MaximumScrollOffset());
7825
7826 // Show more, make sure the scroll actually gets clamped.
7827 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7828 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7829 20.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7830 web_view->MainFrameImpl()->SetScrollOffset(WebSize(0, 2000));
7831 EXPECT_EQ(ScrollOffset(0, 1940),
7832 frame_view->LayoutViewport()->GetScrollOffset());
7833
7834 // Hide until there's 10px showing.
7835 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7836 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7837 -30.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7838 EXPECT_EQ(ScrollOffset(0, 1910),
7839 frame_view->LayoutViewport()->MaximumScrollOffset());
7840
7841 // Simulate a LayoutEmbeddedContent::resize. The frame is resized to
7842 // accommodate the browser controls and Blink's view of the browser controls
7843 // matches that of the CC
7844 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7845 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7846 30.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7847 web_view->ResizeWithBrowserControls(gfx::Size(100, 60), 40.0f, 0, true);
7848 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7849 EXPECT_EQ(ScrollOffset(0, 1940),
7850 frame_view->LayoutViewport()->MaximumScrollOffset());
7851
7852 // Now simulate hiding.
7853 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7854 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7855 -10.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7856 EXPECT_EQ(ScrollOffset(0, 1930),
7857 frame_view->LayoutViewport()->MaximumScrollOffset());
7858
7859 // Reset to original state: 100px widget height, browser controls fully
7860 // hidden.
7861 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7862 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7863 -30.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7864 web_view->ResizeWithBrowserControls(gfx::Size(100, 100),
7865 browser_controls_height, 0, false);
7866 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7867 EXPECT_EQ(ScrollOffset(0, 1900),
7868 frame_view->LayoutViewport()->MaximumScrollOffset());
7869
7870 // Show the browser controls by just 1px, since we're zoomed in to 2X, that
7871 // should allow an extra 0.5px of scrolling in the visual viewport. Make
7872 // sure we're not losing any pixels when applying the adjustment on the
7873 // main frame.
7874 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7875 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7876 1.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7877 EXPECT_EQ(ScrollOffset(0, 1901),
7878 frame_view->LayoutViewport()->MaximumScrollOffset());
7879
7880 web_view->MainFrameWidget()->ApplyViewportChangesForTesting(
7881 {gfx::ScrollOffset(), gfx::Vector2dF(), 1.0f, false,
7882 2.0f / browser_controls_height, 0, cc::BrowserControlsState::kBoth});
7883 EXPECT_EQ(ScrollOffset(0, 1903),
7884 frame_view->LayoutViewport()->MaximumScrollOffset());
7885 }
7886
TEST_F(WebFrameTest,MaximumScrollPositionCanBeNegative)7887 TEST_F(WebFrameTest, MaximumScrollPositionCanBeNegative) {
7888 RegisterMockedHttpURLLoad("rtl-overview-mode.html");
7889
7890 FixedLayoutTestWebWidgetClient client;
7891 client.screen_info_.device_scale_factor = 1;
7892 int viewport_width = 640;
7893 int viewport_height = 480;
7894
7895 frame_test_helpers::WebViewHelper web_view_helper;
7896 web_view_helper.InitializeAndLoad(base_url_ + "rtl-overview-mode.html",
7897 nullptr, nullptr, &client,
7898 ConfigureAndroid);
7899 web_view_helper.GetWebView()->SetInitialPageScaleOverride(-1);
7900 web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled(
7901 true);
7902 web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(true);
7903 web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true);
7904 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
7905 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
7906
7907 LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
7908 ScrollableArea* layout_viewport = frame_view->LayoutViewport();
7909 EXPECT_LT(layout_viewport->MaximumScrollOffset().Width(), 0);
7910 }
7911
TEST_F(WebFrameTest,FullscreenLayerSize)7912 TEST_F(WebFrameTest, FullscreenLayerSize) {
7913 FixedLayoutTestWebWidgetClient client;
7914 RegisterMockedHttpURLLoad("fullscreen_div.html");
7915 frame_test_helpers::WebViewHelper web_view_helper;
7916 int viewport_width = 640;
7917 int viewport_height = 480;
7918 client.screen_info_.rect = gfx::Rect(viewport_width, viewport_height);
7919 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
7920 base_url_ + "fullscreen_div.html", nullptr, nullptr, &client,
7921 ConfigureAndroid);
7922 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
7923 UpdateAllLifecyclePhases(web_view_impl);
7924
7925 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
7926 Document* document = frame->GetDocument();
7927 LocalFrame::NotifyUserActivation(
7928 frame, mojom::UserActivationNotificationType::kTest);
7929 Element* div_fullscreen = document->getElementById("div1");
7930 Fullscreen::RequestFullscreen(*div_fullscreen);
7931 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*document));
7932 web_view_impl->DidEnterFullscreen();
7933 EXPECT_EQ(div_fullscreen, Fullscreen::FullscreenElementFrom(*document));
7934 UpdateAllLifecyclePhases(web_view_impl);
7935 EXPECT_EQ(div_fullscreen, Fullscreen::FullscreenElementFrom(*document));
7936
7937 // Verify that the element is sized to the viewport.
7938 auto* fullscreen_layout_object =
7939 To<LayoutBox>(div_fullscreen->GetLayoutObject());
7940 EXPECT_EQ(viewport_width, fullscreen_layout_object->LogicalWidth().ToInt());
7941 EXPECT_EQ(viewport_height, fullscreen_layout_object->LogicalHeight().ToInt());
7942
7943 // Verify it's updated after a device rotation.
7944 UpdateScreenInfoAndResizeView(&client, &web_view_helper, viewport_height,
7945 viewport_width);
7946 UpdateAllLifecyclePhases(web_view_impl);
7947 EXPECT_EQ(viewport_height, fullscreen_layout_object->LogicalWidth().ToInt());
7948 EXPECT_EQ(viewport_width, fullscreen_layout_object->LogicalHeight().ToInt());
7949 }
7950
TEST_F(WebFrameTest,FullscreenLayerNonScrollable)7951 TEST_F(WebFrameTest, FullscreenLayerNonScrollable) {
7952 FixedLayoutTestWebWidgetClient client;
7953 RegisterMockedHttpURLLoad("fullscreen_div.html");
7954 frame_test_helpers::WebViewHelper web_view_helper;
7955 int viewport_width = 640;
7956 int viewport_height = 480;
7957 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
7958 base_url_ + "fullscreen_div.html", nullptr, nullptr, &client,
7959 ConfigureAndroid);
7960 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
7961 UpdateAllLifecyclePhases(web_view_impl);
7962
7963 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
7964 Document* document = frame->GetDocument();
7965 LocalFrame::NotifyUserActivation(
7966 frame, mojom::UserActivationNotificationType::kTest);
7967 Element* div_fullscreen = document->getElementById("div1");
7968 Fullscreen::RequestFullscreen(*div_fullscreen);
7969 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*document));
7970 web_view_impl->DidEnterFullscreen();
7971 EXPECT_EQ(div_fullscreen, Fullscreen::FullscreenElementFrom(*document));
7972 UpdateAllLifecyclePhases(web_view_impl);
7973 EXPECT_EQ(div_fullscreen, Fullscreen::FullscreenElementFrom(*document));
7974
7975 // Verify that the viewports are nonscrollable.
7976 LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
7977 cc::Layer* layout_viewport_scroll_layer =
7978 frame_view->GetScrollableArea()->LayerForScrolling();
7979 cc::Layer* visual_viewport_scroll_layer =
7980 frame_view->GetPage()->GetVisualViewport().LayerForScrolling();
7981
7982 auto* layout_viewport_scroll_node =
7983 GetScrollNode(layout_viewport_scroll_layer);
7984 ASSERT_FALSE(layout_viewport_scroll_node->user_scrollable_horizontal);
7985 ASSERT_FALSE(layout_viewport_scroll_node->user_scrollable_vertical);
7986 auto* visual_viewport_scroll_node =
7987 GetScrollNode(visual_viewport_scroll_layer);
7988 ASSERT_FALSE(visual_viewport_scroll_node->user_scrollable_horizontal);
7989 ASSERT_FALSE(visual_viewport_scroll_node->user_scrollable_vertical);
7990
7991 // Verify that the viewports are scrollable upon exiting fullscreen.
7992 EXPECT_EQ(div_fullscreen, Fullscreen::FullscreenElementFrom(*document));
7993 web_view_impl->DidExitFullscreen();
7994 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*document));
7995 UpdateAllLifecyclePhases(web_view_impl);
7996 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*document));
7997 layout_viewport_scroll_layer =
7998 frame_view->GetScrollableArea()->LayerForScrolling();
7999 visual_viewport_scroll_layer =
8000 frame_view->GetPage()->GetVisualViewport().LayerForScrolling();
8001 layout_viewport_scroll_node = GetScrollNode(layout_viewport_scroll_layer);
8002 ASSERT_TRUE(layout_viewport_scroll_node->user_scrollable_horizontal);
8003 ASSERT_TRUE(layout_viewport_scroll_node->user_scrollable_vertical);
8004 visual_viewport_scroll_node = GetScrollNode(visual_viewport_scroll_layer);
8005 ASSERT_TRUE(visual_viewport_scroll_node->user_scrollable_horizontal);
8006 ASSERT_TRUE(visual_viewport_scroll_node->user_scrollable_vertical);
8007 }
8008
TEST_F(WebFrameTest,FullscreenMainFrame)8009 TEST_F(WebFrameTest, FullscreenMainFrame) {
8010 FixedLayoutTestWebWidgetClient client;
8011 RegisterMockedHttpURLLoad("fullscreen_div.html");
8012 frame_test_helpers::WebViewHelper web_view_helper;
8013 int viewport_width = 640;
8014 int viewport_height = 480;
8015 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8016 base_url_ + "fullscreen_div.html", nullptr, nullptr, &client,
8017 ConfigureAndroid);
8018 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
8019 UpdateAllLifecyclePhases(web_view_impl);
8020
8021 cc::Layer* cc_scroll_layer = web_view_impl->MainFrameImpl()
8022 ->GetFrame()
8023 ->View()
8024 ->LayoutViewport()
8025 ->LayerForScrolling();
8026 auto* scroll_node = GetScrollNode(cc_scroll_layer);
8027 ASSERT_TRUE(scroll_node->scrollable);
8028 ASSERT_TRUE(scroll_node->user_scrollable_horizontal);
8029 ASSERT_TRUE(scroll_node->user_scrollable_vertical);
8030
8031 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8032 Document* document = frame->GetDocument();
8033 LocalFrame::NotifyUserActivation(
8034 frame, mojom::UserActivationNotificationType::kTest);
8035 Fullscreen::RequestFullscreen(*document->documentElement());
8036 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*document));
8037 web_view_impl->DidEnterFullscreen();
8038 EXPECT_EQ(document->documentElement(),
8039 Fullscreen::FullscreenElementFrom(*document));
8040
8041 UpdateAllLifecyclePhases(web_view_impl);
8042 EXPECT_EQ(document->documentElement(),
8043 Fullscreen::FullscreenElementFrom(*document));
8044
8045 // Verify that the main frame is still scrollable.
8046 cc_scroll_layer = web_view_impl->MainFrameImpl()
8047 ->GetFrame()
8048 ->View()
8049 ->LayoutViewport()
8050 ->LayerForScrolling();
8051 scroll_node = GetScrollNode(cc_scroll_layer);
8052 ASSERT_TRUE(scroll_node->scrollable);
8053 ASSERT_TRUE(scroll_node->user_scrollable_horizontal);
8054 ASSERT_TRUE(scroll_node->user_scrollable_vertical);
8055
8056 // Verify the main frame still behaves correctly after a resize.
8057 web_view_helper.Resize(gfx::Size(viewport_height, viewport_width));
8058 scroll_node = GetScrollNode(cc_scroll_layer);
8059 ASSERT_TRUE(scroll_node->scrollable);
8060 ASSERT_TRUE(scroll_node->user_scrollable_horizontal);
8061 ASSERT_TRUE(scroll_node->user_scrollable_vertical);
8062 }
8063
TEST_F(WebFrameTest,FullscreenSubframe)8064 TEST_F(WebFrameTest, FullscreenSubframe) {
8065 FixedLayoutTestWebWidgetClient client;
8066 RegisterMockedHttpURLLoad("fullscreen_iframe.html");
8067 RegisterMockedHttpURLLoad("fullscreen_div.html");
8068 frame_test_helpers::WebViewHelper web_view_helper;
8069 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8070 base_url_ + "fullscreen_iframe.html", nullptr, nullptr, &client,
8071 ConfigureAndroid);
8072 int viewport_width = 640;
8073 int viewport_height = 480;
8074 UpdateScreenInfoAndResizeView(&client, &web_view_helper, viewport_width,
8075 viewport_height);
8076 UpdateAllLifecyclePhases(web_view_impl);
8077
8078 LocalFrame* frame =
8079 To<WebLocalFrameImpl>(
8080 web_view_helper.GetWebView()->MainFrame()->FirstChild())
8081 ->GetFrame();
8082 Document* document = frame->GetDocument();
8083 LocalFrame::NotifyUserActivation(
8084 frame, mojom::UserActivationNotificationType::kTest);
8085 Element* div_fullscreen = document->getElementById("div1");
8086 Fullscreen::RequestFullscreen(*div_fullscreen);
8087 web_view_impl->DidEnterFullscreen();
8088 UpdateAllLifecyclePhases(web_view_impl);
8089
8090 // Verify that the element is sized to the viewport.
8091 auto* fullscreen_layout_object =
8092 To<LayoutBox>(div_fullscreen->GetLayoutObject());
8093 EXPECT_EQ(viewport_width, fullscreen_layout_object->LogicalWidth().ToInt());
8094 EXPECT_EQ(viewport_height, fullscreen_layout_object->LogicalHeight().ToInt());
8095
8096 // Verify it's updated after a device rotation.
8097 UpdateScreenInfoAndResizeView(&client, &web_view_helper, viewport_height,
8098 viewport_width);
8099 UpdateAllLifecyclePhases(web_view_impl);
8100 EXPECT_EQ(viewport_height, fullscreen_layout_object->LogicalWidth().ToInt());
8101 EXPECT_EQ(viewport_width, fullscreen_layout_object->LogicalHeight().ToInt());
8102 }
8103
8104 // Tests entering nested fullscreen and then exiting via the same code path
8105 // that's used when the browser process exits fullscreen.
TEST_F(WebFrameTest,FullscreenNestedExit)8106 TEST_F(WebFrameTest, FullscreenNestedExit) {
8107 RegisterMockedHttpURLLoad("fullscreen_iframe.html");
8108 RegisterMockedHttpURLLoad("fullscreen_div.html");
8109 frame_test_helpers::WebViewHelper web_view_helper;
8110 WebViewImpl* web_view_impl =
8111 web_view_helper.InitializeAndLoad(base_url_ + "fullscreen_iframe.html");
8112
8113 UpdateAllLifecyclePhases(web_view_impl);
8114
8115 Document* top_doc = web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
8116 Element* top_body = top_doc->body();
8117
8118 auto* iframe = To<HTMLIFrameElement>(top_doc->QuerySelector("iframe"));
8119 Document* iframe_doc = iframe->contentDocument();
8120 Element* iframe_body = iframe_doc->body();
8121
8122 LocalFrame::NotifyUserActivation(
8123 top_doc->GetFrame(), mojom::UserActivationNotificationType::kTest);
8124 Fullscreen::RequestFullscreen(*top_body);
8125
8126 web_view_impl->DidEnterFullscreen();
8127 UpdateAllLifecyclePhases(web_view_impl);
8128
8129 LocalFrame::NotifyUserActivation(
8130 iframe_doc->GetFrame(), mojom::UserActivationNotificationType::kTest);
8131 Fullscreen::RequestFullscreen(*iframe_body);
8132
8133 web_view_impl->DidEnterFullscreen();
8134 Microtask::PerformCheckpoint(V8PerIsolateData::MainThreadIsolate());
8135 UpdateAllLifecyclePhases(web_view_impl);
8136
8137 // We are now in nested fullscreen, with both documents having a non-empty
8138 // fullscreen element stack.
8139 EXPECT_EQ(iframe, Fullscreen::FullscreenElementFrom(*top_doc));
8140 EXPECT_EQ(iframe_body, Fullscreen::FullscreenElementFrom(*iframe_doc));
8141
8142 web_view_impl->DidExitFullscreen();
8143 UpdateAllLifecyclePhases(web_view_impl);
8144
8145 // We should now have fully exited fullscreen.
8146 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*top_doc));
8147 EXPECT_EQ(nullptr, Fullscreen::FullscreenElementFrom(*iframe_doc));
8148 }
8149
TEST_F(WebFrameTest,FullscreenWithTinyViewport)8150 TEST_F(WebFrameTest, FullscreenWithTinyViewport) {
8151 FixedLayoutTestWebWidgetClient client;
8152 RegisterMockedHttpURLLoad("viewport-tiny.html");
8153 frame_test_helpers::WebViewHelper web_view_helper;
8154 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8155 base_url_ + "viewport-tiny.html", nullptr, nullptr, &client,
8156 ConfigureAndroid);
8157 int viewport_width = 384;
8158 int viewport_height = 640;
8159 UpdateScreenInfoAndResizeView(&client, &web_view_helper, viewport_width,
8160 viewport_height);
8161 UpdateAllLifecyclePhases(web_view_impl);
8162
8163 auto* layout_view = web_view_helper.GetWebView()
8164 ->MainFrameImpl()
8165 ->GetFrameView()
8166 ->GetLayoutView();
8167 EXPECT_EQ(320, layout_view->LogicalWidth().Floor());
8168 EXPECT_EQ(533, layout_view->LogicalHeight().Floor());
8169 EXPECT_FLOAT_EQ(1.2, web_view_impl->PageScaleFactor());
8170 EXPECT_FLOAT_EQ(1.2, web_view_impl->MinimumPageScaleFactor());
8171 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8172
8173 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8174 LocalFrame::NotifyUserActivation(
8175 frame, mojom::UserActivationNotificationType::kTest);
8176 Fullscreen::RequestFullscreen(*frame->GetDocument()->documentElement());
8177 web_view_impl->DidEnterFullscreen();
8178 UpdateAllLifecyclePhases(web_view_impl);
8179 EXPECT_EQ(384, layout_view->LogicalWidth().Floor());
8180 EXPECT_EQ(640, layout_view->LogicalHeight().Floor());
8181 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8182 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8183 EXPECT_FLOAT_EQ(1.0, web_view_impl->MaximumPageScaleFactor());
8184
8185 web_view_impl->DidExitFullscreen();
8186 UpdateAllLifecyclePhases(web_view_impl);
8187 EXPECT_EQ(320, layout_view->LogicalWidth().Floor());
8188 EXPECT_EQ(533, layout_view->LogicalHeight().Floor());
8189 EXPECT_FLOAT_EQ(1.2, web_view_impl->PageScaleFactor());
8190 EXPECT_FLOAT_EQ(1.2, web_view_impl->MinimumPageScaleFactor());
8191 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8192 }
8193
TEST_F(WebFrameTest,FullscreenResizeWithTinyViewport)8194 TEST_F(WebFrameTest, FullscreenResizeWithTinyViewport) {
8195 FixedLayoutTestWebWidgetClient client;
8196 RegisterMockedHttpURLLoad("viewport-tiny.html");
8197 frame_test_helpers::WebViewHelper web_view_helper;
8198 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8199 base_url_ + "viewport-tiny.html", nullptr, nullptr, &client,
8200 ConfigureAndroid);
8201 int viewport_width = 384;
8202 int viewport_height = 640;
8203 UpdateScreenInfoAndResizeView(&client, &web_view_helper, viewport_width,
8204 viewport_height);
8205 UpdateAllLifecyclePhases(web_view_impl);
8206
8207 auto* layout_view = web_view_helper.GetWebView()
8208 ->MainFrameImpl()
8209 ->GetFrameView()
8210 ->GetLayoutView();
8211 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8212 LocalFrame::NotifyUserActivation(
8213 frame, mojom::UserActivationNotificationType::kTest);
8214 Fullscreen::RequestFullscreen(*frame->GetDocument()->documentElement());
8215 web_view_impl->DidEnterFullscreen();
8216 UpdateAllLifecyclePhases(web_view_impl);
8217 EXPECT_EQ(384, layout_view->LogicalWidth().Floor());
8218 EXPECT_EQ(640, layout_view->LogicalHeight().Floor());
8219 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8220 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8221 EXPECT_FLOAT_EQ(1.0, web_view_impl->MaximumPageScaleFactor());
8222
8223 viewport_width = 640;
8224 viewport_height = 384;
8225 UpdateScreenInfoAndResizeView(&client, &web_view_helper, viewport_width,
8226 viewport_height);
8227 UpdateAllLifecyclePhases(web_view_impl);
8228 EXPECT_EQ(640, layout_view->LogicalWidth().Floor());
8229 EXPECT_EQ(384, layout_view->LogicalHeight().Floor());
8230 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8231 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8232 EXPECT_FLOAT_EQ(1.0, web_view_impl->MaximumPageScaleFactor());
8233
8234 web_view_impl->DidExitFullscreen();
8235 UpdateAllLifecyclePhases(web_view_impl);
8236 EXPECT_EQ(320, layout_view->LogicalWidth().Floor());
8237 EXPECT_EQ(192, layout_view->LogicalHeight().Floor());
8238 EXPECT_FLOAT_EQ(2, web_view_impl->PageScaleFactor());
8239 EXPECT_FLOAT_EQ(2, web_view_impl->MinimumPageScaleFactor());
8240 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8241 }
8242
TEST_F(WebFrameTest,FullscreenRestoreScaleFactorUponExiting)8243 TEST_F(WebFrameTest, FullscreenRestoreScaleFactorUponExiting) {
8244 // The purpose of this test is to more precisely simulate the sequence of
8245 // resize and switching fullscreen state operations on WebView, with the
8246 // interference from Android status bars like a real device does.
8247 // This verifies we handle the transition and restore states correctly.
8248 WebSize screen_size_minus_status_bars_minus_url_bar(598, 303);
8249 WebSize screen_size_minus_status_bars(598, 359);
8250 WebSize screen_size(640, 384);
8251
8252 FixedLayoutTestWebWidgetClient client;
8253 RegisterMockedHttpURLLoad("fullscreen_restore_scale_factor.html");
8254 frame_test_helpers::WebViewHelper web_view_helper;
8255 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8256 base_url_ + "fullscreen_restore_scale_factor.html", nullptr, nullptr,
8257 &client, &ConfigureAndroid);
8258 UpdateScreenInfoAndResizeView(
8259 &client, &web_view_helper,
8260 screen_size_minus_status_bars_minus_url_bar.width,
8261 screen_size_minus_status_bars_minus_url_bar.height);
8262 auto* layout_view = web_view_helper.GetWebView()
8263 ->MainFrameImpl()
8264 ->GetFrameView()
8265 ->GetLayoutView();
8266 EXPECT_EQ(screen_size_minus_status_bars_minus_url_bar.width,
8267 layout_view->LogicalWidth().Floor());
8268 EXPECT_EQ(screen_size_minus_status_bars_minus_url_bar.height,
8269 layout_view->LogicalHeight().Floor());
8270 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8271 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8272 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8273
8274 {
8275 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8276 LocalFrame::NotifyUserActivation(
8277 frame, mojom::UserActivationNotificationType::kTest);
8278 Fullscreen::RequestFullscreen(*frame->GetDocument()->body());
8279 }
8280
8281 web_view_impl->DidEnterFullscreen();
8282 UpdateAllLifecyclePhases(web_view_impl);
8283 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
8284 screen_size_minus_status_bars.width,
8285 screen_size_minus_status_bars.height);
8286 UpdateScreenInfoAndResizeView(&client, &web_view_helper, screen_size.width,
8287 screen_size.height);
8288 EXPECT_EQ(screen_size.width, layout_view->LogicalWidth().Floor());
8289 EXPECT_EQ(screen_size.height, layout_view->LogicalHeight().Floor());
8290 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8291 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8292 EXPECT_FLOAT_EQ(1.0, web_view_impl->MaximumPageScaleFactor());
8293
8294 web_view_impl->DidExitFullscreen();
8295 UpdateAllLifecyclePhases(web_view_impl);
8296 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
8297 screen_size_minus_status_bars.width,
8298 screen_size_minus_status_bars.height);
8299 UpdateScreenInfoAndResizeView(
8300 &client, &web_view_helper,
8301 screen_size_minus_status_bars_minus_url_bar.width,
8302 screen_size_minus_status_bars_minus_url_bar.height);
8303 EXPECT_EQ(screen_size_minus_status_bars_minus_url_bar.width,
8304 layout_view->LogicalWidth().Floor());
8305 EXPECT_EQ(screen_size_minus_status_bars_minus_url_bar.height,
8306 layout_view->LogicalHeight().Floor());
8307 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8308 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8309 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8310 }
8311
8312 // Tests that leaving fullscreen by navigating to a new page resets the
8313 // fullscreen page scale constraints.
TEST_F(WebFrameTest,ClearFullscreenConstraintsOnNavigation)8314 TEST_F(WebFrameTest, ClearFullscreenConstraintsOnNavigation) {
8315 RegisterMockedHttpURLLoad("viewport-tiny.html");
8316 frame_test_helpers::WebViewHelper web_view_helper;
8317 int viewport_width = 100;
8318 int viewport_height = 200;
8319
8320 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8321 base_url_ + "viewport-tiny.html", nullptr, nullptr, nullptr,
8322 ConfigureAndroid);
8323
8324 web_view_helper.Resize(gfx::Size(viewport_width, viewport_height));
8325 UpdateAllLifecyclePhases(web_view_impl);
8326
8327 // viewport-tiny.html specifies a 320px layout width.
8328 auto* layout_view =
8329 web_view_impl->MainFrameImpl()->GetFrameView()->GetLayoutView();
8330 EXPECT_EQ(320, layout_view->LogicalWidth().Floor());
8331 EXPECT_EQ(640, layout_view->LogicalHeight().Floor());
8332 EXPECT_FLOAT_EQ(0.3125, web_view_impl->PageScaleFactor());
8333 EXPECT_FLOAT_EQ(0.3125, web_view_impl->MinimumPageScaleFactor());
8334 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8335
8336 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8337 LocalFrame::NotifyUserActivation(
8338 frame, mojom::UserActivationNotificationType::kTest);
8339 Fullscreen::RequestFullscreen(*frame->GetDocument()->documentElement());
8340 web_view_impl->DidEnterFullscreen();
8341 UpdateAllLifecyclePhases(web_view_impl);
8342
8343 // Entering fullscreen causes layout size and page scale limits to be
8344 // overridden.
8345 EXPECT_EQ(100, layout_view->LogicalWidth().Floor());
8346 EXPECT_EQ(200, layout_view->LogicalHeight().Floor());
8347 EXPECT_FLOAT_EQ(1.0, web_view_impl->PageScaleFactor());
8348 EXPECT_FLOAT_EQ(1.0, web_view_impl->MinimumPageScaleFactor());
8349 EXPECT_FLOAT_EQ(1.0, web_view_impl->MaximumPageScaleFactor());
8350
8351 const char kSource[] = "<meta name=\"viewport\" content=\"width=200\">";
8352
8353 // Load a new page before exiting fullscreen.
8354 KURL test_url = ToKURL("about:blank");
8355 WebLocalFrame* web_frame = web_view_helper.LocalMainFrame();
8356 frame_test_helpers::LoadHTMLString(web_frame, kSource, test_url);
8357 web_view_impl->DidExitFullscreen();
8358 UpdateAllLifecyclePhases(web_view_impl);
8359
8360 // Make sure the new page's layout size and scale factor limits aren't
8361 // overridden.
8362 layout_view = web_view_impl->MainFrameImpl()->GetFrameView()->GetLayoutView();
8363 EXPECT_EQ(200, layout_view->LogicalWidth().Floor());
8364 EXPECT_EQ(400, layout_view->LogicalHeight().Floor());
8365 EXPECT_FLOAT_EQ(0.5, web_view_impl->MinimumPageScaleFactor());
8366 EXPECT_FLOAT_EQ(5.0, web_view_impl->MaximumPageScaleFactor());
8367 }
8368
TEST_F(WebFrameTest,OverlayFullscreenVideo)8369 TEST_F(WebFrameTest, OverlayFullscreenVideo) {
8370 ScopedForceOverlayFullscreenVideoForTest force_overlay_fullscreen_video(true);
8371 RegisterMockedHttpURLLoad("fullscreen_video.html");
8372 frame_test_helpers::TestWebWidgetClient web_widget_client;
8373 frame_test_helpers::WebViewHelper web_view_helper;
8374 WebViewImpl* web_view_impl =
8375 web_view_helper.InitializeAndLoad(base_url_ + "fullscreen_video.html",
8376 nullptr, nullptr, &web_widget_client);
8377
8378 // Ensure that the local frame view has a paint artifact compositor. It's
8379 // created lazily, and doing so after entering fullscreen would undo the
8380 // overlay video layer modification.
8381 UpdateAllLifecyclePhases(web_view_impl);
8382
8383 const cc::LayerTreeHost* layer_tree_host =
8384 web_widget_client.layer_tree_host();
8385
8386 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8387 LocalFrame::NotifyUserActivation(
8388 frame, mojom::UserActivationNotificationType::kTest);
8389 auto* video =
8390 To<HTMLVideoElement>(frame->GetDocument()->getElementById("video"));
8391 EXPECT_TRUE(video->UsesOverlayFullscreenVideo());
8392 EXPECT_FALSE(video->IsFullscreen());
8393 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
8394
8395 const cc::Layer* root_layer = layer_tree_host->root_layer();
8396 EXPECT_EQ(1u, CcLayersByName(root_layer, "Scrolling Contents Layer").size());
8397 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "other").size());
8398 // The video is not composited when it's not in full screen.
8399 EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "video").size());
8400
8401 video->webkitEnterFullscreen();
8402 web_view_impl->DidEnterFullscreen();
8403 UpdateAllLifecyclePhases(web_view_impl);
8404 EXPECT_TRUE(video->IsFullscreen());
8405 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()),
8406 SK_AlphaTRANSPARENT);
8407
8408 root_layer = layer_tree_host->root_layer();
8409 EXPECT_EQ(0u, CcLayersByName(root_layer, "Scrolling Contents Layer").size());
8410 EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "other").size());
8411 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "video").size());
8412
8413 web_view_impl->DidExitFullscreen();
8414 UpdateAllLifecyclePhases(web_view_impl);
8415 EXPECT_FALSE(video->IsFullscreen());
8416 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
8417
8418 root_layer = layer_tree_host->root_layer();
8419 EXPECT_EQ(1u, CcLayersByName(root_layer, "Scrolling Contents Layer").size());
8420 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "other").size());
8421 // The video is not composited when it's not in full screen.
8422 EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "video").size());
8423 }
8424
TEST_F(WebFrameTest,OverlayFullscreenVideoInIframe)8425 TEST_F(WebFrameTest, OverlayFullscreenVideoInIframe) {
8426 ScopedForceOverlayFullscreenVideoForTest force_overlay_fullscreen_video(true);
8427 RegisterMockedHttpURLLoad("fullscreen_video_in_iframe.html");
8428 RegisterMockedHttpURLLoad("fullscreen_video.html");
8429 frame_test_helpers::TestWebWidgetClient web_widget_client;
8430 frame_test_helpers::WebViewHelper web_view_helper;
8431 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8432 base_url_ + "fullscreen_video_in_iframe.html", nullptr, nullptr,
8433 &web_widget_client);
8434
8435 const cc::LayerTreeHost* layer_tree_host =
8436 web_widget_client.layer_tree_host();
8437 LocalFrame* iframe =
8438 To<WebLocalFrameImpl>(
8439 web_view_helper.GetWebView()->MainFrame()->FirstChild())
8440 ->GetFrame();
8441 LocalFrame::NotifyUserActivation(
8442 iframe, mojom::UserActivationNotificationType::kTest);
8443 auto* video =
8444 To<HTMLVideoElement>(iframe->GetDocument()->getElementById("video"));
8445 EXPECT_TRUE(video->UsesOverlayFullscreenVideo());
8446 EXPECT_FALSE(video->IsFullscreen());
8447 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
8448
8449 video->webkitEnterFullscreen();
8450 web_view_impl->DidEnterFullscreen();
8451 UpdateAllLifecyclePhases(web_view_impl);
8452 EXPECT_TRUE(video->IsFullscreen());
8453 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()),
8454 SK_AlphaTRANSPARENT);
8455
8456 web_view_impl->DidExitFullscreen();
8457 UpdateAllLifecyclePhases(web_view_impl);
8458 EXPECT_FALSE(video->IsFullscreen());
8459 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
8460 }
8461
TEST_F(WebFrameTest,WebXrImmersiveOverlay)8462 TEST_F(WebFrameTest, WebXrImmersiveOverlay) {
8463 RegisterMockedHttpURLLoad("webxr_overlay.html");
8464 frame_test_helpers::TestWebWidgetClient web_widget_client;
8465 frame_test_helpers::WebViewHelper web_view_helper;
8466 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
8467 base_url_ + "webxr_overlay.html", nullptr, nullptr, &web_widget_client);
8468 web_view_helper.Resize(gfx::Size(640, 480));
8469
8470 // Ensure that the local frame view has a paint artifact compositor. It's
8471 // created lazily, and doing so after entering fullscreen would undo the
8472 // overlay layer modification.
8473 UpdateAllLifecyclePhases(web_view_impl);
8474
8475 const cc::LayerTreeHost* layer_tree_host =
8476 web_widget_client.layer_tree_host();
8477
8478 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
8479 Document* document = frame->GetDocument();
8480
8481 Element* overlay = document->getElementById("overlay");
8482 EXPECT_FALSE(Fullscreen::IsFullscreenElement(*overlay));
8483 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
8484
8485 // It's not legal to switch the fullscreen element while in immersive-ar mode,
8486 // so set the fullscreen element first before activating that. This requires
8487 // user activation.
8488 LocalFrame::NotifyUserActivation(
8489 frame, mojom::UserActivationNotificationType::kTest);
8490 Fullscreen::RequestFullscreen(*overlay);
8491 EXPECT_FALSE(document->IsXrOverlay());
8492 document->SetIsXrOverlay(true, overlay);
8493 EXPECT_TRUE(document->IsXrOverlay());
8494
8495 const cc::Layer* root_layer = layer_tree_host->root_layer();
8496 EXPECT_EQ(1u, CcLayersByName(root_layer, "Scrolling Contents Layer").size());
8497 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "other").size());
8498 // The overlay is not composited when it's not in full screen.
8499 EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "overlay").size());
8500 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "inner").size());
8501
8502 web_view_impl->DidEnterFullscreen();
8503 UpdateAllLifecyclePhases(web_view_impl);
8504 EXPECT_TRUE(Fullscreen::IsFullscreenElement(*overlay));
8505 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()),
8506 SK_AlphaTRANSPARENT);
8507
8508 GraphicsLayer* inner_layer =
8509 To<LayoutBoxModelObject>(
8510 frame->GetDocument()->getElementById("inner")->GetLayoutObject())
8511 ->Layer()
8512 ->GetCompositedLayerMapping()
8513 ->MainGraphicsLayer();
8514 EXPECT_TRUE(inner_layer);
8515
8516 root_layer = layer_tree_host->root_layer();
8517 EXPECT_EQ(0u, CcLayersByName(root_layer, "Scrolling Contents Layer").size());
8518 EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "other").size());
8519 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "overlay").size());
8520 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "inner").size());
8521
8522 web_view_impl->DidExitFullscreen();
8523 UpdateAllLifecyclePhases(web_view_impl);
8524 EXPECT_FALSE(Fullscreen::IsFullscreenElement(*overlay));
8525 EXPECT_EQ(SkColorGetA(layer_tree_host->background_color()), SK_AlphaOPAQUE);
8526 document->SetIsXrOverlay(false, overlay);
8527
8528 root_layer = layer_tree_host->root_layer();
8529 EXPECT_EQ(1u, CcLayersByName(root_layer, "Scrolling Contents Layer").size());
8530 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "other").size());
8531 // The overlay is not composited when it's not in full screen.
8532 EXPECT_EQ(0u, CcLayersByDOMElementId(root_layer, "overlay").size());
8533 EXPECT_EQ(1u, CcLayersByDOMElementId(root_layer, "inner").size());
8534 }
8535
TEST_F(WebFrameTest,LayoutBlockPercentHeightDescendants)8536 TEST_F(WebFrameTest, LayoutBlockPercentHeightDescendants) {
8537 RegisterMockedHttpURLLoad("percent-height-descendants.html");
8538 frame_test_helpers::WebViewHelper web_view_helper;
8539 web_view_helper.InitializeAndLoad(base_url_ +
8540 "percent-height-descendants.html");
8541
8542 WebViewImpl* web_view = web_view_helper.GetWebView();
8543 web_view_helper.Resize(gfx::Size(800, 800));
8544 UpdateAllLifecyclePhases(web_view);
8545
8546 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
8547 LayoutBlock* container =
8548 To<LayoutBlock>(document->getElementById("container")->GetLayoutObject());
8549 auto* percent_height_in_anonymous =
8550 To<LayoutBox>(document->getElementById("percent-height-in-anonymous")
8551 ->GetLayoutObject());
8552 auto* percent_height_direct_child =
8553 To<LayoutBox>(document->getElementById("percent-height-direct-child")
8554 ->GetLayoutObject());
8555
8556 EXPECT_TRUE(
8557 container->HasPercentHeightDescendant(percent_height_in_anonymous));
8558 EXPECT_TRUE(
8559 container->HasPercentHeightDescendant(percent_height_direct_child));
8560
8561 ASSERT_TRUE(container->PercentHeightDescendants());
8562 ASSERT_TRUE(container->HasPercentHeightDescendants());
8563 EXPECT_EQ(2U, container->PercentHeightDescendants()->size());
8564 EXPECT_TRUE(container->PercentHeightDescendants()->Contains(
8565 percent_height_in_anonymous));
8566 EXPECT_TRUE(container->PercentHeightDescendants()->Contains(
8567 percent_height_direct_child));
8568
8569 LayoutBlock* anonymous_block = percent_height_in_anonymous->ContainingBlock();
8570 EXPECT_TRUE(anonymous_block->IsAnonymous());
8571 EXPECT_FALSE(anonymous_block->HasPercentHeightDescendants());
8572 }
8573
TEST_F(WebFrameTest,HasVisibleContentOnVisibleFrames)8574 TEST_F(WebFrameTest, HasVisibleContentOnVisibleFrames) {
8575 RegisterMockedHttpURLLoad("visible_frames.html");
8576 frame_test_helpers::WebViewHelper web_view_helper;
8577 WebViewImpl* web_view_impl =
8578 web_view_helper.InitializeAndLoad(base_url_ + "visible_frames.html");
8579 for (WebFrame* frame = web_view_impl->MainFrameImpl()->TraverseNext(); frame;
8580 frame = frame->TraverseNext()) {
8581 EXPECT_TRUE(frame->ToWebLocalFrame()->HasVisibleContent());
8582 }
8583 }
8584
TEST_F(WebFrameTest,HasVisibleContentOnHiddenFrames)8585 TEST_F(WebFrameTest, HasVisibleContentOnHiddenFrames) {
8586 RegisterMockedHttpURLLoad("hidden_frames.html");
8587 frame_test_helpers::WebViewHelper web_view_helper;
8588 WebViewImpl* web_view_impl =
8589 web_view_helper.InitializeAndLoad(base_url_ + "hidden_frames.html");
8590 for (WebFrame* frame = web_view_impl->MainFrameImpl()->TraverseNext(); frame;
8591 frame = frame->TraverseNext()) {
8592 EXPECT_FALSE(frame->ToWebLocalFrame()->HasVisibleContent());
8593 }
8594 }
8595
FetchManifest(Document * document,const KURL & url)8596 static Resource* FetchManifest(Document* document, const KURL& url) {
8597 FetchParameters fetch_parameters =
8598 FetchParameters::CreateForTest(ResourceRequest(url));
8599 fetch_parameters.SetRequestContext(
8600 mojom::blink::RequestContextType::MANIFEST);
8601
8602 return RawResource::FetchSynchronously(fetch_parameters, document->Fetcher());
8603 }
8604
TEST_F(WebFrameTest,ManifestFetch)8605 TEST_F(WebFrameTest, ManifestFetch) {
8606 RegisterMockedHttpURLLoad("foo.html");
8607 RegisterMockedHttpURLLoad("link-manifest-fetch.json");
8608
8609 frame_test_helpers::WebViewHelper web_view_helper;
8610 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
8611 Document* document =
8612 web_view_helper.LocalMainFrame()->GetFrame()->GetDocument();
8613
8614 Resource* resource =
8615 FetchManifest(document, ToKURL(base_url_ + "link-manifest-fetch.json"));
8616
8617 EXPECT_TRUE(resource->IsLoaded());
8618 }
8619
TEST_F(WebFrameTest,ManifestCSPFetchAllow)8620 TEST_F(WebFrameTest, ManifestCSPFetchAllow) {
8621 // TODO(crbug.com/751425): We should use the mock functionality
8622 // via the WebViewHelper instance in each test case.
8623 RegisterMockedURLLoadFromBase(not_base_url_, "link-manifest-fetch.json");
8624 RegisterMockedHttpURLLoadWithCSP("foo.html", "manifest-src *");
8625
8626 frame_test_helpers::WebViewHelper web_view_helper;
8627 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
8628 Document* document =
8629 web_view_helper.LocalMainFrame()->GetFrame()->GetDocument();
8630
8631 Resource* resource = FetchManifest(
8632 document, ToKURL(not_base_url_ + "link-manifest-fetch.json"));
8633
8634 EXPECT_TRUE(resource->IsLoaded());
8635 }
8636
TEST_F(WebFrameTest,ManifestCSPFetchSelf)8637 TEST_F(WebFrameTest, ManifestCSPFetchSelf) {
8638 // TODO(crbug.com/751425): We should use the mock functionality
8639 // via the WebViewHelper instance in each test case.
8640 RegisterMockedURLLoadFromBase(not_base_url_, "link-manifest-fetch.json");
8641 RegisterMockedHttpURLLoadWithCSP("foo.html", "manifest-src 'self'");
8642
8643 frame_test_helpers::WebViewHelper web_view_helper;
8644 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
8645 Document* document =
8646 web_view_helper.LocalMainFrame()->GetFrame()->GetDocument();
8647
8648 Resource* resource = FetchManifest(
8649 document, ToKURL(not_base_url_ + "link-manifest-fetch.json"));
8650
8651 // Fetching resource wasn't allowed.
8652 ASSERT_TRUE(resource);
8653 EXPECT_TRUE(resource->ErrorOccurred());
8654 EXPECT_TRUE(resource->GetResourceError().IsAccessCheck());
8655 }
8656
TEST_F(WebFrameTest,ManifestCSPFetchSelfReportOnly)8657 TEST_F(WebFrameTest, ManifestCSPFetchSelfReportOnly) {
8658 // TODO(crbug.com/751425): We should use the mock functionality
8659 // via the WebViewHelper instance in each test case.
8660 RegisterMockedURLLoadFromBase(not_base_url_, "link-manifest-fetch.json");
8661 RegisterMockedHttpURLLoadWithCSP("foo.html", "manifest-src 'self'",
8662 /* report only */ true);
8663
8664 frame_test_helpers::WebViewHelper web_view_helper;
8665 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
8666 Document* document =
8667 web_view_helper.LocalMainFrame()->GetFrame()->GetDocument();
8668
8669 Resource* resource = FetchManifest(
8670 document, ToKURL(not_base_url_ + "link-manifest-fetch.json"));
8671
8672 EXPECT_TRUE(resource->IsLoaded());
8673 }
8674
TEST_F(WebFrameTest,ReloadBypassingCache)8675 TEST_F(WebFrameTest, ReloadBypassingCache) {
8676 // Check that a reload bypassing cache on a frame will result in the cache
8677 // policy of the request being set to ReloadBypassingCache.
8678 RegisterMockedHttpURLLoad("foo.html");
8679 TestBeginNavigationCacheModeClient client;
8680 frame_test_helpers::WebViewHelper web_view_helper;
8681 web_view_helper.InitializeAndLoad(base_url_ + "foo.html", &client);
8682 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
8683 frame_test_helpers::ReloadFrameBypassingCache(frame);
8684 EXPECT_EQ(mojom::FetchCacheMode::kBypassCache, client.GetCacheMode());
8685 }
8686
NodeImageTestValidation(const IntSize & reference_bitmap_size,DragImage * drag_image)8687 static void NodeImageTestValidation(const IntSize& reference_bitmap_size,
8688 DragImage* drag_image) {
8689 // Prepare the reference bitmap.
8690 SkBitmap bitmap;
8691 bitmap.allocN32Pixels(reference_bitmap_size.Width(),
8692 reference_bitmap_size.Height());
8693 SkCanvas canvas(bitmap, SkSurfaceProps{});
8694 canvas.drawColor(SK_ColorGREEN);
8695
8696 EXPECT_EQ(reference_bitmap_size.Width(), drag_image->Size().Width());
8697 EXPECT_EQ(reference_bitmap_size.Height(), drag_image->Size().Height());
8698 const SkBitmap& drag_bitmap = drag_image->Bitmap();
8699 EXPECT_EQ(0, memcmp(bitmap.getPixels(), drag_bitmap.getPixels(),
8700 bitmap.computeByteSize()));
8701 }
8702
TEST_F(WebFrameTest,NodeImageTestCSSTransformDescendant)8703 TEST_F(WebFrameTest, NodeImageTestCSSTransformDescendant) {
8704 frame_test_helpers::WebViewHelper web_view_helper;
8705 std::unique_ptr<DragImage> drag_image = NodeImageTestSetup(
8706 &web_view_helper, std::string("case-css-3dtransform-descendant"));
8707 EXPECT_TRUE(drag_image);
8708
8709 NodeImageTestValidation(IntSize(40, 40), drag_image.get());
8710 }
8711
TEST_F(WebFrameTest,NodeImageTestCSSTransform)8712 TEST_F(WebFrameTest, NodeImageTestCSSTransform) {
8713 frame_test_helpers::WebViewHelper web_view_helper;
8714 std::unique_ptr<DragImage> drag_image =
8715 NodeImageTestSetup(&web_view_helper, std::string("case-css-transform"));
8716 EXPECT_TRUE(drag_image);
8717
8718 NodeImageTestValidation(IntSize(40, 40), drag_image.get());
8719 }
8720
TEST_F(WebFrameTest,NodeImageTestCSS3DTransform)8721 TEST_F(WebFrameTest, NodeImageTestCSS3DTransform) {
8722 frame_test_helpers::WebViewHelper web_view_helper;
8723 std::unique_ptr<DragImage> drag_image =
8724 NodeImageTestSetup(&web_view_helper, std::string("case-css-3dtransform"));
8725 EXPECT_TRUE(drag_image);
8726
8727 NodeImageTestValidation(IntSize(40, 40), drag_image.get());
8728 }
8729
TEST_F(WebFrameTest,NodeImageTestInlineBlock)8730 TEST_F(WebFrameTest, NodeImageTestInlineBlock) {
8731 frame_test_helpers::WebViewHelper web_view_helper;
8732 std::unique_ptr<DragImage> drag_image =
8733 NodeImageTestSetup(&web_view_helper, std::string("case-inlineblock"));
8734 EXPECT_TRUE(drag_image);
8735
8736 NodeImageTestValidation(IntSize(40, 40), drag_image.get());
8737 }
8738
TEST_F(WebFrameTest,NodeImageTestFloatLeft)8739 TEST_F(WebFrameTest, NodeImageTestFloatLeft) {
8740 frame_test_helpers::WebViewHelper web_view_helper;
8741 std::unique_ptr<DragImage> drag_image = NodeImageTestSetup(
8742 &web_view_helper, std::string("case-float-left-overflow-hidden"));
8743 EXPECT_TRUE(drag_image);
8744
8745 NodeImageTestValidation(IntSize(40, 40), drag_image.get());
8746 }
8747
8748 // Crashes on Android: http://crbug.com/403804
8749 #if defined(OS_ANDROID)
TEST_F(WebFrameTest,DISABLED_PrintingBasic)8750 TEST_F(WebFrameTest, DISABLED_PrintingBasic)
8751 #else
8752 TEST_F(WebFrameTest, PrintingBasic)
8753 #endif
8754 {
8755 frame_test_helpers::WebViewHelper web_view_helper;
8756 web_view_helper.InitializeAndLoad("data:text/html,Hello, world.");
8757
8758 WebLocalFrame* frame = web_view_helper.LocalMainFrame();
8759
8760 WebPrintParams print_params;
8761 print_params.print_content_area.width = 500;
8762 print_params.print_content_area.height = 500;
8763
8764 uint32_t page_count = frame->PrintBegin(print_params, WebNode());
8765 EXPECT_EQ(1u, page_count);
8766 frame->PrintEnd();
8767 }
8768
8769 class ThemeColorTestLocalFrameHost : public FakeLocalFrameHost {
8770 public:
8771 ThemeColorTestLocalFrameHost() = default;
8772 ~ThemeColorTestLocalFrameHost() override = default;
8773
Reset()8774 void Reset() { did_notify_ = false; }
8775
DidNotify() const8776 bool DidNotify() const { return did_notify_; }
8777
8778 private:
8779 // FakeLocalFrameHost:
DidChangeThemeColor(base::Optional<::SkColor> theme_color)8780 void DidChangeThemeColor(base::Optional<::SkColor> theme_color) override {
8781 did_notify_ = true;
8782 }
8783
8784 bool did_notify_ = false;
8785 };
8786
TEST_F(WebFrameTest,ThemeColor)8787 TEST_F(WebFrameTest, ThemeColor) {
8788 RegisterMockedHttpURLLoad("theme_color_test.html");
8789 ThemeColorTestLocalFrameHost host;
8790 frame_test_helpers::TestWebFrameClient client;
8791 host.Init(client.GetRemoteNavigationAssociatedInterfaces());
8792 frame_test_helpers::WebViewHelper web_view_helper;
8793 web_view_helper.InitializeAndLoad(base_url_ + "theme_color_test.html",
8794 &client);
8795 EXPECT_TRUE(host.DidNotify());
8796 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
8797 EXPECT_EQ(Color(0, 0, 255), frame->GetDocument().ThemeColor());
8798 // Change color by rgb.
8799 host.Reset();
8800 frame->ExecuteScript(
8801 WebScriptSource("document.getElementById('tc1').setAttribute('content', "
8802 "'rgb(0, 0, 0)');"));
8803 RunPendingTasks();
8804 EXPECT_TRUE(host.DidNotify());
8805 EXPECT_EQ(Color::kBlack, frame->GetDocument().ThemeColor());
8806 // Change color by hsl.
8807 host.Reset();
8808 frame->ExecuteScript(
8809 WebScriptSource("document.getElementById('tc1').setAttribute('content', "
8810 "'hsl(240,100%, 50%)');"));
8811 RunPendingTasks();
8812 EXPECT_TRUE(host.DidNotify());
8813 EXPECT_EQ(Color(0, 0, 255), frame->GetDocument().ThemeColor());
8814 // Change of second theme-color meta tag will not change frame's theme
8815 // color.
8816 host.Reset();
8817 frame->ExecuteScript(WebScriptSource(
8818 "document.getElementById('tc2').setAttribute('content', '#00FF00');"));
8819 RunPendingTasks();
8820 EXPECT_TRUE(host.DidNotify());
8821 EXPECT_EQ(Color(0, 0, 255), frame->GetDocument().ThemeColor());
8822 // Remove the first theme-color meta tag to apply the second.
8823 host.Reset();
8824 frame->ExecuteScript(
8825 WebScriptSource("document.getElementById('tc1').remove();"));
8826 RunPendingTasks();
8827 EXPECT_TRUE(host.DidNotify());
8828 EXPECT_EQ(Color(0, 255, 0), frame->GetDocument().ThemeColor());
8829 // Remove the name attribute of the remaining meta.
8830 host.Reset();
8831 frame->ExecuteScript(WebScriptSource(
8832 "document.getElementById('tc2').removeAttribute('name');"));
8833 RunPendingTasks();
8834 EXPECT_TRUE(host.DidNotify());
8835 EXPECT_EQ(base::nullopt, frame->GetDocument().ThemeColor());
8836 }
8837
8838 // Make sure that an embedder-triggered detach with a remote frame parent
8839 // doesn't leave behind dangling pointers.
TEST_F(WebFrameTest,EmbedderTriggeredDetachWithRemoteMainFrame)8840 TEST_F(WebFrameTest, EmbedderTriggeredDetachWithRemoteMainFrame) {
8841 frame_test_helpers::WebViewHelper helper;
8842 helper.InitializeRemote();
8843 WebLocalFrame* child_frame =
8844 frame_test_helpers::CreateLocalChild(*helper.RemoteMainFrame());
8845
8846 // Purposely keep the LocalFrame alive so it's the last thing to be destroyed.
8847 Persistent<Frame> child_core_frame = WebFrame::ToCoreFrame(*child_frame);
8848 helper.Reset();
8849 child_core_frame.Clear();
8850 }
8851
8852 class WebFrameSwapTestClient : public frame_test_helpers::TestWebFrameClient {
8853 public:
WebFrameSwapTestClient(WebFrameSwapTestClient * parent=nullptr)8854 explicit WebFrameSwapTestClient(WebFrameSwapTestClient* parent = nullptr) {
8855 local_frame_host_ =
8856 std::make_unique<TestLocalFrameHostForFrameOwnerPropertiesChanges>(
8857 parent);
8858 local_frame_host_->Init(GetRemoteNavigationAssociatedInterfaces());
8859 }
8860
CreateChildFrame(WebLocalFrame * parent,mojom::blink::TreeScopeType scope,const WebString & name,const WebString & fallback_name,const FramePolicy &,const WebFrameOwnerProperties &,mojom::blink::FrameOwnerElementType)8861 WebLocalFrame* CreateChildFrame(
8862 WebLocalFrame* parent,
8863 mojom::blink::TreeScopeType scope,
8864 const WebString& name,
8865 const WebString& fallback_name,
8866 const FramePolicy&,
8867 const WebFrameOwnerProperties&,
8868 mojom::blink::FrameOwnerElementType) override {
8869 return CreateLocalChild(*parent, scope,
8870 std::make_unique<WebFrameSwapTestClient>(this));
8871 }
8872
DidChangeFrameOwnerProperties(mojom::blink::FrameOwnerPropertiesPtr properties)8873 void DidChangeFrameOwnerProperties(
8874 mojom::blink::FrameOwnerPropertiesPtr properties) {
8875 did_propagate_display_none_ |= properties->is_display_none;
8876 }
8877
DidPropagateDisplayNoneProperty() const8878 bool DidPropagateDisplayNoneProperty() const {
8879 return did_propagate_display_none_;
8880 }
8881
8882 private:
8883 class TestLocalFrameHostForFrameOwnerPropertiesChanges
8884 : public FakeLocalFrameHost {
8885 public:
TestLocalFrameHostForFrameOwnerPropertiesChanges(WebFrameSwapTestClient * parent)8886 explicit TestLocalFrameHostForFrameOwnerPropertiesChanges(
8887 WebFrameSwapTestClient* parent)
8888 : parent_(parent) {}
8889 ~TestLocalFrameHostForFrameOwnerPropertiesChanges() override = default;
8890
8891 // FakeLocalFrameHost:
DidChangeFrameOwnerProperties(const base::UnguessableToken & child_frame_token,mojom::blink::FrameOwnerPropertiesPtr properties)8892 void DidChangeFrameOwnerProperties(
8893 const base::UnguessableToken& child_frame_token,
8894 mojom::blink::FrameOwnerPropertiesPtr properties) override {
8895 if (parent_)
8896 parent_->DidChangeFrameOwnerProperties(std::move(properties));
8897 }
8898
8899 bool did_propagate_display_none_ = false;
8900 WebFrameSwapTestClient* parent_ = nullptr;
8901 };
8902
8903 std::unique_ptr<TestLocalFrameHostForFrameOwnerPropertiesChanges>
8904 local_frame_host_;
8905 bool did_propagate_display_none_ = false;
8906 };
8907
8908 class WebFrameSwapTest : public WebFrameTest {
8909 protected:
WebFrameSwapTest()8910 WebFrameSwapTest() {
8911 RegisterMockedHttpURLLoad("frame-a-b-c.html");
8912 RegisterMockedHttpURLLoad("subframe-a.html");
8913 RegisterMockedHttpURLLoad("subframe-b.html");
8914 RegisterMockedHttpURLLoad("subframe-c.html");
8915 RegisterMockedHttpURLLoad("subframe-hello.html");
8916
8917 web_view_helper_.InitializeAndLoad(base_url_ + "frame-a-b-c.html",
8918 &main_frame_client_);
8919 }
8920
Reset()8921 void Reset() { web_view_helper_.Reset(); }
MainFrame() const8922 WebLocalFrame* MainFrame() const { return web_view_helper_.LocalMainFrame(); }
WebView() const8923 WebViewImpl* WebView() const { return web_view_helper_.GetWebView(); }
8924
8925 private:
8926 frame_test_helpers::WebViewHelper web_view_helper_;
8927 WebFrameSwapTestClient main_frame_client_;
8928 };
8929
TEST_F(WebFrameSwapTest,SwapMainFrame)8930 TEST_F(WebFrameSwapTest, SwapMainFrame) {
8931 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
8932 MainFrame()->Swap(remote_frame);
8933
8934 WebLocalFrame* local_frame =
8935 frame_test_helpers::CreateProvisional(*remote_frame);
8936 remote_frame->Swap(local_frame);
8937
8938 // Finally, make sure an embedder triggered load in the local frame swapped
8939 // back in works.
8940 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
8941
8942 std::string content =
8943 WebFrameContentDumper::DumpWebViewAsText(WebView(), 1024).Utf8();
8944 EXPECT_EQ("hello", content);
8945 }
8946
TEST_F(WebFrameSwapTest,ValidateSizeOnRemoteToLocalMainFrameSwap)8947 TEST_F(WebFrameSwapTest, ValidateSizeOnRemoteToLocalMainFrameSwap) {
8948 gfx::Size size(111, 222);
8949
8950 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
8951 MainFrame()->Swap(remote_frame);
8952
8953 remote_frame->View()->Resize(size);
8954
8955 WebLocalFrame* local_frame =
8956 frame_test_helpers::CreateProvisional(*remote_frame);
8957 remote_frame->Swap(local_frame);
8958
8959 // Verify that the size that was set with a remote main frame is correct
8960 // after swapping to a local frame.
8961 Page* page = static_cast<WebViewImpl*>(local_frame->View())
8962 ->GetPage()
8963 ->MainFrame()
8964 ->GetPage();
8965 EXPECT_EQ(size.width(), page->GetVisualViewport().Size().Width());
8966 EXPECT_EQ(size.height(), page->GetVisualViewport().Size().Height());
8967 }
8968
8969 // Verify that size changes to browser controls while the main frame is remote
8970 // are preserved when the main frame swaps to a local frame. See
8971 // https://crbug.com/769321.
TEST_F(WebFrameSwapTest,ValidateBrowserControlsSizeOnRemoteToLocalMainFrameSwap)8972 TEST_F(WebFrameSwapTest,
8973 ValidateBrowserControlsSizeOnRemoteToLocalMainFrameSwap) {
8974 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
8975 MainFrame()->Swap(remote_frame);
8976
8977 // Create a provisional main frame frame but don't swap it in yet.
8978 WebLocalFrame* local_frame =
8979 frame_test_helpers::CreateProvisional(*remote_frame);
8980
8981 WebViewImpl* web_view = static_cast<WebViewImpl*>(local_frame->View());
8982 EXPECT_TRUE(web_view->MainFrame() &&
8983 web_view->MainFrame()->IsWebRemoteFrame());
8984
8985 // Resize the browser controls.
8986 float top_browser_controls_height = 40;
8987 float bottom_browser_controls_height = 60;
8988 web_view->ResizeWithBrowserControls(gfx::Size(100, 100),
8989 top_browser_controls_height,
8990 bottom_browser_controls_height, false);
8991
8992 // Swap the provisional frame in and verify that the browser controls size is
8993 // correct.
8994 remote_frame->Swap(local_frame);
8995 Page* page = static_cast<WebViewImpl*>(local_frame->View())
8996 ->GetPage()
8997 ->MainFrame()
8998 ->GetPage();
8999 EXPECT_EQ(top_browser_controls_height,
9000 page->GetBrowserControls().TopHeight());
9001 EXPECT_EQ(bottom_browser_controls_height,
9002 page->GetBrowserControls().BottomHeight());
9003 }
9004
9005 namespace {
9006
9007 class SwapMainFrameWhenTitleChangesWebFrameClient
9008 : public frame_test_helpers::TestWebFrameClient {
9009 public:
9010 SwapMainFrameWhenTitleChangesWebFrameClient() = default;
9011 ~SwapMainFrameWhenTitleChangesWebFrameClient() override = default;
9012
9013 // frame_test_helpers::TestWebFrameClient:
DidReceiveTitle(const WebString & title)9014 void DidReceiveTitle(const WebString& title) override {
9015 if (title.IsEmpty())
9016 return;
9017
9018 if (!Frame()->Parent())
9019 Frame()->Swap(frame_test_helpers::CreateRemote());
9020 }
9021 };
9022
9023 } // namespace
9024
TEST_F(WebFrameTest,SwapMainFrameWhileLoading)9025 TEST_F(WebFrameTest, SwapMainFrameWhileLoading) {
9026 SwapMainFrameWhenTitleChangesWebFrameClient frame_client;
9027
9028 frame_test_helpers::WebViewHelper web_view_helper;
9029 RegisterMockedHttpURLLoad("frame-a-b-c.html");
9030 RegisterMockedHttpURLLoad("subframe-a.html");
9031 RegisterMockedHttpURLLoad("subframe-b.html");
9032 RegisterMockedHttpURLLoad("subframe-c.html");
9033 RegisterMockedHttpURLLoad("subframe-hello.html");
9034
9035 web_view_helper.InitializeAndLoad(base_url_ + "frame-a-b-c.html",
9036 &frame_client);
9037 }
9038
SwapAndVerifyFirstChildConsistency(const char * const message,WebFrame * parent,WebFrame * new_child)9039 void WebFrameTest::SwapAndVerifyFirstChildConsistency(const char* const message,
9040 WebFrame* parent,
9041 WebFrame* new_child) {
9042 SCOPED_TRACE(message);
9043 parent->FirstChild()->Swap(new_child);
9044
9045 EXPECT_EQ(new_child, parent->FirstChild());
9046 EXPECT_EQ(new_child->Parent(), parent);
9047 EXPECT_EQ(new_child,
9048 parent->LastChild()->PreviousSibling()->PreviousSibling());
9049 EXPECT_EQ(new_child->NextSibling(), parent->LastChild()->PreviousSibling());
9050 }
9051
TEST_F(WebFrameSwapTest,SwapFirstChild)9052 TEST_F(WebFrameSwapTest, SwapFirstChild) {
9053 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9054 SwapAndVerifyFirstChildConsistency("local->remote", MainFrame(),
9055 remote_frame);
9056
9057 WebLocalFrame* local_frame =
9058 frame_test_helpers::CreateProvisional(*remote_frame);
9059 SwapAndVerifyFirstChildConsistency("remote->local", MainFrame(), local_frame);
9060
9061 // FIXME: This almost certainly fires more load events on the iframe element
9062 // than it should.
9063 // Finally, make sure an embedder triggered load in the local frame swapped
9064 // back in works.
9065 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
9066 std::string content =
9067 WebFrameContentDumper::DumpWebViewAsText(WebView(), 1024).Utf8();
9068 EXPECT_EQ(" \n\nhello\n\nb \n\na\n\nc", content);
9069 }
9070
TEST_F(WebFrameSwapTest,DoNotPropagateDisplayNonePropertyOnSwap)9071 TEST_F(WebFrameSwapTest, DoNotPropagateDisplayNonePropertyOnSwap) {
9072 WebFrameSwapTestClient* main_frame_client =
9073 static_cast<WebFrameSwapTestClient*>(MainFrame()->Client());
9074 EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
9075
9076 WebLocalFrame* child_frame = MainFrame()->FirstChild()->ToWebLocalFrame();
9077 frame_test_helpers::LoadFrame(child_frame, "subframe-hello.html");
9078 EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
9079
9080 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9081 child_frame->Swap(remote_frame);
9082 EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
9083
9084 WebLocalFrame* local_frame =
9085 frame_test_helpers::CreateProvisional(*remote_frame);
9086 remote_frame->Swap(local_frame);
9087 EXPECT_FALSE(main_frame_client->DidPropagateDisplayNoneProperty());
9088 Reset();
9089 }
9090
SwapAndVerifyMiddleChildConsistency(const char * const message,WebFrame * parent,WebFrame * new_child)9091 void WebFrameTest::SwapAndVerifyMiddleChildConsistency(
9092 const char* const message,
9093 WebFrame* parent,
9094 WebFrame* new_child) {
9095 SCOPED_TRACE(message);
9096 parent->FirstChild()->NextSibling()->Swap(new_child);
9097
9098 Frame* parent_frame = WebFrame::ToCoreFrame(*parent);
9099 Frame* new_child_frame = WebFrame::ToCoreFrame(*new_child);
9100
9101 EXPECT_EQ(new_child_frame, parent_frame->FirstChild()->NextSibling());
9102 EXPECT_EQ(new_child_frame, parent_frame->LastChild()->PreviousSibling());
9103 EXPECT_EQ(new_child_frame->Parent(), parent_frame);
9104 EXPECT_EQ(new_child_frame, parent_frame->FirstChild()->NextSibling());
9105 EXPECT_EQ(new_child_frame->PreviousSibling(), parent_frame->FirstChild());
9106 EXPECT_EQ(new_child_frame, parent_frame->LastChild()->PreviousSibling());
9107 EXPECT_EQ(new_child_frame->NextSibling(), parent_frame->LastChild());
9108 }
9109
TEST_F(WebFrameSwapTest,SwapMiddleChild)9110 TEST_F(WebFrameSwapTest, SwapMiddleChild) {
9111 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9112 SwapAndVerifyMiddleChildConsistency("local->remote", MainFrame(),
9113 remote_frame);
9114
9115 WebLocalFrame* local_frame =
9116 frame_test_helpers::CreateProvisional(*remote_frame);
9117 SwapAndVerifyMiddleChildConsistency("remote->local", MainFrame(),
9118 local_frame);
9119
9120 // FIXME: This almost certainly fires more load events on the iframe element
9121 // than it should.
9122 // Finally, make sure an embedder triggered load in the local frame swapped
9123 // back in works.
9124 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
9125 std::string content =
9126 WebFrameContentDumper::DumpWebViewAsText(WebView(), 1024).Utf8();
9127 EXPECT_EQ(" \n\na\n\nhello\n\nc", content);
9128 }
9129
SwapAndVerifyLastChildConsistency(const char * const message,WebFrame * parent,WebFrame * new_child)9130 void WebFrameTest::SwapAndVerifyLastChildConsistency(const char* const message,
9131 WebFrame* parent,
9132 WebFrame* new_child) {
9133 SCOPED_TRACE(message);
9134 parent->LastChild()->Swap(new_child);
9135
9136 EXPECT_EQ(new_child, parent->LastChild());
9137 EXPECT_EQ(new_child->Parent(), parent);
9138 EXPECT_EQ(new_child, parent->LastChild()->PreviousSibling()->NextSibling());
9139 EXPECT_EQ(new_child, parent->FirstChild()->NextSibling()->NextSibling());
9140 EXPECT_EQ(new_child->PreviousSibling(), parent->FirstChild()->NextSibling());
9141 }
9142
TEST_F(WebFrameSwapTest,SwapLastChild)9143 TEST_F(WebFrameSwapTest, SwapLastChild) {
9144 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9145 SwapAndVerifyLastChildConsistency("local->remote", MainFrame(), remote_frame);
9146
9147 WebLocalFrame* local_frame =
9148 frame_test_helpers::CreateProvisional(*remote_frame);
9149 SwapAndVerifyLastChildConsistency("remote->local", MainFrame(), local_frame);
9150
9151 // FIXME: This almost certainly fires more load events on the iframe element
9152 // than it should.
9153 // Finally, make sure an embedder triggered load in the local frame swapped
9154 // back in works.
9155 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
9156 std::string content =
9157 WebFrameContentDumper::DumpWebViewAsText(WebView(), 1024).Utf8();
9158 EXPECT_EQ(" \n\na\n\nb \n\na\n\nhello", content);
9159 }
9160
TEST_F(WebFrameSwapTest,DetachProvisionalFrame)9161 TEST_F(WebFrameSwapTest, DetachProvisionalFrame) {
9162 WebRemoteFrameImpl* remote_frame = frame_test_helpers::CreateRemote();
9163 SwapAndVerifyMiddleChildConsistency("local->remote", MainFrame(),
9164 remote_frame);
9165
9166 WebLocalFrameImpl* provisional_frame =
9167 frame_test_helpers::CreateProvisional(*remote_frame);
9168
9169 // The provisional frame should have a local frame owner.
9170 FrameOwner* owner = provisional_frame->GetFrame()->Owner();
9171 ASSERT_TRUE(owner->IsLocal());
9172
9173 // But the owner should point to |remoteFrame|, since the new frame is still
9174 // provisional.
9175 EXPECT_EQ(remote_frame->GetFrame(), owner->ContentFrame());
9176
9177 // After detaching the provisional frame, the frame owner should still point
9178 // at |remoteFrame|.
9179 provisional_frame->Detach();
9180
9181 // The owner should not be affected by detaching the provisional frame, so it
9182 // should still point to |remoteFrame|.
9183 EXPECT_EQ(remote_frame->GetFrame(), owner->ContentFrame());
9184 }
9185
SwapAndVerifySubframeConsistency(const char * const message,WebFrame * old_frame,WebFrame * new_frame)9186 void WebFrameTest::SwapAndVerifySubframeConsistency(const char* const message,
9187 WebFrame* old_frame,
9188 WebFrame* new_frame) {
9189 SCOPED_TRACE(message);
9190
9191 EXPECT_TRUE(old_frame->FirstChild());
9192 old_frame->Swap(new_frame);
9193
9194 EXPECT_FALSE(new_frame->FirstChild());
9195 EXPECT_FALSE(new_frame->LastChild());
9196 }
9197
TEST_F(WebFrameSwapTest,EventsOnDisconnectedSubDocumentSkipped)9198 TEST_F(WebFrameSwapTest, EventsOnDisconnectedSubDocumentSkipped) {
9199 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9200 WebFrame* target_frame = MainFrame()->FirstChild()->NextSibling();
9201 EXPECT_TRUE(target_frame);
9202 SwapAndVerifySubframeConsistency("local->remote", target_frame, remote_frame);
9203 remote_frame->SetReplicatedOrigin(
9204 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9205
9206 WebLocalFrameImpl* local_child = frame_test_helpers::CreateLocalChild(
9207 *remote_frame, "local-inside-remote");
9208
9209 LocalFrame* main_frame = WebView()->MainFrameImpl()->GetFrame();
9210 Document* child_document = local_child->GetFrame()->GetDocument();
9211 EventHandlerRegistry& event_registry =
9212 local_child->GetFrame()->GetEventHandlerRegistry();
9213
9214 // Add the non-connected, but local, child document as having an event.
9215 event_registry.DidAddEventHandler(
9216 *child_document, EventHandlerRegistry::kTouchStartOrMoveEventBlocking);
9217 // Passes if this does not crash or DCHECK.
9218 main_frame->View()->UpdateAllLifecyclePhasesForTest();
9219 }
9220
TEST_F(WebFrameSwapTest,EventsOnDisconnectedElementSkipped)9221 TEST_F(WebFrameSwapTest, EventsOnDisconnectedElementSkipped) {
9222 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9223 WebFrame* target_frame = MainFrame()->FirstChild()->NextSibling();
9224 EXPECT_TRUE(target_frame);
9225 SwapAndVerifySubframeConsistency("local->remote", target_frame, remote_frame);
9226 remote_frame->SetReplicatedOrigin(
9227 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9228
9229 WebLocalFrameImpl* local_child = frame_test_helpers::CreateLocalChild(
9230 *remote_frame, "local-inside-remote");
9231
9232 LocalFrame* main_frame = WebView()->MainFrameImpl()->GetFrame();
9233
9234 // Layout ensures that elements in the local_child frame get LayoutObjects
9235 // attached, but doesn't paint, because the child frame needs to not have
9236 // been composited for the purpose of this test.
9237 local_child->GetFrameView()->UpdateLayout();
9238 Document* child_document = local_child->GetFrame()->GetDocument();
9239 EventHandlerRegistry& event_registry =
9240 local_child->GetFrame()->GetEventHandlerRegistry();
9241
9242 // Add the non-connected body element as having an event.
9243 event_registry.DidAddEventHandler(
9244 *child_document->body(),
9245 EventHandlerRegistry::kTouchStartOrMoveEventBlocking);
9246 // Passes if this does not crash or DCHECK.
9247 main_frame->View()->UpdateAllLifecyclePhasesForTest();
9248 }
9249
TEST_F(WebFrameSwapTest,SwapParentShouldDetachChildren)9250 TEST_F(WebFrameSwapTest, SwapParentShouldDetachChildren) {
9251 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9252 WebFrame* target_frame = MainFrame()->FirstChild()->NextSibling();
9253 EXPECT_TRUE(target_frame);
9254 SwapAndVerifySubframeConsistency("local->remote", target_frame, remote_frame);
9255
9256 target_frame = MainFrame()->FirstChild()->NextSibling();
9257 EXPECT_TRUE(target_frame);
9258
9259 // Create child frames in the target frame before testing the swap.
9260 frame_test_helpers::CreateRemoteChild(*remote_frame);
9261
9262 WebLocalFrame* local_frame =
9263 frame_test_helpers::CreateProvisional(*remote_frame);
9264 SwapAndVerifySubframeConsistency("remote->local", target_frame, local_frame);
9265
9266 // FIXME: This almost certainly fires more load events on the iframe element
9267 // than it should.
9268 // Finally, make sure an embedder triggered load in the local frame swapped
9269 // back in works.
9270 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
9271 std::string content =
9272 WebFrameContentDumper::DumpWebViewAsText(WebView(), 1024).Utf8();
9273 EXPECT_EQ(" \n\na\n\nhello\n\nc", content);
9274 }
9275
TEST_F(WebFrameSwapTest,SwapPreservesGlobalContext)9276 TEST_F(WebFrameSwapTest, SwapPreservesGlobalContext) {
9277 v8::HandleScope scope(v8::Isolate::GetCurrent());
9278 v8::Local<v8::Value> window_top =
9279 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource("window"));
9280 ASSERT_TRUE(window_top->IsObject());
9281 v8::Local<v8::Value> original_window =
9282 MainFrame()->ExecuteScriptAndReturnValue(
9283 WebScriptSource("document.querySelector('#frame2').contentWindow;"));
9284 ASSERT_TRUE(original_window->IsObject());
9285
9286 // Make sure window reference stays the same when swapping to a remote frame.
9287 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9288 WebFrame* target_frame = MainFrame()->FirstChild()->NextSibling();
9289 target_frame->Swap(remote_frame);
9290 v8::Local<v8::Value> remote_window = MainFrame()->ExecuteScriptAndReturnValue(
9291 WebScriptSource("document.querySelector('#frame2').contentWindow;"));
9292 EXPECT_TRUE(original_window->StrictEquals(remote_window));
9293 // Check that its view is consistent with the world.
9294 v8::Local<v8::Value> remote_window_top =
9295 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource(
9296 "document.querySelector('#frame2').contentWindow.top;"));
9297 EXPECT_TRUE(window_top->StrictEquals(remote_window_top));
9298
9299 // Now check that remote -> local works too, since it goes through a different
9300 // code path.
9301 WebLocalFrame* local_frame =
9302 frame_test_helpers::CreateProvisional(*remote_frame);
9303 remote_frame->Swap(local_frame);
9304 v8::Local<v8::Value> local_window = MainFrame()->ExecuteScriptAndReturnValue(
9305 WebScriptSource("document.querySelector('#frame2').contentWindow;"));
9306 EXPECT_TRUE(original_window->StrictEquals(local_window));
9307 v8::Local<v8::Value> local_window_top =
9308 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource(
9309 "document.querySelector('#frame2').contentWindow.top;"));
9310 EXPECT_TRUE(window_top->StrictEquals(local_window_top));
9311 }
9312
TEST_F(WebFrameSwapTest,SetTimeoutAfterSwap)9313 TEST_F(WebFrameSwapTest, SetTimeoutAfterSwap) {
9314 v8::Isolate* isolate = v8::Isolate::GetCurrent();
9315 v8::HandleScope scope(isolate);
9316 MainFrame()->ExecuteScript(
9317 WebScriptSource("savedSetTimeout = window[0].setTimeout"));
9318
9319 // Swap the frame to a remote frame.
9320 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9321 WebFrame* target_frame = MainFrame()->FirstChild();
9322 target_frame->Swap(remote_frame);
9323 remote_frame->SetReplicatedOrigin(
9324 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9325
9326 // Invoking setTimeout should throw a security error.
9327 {
9328 v8::Local<v8::Value> exception = MainFrame()->ExecuteScriptAndReturnValue(
9329 WebScriptSource("try {\n"
9330 " savedSetTimeout.call(window[0], () => {}, 0);\n"
9331 "} catch (e) { e; }"));
9332 ASSERT_TRUE(!exception.IsEmpty());
9333 EXPECT_EQ(
9334 "SecurityError: Blocked a frame with origin \"http://internal.test\" "
9335 "from accessing a cross-origin frame.",
9336 ToCoreString(exception
9337 ->ToString(ToScriptStateForMainWorld(
9338 WebView()->MainFrameImpl()->GetFrame())
9339 ->GetContext())
9340 .ToLocalChecked()));
9341 }
9342 }
9343
TEST_F(WebFrameSwapTest,SwapInitializesGlobal)9344 TEST_F(WebFrameSwapTest, SwapInitializesGlobal) {
9345 v8::HandleScope scope(v8::Isolate::GetCurrent());
9346
9347 v8::Local<v8::Value> window_top =
9348 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource("window"));
9349 ASSERT_TRUE(window_top->IsObject());
9350
9351 v8::Local<v8::Value> last_child = MainFrame()->ExecuteScriptAndReturnValue(
9352 WebScriptSource("saved = window[2]"));
9353 ASSERT_TRUE(last_child->IsObject());
9354
9355 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9356 MainFrame()->LastChild()->Swap(remote_frame);
9357 v8::Local<v8::Value> remote_window_top =
9358 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource("saved.top"));
9359 EXPECT_TRUE(remote_window_top->IsObject());
9360 EXPECT_TRUE(window_top->StrictEquals(remote_window_top));
9361
9362 WebLocalFrame* local_frame =
9363 frame_test_helpers::CreateProvisional(*remote_frame);
9364 remote_frame->Swap(local_frame);
9365 v8::Local<v8::Value> local_window_top =
9366 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource("saved.top"));
9367 EXPECT_TRUE(local_window_top->IsObject());
9368 EXPECT_TRUE(window_top->StrictEquals(local_window_top));
9369 }
9370
TEST_F(WebFrameSwapTest,RemoteFramesAreIndexable)9371 TEST_F(WebFrameSwapTest, RemoteFramesAreIndexable) {
9372 v8::HandleScope scope(v8::Isolate::GetCurrent());
9373
9374 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9375 MainFrame()->LastChild()->Swap(remote_frame);
9376 v8::Local<v8::Value> remote_window =
9377 MainFrame()->ExecuteScriptAndReturnValue(WebScriptSource("window[2]"));
9378 EXPECT_TRUE(remote_window->IsObject());
9379 v8::Local<v8::Value> window_length = MainFrame()->ExecuteScriptAndReturnValue(
9380 WebScriptSource("window.length"));
9381 ASSERT_TRUE(window_length->IsInt32());
9382 EXPECT_EQ(3, window_length.As<v8::Int32>()->Value());
9383 }
9384
TEST_F(WebFrameSwapTest,RemoteFrameLengthAccess)9385 TEST_F(WebFrameSwapTest, RemoteFrameLengthAccess) {
9386 v8::HandleScope scope(v8::Isolate::GetCurrent());
9387
9388 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9389 MainFrame()->LastChild()->Swap(remote_frame);
9390 v8::Local<v8::Value> remote_window_length =
9391 MainFrame()->ExecuteScriptAndReturnValue(
9392 WebScriptSource("window[2].length"));
9393 ASSERT_TRUE(remote_window_length->IsInt32());
9394 EXPECT_EQ(0, remote_window_length.As<v8::Int32>()->Value());
9395 }
9396
TEST_F(WebFrameSwapTest,RemoteWindowNamedAccess)9397 TEST_F(WebFrameSwapTest, RemoteWindowNamedAccess) {
9398 v8::HandleScope scope(v8::Isolate::GetCurrent());
9399
9400 // TODO(dcheng): Once OOPIF unit test infrastructure is in place, test that
9401 // named window access on a remote window works. For now, just test that
9402 // accessing a named property doesn't crash.
9403 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9404 MainFrame()->LastChild()->Swap(remote_frame);
9405 remote_frame->SetReplicatedOrigin(
9406 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9407 v8::Local<v8::Value> remote_window_property =
9408 MainFrame()->ExecuteScriptAndReturnValue(
9409 WebScriptSource("window[2].foo"));
9410 EXPECT_TRUE(remote_window_property.IsEmpty());
9411 }
9412
TEST_F(WebFrameSwapTest,RemoteWindowToString)9413 TEST_F(WebFrameSwapTest, RemoteWindowToString) {
9414 v8::Isolate* isolate = v8::Isolate::GetCurrent();
9415 v8::HandleScope scope(isolate);
9416
9417 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9418 MainFrame()->LastChild()->Swap(remote_frame);
9419 v8::Local<v8::Value> to_string_result =
9420 MainFrame()->ExecuteScriptAndReturnValue(
9421 WebScriptSource("Object.prototype.toString.call(window[2])"));
9422 ASSERT_FALSE(to_string_result.IsEmpty());
9423 EXPECT_STREQ("[object Object]",
9424 *v8::String::Utf8Value(isolate, to_string_result));
9425 }
9426
9427 // TODO(alexmos, dcheng): This test and some other OOPIF tests use
9428 // very little of the test fixture support in WebFrameSwapTest. We should
9429 // clean these tests up.
TEST_F(WebFrameSwapTest,FramesOfRemoteParentAreIndexable)9430 TEST_F(WebFrameSwapTest, FramesOfRemoteParentAreIndexable) {
9431 v8::HandleScope scope(v8::Isolate::GetCurrent());
9432
9433 WebRemoteFrame* remote_parent_frame = frame_test_helpers::CreateRemote();
9434 MainFrame()->Swap(remote_parent_frame);
9435 remote_parent_frame->SetReplicatedOrigin(
9436 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9437
9438 WebLocalFrame* child_frame =
9439 frame_test_helpers::CreateLocalChild(*remote_parent_frame);
9440 frame_test_helpers::LoadFrame(child_frame, base_url_ + "subframe-hello.html");
9441
9442 v8::Local<v8::Value> window =
9443 child_frame->ExecuteScriptAndReturnValue(WebScriptSource("window"));
9444 v8::Local<v8::Value> child_of_remote_parent =
9445 child_frame->ExecuteScriptAndReturnValue(
9446 WebScriptSource("parent.frames[0]"));
9447 EXPECT_TRUE(child_of_remote_parent->IsObject());
9448 EXPECT_TRUE(window->StrictEquals(child_of_remote_parent));
9449
9450 v8::Local<v8::Value> window_length = child_frame->ExecuteScriptAndReturnValue(
9451 WebScriptSource("parent.frames.length"));
9452 ASSERT_TRUE(window_length->IsInt32());
9453 EXPECT_EQ(1, window_length.As<v8::Int32>()->Value());
9454 }
9455
9456 // Check that frames with a remote parent don't crash while accessing
9457 // window.frameElement.
TEST_F(WebFrameSwapTest,FrameElementInFramesWithRemoteParent)9458 TEST_F(WebFrameSwapTest, FrameElementInFramesWithRemoteParent) {
9459 v8::HandleScope scope(v8::Isolate::GetCurrent());
9460
9461 WebRemoteFrame* remote_parent_frame = frame_test_helpers::CreateRemote();
9462 MainFrame()->Swap(remote_parent_frame);
9463 remote_parent_frame->SetReplicatedOrigin(
9464 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9465
9466 WebLocalFrame* child_frame =
9467 frame_test_helpers::CreateLocalChild(*remote_parent_frame);
9468 frame_test_helpers::LoadFrame(child_frame, base_url_ + "subframe-hello.html");
9469
9470 v8::Local<v8::Value> frame_element = child_frame->ExecuteScriptAndReturnValue(
9471 WebScriptSource("window.frameElement"));
9472 // frameElement should be null if cross-origin.
9473 ASSERT_FALSE(frame_element.IsEmpty());
9474 EXPECT_TRUE(frame_element->IsNull());
9475 }
9476
9477 class RemoteToLocalSwapWebFrameClient
9478 : public frame_test_helpers::TestWebFrameClient {
9479 public:
RemoteToLocalSwapWebFrameClient(WebRemoteFrame * remote_frame)9480 explicit RemoteToLocalSwapWebFrameClient(WebRemoteFrame* remote_frame)
9481 : history_commit_type_(kWebHistoryInertCommit),
9482 remote_frame_(remote_frame) {}
9483 ~RemoteToLocalSwapWebFrameClient() override = default;
9484
9485 // frame_test_helpers::TestWebFrameClient:
DidCommitNavigation(const WebHistoryItem &,WebHistoryCommitType history_commit_type,bool)9486 void DidCommitNavigation(const WebHistoryItem&,
9487 WebHistoryCommitType history_commit_type,
9488 bool) override {
9489 history_commit_type_ = history_commit_type;
9490 remote_frame_->Swap(Frame());
9491 }
9492
HistoryCommitType() const9493 WebHistoryCommitType HistoryCommitType() const {
9494 return history_commit_type_;
9495 }
9496
9497 WebHistoryCommitType history_commit_type_;
9498 WebRemoteFrame* remote_frame_;
9499 };
9500
9501 // The commit type should be Initial if we are swapping a RemoteFrame to a
9502 // LocalFrame as it is first being created. This happens when another frame
9503 // exists in the same process, such that we create the RemoteFrame before the
9504 // first navigation occurs.
TEST_F(WebFrameSwapTest,HistoryCommitTypeAfterNewRemoteToLocalSwap)9505 TEST_F(WebFrameSwapTest, HistoryCommitTypeAfterNewRemoteToLocalSwap) {
9506 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9507 WebFrame* target_frame = MainFrame()->FirstChild();
9508 ASSERT_TRUE(target_frame);
9509 target_frame->Swap(remote_frame);
9510 ASSERT_TRUE(MainFrame()->FirstChild());
9511 ASSERT_EQ(MainFrame()->FirstChild(), remote_frame);
9512
9513 RemoteToLocalSwapWebFrameClient client(remote_frame);
9514 WebLocalFrame* local_frame =
9515 frame_test_helpers::CreateProvisional(*remote_frame, &client);
9516 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
9517 EXPECT_EQ(kWebHistoryInertCommit, client.HistoryCommitType());
9518
9519 // Manually reset to break WebViewHelper's dependency on the stack allocated
9520 // TestWebFrameClient.
9521 Reset();
9522 }
9523
9524 // The commit type should be Standard if we are swapping a RemoteFrame to a
9525 // LocalFrame after commits have already happened in the frame. The browser
9526 // process will inform us via setCommittedFirstRealLoad.
TEST_F(WebFrameSwapTest,HistoryCommitTypeAfterExistingRemoteToLocalSwap)9527 TEST_F(WebFrameSwapTest, HistoryCommitTypeAfterExistingRemoteToLocalSwap) {
9528 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote();
9529 WebFrame* target_frame = MainFrame()->FirstChild();
9530 ASSERT_TRUE(target_frame);
9531 target_frame->Swap(remote_frame);
9532 ASSERT_TRUE(MainFrame()->FirstChild());
9533 ASSERT_EQ(MainFrame()->FirstChild(), remote_frame);
9534
9535 RemoteToLocalSwapWebFrameClient client(remote_frame);
9536 WebLocalFrameImpl* local_frame =
9537 frame_test_helpers::CreateProvisional(*remote_frame, &client);
9538 local_frame->SetCommittedFirstRealLoad();
9539 frame_test_helpers::LoadFrame(local_frame, base_url_ + "subframe-hello.html");
9540 EXPECT_EQ(kWebStandardCommit, client.HistoryCommitType());
9541
9542 // Manually reset to break WebViewHelper's dependency on the stack allocated
9543 // TestWebFrameClient.
9544 Reset();
9545 }
9546
9547 class RemoteNavigationClient
9548 : public frame_test_helpers::TestWebRemoteFrameClient {
9549 public:
9550 RemoteNavigationClient() = default;
9551 ~RemoteNavigationClient() override = default;
9552
9553 // frame_test_helpers::TestWebRemoteFrameClient:
Navigate(const WebURLRequest & request,blink::WebLocalFrame * initiator_frame,bool should_replace_current_entry,bool is_opener_navigation,bool initiator_frame_has_download_sandbox_flag,bool blocking_downloads_in_sandbox_enabled,bool initiator_frame_is_ad,CrossVariantMojoRemote<mojom::blink::BlobURLTokenInterfaceBase>,const base::Optional<WebImpression> & impression)9554 void Navigate(const WebURLRequest& request,
9555 blink::WebLocalFrame* initiator_frame,
9556 bool should_replace_current_entry,
9557 bool is_opener_navigation,
9558 bool initiator_frame_has_download_sandbox_flag,
9559 bool blocking_downloads_in_sandbox_enabled,
9560 bool initiator_frame_is_ad,
9561 CrossVariantMojoRemote<mojom::blink::BlobURLTokenInterfaceBase>,
9562 const base::Optional<WebImpression>& impression) override {
9563 last_request_.CopyFrom(request);
9564 }
9565
LastRequest() const9566 const WebURLRequest& LastRequest() const { return last_request_; }
9567
9568 private:
9569 WebURLRequest last_request_;
9570 };
9571
TEST_F(WebFrameSwapTest,NavigateRemoteFrameViaLocation)9572 TEST_F(WebFrameSwapTest, NavigateRemoteFrameViaLocation) {
9573 RemoteNavigationClient client;
9574 WebRemoteFrame* remote_frame = frame_test_helpers::CreateRemote(&client);
9575 WebFrame* target_frame = MainFrame()->FirstChild();
9576 ASSERT_TRUE(target_frame);
9577 target_frame->Swap(remote_frame);
9578 ASSERT_TRUE(MainFrame()->FirstChild());
9579 ASSERT_EQ(MainFrame()->FirstChild(), remote_frame);
9580
9581 remote_frame->SetReplicatedOrigin(
9582 WebSecurityOrigin::CreateFromString("http://127.0.0.1"), false);
9583 MainFrame()->ExecuteScript(
9584 WebScriptSource("document.getElementsByTagName('iframe')[0]."
9585 "contentWindow.location = 'data:text/html,hi'"));
9586 ASSERT_FALSE(client.LastRequest().IsNull());
9587 EXPECT_EQ(WebURL(ToKURL("data:text/html,hi")), client.LastRequest().Url());
9588
9589 // Manually reset to break WebViewHelper's dependency on the stack allocated
9590 // TestWebFrameClient.
9591 Reset();
9592 }
9593
TEST_F(WebFrameSwapTest,WindowOpenOnRemoteFrame)9594 TEST_F(WebFrameSwapTest, WindowOpenOnRemoteFrame) {
9595 RemoteNavigationClient remote_client;
9596 WebRemoteFrame* remote_frame =
9597 frame_test_helpers::CreateRemote(&remote_client);
9598 MainFrame()->FirstChild()->Swap(remote_frame);
9599 remote_frame->SetReplicatedOrigin(
9600 WebSecurityOrigin(SecurityOrigin::CreateUniqueOpaque()), false);
9601
9602 ASSERT_TRUE(MainFrame()->FirstChild()->IsWebRemoteFrame());
9603 LocalDOMWindow* main_window =
9604 To<WebLocalFrameImpl>(MainFrame())->GetFrame()->DomWindow();
9605
9606 String destination = "data:text/html:destination";
9607 NonThrowableExceptionState exception_state;
9608 ScriptState* script_state =
9609 ToScriptStateForMainWorld(main_window->GetFrame());
9610 ScriptState::Scope entered_context_scope(script_state);
9611 v8::Context::BackupIncumbentScope incumbent_context_scope(
9612 script_state->GetContext());
9613 main_window->open(script_state->GetIsolate(), destination, "frame1", "",
9614 exception_state);
9615 ASSERT_FALSE(remote_client.LastRequest().IsNull());
9616 EXPECT_EQ(remote_client.LastRequest().Url(), WebURL(KURL(destination)));
9617
9618 // Pointing a named frame to an empty URL should just return a reference to
9619 // the frame's window without navigating it.
9620 DOMWindow* result = main_window->open(script_state->GetIsolate(), "",
9621 "frame1", "", exception_state);
9622 EXPECT_EQ(remote_client.LastRequest().Url(), WebURL(KURL(destination)));
9623 EXPECT_EQ(result, WebFrame::ToCoreFrame(*remote_frame)->DomWindow());
9624
9625 Reset();
9626 }
9627
9628 // blink::mojom::RemoteMainFrameHost instance that intecepts CloseWindowSoon()
9629 // mojo calls and provides a getter to know if it was ever called.
9630 class TestRemoteMainFrameHostForWindowClose : public FakeRemoteMainFrameHost {
9631 public:
9632 TestRemoteMainFrameHostForWindowClose() = default;
9633 ~TestRemoteMainFrameHostForWindowClose() override = default;
9634
9635 // FakeRemoteMainFrameHost:
RouteCloseEvent()9636 void RouteCloseEvent() override { remote_window_closed_ = true; }
9637
remote_window_closed() const9638 bool remote_window_closed() const { return remote_window_closed_; }
9639
9640 private:
9641 bool remote_window_closed_ = false;
9642 };
9643
9644 class RemoteWindowCloseTest : public WebFrameTest {
9645 public:
RemoteWindowCloseTest()9646 RemoteWindowCloseTest() {
9647 remote_main_frame_host_.Init(
9648 remote_frame_client_.GetRemoteAssociatedInterfaces());
9649 }
9650
9651 ~RemoteWindowCloseTest() override = default;
9652
remote_frame_client()9653 frame_test_helpers::TestWebRemoteFrameClient* remote_frame_client() {
9654 return &remote_frame_client_;
9655 }
9656
Closed() const9657 bool Closed() const { return remote_main_frame_host_.remote_window_closed(); }
9658
9659 private:
9660 TestRemoteMainFrameHostForWindowClose remote_main_frame_host_;
9661 frame_test_helpers::TestWebRemoteFrameClient remote_frame_client_;
9662 };
9663
TEST_F(RemoteWindowCloseTest,WindowOpenRemoteClose)9664 TEST_F(RemoteWindowCloseTest, WindowOpenRemoteClose) {
9665 frame_test_helpers::WebViewHelper main_web_view;
9666 main_web_view.Initialize();
9667
9668 // Create a remote window that will be closed later in the test.
9669 frame_test_helpers::WebViewHelper popup;
9670 popup.InitializeRemote(remote_frame_client(), nullptr, nullptr);
9671 popup.GetWebView()->DidAttachRemoteMainFrame();
9672
9673 LocalFrame* local_frame = main_web_view.LocalMainFrame()->GetFrame();
9674 RemoteFrame* remote_frame = popup.RemoteMainFrame()->GetFrame();
9675
9676 remote_frame->SetOpenerDoNotNotify(local_frame);
9677
9678 // Attempt to close the window, which should fail as it isn't opened
9679 // by a script.
9680 ScriptState* local_script_state = ToScriptStateForMainWorld(local_frame);
9681 ScriptState::Scope entered_context_scope(local_script_state);
9682 v8::Context::BackupIncumbentScope incumbent_context_scope(
9683 local_script_state->GetContext());
9684 remote_frame->DomWindow()->close(local_script_state->GetIsolate());
9685 EXPECT_FALSE(Closed());
9686
9687 // Marking it as opened by a script should now allow it to be closed.
9688 remote_frame->GetPage()->SetOpenedByDOM();
9689 remote_frame->DomWindow()->close(local_script_state->GetIsolate());
9690
9691 // The request to close the remote window is not immediately sent to make sure
9692 // that the JS finishes executing, so we need to wait for pending tasks first.
9693 RunPendingTasks();
9694 EXPECT_TRUE(Closed());
9695 }
9696
TEST_F(WebFrameTest,NavigateRemoteToLocalWithOpener)9697 TEST_F(WebFrameTest, NavigateRemoteToLocalWithOpener) {
9698 frame_test_helpers::WebViewHelper main_web_view;
9699 main_web_view.Initialize();
9700 WebLocalFrame* main_frame = main_web_view.LocalMainFrame();
9701
9702 // Create a popup with a remote frame and set its opener to the main frame.
9703 frame_test_helpers::WebViewHelper popup_helper;
9704 popup_helper.InitializeRemoteWithOpener(
9705 main_frame, nullptr, SecurityOrigin::CreateFromString("http://foo.com"));
9706 WebRemoteFrame* popup_remote_frame = popup_helper.RemoteMainFrame();
9707 EXPECT_FALSE(main_frame->GetSecurityOrigin().CanAccess(
9708 popup_remote_frame->GetSecurityOrigin()));
9709
9710 // Do a remote-to-local swap in the popup.
9711 WebLocalFrame* popup_local_frame =
9712 frame_test_helpers::CreateProvisional(*popup_remote_frame);
9713 popup_remote_frame->Swap(popup_local_frame);
9714
9715 // The initial document created during the remote-to-local swap should have
9716 // inherited its opener's SecurityOrigin.
9717 EXPECT_TRUE(main_frame->GetSecurityOrigin().CanAccess(
9718 popup_helper.LocalMainFrame()->GetSecurityOrigin()));
9719 }
9720
TEST_F(WebFrameTest,SwapWithOpenerCycle)9721 TEST_F(WebFrameTest, SwapWithOpenerCycle) {
9722 // First, create a remote main frame with itself as the opener.
9723 frame_test_helpers::WebViewHelper helper;
9724 helper.InitializeRemote();
9725 WebRemoteFrame* remote_frame = helper.RemoteMainFrame();
9726 WebFrame::ToCoreFrame(*helper.RemoteMainFrame())
9727 ->SetOpenerDoNotNotify(WebFrame::ToCoreFrame(*remote_frame));
9728
9729 // Now swap in a local frame. It shouldn't crash.
9730 WebLocalFrame* local_frame =
9731 frame_test_helpers::CreateProvisional(*remote_frame);
9732 remote_frame->Swap(local_frame);
9733
9734 // And the opener cycle should still be preserved.
9735 EXPECT_EQ(local_frame, local_frame->Opener());
9736 }
9737
9738 class CommitTypeWebFrameClient : public frame_test_helpers::TestWebFrameClient {
9739 public:
CommitTypeWebFrameClient()9740 CommitTypeWebFrameClient() : history_commit_type_(kWebHistoryInertCommit) {}
9741 ~CommitTypeWebFrameClient() override = default;
9742
9743 // frame_test_helpers::TestWebFrameClient:
DidCommitNavigation(const WebHistoryItem &,WebHistoryCommitType history_commit_type,bool)9744 void DidCommitNavigation(const WebHistoryItem&,
9745 WebHistoryCommitType history_commit_type,
9746 bool) override {
9747 history_commit_type_ = history_commit_type;
9748 }
9749
HistoryCommitType() const9750 WebHistoryCommitType HistoryCommitType() const {
9751 return history_commit_type_;
9752 }
9753
9754 private:
9755 WebHistoryCommitType history_commit_type_;
9756 };
9757
TEST_F(WebFrameTest,RemoteFrameInitialCommitType)9758 TEST_F(WebFrameTest, RemoteFrameInitialCommitType) {
9759 frame_test_helpers::WebViewHelper helper;
9760 helper.InitializeRemote(nullptr, SecurityOrigin::CreateFromString(
9761 WebString::FromUTF8(base_url_)));
9762
9763 // If an iframe has a remote main frame, ensure the inital commit is correctly
9764 // identified as kWebHistoryInertCommit.
9765 CommitTypeWebFrameClient child_frame_client;
9766 WebLocalFrame* child_frame = frame_test_helpers::CreateLocalChild(
9767 *helper.RemoteMainFrame(), "frameName", WebFrameOwnerProperties(),
9768 nullptr, &child_frame_client);
9769 RegisterMockedHttpURLLoad("foo.html");
9770 frame_test_helpers::LoadFrame(child_frame, base_url_ + "foo.html");
9771 EXPECT_EQ(kWebHistoryInertCommit, child_frame_client.HistoryCommitType());
9772
9773 helper.Reset();
9774 }
9775
TEST_F(WebFrameTest,DetachRemoteFrame)9776 TEST_F(WebFrameTest, DetachRemoteFrame) {
9777 frame_test_helpers::WebViewHelper helper;
9778 helper.InitializeRemote();
9779 WebRemoteFrame* child_frame =
9780 frame_test_helpers::CreateRemoteChild(*helper.RemoteMainFrame());
9781 child_frame->Detach();
9782 }
9783
9784 class TestConsoleMessageWebFrameClient
9785 : public frame_test_helpers::TestWebFrameClient {
9786 public:
9787 TestConsoleMessageWebFrameClient() = default;
9788 ~TestConsoleMessageWebFrameClient() override = default;
9789
9790 // frame_test_helpers::TestWebFrameClient:
DidAddMessageToConsole(const WebConsoleMessage & message,const WebString & source_name,unsigned source_line,const WebString & stack_trace)9791 void DidAddMessageToConsole(const WebConsoleMessage& message,
9792 const WebString& source_name,
9793 unsigned source_line,
9794 const WebString& stack_trace) override {
9795 messages.push_back(message);
9796 }
9797
9798 Vector<WebConsoleMessage> messages;
9799 };
9800
TEST_F(WebFrameTest,CrossDomainAccessErrorsUseCallingWindow)9801 TEST_F(WebFrameTest, CrossDomainAccessErrorsUseCallingWindow) {
9802 RegisterMockedHttpURLLoad("hidden_frames.html");
9803 RegisterMockedChromeURLLoad("hello_world.html");
9804
9805 frame_test_helpers::WebViewHelper web_view_helper;
9806 TestConsoleMessageWebFrameClient web_frame_client;
9807 web_view_helper.InitializeAndLoad(base_url_ + "hidden_frames.html",
9808 &web_frame_client);
9809
9810 // Create another window with a cross-origin page, and point its opener to
9811 // first window.
9812 frame_test_helpers::WebViewHelper popup_web_view_helper;
9813 TestConsoleMessageWebFrameClient popup_web_frame_client;
9814 WebViewImpl* popup_view = popup_web_view_helper.InitializeAndLoad(
9815 chrome_url_ + "hello_world.html", &popup_web_frame_client);
9816 WebFrame::ToCoreFrame(*popup_view->MainFrame())
9817 ->SetOpenerDoNotNotify(
9818 WebFrame::ToCoreFrame(*web_view_helper.GetWebView()->MainFrame()));
9819
9820 // Attempt a blocked navigation of an opener's subframe, and ensure that
9821 // the error shows up on the popup (calling) window's console, rather than
9822 // the target window.
9823 popup_view->MainFrameImpl()->ExecuteScript(WebScriptSource(
9824 "try { opener.frames[1].location.href='data:text/html,foo'; } catch (e) "
9825 "{}"));
9826 EXPECT_TRUE(web_frame_client.messages.IsEmpty());
9827 ASSERT_EQ(1u, popup_web_frame_client.messages.size());
9828 EXPECT_TRUE(std::string::npos !=
9829 popup_web_frame_client.messages[0].text.Utf8().find(
9830 "Unsafe JavaScript attempt to initiate navigation"));
9831
9832 // Try setting a cross-origin iframe element's source to a javascript: URL,
9833 // and check that this error is also printed on the calling window.
9834 popup_view->MainFrameImpl()->ExecuteScript(
9835 WebScriptSource("opener.document.querySelectorAll('iframe')[1].src='"
9836 "javascript:alert()'"));
9837 EXPECT_TRUE(web_frame_client.messages.IsEmpty());
9838 ASSERT_EQ(2u, popup_web_frame_client.messages.size());
9839 EXPECT_TRUE(
9840 std::string::npos !=
9841 popup_web_frame_client.messages[1].text.Utf8().find("Blocked a frame"));
9842
9843 // Manually reset to break WebViewHelpers' dependencies on the stack
9844 // allocated WebLocalFrameClients.
9845 web_view_helper.Reset();
9846 popup_web_view_helper.Reset();
9847 }
9848
TEST_F(WebFrameTest,ResizeInvalidatesDeviceMediaQueries)9849 TEST_F(WebFrameTest, ResizeInvalidatesDeviceMediaQueries) {
9850 RegisterMockedHttpURLLoad("device_media_queries.html");
9851 FixedLayoutTestWebWidgetClient client;
9852 frame_test_helpers::WebViewHelper web_view_helper;
9853 web_view_helper.InitializeAndLoad(base_url_ + "device_media_queries.html",
9854 nullptr, nullptr, &client,
9855 ConfigureAndroid);
9856 auto* frame =
9857 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
9858 Element* element = frame->GetDocument()->getElementById("test");
9859 ASSERT_TRUE(element);
9860
9861 client.screen_info_.available_rect = gfx::Rect(700, 500);
9862 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
9863 client.screen_info_.available_rect.width(),
9864 client.screen_info_.available_rect.height());
9865 EXPECT_EQ(300, element->OffsetWidth());
9866 EXPECT_EQ(300, element->OffsetHeight());
9867
9868 client.screen_info_.available_rect = gfx::Rect(710, 500);
9869 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
9870 client.screen_info_.available_rect.width(),
9871 client.screen_info_.available_rect.height());
9872 EXPECT_EQ(400, element->OffsetWidth());
9873 EXPECT_EQ(300, element->OffsetHeight());
9874
9875 client.screen_info_.available_rect = gfx::Rect(690, 500);
9876 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
9877 client.screen_info_.available_rect.width(),
9878 client.screen_info_.available_rect.height());
9879 EXPECT_EQ(200, element->OffsetWidth());
9880 EXPECT_EQ(300, element->OffsetHeight());
9881
9882 client.screen_info_.available_rect = gfx::Rect(700, 510);
9883 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
9884 client.screen_info_.available_rect.width(),
9885 client.screen_info_.available_rect.height());
9886 EXPECT_EQ(300, element->OffsetWidth());
9887 EXPECT_EQ(400, element->OffsetHeight());
9888
9889 client.screen_info_.available_rect = gfx::Rect(700, 490);
9890 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
9891 client.screen_info_.available_rect.width(),
9892 client.screen_info_.available_rect.height());
9893 EXPECT_EQ(300, element->OffsetWidth());
9894 EXPECT_EQ(200, element->OffsetHeight());
9895
9896 client.screen_info_.available_rect = gfx::Rect(690, 510);
9897 UpdateScreenInfoAndResizeView(&client, &web_view_helper,
9898 client.screen_info_.available_rect.width(),
9899 client.screen_info_.available_rect.height());
9900 EXPECT_EQ(200, element->OffsetWidth());
9901 EXPECT_EQ(400, element->OffsetHeight());
9902 }
9903
9904 class DeviceEmulationTest : public WebFrameTest {
9905 protected:
DeviceEmulationTest()9906 DeviceEmulationTest() {
9907 RegisterMockedHttpURLLoad("device_emulation.html");
9908 client_.screen_info_.device_scale_factor = 1;
9909 web_view_helper_.InitializeAndLoad(base_url_ + "device_emulation.html",
9910 nullptr, nullptr, &client_);
9911 }
9912
TestResize(const gfx::Size & size,const String & expected_size)9913 void TestResize(const gfx::Size& size, const String& expected_size) {
9914 client_.screen_info_.available_rect = gfx::Rect(size);
9915 UpdateScreenInfoAndResizeView(&client_, &web_view_helper_,
9916 client_.screen_info_.available_rect.width(),
9917 client_.screen_info_.available_rect.height());
9918 EXPECT_EQ(expected_size, DumpSize("test"));
9919 }
9920
DumpSize(const String & id)9921 String DumpSize(const String& id) {
9922 String code = "dumpSize('" + id + "')";
9923 v8::HandleScope scope(v8::Isolate::GetCurrent());
9924 ScriptExecutionCallbackHelper callback_helper(
9925 web_view_helper_.LocalMainFrame()->MainWorldScriptContext());
9926 web_view_helper_.GetWebView()
9927 ->MainFrameImpl()
9928 ->RequestExecuteScriptAndReturnValue(WebScriptSource(WebString(code)),
9929 false, &callback_helper);
9930 RunPendingTasks();
9931 EXPECT_TRUE(callback_helper.DidComplete());
9932 return callback_helper.StringValue();
9933 }
9934
9935 FixedLayoutTestWebWidgetClient client_;
9936 frame_test_helpers::WebViewHelper web_view_helper_;
9937 };
9938
TEST_F(DeviceEmulationTest,DeviceSizeInvalidatedOnResize)9939 TEST_F(DeviceEmulationTest, DeviceSizeInvalidatedOnResize) {
9940 DeviceEmulationParams params;
9941 params.screen_type = mojom::EmulatedScreenType::kMobile;
9942 web_view_helper_.GetWebView()->EnableDeviceEmulation(params);
9943
9944 TestResize(gfx::Size(700, 500), "300x300");
9945 TestResize(gfx::Size(710, 500), "400x300");
9946 TestResize(gfx::Size(690, 500), "200x300");
9947 TestResize(gfx::Size(700, 510), "300x400");
9948 TestResize(gfx::Size(700, 490), "300x200");
9949 TestResize(gfx::Size(710, 510), "400x400");
9950 TestResize(gfx::Size(690, 490), "200x200");
9951 TestResize(gfx::Size(800, 600), "400x400");
9952
9953 web_view_helper_.GetWebView()->DisableDeviceEmulation();
9954 }
9955
TEST_F(DeviceEmulationTest,PointerAndHoverTypes)9956 TEST_F(DeviceEmulationTest, PointerAndHoverTypes) {
9957 web_view_helper_.GetWebView()
9958 ->GetDevToolsEmulator()
9959 ->SetTouchEventEmulationEnabled(true, 1);
9960 EXPECT_EQ("20x20", DumpSize("pointer"));
9961 web_view_helper_.GetWebView()
9962 ->GetDevToolsEmulator()
9963 ->SetTouchEventEmulationEnabled(false, 1);
9964 }
9965
TEST_F(WebFrameTest,CreateLocalChildWithPreviousSibling)9966 TEST_F(WebFrameTest, CreateLocalChildWithPreviousSibling) {
9967 frame_test_helpers::WebViewHelper helper;
9968 helper.InitializeRemote();
9969 WebRemoteFrame* parent = helper.RemoteMainFrame();
9970
9971 WebLocalFrame* second_frame(
9972 frame_test_helpers::CreateLocalChild(*parent, "name2"));
9973 WebLocalFrame* fourth_frame(frame_test_helpers::CreateLocalChild(
9974 *parent, "name4", WebFrameOwnerProperties(), second_frame));
9975 WebLocalFrame* third_frame(frame_test_helpers::CreateLocalChild(
9976 *parent, "name3", WebFrameOwnerProperties(), second_frame));
9977 WebLocalFrame* first_frame(
9978 frame_test_helpers::CreateLocalChild(*parent, "name1"));
9979
9980 EXPECT_EQ(first_frame, parent->FirstChild());
9981 EXPECT_EQ(nullptr, first_frame->PreviousSibling());
9982 EXPECT_EQ(second_frame, first_frame->NextSibling());
9983
9984 EXPECT_EQ(first_frame, second_frame->PreviousSibling());
9985 EXPECT_EQ(third_frame, second_frame->NextSibling());
9986
9987 EXPECT_EQ(second_frame, third_frame->PreviousSibling());
9988 EXPECT_EQ(fourth_frame, third_frame->NextSibling());
9989
9990 EXPECT_EQ(third_frame, fourth_frame->PreviousSibling());
9991 EXPECT_EQ(nullptr, fourth_frame->NextSibling());
9992 EXPECT_EQ(fourth_frame, parent->LastChild());
9993
9994 EXPECT_EQ(parent, first_frame->Parent());
9995 EXPECT_EQ(parent, second_frame->Parent());
9996 EXPECT_EQ(parent, third_frame->Parent());
9997 EXPECT_EQ(parent, fourth_frame->Parent());
9998 }
9999
TEST_F(WebFrameTest,SendBeaconFromChildWithRemoteMainFrame)10000 TEST_F(WebFrameTest, SendBeaconFromChildWithRemoteMainFrame) {
10001 frame_test_helpers::WebViewHelper helper;
10002 helper.InitializeRemote();
10003
10004 WebLocalFrame* local_frame =
10005 frame_test_helpers::CreateLocalChild(*helper.RemoteMainFrame());
10006
10007 // Finally, make sure an embedder triggered load in the local frame swapped
10008 // back in works.
10009 RegisterMockedHttpURLLoad("send_beacon.html");
10010 RegisterMockedHttpURLLoad("reload_post.html"); // url param to sendBeacon()
10011 frame_test_helpers::LoadFrame(local_frame, base_url_ + "send_beacon.html");
10012 // Wait for the post.
10013 frame_test_helpers::PumpPendingRequestsForFrameToLoad(local_frame);
10014 }
10015
TEST_F(WebFrameTest,SiteForCookiesFromChildWithRemoteMainFrame)10016 TEST_F(WebFrameTest, SiteForCookiesFromChildWithRemoteMainFrame) {
10017 frame_test_helpers::WebViewHelper helper;
10018 helper.InitializeRemote(nullptr,
10019 SecurityOrigin::Create(ToKURL(not_base_url_)));
10020
10021 WebLocalFrame* local_frame =
10022 frame_test_helpers::CreateLocalChild(*helper.RemoteMainFrame());
10023
10024 RegisterMockedHttpURLLoad("foo.html");
10025 frame_test_helpers::LoadFrame(local_frame, base_url_ + "foo.html");
10026 EXPECT_TRUE(local_frame->GetDocument().SiteForCookies().IsNull());
10027
10028 SchemeRegistry::RegisterURLSchemeAsFirstPartyWhenTopLevel("http");
10029 EXPECT_TRUE(net::SiteForCookies::FromUrl(ToKURL(not_base_url_))
10030 .IsEquivalent(local_frame->GetDocument().SiteForCookies()));
10031 SchemeRegistry::RemoveURLSchemeAsFirstPartyWhenTopLevel("http");
10032 }
10033
10034 // See https://crbug.com/525285.
TEST_F(WebFrameTest,RemoteToLocalSwapOnMainFrameInitializesCoreFrame)10035 TEST_F(WebFrameTest, RemoteToLocalSwapOnMainFrameInitializesCoreFrame) {
10036 frame_test_helpers::WebViewHelper helper;
10037 helper.InitializeRemote();
10038
10039 frame_test_helpers::CreateLocalChild(*helper.RemoteMainFrame());
10040
10041 // Do a remote-to-local swap of the top frame.
10042 WebLocalFrame* local_root =
10043 frame_test_helpers::CreateProvisional(*helper.RemoteMainFrame());
10044 helper.RemoteMainFrame()->Swap(local_root);
10045
10046 // Load a page with a child frame in the new root to make sure this doesn't
10047 // crash when the child frame invokes setCoreFrame.
10048 RegisterMockedHttpURLLoad("single_iframe.html");
10049 RegisterMockedHttpURLLoad("visible_iframe.html");
10050 frame_test_helpers::LoadFrame(local_root, base_url_ + "single_iframe.html");
10051 }
10052
10053 // See https://crbug.com/628942.
TEST_F(WebFrameTest,PausedPageLoadWithRemoteMainFrame)10054 TEST_F(WebFrameTest, PausedPageLoadWithRemoteMainFrame) {
10055 frame_test_helpers::WebViewHelper helper;
10056 helper.InitializeRemote();
10057 WebRemoteFrameImpl* remote_root = helper.RemoteMainFrame();
10058
10059 // Check that ScopedPagePauser properly triggers deferred loading for
10060 // the current Page.
10061 Page* page = remote_root->GetFrame()->GetPage();
10062 EXPECT_FALSE(page->Paused());
10063 {
10064 ScopedPagePauser pauser;
10065 EXPECT_TRUE(page->Paused());
10066 }
10067 EXPECT_FALSE(page->Paused());
10068
10069 // Repeat this for a page with a local child frame, and ensure that the
10070 // child frame's loads are also suspended.
10071 WebLocalFrameImpl* web_local_child =
10072 frame_test_helpers::CreateLocalChild(*remote_root);
10073 RegisterMockedHttpURLLoad("foo.html");
10074 frame_test_helpers::LoadFrame(web_local_child, base_url_ + "foo.html");
10075 LocalFrame* local_child = web_local_child->GetFrame();
10076 EXPECT_FALSE(page->Paused());
10077 EXPECT_FALSE(
10078 local_child->GetDocument()->Fetcher()->GetProperties().IsPaused());
10079 {
10080 ScopedPagePauser pauser;
10081 EXPECT_TRUE(page->Paused());
10082 EXPECT_TRUE(
10083 local_child->GetDocument()->Fetcher()->GetProperties().IsPaused());
10084 }
10085 EXPECT_FALSE(page->Paused());
10086 EXPECT_FALSE(
10087 local_child->GetDocument()->Fetcher()->GetProperties().IsPaused());
10088 }
10089
10090 class OverscrollWidgetInputHandlerHost
10091 : public frame_test_helpers::TestWidgetInputHandlerHost {
10092 public:
10093 MOCK_METHOD5(DidOverscroll,
10094 void(const gfx::Vector2dF&,
10095 const gfx::Vector2dF&,
10096 const gfx::PointF&,
10097 const gfx::Vector2dF&,
10098 cc::OverscrollBehavior));
10099
DidOverscroll(mojom::blink::DidOverscrollParamsPtr params)10100 void DidOverscroll(mojom::blink::DidOverscrollParamsPtr params) override {
10101 DidOverscroll(params->latest_overscroll_delta,
10102 params->accumulated_overscroll,
10103 params->causal_event_viewport_point,
10104 params->current_fling_velocity, params->overscroll_behavior);
10105 }
10106 };
10107
10108 class OverscrollWebWidgetClient
10109 : public frame_test_helpers::TestWebWidgetClient {
10110 public:
10111 OverscrollWebWidgetClient() = default;
10112
GetInputHandlerHost()10113 frame_test_helpers::TestWidgetInputHandlerHost* GetInputHandlerHost()
10114 override {
10115 return &input_handler_host_;
10116 }
10117
GetOverscrollWidgetInputHandlerHost()10118 OverscrollWidgetInputHandlerHost& GetOverscrollWidgetInputHandlerHost() {
10119 return input_handler_host_;
10120 }
10121
10122 private:
10123 OverscrollWidgetInputHandlerHost input_handler_host_;
10124 };
10125
10126 class WebFrameOverscrollTest
10127 : public WebFrameTest,
10128 public testing::WithParamInterface<WebGestureDevice> {
10129 public:
WebFrameOverscrollTest()10130 WebFrameOverscrollTest() {}
10131
10132 protected:
GenerateEvent(WebInputEvent::Type type,float delta_x=0.0,float delta_y=0.0)10133 WebCoalescedInputEvent GenerateEvent(WebInputEvent::Type type,
10134 float delta_x = 0.0,
10135 float delta_y = 0.0) {
10136 WebGestureEvent event(type, WebInputEvent::kNoModifiers,
10137 WebInputEvent::GetStaticTimeStampForTests(),
10138 GetParam());
10139 // TODO(wjmaclean): Make sure that touchpad device is only ever used for
10140 // gesture scrolling event types.
10141 event.SetPositionInWidget(gfx::PointF(100, 100));
10142 if (type == WebInputEvent::Type::kGestureScrollUpdate) {
10143 event.data.scroll_update.delta_x = delta_x;
10144 event.data.scroll_update.delta_y = delta_y;
10145 } else if (type == WebInputEvent::Type::kGestureScrollBegin) {
10146 event.data.scroll_begin.delta_x_hint = delta_x;
10147 event.data.scroll_begin.delta_y_hint = delta_y;
10148 }
10149 return WebCoalescedInputEvent(event, ui::LatencyInfo());
10150 }
10151
ScrollBegin(frame_test_helpers::WebViewHelper * web_view_helper,float delta_x_hint,float delta_y_hint)10152 void ScrollBegin(frame_test_helpers::WebViewHelper* web_view_helper,
10153 float delta_x_hint,
10154 float delta_y_hint) {
10155 web_view_helper->GetWebView()->MainFrameWidget()->HandleInputEvent(
10156 GenerateEvent(WebInputEvent::Type::kGestureScrollBegin, delta_x_hint,
10157 delta_y_hint));
10158 }
10159
ScrollUpdate(frame_test_helpers::WebViewHelper * web_view_helper,float delta_x,float delta_y)10160 void ScrollUpdate(frame_test_helpers::WebViewHelper* web_view_helper,
10161 float delta_x,
10162 float delta_y) {
10163 web_view_helper->GetWebView()->MainFrameWidget()->HandleInputEvent(
10164 GenerateEvent(WebInputEvent::Type::kGestureScrollUpdate, delta_x,
10165 delta_y));
10166 }
10167
ScrollEnd(frame_test_helpers::WebViewHelper * web_view_helper)10168 void ScrollEnd(frame_test_helpers::WebViewHelper* web_view_helper) {
10169 web_view_helper->GetWebView()->MainFrameWidget()->HandleInputEvent(
10170 GenerateEvent(WebInputEvent::Type::kGestureScrollEnd));
10171 }
10172 };
10173
10174 INSTANTIATE_TEST_SUITE_P(All,
10175 WebFrameOverscrollTest,
10176 testing::Values(WebGestureDevice::kTouchpad,
10177 WebGestureDevice::kTouchscreen));
10178
TEST_P(WebFrameOverscrollTest,AccumulatedRootOverscrollAndUnsedDeltaValuesOnOverscroll)10179 TEST_P(WebFrameOverscrollTest,
10180 AccumulatedRootOverscrollAndUnsedDeltaValuesOnOverscroll) {
10181 OverscrollWebWidgetClient client;
10182 RegisterMockedHttpURLLoad("overscroll/overscroll.html");
10183 frame_test_helpers::WebViewHelper web_view_helper;
10184
10185 web_view_helper.InitializeAndLoad(base_url_ + "overscroll/overscroll.html",
10186 nullptr, nullptr, &client,
10187 ConfigureAndroid);
10188 web_view_helper.Resize(gfx::Size(200, 200));
10189
10190 // Calculation of accumulatedRootOverscroll and unusedDelta on multiple
10191 // scrollUpdate.
10192 ScrollBegin(&web_view_helper, -300, -316);
10193 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10194 DidOverscroll(gfx::Vector2dF(8, 16), gfx::Vector2dF(8, 16),
10195 gfx::PointF(100, 100), gfx::Vector2dF(),
10196 kOverscrollBehaviorAuto));
10197 ScrollUpdate(&web_view_helper, -308, -316);
10198 base::RunLoop().RunUntilIdle();
10199 Mock::VerifyAndClearExpectations(
10200 &client.GetOverscrollWidgetInputHandlerHost());
10201
10202 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10203 DidOverscroll(gfx::Vector2dF(0, 13), gfx::Vector2dF(8, 29),
10204 gfx::PointF(100, 100), gfx::Vector2dF(),
10205 kOverscrollBehaviorAuto));
10206 ScrollUpdate(&web_view_helper, 0, -13);
10207 base::RunLoop().RunUntilIdle();
10208 Mock::VerifyAndClearExpectations(
10209 &client.GetOverscrollWidgetInputHandlerHost());
10210
10211 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10212 DidOverscroll(gfx::Vector2dF(20, 13), gfx::Vector2dF(28, 42),
10213 gfx::PointF(100, 100), gfx::Vector2dF(),
10214 kOverscrollBehaviorAuto));
10215 ScrollUpdate(&web_view_helper, -20, -13);
10216 base::RunLoop().RunUntilIdle();
10217 Mock::VerifyAndClearExpectations(
10218 &client.GetOverscrollWidgetInputHandlerHost());
10219
10220 // Overscroll is not reported.
10221 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10222 DidOverscroll(_, _, _, _, _))
10223 .Times(0);
10224 ScrollUpdate(&web_view_helper, 0, 1);
10225 base::RunLoop().RunUntilIdle();
10226 Mock::VerifyAndClearExpectations(
10227 &client.GetOverscrollWidgetInputHandlerHost());
10228
10229 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10230 DidOverscroll(_, _, _, _, _))
10231 .Times(0);
10232 ScrollUpdate(&web_view_helper, 1, 0);
10233 base::RunLoop().RunUntilIdle();
10234 Mock::VerifyAndClearExpectations(
10235 &client.GetOverscrollWidgetInputHandlerHost());
10236
10237 // Overscroll is reported.
10238 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10239 DidOverscroll(gfx::Vector2dF(0, -701), gfx::Vector2dF(0, -701),
10240 gfx::PointF(100, 100), gfx::Vector2dF(),
10241 kOverscrollBehaviorAuto));
10242 ScrollUpdate(&web_view_helper, 0, 1000);
10243 base::RunLoop().RunUntilIdle();
10244 Mock::VerifyAndClearExpectations(
10245 &client.GetOverscrollWidgetInputHandlerHost());
10246
10247 // Overscroll is not reported.
10248 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10249 DidOverscroll(_, _, _, _, _))
10250 .Times(0);
10251 ScrollEnd(&web_view_helper);
10252 base::RunLoop().RunUntilIdle();
10253 Mock::VerifyAndClearExpectations(
10254 &client.GetOverscrollWidgetInputHandlerHost());
10255 }
10256
TEST_P(WebFrameOverscrollTest,AccumulatedOverscrollAndUnusedDeltaValuesOnDifferentAxesOverscroll)10257 TEST_P(WebFrameOverscrollTest,
10258 AccumulatedOverscrollAndUnusedDeltaValuesOnDifferentAxesOverscroll) {
10259 OverscrollWebWidgetClient client;
10260 RegisterMockedHttpURLLoad("overscroll/div-overscroll.html");
10261 frame_test_helpers::WebViewHelper web_view_helper;
10262
10263 web_view_helper.InitializeAndLoad(
10264 base_url_ + "overscroll/div-overscroll.html", nullptr, nullptr, &client,
10265 ConfigureAndroid);
10266 web_view_helper.Resize(gfx::Size(200, 200));
10267
10268 ScrollBegin(&web_view_helper, 0, -316);
10269
10270 // Scroll the Div to the end.
10271 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10272 DidOverscroll(_, _, _, _, _))
10273 .Times(0);
10274 ScrollUpdate(&web_view_helper, 0, -316);
10275 base::RunLoop().RunUntilIdle();
10276 Mock::VerifyAndClearExpectations(
10277 &client.GetOverscrollWidgetInputHandlerHost());
10278
10279 ScrollEnd(&web_view_helper);
10280 ScrollBegin(&web_view_helper, 0, -100);
10281
10282 // Now On Scrolling DIV, scroll is bubbled and root layer is over-scrolled.
10283 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10284 DidOverscroll(gfx::Vector2dF(0, 100), gfx::Vector2dF(0, 100),
10285 gfx::PointF(100, 100), gfx::Vector2dF(),
10286 kOverscrollBehaviorAuto));
10287 ScrollUpdate(&web_view_helper, 0, -100);
10288 ScrollUpdate(&web_view_helper, 0, -100);
10289 base::RunLoop().RunUntilIdle();
10290 Mock::VerifyAndClearExpectations(
10291 &client.GetOverscrollWidgetInputHandlerHost());
10292
10293 // TODO(bokan): This has never worked but by the accident that this test was
10294 // being run in a WebView without a size. This test should be fixed along with
10295 // the bug, crbug.com/589320.
10296 // Page scrolls vertically, but over-scrolls horizontally.
10297 // EXPECT_CALL(client, didOverscroll(gfx::Vector2dF(-100, 0),
10298 // gfx::Vector2dF(-100, 0), gfx::PointF(100, 100), gfx::Vector2dF()));
10299 // ScrollUpdate(&webViewHelper, 100, 50);
10300 // Mock::VerifyAndClearExpectations(&client);
10301
10302 // Scrolling up, Overscroll is not reported.
10303 // EXPECT_CALL(client, didOverscroll(_, _, _, _)).Times(0);
10304 // ScrollUpdate(&webViewHelper, 0, -50);
10305 // Mock::VerifyAndClearExpectations(&client);
10306
10307 // Page scrolls horizontally, but over-scrolls vertically.
10308 // EXPECT_CALL(client, didOverscroll(gfx::Vector2dF(0, 100), gfx::Vector2dF(0,
10309 // 100), gfx::PointF(100, 100), gfx::Vector2dF()));
10310 // ScrollUpdate(&webViewHelper, -100, -100);
10311 // Mock::VerifyAndClearExpectations(&client);
10312 }
10313
TEST_P(WebFrameOverscrollTest,RootLayerOverscrolledOnInnerDivOverScroll)10314 TEST_P(WebFrameOverscrollTest, RootLayerOverscrolledOnInnerDivOverScroll) {
10315 OverscrollWebWidgetClient client;
10316 RegisterMockedHttpURLLoad("overscroll/div-overscroll.html");
10317 frame_test_helpers::WebViewHelper web_view_helper;
10318
10319 web_view_helper.InitializeAndLoad(
10320 base_url_ + "overscroll/div-overscroll.html", nullptr, nullptr, &client,
10321 ConfigureAndroid);
10322 web_view_helper.Resize(gfx::Size(200, 200));
10323
10324 ScrollBegin(&web_view_helper, 0, -316);
10325
10326 // Scroll the Div to the end.
10327 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10328 DidOverscroll(_, _, _, _, _))
10329 .Times(0);
10330 ScrollUpdate(&web_view_helper, 0, -316);
10331 base::RunLoop().RunUntilIdle();
10332 Mock::VerifyAndClearExpectations(
10333 &client.GetOverscrollWidgetInputHandlerHost());
10334
10335 ScrollEnd(&web_view_helper);
10336 ScrollBegin(&web_view_helper, 0, -150);
10337
10338 // Now On Scrolling DIV, scroll is bubbled and root layer is over-scrolled.
10339 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10340 DidOverscroll(gfx::Vector2dF(0, 50), gfx::Vector2dF(0, 50),
10341 gfx::PointF(100, 100), gfx::Vector2dF(),
10342 kOverscrollBehaviorAuto));
10343 ScrollUpdate(&web_view_helper, 0, -150);
10344 base::RunLoop().RunUntilIdle();
10345 Mock::VerifyAndClearExpectations(
10346 &client.GetOverscrollWidgetInputHandlerHost());
10347 }
10348
TEST_P(WebFrameOverscrollTest,RootLayerOverscrolledOnInnerIFrameOverScroll)10349 TEST_P(WebFrameOverscrollTest, RootLayerOverscrolledOnInnerIFrameOverScroll) {
10350 OverscrollWebWidgetClient client;
10351 RegisterMockedHttpURLLoad("overscroll/iframe-overscroll.html");
10352 RegisterMockedHttpURLLoad("overscroll/scrollable-iframe.html");
10353 frame_test_helpers::WebViewHelper web_view_helper;
10354
10355 web_view_helper.InitializeAndLoad(
10356 base_url_ + "overscroll/iframe-overscroll.html", nullptr, nullptr,
10357 &client, ConfigureAndroid);
10358 web_view_helper.Resize(gfx::Size(200, 200));
10359
10360 ScrollBegin(&web_view_helper, 0, -320);
10361 // Scroll the IFrame to the end.
10362 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10363 DidOverscroll(_, _, _, _, _))
10364 .Times(0);
10365
10366 // This scroll will fully scroll the iframe but will be consumed before being
10367 // counted as overscroll.
10368 ScrollUpdate(&web_view_helper, 0, -320);
10369
10370 // This scroll will again target the iframe but wont bubble further up. Make
10371 // sure that the unused scroll isn't handled as overscroll.
10372 ScrollUpdate(&web_view_helper, 0, -50);
10373 base::RunLoop().RunUntilIdle();
10374 Mock::VerifyAndClearExpectations(
10375 &client.GetOverscrollWidgetInputHandlerHost());
10376
10377 ScrollEnd(&web_view_helper);
10378 ScrollBegin(&web_view_helper, 0, -150);
10379
10380 // Now On Scrolling IFrame, scroll is bubbled and root layer is over-scrolled.
10381 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10382 DidOverscroll(gfx::Vector2dF(0, 50), gfx::Vector2dF(0, 50),
10383 gfx::PointF(100, 100), gfx::Vector2dF(),
10384 kOverscrollBehaviorAuto));
10385 ScrollUpdate(&web_view_helper, 0, -150);
10386 base::RunLoop().RunUntilIdle();
10387 Mock::VerifyAndClearExpectations(
10388 &client.GetOverscrollWidgetInputHandlerHost());
10389
10390 ScrollEnd(&web_view_helper);
10391 }
10392
TEST_P(WebFrameOverscrollTest,ScaledPageRootLayerOverscrolled)10393 TEST_P(WebFrameOverscrollTest, ScaledPageRootLayerOverscrolled) {
10394 OverscrollWebWidgetClient client;
10395 RegisterMockedHttpURLLoad("overscroll/overscroll.html");
10396 frame_test_helpers::WebViewHelper web_view_helper;
10397
10398 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
10399 base_url_ + "overscroll/overscroll.html", nullptr, nullptr, &client,
10400 ConfigureAndroid);
10401 web_view_helper.Resize(gfx::Size(200, 200));
10402 web_view_impl->SetPageScaleFactor(3.0);
10403
10404 // Calculation of accumulatedRootOverscroll and unusedDelta on scaled page.
10405 // The point is (99, 99) because we clamp in the division by 3 to 33 so when
10406 // we go back to viewport coordinates it becomes (99, 99).
10407 ScrollBegin(&web_view_helper, 0, 30);
10408 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10409 DidOverscroll(gfx::Vector2dF(0, -30), gfx::Vector2dF(0, -30),
10410 gfx::PointF(99, 99), gfx::Vector2dF(),
10411 kOverscrollBehaviorAuto));
10412 ScrollUpdate(&web_view_helper, 0, 30);
10413 base::RunLoop().RunUntilIdle();
10414 Mock::VerifyAndClearExpectations(
10415 &client.GetOverscrollWidgetInputHandlerHost());
10416
10417 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10418 DidOverscroll(gfx::Vector2dF(0, -30), gfx::Vector2dF(0, -60),
10419 gfx::PointF(99, 99), gfx::Vector2dF(),
10420 kOverscrollBehaviorAuto));
10421 ScrollUpdate(&web_view_helper, 0, 30);
10422 base::RunLoop().RunUntilIdle();
10423 Mock::VerifyAndClearExpectations(
10424 &client.GetOverscrollWidgetInputHandlerHost());
10425
10426 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10427 DidOverscroll(gfx::Vector2dF(-30, -30), gfx::Vector2dF(-30, -90),
10428 gfx::PointF(99, 99), gfx::Vector2dF(),
10429 kOverscrollBehaviorAuto));
10430 ScrollUpdate(&web_view_helper, 30, 30);
10431 base::RunLoop().RunUntilIdle();
10432 Mock::VerifyAndClearExpectations(
10433 &client.GetOverscrollWidgetInputHandlerHost());
10434
10435 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10436 DidOverscroll(gfx::Vector2dF(-30, 0), gfx::Vector2dF(-60, -90),
10437 gfx::PointF(99, 99), gfx::Vector2dF(),
10438 kOverscrollBehaviorAuto));
10439 ScrollUpdate(&web_view_helper, 30, 0);
10440 base::RunLoop().RunUntilIdle();
10441 Mock::VerifyAndClearExpectations(
10442 &client.GetOverscrollWidgetInputHandlerHost());
10443
10444 // Overscroll is not reported.
10445 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10446 DidOverscroll(_, _, _, _, _))
10447 .Times(0);
10448 ScrollEnd(&web_view_helper);
10449 base::RunLoop().RunUntilIdle();
10450 Mock::VerifyAndClearExpectations(
10451 &client.GetOverscrollWidgetInputHandlerHost());
10452 }
10453
TEST_P(WebFrameOverscrollTest,NoOverscrollForSmallvalues)10454 TEST_P(WebFrameOverscrollTest, NoOverscrollForSmallvalues) {
10455 OverscrollWebWidgetClient client;
10456 RegisterMockedHttpURLLoad("overscroll/overscroll.html");
10457 frame_test_helpers::WebViewHelper web_view_helper;
10458
10459 web_view_helper.InitializeAndLoad(base_url_ + "overscroll/overscroll.html",
10460 nullptr, nullptr, &client,
10461 ConfigureAndroid);
10462 web_view_helper.Resize(gfx::Size(200, 200));
10463
10464 ScrollBegin(&web_view_helper, 10, 10);
10465 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10466 DidOverscroll(gfx::Vector2dF(-10, -10), gfx::Vector2dF(-10, -10),
10467 gfx::PointF(100, 100), gfx::Vector2dF(),
10468 kOverscrollBehaviorAuto));
10469 ScrollUpdate(&web_view_helper, 10, 10);
10470 base::RunLoop().RunUntilIdle();
10471 Mock::VerifyAndClearExpectations(
10472 &client.GetOverscrollWidgetInputHandlerHost());
10473
10474 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10475 DidOverscroll(gfx::Vector2dF(0, -0.10),
10476 gfx::Vector2dF(-10, -10.10), gfx::PointF(100, 100),
10477 gfx::Vector2dF(), kOverscrollBehaviorAuto));
10478 ScrollUpdate(&web_view_helper, 0, 0.10);
10479 base::RunLoop().RunUntilIdle();
10480 Mock::VerifyAndClearExpectations(
10481 &client.GetOverscrollWidgetInputHandlerHost());
10482
10483 EXPECT_CALL(
10484 client.GetOverscrollWidgetInputHandlerHost(),
10485 DidOverscroll(gfx::Vector2dF(-0.10, 0), gfx::Vector2dF(-10.10, -10.10),
10486 gfx::PointF(100, 100), gfx::Vector2dF(),
10487 kOverscrollBehaviorAuto));
10488 ScrollUpdate(&web_view_helper, 0.10, 0);
10489 base::RunLoop().RunUntilIdle();
10490 Mock::VerifyAndClearExpectations(
10491 &client.GetOverscrollWidgetInputHandlerHost());
10492
10493 // For residual values overscrollDelta should be reset and DidOverscroll
10494 // shouldn't be called.
10495 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10496 DidOverscroll(_, _, _, _, _))
10497 .Times(0);
10498 ScrollUpdate(&web_view_helper, 0, 0.09);
10499 base::RunLoop().RunUntilIdle();
10500 Mock::VerifyAndClearExpectations(
10501 &client.GetOverscrollWidgetInputHandlerHost());
10502
10503 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10504 DidOverscroll(_, _, _, _, _))
10505 .Times(0);
10506 ScrollUpdate(&web_view_helper, 0.09, 0.09);
10507 base::RunLoop().RunUntilIdle();
10508 Mock::VerifyAndClearExpectations(
10509 &client.GetOverscrollWidgetInputHandlerHost());
10510
10511 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10512 DidOverscroll(_, _, _, _, _))
10513 .Times(0);
10514 ScrollUpdate(&web_view_helper, 0.09, 0);
10515 base::RunLoop().RunUntilIdle();
10516 Mock::VerifyAndClearExpectations(
10517 &client.GetOverscrollWidgetInputHandlerHost());
10518
10519 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10520 DidOverscroll(_, _, _, _, _))
10521 .Times(0);
10522 ScrollUpdate(&web_view_helper, 0, -0.09);
10523 base::RunLoop().RunUntilIdle();
10524 Mock::VerifyAndClearExpectations(
10525 &client.GetOverscrollWidgetInputHandlerHost());
10526
10527 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10528 DidOverscroll(_, _, _, _, _))
10529 .Times(0);
10530 ScrollUpdate(&web_view_helper, -0.09, -0.09);
10531 base::RunLoop().RunUntilIdle();
10532 Mock::VerifyAndClearExpectations(
10533 &client.GetOverscrollWidgetInputHandlerHost());
10534
10535 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10536 DidOverscroll(_, _, _, _, _))
10537 .Times(0);
10538 ScrollUpdate(&web_view_helper, -0.09, 0);
10539 base::RunLoop().RunUntilIdle();
10540 Mock::VerifyAndClearExpectations(
10541 &client.GetOverscrollWidgetInputHandlerHost());
10542
10543 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10544 DidOverscroll(_, _, _, _, _))
10545 .Times(0);
10546 ScrollEnd(&web_view_helper);
10547 base::RunLoop().RunUntilIdle();
10548 Mock::VerifyAndClearExpectations(
10549 &client.GetOverscrollWidgetInputHandlerHost());
10550 }
10551
TEST_P(WebFrameOverscrollTest,OverscrollBehaviorGoesToCompositor)10552 TEST_P(WebFrameOverscrollTest, OverscrollBehaviorGoesToCompositor) {
10553 OverscrollWebWidgetClient client;
10554 RegisterMockedHttpURLLoad("overscroll/overscroll.html");
10555 frame_test_helpers::WebViewHelper web_view_helper;
10556
10557 web_view_helper.InitializeAndLoad(base_url_ + "overscroll/overscroll.html",
10558 nullptr, nullptr, &client,
10559 ConfigureAndroid);
10560 web_view_helper.Resize(gfx::Size(200, 200));
10561
10562 WebLocalFrame* mainFrame =
10563 web_view_helper.GetWebView()->MainFrame()->ToWebLocalFrame();
10564 EXPECT_EQ(web_view_helper.GetLayerTreeHost()->overscroll_behavior(),
10565 kOverscrollBehaviorAuto);
10566 mainFrame->ExecuteScript(
10567 WebScriptSource(WebString("document.body.style="
10568 "'overscroll-behavior: auto;'")));
10569 ScrollBegin(&web_view_helper, 100, 116);
10570 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10571 DidOverscroll(gfx::Vector2dF(-100, -100),
10572 gfx::Vector2dF(-100, -100), gfx::PointF(100, 100),
10573 gfx::Vector2dF(), kOverscrollBehaviorAuto));
10574 ScrollUpdate(&web_view_helper, 100, 100);
10575 base::RunLoop().RunUntilIdle();
10576 Mock::VerifyAndClearExpectations(
10577 &client.GetOverscrollWidgetInputHandlerHost());
10578 EXPECT_EQ(web_view_helper.GetLayerTreeHost()->overscroll_behavior(),
10579 kOverscrollBehaviorAuto);
10580
10581 mainFrame->ExecuteScript(
10582 WebScriptSource(WebString("document.body.style="
10583 "'overscroll-behavior: contain;'")));
10584 ScrollBegin(&web_view_helper, 100, 116);
10585 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10586 DidOverscroll(gfx::Vector2dF(-100, -100),
10587 gfx::Vector2dF(-200, -200), gfx::PointF(100, 100),
10588 gfx::Vector2dF(), kOverscrollBehaviorContain));
10589 ScrollUpdate(&web_view_helper, 100, 100);
10590 base::RunLoop().RunUntilIdle();
10591 Mock::VerifyAndClearExpectations(
10592 &client.GetOverscrollWidgetInputHandlerHost());
10593 EXPECT_EQ(web_view_helper.GetLayerTreeHost()->overscroll_behavior(),
10594 kOverscrollBehaviorContain);
10595
10596 mainFrame->ExecuteScript(
10597 WebScriptSource(WebString("document.body.style="
10598 "'overscroll-behavior: none;'")));
10599 ScrollBegin(&web_view_helper, 100, 116);
10600 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10601 DidOverscroll(gfx::Vector2dF(-100, -100),
10602 gfx::Vector2dF(-300, -300), gfx::PointF(100, 100),
10603 gfx::Vector2dF(), kOverscrollBehaviorNone));
10604 ScrollUpdate(&web_view_helper, 100, 100);
10605 base::RunLoop().RunUntilIdle();
10606 Mock::VerifyAndClearExpectations(
10607 &client.GetOverscrollWidgetInputHandlerHost());
10608 EXPECT_EQ(web_view_helper.GetLayerTreeHost()->overscroll_behavior(),
10609 kOverscrollBehaviorNone);
10610 }
10611
TEST_P(WebFrameOverscrollTest,OnlyMainFrameOverscrollBehaviorHasEffect)10612 TEST_P(WebFrameOverscrollTest, OnlyMainFrameOverscrollBehaviorHasEffect) {
10613 OverscrollWebWidgetClient client;
10614 RegisterMockedHttpURLLoad("overscroll/iframe-overscroll.html");
10615 RegisterMockedHttpURLLoad("overscroll/scrollable-iframe.html");
10616 frame_test_helpers::WebViewHelper web_view_helper;
10617
10618 web_view_helper.InitializeAndLoad(
10619 base_url_ + "overscroll/iframe-overscroll.html", nullptr, nullptr,
10620 &client, ConfigureAndroid);
10621 web_view_helper.Resize(gfx::Size(200, 200));
10622
10623 WebLocalFrame* mainFrame =
10624 web_view_helper.GetWebView()->MainFrame()->ToWebLocalFrame();
10625 mainFrame->ExecuteScript(
10626 WebScriptSource(WebString("document.body.style="
10627 "'overscroll-behavior: auto;'")));
10628 WebLocalFrame* subframe = web_view_helper.GetWebView()
10629 ->MainFrame()
10630 ->FirstChild()
10631 ->ToWebLocalFrame();
10632 subframe->ExecuteScript(
10633 WebScriptSource(WebString("document.body.style="
10634 "'overscroll-behavior: none;'")));
10635
10636 ScrollBegin(&web_view_helper, 100, 116);
10637 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10638 DidOverscroll(gfx::Vector2dF(-100, -100),
10639 gfx::Vector2dF(-100, -100), gfx::PointF(100, 100),
10640 gfx::Vector2dF(), kOverscrollBehaviorAuto));
10641 ScrollUpdate(&web_view_helper, 100, 100);
10642 base::RunLoop().RunUntilIdle();
10643 Mock::VerifyAndClearExpectations(
10644 &client.GetOverscrollWidgetInputHandlerHost());
10645 EXPECT_EQ(web_view_helper.GetLayerTreeHost()->overscroll_behavior(),
10646 kOverscrollBehaviorAuto);
10647
10648 mainFrame->ExecuteScript(
10649 WebScriptSource(WebString("document.body.style="
10650 "'overscroll-behavior: contain;'")));
10651 EXPECT_CALL(client.GetOverscrollWidgetInputHandlerHost(),
10652 DidOverscroll(gfx::Vector2dF(-100, -100),
10653 gfx::Vector2dF(-200, -200), gfx::PointF(100, 100),
10654 gfx::Vector2dF(), kOverscrollBehaviorContain));
10655 ScrollUpdate(&web_view_helper, 100, 100);
10656 base::RunLoop().RunUntilIdle();
10657 Mock::VerifyAndClearExpectations(
10658 &client.GetOverscrollWidgetInputHandlerHost());
10659 EXPECT_EQ(web_view_helper.GetLayerTreeHost()->overscroll_behavior(),
10660 kOverscrollBehaviorContain);
10661 }
10662
TEST_F(WebFrameTest,OrientationFrameDetach)10663 TEST_F(WebFrameTest, OrientationFrameDetach) {
10664 ScopedOrientationEventForTest orientation_event(true);
10665 RegisterMockedHttpURLLoad("orientation-frame-detach.html");
10666 frame_test_helpers::WebViewHelper web_view_helper;
10667 WebViewImpl* web_view_impl = web_view_helper.InitializeAndLoad(
10668 base_url_ + "orientation-frame-detach.html");
10669 web_view_impl->MainFrameImpl()->SendOrientationChangeEvent();
10670 }
10671
TEST_F(WebFrameTest,MaxFrames)10672 TEST_F(WebFrameTest, MaxFrames) {
10673 frame_test_helpers::WebViewHelper web_view_helper;
10674 web_view_helper.InitializeRemote();
10675 Page* page = web_view_helper.GetWebView()->GetPage();
10676
10677 WebLocalFrameImpl* frame =
10678 frame_test_helpers::CreateLocalChild(*web_view_helper.RemoteMainFrame());
10679 while (page->SubframeCount() < Page::MaxNumberOfFrames()) {
10680 frame_test_helpers::CreateRemoteChild(*web_view_helper.RemoteMainFrame());
10681 }
10682 auto* iframe = MakeGarbageCollected<HTMLIFrameElement>(
10683 *frame->GetFrame()->GetDocument());
10684 iframe->setAttribute(html_names::kSrcAttr, "");
10685 frame->GetFrame()->GetDocument()->body()->appendChild(iframe);
10686 EXPECT_FALSE(iframe->ContentFrame());
10687 }
10688
10689 class TestViewportIntersection : public FakeRemoteFrameHost {
10690 public:
10691 TestViewportIntersection() = default;
10692 ~TestViewportIntersection() override = default;
10693
GetIntersectionState() const10694 const mojom::blink::ViewportIntersectionStatePtr& GetIntersectionState()
10695 const {
10696 return intersection_state_;
10697 }
10698
10699 // FakeRemoteFrameHost:
UpdateViewportIntersection(mojom::blink::ViewportIntersectionStatePtr intersection_state)10700 void UpdateViewportIntersection(
10701 mojom::blink::ViewportIntersectionStatePtr intersection_state) override {
10702 intersection_state_ = std::move(intersection_state);
10703 }
10704
10705 private:
10706 mojom::blink::ViewportIntersectionStatePtr intersection_state_;
10707 };
10708
TEST_F(WebFrameTest,RotatedIframeViewportIntersection)10709 TEST_F(WebFrameTest, RotatedIframeViewportIntersection) {
10710 frame_test_helpers::WebViewHelper web_view_helper;
10711 web_view_helper.Initialize();
10712 WebViewImpl* web_view = web_view_helper.GetWebView();
10713 web_view->Resize(gfx::Size(800, 600));
10714 InitializeWithHTML(*web_view->MainFrameImpl()->GetFrame(), R"HTML(
10715 <!DOCTYPE html>
10716 <style>
10717 iframe {
10718 position: absolute;
10719 top: 200px;
10720 left: 200px;
10721 transform: rotate(45deg);
10722 }
10723 </style>
10724 <iframe></iframe>
10725 )HTML");
10726 frame_test_helpers::TestWebRemoteFrameClient remote_frame_client;
10727 TestViewportIntersection remote_frame_host;
10728 remote_frame_host.Init(remote_frame_client.GetRemoteAssociatedInterfaces());
10729 WebRemoteFrameImpl* remote_frame =
10730 frame_test_helpers::CreateRemote(&remote_frame_client);
10731 web_view_helper.LocalMainFrame()->FirstChild()->Swap(remote_frame);
10732 web_view->MainFrameImpl()
10733 ->GetFrame()
10734 ->View()
10735 ->UpdateAllLifecyclePhasesForTest();
10736 base::RunLoop().RunUntilIdle();
10737 ASSERT_TRUE(!remote_frame_host.GetIntersectionState()
10738 ->viewport_intersection.IsEmpty());
10739 EXPECT_TRUE(IntRect(IntPoint(), remote_frame->GetFrame()->View()->Size())
10740 .Contains(IntRect(remote_frame_host.GetIntersectionState()
10741 ->viewport_intersection)));
10742 ASSERT_TRUE(!remote_frame_host.GetIntersectionState()
10743 ->main_frame_intersection.IsEmpty());
10744 EXPECT_TRUE(IntRect(IntPoint(), remote_frame->GetFrame()->View()->Size())
10745 .Contains(IntRect(remote_frame_host.GetIntersectionState()
10746 ->main_frame_intersection)));
10747 remote_frame->Detach();
10748 }
10749
TEST_F(WebFrameTest,ImageDocumentLoadResponseEnd)10750 TEST_F(WebFrameTest, ImageDocumentLoadResponseEnd) {
10751 // Loading an image resource directly generates an ImageDocument with
10752 // the document loader feeding image data into the resource of a generated
10753 // img tag. We expect the load finish time to be the same for the document
10754 // and the image resource.
10755
10756 RegisterMockedHttpURLLoadWithMimeType("white-1x1.png", "image/png");
10757 frame_test_helpers::WebViewHelper web_view_helper;
10758 web_view_helper.InitializeAndLoad(base_url_ + "white-1x1.png");
10759 WebViewImpl* web_view = web_view_helper.GetWebView();
10760 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
10761
10762 EXPECT_TRUE(document);
10763 EXPECT_TRUE(IsA<ImageDocument>(document));
10764
10765 auto* img_document = To<ImageDocument>(document);
10766 ImageResourceContent* image_content = img_document->CachedImage();
10767
10768 EXPECT_TRUE(image_content);
10769 EXPECT_NE(base::TimeTicks(), image_content->LoadResponseEnd());
10770
10771 DocumentLoader* loader = document->Loader();
10772
10773 EXPECT_TRUE(loader);
10774 EXPECT_EQ(loader->GetTiming().ResponseEnd(),
10775 image_content->LoadResponseEnd());
10776 }
10777
TEST_F(WebFrameTest,CopyImageDocument)10778 TEST_F(WebFrameTest, CopyImageDocument) {
10779 // After loading an image document, we should be able to copy it directly.
10780
10781 RegisterMockedHttpURLLoadWithMimeType("white-1x1.png", "image/png");
10782 frame_test_helpers::WebViewHelper web_view_helper;
10783 web_view_helper.InitializeAndLoad(base_url_ + "white-1x1.png");
10784 WebViewImpl* web_view = web_view_helper.GetWebView();
10785 WebLocalFrameImpl* web_frame = web_view->MainFrameImpl();
10786 Document* document = web_frame->GetFrame()->GetDocument();
10787
10788 ASSERT_TRUE(document);
10789 EXPECT_TRUE(IsA<ImageDocument>(document));
10790
10791 // Setup a mock clipboard host.
10792 PageTestBase::MockClipboardHostProvider mock_clipboard_host_provider(
10793 web_frame->GetFrame()->GetBrowserInterfaceBroker());
10794
10795 SystemClipboard* system_clipboard =
10796 document->GetFrame()->GetSystemClipboard();
10797 ASSERT_TRUE(system_clipboard);
10798
10799 EXPECT_TRUE(system_clipboard->ReadAvailableTypes().IsEmpty());
10800
10801 bool result = web_frame->ExecuteCommand("Copy");
10802 test::RunPendingTasks();
10803
10804 EXPECT_TRUE(result);
10805
10806 Vector<String> types = system_clipboard->ReadAvailableTypes();
10807 EXPECT_EQ(2u, types.size());
10808 EXPECT_EQ("text/html", types[0]);
10809 EXPECT_EQ("image/png", types[1]);
10810
10811 // Clear clipboard data
10812 system_clipboard->WritePlainText("");
10813 system_clipboard->CommitWrite();
10814 }
10815
TEST_F(WebFrameTest,CopyTextInImageDocument)10816 TEST_F(WebFrameTest, CopyTextInImageDocument) {
10817 // If Javascript inserts other contents into an image document, we should be
10818 // able to copy those contents, not just the image itself.
10819
10820 RegisterMockedHttpURLLoadWithMimeType("white-1x1.png", "image/png");
10821 frame_test_helpers::WebViewHelper web_view_helper;
10822 web_view_helper.InitializeAndLoad(base_url_ + "white-1x1.png");
10823 WebViewImpl* web_view = web_view_helper.GetWebView();
10824 WebLocalFrameImpl* web_frame = web_view->MainFrameImpl();
10825 Document* document = web_frame->GetFrame()->GetDocument();
10826
10827 ASSERT_TRUE(document);
10828 EXPECT_TRUE(IsA<ImageDocument>(document));
10829
10830 Node* text = document->createTextNode("copy me");
10831 document->body()->appendChild(text);
10832 document->GetFrame()->Selection().SetSelection(
10833 SelectionInDOMTree::Builder().SelectAllChildren(*text).Build(),
10834 SetSelectionOptions());
10835
10836 // Setup a mock clipboard host.
10837 PageTestBase::MockClipboardHostProvider mock_clipboard_host_provider(
10838 web_frame->GetFrame()->GetBrowserInterfaceBroker());
10839
10840 SystemClipboard* system_clipboard =
10841 document->GetFrame()->GetSystemClipboard();
10842 ASSERT_TRUE(system_clipboard);
10843
10844 EXPECT_TRUE(system_clipboard->ReadAvailableTypes().IsEmpty());
10845
10846 bool result = web_frame->ExecuteCommand("Copy");
10847 test::RunPendingTasks();
10848
10849 EXPECT_TRUE(result);
10850
10851 Vector<String> types = system_clipboard->ReadAvailableTypes();
10852 EXPECT_EQ(2u, types.size());
10853 EXPECT_EQ("text/plain", types[0]);
10854 EXPECT_EQ("text/html", types[1]);
10855
10856 // Clear clipboard data
10857 system_clipboard->WritePlainText("");
10858 system_clipboard->CommitWrite();
10859 }
10860
10861 class TestRemoteFrameHostForVisibility : public FakeRemoteFrameHost {
10862 public:
10863 TestRemoteFrameHostForVisibility() = default;
10864 ~TestRemoteFrameHostForVisibility() override = default;
10865
10866 // FakeRemoteFrameHost:
VisibilityChanged(blink::mojom::FrameVisibility visibility)10867 void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {
10868 visibility_ = visibility;
10869 }
10870
visibility() const10871 blink::mojom::FrameVisibility visibility() const { return visibility_; }
10872
10873 private:
10874 blink::mojom::FrameVisibility visibility_ =
10875 blink::mojom::FrameVisibility::kRenderedInViewport;
10876 };
10877
10878 class WebRemoteFrameVisibilityChangeTest : public WebFrameTest {
10879 public:
WebRemoteFrameVisibilityChangeTest()10880 WebRemoteFrameVisibilityChangeTest() {
10881 RegisterMockedHttpURLLoad("visible_iframe.html");
10882 RegisterMockedHttpURLLoad("single_iframe.html");
10883 frame_ =
10884 web_view_helper_.InitializeAndLoad(base_url_ + "single_iframe.html")
10885 ->MainFrameImpl();
10886 web_view_helper_.Resize(gfx::Size(640, 480));
10887 remote_frame_host_.Init(
10888 remote_frame_client_.GetRemoteAssociatedInterfaces());
10889 web_remote_frame_ = frame_test_helpers::CreateRemote(&remote_frame_client_);
10890 }
10891
10892 ~WebRemoteFrameVisibilityChangeTest() override = default;
10893
ExecuteScriptOnMainFrame(const WebScriptSource & script)10894 void ExecuteScriptOnMainFrame(const WebScriptSource& script) {
10895 MainFrame()->ExecuteScript(script);
10896 web_view_helper_.GetWebView()
10897 ->MainFrameViewWidget()
10898 ->SynchronouslyCompositeForTesting(base::TimeTicks::Now());
10899 RunPendingTasks();
10900 }
10901
SwapLocalFrameToRemoteFrame()10902 void SwapLocalFrameToRemoteFrame() {
10903 MainFrame()->LastChild()->Swap(RemoteFrame());
10904 }
10905
MainFrame()10906 WebLocalFrame* MainFrame() { return frame_; }
RemoteFrame()10907 WebRemoteFrameImpl* RemoteFrame() { return web_remote_frame_; }
RemoteFrameHost()10908 TestRemoteFrameHostForVisibility* RemoteFrameHost() {
10909 return &remote_frame_host_;
10910 }
10911
10912 private:
10913 TestRemoteFrameHostForVisibility remote_frame_host_;
10914 frame_test_helpers::TestWebRemoteFrameClient remote_frame_client_;
10915 frame_test_helpers::WebViewHelper web_view_helper_;
10916 WebLocalFrame* frame_;
10917 Persistent<WebRemoteFrameImpl> web_remote_frame_;
10918 };
10919
TEST_F(WebRemoteFrameVisibilityChangeTest,FrameVisibilityChange)10920 TEST_F(WebRemoteFrameVisibilityChangeTest, FrameVisibilityChange) {
10921 SwapLocalFrameToRemoteFrame();
10922 ExecuteScriptOnMainFrame(WebScriptSource(
10923 "document.querySelector('iframe').style.display = 'none';"));
10924 EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
10925 RemoteFrameHost()->visibility());
10926
10927 ExecuteScriptOnMainFrame(WebScriptSource(
10928 "document.querySelector('iframe').style.display = 'block';"));
10929 EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
10930 RemoteFrameHost()->visibility());
10931
10932 ExecuteScriptOnMainFrame(WebScriptSource(
10933 "var padding = document.createElement('div');"
10934 "padding.style = 'width: 400px; height: 800px;';"
10935 "document.body.insertBefore(padding, document.body.firstChild);"));
10936 EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
10937 RemoteFrameHost()->visibility());
10938
10939 ExecuteScriptOnMainFrame(
10940 WebScriptSource("document.scrollingElement.scrollTop = 800;"));
10941 EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
10942 RemoteFrameHost()->visibility());
10943 }
10944
TEST_F(WebRemoteFrameVisibilityChangeTest,ParentVisibilityChange)10945 TEST_F(WebRemoteFrameVisibilityChangeTest, ParentVisibilityChange) {
10946 SwapLocalFrameToRemoteFrame();
10947 ExecuteScriptOnMainFrame(
10948 WebScriptSource("document.querySelector('iframe').parentElement.style."
10949 "display = 'none';"));
10950 EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
10951 RemoteFrameHost()->visibility());
10952 }
10953
10954 class TestLocalFrameHostForVisibility : public FakeLocalFrameHost {
10955 public:
10956 TestLocalFrameHostForVisibility() = default;
10957 ~TestLocalFrameHostForVisibility() override = default;
10958
10959 // FakeLocalFrameHost:
VisibilityChanged(blink::mojom::FrameVisibility visibility)10960 void VisibilityChanged(blink::mojom::FrameVisibility visibility) override {
10961 visibility_ = visibility;
10962 }
10963
visibility() const10964 blink::mojom::FrameVisibility visibility() const { return visibility_; }
10965
10966 private:
10967 blink::mojom::FrameVisibility visibility_ =
10968 blink::mojom::FrameVisibility::kRenderedInViewport;
10969 };
10970
10971 class WebLocalFrameVisibilityChangeTest
10972 : public WebFrameTest,
10973 public frame_test_helpers::TestWebFrameClient {
10974 public:
WebLocalFrameVisibilityChangeTest()10975 WebLocalFrameVisibilityChangeTest() {
10976 RegisterMockedHttpURLLoad("visible_iframe.html");
10977 RegisterMockedHttpURLLoad("single_iframe.html");
10978 child_host_.Init(child_client_.GetRemoteNavigationAssociatedInterfaces());
10979 frame_ = web_view_helper_
10980 .InitializeAndLoad(base_url_ + "single_iframe.html", this)
10981 ->MainFrameImpl();
10982 web_view_helper_.Resize(gfx::Size(640, 480));
10983 }
10984
10985 ~WebLocalFrameVisibilityChangeTest() override = default;
10986
ExecuteScriptOnMainFrame(const WebScriptSource & script)10987 void ExecuteScriptOnMainFrame(const WebScriptSource& script) {
10988 MainFrame()->ExecuteScript(script);
10989 web_view_helper_.GetWebView()
10990 ->MainFrameViewWidget()
10991 ->SynchronouslyCompositeForTesting(base::TimeTicks::Now());
10992 RunPendingTasks();
10993 }
10994
MainFrame()10995 WebLocalFrame* MainFrame() { return frame_; }
10996
10997 // frame_test_helpers::TestWebFrameClient:
CreateChildFrame(WebLocalFrame * parent,mojom::blink::TreeScopeType scope,const WebString & name,const WebString & fallback_name,const FramePolicy &,const WebFrameOwnerProperties &,mojom::blink::FrameOwnerElementType)10998 WebLocalFrame* CreateChildFrame(
10999 WebLocalFrame* parent,
11000 mojom::blink::TreeScopeType scope,
11001 const WebString& name,
11002 const WebString& fallback_name,
11003 const FramePolicy&,
11004 const WebFrameOwnerProperties&,
11005 mojom::blink::FrameOwnerElementType) override {
11006 return CreateLocalChild(*parent, scope, &child_client_);
11007 }
11008
ChildHost()11009 TestLocalFrameHostForVisibility& ChildHost() { return child_host_; }
11010
11011 private:
11012 TestLocalFrameHostForVisibility child_host_;
11013 frame_test_helpers::TestWebFrameClient child_client_;
11014 frame_test_helpers::WebViewHelper web_view_helper_;
11015 WebLocalFrame* frame_;
11016 };
11017
TEST_F(WebLocalFrameVisibilityChangeTest,FrameVisibilityChange)11018 TEST_F(WebLocalFrameVisibilityChangeTest, FrameVisibilityChange) {
11019 ExecuteScriptOnMainFrame(WebScriptSource(
11020 "document.querySelector('iframe').style.display = 'none';"));
11021 EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
11022 ChildHost().visibility());
11023
11024 ExecuteScriptOnMainFrame(WebScriptSource(
11025 "document.querySelector('iframe').style.display = 'block';"));
11026 EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
11027 ChildHost().visibility());
11028
11029 ExecuteScriptOnMainFrame(WebScriptSource(
11030 "var padding = document.createElement('div');"
11031 "padding.style = 'width: 400px; height: 800px;';"
11032 "document.body.insertBefore(padding, document.body.firstChild);"));
11033 EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
11034 ChildHost().visibility());
11035
11036 ExecuteScriptOnMainFrame(
11037 WebScriptSource("document.scrollingElement.scrollTop = 800;"));
11038 EXPECT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
11039 ChildHost().visibility());
11040 }
11041
TEST_F(WebLocalFrameVisibilityChangeTest,ParentVisibilityChange)11042 TEST_F(WebLocalFrameVisibilityChangeTest, ParentVisibilityChange) {
11043 ExecuteScriptOnMainFrame(
11044 WebScriptSource("document.querySelector('iframe').parentElement.style."
11045 "display = 'none';"));
11046 EXPECT_EQ(blink::mojom::FrameVisibility::kNotRendered,
11047 ChildHost().visibility());
11048 }
11049
EnableGlobalReuseForUnownedMainFrames(WebSettings * settings)11050 static void EnableGlobalReuseForUnownedMainFrames(WebSettings* settings) {
11051 settings->SetShouldReuseGlobalForUnownedMainFrame(true);
11052 }
11053
11054 // A main frame with no opener should have a unique security origin. Thus, the
11055 // global should never be reused on the initial navigation.
TEST(WebFrameGlobalReuseTest,MainFrameWithNoOpener)11056 TEST(WebFrameGlobalReuseTest, MainFrameWithNoOpener) {
11057 frame_test_helpers::WebViewHelper helper;
11058 helper.Initialize();
11059
11060 WebLocalFrame* main_frame = helper.LocalMainFrame();
11061 v8::HandleScope scope(v8::Isolate::GetCurrent());
11062 main_frame->ExecuteScript(WebScriptSource("hello = 'world';"));
11063 frame_test_helpers::LoadFrame(main_frame, "data:text/html,new page");
11064 v8::Local<v8::Value> result =
11065 main_frame->ExecuteScriptAndReturnValue(WebScriptSource("hello"));
11066 EXPECT_TRUE(result.IsEmpty());
11067 }
11068
11069 // Child frames should never reuse the global on a cross-origin navigation, even
11070 // if the setting is enabled. It's not safe to since the parent could have
11071 // injected script before the initial navigation.
TEST(WebFrameGlobalReuseTest,ChildFrame)11072 TEST(WebFrameGlobalReuseTest, ChildFrame) {
11073 frame_test_helpers::WebViewHelper helper;
11074 helper.Initialize(nullptr, nullptr, nullptr,
11075 EnableGlobalReuseForUnownedMainFrames);
11076
11077 WebLocalFrame* main_frame = helper.LocalMainFrame();
11078 frame_test_helpers::LoadFrame(main_frame, "data:text/html,<iframe></iframe>");
11079
11080 WebLocalFrame* child_frame = main_frame->FirstChild()->ToWebLocalFrame();
11081 v8::HandleScope scope(v8::Isolate::GetCurrent());
11082 child_frame->ExecuteScript(WebScriptSource("hello = 'world';"));
11083 frame_test_helpers::LoadFrame(child_frame, "data:text/html,new page");
11084 v8::Local<v8::Value> result =
11085 child_frame->ExecuteScriptAndReturnValue(WebScriptSource("hello"));
11086 EXPECT_TRUE(result.IsEmpty());
11087 }
11088
11089 // A main frame with an opener should never reuse the global on a cross-origin
11090 // navigation, even if the setting is enabled. It's not safe to since the opener
11091 // could have injected script.
TEST(WebFrameGlobalReuseTest,MainFrameWithOpener)11092 TEST(WebFrameGlobalReuseTest, MainFrameWithOpener) {
11093 frame_test_helpers::WebViewHelper opener_helper;
11094 opener_helper.Initialize();
11095 frame_test_helpers::WebViewHelper helper;
11096 helper.InitializeWithOpener(opener_helper.GetWebView()->MainFrame(), nullptr,
11097 nullptr, nullptr,
11098 EnableGlobalReuseForUnownedMainFrames);
11099
11100 WebLocalFrame* main_frame = helper.LocalMainFrame();
11101 v8::HandleScope scope(v8::Isolate::GetCurrent());
11102 main_frame->ExecuteScript(WebScriptSource("hello = 'world';"));
11103 frame_test_helpers::LoadFrame(main_frame, "data:text/html,new page");
11104 v8::Local<v8::Value> result =
11105 main_frame->ExecuteScriptAndReturnValue(WebScriptSource("hello"));
11106 EXPECT_TRUE(result.IsEmpty());
11107 }
11108
11109 // A main frame that is unrelated to any other frame /can/ reuse the global if
11110 // the setting is enabled. In this case, it's impossible for any other frames to
11111 // have touched the global. Only the embedder could have injected script, and
11112 // the embedder enabling this setting is a signal that the injected script needs
11113 // to persist on the first navigation away from the initial empty document.
TEST(WebFrameGlobalReuseTest,ReuseForMainFrameIfEnabled)11114 TEST(WebFrameGlobalReuseTest, ReuseForMainFrameIfEnabled) {
11115 frame_test_helpers::WebViewHelper helper;
11116 helper.Initialize(nullptr, nullptr, nullptr,
11117 EnableGlobalReuseForUnownedMainFrames);
11118
11119 WebLocalFrame* main_frame = helper.LocalMainFrame();
11120 v8::HandleScope scope(v8::Isolate::GetCurrent());
11121 main_frame->ExecuteScript(WebScriptSource("hello = 'world';"));
11122 frame_test_helpers::LoadFrame(main_frame, "data:text/html,new page");
11123 v8::Local<v8::Value> result =
11124 main_frame->ExecuteScriptAndReturnValue(WebScriptSource("hello"));
11125 ASSERT_TRUE(result->IsString());
11126 EXPECT_EQ("world",
11127 ToCoreString(result->ToString(main_frame->MainWorldScriptContext())
11128 .ToLocalChecked()));
11129 }
11130
11131 // This class intercepts the registration of Blob instances.
11132 //
11133 // Given that the content of the Blob is known (data URL)
11134 // it gets the data from the DataElement's BytesProvider, and creates
11135 // FakeBlob's accordingly.
11136 class BlobRegistryForSaveImageFromDataURL : public mojom::blink::BlobRegistry {
11137 public:
Register(mojo::PendingReceiver<mojom::blink::Blob> blob,const String & uuid,const String & content_type,const String & content_disposition,Vector<mojom::blink::DataElementPtr> elements,RegisterCallback callback)11138 void Register(mojo::PendingReceiver<mojom::blink::Blob> blob,
11139 const String& uuid,
11140 const String& content_type,
11141 const String& content_disposition,
11142 Vector<mojom::blink::DataElementPtr> elements,
11143 RegisterCallback callback) override {
11144 DCHECK_EQ(elements.size(), 1u);
11145 DCHECK(elements[0]->is_bytes());
11146
11147 auto& element0 = elements[0];
11148 const auto& bytes = element0->get_bytes();
11149 auto length = bytes->length;
11150 String body(reinterpret_cast<const char*>(bytes->embedded_data->data()),
11151 static_cast<uint32_t>(length));
11152 mojo::MakeSelfOwnedReceiver(std::make_unique<FakeBlob>(uuid, body),
11153 std::move(blob));
11154 std::move(callback).Run();
11155 }
11156
RegisterFromStream(const String & content_type,const String & content_disposition,uint64_t expected_length,mojo::ScopedDataPipeConsumerHandle,mojo::PendingAssociatedRemote<mojom::blink::ProgressClient>,RegisterFromStreamCallback)11157 void RegisterFromStream(
11158 const String& content_type,
11159 const String& content_disposition,
11160 uint64_t expected_length,
11161 mojo::ScopedDataPipeConsumerHandle,
11162 mojo::PendingAssociatedRemote<mojom::blink::ProgressClient>,
11163 RegisterFromStreamCallback) override {
11164 NOTREACHED();
11165 }
11166
GetBlobFromUUID(mojo::PendingReceiver<mojom::blink::Blob>,const String & uuid,GetBlobFromUUIDCallback)11167 void GetBlobFromUUID(mojo::PendingReceiver<mojom::blink::Blob>,
11168 const String& uuid,
11169 GetBlobFromUUIDCallback) override {
11170 NOTREACHED();
11171 }
11172
URLStoreForOrigin(const scoped_refptr<const SecurityOrigin> &,mojo::PendingAssociatedReceiver<mojom::blink::BlobURLStore>)11173 void URLStoreForOrigin(
11174 const scoped_refptr<const SecurityOrigin>&,
11175 mojo::PendingAssociatedReceiver<mojom::blink::BlobURLStore>) override {
11176 NOTREACHED();
11177 }
11178 };
11179
11180 // blink::mojom::LocalFrameHost instance that intecepts DownloadURL() mojo
11181 // calls and reads the blob data URL sent by the renderer accordingly.
11182 class TestLocalFrameHostForSaveImageFromDataURL : public FakeLocalFrameHost {
11183 public:
TestLocalFrameHostForSaveImageFromDataURL()11184 TestLocalFrameHostForSaveImageFromDataURL()
11185 : blob_registry_receiver_(
11186 &blob_registry_,
11187 blob_registry_remote_.BindNewPipeAndPassReceiver()) {
11188 BlobDataHandle::SetBlobRegistryForTesting(blob_registry_remote_.get());
11189 }
~TestLocalFrameHostForSaveImageFromDataURL()11190 ~TestLocalFrameHostForSaveImageFromDataURL() override {
11191 BlobDataHandle::SetBlobRegistryForTesting(nullptr);
11192 }
11193
11194 // FakeLocalFrameHost:
DownloadURL(mojom::blink::DownloadURLParamsPtr params)11195 void DownloadURL(mojom::blink::DownloadURLParamsPtr params) override {
11196 mojo::Remote<mojom::blink::Blob> blob(std::move(params->data_url_blob));
11197 mojo::ScopedDataPipeProducerHandle producer_handle;
11198 mojo::ScopedDataPipeConsumerHandle consumer_handle;
11199 auto result =
11200 mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
11201 DCHECK(result == MOJO_RESULT_OK);
11202
11203 blob->ReadAll(std::move(producer_handle), mojo::NullRemote());
11204
11205 DataPipeDrainerClient client(&data_url_);
11206 auto data_pipe_drainer = std::make_unique<mojo::DataPipeDrainer>(
11207 &client, std::move(consumer_handle));
11208 client.Run();
11209 }
11210
Result() const11211 const String& Result() const { return data_url_; }
Reset()11212 void Reset() { data_url_ = String(); }
11213
11214 private:
11215 // Helper class to copy a blob to a string.
11216 class DataPipeDrainerClient : public mojo::DataPipeDrainer::Client {
11217 public:
DataPipeDrainerClient(String * output)11218 explicit DataPipeDrainerClient(String* output)
11219 : run_loop_(base::RunLoop::Type::kNestableTasksAllowed),
11220 output_(output) {}
Run()11221 void Run() { run_loop_.Run(); }
11222
OnDataAvailable(const void * data,size_t num_bytes)11223 void OnDataAvailable(const void* data, size_t num_bytes) override {
11224 *output_ = String(reinterpret_cast<const char*>(data), num_bytes);
11225 }
OnDataComplete()11226 void OnDataComplete() override { run_loop_.Quit(); }
11227
11228 private:
11229 base::RunLoop run_loop_;
11230 String* output_;
11231 };
11232
11233 BlobRegistryForSaveImageFromDataURL blob_registry_;
11234 mojo::Remote<mojom::blink::BlobRegistry> blob_registry_remote_;
11235 mojo::Receiver<mojom::blink::BlobRegistry> blob_registry_receiver_;
11236
11237 // Data URL retrieved from the blob.
11238 String data_url_;
11239 };
11240
TEST_F(WebFrameTest,SaveImageAt)11241 TEST_F(WebFrameTest, SaveImageAt) {
11242 std::string url = base_url_ + "image-with-data-url.html";
11243 // TODO(crbug.com/751425): We should use the mock functionality
11244 // via the WebViewHelper instance in each test case.
11245 RegisterMockedURLLoadFromBase(base_url_, "image-with-data-url.html");
11246 url_test_helpers::RegisterMockedURLLoad(
11247 ToKURL("http://test"), test::CoreTestDataPath("white-1x1.png"));
11248
11249 TestLocalFrameHostForSaveImageFromDataURL frame_host;
11250 frame_test_helpers::TestWebFrameClient web_frame_client;
11251 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
11252 frame_test_helpers::WebViewHelper web_view_helper;
11253 RunPendingTasks();
11254
11255 WebViewImpl* web_view =
11256 web_view_helper.InitializeAndLoad(url, &web_frame_client);
11257 web_view->MainFrameViewWidget()->Resize(gfx::Size(400, 400));
11258 UpdateAllLifecyclePhases(web_view);
11259
11260 LocalFrame* local_frame = To<LocalFrame>(web_view->GetPage()->MainFrame());
11261
11262 frame_host.Reset();
11263 local_frame->SaveImageAt(gfx::Point(1, 1));
11264 // Note that in this test does not use RunPendingTasks() since
11265 // TestLocalFrameHostForSaveImageFromDataURL trigger its own loops, so nesting
11266 // must be allowed.
11267 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11268
11269 EXPECT_EQ(
11270 String::FromUTF8("data:image/gif;base64"
11271 ",R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="),
11272 frame_host.Result());
11273
11274 frame_host.Reset();
11275
11276 local_frame->SaveImageAt(gfx::Point(1, 2));
11277 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11278 EXPECT_EQ(String(), frame_host.Result());
11279
11280 web_view->SetPageScaleFactor(4);
11281 web_view->SetVisualViewportOffset(gfx::PointF(1, 1));
11282
11283 frame_host.Reset();
11284 local_frame->SaveImageAt(gfx::Point(3, 3));
11285 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11286 EXPECT_EQ(
11287 String::FromUTF8("data:image/gif;base64"
11288 ",R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="),
11289 frame_host.Result());
11290
11291 // Explicitly reset to break dependency on locally scoped client.
11292 web_view_helper.Reset();
11293 }
11294
TEST_F(WebFrameTest,SaveImageWithImageMap)11295 TEST_F(WebFrameTest, SaveImageWithImageMap) {
11296 std::string url = base_url_ + "image-map.html";
11297 // TODO(crbug.com/751425): We should use the mock functionality
11298 // via the WebViewHelper instance in each test case.
11299 RegisterMockedURLLoadFromBase(base_url_, "image-map.html");
11300
11301 TestLocalFrameHostForSaveImageFromDataURL frame_host;
11302 frame_test_helpers::WebViewHelper helper;
11303 frame_test_helpers::TestWebFrameClient client;
11304 frame_host.Init(client.GetRemoteNavigationAssociatedInterfaces());
11305 WebViewImpl* web_view = helper.InitializeAndLoad(url, &client);
11306 web_view->MainFrameViewWidget()->Resize(gfx::Size(400, 400));
11307 RunPendingTasks();
11308
11309 LocalFrame* local_frame = To<LocalFrame>(web_view->GetPage()->MainFrame());
11310
11311 frame_host.Reset();
11312 local_frame->SaveImageAt(gfx::Point(25, 25));
11313 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11314 EXPECT_EQ(
11315 String::FromUTF8("data:image/gif;base64"
11316 ",R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="),
11317 frame_host.Result());
11318
11319 frame_host.Reset();
11320 local_frame->SaveImageAt(gfx::Point(75, 25));
11321 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11322 EXPECT_EQ(
11323 String::FromUTF8("data:image/gif;base64"
11324 ",R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="),
11325 frame_host.Result());
11326
11327 frame_host.Reset();
11328 local_frame->SaveImageAt(gfx::Point(125, 25));
11329 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11330 EXPECT_EQ(String(), frame_host.Result());
11331
11332 // Explicitly reset to break dependency on locally scoped client.
11333 helper.Reset();
11334 }
11335
TEST_F(WebFrameTest,CopyImageWithImageMap)11336 TEST_F(WebFrameTest, CopyImageWithImageMap) {
11337 std::string url = base_url_ + "image-map.html";
11338 // TODO(crbug.com/751425): We should use the mock functionality
11339 // via the WebViewHelper instance in each test case.
11340 RegisterMockedURLLoadFromBase(base_url_, "image-map.html");
11341
11342 TestLocalFrameHostForSaveImageFromDataURL frame_host;
11343 frame_test_helpers::WebViewHelper helper;
11344 frame_test_helpers::TestWebFrameClient client;
11345 frame_host.Init(client.GetRemoteNavigationAssociatedInterfaces());
11346 WebViewImpl* web_view = helper.InitializeAndLoad(url, &client);
11347 web_view->MainFrameViewWidget()->Resize(gfx::Size(400, 400));
11348 RunPendingTasks();
11349
11350 frame_host.Reset();
11351 LocalFrame* local_frame = To<LocalFrame>(web_view->GetPage()->MainFrame());
11352 local_frame->SaveImageAt(gfx::Point(25, 25));
11353 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11354 EXPECT_EQ(
11355 String::FromUTF8("data:image/gif;base64"
11356 ",R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="),
11357 frame_host.Result());
11358
11359 frame_host.Reset();
11360 local_frame->SaveImageAt(gfx::Point(75, 25));
11361 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11362 EXPECT_EQ(
11363 String::FromUTF8("data:image/gif;base64"
11364 ",R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="),
11365 frame_host.Result());
11366
11367 frame_host.Reset();
11368 local_frame->SaveImageAt(gfx::Point(125, 25));
11369 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle();
11370 EXPECT_EQ(String(), frame_host.Result());
11371 // Explicitly reset to break dependency on locally scoped client.
11372 helper.Reset();
11373 }
11374
TEST_F(WebFrameTest,LoadJavascriptURLInNewFrame)11375 TEST_F(WebFrameTest, LoadJavascriptURLInNewFrame) {
11376 frame_test_helpers::WebViewHelper helper;
11377 helper.Initialize();
11378
11379 std::string redirect_url = base_url_ + "foo.html";
11380 KURL javascript_url = ToKURL("javascript:location='" + redirect_url + "'");
11381 url_test_helpers::RegisterMockedURLLoad(ToKURL(redirect_url),
11382 test::CoreTestDataPath("foo.html"));
11383 helper.LocalMainFrame()->LoadJavaScriptURL(javascript_url);
11384 RunPendingTasks();
11385
11386 // The result of the JS url replaces the existing contents on the
11387 // Document, but the JS-triggered navigation should still occur.
11388 EXPECT_NE("", To<LocalFrame>(helper.GetWebView()->GetPage()->MainFrame())
11389 ->GetDocument()
11390 ->documentElement()
11391 ->innerText());
11392 EXPECT_EQ(ToKURL(redirect_url),
11393 To<LocalFrame>(helper.GetWebView()->GetPage()->MainFrame())
11394 ->GetDocument()
11395 ->Url());
11396 }
11397
TEST_F(WebFrameTest,EmptyJavascriptFrameUrl)11398 TEST_F(WebFrameTest, EmptyJavascriptFrameUrl) {
11399 std::string url = "data:text/html,<iframe src=\"javascript:''\"></iframe>";
11400 frame_test_helpers::WebViewHelper helper;
11401 helper.InitializeAndLoad(url);
11402 RunPendingTasks();
11403
11404 LocalFrame* child = To<LocalFrame>(
11405 helper.GetWebView()->GetPage()->MainFrame()->Tree().FirstChild());
11406 EXPECT_EQ(BlankURL(), child->GetDocument()->Url());
11407 EXPECT_EQ(BlankURL(), child->Loader().GetDocumentLoader()->Url());
11408 }
11409
11410 class TestResourcePriorityWebFrameClient
11411 : public frame_test_helpers::TestWebFrameClient {
11412 public:
11413 class ExpectedRequest {
11414 public:
ExpectedRequest(const KURL & url,WebURLRequest::Priority priority)11415 ExpectedRequest(const KURL& url, WebURLRequest::Priority priority)
11416 : url(url), priority(priority), seen(false) {}
11417
11418 KURL url;
11419 WebURLRequest::Priority priority;
11420 bool seen;
11421 };
11422
11423 TestResourcePriorityWebFrameClient() = default;
11424 ~TestResourcePriorityWebFrameClient() override = default;
11425
11426 // frame_test_helpers::TestWebFrameClient:
WillSendRequest(WebURLRequest & request,ForRedirect for_redirect)11427 void WillSendRequest(WebURLRequest& request,
11428 ForRedirect for_redirect) override {
11429 ExpectedRequest* expected_request = expected_requests_.at(request.Url());
11430 DCHECK(expected_request);
11431 EXPECT_EQ(expected_request->priority, request.GetPriority());
11432 expected_request->seen = true;
11433 }
11434
AddExpectedRequest(const KURL & url,WebURLRequest::Priority priority)11435 void AddExpectedRequest(const KURL& url, WebURLRequest::Priority priority) {
11436 expected_requests_.insert(url,
11437 std::make_unique<ExpectedRequest>(url, priority));
11438 }
11439
VerifyAllRequests()11440 void VerifyAllRequests() {
11441 for (const auto& request : expected_requests_)
11442 EXPECT_TRUE(request.value->seen);
11443 }
11444
11445 private:
11446 HashMap<KURL, std::unique_ptr<ExpectedRequest>> expected_requests_;
11447 };
11448
TEST_F(WebFrameTest,ChangeResourcePriority)11449 TEST_F(WebFrameTest, ChangeResourcePriority) {
11450 TestResourcePriorityWebFrameClient client;
11451 RegisterMockedHttpURLLoad("promote_img_in_viewport_priority.html");
11452 RegisterMockedHttpURLLoad("image_slow.pl");
11453 RegisterMockedHttpURLLoad("image_slow_out_of_viewport.pl");
11454 client.AddExpectedRequest(ToKURL("http://internal.test/image_slow.pl"),
11455 WebURLRequest::Priority::kLow);
11456 client.AddExpectedRequest(
11457 ToKURL("http://internal.test/image_slow_out_of_viewport.pl"),
11458 WebURLRequest::Priority::kLow);
11459
11460 frame_test_helpers::WebViewHelper helper;
11461 helper.Initialize(&client);
11462 helper.Resize(gfx::Size(640, 480));
11463 frame_test_helpers::LoadFrame(
11464 helper.GetWebView()->MainFrameImpl(),
11465 base_url_ + "promote_img_in_viewport_priority.html");
11466
11467 // Ensure the image in the viewport got promoted after the request was sent.
11468 Resource* image = To<WebLocalFrameImpl>(helper.GetWebView()->MainFrame())
11469 ->GetFrame()
11470 ->GetDocument()
11471 ->Fetcher()
11472 ->AllResources()
11473 .at(ToKURL("http://internal.test/image_slow.pl"));
11474 DCHECK(image);
11475 EXPECT_EQ(ResourceLoadPriority::kHigh,
11476 image->GetResourceRequest().Priority());
11477
11478 client.VerifyAllRequests();
11479 }
11480
TEST_F(WebFrameTest,ScriptPriority)11481 TEST_F(WebFrameTest, ScriptPriority) {
11482 TestResourcePriorityWebFrameClient client;
11483 RegisterMockedHttpURLLoad("script_priority.html");
11484 RegisterMockedHttpURLLoad("priorities/defer.js");
11485 RegisterMockedHttpURLLoad("priorities/async.js");
11486 RegisterMockedHttpURLLoad("priorities/head.js");
11487 RegisterMockedHttpURLLoad("priorities/document-write.js");
11488 RegisterMockedHttpURLLoad("priorities/injected.js");
11489 RegisterMockedHttpURLLoad("priorities/injected-async.js");
11490 RegisterMockedHttpURLLoad("priorities/body.js");
11491 client.AddExpectedRequest(ToKURL("http://internal.test/priorities/defer.js"),
11492 WebURLRequest::Priority::kLow);
11493 client.AddExpectedRequest(ToKURL("http://internal.test/priorities/async.js"),
11494 WebURLRequest::Priority::kLow);
11495 client.AddExpectedRequest(ToKURL("http://internal.test/priorities/head.js"),
11496 WebURLRequest::Priority::kHigh);
11497 client.AddExpectedRequest(
11498 ToKURL("http://internal.test/priorities/document-write.js"),
11499 WebURLRequest::Priority::kHigh);
11500 client.AddExpectedRequest(
11501 ToKURL("http://internal.test/priorities/injected.js"),
11502 WebURLRequest::Priority::kLow);
11503 client.AddExpectedRequest(
11504 ToKURL("http://internal.test/priorities/injected-async.js"),
11505 WebURLRequest::Priority::kLow);
11506 client.AddExpectedRequest(ToKURL("http://internal.test/priorities/body.js"),
11507 WebURLRequest::Priority::kHigh);
11508
11509 frame_test_helpers::WebViewHelper helper;
11510 helper.InitializeAndLoad(base_url_ + "script_priority.html", &client);
11511 client.VerifyAllRequests();
11512 }
11513
11514 class MultipleDataChunkDelegate : public WebURLLoaderTestDelegate {
11515 public:
11516 MultipleDataChunkDelegate() = default;
11517 ~MultipleDataChunkDelegate() override = default;
11518
11519 // WebURLLoaderTestDelegate:
DidReceiveData(WebURLLoaderClient * original_client,const char * data,int data_length)11520 void DidReceiveData(WebURLLoaderClient* original_client,
11521 const char* data,
11522 int data_length) override {
11523 EXPECT_GT(data_length, 16);
11524 original_client->DidReceiveData(data, 16);
11525 // This didReceiveData call shouldn't crash due to a failed assertion.
11526 original_client->DidReceiveData(data + 16, data_length - 16);
11527 }
11528 };
11529
TEST_F(WebFrameTest,ImageDocumentDecodeError)11530 TEST_F(WebFrameTest, ImageDocumentDecodeError) {
11531 std::string url = base_url_ + "not_an_image.ico";
11532 url_test_helpers::RegisterMockedURLLoad(
11533 ToKURL(url), test::CoreTestDataPath("not_an_image.ico"), "image/x-icon");
11534 MultipleDataChunkDelegate delegate;
11535 url_test_helpers::SetLoaderDelegate(&delegate);
11536 frame_test_helpers::WebViewHelper helper;
11537 helper.InitializeAndLoad(url);
11538 url_test_helpers::SetLoaderDelegate(nullptr);
11539
11540 Document* document =
11541 To<LocalFrame>(helper.GetWebView()->GetPage()->MainFrame())
11542 ->GetDocument();
11543 EXPECT_TRUE(IsA<ImageDocument>(document));
11544 EXPECT_EQ(ResourceStatus::kDecodeError,
11545 To<ImageDocument>(document)->CachedImage()->GetContentStatus());
11546 }
11547
11548 // Ensure that the root layer -- whose size is ordinarily derived from the
11549 // content size -- maintains a minimum height matching the viewport in cases
11550 // where the content is smaller.
TEST_F(WebFrameTest,RootLayerMinimumHeight)11551 TEST_F(WebFrameTest, RootLayerMinimumHeight) {
11552 constexpr int kViewportWidth = 320;
11553 constexpr int kViewportHeight = 640;
11554 constexpr int kBrowserControlsHeight = 100;
11555
11556 frame_test_helpers::WebViewHelper web_view_helper;
11557 web_view_helper.Initialize(nullptr, nullptr, nullptr, ConfigureAndroid);
11558 WebViewImpl* web_view = web_view_helper.GetWebView();
11559 web_view->ResizeWithBrowserControls(
11560 gfx::Size(kViewportWidth, kViewportHeight - kBrowserControlsHeight),
11561 kBrowserControlsHeight, 0, true);
11562
11563 InitializeWithHTML(
11564 *web_view->MainFrameImpl()->GetFrame(),
11565 "<!DOCTYPE html>"
11566 "<meta name='viewport' content='width=device-width, initial-scale=1'>"
11567 "<style>"
11568 " html, body {width:100%;height:540px;margin:0px}"
11569 " #elem {"
11570 " overflow: scroll;"
11571 " width: 100px;"
11572 " height: 10px;"
11573 " position: fixed;"
11574 " left: 0px;"
11575 " bottom: 0px;"
11576 " }"
11577 "</style>"
11578 "<div id='elem'></div>");
11579 UpdateAllLifecyclePhases(web_view);
11580
11581 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
11582 LocalFrameView* frame_view = web_view->MainFrameImpl()->GetFrameView();
11583 PaintLayerCompositor* compositor = frame_view->GetLayoutView()->Compositor();
11584
11585 EXPECT_EQ(kViewportHeight - kBrowserControlsHeight,
11586 compositor->RootLayer()->BoundingBoxForCompositing().Height());
11587
11588 document->View()->SetTracksRasterInvalidations(true);
11589
11590 web_view->ResizeWithBrowserControls(
11591 gfx::Size(kViewportWidth, kViewportHeight), kBrowserControlsHeight, 0,
11592 false);
11593
11594 EXPECT_EQ(kViewportHeight,
11595 compositor->RootLayer()->BoundingBoxForCompositing().Height());
11596 EXPECT_EQ(kViewportHeight, compositor->RootGraphicsLayer()->Size().height());
11597 EXPECT_EQ(kViewportHeight, compositor->RootGraphicsLayer()->Size().height());
11598
11599 const RasterInvalidationTracking* invalidation_tracking =
11600 document->GetLayoutView()
11601 ->Layer()
11602 ->GetCompositedLayerMapping()
11603 ->MainGraphicsLayer()
11604 ->GetRasterInvalidationTracking();
11605 ASSERT_TRUE(invalidation_tracking);
11606 const auto& raster_invalidations = invalidation_tracking->Invalidations();
11607
11608 // We don't issue raster invalidation, because the content paints into the
11609 // scrolling contents layer whose size hasn't changed.
11610 EXPECT_TRUE(raster_invalidations.IsEmpty());
11611
11612 document->View()->SetTracksRasterInvalidations(false);
11613 }
11614
11615 // Load a page with display:none set and try to scroll it. It shouldn't crash
11616 // due to lack of layoutObject. crbug.com/653327.
TEST_F(WebFrameTest,ScrollBeforeLayoutDoesntCrash)11617 TEST_F(WebFrameTest, ScrollBeforeLayoutDoesntCrash) {
11618 RegisterMockedHttpURLLoad("display-none.html");
11619 frame_test_helpers::WebViewHelper web_view_helper;
11620 web_view_helper.InitializeAndLoad(base_url_ + "display-none.html");
11621 WebViewImpl* web_view = web_view_helper.GetWebView();
11622 web_view_helper.Resize(gfx::Size(640, 480));
11623
11624 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
11625 document->documentElement()->SetLayoutObject(nullptr);
11626
11627 WebGestureEvent begin_event(
11628 WebInputEvent::Type::kGestureScrollBegin, WebInputEvent::kNoModifiers,
11629 WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchpad);
11630 WebGestureEvent update_event(
11631 WebInputEvent::Type::kGestureScrollUpdate, WebInputEvent::kNoModifiers,
11632 WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchpad);
11633 WebGestureEvent end_event(
11634 WebInputEvent::Type::kGestureScrollEnd, WebInputEvent::kNoModifiers,
11635 WebInputEvent::GetStaticTimeStampForTests(), WebGestureDevice::kTouchpad);
11636
11637 // Try GestureScrollEnd and GestureScrollUpdate first to make sure that not
11638 // seeing a Begin first doesn't break anything. (This currently happens).
11639 web_view_helper.GetWebView()->MainFrameWidget()->HandleInputEvent(
11640 WebCoalescedInputEvent(end_event, ui::LatencyInfo()));
11641 web_view_helper.GetWebView()->MainFrameWidget()->HandleInputEvent(
11642 WebCoalescedInputEvent(update_event, ui::LatencyInfo()));
11643
11644 // Try a full Begin/Update/End cycle.
11645 web_view_helper.GetWebView()->MainFrameWidget()->HandleInputEvent(
11646 WebCoalescedInputEvent(begin_event, ui::LatencyInfo()));
11647 web_view_helper.GetWebView()->MainFrameWidget()->HandleInputEvent(
11648 WebCoalescedInputEvent(update_event, ui::LatencyInfo()));
11649 web_view_helper.GetWebView()->MainFrameWidget()->HandleInputEvent(
11650 WebCoalescedInputEvent(end_event, ui::LatencyInfo()));
11651 }
11652
TEST_F(WebFrameTest,MouseOverDifferntNodeClearsTooltip)11653 TEST_F(WebFrameTest, MouseOverDifferntNodeClearsTooltip) {
11654 frame_test_helpers::WebViewHelper web_view_helper;
11655 web_view_helper.Initialize();
11656 web_view_helper.Resize(gfx::Size(200, 200));
11657 WebViewImpl* web_view = web_view_helper.GetWebView();
11658
11659 InitializeWithHTML(
11660 *web_view->MainFrameImpl()->GetFrame(),
11661 "<head>"
11662 " <style type='text/css'>"
11663 " div"
11664 " {"
11665 " width: 200px;"
11666 " height: 100px;"
11667 " background-color: #eeeeff;"
11668 " }"
11669 " div:hover"
11670 " {"
11671 " background-color: #ddddff;"
11672 " }"
11673 " </style>"
11674 "</head>"
11675 "<body>"
11676 " <div id='div1' title='Title Attribute Value'>Hover HERE</div>"
11677 " <div id='div2' title='Title Attribute Value'>Then HERE</div>"
11678 " <br><br><br>"
11679 "</body>");
11680 UpdateAllLifecyclePhases(web_view);
11681
11682 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
11683 Element* div1_tag = document->getElementById("div1");
11684
11685 HitTestResult hit_test_result =
11686 web_view->MainFrameViewWidget()->CoreHitTestResultAt(
11687 gfx::PointF(div1_tag->OffsetLeft() + 5, div1_tag->OffsetTop() + 5));
11688
11689 EXPECT_TRUE(hit_test_result.InnerElement());
11690
11691 // Mouse over link. Mouse cursor should be hand.
11692 WebMouseEvent mouse_move_over_link_event(
11693 WebInputEvent::Type::kMouseMove,
11694 gfx::PointF(div1_tag->OffsetLeft() + 5, div1_tag->OffsetTop() + 5),
11695 gfx::PointF(div1_tag->OffsetLeft() + 5, div1_tag->OffsetTop() + 5),
11696 WebPointerProperties::Button::kNoButton, 0, WebInputEvent::kNoModifiers,
11697 base::TimeTicks::Now());
11698 mouse_move_over_link_event.SetFrameScale(1);
11699 document->GetFrame()->GetEventHandler().HandleMouseMoveEvent(
11700 mouse_move_over_link_event, Vector<WebMouseEvent>(),
11701 Vector<WebMouseEvent>());
11702
11703 EXPECT_EQ(
11704 document->HoverElement(),
11705 document->GetFrame()->GetChromeClient().LastSetTooltipNodeForTesting());
11706 EXPECT_EQ(
11707 div1_tag,
11708 document->GetFrame()->GetChromeClient().LastSetTooltipNodeForTesting());
11709
11710 Element* div2_tag = document->getElementById("div2");
11711
11712 WebMouseEvent mouse_move_event(
11713 WebInputEvent::Type::kMouseMove,
11714 gfx::PointF(div2_tag->OffsetLeft() + 5, div2_tag->OffsetTop() + 5),
11715 gfx::PointF(div2_tag->OffsetLeft() + 5, div2_tag->OffsetTop() + 5),
11716 WebPointerProperties::Button::kNoButton, 0, WebInputEvent::kNoModifiers,
11717 base::TimeTicks::Now());
11718 mouse_move_event.SetFrameScale(1);
11719 document->GetFrame()->GetEventHandler().HandleMouseMoveEvent(
11720 mouse_move_event, Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
11721
11722 EXPECT_EQ(
11723 document->HoverElement(),
11724 document->GetFrame()->GetChromeClient().LastSetTooltipNodeForTesting());
11725 EXPECT_EQ(
11726 div2_tag,
11727 document->GetFrame()->GetChromeClient().LastSetTooltipNodeForTesting());
11728 }
11729
11730 class WebFrameSimTest : public SimTest {
11731 public:
UseAndroidSettings()11732 void UseAndroidSettings() {
11733 WebView().GetPage()->GetSettings().SetViewportMetaEnabled(true);
11734 WebView().GetPage()->GetSettings().SetViewportEnabled(true);
11735 WebView().GetPage()->GetSettings().SetMainFrameResizesAreOrientationChanges(
11736 true);
11737 WebView().GetPage()->GetSettings().SetViewportStyle(
11738 mojom::blink::ViewportStyle::kMobile);
11739 WebView().GetSettings()->SetAutoZoomFocusedNodeToLegibleScale(true);
11740 WebView().GetSettings()->SetShrinksViewportContentToFit(true);
11741 WebView().SetDefaultPageScaleLimits(0.25f, 5);
11742 }
11743 };
11744
TEST_F(WebFrameSimTest,HitTestWithIgnoreClippingAtNegativeOffset)11745 TEST_F(WebFrameSimTest, HitTestWithIgnoreClippingAtNegativeOffset) {
11746 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
11747 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
11748
11749 SimRequest r("https://example.com/test.html", "text/html");
11750 LoadURL("https://example.com/test.html");
11751 r.Complete(R"HTML(
11752 <!DOCTYPE html>
11753 <style>
11754 body, html {
11755 width: 100%;
11756 height: 1000px;
11757 margin: 0;
11758 }
11759 #top {
11760 position: absolute;
11761 top: 500px;
11762 height: 100px;
11763 width: 100%;
11764
11765 }
11766 #bottom {
11767 position: absolute;
11768 top: 600px;
11769 width: 100%;
11770 height: 500px;
11771 }
11772 </style>
11773 <div id="top"></div>
11774 <div id="bottom"></div>
11775 )HTML");
11776
11777 Compositor().BeginFrame();
11778
11779 auto* frame_view = To<LocalFrame>(WebView().GetPage()->MainFrame())->View();
11780
11781 frame_view->GetScrollableArea()->SetScrollOffset(
11782 ScrollOffset(0, 600), mojom::blink::ScrollType::kProgrammatic);
11783 Compositor().BeginFrame();
11784
11785 HitTestRequest request = HitTestRequest::kMove | HitTestRequest::kReadOnly |
11786 HitTestRequest::kActive |
11787 HitTestRequest::kIgnoreClipping;
11788 HitTestLocation location(
11789 frame_view->ConvertFromRootFrame(PhysicalOffset(100, -50)));
11790 HitTestResult result(request, location);
11791 frame_view->GetLayoutView()->HitTest(location, result);
11792
11793 EXPECT_EQ(GetDocument().getElementById("top"), result.InnerNode());
11794 }
11795
TEST_F(WebFrameSimTest,TickmarksDocumentRelative)11796 TEST_F(WebFrameSimTest, TickmarksDocumentRelative) {
11797 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
11798 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
11799
11800 SimRequest request("https://example.com/test.html", "text/html");
11801 LoadURL("https://example.com/test.html");
11802 request.Complete(R"HTML(
11803 <!DOCTYPE html>
11804 <style>
11805 body, html {
11806 width: 4000px;
11807 height: 4000px;
11808 margin: 0;
11809 }
11810 div {
11811 position: absolute;
11812 left: 800px;
11813 top: 2000px;
11814 }
11815 </style>
11816 <div>test</div>
11817 )HTML");
11818
11819 Compositor().BeginFrame();
11820
11821 auto* frame = To<WebLocalFrameImpl>(WebView().MainFrame());
11822 auto* frame_view = To<LocalFrame>(WebView().GetPage()->MainFrame())->View();
11823
11824 frame_view->GetScrollableArea()->SetScrollOffset(
11825 ScrollOffset(3000, 1000), mojom::blink::ScrollType::kProgrammatic);
11826 auto options = mojom::blink::FindOptions::New();
11827 options->run_synchronously_for_testing = true;
11828 WebString search_text = WebString::FromUTF8("test");
11829 const int kFindIdentifier = 12345;
11830 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(kFindIdentifier, search_text,
11831 *options, false));
11832
11833 frame->EnsureTextFinder().ResetMatchCount();
11834 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
11835 search_text, *options);
11836
11837 // Get the tickmarks for the original find request.
11838 Vector<IntRect> original_tickmarks =
11839 frame_view->LayoutViewport()->GetTickmarks();
11840 EXPECT_EQ(1u, original_tickmarks.size());
11841
11842 EXPECT_EQ(IntPoint(800, 2000), original_tickmarks[0].Location());
11843 }
11844
TEST_F(WebFrameSimTest,FindInPageSelectNextMatch)11845 TEST_F(WebFrameSimTest, FindInPageSelectNextMatch) {
11846 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
11847 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
11848
11849 SimRequest request("https://example.com/test.html", "text/html");
11850 LoadURL("https://example.com/test.html");
11851 request.Complete(R"HTML(
11852 <!DOCTYPE html>
11853 <style>
11854 body, html {
11855 width: 4000px;
11856 height: 4000px;
11857 margin: 0;
11858 }
11859 #box1 {
11860 position: absolute;
11861 left: 800px;
11862 top: 2000px;
11863 }
11864
11865 #box2 {
11866 position: absolute;
11867 left: 1000px;
11868 top: 3000px;
11869 }
11870 </style>
11871 <div id="box1">test</div>
11872 <div id="box2">test</div>
11873 )HTML");
11874
11875 Compositor().BeginFrame();
11876
11877 auto* frame = To<WebLocalFrameImpl>(WebView().MainFrame());
11878 auto* local_frame = To<LocalFrame>(WebView().GetPage()->MainFrame());
11879 auto* frame_view = local_frame->View();
11880
11881 Element* box1 = GetDocument().getElementById("box1");
11882 Element* box2 = GetDocument().getElementById("box2");
11883
11884 IntRect box1_rect = box1->GetLayoutObject()->AbsoluteBoundingBoxRect();
11885 IntRect box2_rect = box2->GetLayoutObject()->AbsoluteBoundingBoxRect();
11886
11887 frame_view->GetScrollableArea()->SetScrollOffset(
11888 ScrollOffset(3000, 1000), mojom::blink::ScrollType::kProgrammatic);
11889 auto options = mojom::blink::FindOptions::New();
11890 options->run_synchronously_for_testing = true;
11891 WebString search_text = WebString::FromUTF8("test");
11892 const int kFindIdentifier = 12345;
11893 EXPECT_TRUE(frame->GetFindInPage()->FindInternal(kFindIdentifier, search_text,
11894 *options, false));
11895
11896 frame->EnsureTextFinder().ResetMatchCount();
11897 frame->EnsureTextFinder().StartScopingStringMatches(kFindIdentifier,
11898 search_text, *options);
11899
11900 WebVector<gfx::RectF> web_match_rects =
11901 frame->EnsureTextFinder().FindMatchRects();
11902 ASSERT_EQ(2ul, web_match_rects.size());
11903
11904 FloatRect result_rect = static_cast<FloatRect>(web_match_rects[0]);
11905 frame->EnsureTextFinder().SelectNearestFindMatch(result_rect.Center(),
11906 nullptr);
11907
11908 EXPECT_TRUE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
11909 box1_rect));
11910 result_rect = static_cast<FloatRect>(web_match_rects[1]);
11911 frame->EnsureTextFinder().SelectNearestFindMatch(result_rect.Center(),
11912 nullptr);
11913
11914 EXPECT_TRUE(
11915 frame_view->GetScrollableArea()->VisibleContentRect().Contains(box2_rect))
11916 << "Box [" << box2_rect.ToString() << "] is not visible in viewport ["
11917 << frame_view->GetScrollableArea()->VisibleContentRect().ToString()
11918 << "]";
11919 }
11920
11921 // Test bubbling a document (End key) scroll from an inner iframe. This test
11922 // passes if it does not crash. https://crbug.com/904247.
TEST_F(WebFrameSimTest,ScrollToEndBubblingCrash)11923 TEST_F(WebFrameSimTest, ScrollToEndBubblingCrash) {
11924 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
11925 WebView().GetPage()->GetSettings().SetScrollAnimatorEnabled(false);
11926
11927 SimRequest request("https://example.com/test.html", "text/html");
11928 LoadURL("https://example.com/test.html");
11929 request.Complete(R"HTML(
11930 <!DOCTYPE html>
11931 <style>
11932 body, html {
11933 width: 100%;
11934 height: 100%;
11935 margin: 0;
11936 }
11937 #frame {
11938 width: 100%;
11939 height: 100%;
11940 border: 0;
11941 }
11942 </style>
11943 <iframe id="frame" srcdoc="
11944 <!DOCTYPE html>
11945 <style>html {height: 300%;}</style>
11946 "></iframe>
11947 )HTML");
11948
11949 Compositor().BeginFrame();
11950 RunPendingTasks();
11951
11952 // Focus the iframe.
11953 WebView().AdvanceFocus(false);
11954
11955 WebKeyboardEvent key_event(WebInputEvent::Type::kRawKeyDown,
11956 WebInputEvent::kNoModifiers,
11957 WebInputEvent::GetStaticTimeStampForTests());
11958 key_event.windows_key_code = VKEY_END;
11959
11960 // Scroll the iframe to the end.
11961 key_event.SetType(WebInputEvent::Type::kRawKeyDown);
11962 WebView().MainFrameWidget()->HandleInputEvent(
11963 WebCoalescedInputEvent(key_event, ui::LatencyInfo()));
11964 key_event.SetType(WebInputEvent::Type::kKeyUp);
11965 WebView().MainFrameWidget()->HandleInputEvent(
11966 WebCoalescedInputEvent(key_event, ui::LatencyInfo()));
11967
11968 Compositor().BeginFrame();
11969
11970 // End key should now bubble from the iframe up to the main viewport.
11971 key_event.SetType(WebInputEvent::Type::kRawKeyDown);
11972 WebView().MainFrameWidget()->HandleInputEvent(
11973 WebCoalescedInputEvent(key_event, ui::LatencyInfo()));
11974 key_event.SetType(WebInputEvent::Type::kKeyUp);
11975 WebView().MainFrameWidget()->HandleInputEvent(
11976 WebCoalescedInputEvent(key_event, ui::LatencyInfo()));
11977 }
11978
TEST_F(WebFrameSimTest,TestScrollFocusedEditableElementIntoView)11979 TEST_F(WebFrameSimTest, TestScrollFocusedEditableElementIntoView) {
11980 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
11981 WebView().SetDefaultPageScaleLimits(1.f, 4);
11982 WebView().EnableFakePageScaleAnimationForTesting(true);
11983 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
11984 WebView().GetPage()->GetSettings().SetViewportEnabled(false);
11985 WebView().GetSettings()->SetAutoZoomFocusedNodeToLegibleScale(true);
11986
11987 SimRequest request("https://example.com/test.html", "text/html");
11988 LoadURL("https://example.com/test.html");
11989 request.Complete(R"HTML(
11990 <!DOCTYPE html>
11991 <style>
11992 ::-webkit-scrollbar {
11993 width: 0px;
11994 height: 0px;
11995 }
11996 body {
11997 margin: 0px;
11998 }
11999 input {
12000 border: 0;
12001 padding: 0;
12002 position: absolute;
12003 left: 200px;
12004 top: 600px;
12005 width: 100px;
12006 height: 20px;
12007 }
12008 #content {
12009 background: silver;
12010 width: 500px;
12011 height: 600px;
12012 }
12013 </style>
12014 <div id="content">a</div>
12015 <input type="text">
12016 )HTML");
12017
12018 Compositor().BeginFrame();
12019
12020 WebView().AdvanceFocus(false);
12021
12022 auto* frame = To<LocalFrame>(WebView().GetPage()->MainFrame());
12023 LocalFrameView* frame_view = frame->View();
12024 IntRect inputRect(200, 600, 100, 20);
12025
12026 frame_view->GetScrollableArea()->SetScrollOffset(
12027 ScrollOffset(0, 0), mojom::blink::ScrollType::kProgrammatic);
12028
12029 ASSERT_EQ(FloatPoint(),
12030 frame_view->GetScrollableArea()->VisibleContentRect().Location());
12031
12032 WebView()
12033 .MainFrameImpl()
12034 ->FrameWidget()
12035 ->ScrollFocusedEditableElementIntoView();
12036
12037 EXPECT_EQ(1, WebView().FakePageScaleAnimationPageScaleForTesting());
12038
12039 frame_view->LayoutViewport()->SetScrollOffset(
12040 ToFloatSize(FloatPoint(
12041 WebView().FakePageScaleAnimationTargetPositionForTesting())),
12042 mojom::blink::ScrollType::kProgrammatic);
12043
12044 EXPECT_TRUE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
12045 inputRect));
12046
12047 // Reset the testing getters.
12048 WebView().EnableFakePageScaleAnimationForTesting(true);
12049
12050 // This input is already in view, this shouldn't cause a scroll.
12051 WebView()
12052 .MainFrameImpl()
12053 ->FrameWidget()
12054 ->ScrollFocusedEditableElementIntoView();
12055
12056 EXPECT_EQ(0, WebView().FakePageScaleAnimationPageScaleForTesting());
12057 EXPECT_EQ(IntPoint(),
12058 WebView().FakePageScaleAnimationTargetPositionForTesting());
12059
12060 // Now resize the visual viewport so that the input box is no longer in view
12061 // (e.g. a keyboard is overlaid).
12062 WebView().ResizeVisualViewport(gfx::Size(200, 100));
12063 ASSERT_FALSE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
12064 inputRect));
12065
12066 WebView()
12067 .MainFrameImpl()
12068 ->FrameWidget()
12069 ->ScrollFocusedEditableElementIntoView();
12070 frame_view->GetScrollableArea()->SetScrollOffset(
12071 ToFloatSize(FloatPoint(
12072 WebView().FakePageScaleAnimationTargetPositionForTesting())),
12073 mojom::blink::ScrollType::kProgrammatic);
12074
12075 EXPECT_TRUE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
12076 inputRect));
12077 EXPECT_EQ(1, WebView().FakePageScaleAnimationPageScaleForTesting());
12078 }
12079
12080 // Ensures scrolling a focused editable text into view that's located in the
12081 // root scroller works by scrolling the root scroller.
TEST_F(WebFrameSimTest,TestScrollFocusedEditableInRootScroller)12082 TEST_F(WebFrameSimTest, TestScrollFocusedEditableInRootScroller) {
12083 ScopedImplicitRootScrollerForTest implicit_root_scroller(true);
12084
12085 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
12086 WebView().SetDefaultPageScaleLimits(1.f, 4);
12087 WebView().EnableFakePageScaleAnimationForTesting(true);
12088 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
12089 WebView().GetPage()->GetSettings().SetViewportEnabled(false);
12090 WebView().GetSettings()->SetAutoZoomFocusedNodeToLegibleScale(true);
12091
12092 SimRequest request("https://example.com/test.html", "text/html");
12093 LoadURL("https://example.com/test.html");
12094 request.Complete(R"HTML(
12095 <!DOCTYPE html>
12096 <style>
12097 ::-webkit-scrollbar {
12098 width: 0px;
12099 height: 0px;
12100 }
12101 body,html {
12102 width: 100%;
12103 height: 100%;
12104 margin: 0px;
12105 }
12106 input {
12107 border: 0;
12108 padding: 0;
12109 margin-left: 200px;
12110 margin-top: 700px;
12111 width: 100px;
12112 height: 20px;
12113 }
12114 #scroller {
12115 background: silver;
12116 width: 100%;
12117 height: 100%;
12118 overflow: auto;
12119 }
12120 </style>
12121 <div id="scroller" tabindex="-1">
12122 <input type="text">
12123 </div>
12124 )HTML");
12125
12126 Compositor().BeginFrame();
12127
12128 TopDocumentRootScrollerController& rs_controller =
12129 GetDocument().GetPage()->GlobalRootScrollerController();
12130
12131 Element* scroller = GetDocument().getElementById("scroller");
12132 ASSERT_EQ(scroller, rs_controller.GlobalRootScroller());
12133
12134 auto* frame = To<LocalFrame>(WebView().GetPage()->MainFrame());
12135 VisualViewport& visual_viewport = frame->GetPage()->GetVisualViewport();
12136
12137 WebView().AdvanceFocus(false);
12138
12139 rs_controller.RootScrollerArea()->SetScrollOffset(
12140 ScrollOffset(0, 300), mojom::blink::ScrollType::kProgrammatic);
12141
12142 LocalFrameView* frame_view = frame->View();
12143 IntRect inputRect(200, 700, 100, 20);
12144 ASSERT_EQ(1, visual_viewport.Scale());
12145 ASSERT_EQ(FloatPoint(0, 300),
12146 frame_view->GetScrollableArea()->VisibleContentRect().Location());
12147 ASSERT_FALSE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
12148 inputRect));
12149
12150 WebView()
12151 .MainFrameImpl()
12152 ->FrameWidget()
12153 ->ScrollFocusedEditableElementIntoView();
12154
12155 EXPECT_EQ(1, WebView().FakePageScaleAnimationPageScaleForTesting());
12156
12157 ScrollOffset target_offset = ToFloatSize(
12158 FloatPoint(WebView().FakePageScaleAnimationTargetPositionForTesting()));
12159
12160 rs_controller.RootScrollerArea()->SetScrollOffset(
12161 target_offset, mojom::blink::ScrollType::kProgrammatic);
12162
12163 EXPECT_TRUE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
12164 inputRect));
12165 }
12166
TEST_F(WebFrameSimTest,ScrollFocusedIntoViewClipped)12167 TEST_F(WebFrameSimTest, ScrollFocusedIntoViewClipped) {
12168 // The Android On-Screen Keyboard (OSK) resizes the Widget Blink is hosted
12169 // in. When the keyboard is shown, we scroll and zoom in on the currently
12170 // focused editable element. However, the scroll and zoom is a smoothly
12171 // animated "PageScaleAnimation" that's performed in CC only on the viewport
12172 // layers. There are some situations in which the widget resize causes the
12173 // focued input to be hidden by clipping parents that aren't the main frame.
12174 // In these cases, there's no way to scroll just the viewport to make the
12175 // input visible, we need to also scroll those clip/scroller elements This
12176 // test ensures we do so. https://crbug.com/270018.
12177 UseAndroidSettings();
12178 WebView().MainFrameViewWidget()->Resize(gfx::Size(400, 600));
12179 WebView().EnableFakePageScaleAnimationForTesting(true);
12180 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
12181
12182 SimRequest request("https://example.com/test.html", "text/html");
12183 LoadURL("https://example.com/test.html");
12184 request.Complete(R"HTML(
12185 <!DOCTYPE html>
12186 <style>
12187 ::-webkit-scrollbar {
12188 width: 0px;
12189 height: 0px;
12190 }
12191 body, html {
12192 margin: 0px;
12193 width: 100%;
12194 height: 100%;
12195 }
12196 input {
12197 padding: 0;
12198 position: relative;
12199 top: 1400px;
12200 width: 100px;
12201 height: 20px;
12202 }
12203 #clip {
12204 width: 100%;
12205 height: 100%;
12206 overflow: hidden;
12207 }
12208 #container {
12209 width: 980px;
12210 height: 1470px;
12211 }
12212 </style>
12213 <div id="clip">
12214 <div id="container">
12215 <input type="text" id="target">
12216 </div>
12217 </div>
12218 )HTML");
12219
12220 Compositor().BeginFrame();
12221 WebView().AdvanceFocus(false);
12222
12223 auto* frame = To<LocalFrame>(WebView().GetPage()->MainFrame());
12224 LocalFrameView* frame_view = frame->View();
12225 VisualViewport& visual_viewport = frame->GetPage()->GetVisualViewport();
12226
12227 ASSERT_EQ(FloatPoint(),
12228 frame_view->GetScrollableArea()->VisibleContentRect().Location());
12229
12230 // Simulate the keyboard being shown and resizing the widget. Cause a scroll
12231 // into view after.
12232 WebView().MainFrameViewWidget()->Resize(gfx::Size(400, 300));
12233
12234 float scale_before = visual_viewport.Scale();
12235 WebView()
12236 .MainFrameImpl()
12237 ->FrameWidget()
12238 ->ScrollFocusedEditableElementIntoView();
12239
12240 Element* input = GetDocument().getElementById("target");
12241 IntRect input_rect(input->getBoundingClientRect()->top(),
12242 input->getBoundingClientRect()->left(),
12243 input->getBoundingClientRect()->width(),
12244 input->getBoundingClientRect()->height());
12245
12246 IntRect visible_content_rect(IntPoint(), frame_view->Size());
12247 EXPECT_TRUE(visible_content_rect.Contains(input_rect))
12248 << "Layout viewport [" << visible_content_rect.ToString()
12249 << "] does not contain input rect [" << input_rect.ToString()
12250 << "] after scroll into view.";
12251
12252 EXPECT_TRUE(visual_viewport.VisibleRect().Contains(input_rect))
12253 << "Visual viewport [" << visual_viewport.VisibleRect().ToString()
12254 << "] does not contain input rect [" << input_rect.ToString()
12255 << "] after scroll into view.";
12256
12257 // Make sure we also zoomed in on the input.
12258 EXPECT_GT(WebView().FakePageScaleAnimationPageScaleForTesting(),
12259 scale_before);
12260
12261 // Additional gut-check that we actually scrolled the non-user-scrollable
12262 // clip element to make sure the input is in view.
12263 Element* clip = GetDocument().getElementById("clip");
12264 EXPECT_GT(clip->scrollTop(), 0);
12265 }
12266
12267 // This test ensures that we scroll to the correct scale when the focused
12268 // element has a selection rather than a carret.
TEST_F(WebFrameSimTest,ScrollFocusedSelectionIntoView)12269 TEST_F(WebFrameSimTest, ScrollFocusedSelectionIntoView) {
12270 UseAndroidSettings();
12271 WebView().MainFrameViewWidget()->Resize(gfx::Size(400, 600));
12272 WebView().EnableFakePageScaleAnimationForTesting(true);
12273 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
12274
12275 SimRequest request("https://example.com/test.html", "text/html");
12276 LoadURL("https://example.com/test.html");
12277 request.Complete(R"HTML(
12278 <!DOCTYPE html>
12279 <style>
12280 ::-webkit-scrollbar {
12281 width: 0px;
12282 height: 0px;
12283 }
12284 body, html {
12285 margin: 0px;
12286 width: 100%;
12287 height: 100%;
12288 }
12289 input {
12290 padding: 0;
12291 width: 100px;
12292 height: 20px;
12293 }
12294 </style>
12295 <input type="text" id="target" value="test">
12296 )HTML");
12297
12298 Compositor().BeginFrame();
12299 WebView().AdvanceFocus(false);
12300
12301 auto* input = To<HTMLInputElement>(GetDocument().getElementById("target"));
12302 input->select();
12303
12304 // Simulate the keyboard being shown and resizing the widget. Cause a scroll
12305 // into view after.
12306 ASSERT_EQ(WebView().FakePageScaleAnimationPageScaleForTesting(), 0.f);
12307 WebFrameWidget* widget = WebView().MainFrameImpl()->FrameWidgetImpl();
12308 widget->ScrollFocusedEditableElementIntoView();
12309
12310 // Make sure zoomed in but only up to a legible scale. The bounds are
12311 // arbitrary and fuzzy since we don't specifically care to constrain the
12312 // amount of zooming (that should be tested elsewhere), we just care that it
12313 // zooms but not off to infinity.
12314 EXPECT_GT(WebView().FakePageScaleAnimationPageScaleForTesting(), .75f);
12315 EXPECT_LT(WebView().FakePageScaleAnimationPageScaleForTesting(), 2.f);
12316 }
12317
TEST_F(WebFrameSimTest,DoubleTapZoomWhileScrolled)12318 TEST_F(WebFrameSimTest, DoubleTapZoomWhileScrolled) {
12319 UseAndroidSettings();
12320 WebView().MainFrameViewWidget()->Resize(gfx::Size(490, 500));
12321 WebView().EnableFakePageScaleAnimationForTesting(true);
12322 WebView().GetSettings()->SetTextAutosizingEnabled(false);
12323 WebView().SetDefaultPageScaleLimits(0.5f, 4);
12324
12325 SimRequest request("https://example.com/test.html", "text/html");
12326 LoadURL("https://example.com/test.html");
12327 request.Complete(R"HTML(
12328 <!DOCTYPE html>
12329 <style>
12330 ::-webkit-scrollbar {
12331 width: 0px;
12332 height: 0px;
12333 }
12334 body {
12335 margin: 0px;
12336 width: 10000px;
12337 height: 10000px;
12338 }
12339 #target {
12340 position: absolute;
12341 left: 2000px;
12342 top: 3000px;
12343 width: 100px;
12344 height: 100px;
12345 background-color: blue;
12346 }
12347 </style>
12348 <div id="target"></div>
12349 )HTML");
12350
12351 Compositor().BeginFrame();
12352
12353 auto* frame = To<LocalFrame>(WebView().GetPage()->MainFrame());
12354 LocalFrameView* frame_view = frame->View();
12355 VisualViewport& visual_viewport = frame->GetPage()->GetVisualViewport();
12356 IntRect target_rect_in_document(2000, 3000, 100, 100);
12357
12358 ASSERT_EQ(0.5f, visual_viewport.Scale());
12359
12360 // Center the target in the screen.
12361 frame_view->GetScrollableArea()->SetScrollOffset(
12362 ScrollOffset(2000 - 440, 3000 - 450),
12363 mojom::blink::ScrollType::kProgrammatic);
12364 Element* target = GetDocument().QuerySelector("#target");
12365 DOMRect* rect = target->getBoundingClientRect();
12366 ASSERT_EQ(440, rect->left());
12367 ASSERT_EQ(450, rect->top());
12368
12369 // Double-tap on the target. Expect that we zoom in and the target is
12370 // contained in the visual viewport.
12371 {
12372 gfx::Point point(445, 455);
12373 WebRect block_bounds = ComputeBlockBoundHelper(&WebView(), point, false);
12374 WebView().AnimateDoubleTapZoom(IntPoint(point), block_bounds);
12375 EXPECT_TRUE(WebView().FakeDoubleTapAnimationPendingForTesting());
12376 ScrollOffset new_offset = ToScrollOffset(
12377 FloatPoint(WebView().FakePageScaleAnimationTargetPositionForTesting()));
12378 float new_scale = WebView().FakePageScaleAnimationPageScaleForTesting();
12379 visual_viewport.SetScale(new_scale);
12380 frame_view->GetScrollableArea()->SetScrollOffset(
12381 new_offset, mojom::blink::ScrollType::kProgrammatic);
12382
12383 EXPECT_FLOAT_EQ(1, visual_viewport.Scale());
12384 EXPECT_TRUE(frame_view->GetScrollableArea()->VisibleContentRect().Contains(
12385 target_rect_in_document));
12386 }
12387
12388 // Reset the testing getters.
12389 WebView().EnableFakePageScaleAnimationForTesting(true);
12390
12391 // Double-tap on the target again. We should zoom out and the target should
12392 // remain on screen.
12393 {
12394 gfx::Point point(445, 455);
12395 WebRect block_bounds = ComputeBlockBoundHelper(&WebView(), point, false);
12396 WebView().AnimateDoubleTapZoom(IntPoint(point), block_bounds);
12397 EXPECT_TRUE(WebView().FakeDoubleTapAnimationPendingForTesting());
12398 IntPoint target_offset(
12399 WebView().FakePageScaleAnimationTargetPositionForTesting());
12400 float new_scale = WebView().FakePageScaleAnimationPageScaleForTesting();
12401
12402 EXPECT_FLOAT_EQ(0.5f, new_scale);
12403 EXPECT_TRUE(target_rect_in_document.Contains(target_offset));
12404 }
12405 }
12406
TEST_F(WebFrameSimTest,ChangeBackgroundColor)12407 TEST_F(WebFrameSimTest, ChangeBackgroundColor) {
12408 SimRequest main_resource("https://example.com/test.html", "text/html");
12409
12410 LoadURL("https://example.com/test.html");
12411 main_resource.Complete("<!DOCTYPE html><body></body>");
12412
12413 Element* body = GetDocument().QuerySelector("body");
12414 EXPECT_TRUE(!!body);
12415
12416 Compositor().BeginFrame();
12417 // White is the default background of a web page.
12418 EXPECT_EQ(SK_ColorWHITE, Compositor().background_color());
12419
12420 // Setting the background of the body to red will cause the background
12421 // color of the WebView to switch to red.
12422 body->SetInlineStyleProperty(CSSPropertyID::kBackgroundColor, "red");
12423 Compositor().BeginFrame();
12424 EXPECT_EQ(SK_ColorRED, Compositor().background_color());
12425 }
12426
12427 // Ensure we don't crash if we try to scroll into view the focused editable
12428 // element which doesn't have a LayoutObject.
TEST_F(WebFrameSimTest,ScrollFocusedEditableIntoViewNoLayoutObject)12429 TEST_F(WebFrameSimTest, ScrollFocusedEditableIntoViewNoLayoutObject) {
12430 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 600));
12431 WebView().GetPage()->GetSettings().SetTextAutosizingEnabled(false);
12432
12433 SimRequest r("https://example.com/test.html", "text/html");
12434 LoadURL("https://example.com/test.html");
12435 r.Complete(R"HTML(
12436 <!DOCTYPE html>
12437 <style>
12438 input {
12439 position: absolute;
12440 top: 1000px;
12441 left: 800px;
12442 }
12443
12444 @media (max-height: 500px) {
12445 input {
12446 display: none;
12447 }
12448 }
12449 </style>
12450 <input id="target" type="text"></input>
12451 )HTML");
12452
12453 Compositor().BeginFrame();
12454
12455 Element* input = GetDocument().getElementById("target");
12456 input->focus();
12457
12458 ScrollableArea* area = GetDocument().View()->LayoutViewport();
12459 area->SetScrollOffset(ScrollOffset(0, 0),
12460 mojom::blink::ScrollType::kProgrammatic);
12461
12462 ASSERT_TRUE(input->GetLayoutObject());
12463 ASSERT_EQ(input, WebView().FocusedElement());
12464 ASSERT_EQ(ScrollOffset(0, 0), area->GetScrollOffset());
12465
12466 // The resize should cause the focused element to lose its LayoutObject. If
12467 // this resize came from the Android on-screen keyboard, this would be
12468 // followed by a ScrollFocusedEditableElementIntoView. Ensure we don't crash.
12469 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 300));
12470
12471 ASSERT_FALSE(input->GetLayoutObject());
12472 ASSERT_EQ(input, WebView().FocusedElement());
12473
12474 WebFrameWidget* widget = WebView().MainFrameImpl()->FrameWidgetImpl();
12475 widget->ScrollFocusedEditableElementIntoView();
12476 Compositor().BeginFrame();
12477
12478 // Shouldn't cause any scrolling either.
12479 EXPECT_EQ(ScrollOffset(0, 0), area->GetScrollOffset());
12480 }
12481
TEST_F(WebFrameSimTest,DisplayNoneIFrameHasNoLayoutObjects)12482 TEST_F(WebFrameSimTest, DisplayNoneIFrameHasNoLayoutObjects) {
12483 SimRequest main_resource("https://example.com/test.html", "text/html");
12484 SimRequest frame_resource("https://example.com/frame.html", "text/html");
12485
12486 LoadURL("https://example.com/test.html");
12487 main_resource.Complete(
12488 "<!DOCTYPE html>"
12489 "<iframe src=frame.html style='display: none'></iframe>");
12490 frame_resource.Complete(
12491 "<!DOCTYPE html>"
12492 "<html><body>This is a visible iframe.</body></html>");
12493
12494 Element* element = GetDocument().QuerySelector("iframe");
12495 auto* frame_owner_element = To<HTMLFrameOwnerElement>(element);
12496 Document* iframe_doc = frame_owner_element->contentDocument();
12497 EXPECT_FALSE(iframe_doc->documentElement()->GetLayoutObject());
12498
12499 // Changing the display from 'none' -> 'block' should cause layout objects to
12500 // appear.
12501 element->SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kBlock);
12502 Compositor().BeginFrame();
12503 EXPECT_TRUE(iframe_doc->documentElement()->GetLayoutObject());
12504
12505 // Changing the display from 'block' -> 'none' should cause layout objects to
12506 // disappear.
12507 element->SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone);
12508
12509 Compositor().BeginFrame();
12510 EXPECT_FALSE(iframe_doc->documentElement()->GetLayoutObject());
12511 }
12512
12513 // Although it is not spec compliant, many websites intentionally call
12514 // Window.print() on display:none iframes. https://crbug.com/819327.
TEST_F(WebFrameSimTest,DisplayNoneIFramePrints)12515 TEST_F(WebFrameSimTest, DisplayNoneIFramePrints) {
12516 SimRequest main_resource("https://example.com/test.html", "text/html");
12517 SimRequest frame_resource("https://example.com/frame.html", "text/html");
12518
12519 LoadURL("https://example.com/test.html");
12520 main_resource.Complete(
12521 "<!DOCTYPE html>"
12522 "<iframe src=frame.html style='display: none'></iframe>");
12523 frame_resource.Complete(
12524 "<!DOCTYPE html>"
12525 "<html><body>This is a visible iframe.</body></html>");
12526
12527 Element* element = GetDocument().QuerySelector("iframe");
12528 auto* frame_owner_element = To<HTMLFrameOwnerElement>(element);
12529 Document* iframe_doc = frame_owner_element->contentDocument();
12530 EXPECT_FALSE(iframe_doc->documentElement()->GetLayoutObject());
12531
12532 FloatSize page_size(400, 400);
12533 float maximum_shrink_ratio = 1.0;
12534 iframe_doc->GetFrame()->StartPrinting(page_size, page_size,
12535 maximum_shrink_ratio);
12536 EXPECT_TRUE(iframe_doc->documentElement()->GetLayoutObject());
12537
12538 iframe_doc->GetFrame()->EndPrinting();
12539 EXPECT_FALSE(iframe_doc->documentElement()->GetLayoutObject());
12540 }
12541
TEST_F(WebFrameSimTest,NormalIFrameHasLayoutObjects)12542 TEST_F(WebFrameSimTest, NormalIFrameHasLayoutObjects) {
12543 SimRequest main_resource("https://example.com/test.html", "text/html");
12544 SimRequest frame_resource("https://example.com/frame.html", "text/html");
12545
12546 LoadURL("https://example.com/test.html");
12547 main_resource.Complete(
12548 "<!DOCTYPE html>"
12549 "<iframe src=frame.html style='display: block'></iframe>");
12550 frame_resource.Complete(
12551 "<!DOCTYPE html>"
12552 "<html><body>This is a visible iframe.</body></html>");
12553
12554 Element* element = GetDocument().QuerySelector("iframe");
12555 auto* frame_owner_element = To<HTMLFrameOwnerElement>(element);
12556 Document* iframe_doc = frame_owner_element->contentDocument();
12557 EXPECT_TRUE(iframe_doc->documentElement()->GetLayoutObject());
12558
12559 // Changing the display from 'block' -> 'none' should cause layout objects to
12560 // disappear.
12561 element->SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone);
12562 Compositor().BeginFrame();
12563 EXPECT_FALSE(iframe_doc->documentElement()->GetLayoutObject());
12564 }
12565
TEST_F(WebFrameSimTest,RtlInitialScrollOffsetWithViewport)12566 TEST_F(WebFrameSimTest, RtlInitialScrollOffsetWithViewport) {
12567 UseAndroidSettings();
12568
12569 WebView().MainFrameViewWidget()->Resize(gfx::Size(400, 400));
12570 WebView().SetDefaultPageScaleLimits(0.25f, 2);
12571
12572 SimRequest main_resource("https://example.com/test.html", "text/html");
12573 LoadURL("https://example.com/test.html");
12574 main_resource.Complete(R"HTML(
12575 <meta name='viewport' content='width=device-width, minimum-scale=1'>
12576 <body dir='rtl'>
12577 <div style='width: 3000px; height: 20px'></div>
12578 )HTML");
12579
12580 Compositor().BeginFrame();
12581 ScrollableArea* area = GetDocument().View()->LayoutViewport();
12582 ASSERT_EQ(ScrollOffset(0, 0), area->GetScrollOffset());
12583 }
12584
TEST_F(WebFrameSimTest,LayoutViewportExceedsLayoutOverflow)12585 TEST_F(WebFrameSimTest, LayoutViewportExceedsLayoutOverflow) {
12586 UseAndroidSettings();
12587
12588 WebView().ResizeWithBrowserControls(gfx::Size(400, 540), 60, 0, true);
12589 WebView().SetDefaultPageScaleLimits(0.25f, 2);
12590
12591 SimRequest main_resource("https://example.com/test.html", "text/html");
12592 LoadURL("https://example.com/test.html");
12593 main_resource.Complete(R"HTML(
12594 <meta name='viewport' content='width=device-width, minimum-scale=1'>
12595 <body style='margin: 0; height: 95vh'>
12596 )HTML");
12597
12598 Compositor().BeginFrame();
12599 ScrollableArea* area = GetDocument().View()->LayoutViewport();
12600 ASSERT_EQ(540, area->VisibleHeight());
12601 ASSERT_EQ(IntSize(400, 570), area->ContentsSize());
12602
12603 // Hide browser controls, growing layout viewport without affecting ICB.
12604 WebView().ResizeWithBrowserControls(gfx::Size(400, 600), 60, 0, false);
12605 Compositor().BeginFrame();
12606
12607 // ContentsSize() should grow to accommodate new visible size.
12608 ASSERT_EQ(600, area->VisibleHeight());
12609 ASSERT_EQ(IntSize(400, 600), area->ContentsSize());
12610 }
12611
TEST_F(WebFrameSimTest,LayoutViewLocalVisualRect)12612 TEST_F(WebFrameSimTest, LayoutViewLocalVisualRect) {
12613 UseAndroidSettings();
12614
12615 WebView().MainFrameViewWidget()->Resize(gfx::Size(600, 400));
12616 WebView().SetDefaultPageScaleLimits(0.5f, 2);
12617
12618 SimRequest main_resource("https://example.com/test.html", "text/html");
12619 LoadURL("https://example.com/test.html");
12620 main_resource.Complete(R"HTML(
12621 <meta name='viewport' content='width=device-width, minimum-scale=0.5'>
12622 <body style='margin: 0; width: 1800px; height: 1200px'></div>
12623 )HTML");
12624
12625 Compositor().BeginFrame();
12626 ASSERT_EQ(PhysicalRect(0, 0, 1200, 800),
12627 GetDocument().GetLayoutView()->LocalVisualRect());
12628 }
12629
TEST_F(WebFrameSimTest,NamedLookupIgnoresEmptyNames)12630 TEST_F(WebFrameSimTest, NamedLookupIgnoresEmptyNames) {
12631 SimRequest main_resource("https://example.com/main.html", "text/html");
12632 LoadURL("https://example.com/main.html");
12633 main_resource.Complete(R"HTML(
12634 <body>
12635 <iframe name="" src="data:text/html,"></iframe>
12636 </body>)HTML");
12637
12638 EXPECT_EQ(nullptr, MainFrame().GetFrame()->Tree().ScopedChild(""));
12639 EXPECT_EQ(nullptr,
12640 MainFrame().GetFrame()->Tree().ScopedChild(AtomicString()));
12641 EXPECT_EQ(nullptr, MainFrame().GetFrame()->Tree().ScopedChild(g_empty_atom));
12642 }
12643
TEST_F(WebFrameTest,NoLoadingCompletionCallbacksInDetach)12644 TEST_F(WebFrameTest, NoLoadingCompletionCallbacksInDetach) {
12645 class LoadingObserverFrameClient
12646 : public frame_test_helpers::TestWebFrameClient {
12647 public:
12648 LoadingObserverFrameClient() = default;
12649 ~LoadingObserverFrameClient() override = default;
12650
12651 // frame_test_helpers::TestWebFrameClient:
12652 void FrameDetached() override {
12653 did_call_frame_detached_ = true;
12654 TestWebFrameClient::FrameDetached();
12655 }
12656
12657 void DidStopLoading() override {
12658 // TODO(dcheng): Investigate not calling this as well during frame detach.
12659 did_call_did_stop_loading_ = true;
12660 TestWebFrameClient::DidStopLoading();
12661 }
12662
12663 void DidFinishDocumentLoad() override {
12664 // TODO(dcheng): Investigate not calling this as well during frame detach.
12665 did_call_did_finish_document_load_ = true;
12666 }
12667
12668 void DidHandleOnloadEvents() override {
12669 // TODO(dcheng): Investigate not calling this as well during frame detach.
12670 did_call_did_handle_onload_events_ = true;
12671 }
12672
12673 void DidFinishLoad() override {
12674 EXPECT_TRUE(false) << "didFinishLoad() should not have been called.";
12675 }
12676
12677 bool DidCallFrameDetached() const { return did_call_frame_detached_; }
12678 bool DidCallDidStopLoading() const { return did_call_did_stop_loading_; }
12679 bool DidCallDidFinishDocumentLoad() const {
12680 return did_call_did_finish_document_load_;
12681 }
12682 bool DidCallDidHandleOnloadEvents() const {
12683 return did_call_did_handle_onload_events_;
12684 }
12685
12686 private:
12687 bool did_call_frame_detached_ = false;
12688 bool did_call_did_stop_loading_ = false;
12689 bool did_call_did_finish_document_load_ = false;
12690 bool did_call_did_handle_onload_events_ = false;
12691 };
12692
12693 class MainFrameClient : public frame_test_helpers::TestWebFrameClient {
12694 public:
12695 MainFrameClient() = default;
12696 ~MainFrameClient() override = default;
12697
12698 // frame_test_helpers::TestWebFrameClient:
12699 WebLocalFrame* CreateChildFrame(
12700 WebLocalFrame* parent,
12701 mojom::blink::TreeScopeType scope,
12702 const WebString& name,
12703 const WebString& fallback_name,
12704 const FramePolicy&,
12705 const WebFrameOwnerProperties&,
12706 mojom::blink::FrameOwnerElementType) override {
12707 return CreateLocalChild(*parent, scope, &child_client_);
12708 }
12709
12710 LoadingObserverFrameClient& ChildClient() { return child_client_; }
12711
12712 private:
12713 LoadingObserverFrameClient child_client_;
12714 };
12715
12716 RegisterMockedHttpURLLoad("single_iframe.html");
12717 url_test_helpers::RegisterMockedURLLoad(
12718 ToKURL(base_url_ + "visible_iframe.html"),
12719 test::CoreTestDataPath("frame_with_frame.html"));
12720 RegisterMockedHttpURLLoad("parent_detaching_frame.html");
12721
12722 frame_test_helpers::WebViewHelper web_view_helper;
12723 MainFrameClient main_frame_client;
12724 web_view_helper.InitializeAndLoad(base_url_ + "single_iframe.html",
12725 &main_frame_client);
12726
12727 EXPECT_TRUE(main_frame_client.ChildClient().DidCallFrameDetached());
12728 EXPECT_TRUE(main_frame_client.ChildClient().DidCallDidStopLoading());
12729 EXPECT_TRUE(main_frame_client.ChildClient().DidCallDidFinishDocumentLoad());
12730 EXPECT_TRUE(main_frame_client.ChildClient().DidCallDidHandleOnloadEvents());
12731
12732 web_view_helper.Reset();
12733 }
12734
TEST_F(WebFrameTest,ClearClosedOpener)12735 TEST_F(WebFrameTest, ClearClosedOpener) {
12736 frame_test_helpers::WebViewHelper opener_helper;
12737 opener_helper.Initialize();
12738 frame_test_helpers::WebViewHelper helper;
12739 helper.InitializeWithOpener(opener_helper.GetWebView()->MainFrame());
12740
12741 opener_helper.Reset();
12742 EXPECT_EQ(nullptr, helper.LocalMainFrame()->Opener());
12743 }
12744
12745 class ShowVirtualKeyboardObserverWidgetClient
12746 : public frame_test_helpers::TestWebWidgetClient {
12747 public:
12748 ShowVirtualKeyboardObserverWidgetClient() = default;
12749 ~ShowVirtualKeyboardObserverWidgetClient() override = default;
12750
12751 // frame_test_helpers::TestWebWidgetClient:
TextInputStateChanged(ui::mojom::blink::TextInputStatePtr state)12752 void TextInputStateChanged(
12753 ui::mojom::blink::TextInputStatePtr state) override {
12754 did_show_virtual_keyboard_ |= state->show_ime_if_needed;
12755 }
12756
DidShowVirtualKeyboard() const12757 bool DidShowVirtualKeyboard() const { return did_show_virtual_keyboard_; }
12758
12759 private:
12760 bool did_show_virtual_keyboard_ = false;
12761 };
12762
TEST_F(WebFrameTest,ShowVirtualKeyboardOnElementFocus)12763 TEST_F(WebFrameTest, ShowVirtualKeyboardOnElementFocus) {
12764 frame_test_helpers::WebViewHelper web_view_helper;
12765 web_view_helper.InitializeRemote();
12766
12767 ShowVirtualKeyboardObserverWidgetClient web_widget_client;
12768 WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
12769 *web_view_helper.RemoteMainFrame(), "child", WebFrameOwnerProperties(),
12770 nullptr, nullptr, &web_widget_client);
12771
12772 RegisterMockedHttpURLLoad("input_field_default.html");
12773 frame_test_helpers::LoadFrame(local_frame,
12774 base_url_ + "input_field_default.html");
12775
12776 // Simulate an input element focus leading to Element::focus() call with a
12777 // user gesture.
12778 LocalFrame::NotifyUserActivation(
12779 local_frame->GetFrame(), mojom::UserActivationNotificationType::kTest);
12780 local_frame->ExecuteScript(
12781 WebScriptSource("window.focus();"
12782 "document.querySelector('input').focus();"));
12783
12784 RunPendingTasks();
12785 // Verify that the right WebWidgetClient has been notified.
12786 #if BUILDFLAG(IS_ASH)
12787 EXPECT_FALSE(web_widget_client.DidShowVirtualKeyboard());
12788 #else
12789 EXPECT_TRUE(web_widget_client.DidShowVirtualKeyboard());
12790 #endif
12791 web_view_helper.Reset();
12792 }
12793
12794 class ContextMenuWebFrameClient
12795 : public frame_test_helpers::TestWebFrameClient {
12796 public:
12797 ContextMenuWebFrameClient() = default;
12798 ~ContextMenuWebFrameClient() override = default;
12799
12800 // WebLocalFrameClient:
ShowContextMenu(const WebContextMenuData & data,const base::Optional<gfx::Point> &)12801 void ShowContextMenu(const WebContextMenuData& data,
12802 const base::Optional<gfx::Point>&) override {
12803 menu_data_ = data;
12804 }
12805
GetMenuData()12806 WebContextMenuData GetMenuData() { return menu_data_; }
12807
12808 private:
12809 WebContextMenuData menu_data_;
12810 DISALLOW_COPY_AND_ASSIGN(ContextMenuWebFrameClient);
12811 };
12812
TestSelectAll(const std::string & html)12813 bool TestSelectAll(const std::string& html) {
12814 ContextMenuWebFrameClient frame;
12815 frame_test_helpers::WebViewHelper web_view_helper;
12816 WebViewImpl* web_view = web_view_helper.Initialize(&frame);
12817 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(), html,
12818 ToKURL("about:blank"));
12819 web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
12820 web_view->MainFrameWidget()->UpdateAllLifecyclePhases(
12821 DocumentUpdateReason::kTest);
12822 RunPendingTasks();
12823 web_view->MainFrameImpl()->GetFrame()->SetInitialFocus(false);
12824 RunPendingTasks();
12825
12826 WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
12827 WebInputEvent::kNoModifiers,
12828 WebInputEvent::GetStaticTimeStampForTests());
12829
12830 mouse_event.button = WebMouseEvent::Button::kRight;
12831 mouse_event.SetPositionInWidget(8, 8);
12832 mouse_event.click_count = 1;
12833 web_view->MainFrameWidget()->HandleInputEvent(
12834 WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()));
12835 RunPendingTasks();
12836 web_view_helper.Reset();
12837 return frame.GetMenuData().edit_flags &
12838 ContextMenuDataEditFlags::kCanSelectAll;
12839 }
12840
TEST_F(WebFrameTest,ContextMenuDataSelectAll)12841 TEST_F(WebFrameTest, ContextMenuDataSelectAll) {
12842 EXPECT_FALSE(TestSelectAll("<textarea></textarea>"));
12843 EXPECT_TRUE(TestSelectAll("<textarea>nonempty</textarea>"));
12844 EXPECT_FALSE(TestSelectAll("<input>"));
12845 EXPECT_TRUE(TestSelectAll("<input value='nonempty'>"));
12846 EXPECT_FALSE(TestSelectAll("<div contenteditable></div>"));
12847 EXPECT_TRUE(TestSelectAll("<div contenteditable>nonempty</div>"));
12848 EXPECT_TRUE(TestSelectAll("<div contenteditable>\n</div>"));
12849 }
12850
TEST_F(WebFrameTest,ContextMenuDataSelectedText)12851 TEST_F(WebFrameTest, ContextMenuDataSelectedText) {
12852 ContextMenuWebFrameClient frame;
12853 frame_test_helpers::WebViewHelper web_view_helper;
12854 WebViewImpl* web_view = web_view_helper.Initialize(&frame);
12855 const std::string& html = "<input value=' '>";
12856 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(), html,
12857 ToKURL("about:blank"));
12858 web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
12859 UpdateAllLifecyclePhases(web_view);
12860 RunPendingTasks();
12861 web_view->MainFrameImpl()->GetFrame()->SetInitialFocus(false);
12862 RunPendingTasks();
12863
12864 web_view->MainFrameImpl()->ExecuteCommand(WebString::FromUTF8("SelectAll"));
12865
12866 WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
12867 WebInputEvent::kNoModifiers,
12868 WebInputEvent::GetStaticTimeStampForTests());
12869
12870 mouse_event.button = WebMouseEvent::Button::kRight;
12871 mouse_event.SetPositionInWidget(8, 8);
12872 mouse_event.click_count = 1;
12873 web_view->MainFrameWidget()->HandleInputEvent(
12874 WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()));
12875 RunPendingTasks();
12876 web_view_helper.Reset();
12877 EXPECT_EQ(frame.GetMenuData().selected_text, " ");
12878 }
12879
TEST_F(WebFrameTest,ContextMenuDataPasswordSelectedText)12880 TEST_F(WebFrameTest, ContextMenuDataPasswordSelectedText) {
12881 ContextMenuWebFrameClient frame;
12882 frame_test_helpers::WebViewHelper web_view_helper;
12883 WebViewImpl* web_view = web_view_helper.Initialize(&frame);
12884 const std::string& html = "<input type='password' value='password'>";
12885 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(), html,
12886 ToKURL("about:blank"));
12887 web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
12888 UpdateAllLifecyclePhases(web_view);
12889 RunPendingTasks();
12890 web_view->MainFrameImpl()->GetFrame()->SetInitialFocus(false);
12891 RunPendingTasks();
12892
12893 web_view->MainFrameImpl()->ExecuteCommand(WebString::FromUTF8("SelectAll"));
12894
12895 WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
12896 WebInputEvent::kNoModifiers,
12897 WebInputEvent::GetStaticTimeStampForTests());
12898
12899 mouse_event.button = WebMouseEvent::Button::kRight;
12900 mouse_event.SetPositionInWidget(8, 8);
12901 mouse_event.click_count = 1;
12902 web_view->MainFrameWidget()->HandleInputEvent(
12903 WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()));
12904
12905 RunPendingTasks();
12906 web_view_helper.Reset();
12907 EXPECT_EQ(frame.GetMenuData().input_field_type,
12908 blink::ContextMenuDataInputFieldType::kPassword);
12909 EXPECT_FALSE(frame.GetMenuData().selected_text.IsEmpty());
12910 }
12911
TEST_F(WebFrameTest,ContextMenuDataNonLocatedMenu)12912 TEST_F(WebFrameTest, ContextMenuDataNonLocatedMenu) {
12913 ContextMenuWebFrameClient frame;
12914 frame_test_helpers::WebViewHelper web_view_helper;
12915 WebViewImpl* web_view = web_view_helper.Initialize(&frame);
12916 const std::string& html =
12917 "<div style='font-size: 1000%; line-height: 0.7em'>Select me<br/>"
12918 "Next line</div>";
12919 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(), html,
12920 ToKURL("about:blank"));
12921 web_view->MainFrameViewWidget()->Resize(gfx::Size(500, 300));
12922 UpdateAllLifecyclePhases(web_view);
12923 RunPendingTasks();
12924 web_view->MainFrameImpl()->GetFrame()->SetInitialFocus(false);
12925 RunPendingTasks();
12926
12927 WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
12928 WebInputEvent::kNoModifiers,
12929 WebInputEvent::GetStaticTimeStampForTests());
12930
12931 mouse_event.button = WebMouseEvent::Button::kLeft;
12932 mouse_event.SetPositionInWidget(0, 0);
12933 mouse_event.click_count = 2;
12934 web_view->MainFrameWidget()->HandleInputEvent(
12935 WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()));
12936
12937 web_view->MainFrameImpl()->LocalRootFrameWidget()->ShowContextMenu(
12938 ui::mojom::MenuSourceType::TOUCH,
12939 web_view->MainFrameImpl()->GetPositionInViewportForTesting());
12940
12941 RunPendingTasks();
12942 web_view_helper.Reset();
12943 EXPECT_EQ(frame.GetMenuData().source_type, kMenuSourceTouch);
12944 EXPECT_FALSE(frame.GetMenuData().selected_text.IsEmpty());
12945 }
12946
TEST_F(WebFrameTest,LocalFrameWithRemoteParentIsTransparent)12947 TEST_F(WebFrameTest, LocalFrameWithRemoteParentIsTransparent) {
12948 frame_test_helpers::WebViewHelper helper;
12949 helper.InitializeRemote();
12950
12951 WebLocalFrameImpl* local_frame =
12952 frame_test_helpers::CreateLocalChild(*helper.RemoteMainFrame());
12953 frame_test_helpers::LoadFrame(local_frame, "data:text/html,some page");
12954
12955 // Local frame with remote parent should have transparent baseBackgroundColor.
12956 Color color = local_frame->GetFrameView()->BaseBackgroundColor();
12957 EXPECT_EQ(Color::kTransparent, color);
12958 }
12959
12960 class TestFallbackWebFrameClient
12961 : public frame_test_helpers::TestWebFrameClient {
12962 public:
TestFallbackWebFrameClient()12963 TestFallbackWebFrameClient() : child_client_(nullptr) {}
12964 ~TestFallbackWebFrameClient() override = default;
12965
SetChildWebFrameClient(TestFallbackWebFrameClient * client)12966 void SetChildWebFrameClient(TestFallbackWebFrameClient* client) {
12967 child_client_ = client;
12968 }
12969
12970 // frame_test_helpers::TestWebFrameClient:
CreateChildFrame(WebLocalFrame * parent,mojom::blink::TreeScopeType scope,const WebString &,const WebString &,const FramePolicy &,const WebFrameOwnerProperties & frameOwnerProperties,mojom::blink::FrameOwnerElementType)12971 WebLocalFrame* CreateChildFrame(
12972 WebLocalFrame* parent,
12973 mojom::blink::TreeScopeType scope,
12974 const WebString&,
12975 const WebString&,
12976 const FramePolicy&,
12977 const WebFrameOwnerProperties& frameOwnerProperties,
12978 mojom::blink::FrameOwnerElementType) override {
12979 DCHECK(child_client_);
12980 return CreateLocalChild(*parent, scope, child_client_);
12981 }
BeginNavigation(std::unique_ptr<WebNavigationInfo> info)12982 void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override {
12983 if (child_client_ || KURL(info->url_request.Url()) == BlankURL()) {
12984 TestWebFrameClient::BeginNavigation(std::move(info));
12985 return;
12986 }
12987 Frame()->WillStartNavigation(*info);
12988 }
12989
12990 private:
12991 TestFallbackWebFrameClient* child_client_;
12992 };
12993
TEST_F(WebFrameTest,FallbackForNonexistentProvisionalNavigation)12994 TEST_F(WebFrameTest, FallbackForNonexistentProvisionalNavigation) {
12995 RegisterMockedHttpURLLoad("fallback.html");
12996 TestFallbackWebFrameClient main_client;
12997 TestFallbackWebFrameClient child_client;
12998 main_client.SetChildWebFrameClient(&child_client);
12999
13000 frame_test_helpers::WebViewHelper web_view_helper_;
13001 web_view_helper_.Initialize(&main_client);
13002
13003 WebLocalFrameImpl* main_frame = web_view_helper_.LocalMainFrame();
13004 WebURLRequest request(ToKURL(base_url_ + "fallback.html"));
13005 main_frame->StartNavigation(request);
13006
13007 // Because the child frame will have placeholder document loader, the main
13008 // frame will not finish loading, so
13009 // frame_test_helpers::PumpPendingRequestsForFrameToLoad doesn't work here.
13010 url_test_helpers::ServeAsynchronousRequests();
13011
13012 // Overwrite the client-handled child frame navigation with about:blank.
13013 WebLocalFrame* child = main_frame->FirstChild()->ToWebLocalFrame();
13014 frame_test_helpers::LoadFrameDontWait(child, BlankURL());
13015
13016 // Failing the original child frame navigation and trying to render fallback
13017 // content shouldn't crash. It should return NoLoadInProgress. This is so the
13018 // caller won't attempt to replace the correctly empty frame with an error
13019 // page.
13020 EXPECT_EQ(WebNavigationControl::NoLoadInProgress,
13021 To<WebLocalFrameImpl>(child)->MaybeRenderFallbackContent(
13022 WebURLError(ResourceError::Failure(request.Url()))));
13023 }
13024
TEST_F(WebFrameTest,AltTextOnAboutBlankPage)13025 TEST_F(WebFrameTest, AltTextOnAboutBlankPage) {
13026 frame_test_helpers::WebViewHelper web_view_helper;
13027 web_view_helper.InitializeAndLoad("about:blank");
13028 web_view_helper.Resize(gfx::Size(640, 480));
13029 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13030
13031 const char kSource[] =
13032 "<img id='foo' src='foo' alt='foo alt' width='200' height='200'>";
13033 frame_test_helpers::LoadHTMLString(frame, kSource, ToKURL("about:blank"));
13034 UpdateAllLifecyclePhases(web_view_helper.GetWebView());
13035 RunPendingTasks();
13036
13037 // Check LayoutText with alt text "foo alt"
13038 LayoutObject* layout_object = frame->GetFrame()
13039 ->GetDocument()
13040 ->getElementById("foo")
13041 ->GetLayoutObject()
13042 ->SlowFirstChild();
13043 String text = "";
13044 for (LayoutObject* obj = layout_object; obj; obj = obj->NextInPreOrder()) {
13045 if (obj->IsText()) {
13046 text = To<LayoutText>(obj)->GetText();
13047 break;
13048 }
13049 }
13050 EXPECT_EQ("foo alt", text.Utf8());
13051 }
13052
TEST_F(WebFrameTest,NavigatorPluginsClearedWhenPluginsDisabled)13053 TEST_F(WebFrameTest, NavigatorPluginsClearedWhenPluginsDisabled) {
13054 ScopedFakePluginRegistry fake_plugins;
13055 frame_test_helpers::WebViewHelper web_view_helper;
13056 web_view_helper.Initialize();
13057 v8::Isolate* isolate = v8::Isolate::GetCurrent();
13058 v8::Local<v8::Context> context = isolate->GetCurrentContext();
13059 v8::HandleScope scope(isolate);
13060 v8::Local<v8::Value> result =
13061 web_view_helper.LocalMainFrame()->ExecuteScriptAndReturnValue(
13062 WebScriptSource("navigator.plugins.length"));
13063 EXPECT_NE(0, result->Int32Value(context).ToChecked());
13064 web_view_helper.GetWebView()->GetPage()->GetSettings().SetPluginsEnabled(
13065 false);
13066 result = web_view_helper.LocalMainFrame()->ExecuteScriptAndReturnValue(
13067 WebScriptSource("navigator.plugins.length"));
13068 EXPECT_EQ(0, result->Int32Value(context).ToChecked());
13069 }
13070
TEST_F(WebFrameTest,RecordSameDocumentNavigationToHistogram)13071 TEST_F(WebFrameTest, RecordSameDocumentNavigationToHistogram) {
13072 const char* histogramName =
13073 "RendererScheduler.UpdateForSameDocumentNavigationCount";
13074 frame_test_helpers::WebViewHelper web_view_helper;
13075 HistogramTester tester;
13076 web_view_helper.InitializeAndLoad("about:blank");
13077 auto* frame =
13078 To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame());
13079
13080 DocumentLoader& document_loader = *web_view_helper.GetWebView()
13081 ->MainFrameImpl()
13082 ->GetFrame()
13083 ->GetDocument()
13084 ->Loader();
13085 scoped_refptr<SerializedScriptValue> message =
13086 SerializeString("message", ToScriptStateForMainWorld(frame));
13087 tester.ExpectTotalCount(histogramName, 0);
13088 document_loader.UpdateForSameDocumentNavigation(
13089 ToKURL("about:blank"), kSameDocumentNavigationHistoryApi, message,
13090 mojom::blink::ScrollRestorationType::kAuto,
13091 WebFrameLoadType::kReplaceCurrentItem, frame->GetDocument());
13092 // The bucket index corresponds to the definition of
13093 // |SinglePageAppNavigationType|.
13094 tester.ExpectBucketCount(histogramName,
13095 kSPANavTypeHistoryPushStateOrReplaceState, 1);
13096 document_loader.UpdateForSameDocumentNavigation(
13097 ToKURL("about:blank"), kSameDocumentNavigationDefault, message,
13098 mojom::blink::ScrollRestorationType::kManual,
13099 WebFrameLoadType::kBackForward, frame->GetDocument());
13100 tester.ExpectBucketCount(histogramName,
13101 kSPANavTypeSameDocumentBackwardOrForward, 1);
13102 document_loader.UpdateForSameDocumentNavigation(
13103 ToKURL("about:blank"), kSameDocumentNavigationDefault, message,
13104 mojom::blink::ScrollRestorationType::kManual,
13105 WebFrameLoadType::kReplaceCurrentItem, frame->GetDocument());
13106 tester.ExpectBucketCount(histogramName, kSPANavTypeOtherFragmentNavigation,
13107 1);
13108 // kSameDocumentNavigationHistoryApi and WebFrameLoadType::kBackForward is an
13109 // illegal combination, which has been caught by DCHECK in
13110 // UpdateForSameDocumentNavigation().
13111
13112 tester.ExpectTotalCount(histogramName, 3);
13113 }
13114
TestFramePrinting(WebLocalFrameImpl * frame)13115 static void TestFramePrinting(WebLocalFrameImpl* frame) {
13116 WebPrintParams print_params;
13117 WebSize page_size(500, 500);
13118 print_params.print_content_area.width = page_size.width;
13119 print_params.print_content_area.height = page_size.height;
13120 EXPECT_EQ(1u, frame->PrintBegin(print_params, WebNode()));
13121 PaintRecorder recorder;
13122 frame->PrintPagesForTesting(recorder.beginRecording(IntRect()), page_size,
13123 page_size);
13124 frame->PrintEnd();
13125 }
13126
TEST_F(WebFrameTest,PrintDetachedIframe)13127 TEST_F(WebFrameTest, PrintDetachedIframe) {
13128 RegisterMockedHttpURLLoad("print-detached-iframe.html");
13129 frame_test_helpers::WebViewHelper web_view_helper;
13130 web_view_helper.InitializeAndLoad(base_url_ + "print-detached-iframe.html");
13131 TestFramePrinting(
13132 To<WebLocalFrameImpl>(web_view_helper.LocalMainFrame()->FirstChild()));
13133 }
13134
TEST_F(WebFrameTest,PrintIframeUnderDetached)13135 TEST_F(WebFrameTest, PrintIframeUnderDetached) {
13136 RegisterMockedHttpURLLoad("print-detached-iframe.html");
13137 frame_test_helpers::WebViewHelper web_view_helper;
13138 web_view_helper.InitializeAndLoad(base_url_ + "print-detached-iframe.html");
13139 TestFramePrinting(To<WebLocalFrameImpl>(
13140 web_view_helper.LocalMainFrame()->FirstChild()->FirstChild()));
13141 }
13142
13143 namespace {
13144
13145 struct TextRunDOMNodeIdInfo {
13146 int glyph_len;
13147 DOMNodeId dom_node_id;
13148 };
13149
13150 // Given a PaintRecord and a starting DOMNodeId, recursively iterate over all of
13151 // the (nested) paint ops, and populate |text_runs| with the number of glyphs
13152 // and the DOMNodeId of each text run.
RecursiveCollectTextRunDOMNodeIds(sk_sp<const PaintRecord> paint_record,DOMNodeId dom_node_id,std::vector<TextRunDOMNodeIdInfo> * text_runs)13153 void RecursiveCollectTextRunDOMNodeIds(
13154 sk_sp<const PaintRecord> paint_record,
13155 DOMNodeId dom_node_id,
13156 std::vector<TextRunDOMNodeIdInfo>* text_runs) {
13157 for (cc::PaintOpBuffer::Iterator it(paint_record.get()); it; ++it) {
13158 if ((*it)->GetType() == cc::PaintOpType::DrawRecord) {
13159 cc::DrawRecordOp* draw_record_op = static_cast<cc::DrawRecordOp*>(*it);
13160 RecursiveCollectTextRunDOMNodeIds(draw_record_op->record, dom_node_id,
13161 text_runs);
13162 } else if ((*it)->GetType() == cc::PaintOpType::SetNodeId) {
13163 cc::SetNodeIdOp* set_node_id_op = static_cast<cc::SetNodeIdOp*>(*it);
13164 dom_node_id = set_node_id_op->node_id;
13165 } else if ((*it)->GetType() == cc::PaintOpType::DrawTextBlob) {
13166 cc::DrawTextBlobOp* draw_text_op = static_cast<cc::DrawTextBlobOp*>(*it);
13167 SkTextBlob::Iter iter(*draw_text_op->blob);
13168 SkTextBlob::Iter::Run run;
13169 while (iter.next(&run)) {
13170 TextRunDOMNodeIdInfo text_run_info;
13171 text_run_info.glyph_len = run.fGlyphCount;
13172 text_run_info.dom_node_id = dom_node_id;
13173 text_runs->push_back(text_run_info);
13174 }
13175 }
13176 }
13177 }
13178
13179 } // namespace
13180
TEST_F(WebFrameTest,FirstLetterHasDOMNodeIdWhenPrinting)13181 TEST_F(WebFrameTest, FirstLetterHasDOMNodeIdWhenPrinting) {
13182 // When printing, every DrawText painting op needs to have an associated
13183 // DOM Node ID. This test ensures that when the first-letter style is used,
13184 // the drawing op for the first letter is correctly associated with the same
13185 // DOM Node ID as the following text.
13186
13187 // Load a web page with two elements containing the text
13188 // "Hello" and "World", where "World" has a first-letter style.
13189 RegisterMockedHttpURLLoad("first-letter.html");
13190 frame_test_helpers::WebViewHelper web_view_helper;
13191 web_view_helper.InitializeAndLoad(base_url_ + "first-letter.html");
13192
13193 // Print the page and capture the PaintRecord.
13194 WebPrintParams print_params;
13195 WebSize page_size(500, 500);
13196 print_params.print_content_area.width = page_size.width;
13197 print_params.print_content_area.height = page_size.height;
13198 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13199 EXPECT_EQ(1u, frame->PrintBegin(print_params, WebNode()));
13200 PaintRecorder recorder;
13201 frame->PrintPagesForTesting(recorder.beginRecording(IntRect()), page_size,
13202 page_size);
13203 frame->PrintEnd();
13204 sk_sp<PaintRecord> paint_record = recorder.finishRecordingAsPicture();
13205
13206 // Unpack the paint record and collect info about the text runs.
13207 std::vector<TextRunDOMNodeIdInfo> text_runs;
13208 RecursiveCollectTextRunDOMNodeIds(paint_record, 0, &text_runs);
13209
13210 // The first text run should be "Hello".
13211 ASSERT_EQ(3U, text_runs.size());
13212 EXPECT_EQ(5, text_runs[0].glyph_len);
13213 EXPECT_NE(kInvalidDOMNodeId, text_runs[0].dom_node_id);
13214
13215 // The second text run should be "W", the first letter of "World".
13216 EXPECT_EQ(1, text_runs[1].glyph_len);
13217 EXPECT_NE(kInvalidDOMNodeId, text_runs[1].dom_node_id);
13218
13219 // The last text run should be "orld", the rest of "World".
13220 EXPECT_EQ(4, text_runs[2].glyph_len);
13221 EXPECT_NE(kInvalidDOMNodeId, text_runs[2].dom_node_id);
13222
13223 // The second and third text runs should have the same DOM Node ID.
13224 EXPECT_EQ(text_runs[1].dom_node_id, text_runs[2].dom_node_id);
13225 }
13226
TEST_F(WebFrameTest,RightClickActivatesForExecuteCommand)13227 TEST_F(WebFrameTest, RightClickActivatesForExecuteCommand) {
13228 frame_test_helpers::WebViewHelper web_view_helper;
13229 WebViewImpl* web_view = web_view_helper.InitializeAndLoad("about:blank");
13230 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13231
13232 // Setup a mock clipboard host.
13233 PageTestBase::MockClipboardHostProvider mock_clipboard_host_provider(
13234 frame->GetFrame()->GetBrowserInterfaceBroker());
13235
13236 EXPECT_FALSE(frame->GetFrame()->HasStickyUserActivation());
13237 frame->ExecuteScript(
13238 WebScriptSource(WebString("document.execCommand('copy');")));
13239 EXPECT_FALSE(frame->GetFrame()->HasStickyUserActivation());
13240
13241 // Right-click to activate the page.
13242 WebMouseEvent mouse_event(WebInputEvent::Type::kMouseDown,
13243 WebInputEvent::kNoModifiers,
13244 WebInputEvent::GetStaticTimeStampForTests());
13245 mouse_event.button = WebMouseEvent::Button::kRight;
13246 mouse_event.click_count = 1;
13247 web_view->MainFrameWidget()->HandleInputEvent(
13248 WebCoalescedInputEvent(mouse_event, ui::LatencyInfo()));
13249 RunPendingTasks();
13250
13251 frame->ExecuteCommand(WebString::FromUTF8("Paste"));
13252 EXPECT_TRUE(frame->GetFrame()->HasStickyUserActivation());
13253 }
13254
TEST_F(WebFrameTest,GetCanonicalUrlForSharingNone)13255 TEST_F(WebFrameTest, GetCanonicalUrlForSharingNone) {
13256 frame_test_helpers::WebViewHelper web_view_helper;
13257 web_view_helper.InitializeAndLoad("about:blank");
13258 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13259 EXPECT_TRUE(frame->GetDocument().CanonicalUrlForSharing().IsNull());
13260 }
13261
TEST_F(WebFrameTest,GetCanonicalUrlForSharingNotInHead)13262 TEST_F(WebFrameTest, GetCanonicalUrlForSharingNotInHead) {
13263 frame_test_helpers::WebViewHelper web_view_helper;
13264 web_view_helper.Initialize();
13265 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13266 frame_test_helpers::LoadHTMLString(
13267 frame, R"(
13268 <body>
13269 <link rel="canonical" href="https://example.com/canonical.html">
13270 </body>)",
13271 ToKURL("https://example.com/test_page.html"));
13272 EXPECT_TRUE(frame->GetDocument().CanonicalUrlForSharing().IsNull());
13273 }
13274
TEST_F(WebFrameTest,GetCanonicalUrlForSharing)13275 TEST_F(WebFrameTest, GetCanonicalUrlForSharing) {
13276 frame_test_helpers::WebViewHelper web_view_helper;
13277 web_view_helper.Initialize();
13278 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13279 frame_test_helpers::LoadHTMLString(
13280 frame, R"(
13281 <head>
13282 <link rel="canonical" href="https://example.com/canonical.html">
13283 </head>)",
13284 ToKURL("https://example.com/test_page.html"));
13285 EXPECT_EQ(WebURL(ToKURL("https://example.com/canonical.html")),
13286 frame->GetDocument().CanonicalUrlForSharing());
13287 }
13288
TEST_F(WebFrameTest,GetCanonicalUrlForSharingMultiple)13289 TEST_F(WebFrameTest, GetCanonicalUrlForSharingMultiple) {
13290 frame_test_helpers::WebViewHelper web_view_helper;
13291 web_view_helper.Initialize();
13292 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13293 frame_test_helpers::LoadHTMLString(
13294 frame, R"(
13295 <head>
13296 <link rel="canonical" href="https://example.com/canonical1.html">
13297 <link rel="canonical" href="https://example.com/canonical2.html">
13298 </head>)",
13299 ToKURL("https://example.com/test_page.html"));
13300 EXPECT_EQ(WebURL(ToKURL("https://example.com/canonical1.html")),
13301 frame->GetDocument().CanonicalUrlForSharing());
13302 }
13303
TEST_F(WebFrameTest,NavigationTimingInfo)13304 TEST_F(WebFrameTest, NavigationTimingInfo) {
13305 RegisterMockedHttpURLLoad("foo.html");
13306 frame_test_helpers::WebViewHelper web_view_helper;
13307 web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
13308 ResourceTimingInfo* navigation_timing_info = web_view_helper.LocalMainFrame()
13309 ->GetFrame()
13310 ->Loader()
13311 .GetDocumentLoader()
13312 ->GetNavigationTimingInfo();
13313 EXPECT_EQ(navigation_timing_info->TransferSize(), static_cast<uint64_t>(34));
13314 }
13315
TEST_F(WebFrameSimTest,EnterFullscreenResetScrollAndScaleState)13316 TEST_F(WebFrameSimTest, EnterFullscreenResetScrollAndScaleState) {
13317 UseAndroidSettings();
13318 WebView().MainFrameViewWidget()->Resize(gfx::Size(490, 500));
13319 WebView().EnableFakePageScaleAnimationForTesting(true);
13320 WebView().GetSettings()->SetTextAutosizingEnabled(false);
13321 WebView().SetDefaultPageScaleLimits(0.5f, 4);
13322
13323 SimRequest request("https://example.com/test.html", "text/html");
13324 LoadURL("https://example.com/test.html");
13325 request.Complete(R"HTML(
13326 <!DOCTYPE html>
13327 <style>
13328 body {
13329 margin: 0px;
13330 width: 10000px;
13331 height: 10000px;
13332 }
13333 </style>
13334 )HTML");
13335
13336 Compositor().BeginFrame();
13337
13338 // Make the page scale and scroll with the given parameters.
13339 EXPECT_EQ(0.5f, WebView().PageScaleFactor());
13340 WebView().SetPageScaleFactor(2.0f);
13341 WebView().MainFrameImpl()->SetScrollOffset(WebSize(94, 111));
13342 WebView().SetVisualViewportOffset(gfx::PointF(12, 20));
13343 EXPECT_EQ(2.0f, WebView().PageScaleFactor());
13344 EXPECT_EQ(94, WebView().MainFrameImpl()->GetScrollOffset().width);
13345 EXPECT_EQ(111, WebView().MainFrameImpl()->GetScrollOffset().height);
13346 EXPECT_EQ(12, WebView().VisualViewportOffset().x());
13347 EXPECT_EQ(20, WebView().VisualViewportOffset().y());
13348
13349 auto* frame = To<LocalFrame>(WebView().GetPage()->MainFrame());
13350 Element* element = frame->GetDocument()->body();
13351 LocalFrame::NotifyUserActivation(
13352 frame, mojom::UserActivationNotificationType::kTest);
13353 Fullscreen::RequestFullscreen(*element);
13354 WebView().DidEnterFullscreen();
13355
13356 // Page scale factor must be 1.0 during fullscreen for elements to be sized
13357 // properly.
13358 EXPECT_EQ(1.0f, WebView().PageScaleFactor());
13359
13360 // Confirm that exiting fullscreen restores back to default values.
13361 WebView().DidExitFullscreen();
13362 WebView().MainFrameWidget()->UpdateAllLifecyclePhases(
13363 DocumentUpdateReason::kTest);
13364
13365 EXPECT_EQ(0.5f, WebView().PageScaleFactor());
13366 EXPECT_EQ(94, WebView().MainFrameImpl()->GetScrollOffset().width);
13367 EXPECT_EQ(111, WebView().MainFrameImpl()->GetScrollOffset().height);
13368 EXPECT_EQ(0, WebView().VisualViewportOffset().x());
13369 EXPECT_EQ(0, WebView().VisualViewportOffset().y());
13370 }
13371
TEST_F(WebFrameSimTest,GetPageSizeType)13372 TEST_F(WebFrameSimTest, GetPageSizeType) {
13373 WebView().MainFrameViewWidget()->Resize(gfx::Size(500, 500));
13374
13375 SimRequest request("https://example.com/test.html", "text/html");
13376 LoadURL("https://example.com/test.html");
13377 request.Complete(R"HTML(
13378 <!DOCTYPE html>
13379 <style>
13380 @page {}
13381 </style>
13382 )HTML");
13383
13384 Compositor().BeginFrame();
13385 RunPendingTasks();
13386
13387 const struct {
13388 const char* size;
13389 PageSizeType page_size_type;
13390 } test_cases[] = {
13391 {"auto", PageSizeType::kAuto},
13392 {"portrait", PageSizeType::kPortrait},
13393 {"landscape", PageSizeType::kLandscape},
13394 {"a4", PageSizeType::kFixed},
13395 {"letter", PageSizeType::kFixed},
13396 {"a4 portrait", PageSizeType::kFixed},
13397 {"letter landscape", PageSizeType::kFixed},
13398 {"10in", PageSizeType::kFixed},
13399 {"10in 12cm", PageSizeType::kFixed},
13400 };
13401
13402 auto* main_frame = WebView().MainFrame()->ToWebLocalFrame();
13403 auto* doc = To<LocalFrame>(WebView().GetPage()->MainFrame())->GetDocument();
13404 auto* sheet = To<CSSStyleSheet>(doc->StyleSheets().item(0));
13405 CSSStyleDeclaration* style_decl =
13406 To<CSSPageRule>(sheet->cssRules(ASSERT_NO_EXCEPTION)->item(0))->style();
13407
13408 // Initially empty @page rule.
13409 EXPECT_EQ(PageSizeType::kAuto, main_frame->GetPageSizeType(1));
13410
13411 for (const auto& test : test_cases) {
13412 style_decl->setProperty(doc->GetExecutionContext(), "size", test.size, "",
13413 ASSERT_NO_EXCEPTION);
13414 EXPECT_EQ(test.page_size_type, main_frame->GetPageSizeType(1));
13415 }
13416 }
13417
TEST_F(WebFrameSimTest,PageOrientation)13418 TEST_F(WebFrameSimTest, PageOrientation) {
13419 ScopedNamedPagesForTest named_pages_enabler(true);
13420 gfx::Size page_size(500, 500);
13421 WebView().MainFrameWidget()->Resize(page_size);
13422
13423 SimRequest request("https://example.com/test.html", "text/html");
13424 LoadURL("https://example.com/test.html");
13425 request.Complete(R"HTML(
13426 <!DOCTYPE html>
13427 <style>
13428 @page upright { page-orientation: upright; }
13429 @page clockwise { page-orientation: rotate-right; }
13430 @page counter-clockwise { page-orientation: rotate-left; }
13431 div { height: 10px; }
13432 </style>
13433 <!-- First page: -->
13434 <div style="page:upright;"></div>
13435 <!-- Second page: -->
13436 <div style="page:counter-clockwise;"></div>
13437 <!-- Third page: -->
13438 <div style="page:clockwise;"></div>
13439 <div style="page:clockwise;"></div>
13440 <!-- Fourth page: -->
13441 <div></div>
13442 )HTML");
13443
13444 Compositor().BeginFrame();
13445 RunPendingTasks();
13446
13447 auto* frame = WebView().MainFrame()->ToWebLocalFrame();
13448 WebPrintParams print_params;
13449 print_params.print_content_area.width = page_size.width();
13450 print_params.print_content_area.height = page_size.height();
13451 EXPECT_EQ(4u, frame->PrintBegin(print_params, WebNode()));
13452
13453 WebPrintPageDescription description;
13454
13455 frame->GetPageDescription(0, &description);
13456 EXPECT_EQ(description.orientation, PageOrientation::kUpright);
13457
13458 frame->GetPageDescription(1, &description);
13459 EXPECT_EQ(description.orientation, PageOrientation::kRotateLeft);
13460
13461 frame->GetPageDescription(2, &description);
13462 EXPECT_EQ(description.orientation, PageOrientation::kRotateRight);
13463
13464 frame->GetPageDescription(3, &description);
13465 EXPECT_EQ(description.orientation, PageOrientation::kUpright);
13466
13467 frame->PrintEnd();
13468 }
13469
TEST_F(WebFrameSimTest,MainFrameTransformOffsetPixelSnapped)13470 TEST_F(WebFrameSimTest, MainFrameTransformOffsetPixelSnapped) {
13471 SimRequest request("https://example.com/test.html", "text/html");
13472 LoadURL("https://example.com/test.html");
13473 request.Complete(R"HTML(
13474 <!DOCTYPE html>
13475 <iframe id="iframe" style="position:absolute;top:7px;left:13.5px;border:none"></iframe>
13476 )HTML");
13477 frame_test_helpers::TestWebRemoteFrameClient remote_frame_client;
13478 TestViewportIntersection remote_frame_host;
13479 remote_frame_host.Init(remote_frame_client.GetRemoteAssociatedInterfaces());
13480 WebRemoteFrame* remote_frame =
13481 frame_test_helpers::CreateRemote(&remote_frame_client);
13482 MainFrame().FirstChild()->Swap(remote_frame);
13483 Compositor().BeginFrame();
13484 RunPendingTasks();
13485 EXPECT_TRUE(remote_frame_host.GetIntersectionState()
13486 ->main_frame_transform.IsIdentityOrIntegerTranslation());
13487 EXPECT_EQ(remote_frame_host.GetIntersectionState()
13488 ->main_frame_transform.matrix()
13489 .get(0, 3),
13490 14.f);
13491 EXPECT_EQ(remote_frame_host.GetIntersectionState()
13492 ->main_frame_transform.matrix()
13493 .get(1, 3),
13494 7.f);
13495 MainFrame().FirstChild()->Detach();
13496 }
13497
TEST_F(WebFrameTest,MediaQueriesInLocalFrameInsideRemote)13498 TEST_F(WebFrameTest, MediaQueriesInLocalFrameInsideRemote) {
13499 frame_test_helpers::WebViewHelper helper;
13500 helper.InitializeRemote();
13501
13502 FixedLayoutTestWebWidgetClient client;
13503 client.screen_info_.is_monochrome = false;
13504 client.screen_info_.depth_per_component = 8;
13505
13506 WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
13507 *helper.RemoteMainFrame(), WebString(), WebFrameOwnerProperties(),
13508 nullptr, nullptr, &client);
13509
13510 ASSERT_TRUE(local_frame->GetFrame());
13511 MediaValues* media_values =
13512 MediaValues::CreateDynamicIfFrameExists(local_frame->GetFrame());
13513 ASSERT_TRUE(media_values);
13514 EXPECT_EQ(0, media_values->MonochromeBitsPerComponent());
13515 EXPECT_EQ(8, media_values->ColorBitsPerComponent());
13516 // Need to explicitly reset helper to make sure local_frame is not deleted
13517 // first.
13518 helper.Reset();
13519 }
13520
TEST_F(WebFrameTest,RemoteViewportAndMainframeIntersections)13521 TEST_F(WebFrameTest, RemoteViewportAndMainframeIntersections) {
13522 frame_test_helpers::WebViewHelper helper;
13523 helper.InitializeRemote();
13524 WebLocalFrameImpl* local_frame = frame_test_helpers::CreateLocalChild(
13525 *helper.RemoteMainFrame(), "frameName");
13526 frame_test_helpers::LoadHTMLString(local_frame, R"HTML(
13527 <!DOCTYPE html>
13528 <style>
13529 #target {
13530 position: absolute;
13531 top: 10px;
13532 left: 20px;
13533 width: 200px;
13534 height: 100px;
13535 }
13536 </style>
13537 <div id=target></div>
13538 )HTML",
13539 ToKURL("about:blank"));
13540
13541 Element* target =
13542 local_frame->GetFrame()->GetDocument()->getElementById("target");
13543 ASSERT_TRUE(target);
13544 ASSERT_TRUE(target->GetLayoutObject());
13545
13546 // Simulate the local child frame being positioned at (7, -11) in the parent's
13547 // viewport, indicating that the top 11px of the child's content is clipped
13548 // by the parent. Let the local child frame be at (7, 40) in the parent
13549 // element.
13550 WebFrameWidget* widget = local_frame->FrameWidget();
13551 ASSERT_TRUE(widget);
13552 gfx::Transform viewport_transform;
13553 viewport_transform.Translate(7, -11);
13554 gfx::Rect viewport_intersection(0, 11, 200, 89);
13555 gfx::Rect mainframe_intersection(0, 0, 200, 140);
13556 blink::mojom::FrameOcclusionState occlusion_state =
13557 blink::mojom::FrameOcclusionState::kUnknown;
13558
13559 static_cast<WebFrameWidgetBase*>(widget)->SetRemoteViewportIntersection(
13560 {viewport_intersection, mainframe_intersection, viewport_intersection,
13561 occlusion_state, gfx::Size(), gfx::Point(), viewport_transform});
13562
13563 // The viewport intersection should be applied by the layout geometry mapping
13564 // code when these flags are used.
13565 int viewport_intersection_flags =
13566 kTraverseDocumentBoundaries | kApplyRemoteMainFrameTransform;
13567
13568 // Expectation is: (target location) + (viewport offset) = (20, 10) + (7, -11)
13569 PhysicalOffset offset = target->GetLayoutObject()->LocalToAbsolutePoint(
13570 PhysicalOffset(), viewport_intersection_flags);
13571 EXPECT_EQ(PhysicalOffset(27, -1), offset);
13572
13573 PhysicalRect rect(0, 0, 25, 35);
13574 local_frame->GetFrame()
13575 ->GetDocument()
13576 ->GetLayoutView()
13577 ->MapToVisualRectInAncestorSpace(
13578 nullptr, rect, viewport_intersection_flags, kDefaultVisualRectFlags);
13579 EXPECT_EQ(PhysicalRect(7, 0, 25, 24), rect);
13580
13581 // Without the main frame overflow clip the rect should not be clipped and the
13582 // coordinates returned are the rects coordinates in the viewport space.
13583 PhysicalRect mainframe_rect(0, 0, 25, 35);
13584 local_frame->GetFrame()
13585 ->GetDocument()
13586 ->GetLayoutView()
13587 ->MapToVisualRectInAncestorSpace(nullptr, mainframe_rect,
13588 viewport_intersection_flags,
13589 kDontApplyMainFrameOverflowClip);
13590 EXPECT_EQ(PhysicalRect(7, -11, 25, 35), mainframe_rect);
13591 }
13592
13593 class TestUpdateFaviconURLLocalFrameHost : public FakeLocalFrameHost {
13594 public:
13595 TestUpdateFaviconURLLocalFrameHost() = default;
13596 ~TestUpdateFaviconURLLocalFrameHost() override = default;
13597
13598 // FakeLocalFrameHost:
UpdateFaviconURL(WTF::Vector<blink::mojom::blink::FaviconURLPtr> favicon_urls)13599 void UpdateFaviconURL(
13600 WTF::Vector<blink::mojom::blink::FaviconURLPtr> favicon_urls) override {
13601 did_notify_ = true;
13602 }
13603
13604 bool did_notify_ = false;
13605 };
13606
13607 // Ensure the render view sends favicon url update events correctly.
TEST_F(WebFrameTest,FaviconURLUpdateEvent)13608 TEST_F(WebFrameTest, FaviconURLUpdateEvent) {
13609 TestUpdateFaviconURLLocalFrameHost frame_host;
13610 frame_test_helpers::TestWebFrameClient web_frame_client;
13611 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
13612 frame_test_helpers::WebViewHelper web_view_helper;
13613 web_view_helper.Initialize(&web_frame_client);
13614 RunPendingTasks();
13615
13616 WebViewImpl* web_view = web_view_helper.GetWebView();
13617 LocalFrame* frame = web_view->MainFrameImpl()->GetFrame();
13618
13619 // An event should be sent when a favicon url exists.
13620 frame->GetDocument()->documentElement()->setInnerHTML(
13621 "<html>"
13622 "<head>"
13623 "<link rel='icon' href='http://www.google.com/favicon.ico'>"
13624 "</head>"
13625 "</html>");
13626 RunPendingTasks();
13627
13628 EXPECT_TRUE(frame_host.did_notify_);
13629
13630 frame_host.did_notify_ = false;
13631
13632 // An event should not be sent if no favicon url exists. This is an assumption
13633 // made by some of Chrome's favicon handling.
13634 frame->GetDocument()->documentElement()->setInnerHTML(
13635 "<html>"
13636 "<head>"
13637 "</head>"
13638 "</html>");
13639 RunPendingTasks();
13640
13641 EXPECT_FALSE(frame_host.did_notify_);
13642 web_view_helper.Reset();
13643 }
13644
13645 class TestFocusedElementChangedLocalFrameHost : public FakeLocalFrameHost {
13646 public:
13647 TestFocusedElementChangedLocalFrameHost() = default;
13648 ~TestFocusedElementChangedLocalFrameHost() override = default;
13649
13650 // FakeLocalFrameHost:
FocusedElementChanged(bool is_editable_element,const gfx::Rect & bounds_in_frame_widget,blink::mojom::FocusType focus_type)13651 void FocusedElementChanged(bool is_editable_element,
13652 const gfx::Rect& bounds_in_frame_widget,
13653 blink::mojom::FocusType focus_type) override {
13654 did_notify_ = true;
13655 }
13656
13657 bool did_notify_ = false;
13658 };
13659
TEST_F(WebFrameTest,FocusElementCallsFocusedElementChanged)13660 TEST_F(WebFrameTest, FocusElementCallsFocusedElementChanged) {
13661 TestFocusedElementChangedLocalFrameHost frame_host;
13662 frame_test_helpers::TestWebFrameClient web_frame_client;
13663 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
13664 frame_test_helpers::WebViewHelper web_view_helper;
13665 web_view_helper.Initialize(&web_frame_client);
13666 RunPendingTasks();
13667 auto* main_frame = web_view_helper.GetWebView()->MainFrameImpl();
13668
13669 main_frame->GetFrame()->GetDocument()->documentElement()->setInnerHTML(
13670 "<input id='test1' value='hello1'></input>"
13671 "<input id='test2' value='hello2'></input>");
13672 RunPendingTasks();
13673
13674 EXPECT_FALSE(frame_host.did_notify_);
13675
13676 main_frame->ExecuteScript(
13677 WebScriptSource(WebString("document.getElementById('test1').focus();")));
13678 RunPendingTasks();
13679 EXPECT_TRUE(frame_host.did_notify_);
13680 frame_host.did_notify_ = false;
13681
13682 main_frame->ExecuteScript(
13683 WebScriptSource(WebString("document.getElementById('test2').focus();")));
13684 RunPendingTasks();
13685 EXPECT_TRUE(frame_host.did_notify_);
13686 frame_host.did_notify_ = false;
13687
13688 main_frame->ExecuteScript(
13689 WebScriptSource(WebString("document.getElementById('test2').blur();")));
13690 RunPendingTasks();
13691 EXPECT_TRUE(frame_host.did_notify_);
13692 }
13693
13694 // Tests that form.submit() cancels any navigations already sent to the browser
13695 // process.
TEST_F(WebFrameTest,FormSubmitCancelsNavigation)13696 TEST_F(WebFrameTest, FormSubmitCancelsNavigation) {
13697 frame_test_helpers::TestWebFrameClient web_frame_client;
13698 frame_test_helpers::WebViewHelper web_view_helper;
13699 web_view_helper.Initialize(&web_frame_client);
13700 RegisterMockedHttpURLLoad("foo.html");
13701 RegisterMockedHttpURLLoad("bar.html");
13702 auto* main_frame = web_view_helper.GetWebView()->MainFrameImpl();
13703 auto* local_frame = main_frame->GetFrame();
13704 auto* window = local_frame->DomWindow();
13705
13706 window->document()->documentElement()->setInnerHTML(
13707 "<form id=formid action='http://internal.test/bar.html'></form>");
13708 ASSERT_FALSE(local_frame->Loader().HasProvisionalNavigation());
13709
13710 FrameLoadRequest request(window,
13711 ResourceRequest("http://internal.test/foo.html"));
13712 local_frame->Navigate(request, WebFrameLoadType::kStandard);
13713 ASSERT_TRUE(local_frame->Loader().HasProvisionalNavigation());
13714
13715 main_frame->ExecuteScript(WebScriptSource(WebString("formid.submit()")));
13716 EXPECT_FALSE(local_frame->Loader().HasProvisionalNavigation());
13717
13718 RunPendingTasks();
13719 }
13720
13721 class TestLocalFrameHostForAnchorWithDownloadAttr : public FakeLocalFrameHost {
13722 public:
13723 TestLocalFrameHostForAnchorWithDownloadAttr() = default;
13724 ~TestLocalFrameHostForAnchorWithDownloadAttr() override = default;
13725
13726 // FakeLocalFrameHost:
DownloadURL(mojom::blink::DownloadURLParamsPtr params)13727 void DownloadURL(mojom::blink::DownloadURLParamsPtr params) override {
13728 referrer_ = params->referrer ? params->referrer->url : KURL();
13729 referrer_policy_ = params->referrer
13730 ? params->referrer->policy
13731 : ReferrerUtils::MojoReferrerPolicyResolveDefault(
13732 network::mojom::ReferrerPolicy::kDefault);
13733 }
13734
13735 KURL referrer_;
13736 network::mojom::ReferrerPolicy referrer_policy_;
13737 };
13738
TEST_F(WebFrameTest,DownloadReferrerPolicy)13739 TEST_F(WebFrameTest, DownloadReferrerPolicy) {
13740 TestLocalFrameHostForAnchorWithDownloadAttr frame_host;
13741 frame_test_helpers::TestWebFrameClient web_frame_client;
13742 frame_host.Init(web_frame_client.GetRemoteNavigationAssociatedInterfaces());
13743 frame_test_helpers::WebViewHelper web_view_helper;
13744 web_view_helper.Initialize(&web_frame_client);
13745
13746 WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
13747 KURL test_url = ToKURL("http://www.test.com/foo/index.html");
13748 // 1.<meta name='referrer' content='no-referrer'>
13749 frame_test_helpers::LoadHTMLString(
13750 frame, GetHTMLStringForReferrerPolicy("no-referrer", std::string()),
13751 test_url);
13752 EXPECT_TRUE(frame_host.referrer_.IsEmpty());
13753 EXPECT_EQ(frame_host.referrer_policy_,
13754 network::mojom::ReferrerPolicy::kNever);
13755
13756 // 2.<meta name='referrer' content='origin'>
13757 frame_test_helpers::LoadHTMLString(
13758 frame, GetHTMLStringForReferrerPolicy("origin", std::string()), test_url);
13759 EXPECT_EQ(frame_host.referrer_, ToKURL("http://www.test.com/"));
13760 EXPECT_EQ(frame_host.referrer_policy_,
13761 network::mojom::ReferrerPolicy::kOrigin);
13762
13763 // 3.Without any declared referrer-policy attribute
13764 frame_test_helpers::LoadHTMLString(
13765 frame, GetHTMLStringForReferrerPolicy(std::string(), std::string()),
13766 test_url);
13767 EXPECT_EQ(frame_host.referrer_, test_url);
13768 EXPECT_EQ(frame_host.referrer_policy_,
13769 ReferrerUtils::MojoReferrerPolicyResolveDefault(
13770 network::mojom::ReferrerPolicy::kDefault));
13771
13772 // 4.referrerpolicy='origin'
13773 frame_test_helpers::LoadHTMLString(
13774 frame, GetHTMLStringForReferrerPolicy(std::string(), "origin"), test_url);
13775 EXPECT_EQ(frame_host.referrer_, ToKURL("http://www.test.com/"));
13776 EXPECT_EQ(frame_host.referrer_policy_,
13777 network::mojom::ReferrerPolicy::kOrigin);
13778
13779 // 5.referrerpolicy='same-origin'
13780 frame_test_helpers::LoadHTMLString(
13781 frame, GetHTMLStringForReferrerPolicy(std::string(), "same-origin"),
13782 test_url);
13783 EXPECT_EQ(frame_host.referrer_, test_url);
13784 EXPECT_EQ(frame_host.referrer_policy_,
13785 network::mojom::ReferrerPolicy::kSameOrigin);
13786
13787 // 6.referrerpolicy='no-referrer'
13788 frame_test_helpers::LoadHTMLString(
13789 frame, GetHTMLStringForReferrerPolicy(std::string(), "no-referrer"),
13790 test_url);
13791 EXPECT_TRUE(frame_host.referrer_.IsEmpty());
13792 EXPECT_EQ(frame_host.referrer_policy_,
13793 network::mojom::ReferrerPolicy::kNever);
13794 web_view_helper.Reset();
13795 }
13796
13797 } // namespace blink
13798