1 /*
2 * Copyright (C) 2011, 2012 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_view.h"
32
33 #include <limits>
34 #include <memory>
35 #include <string>
36
37 #include "base/bind_helpers.h"
38 #include "base/stl_util.h"
39 #include "base/test/test_mock_time_task_runner.h"
40 #include "base/time/time.h"
41 #include "build/build_config.h"
42 #include "cc/trees/layer_tree_host.h"
43 #include "gin/handle.h"
44 #include "gin/object_template_builder.h"
45 #include "gin/wrappable.h"
46 #include "mojo/public/cpp/bindings/pending_receiver.h"
47 #include "mojo/public/cpp/bindings/pending_remote.h"
48 #include "mojo/public/cpp/bindings/receiver.h"
49 #include "testing/gtest/include/gtest/gtest.h"
50 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
51 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
52 #include "third_party/blink/public/common/input/web_input_event.h"
53 #include "third_party/blink/public/common/input/web_keyboard_event.h"
54 #include "third_party/blink/public/common/page/page_zoom.h"
55 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
56 #include "third_party/blink/public/mojom/manifest/display_mode.mojom-shared.h"
57 #include "third_party/blink/public/platform/web_coalesced_input_event.h"
58 #include "third_party/blink/public/platform/web_drag_data.h"
59 #include "third_party/blink/public/platform/web_drag_operation.h"
60 #include "third_party/blink/public/platform/web_size.h"
61 #include "third_party/blink/public/platform/web_url_loader_mock_factory.h"
62 #include "third_party/blink/public/public_buildflags.h"
63 #include "third_party/blink/public/web/web_autofill_client.h"
64 #include "third_party/blink/public/web/web_console_message.h"
65 #include "third_party/blink/public/web/web_device_emulation_params.h"
66 #include "third_party/blink/public/web/web_document.h"
67 #include "third_party/blink/public/web/web_element.h"
68 #include "third_party/blink/public/web/web_frame.h"
69 #include "third_party/blink/public/web/web_frame_content_dumper.h"
70 #include "third_party/blink/public/web/web_frame_widget.h"
71 #include "third_party/blink/public/web/web_hit_test_result.h"
72 #include "third_party/blink/public/web/web_input_method_controller.h"
73 #include "third_party/blink/public/web/web_local_frame.h"
74 #include "third_party/blink/public/web/web_local_frame_client.h"
75 #include "third_party/blink/public/web/web_print_params.h"
76 #include "third_party/blink/public/web/web_script_source.h"
77 #include "third_party/blink/public/web/web_settings.h"
78 #include "third_party/blink/public/web/web_tree_scope_type.h"
79 #include "third_party/blink/public/web/web_view_client.h"
80 #include "third_party/blink/public/web/web_widget.h"
81 #include "third_party/blink/public/web/web_widget_client.h"
82 #include "third_party/blink/renderer/bindings/core/v8/v8_document.h"
83 #include "third_party/blink/renderer/core/dom/document.h"
84 #include "third_party/blink/renderer/core/dom/element.h"
85 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
86 #include "third_party/blink/renderer/core/editing/frame_selection.h"
87 #include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
88 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
89 #include "third_party/blink/renderer/core/exported/web_settings_impl.h"
90 #include "third_party/blink/renderer/core/exported/web_view_impl.h"
91 #include "third_party/blink/renderer/core/frame/event_handler_registry.h"
92 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
93 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
94 #include "third_party/blink/renderer/core/frame/local_frame.h"
95 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
96 #include "third_party/blink/renderer/core/frame/settings.h"
97 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
98 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
99 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
100 #include "third_party/blink/renderer/core/html/forms/external_date_time_chooser.h"
101 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
102 #include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
103 #include "third_party/blink/renderer/core/html/html_document.h"
104 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
105 #include "third_party/blink/renderer/core/html/html_object_element.h"
106 #include "third_party/blink/renderer/core/html/html_span_element.h"
107 #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
108 #include "third_party/blink/renderer/core/layout/layout_theme.h"
109 #include "third_party/blink/renderer/core/layout/layout_view.h"
110 #include "third_party/blink/renderer/core/loader/document_loader.h"
111 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
112 #include "third_party/blink/renderer/core/loader/interactive_detector.h"
113 #include "third_party/blink/renderer/core/page/chrome_client.h"
114 #include "third_party/blink/renderer/core/page/context_menu_controller.h"
115 #include "third_party/blink/renderer/core/page/focus_controller.h"
116 #include "third_party/blink/renderer/core/page/page.h"
117 #include "third_party/blink/renderer/core/page/page_hidden_state.h"
118 #include "third_party/blink/renderer/core/page/print_context.h"
119 #include "third_party/blink/renderer/core/page/scoped_page_pauser.h"
120 #include "third_party/blink/renderer/core/paint/paint_layer.h"
121 #include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
122 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
123 #include "third_party/blink/renderer/core/scroll/scroll_types.h"
124 #include "third_party/blink/renderer/core/testing/color_scheme_helper.h"
125 #include "third_party/blink/renderer/core/testing/fake_web_plugin.h"
126 #include "third_party/blink/renderer/core/testing/mock_clipboard_host.h"
127 #include "third_party/blink/renderer/core/testing/page_test_base.h"
128 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
129 #include "third_party/blink/renderer/core/timing/event_timing.h"
130 #include "third_party/blink/renderer/core/timing/window_performance.h"
131 #include "third_party/blink/renderer/platform/cursors.h"
132 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
133 #include "third_party/blink/renderer/platform/geometry/int_size.h"
134 #include "third_party/blink/renderer/platform/graphics/color.h"
135 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
136 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
137 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
138 #include "third_party/blink/renderer/platform/keyboard_codes.h"
139 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
140 #include "third_party/blink/renderer/platform/testing/histogram_tester.h"
141 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
142 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
143 #include "third_party/skia/include/core/SkBitmap.h"
144 #include "third_party/skia/include/core/SkCanvas.h"
145 #include "ui/base/cursor/cursor.h"
146 #include "ui/base/mojom/cursor_type.mojom-blink.h"
147 #include "ui/events/keycodes/dom/dom_key.h"
148 #include "v8/include/v8.h"
149
150 #if defined(OS_MACOSX)
151 #include "third_party/blink/public/web/mac/web_substring_util.h"
152 #endif
153
154 #if BUILDFLAG(ENABLE_UNHANDLED_TAP)
155 #include "third_party/blink/public/mojom/unhandled_tap_notifier/unhandled_tap_notifier.mojom-blink.h"
156 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
157 #endif // BUILDFLAG(ENABLE_UNHANDLED_TAP)
158
159 using blink::frame_test_helpers::LoadFrame;
160 using blink::url_test_helpers::ToKURL;
161 using blink::url_test_helpers::RegisterMockedURLLoad;
162 using blink::test::RunPendingTasks;
163
164 namespace blink {
165
166 enum HorizontalScrollbarState {
167 kNoHorizontalScrollbar,
168 kVisibleHorizontalScrollbar,
169 };
170
171 enum VerticalScrollbarState {
172 kNoVerticalScrollbar,
173 kVisibleVerticalScrollbar,
174 };
175
176 class TestData {
177 public:
SetWebView(WebView * web_view)178 void SetWebView(WebView* web_view) {
179 web_view_ = static_cast<WebViewImpl*>(web_view);
180 }
SetSize(const WebSize & new_size)181 void SetSize(const WebSize& new_size) { size_ = new_size; }
GetHorizontalScrollbarState() const182 HorizontalScrollbarState GetHorizontalScrollbarState() const {
183 return web_view_->HasHorizontalScrollbar() ? kVisibleHorizontalScrollbar
184 : kNoHorizontalScrollbar;
185 }
GetVerticalScrollbarState() const186 VerticalScrollbarState GetVerticalScrollbarState() const {
187 return web_view_->HasVerticalScrollbar() ? kVisibleVerticalScrollbar
188 : kNoVerticalScrollbar;
189 }
Width() const190 int Width() const { return size_.width; }
Height() const191 int Height() const { return size_.height; }
192
193 private:
194 WebSize size_;
195 WebViewImpl* web_view_;
196 };
197
198 class AutoResizeWebViewClient : public frame_test_helpers::TestWebViewClient {
199 public:
200 // WebViewClient methods
DidAutoResize(const WebSize & new_size)201 void DidAutoResize(const WebSize& new_size) override {
202 test_data_.SetSize(new_size);
203 }
204
205 // Local methods
GetTestData()206 TestData& GetTestData() { return test_data_; }
207
208 private:
209 TestData test_data_;
210 };
211
212 class TapHandlingWebWidgetClient
213 : public frame_test_helpers::TestWebWidgetClient {
214 public:
215 // WebWidgetClient overrides.
DidHandleGestureEvent(const WebGestureEvent & event,bool event_cancelled)216 void DidHandleGestureEvent(const WebGestureEvent& event,
217 bool event_cancelled) override {
218 if (event.GetType() == WebInputEvent::kGestureTap) {
219 tap_x_ = event.PositionInWidget().x();
220 tap_y_ = event.PositionInWidget().y();
221 } else if (event.GetType() == WebInputEvent::kGestureLongPress) {
222 longpress_x_ = event.PositionInWidget().x();
223 longpress_y_ = event.PositionInWidget().y();
224 }
225 }
226
227 // Local methods
Reset()228 void Reset() {
229 tap_x_ = -1;
230 tap_y_ = -1;
231 longpress_x_ = -1;
232 longpress_y_ = -1;
233 }
TapX()234 int TapX() { return tap_x_; }
TapY()235 int TapY() { return tap_y_; }
LongpressX()236 int LongpressX() { return longpress_x_; }
LongpressY()237 int LongpressY() { return longpress_y_; }
238
239 private:
240 int tap_x_;
241 int tap_y_;
242 int longpress_x_;
243 int longpress_y_;
244 };
245
246 class WebViewTest : public testing::Test {
247 public:
WebViewTest()248 WebViewTest() : base_url_("http://www.test.com/") {}
249
SetUp()250 void SetUp() override {
251 test_task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
252 // Advance clock so time is not 0.
253 test_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(1));
254 EventTiming::SetTickClockForTesting(test_task_runner_->GetMockTickClock());
255 }
256
TearDown()257 void TearDown() override {
258 EventTiming::SetTickClockForTesting(nullptr);
259 url_test_helpers::UnregisterAllURLsAndClearMemoryCache();
260 }
261
262 protected:
SetViewportSize(const WebSize & size)263 void SetViewportSize(const WebSize& size) {
264 cc::LayerTreeHost* layer_tree_host = web_view_helper_.GetLayerTreeHost();
265 layer_tree_host->SetViewportRectAndScale(
266 gfx::Rect(static_cast<gfx::Size>(size)), /*device_scale_factor=*/1.f,
267 layer_tree_host->local_surface_id_allocation_from_parent());
268 }
269
RegisterMockedHttpURLLoad(const std::string & file_name)270 std::string RegisterMockedHttpURLLoad(const std::string& file_name) {
271 // TODO(crbug.com/751425): We should use the mock functionality
272 // via |web_view_helper_|.
273 return url_test_helpers::RegisterMockedURLLoadFromBase(
274 WebString::FromUTF8(base_url_), test::CoreTestDataPath(),
275 WebString::FromUTF8(file_name))
276 .GetString()
277 .Utf8();
278 }
279
280 void TestAutoResize(const WebSize& min_auto_resize,
281 const WebSize& max_auto_resize,
282 const std::string& page_width,
283 const std::string& page_height,
284 int expected_width,
285 int expected_height,
286 HorizontalScrollbarState expected_horizontal_state,
287 VerticalScrollbarState expected_vertical_state);
288
289 void TestTextInputType(WebTextInputType expected_type,
290 const std::string& html_file);
291 void TestInputMode(WebTextInputMode expected_input_mode,
292 const std::string& html_file);
293 void TestInputAction(ui::TextInputAction expected_input_action,
294 const std::string& html_file);
295 bool TapElement(WebInputEvent::Type, Element*);
296 bool TapElementById(WebInputEvent::Type, const WebString& id);
297 IntSize PrintICBSizeFromPageSize(const FloatSize& page_size);
298
299 ExternalDateTimeChooser* GetExternalDateTimeChooser(
300 WebViewImpl* web_view_impl);
301
UpdateAllLifecyclePhases()302 void UpdateAllLifecyclePhases() {
303 web_view_helper_.GetWebView()->MainFrameWidget()->UpdateAllLifecyclePhases(
304 DocumentUpdateReason::kTest);
305 }
306
GetTestInteractiveDetector(Document & document)307 InteractiveDetector* GetTestInteractiveDetector(Document& document) {
308 InteractiveDetector* detector(InteractiveDetector::From(document));
309 EXPECT_NE(nullptr, detector);
310 detector->SetTaskRunnerForTesting(test_task_runner_);
311 detector->SetTickClockForTesting(test_task_runner_->GetMockTickClock());
312 return detector;
313 }
314
315 std::string base_url_;
316 frame_test_helpers::WebViewHelper web_view_helper_;
317 scoped_refptr<base::TestMockTimeTaskRunner> test_task_runner_;
318 };
319
HitTestIsContentEditable(WebView * view,int x,int y)320 static bool HitTestIsContentEditable(WebView* view, int x, int y) {
321 gfx::Point hit_point(x, y);
322 WebHitTestResult hit_test_result =
323 view->MainFrameWidget()->HitTestResultAt(hit_point);
324 return hit_test_result.IsContentEditable();
325 }
326
HitTestElementId(WebView * view,int x,int y)327 static std::string HitTestElementId(WebView* view, int x, int y) {
328 gfx::Point hit_point(x, y);
329 WebHitTestResult hit_test_result =
330 view->MainFrameWidget()->HitTestResultAt(hit_point);
331 return hit_test_result.GetNode().To<WebElement>().GetAttribute("id").Utf8();
332 }
333
OutlineColor(Element * element)334 static Color OutlineColor(Element* element) {
335 return element->GetComputedStyle()->VisitedDependentColor(
336 GetCSSPropertyOutlineColor());
337 }
338
TEST_F(WebViewTest,HitTestVideo)339 TEST_F(WebViewTest, HitTestVideo) {
340 // Test that hit tests on parts of a video element result in hits on the video
341 // element itself as opposed to its child elements.
342 std::string url = RegisterMockedHttpURLLoad("video_200x200.html");
343 WebView* web_view = web_view_helper_.InitializeAndLoad(url);
344 web_view->MainFrameWidget()->Resize(WebSize(200, 200));
345
346 // Center of video.
347 EXPECT_EQ("video", HitTestElementId(web_view, 100, 100));
348
349 // Play button.
350 EXPECT_EQ("video", HitTestElementId(web_view, 10, 195));
351
352 // Timeline bar.
353 EXPECT_EQ("video", HitTestElementId(web_view, 100, 195));
354 }
355
TEST_F(WebViewTest,HitTestContentEditableImageMaps)356 TEST_F(WebViewTest, HitTestContentEditableImageMaps) {
357 std::string url =
358 RegisterMockedHttpURLLoad("content-editable-image-maps.html");
359 WebView* web_view = web_view_helper_.InitializeAndLoad(url);
360 web_view->MainFrameWidget()->Resize(WebSize(500, 500));
361
362 EXPECT_EQ("areaANotEditable", HitTestElementId(web_view, 25, 25));
363 EXPECT_FALSE(HitTestIsContentEditable(web_view, 25, 25));
364 EXPECT_EQ("imageANotEditable", HitTestElementId(web_view, 75, 25));
365 EXPECT_FALSE(HitTestIsContentEditable(web_view, 75, 25));
366
367 EXPECT_EQ("areaBNotEditable", HitTestElementId(web_view, 25, 125));
368 EXPECT_FALSE(HitTestIsContentEditable(web_view, 25, 125));
369 EXPECT_EQ("imageBEditable", HitTestElementId(web_view, 75, 125));
370 EXPECT_TRUE(HitTestIsContentEditable(web_view, 75, 125));
371
372 EXPECT_EQ("areaCNotEditable", HitTestElementId(web_view, 25, 225));
373 EXPECT_FALSE(HitTestIsContentEditable(web_view, 25, 225));
374 EXPECT_EQ("imageCNotEditable", HitTestElementId(web_view, 75, 225));
375 EXPECT_FALSE(HitTestIsContentEditable(web_view, 75, 225));
376
377 EXPECT_EQ("areaDEditable", HitTestElementId(web_view, 25, 325));
378 EXPECT_TRUE(HitTestIsContentEditable(web_view, 25, 325));
379 EXPECT_EQ("imageDNotEditable", HitTestElementId(web_view, 75, 325));
380 EXPECT_FALSE(HitTestIsContentEditable(web_view, 75, 325));
381 }
382
HitTestAbsoluteUrl(WebView * view,int x,int y)383 static std::string HitTestAbsoluteUrl(WebView* view, int x, int y) {
384 gfx::Point hit_point(x, y);
385 WebHitTestResult hit_test_result =
386 view->MainFrameWidget()->HitTestResultAt(hit_point);
387 return hit_test_result.AbsoluteImageURL().GetString().Utf8();
388 }
389
HitTestUrlElement(WebView * view,int x,int y)390 static WebElement HitTestUrlElement(WebView* view, int x, int y) {
391 gfx::Point hit_point(x, y);
392 WebHitTestResult hit_test_result =
393 view->MainFrameWidget()->HitTestResultAt(hit_point);
394 return hit_test_result.UrlElement();
395 }
396
TEST_F(WebViewTest,ImageMapUrls)397 TEST_F(WebViewTest, ImageMapUrls) {
398 std::string url = RegisterMockedHttpURLLoad("image-map.html");
399 WebView* web_view = web_view_helper_.InitializeAndLoad(url);
400 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
401
402 std::string image_url =
403 "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=";
404
405 EXPECT_EQ("area", HitTestElementId(web_view, 25, 25));
406 EXPECT_EQ("area",
407 HitTestUrlElement(web_view, 25, 25).GetAttribute("id").Utf8());
408 EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 25, 25));
409
410 EXPECT_EQ("image", HitTestElementId(web_view, 75, 25));
411 EXPECT_TRUE(HitTestUrlElement(web_view, 75, 25).IsNull());
412 EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 75, 25));
413 }
414
TEST_F(WebViewTest,BrokenImage)415 TEST_F(WebViewTest, BrokenImage) {
416 url_test_helpers::RegisterMockedErrorURLLoad(
417 KURL(ToKURL(base_url_), "non_existent.png"));
418 std::string url = RegisterMockedHttpURLLoad("image-broken.html");
419
420 WebViewImpl* web_view = web_view_helper_.Initialize();
421 web_view->GetSettings()->SetLoadsImagesAutomatically(true);
422 LoadFrame(web_view->MainFrameImpl(), url);
423 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
424
425 std::string image_url = "http://www.test.com/non_existent.png";
426
427 EXPECT_EQ("image", HitTestElementId(web_view, 25, 25));
428 EXPECT_TRUE(HitTestUrlElement(web_view, 25, 25).IsNull());
429 EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 25, 25));
430 }
431
TEST_F(WebViewTest,BrokenInputImage)432 TEST_F(WebViewTest, BrokenInputImage) {
433 url_test_helpers::RegisterMockedErrorURLLoad(
434 KURL(ToKURL(base_url_), "non_existent.png"));
435 std::string url = RegisterMockedHttpURLLoad("input-image-broken.html");
436
437 WebViewImpl* web_view = web_view_helper_.Initialize();
438 web_view->GetSettings()->SetLoadsImagesAutomatically(true);
439 LoadFrame(web_view->MainFrameImpl(), url);
440 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
441
442 std::string image_url = "http://www.test.com/non_existent.png";
443
444 EXPECT_EQ("image", HitTestElementId(web_view, 25, 25));
445 EXPECT_TRUE(HitTestUrlElement(web_view, 25, 25).IsNull());
446 EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 25, 25));
447 }
448
TEST_F(WebViewTest,SetBaseBackgroundColor)449 TEST_F(WebViewTest, SetBaseBackgroundColor) {
450 const SkColor kDarkCyan = SkColorSetARGB(0xFF, 0x22, 0x77, 0x88);
451 const SkColor kTranslucentPutty = SkColorSetARGB(0x80, 0xBF, 0xB1, 0x96);
452
453 WebViewImpl* web_view = web_view_helper_.Initialize();
454 EXPECT_EQ(SK_ColorWHITE, web_view->BackgroundColor());
455
456 web_view->SetBaseBackgroundColor(SK_ColorBLUE);
457 EXPECT_EQ(SK_ColorBLUE, web_view->BackgroundColor());
458
459 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
460 frame_test_helpers::LoadHTMLString(
461 web_view->MainFrameImpl(),
462 "<html><head><style>body "
463 "{background-color:#227788}</style></head></"
464 "html>",
465 base_url);
466 EXPECT_EQ(kDarkCyan, web_view->BackgroundColor());
467
468 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
469 "<html><head><style>body "
470 "{background-color:rgba(255,0,0,0.5)}</"
471 "style></head></html>",
472 base_url);
473 // Expected: red (50% alpha) blended atop base of SK_ColorBLUE.
474 EXPECT_EQ(0xFF80007F, web_view->BackgroundColor());
475
476 web_view->SetBaseBackgroundColor(kTranslucentPutty);
477 // Expected: red (50% alpha) blended atop kTranslucentPutty. Note the alpha.
478 EXPECT_EQ(0xBFE93A31, web_view->BackgroundColor());
479
480 web_view->SetBaseBackgroundColor(SK_ColorTRANSPARENT);
481 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
482 "<html><head><style>body "
483 "{background-color:transparent}</style></"
484 "head></html>",
485 base_url);
486 // Expected: transparent on top of transparent will still be transparent.
487 EXPECT_EQ(SK_ColorTRANSPARENT, web_view->BackgroundColor());
488
489 LocalFrame* frame = web_view->MainFrameImpl()->GetFrame();
490 // The shutdown() calls are a hack to prevent this test
491 // from violating invariants about frame state during navigation/detach.
492 frame->GetDocument()->Shutdown();
493
494 // Creating a new frame view with the background color having 0 alpha.
495 frame->CreateView(IntSize(1024, 768), Color::kTransparent);
496 EXPECT_EQ(SK_ColorTRANSPARENT, frame->View()->BaseBackgroundColor());
497 frame->View()->Dispose();
498
499 const Color transparent_red(100, 0, 0, 0);
500 frame->CreateView(IntSize(1024, 768), transparent_red);
501 EXPECT_EQ(transparent_red, frame->View()->BaseBackgroundColor());
502 frame->View()->Dispose();
503 }
504
TEST_F(WebViewTest,SetBaseBackgroundColorBeforeMainFrame)505 TEST_F(WebViewTest, SetBaseBackgroundColorBeforeMainFrame) {
506 // Note: this test doesn't use WebViewHelper since it intentionally runs
507 // initialization code between WebView and WebLocalFrame creation.
508 frame_test_helpers::TestWebViewClient web_view_client;
509 frame_test_helpers::TestWebWidgetClient web_widget_client;
510 WebViewImpl* web_view = static_cast<WebViewImpl*>(
511 WebView::Create(&web_view_client, false,
512 /*compositing_enabled=*/true, nullptr,
513 mojo::ScopedInterfaceEndpointHandle()));
514
515 EXPECT_NE(SK_ColorBLUE, web_view->BackgroundColor());
516 // WebView does not have a frame yet, but we should still be able to set the
517 // background color.
518 web_view->SetBaseBackgroundColor(SK_ColorBLUE);
519 EXPECT_EQ(SK_ColorBLUE, web_view->BackgroundColor());
520
521 frame_test_helpers::TestWebFrameClient web_frame_client;
522 WebLocalFrame* frame = WebLocalFrame::CreateMainFrame(
523 web_view, &web_frame_client, nullptr, nullptr);
524 web_frame_client.Bind(frame);
525
526 {
527 // Copy the steps done from WebViewHelper::InitializeWithOpener() to set up
528 // the appropriate pointers!
529 WebFrameWidget* widget = blink::WebFrameWidget::CreateForMainFrame(
530 &web_widget_client, frame,
531 CrossVariantMojoAssociatedRemote<mojom::FrameWidgetHostInterfaceBase>(),
532 CrossVariantMojoAssociatedReceiver<mojom::FrameWidgetInterfaceBase>(),
533 CrossVariantMojoAssociatedRemote<mojom::WidgetHostInterfaceBase>(),
534 CrossVariantMojoAssociatedReceiver<mojom::WidgetInterfaceBase>());
535 widget->SetCompositorHosts(web_widget_client.layer_tree_host(),
536 web_widget_client.animation_host());
537 widget->SetCompositorVisible(true);
538 web_view->DidAttachLocalMainFrame();
539 }
540
541 // The color should be passed to the compositor.
542 cc::LayerTreeHost* host = web_widget_client.layer_tree_host();
543 EXPECT_EQ(SK_ColorBLUE, web_view->BackgroundColor());
544 EXPECT_EQ(SK_ColorBLUE, host->background_color());
545
546 web_view->Close();
547 }
548
TEST_F(WebViewTest,SetBaseBackgroundColorAndBlendWithExistingContent)549 TEST_F(WebViewTest, SetBaseBackgroundColorAndBlendWithExistingContent) {
550 const SkColor kAlphaRed = SkColorSetARGB(0x80, 0xFF, 0x00, 0x00);
551 const SkColor kAlphaGreen = SkColorSetARGB(0x80, 0x00, 0xFF, 0x00);
552 const int kWidth = 100;
553 const int kHeight = 100;
554
555 WebViewImpl* web_view = web_view_helper_.Initialize();
556
557 // Set WebView background to green with alpha.
558 web_view->SetBaseBackgroundColor(kAlphaGreen);
559 web_view->GetSettings()->SetShouldClearDocumentBackground(false);
560 web_view->MainFrameWidget()->Resize(WebSize(kWidth, kHeight));
561 UpdateAllLifecyclePhases();
562
563 // Set canvas background to red with alpha.
564 SkBitmap bitmap;
565 bitmap.allocN32Pixels(kWidth, kHeight);
566 SkCanvas canvas(bitmap);
567 canvas.clear(kAlphaRed);
568
569 PaintRecordBuilder builder;
570
571 // Paint the root of the main frame in the way that CompositedLayerMapping
572 // would.
573 LocalFrameView* view = web_view_helper_.LocalMainFrame()->GetFrameView();
574 PaintLayer* root_layer = view->GetLayoutView()->Layer();
575 CullRect paint_rect(IntRect(0, 0, kWidth, kHeight));
576 PaintLayerPaintingInfo painting_info(
577 root_layer, paint_rect, kGlobalPaintNormalPhase, PhysicalOffset());
578
579 view->GetLayoutView()->GetDocument().Lifecycle().AdvanceTo(
580 DocumentLifecycle::kInPaint);
581 PaintLayerPainter(*root_layer)
582 .PaintLayerContents(builder.Context(), painting_info,
583 kPaintLayerPaintingCompositingAllPhases);
584 view->GetLayoutView()->GetDocument().Lifecycle().AdvanceTo(
585 DocumentLifecycle::kPaintClean);
586 builder.EndRecording()->Playback(&canvas);
587
588 // The result should be a blend of red and green.
589 SkColor color = bitmap.getColor(kWidth / 2, kHeight / 2);
590 EXPECT_TRUE(RedChannel(color));
591 EXPECT_TRUE(GreenChannel(color));
592 }
593
TEST_F(WebViewTest,SetBaseBackgroundColorWithColorScheme)594 TEST_F(WebViewTest, SetBaseBackgroundColorWithColorScheme) {
595 ScopedCSSColorSchemeForTest enable_color_scheme(true);
596
597 WebViewImpl* web_view = web_view_helper_.Initialize();
598 ColorSchemeHelper color_scheme_helper(*(web_view->GetPage()));
599 color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
600 web_view->SetBaseBackgroundColor(SK_ColorBLUE);
601
602 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
603 frame_test_helpers::LoadHTMLString(
604 web_view->MainFrameImpl(),
605 "<style>:root { color-scheme: light dark }<style>", base_url);
606 UpdateAllLifecyclePhases();
607
608 LocalFrameView* frame_view = web_view->MainFrameImpl()->GetFrame()->View();
609 EXPECT_EQ(Color(0, 0, 255), frame_view->BaseBackgroundColor());
610
611 color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kDark);
612 UpdateAllLifecyclePhases();
613 EXPECT_EQ(Color::kBlack, frame_view->BaseBackgroundColor());
614
615 // Don't let dark color-scheme override a transparent background.
616 web_view->SetBaseBackgroundColor(SK_ColorTRANSPARENT);
617 EXPECT_EQ(Color::kTransparent, frame_view->BaseBackgroundColor());
618 web_view->SetBaseBackgroundColor(SK_ColorBLUE);
619 EXPECT_EQ(Color::kBlack, frame_view->BaseBackgroundColor());
620
621 color_scheme_helper.SetPreferredColorScheme(PreferredColorScheme::kLight);
622 UpdateAllLifecyclePhases();
623 EXPECT_EQ(Color(0, 0, 255), frame_view->BaseBackgroundColor());
624 }
625
TEST_F(WebViewTest,FocusIsInactive)626 TEST_F(WebViewTest, FocusIsInactive) {
627 RegisterMockedHttpURLLoad("visible_iframe.html");
628 WebViewImpl* web_view =
629 web_view_helper_.InitializeAndLoad(base_url_ + "visible_iframe.html");
630
631 web_view->MainFrameWidget()->SetFocus(true);
632 web_view->SetIsActive(true);
633 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
634 EXPECT_TRUE(IsA<HTMLDocument>(frame->GetFrame()->GetDocument()));
635
636 Document* document = frame->GetFrame()->GetDocument();
637 EXPECT_TRUE(document->hasFocus());
638 web_view->MainFrameWidget()->SetFocus(false);
639 web_view->SetIsActive(false);
640 EXPECT_FALSE(document->hasFocus());
641 web_view->MainFrameWidget()->SetFocus(true);
642 web_view->SetIsActive(true);
643 EXPECT_TRUE(document->hasFocus());
644 web_view->MainFrameWidget()->SetFocus(true);
645 web_view->SetIsActive(false);
646 EXPECT_FALSE(document->hasFocus());
647 web_view->MainFrameWidget()->SetFocus(false);
648 web_view->SetIsActive(true);
649 EXPECT_FALSE(document->hasFocus());
650 web_view->SetIsActive(false);
651 web_view->MainFrameWidget()->SetFocus(true);
652 EXPECT_TRUE(document->hasFocus());
653 web_view->SetIsActive(true);
654 web_view->MainFrameWidget()->SetFocus(false);
655 EXPECT_FALSE(document->hasFocus());
656 }
657
TEST_F(WebViewTest,DocumentHasFocus)658 TEST_F(WebViewTest, DocumentHasFocus) {
659 WebViewImpl* web_view = web_view_helper_.Initialize();
660 web_view->MainFrameWidget()->SetFocus(true);
661
662 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
663 frame_test_helpers::LoadHTMLString(
664 web_view->MainFrameImpl(),
665 "<input id=input></input>"
666 "<div id=log></div>"
667 "<script>"
668 " document.getElementById('input').addEventListener('focus', () => {"
669 " document.getElementById('log').textContent = 'document.hasFocus(): "
670 "' + document.hasFocus();"
671 " });"
672 " document.getElementById('input').addEventListener('blur', () => {"
673 " document.getElementById('log').textContent = '';"
674 " });"
675 " document.getElementById('input').focus();"
676 "</script>",
677 base_url);
678
679 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
680 Document* document = frame->GetFrame()->GetDocument();
681 WebElement log_element = frame->GetDocument().GetElementById("log");
682 EXPECT_TRUE(document->hasFocus());
683 EXPECT_EQ("document.hasFocus(): true", log_element.TextContent());
684
685 web_view->SetIsActive(false);
686 web_view->MainFrameWidget()->SetFocus(false);
687 EXPECT_FALSE(document->hasFocus());
688 EXPECT_TRUE(log_element.TextContent().IsEmpty());
689
690 web_view->MainFrameWidget()->SetFocus(true);
691 EXPECT_TRUE(document->hasFocus());
692 EXPECT_EQ("document.hasFocus(): true", log_element.TextContent());
693 }
694
TEST_F(WebViewTest,PlatformColorsChangedOnDeviceEmulation)695 TEST_F(WebViewTest, PlatformColorsChangedOnDeviceEmulation) {
696 WebViewImpl* web_view_impl = web_view_helper_.Initialize();
697 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
698 frame_test_helpers::LoadHTMLString(
699 web_view_impl->MainFrameImpl(),
700 "<style>"
701 " span { outline-color: -webkit-focus-ring-color; }"
702 "</style>"
703 "<span id='span1'></span>",
704 base_url);
705 UpdateAllLifecyclePhases();
706
707 WebDeviceEmulationParams params;
708 params.screen_position = WebDeviceEmulationParams::kMobile;
709
710 Document& document =
711 *web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
712
713 Element* span1 = document.getElementById("span1");
714 ASSERT_TRUE(span1);
715
716 // Check non-MobileLayoutTheme color.
717 Color original = LayoutTheme::GetTheme().FocusRingColor();
718 EXPECT_EQ(original, OutlineColor(span1));
719
720 // Set the focus ring color for the mobile theme to something known.
721 Color custom_color = MakeRGB(123, 145, 167);
722 {
723 ScopedMobileLayoutThemeForTest mobile_layout_theme_enabled(true);
724 LayoutTheme::GetTheme().SetCustomFocusRingColor(custom_color);
725 }
726
727 EXPECT_NE(custom_color, original);
728 web_view_impl->EnableDeviceEmulation(params);
729
730 // All <span>s should have the custom outline color, and not (for example)
731 // the original color fetched from cache.
732 auto* span2 = MakeGarbageCollected<HTMLSpanElement>(document);
733 document.body()->AppendChild(span2);
734 UpdateAllLifecyclePhases();
735 EXPECT_EQ(custom_color, OutlineColor(span1));
736 EXPECT_EQ(custom_color, OutlineColor(span2));
737
738 // Disable mobile emulation. All <span>s should once again have the
739 // original outline color.
740 web_view_impl->DisableDeviceEmulation();
741 auto* span3 = MakeGarbageCollected<HTMLSpanElement>(document);
742 document.body()->AppendChild(span3);
743 UpdateAllLifecyclePhases();
744 EXPECT_EQ(original, OutlineColor(span1));
745 EXPECT_EQ(original, OutlineColor(span2));
746 EXPECT_EQ(original, OutlineColor(span3));
747 }
748
TEST_F(WebViewTest,ActiveState)749 TEST_F(WebViewTest, ActiveState) {
750 RegisterMockedHttpURLLoad("visible_iframe.html");
751 WebView* web_view =
752 web_view_helper_.InitializeAndLoad(base_url_ + "visible_iframe.html");
753
754 ASSERT_TRUE(web_view);
755
756 web_view->SetIsActive(true);
757 EXPECT_TRUE(web_view->IsActive());
758
759 web_view->SetIsActive(false);
760 EXPECT_FALSE(web_view->IsActive());
761
762 web_view->SetIsActive(true);
763 EXPECT_TRUE(web_view->IsActive());
764 }
765
TEST_F(WebViewTest,HitTestResultAtWithPageScale)766 TEST_F(WebViewTest, HitTestResultAtWithPageScale) {
767 std::string url = base_url_ + "specify_size.html?" + "50px" + ":" + "50px";
768 url_test_helpers::RegisterMockedURLLoad(
769 ToKURL(url), test::CoreTestDataPath("specify_size.html"));
770 WebView* web_view = web_view_helper_.InitializeAndLoad(url);
771 web_view->MainFrameWidget()->Resize(WebSize(100, 100));
772 gfx::Point hit_point(75, 75);
773
774 // Image is at top left quandrant, so should not hit it.
775 WebHitTestResult negative_result =
776 web_view->MainFrameWidget()->HitTestResultAt(hit_point);
777 EXPECT_FALSE(
778 negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
779 negative_result.Reset();
780
781 // Scale page up 2x so image should occupy the whole viewport.
782 web_view->SetPageScaleFactor(2.0f);
783 WebHitTestResult positive_result =
784 web_view->MainFrameWidget()->HitTestResultAt(hit_point);
785 EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
786 positive_result.Reset();
787 }
788
TEST_F(WebViewTest,HitTestResultAtWithPageScaleAndPan)789 TEST_F(WebViewTest, HitTestResultAtWithPageScaleAndPan) {
790 std::string url = base_url_ + "specify_size.html?" + "50px" + ":" + "50px";
791 url_test_helpers::RegisterMockedURLLoad(
792 ToKURL(url), test::CoreTestDataPath("specify_size.html"));
793 WebViewImpl* web_view = web_view_helper_.Initialize();
794 LoadFrame(web_view->MainFrameImpl(), url);
795 web_view->MainFrameWidget()->Resize(WebSize(100, 100));
796 gfx::Point hit_point(75, 75);
797
798 // Image is at top left quandrant, so should not hit it.
799 WebHitTestResult negative_result = web_view->HitTestResultAt(hit_point);
800 EXPECT_FALSE(
801 negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
802 negative_result.Reset();
803
804 // Scale page up 2x so image should occupy the whole viewport.
805 web_view->SetPageScaleFactor(2.0f);
806 WebHitTestResult positive_result = web_view->HitTestResultAt(hit_point);
807 EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
808 positive_result.Reset();
809
810 // Pan around the zoomed in page so the image is not visible in viewport.
811 web_view->SetVisualViewportOffset(gfx::PointF(100, 100));
812 WebHitTestResult negative_result2 = web_view->HitTestResultAt(hit_point);
813 EXPECT_FALSE(
814 negative_result2.GetNode().To<WebElement>().HasHTMLTagName("img"));
815 negative_result2.Reset();
816 }
817
TEST_F(WebViewTest,HitTestResultForTapWithTapArea)818 TEST_F(WebViewTest, HitTestResultForTapWithTapArea) {
819 std::string url = RegisterMockedHttpURLLoad("hit_test.html");
820 WebView* web_view = web_view_helper_.InitializeAndLoad(url);
821 web_view->MainFrameWidget()->Resize(WebSize(100, 100));
822 gfx::Point hit_point(55, 55);
823
824 // Image is at top left quandrant, so should not hit it.
825 WebHitTestResult negative_result =
826 web_view->MainFrameWidget()->HitTestResultAt(hit_point);
827 EXPECT_FALSE(
828 negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
829 negative_result.Reset();
830
831 // The tap area is 20 by 20 square, centered at 55, 55.
832 WebSize tap_area(20, 20);
833 WebHitTestResult positive_result =
834 web_view->HitTestResultForTap(hit_point, tap_area);
835 EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
836 positive_result.Reset();
837
838 // Move the hit point the image is just outside the tapped area now.
839 hit_point = gfx::Point(61, 61);
840 WebHitTestResult negative_result2 =
841 web_view->HitTestResultForTap(hit_point, tap_area);
842 EXPECT_FALSE(
843 negative_result2.GetNode().To<WebElement>().HasHTMLTagName("img"));
844 negative_result2.Reset();
845 }
846
TEST_F(WebViewTest,HitTestResultForTapWithTapAreaPageScaleAndPan)847 TEST_F(WebViewTest, HitTestResultForTapWithTapAreaPageScaleAndPan) {
848 std::string url = RegisterMockedHttpURLLoad("hit_test.html");
849 WebViewImpl* web_view = web_view_helper_.Initialize();
850 LoadFrame(web_view->MainFrameImpl(), url);
851 web_view->MainFrameWidget()->Resize(WebSize(100, 100));
852 gfx::Point hit_point(55, 55);
853
854 // Image is at top left quandrant, so should not hit it.
855 WebHitTestResult negative_result = web_view->HitTestResultAt(hit_point);
856 EXPECT_FALSE(
857 negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
858 negative_result.Reset();
859
860 // The tap area is 20 by 20 square, centered at 55, 55.
861 WebSize tap_area(20, 20);
862 WebHitTestResult positive_result =
863 web_view->HitTestResultForTap(hit_point, tap_area);
864 EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
865 positive_result.Reset();
866
867 // Zoom in and pan around the page so the image is not visible in viewport.
868 web_view->SetPageScaleFactor(2.0f);
869 web_view->SetVisualViewportOffset(gfx::PointF(100, 100));
870 WebHitTestResult negative_result2 =
871 web_view->HitTestResultForTap(hit_point, tap_area);
872 EXPECT_FALSE(
873 negative_result2.GetNode().To<WebElement>().HasHTMLTagName("img"));
874 negative_result2.Reset();
875 }
876
TestAutoResize(const WebSize & min_auto_resize,const WebSize & max_auto_resize,const std::string & page_width,const std::string & page_height,int expected_width,int expected_height,HorizontalScrollbarState expected_horizontal_state,VerticalScrollbarState expected_vertical_state)877 void WebViewTest::TestAutoResize(
878 const WebSize& min_auto_resize,
879 const WebSize& max_auto_resize,
880 const std::string& page_width,
881 const std::string& page_height,
882 int expected_width,
883 int expected_height,
884 HorizontalScrollbarState expected_horizontal_state,
885 VerticalScrollbarState expected_vertical_state) {
886 AutoResizeWebViewClient client;
887 std::string url =
888 base_url_ + "specify_size.html?" + page_width + ":" + page_height;
889 url_test_helpers::RegisterMockedURLLoad(
890 ToKURL(url), test::CoreTestDataPath("specify_size.html"));
891 WebViewImpl* web_view =
892 web_view_helper_.InitializeAndLoad(url, nullptr, &client);
893 client.GetTestData().SetWebView(web_view);
894
895 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
896 LocalFrameView* frame_view = frame->GetFrame()->View();
897 frame_view->UpdateLayout();
898 EXPECT_FALSE(frame_view->LayoutPending());
899 EXPECT_FALSE(frame_view->NeedsLayout());
900
901 web_view->EnableAutoResizeMode(min_auto_resize, max_auto_resize);
902 EXPECT_TRUE(frame_view->LayoutPending());
903 EXPECT_TRUE(frame_view->NeedsLayout());
904 frame_view->UpdateLayout();
905
906 EXPECT_TRUE(frame->GetFrame()->GetDocument()->IsHTMLDocument());
907
908 EXPECT_EQ(expected_width, client.GetTestData().Width());
909 EXPECT_EQ(expected_height, client.GetTestData().Height());
910
911 // Android disables main frame scrollbars.
912 #if !defined(OS_ANDROID)
913 EXPECT_EQ(expected_horizontal_state,
914 client.GetTestData().GetHorizontalScrollbarState());
915 EXPECT_EQ(expected_vertical_state,
916 client.GetTestData().GetVerticalScrollbarState());
917 #endif
918
919 // Explicitly reset to break dependency on locally scoped client.
920 web_view_helper_.Reset();
921 }
922
TEST_F(WebViewTest,AutoResizeMinimumSize)923 TEST_F(WebViewTest, AutoResizeMinimumSize) {
924 WebSize min_auto_resize(91, 56);
925 WebSize max_auto_resize(403, 302);
926 std::string page_width = "91px";
927 std::string page_height = "56px";
928 int expected_width = 91;
929 int expected_height = 56;
930 TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
931 expected_width, expected_height, kNoHorizontalScrollbar,
932 kNoVerticalScrollbar);
933 }
934
TEST_F(WebViewTest,AutoResizeHeightOverflowAndFixedWidth)935 TEST_F(WebViewTest, AutoResizeHeightOverflowAndFixedWidth) {
936 WebSize min_auto_resize(90, 95);
937 WebSize max_auto_resize(90, 100);
938 std::string page_width = "60px";
939 std::string page_height = "200px";
940 int expected_width = 90;
941 int expected_height = 100;
942 TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
943 expected_width, expected_height, kNoHorizontalScrollbar,
944 kVisibleVerticalScrollbar);
945 }
946
TEST_F(WebViewTest,AutoResizeFixedHeightAndWidthOverflow)947 TEST_F(WebViewTest, AutoResizeFixedHeightAndWidthOverflow) {
948 WebSize min_auto_resize(90, 100);
949 WebSize max_auto_resize(200, 100);
950 std::string page_width = "300px";
951 std::string page_height = "80px";
952 int expected_width = 200;
953 int expected_height = 100;
954 TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
955 expected_width, expected_height, kVisibleHorizontalScrollbar,
956 kNoVerticalScrollbar);
957 }
958
959 // Next three tests disabled for https://bugs.webkit.org/show_bug.cgi?id=92318 .
960 // It seems we can run three AutoResize tests, then the next one breaks.
TEST_F(WebViewTest,AutoResizeInBetweenSizes)961 TEST_F(WebViewTest, AutoResizeInBetweenSizes) {
962 WebSize min_auto_resize(90, 95);
963 WebSize max_auto_resize(200, 300);
964 std::string page_width = "100px";
965 std::string page_height = "200px";
966 int expected_width = 100;
967 int expected_height = 200;
968 TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
969 expected_width, expected_height, kNoHorizontalScrollbar,
970 kNoVerticalScrollbar);
971 }
972
TEST_F(WebViewTest,AutoResizeOverflowSizes)973 TEST_F(WebViewTest, AutoResizeOverflowSizes) {
974 WebSize min_auto_resize(90, 95);
975 WebSize max_auto_resize(200, 300);
976 std::string page_width = "300px";
977 std::string page_height = "400px";
978 int expected_width = 200;
979 int expected_height = 300;
980 TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
981 expected_width, expected_height, kVisibleHorizontalScrollbar,
982 kVisibleVerticalScrollbar);
983 }
984
TEST_F(WebViewTest,AutoResizeMaxSize)985 TEST_F(WebViewTest, AutoResizeMaxSize) {
986 WebSize min_auto_resize(90, 95);
987 WebSize max_auto_resize(200, 300);
988 std::string page_width = "200px";
989 std::string page_height = "300px";
990 int expected_width = 200;
991 int expected_height = 300;
992 TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
993 expected_width, expected_height, kNoHorizontalScrollbar,
994 kNoVerticalScrollbar);
995 }
996
TestTextInputType(WebTextInputType expected_type,const std::string & html_file)997 void WebViewTest::TestTextInputType(WebTextInputType expected_type,
998 const std::string& html_file) {
999 RegisterMockedHttpURLLoad(html_file);
1000 WebViewImpl* web_view =
1001 web_view_helper_.InitializeAndLoad(base_url_ + html_file);
1002 WebInputMethodController* controller =
1003 web_view->MainFrameImpl()->GetInputMethodController();
1004 EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputType());
1005 EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputInfo().type);
1006 web_view->SetInitialFocus(false);
1007 EXPECT_EQ(expected_type, controller->TextInputType());
1008 EXPECT_EQ(expected_type, controller->TextInputInfo().type);
1009 web_view->FocusedElement()->blur();
1010 EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputType());
1011 EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputInfo().type);
1012 }
1013
TEST_F(WebViewTest,TextInputType)1014 TEST_F(WebViewTest, TextInputType) {
1015 TestTextInputType(kWebTextInputTypeText, "input_field_default.html");
1016 TestTextInputType(kWebTextInputTypePassword, "input_field_password.html");
1017 TestTextInputType(kWebTextInputTypeEmail, "input_field_email.html");
1018 TestTextInputType(kWebTextInputTypeSearch, "input_field_search.html");
1019 TestTextInputType(kWebTextInputTypeNumber, "input_field_number.html");
1020 TestTextInputType(kWebTextInputTypeTelephone, "input_field_tel.html");
1021 TestTextInputType(kWebTextInputTypeURL, "input_field_url.html");
1022 }
1023
TEST_F(WebViewTest,TextInputInfoUpdateStyleAndLayout)1024 TEST_F(WebViewTest, TextInputInfoUpdateStyleAndLayout) {
1025 frame_test_helpers::WebViewHelper web_view_helper;
1026 WebViewImpl* web_view_impl = web_view_helper.Initialize();
1027
1028 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
1029 // Here, we need to construct a document that has a special property:
1030 // Adding id="foo" to the <path> element will trigger creation of an SVG
1031 // instance tree for the use <use> element.
1032 // This is significant, because SVG instance trees are actually created lazily
1033 // during Document::updateStyleAndLayout code, thus incrementing the DOM tree
1034 // version and freaking out the EphemeralRange (invalidating it).
1035 frame_test_helpers::LoadHTMLString(
1036 web_view_impl->MainFrameImpl(),
1037 "<svg height='100%' version='1.1' viewBox='0 0 14 14' width='100%'>"
1038 "<use xmlns:xlink='http://www.w3.org/1999/xlink' xlink:href='#foo'></use>"
1039 "<path d='M 100 100 L 300 100 L 200 300 z' fill='#000'></path>"
1040 "</svg>"
1041 "<input>",
1042 base_url);
1043 web_view_impl->SetInitialFocus(false);
1044
1045 // Add id="foo" to <path>, thus triggering the condition described above.
1046 Document* document =
1047 web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
1048 document->body()
1049 ->QuerySelector("path", ASSERT_NO_EXCEPTION)
1050 ->SetIdAttribute("foo");
1051
1052 // This should not DCHECK.
1053 EXPECT_EQ(kWebTextInputTypeText, web_view_impl->MainFrameImpl()
1054 ->GetInputMethodController()
1055 ->TextInputInfo()
1056 .type);
1057 }
1058
TestInputMode(WebTextInputMode expected_input_mode,const std::string & html_file)1059 void WebViewTest::TestInputMode(WebTextInputMode expected_input_mode,
1060 const std::string& html_file) {
1061 RegisterMockedHttpURLLoad(html_file);
1062 WebViewImpl* web_view_impl =
1063 web_view_helper_.InitializeAndLoad(base_url_ + html_file);
1064 web_view_impl->SetInitialFocus(false);
1065 EXPECT_EQ(expected_input_mode, web_view_impl->MainFrameImpl()
1066 ->GetInputMethodController()
1067 ->TextInputInfo()
1068 .input_mode);
1069 }
1070
TEST_F(WebViewTest,InputMode)1071 TEST_F(WebViewTest, InputMode) {
1072 TestInputMode(WebTextInputMode::kWebTextInputModeDefault,
1073 "input_mode_default.html");
1074 TestInputMode(WebTextInputMode::kWebTextInputModeDefault,
1075 "input_mode_default_unknown.html");
1076 TestInputMode(WebTextInputMode::kWebTextInputModeNone,
1077 "input_mode_type_none.html");
1078 TestInputMode(WebTextInputMode::kWebTextInputModeText,
1079 "input_mode_type_text.html");
1080 TestInputMode(WebTextInputMode::kWebTextInputModeTel,
1081 "input_mode_type_tel.html");
1082 TestInputMode(WebTextInputMode::kWebTextInputModeUrl,
1083 "input_mode_type_url.html");
1084 TestInputMode(WebTextInputMode::kWebTextInputModeEmail,
1085 "input_mode_type_email.html");
1086 TestInputMode(WebTextInputMode::kWebTextInputModeNumeric,
1087 "input_mode_type_numeric.html");
1088 TestInputMode(WebTextInputMode::kWebTextInputModeDecimal,
1089 "input_mode_type_decimal.html");
1090 TestInputMode(WebTextInputMode::kWebTextInputModeSearch,
1091 "input_mode_type_search.html");
1092 }
1093
TestInputAction(ui::TextInputAction expected_input_action,const std::string & html_file)1094 void WebViewTest::TestInputAction(ui::TextInputAction expected_input_action,
1095 const std::string& html_file) {
1096 RegisterMockedHttpURLLoad(html_file);
1097 WebViewImpl* web_view_impl =
1098 web_view_helper_.InitializeAndLoad(base_url_ + html_file);
1099 web_view_impl->SetInitialFocus(false);
1100 EXPECT_EQ(expected_input_action, web_view_impl->MainFrameImpl()
1101 ->GetInputMethodController()
1102 ->TextInputInfo()
1103 .action);
1104 }
1105
TEST_F(WebViewTest,TextInputAction)1106 TEST_F(WebViewTest, TextInputAction) {
1107 TestInputAction(ui::TextInputAction::kDefault, "enter_key_hint_default.html");
1108 TestInputAction(ui::TextInputAction::kDefault,
1109 "enter_key_hint_default_unknown.html");
1110 TestInputAction(ui::TextInputAction::kEnter, "enter_key_hint_enter.html");
1111 TestInputAction(ui::TextInputAction::kGo, "enter_key_hint_go.html");
1112 TestInputAction(ui::TextInputAction::kDone, "enter_key_hint_done.html");
1113 TestInputAction(ui::TextInputAction::kNext, "enter_key_hint_next.html");
1114 TestInputAction(ui::TextInputAction::kPrevious,
1115 "enter_key_hint_previous.html");
1116 TestInputAction(ui::TextInputAction::kSearch, "enter_key_hint_search.html");
1117 TestInputAction(ui::TextInputAction::kSend, "enter_key_hint_send.html");
1118 TestInputAction(ui::TextInputAction::kNext, "enter_key_hint_mixed_case.html");
1119 }
1120
TEST_F(WebViewTest,TextInputInfoWithReplacedElements)1121 TEST_F(WebViewTest, TextInputInfoWithReplacedElements) {
1122 std::string url = RegisterMockedHttpURLLoad("div_with_image.html");
1123 url_test_helpers::RegisterMockedURLLoad(
1124 ToKURL("http://www.test.com/foo.png"),
1125 test::CoreTestDataPath("white-1x1.png"));
1126 WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(url);
1127 web_view_impl->SetInitialFocus(false);
1128 WebTextInputInfo info = web_view_impl->MainFrameImpl()
1129 ->GetInputMethodController()
1130 ->TextInputInfo();
1131
1132 EXPECT_EQ("foo\xef\xbf\xbc", info.value.Utf8());
1133 }
1134
TEST_F(WebViewTest,SetEditableSelectionOffsetsAndTextInputInfo)1135 TEST_F(WebViewTest, SetEditableSelectionOffsetsAndTextInputInfo) {
1136 RegisterMockedHttpURLLoad("input_field_populated.html");
1137 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1138 base_url_ + "input_field_populated.html");
1139 web_view->SetInitialFocus(false);
1140 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1141 WebInputMethodController* active_input_method_controller =
1142 frame->GetInputMethodController();
1143 frame->SetEditableSelectionOffsets(5, 13);
1144 EXPECT_EQ("56789abc", frame->SelectionAsText());
1145 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1146 EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value);
1147 EXPECT_EQ(5, info.selection_start);
1148 EXPECT_EQ(13, info.selection_end);
1149 EXPECT_EQ(-1, info.composition_start);
1150 EXPECT_EQ(-1, info.composition_end);
1151
1152 RegisterMockedHttpURLLoad("content_editable_populated.html");
1153 web_view = web_view_helper_.InitializeAndLoad(
1154 base_url_ + "content_editable_populated.html");
1155 web_view->SetInitialFocus(false);
1156 frame = web_view->MainFrameImpl();
1157 active_input_method_controller = frame->GetInputMethodController();
1158 frame->SetEditableSelectionOffsets(8, 19);
1159 EXPECT_EQ("89abcdefghi", frame->SelectionAsText());
1160 info = active_input_method_controller->TextInputInfo();
1161 EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value);
1162 EXPECT_EQ(8, info.selection_start);
1163 EXPECT_EQ(19, info.selection_end);
1164 EXPECT_EQ(-1, info.composition_start);
1165 EXPECT_EQ(-1, info.composition_end);
1166 }
1167
1168 // Regression test for crbug.com/663645
TEST_F(WebViewTest,FinishComposingTextDoesNotAssert)1169 TEST_F(WebViewTest, FinishComposingTextDoesNotAssert) {
1170 RegisterMockedHttpURLLoad("input_field_default.html");
1171 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1172 base_url_ + "input_field_default.html");
1173 web_view->SetInitialFocus(false);
1174
1175 WebInputMethodController* active_input_method_controller =
1176 web_view->MainFrameImpl()
1177 ->FrameWidget()
1178 ->GetActiveWebInputMethodController();
1179
1180 // The test requires non-empty composition.
1181 std::string composition_text("hello");
1182 WebVector<WebImeTextSpan> empty_ime_text_spans;
1183 active_input_method_controller->SetComposition(
1184 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1185 WebRange(), 5, 5);
1186
1187 // Do arbitrary change to make layout dirty.
1188 Document& document = *web_view->MainFrameImpl()->GetFrame()->GetDocument();
1189 Element* br = document.CreateRawElement(html_names::kBrTag);
1190 document.body()->AppendChild(br);
1191
1192 // Should not hit assertion when calling
1193 // WebInputMethodController::finishComposingText with non-empty composition
1194 // and dirty layout.
1195 active_input_method_controller->FinishComposingText(
1196 WebInputMethodController::kKeepSelection);
1197 }
1198
1199 // Regression test for https://crbug.com/873999
TEST_F(WebViewTest,LongPressOutsideInputShouldNotSelectPlaceholderText)1200 TEST_F(WebViewTest, LongPressOutsideInputShouldNotSelectPlaceholderText) {
1201 RegisterMockedHttpURLLoad("input_placeholder.html");
1202 WebViewImpl* web_view =
1203 web_view_helper_.InitializeAndLoad(base_url_ + "input_placeholder.html");
1204 web_view->SetInitialFocus(false);
1205 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
1206 UpdateAllLifecyclePhases();
1207 RunPendingTasks();
1208
1209 WebString input_id = WebString::FromUTF8("input");
1210
1211 // Focus in input.
1212 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureTap, input_id));
1213
1214 // Long press below input.
1215 WebGestureEvent event(WebInputEvent::kGestureLongPress,
1216 WebInputEvent::kNoModifiers,
1217 WebInputEvent::GetStaticTimeStampForTests(),
1218 WebGestureDevice::kTouchscreen);
1219 event.SetPositionInWidget(gfx::PointF(100, 150));
1220 EXPECT_EQ(WebInputEventResult::kHandledSystem,
1221 web_view->MainFrameWidget()->HandleInputEvent(
1222 WebCoalescedInputEvent(event)));
1223 EXPECT_TRUE(web_view->MainFrameImpl()->SelectionAsText().IsEmpty());
1224 }
1225
TEST_F(WebViewTest,FinishComposingTextCursorPositionChange)1226 TEST_F(WebViewTest, FinishComposingTextCursorPositionChange) {
1227 RegisterMockedHttpURLLoad("input_field_populated.html");
1228 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1229 base_url_ + "input_field_populated.html");
1230 web_view->SetInitialFocus(false);
1231
1232 // Set up a composition that needs to be committed.
1233 std::string composition_text("hello");
1234
1235 WebInputMethodController* active_input_method_controller =
1236 web_view->MainFrameImpl()
1237 ->FrameWidget()
1238 ->GetActiveWebInputMethodController();
1239 WebVector<WebImeTextSpan> empty_ime_text_spans;
1240 active_input_method_controller->SetComposition(
1241 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1242 WebRange(), 3, 3);
1243
1244 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1245 EXPECT_EQ("hello", info.value.Utf8());
1246 EXPECT_EQ(3, info.selection_start);
1247 EXPECT_EQ(3, info.selection_end);
1248 EXPECT_EQ(0, info.composition_start);
1249 EXPECT_EQ(5, info.composition_end);
1250
1251 active_input_method_controller->FinishComposingText(
1252 WebInputMethodController::kKeepSelection);
1253 info = active_input_method_controller->TextInputInfo();
1254 EXPECT_EQ(3, info.selection_start);
1255 EXPECT_EQ(3, info.selection_end);
1256 EXPECT_EQ(-1, info.composition_start);
1257 EXPECT_EQ(-1, info.composition_end);
1258
1259 active_input_method_controller->SetComposition(
1260 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1261 WebRange(), 3, 3);
1262 info = active_input_method_controller->TextInputInfo();
1263 EXPECT_EQ("helhellolo", info.value.Utf8());
1264 EXPECT_EQ(6, info.selection_start);
1265 EXPECT_EQ(6, info.selection_end);
1266 EXPECT_EQ(3, info.composition_start);
1267 EXPECT_EQ(8, info.composition_end);
1268
1269 active_input_method_controller->FinishComposingText(
1270 WebInputMethodController::kDoNotKeepSelection);
1271 info = active_input_method_controller->TextInputInfo();
1272 EXPECT_EQ(8, info.selection_start);
1273 EXPECT_EQ(8, info.selection_end);
1274 EXPECT_EQ(-1, info.composition_start);
1275 EXPECT_EQ(-1, info.composition_end);
1276 }
1277
TEST_F(WebViewTest,SetCompositionForNewCaretPositions)1278 TEST_F(WebViewTest, SetCompositionForNewCaretPositions) {
1279 RegisterMockedHttpURLLoad("input_field_populated.html");
1280 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1281 base_url_ + "input_field_populated.html");
1282 web_view->SetInitialFocus(false);
1283 WebInputMethodController* active_input_method_controller =
1284 web_view->MainFrameImpl()
1285 ->FrameWidget()
1286 ->GetActiveWebInputMethodController();
1287
1288 WebVector<WebImeTextSpan> empty_ime_text_spans;
1289
1290 active_input_method_controller->CommitText("hello", empty_ime_text_spans,
1291 WebRange(), 0);
1292 active_input_method_controller->CommitText("world", empty_ime_text_spans,
1293 WebRange(), -5);
1294 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1295
1296 EXPECT_EQ("helloworld", info.value.Utf8());
1297 EXPECT_EQ(5, info.selection_start);
1298 EXPECT_EQ(5, info.selection_end);
1299 EXPECT_EQ(-1, info.composition_start);
1300 EXPECT_EQ(-1, info.composition_end);
1301
1302 // Set up a composition that needs to be committed.
1303 std::string composition_text("ABC");
1304
1305 // Caret is on the left of composing text.
1306 active_input_method_controller->SetComposition(
1307 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1308 WebRange(), 0, 0);
1309 info = active_input_method_controller->TextInputInfo();
1310 EXPECT_EQ("helloABCworld", info.value.Utf8());
1311 EXPECT_EQ(5, info.selection_start);
1312 EXPECT_EQ(5, info.selection_end);
1313 EXPECT_EQ(5, info.composition_start);
1314 EXPECT_EQ(8, info.composition_end);
1315
1316 // Caret is on the right of composing text.
1317 active_input_method_controller->SetComposition(
1318 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1319 WebRange(), 3, 3);
1320 info = active_input_method_controller->TextInputInfo();
1321 EXPECT_EQ("helloABCworld", info.value.Utf8());
1322 EXPECT_EQ(8, info.selection_start);
1323 EXPECT_EQ(8, info.selection_end);
1324 EXPECT_EQ(5, info.composition_start);
1325 EXPECT_EQ(8, info.composition_end);
1326
1327 // Caret is between composing text and left boundary.
1328 active_input_method_controller->SetComposition(
1329 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1330 WebRange(), -2, -2);
1331 info = active_input_method_controller->TextInputInfo();
1332 EXPECT_EQ("helloABCworld", info.value.Utf8());
1333 EXPECT_EQ(3, info.selection_start);
1334 EXPECT_EQ(3, info.selection_end);
1335 EXPECT_EQ(5, info.composition_start);
1336 EXPECT_EQ(8, info.composition_end);
1337
1338 // Caret is between composing text and right boundary.
1339 active_input_method_controller->SetComposition(
1340 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1341 WebRange(), 5, 5);
1342 info = active_input_method_controller->TextInputInfo();
1343 EXPECT_EQ("helloABCworld", info.value.Utf8());
1344 EXPECT_EQ(10, info.selection_start);
1345 EXPECT_EQ(10, info.selection_end);
1346 EXPECT_EQ(5, info.composition_start);
1347 EXPECT_EQ(8, info.composition_end);
1348
1349 // Caret is on the left boundary.
1350 active_input_method_controller->SetComposition(
1351 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1352 WebRange(), -5, -5);
1353 info = active_input_method_controller->TextInputInfo();
1354 EXPECT_EQ("helloABCworld", info.value.Utf8());
1355 EXPECT_EQ(0, info.selection_start);
1356 EXPECT_EQ(0, info.selection_end);
1357 EXPECT_EQ(5, info.composition_start);
1358 EXPECT_EQ(8, info.composition_end);
1359
1360 // Caret is on the right boundary.
1361 active_input_method_controller->SetComposition(
1362 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1363 WebRange(), 8, 8);
1364 info = active_input_method_controller->TextInputInfo();
1365 EXPECT_EQ("helloABCworld", info.value.Utf8());
1366 EXPECT_EQ(13, info.selection_start);
1367 EXPECT_EQ(13, info.selection_end);
1368 EXPECT_EQ(5, info.composition_start);
1369 EXPECT_EQ(8, info.composition_end);
1370
1371 // Caret exceeds the left boundary.
1372 active_input_method_controller->SetComposition(
1373 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1374 WebRange(), -100, -100);
1375 info = active_input_method_controller->TextInputInfo();
1376 EXPECT_EQ("helloABCworld", info.value.Utf8());
1377 EXPECT_EQ(0, info.selection_start);
1378 EXPECT_EQ(0, info.selection_end);
1379 EXPECT_EQ(5, info.composition_start);
1380 EXPECT_EQ(8, info.composition_end);
1381
1382 // Caret exceeds the right boundary.
1383 active_input_method_controller->SetComposition(
1384 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1385 WebRange(), 100, 100);
1386 info = active_input_method_controller->TextInputInfo();
1387 EXPECT_EQ("helloABCworld", info.value.Utf8());
1388 EXPECT_EQ(13, info.selection_start);
1389 EXPECT_EQ(13, info.selection_end);
1390 EXPECT_EQ(5, info.composition_start);
1391 EXPECT_EQ(8, info.composition_end);
1392 }
1393
TEST_F(WebViewTest,SetCompositionWithEmptyText)1394 TEST_F(WebViewTest, SetCompositionWithEmptyText) {
1395 RegisterMockedHttpURLLoad("input_field_populated.html");
1396 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1397 base_url_ + "input_field_populated.html");
1398 web_view->SetInitialFocus(false);
1399 WebInputMethodController* active_input_method_controller =
1400 web_view->MainFrameImpl()
1401 ->FrameWidget()
1402 ->GetActiveWebInputMethodController();
1403
1404 WebVector<WebImeTextSpan> empty_ime_text_spans;
1405
1406 active_input_method_controller->CommitText("hello", empty_ime_text_spans,
1407 WebRange(), 0);
1408 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1409
1410 EXPECT_EQ("hello", info.value.Utf8());
1411 EXPECT_EQ(5, info.selection_start);
1412 EXPECT_EQ(5, info.selection_end);
1413 EXPECT_EQ(-1, info.composition_start);
1414 EXPECT_EQ(-1, info.composition_end);
1415
1416 active_input_method_controller->SetComposition(
1417 WebString::FromUTF8(""), empty_ime_text_spans, WebRange(), 0, 0);
1418 info = active_input_method_controller->TextInputInfo();
1419 EXPECT_EQ("hello", info.value.Utf8());
1420 EXPECT_EQ(5, info.selection_start);
1421 EXPECT_EQ(5, info.selection_end);
1422 EXPECT_EQ(-1, info.composition_start);
1423 EXPECT_EQ(-1, info.composition_end);
1424
1425 active_input_method_controller->SetComposition(
1426 WebString::FromUTF8(""), empty_ime_text_spans, WebRange(), -2, -2);
1427 info = active_input_method_controller->TextInputInfo();
1428 EXPECT_EQ("hello", info.value.Utf8());
1429 EXPECT_EQ(3, info.selection_start);
1430 EXPECT_EQ(3, info.selection_end);
1431 EXPECT_EQ(-1, info.composition_start);
1432 EXPECT_EQ(-1, info.composition_end);
1433 }
1434
TEST_F(WebViewTest,CommitTextForNewCaretPositions)1435 TEST_F(WebViewTest, CommitTextForNewCaretPositions) {
1436 RegisterMockedHttpURLLoad("input_field_populated.html");
1437 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1438 base_url_ + "input_field_populated.html");
1439 web_view->SetInitialFocus(false);
1440 WebInputMethodController* active_input_method_controller =
1441 web_view->MainFrameImpl()
1442 ->FrameWidget()
1443 ->GetActiveWebInputMethodController();
1444
1445 WebVector<WebImeTextSpan> empty_ime_text_spans;
1446
1447 // Caret is on the left of composing text.
1448 active_input_method_controller->CommitText("ab", empty_ime_text_spans,
1449 WebRange(), -2);
1450 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1451 EXPECT_EQ("ab", info.value.Utf8());
1452 EXPECT_EQ(0, info.selection_start);
1453 EXPECT_EQ(0, info.selection_end);
1454 EXPECT_EQ(-1, info.composition_start);
1455 EXPECT_EQ(-1, info.composition_end);
1456
1457 // Caret is on the right of composing text.
1458 active_input_method_controller->CommitText("c", empty_ime_text_spans,
1459 WebRange(), 1);
1460 info = active_input_method_controller->TextInputInfo();
1461 EXPECT_EQ("cab", info.value.Utf8());
1462 EXPECT_EQ(2, info.selection_start);
1463 EXPECT_EQ(2, info.selection_end);
1464 EXPECT_EQ(-1, info.composition_start);
1465 EXPECT_EQ(-1, info.composition_end);
1466
1467 // Caret is on the left boundary.
1468 active_input_method_controller->CommitText("def", empty_ime_text_spans,
1469 WebRange(), -5);
1470 info = active_input_method_controller->TextInputInfo();
1471 EXPECT_EQ("cadefb", info.value.Utf8());
1472 EXPECT_EQ(0, info.selection_start);
1473 EXPECT_EQ(0, info.selection_end);
1474 EXPECT_EQ(-1, info.composition_start);
1475 EXPECT_EQ(-1, info.composition_end);
1476
1477 // Caret is on the right boundary.
1478 active_input_method_controller->CommitText("g", empty_ime_text_spans,
1479 WebRange(), 6);
1480 info = active_input_method_controller->TextInputInfo();
1481 EXPECT_EQ("gcadefb", info.value.Utf8());
1482 EXPECT_EQ(7, info.selection_start);
1483 EXPECT_EQ(7, info.selection_end);
1484 EXPECT_EQ(-1, info.composition_start);
1485 EXPECT_EQ(-1, info.composition_end);
1486
1487 // Caret exceeds the left boundary.
1488 active_input_method_controller->CommitText("hi", empty_ime_text_spans,
1489 WebRange(), -100);
1490 info = active_input_method_controller->TextInputInfo();
1491 EXPECT_EQ("gcadefbhi", info.value.Utf8());
1492 EXPECT_EQ(0, info.selection_start);
1493 EXPECT_EQ(0, info.selection_end);
1494 EXPECT_EQ(-1, info.composition_start);
1495 EXPECT_EQ(-1, info.composition_end);
1496
1497 // Caret exceeds the right boundary.
1498 active_input_method_controller->CommitText("jk", empty_ime_text_spans,
1499 WebRange(), 100);
1500 info = active_input_method_controller->TextInputInfo();
1501 EXPECT_EQ("jkgcadefbhi", info.value.Utf8());
1502 EXPECT_EQ(11, info.selection_start);
1503 EXPECT_EQ(11, info.selection_end);
1504 EXPECT_EQ(-1, info.composition_start);
1505 EXPECT_EQ(-1, info.composition_end);
1506 }
1507
TEST_F(WebViewTest,CommitTextWhileComposing)1508 TEST_F(WebViewTest, CommitTextWhileComposing) {
1509 RegisterMockedHttpURLLoad("input_field_populated.html");
1510 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1511 base_url_ + "input_field_populated.html");
1512 web_view->SetInitialFocus(false);
1513 WebInputMethodController* active_input_method_controller =
1514 web_view->MainFrameImpl()
1515 ->FrameWidget()
1516 ->GetActiveWebInputMethodController();
1517
1518 WebVector<WebImeTextSpan> empty_ime_text_spans;
1519 active_input_method_controller->SetComposition(
1520 WebString::FromUTF8("abc"), empty_ime_text_spans, WebRange(), 0, 0);
1521 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1522 EXPECT_EQ("abc", info.value.Utf8());
1523 EXPECT_EQ(0, info.selection_start);
1524 EXPECT_EQ(0, info.selection_end);
1525 EXPECT_EQ(0, info.composition_start);
1526 EXPECT_EQ(3, info.composition_end);
1527
1528 // Deletes ongoing composition, inserts the specified text and moves the
1529 // caret.
1530 active_input_method_controller->CommitText("hello", empty_ime_text_spans,
1531 WebRange(), -2);
1532 info = active_input_method_controller->TextInputInfo();
1533 EXPECT_EQ("hello", info.value.Utf8());
1534 EXPECT_EQ(3, info.selection_start);
1535 EXPECT_EQ(3, info.selection_end);
1536 EXPECT_EQ(-1, info.composition_start);
1537 EXPECT_EQ(-1, info.composition_end);
1538
1539 active_input_method_controller->SetComposition(
1540 WebString::FromUTF8("abc"), empty_ime_text_spans, WebRange(), 0, 0);
1541 info = active_input_method_controller->TextInputInfo();
1542 EXPECT_EQ("helabclo", info.value.Utf8());
1543 EXPECT_EQ(3, info.selection_start);
1544 EXPECT_EQ(3, info.selection_end);
1545 EXPECT_EQ(3, info.composition_start);
1546 EXPECT_EQ(6, info.composition_end);
1547
1548 // Deletes ongoing composition and moves the caret.
1549 active_input_method_controller->CommitText("", empty_ime_text_spans,
1550 WebRange(), 2);
1551 info = active_input_method_controller->TextInputInfo();
1552 EXPECT_EQ("hello", info.value.Utf8());
1553 EXPECT_EQ(5, info.selection_start);
1554 EXPECT_EQ(5, info.selection_end);
1555 EXPECT_EQ(-1, info.composition_start);
1556 EXPECT_EQ(-1, info.composition_end);
1557
1558 // Inserts the specified text and moves the caret.
1559 active_input_method_controller->CommitText("world", empty_ime_text_spans,
1560 WebRange(), -5);
1561 info = active_input_method_controller->TextInputInfo();
1562 EXPECT_EQ("helloworld", info.value.Utf8());
1563 EXPECT_EQ(5, info.selection_start);
1564 EXPECT_EQ(5, info.selection_end);
1565 EXPECT_EQ(-1, info.composition_start);
1566 EXPECT_EQ(-1, info.composition_end);
1567
1568 // Only moves the caret.
1569 active_input_method_controller->CommitText("", empty_ime_text_spans,
1570 WebRange(), 5);
1571 info = active_input_method_controller->TextInputInfo();
1572 EXPECT_EQ("helloworld", info.value.Utf8());
1573 EXPECT_EQ(10, info.selection_start);
1574 EXPECT_EQ(10, info.selection_end);
1575 EXPECT_EQ(-1, info.composition_start);
1576 EXPECT_EQ(-1, info.composition_end);
1577 }
1578
TEST_F(WebViewTest,FinishCompositionDoesNotRevealSelection)1579 TEST_F(WebViewTest, FinishCompositionDoesNotRevealSelection) {
1580 RegisterMockedHttpURLLoad("form_with_input.html");
1581 WebViewImpl* web_view =
1582 web_view_helper_.InitializeAndLoad(base_url_ + "form_with_input.html");
1583 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
1584 web_view->SetInitialFocus(false);
1585 EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
1586 EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().height);
1587
1588 // Set up a composition from existing text that needs to be committed.
1589 Vector<ImeTextSpan> empty_ime_text_spans;
1590 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1591 frame->GetFrame()->GetInputMethodController().SetCompositionFromExistingText(
1592 empty_ime_text_spans, 0, 3);
1593
1594 // Scroll the input field out of the viewport.
1595 Element* element = static_cast<Element*>(
1596 web_view->MainFrameImpl()->GetDocument().GetElementById("btn"));
1597 element->scrollIntoView();
1598 float offset_height = web_view->MainFrameImpl()->GetScrollOffset().height;
1599 EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
1600 EXPECT_LT(0, offset_height);
1601
1602 WebTextInputInfo info = frame->GetInputMethodController()->TextInputInfo();
1603 EXPECT_EQ("hello", info.value.Utf8());
1604
1605 // Verify that the input field is not scrolled back into the viewport.
1606 frame->FrameWidget()
1607 ->GetActiveWebInputMethodController()
1608 ->FinishComposingText(WebInputMethodController::kDoNotKeepSelection);
1609 EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
1610 EXPECT_EQ(offset_height, web_view->MainFrameImpl()->GetScrollOffset().height);
1611 }
1612
TEST_F(WebViewTest,InsertNewLinePlacementAfterFinishComposingText)1613 TEST_F(WebViewTest, InsertNewLinePlacementAfterFinishComposingText) {
1614 RegisterMockedHttpURLLoad("text_area_populated.html");
1615 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1616 base_url_ + "text_area_populated.html");
1617 web_view->SetInitialFocus(false);
1618
1619 WebVector<WebImeTextSpan> empty_ime_text_spans;
1620
1621 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1622 WebInputMethodController* active_input_method_controller =
1623 frame->GetInputMethodController();
1624 frame->SetEditableSelectionOffsets(4, 4);
1625 frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
1626
1627 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1628 EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value.Utf8());
1629 EXPECT_EQ(4, info.selection_start);
1630 EXPECT_EQ(4, info.selection_end);
1631 EXPECT_EQ(8, info.composition_start);
1632 EXPECT_EQ(12, info.composition_end);
1633
1634 active_input_method_controller->FinishComposingText(
1635 WebInputMethodController::kKeepSelection);
1636 info = active_input_method_controller->TextInputInfo();
1637 EXPECT_EQ(4, info.selection_start);
1638 EXPECT_EQ(4, info.selection_end);
1639 EXPECT_EQ(-1, info.composition_start);
1640 EXPECT_EQ(-1, info.composition_end);
1641
1642 std::string composition_text("\n");
1643 active_input_method_controller->CommitText(
1644 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1645 WebRange(), 0);
1646 info = active_input_method_controller->TextInputInfo();
1647 EXPECT_EQ(5, info.selection_start);
1648 EXPECT_EQ(5, info.selection_end);
1649 EXPECT_EQ(-1, info.composition_start);
1650 EXPECT_EQ(-1, info.composition_end);
1651 EXPECT_EQ("0123\n456789abcdefghijklmnopqrstuvwxyz", info.value.Utf8());
1652 }
1653
TEST_F(WebViewTest,ExtendSelectionAndDelete)1654 TEST_F(WebViewTest, ExtendSelectionAndDelete) {
1655 RegisterMockedHttpURLLoad("input_field_populated.html");
1656 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1657 base_url_ + "input_field_populated.html");
1658 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1659 web_view->SetInitialFocus(false);
1660 frame->SetEditableSelectionOffsets(10, 10);
1661 frame->ExtendSelectionAndDelete(5, 8);
1662 WebInputMethodController* active_input_method_controller =
1663 frame->GetInputMethodController();
1664 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1665 EXPECT_EQ("01234ijklmnopqrstuvwxyz", info.value.Utf8());
1666 EXPECT_EQ(5, info.selection_start);
1667 EXPECT_EQ(5, info.selection_end);
1668 frame->ExtendSelectionAndDelete(10, 0);
1669 info = active_input_method_controller->TextInputInfo();
1670 EXPECT_EQ("ijklmnopqrstuvwxyz", info.value.Utf8());
1671 }
1672
TEST_F(WebViewTest,DeleteSurroundingText)1673 TEST_F(WebViewTest, DeleteSurroundingText) {
1674 RegisterMockedHttpURLLoad("input_field_populated.html");
1675 WebView* web_view = web_view_helper_.InitializeAndLoad(
1676 base_url_ + "input_field_populated.html");
1677 auto* frame = To<WebLocalFrameImpl>(web_view->MainFrame());
1678 WebInputMethodController* active_input_method_controller =
1679 frame->GetInputMethodController();
1680 web_view->SetInitialFocus(false);
1681
1682 frame->SetEditableSelectionOffsets(10, 10);
1683 frame->DeleteSurroundingText(5, 8);
1684 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1685 EXPECT_EQ("01234ijklmnopqrstuvwxyz", info.value.Utf8());
1686 EXPECT_EQ(5, info.selection_start);
1687 EXPECT_EQ(5, info.selection_end);
1688
1689 frame->SetEditableSelectionOffsets(5, 10);
1690 frame->DeleteSurroundingText(3, 5);
1691 info = active_input_method_controller->TextInputInfo();
1692 EXPECT_EQ("01ijklmstuvwxyz", info.value.Utf8());
1693 EXPECT_EQ(2, info.selection_start);
1694 EXPECT_EQ(7, info.selection_end);
1695
1696 frame->SetEditableSelectionOffsets(5, 5);
1697 frame->DeleteSurroundingText(10, 0);
1698 info = active_input_method_controller->TextInputInfo();
1699 EXPECT_EQ("lmstuvwxyz", info.value.Utf8());
1700 EXPECT_EQ(0, info.selection_start);
1701 EXPECT_EQ(0, info.selection_end);
1702
1703 frame->DeleteSurroundingText(0, 20);
1704 info = active_input_method_controller->TextInputInfo();
1705 EXPECT_EQ("", info.value.Utf8());
1706 EXPECT_EQ(0, info.selection_start);
1707 EXPECT_EQ(0, info.selection_end);
1708
1709 frame->DeleteSurroundingText(10, 10);
1710 info = active_input_method_controller->TextInputInfo();
1711 EXPECT_EQ("", info.value.Utf8());
1712 EXPECT_EQ(0, info.selection_start);
1713 EXPECT_EQ(0, info.selection_end);
1714 }
1715
TEST_F(WebViewTest,SetCompositionFromExistingText)1716 TEST_F(WebViewTest, SetCompositionFromExistingText) {
1717 RegisterMockedHttpURLLoad("input_field_populated.html");
1718 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1719 base_url_ + "input_field_populated.html");
1720 web_view->SetInitialFocus(false);
1721 WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
1722 ime_text_spans[0] =
1723 WebImeTextSpan(WebImeTextSpan::Type::kComposition, 0, 4,
1724 ui::mojom::ImeTextSpanThickness::kThin,
1725 ui::mojom::ImeTextSpanUnderlineStyle::kSolid, 0, 0);
1726 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1727 WebInputMethodController* active_input_method_controller =
1728 frame->GetInputMethodController();
1729 frame->SetEditableSelectionOffsets(4, 10);
1730 frame->SetCompositionFromExistingText(8, 12, ime_text_spans);
1731 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1732 EXPECT_EQ(4, info.selection_start);
1733 EXPECT_EQ(10, info.selection_end);
1734 EXPECT_EQ(8, info.composition_start);
1735 EXPECT_EQ(12, info.composition_end);
1736 WebVector<WebImeTextSpan> empty_ime_text_spans;
1737 frame->SetCompositionFromExistingText(0, 0, empty_ime_text_spans);
1738 info = active_input_method_controller->TextInputInfo();
1739 EXPECT_EQ(4, info.selection_start);
1740 EXPECT_EQ(10, info.selection_end);
1741 EXPECT_EQ(-1, info.composition_start);
1742 EXPECT_EQ(-1, info.composition_end);
1743 }
1744
TEST_F(WebViewTest,SetCompositionFromExistingTextInTextArea)1745 TEST_F(WebViewTest, SetCompositionFromExistingTextInTextArea) {
1746 RegisterMockedHttpURLLoad("text_area_populated.html");
1747 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1748 base_url_ + "text_area_populated.html");
1749 web_view->SetInitialFocus(false);
1750 WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
1751 ime_text_spans[0] =
1752 WebImeTextSpan(WebImeTextSpan::Type::kComposition, 0, 4,
1753 ui::mojom::ImeTextSpanThickness::kThin,
1754 ui::mojom::ImeTextSpanUnderlineStyle::kSolid, 0, 0);
1755 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1756 WebInputMethodController* active_input_method_controller =
1757 frame->FrameWidget()->GetActiveWebInputMethodController();
1758 frame->SetEditableSelectionOffsets(27, 27);
1759 std::string new_line_text("\n");
1760 WebVector<WebImeTextSpan> empty_ime_text_spans;
1761 active_input_method_controller->CommitText(
1762 WebString::FromUTF8(new_line_text.c_str()), empty_ime_text_spans,
1763 WebRange(), 0);
1764 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1765 EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz", info.value.Utf8());
1766
1767 frame->SetEditableSelectionOffsets(31, 31);
1768 frame->SetCompositionFromExistingText(30, 34, ime_text_spans);
1769 info = active_input_method_controller->TextInputInfo();
1770 EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz", info.value.Utf8());
1771 EXPECT_EQ(31, info.selection_start);
1772 EXPECT_EQ(31, info.selection_end);
1773 EXPECT_EQ(30, info.composition_start);
1774 EXPECT_EQ(34, info.composition_end);
1775
1776 std::string composition_text("yolo");
1777 active_input_method_controller->CommitText(
1778 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
1779 WebRange(), 0);
1780 info = active_input_method_controller->TextInputInfo();
1781 EXPECT_EQ("0123456789abcdefghijklmnopq\nrsyoloxyz", info.value.Utf8());
1782 EXPECT_EQ(34, info.selection_start);
1783 EXPECT_EQ(34, info.selection_end);
1784 EXPECT_EQ(-1, info.composition_start);
1785 EXPECT_EQ(-1, info.composition_end);
1786 }
1787
TEST_F(WebViewTest,SetCompositionFromExistingTextInRichText)1788 TEST_F(WebViewTest, SetCompositionFromExistingTextInRichText) {
1789 RegisterMockedHttpURLLoad("content_editable_rich_text.html");
1790 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1791 base_url_ + "content_editable_rich_text.html");
1792 web_view->SetInitialFocus(false);
1793 WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
1794 ime_text_spans[0] =
1795 WebImeTextSpan(WebImeTextSpan::Type::kComposition, 0, 4,
1796 ui::mojom::ImeTextSpanThickness::kThin,
1797 ui::mojom::ImeTextSpanUnderlineStyle::kSolid, 0, 0);
1798 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1799 frame->SetEditableSelectionOffsets(1, 1);
1800 WebDocument document = web_view->MainFrameImpl()->GetDocument();
1801 EXPECT_FALSE(document.GetElementById("bold").IsNull());
1802 frame->SetCompositionFromExistingText(0, 4, ime_text_spans);
1803 EXPECT_FALSE(document.GetElementById("bold").IsNull());
1804 }
1805
TEST_F(WebViewTest,SetEditableSelectionOffsetsKeepsComposition)1806 TEST_F(WebViewTest, SetEditableSelectionOffsetsKeepsComposition) {
1807 RegisterMockedHttpURLLoad("input_field_populated.html");
1808 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1809 base_url_ + "input_field_populated.html");
1810 web_view->SetInitialFocus(false);
1811
1812 std::string composition_text_first("hello ");
1813 std::string composition_text_second("world");
1814 WebVector<WebImeTextSpan> empty_ime_text_spans;
1815 WebInputMethodController* active_input_method_controller =
1816 web_view->MainFrameImpl()
1817 ->FrameWidget()
1818 ->GetActiveWebInputMethodController();
1819 active_input_method_controller->CommitText(
1820 WebString::FromUTF8(composition_text_first.c_str()), empty_ime_text_spans,
1821 WebRange(), 0);
1822 active_input_method_controller->SetComposition(
1823 WebString::FromUTF8(composition_text_second.c_str()),
1824 empty_ime_text_spans, WebRange(), 5, 5);
1825
1826 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
1827 EXPECT_EQ("hello world", info.value.Utf8());
1828 EXPECT_EQ(11, info.selection_start);
1829 EXPECT_EQ(11, info.selection_end);
1830 EXPECT_EQ(6, info.composition_start);
1831 EXPECT_EQ(11, info.composition_end);
1832
1833 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
1834 frame->SetEditableSelectionOffsets(6, 6);
1835 info = active_input_method_controller->TextInputInfo();
1836 EXPECT_EQ("hello world", info.value.Utf8());
1837 EXPECT_EQ(6, info.selection_start);
1838 EXPECT_EQ(6, info.selection_end);
1839 EXPECT_EQ(6, info.composition_start);
1840 EXPECT_EQ(11, info.composition_end);
1841
1842 frame->SetEditableSelectionOffsets(8, 8);
1843 info = active_input_method_controller->TextInputInfo();
1844 EXPECT_EQ("hello world", info.value.Utf8());
1845 EXPECT_EQ(8, info.selection_start);
1846 EXPECT_EQ(8, info.selection_end);
1847 EXPECT_EQ(6, info.composition_start);
1848 EXPECT_EQ(11, info.composition_end);
1849
1850 frame->SetEditableSelectionOffsets(11, 11);
1851 info = active_input_method_controller->TextInputInfo();
1852 EXPECT_EQ("hello world", info.value.Utf8());
1853 EXPECT_EQ(11, info.selection_start);
1854 EXPECT_EQ(11, info.selection_end);
1855 EXPECT_EQ(6, info.composition_start);
1856 EXPECT_EQ(11, info.composition_end);
1857
1858 frame->SetEditableSelectionOffsets(6, 11);
1859 info = active_input_method_controller->TextInputInfo();
1860 EXPECT_EQ("hello world", info.value.Utf8());
1861 EXPECT_EQ(6, info.selection_start);
1862 EXPECT_EQ(11, info.selection_end);
1863 EXPECT_EQ(6, info.composition_start);
1864 EXPECT_EQ(11, info.composition_end);
1865
1866 frame->SetEditableSelectionOffsets(2, 2);
1867 info = active_input_method_controller->TextInputInfo();
1868 EXPECT_EQ("hello world", info.value.Utf8());
1869 EXPECT_EQ(2, info.selection_start);
1870 EXPECT_EQ(2, info.selection_end);
1871 // Composition range should be reset by browser process or keyboard apps.
1872 EXPECT_EQ(6, info.composition_start);
1873 EXPECT_EQ(11, info.composition_end);
1874 }
1875
TEST_F(WebViewTest,IsSelectionAnchorFirst)1876 TEST_F(WebViewTest, IsSelectionAnchorFirst) {
1877 RegisterMockedHttpURLLoad("input_field_populated.html");
1878 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
1879 base_url_ + "input_field_populated.html");
1880 WebLocalFrame* frame = web_view->MainFrameImpl();
1881
1882 web_view->SetInitialFocus(false);
1883 frame->SetEditableSelectionOffsets(4, 10);
1884 EXPECT_TRUE(frame->IsSelectionAnchorFirst());
1885 WebRect anchor;
1886 WebRect focus;
1887 web_view->MainFrameWidget()->SelectionBounds(anchor, focus);
1888 frame->SelectRange(gfx::Point(focus.x, focus.y),
1889 gfx::Point(anchor.x, anchor.y));
1890 EXPECT_FALSE(frame->IsSelectionAnchorFirst());
1891 }
1892
TEST_F(WebViewTest,MoveFocusToNextFocusableElementInFormWithKeyEventListenersAndNonEditableElements)1893 TEST_F(
1894 WebViewTest,
1895 MoveFocusToNextFocusableElementInFormWithKeyEventListenersAndNonEditableElements) {
1896 const std::string test_file =
1897 "advance_focus_in_form_with_key_event_listeners.html";
1898 RegisterMockedHttpURLLoad(test_file);
1899 WebViewImpl* web_view =
1900 web_view_helper_.InitializeAndLoad(base_url_ + test_file);
1901 web_view->SetInitialFocus(false);
1902 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
1903 WebInputMethodController* active_input_method_controller =
1904 web_view->MainFrameImpl()
1905 ->FrameWidget()
1906 ->GetActiveWebInputMethodController();
1907 const int default_text_input_flags = kWebTextInputFlagNone;
1908
1909 struct FocusedElement {
1910 AtomicString element_id;
1911 int next_previous_flags;
1912 } focused_elements[] = {
1913 {"input1",
1914 default_text_input_flags | kWebTextInputFlagHaveNextFocusableElement},
1915 {"contenteditable1", kWebTextInputFlagHaveNextFocusableElement |
1916 kWebTextInputFlagHavePreviousFocusableElement},
1917 {"input2", default_text_input_flags |
1918 kWebTextInputFlagHaveNextFocusableElement |
1919 kWebTextInputFlagHavePreviousFocusableElement},
1920 {"textarea1", default_text_input_flags |
1921 kWebTextInputFlagHaveNextFocusableElement |
1922 kWebTextInputFlagHavePreviousFocusableElement},
1923 {"input3", default_text_input_flags |
1924 kWebTextInputFlagHaveNextFocusableElement |
1925 kWebTextInputFlagHavePreviousFocusableElement},
1926 {"textarea2", default_text_input_flags |
1927 kWebTextInputFlagHavePreviousFocusableElement},
1928 };
1929
1930 // Forward Navigation in form1 with NEXT
1931 Element* input1 = document->getElementById("input1");
1932 input1->focus();
1933 Element* current_focus = nullptr;
1934 Element* next_focus = nullptr;
1935 int next_previous_flags;
1936 for (size_t i = 0; i < base::size(focused_elements); ++i) {
1937 current_focus = document->getElementById(focused_elements[i].element_id);
1938 EXPECT_EQ(current_focus, document->FocusedElement());
1939 next_previous_flags =
1940 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
1941 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
1942 next_focus =
1943 document->GetPage()->GetFocusController().NextFocusableElementInForm(
1944 current_focus, mojom::blink::FocusType::kForward);
1945 if (next_focus) {
1946 EXPECT_EQ(next_focus->GetIdAttribute(),
1947 focused_elements[i + 1].element_id);
1948 }
1949 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
1950 mojom::blink::FocusType::kForward);
1951 }
1952 // Now focus will stay on previous focus itself, because it has no next
1953 // element.
1954 EXPECT_EQ(current_focus, document->FocusedElement());
1955
1956 // Backward Navigation in form1 with PREVIOUS
1957 for (size_t i = base::size(focused_elements); i-- > 0;) {
1958 current_focus = document->getElementById(focused_elements[i].element_id);
1959 EXPECT_EQ(current_focus, document->FocusedElement());
1960 next_previous_flags =
1961 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
1962 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
1963 next_focus =
1964 document->GetPage()->GetFocusController().NextFocusableElementInForm(
1965 current_focus, mojom::blink::FocusType::kBackward);
1966 if (next_focus) {
1967 EXPECT_EQ(next_focus->GetIdAttribute(),
1968 focused_elements[i - 1].element_id);
1969 }
1970 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
1971 mojom::blink::FocusType::kBackward);
1972 }
1973 // Now focus will stay on previous focus itself, because it has no previous
1974 // element.
1975 EXPECT_EQ(current_focus, document->FocusedElement());
1976
1977 // Setting a non editable element as focus in form1, and ensuring editable
1978 // navigation is fine in forward and backward.
1979 Element* button1 = document->getElementById("button1");
1980 button1->focus();
1981 next_previous_flags =
1982 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
1983 EXPECT_EQ(kWebTextInputFlagHaveNextFocusableElement |
1984 kWebTextInputFlagHavePreviousFocusableElement,
1985 next_previous_flags);
1986 next_focus =
1987 document->GetPage()->GetFocusController().NextFocusableElementInForm(
1988 button1, mojom::blink::FocusType::kForward);
1989 EXPECT_EQ(next_focus->GetIdAttribute(), "contenteditable1");
1990 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
1991 mojom::blink::FocusType::kForward);
1992 Element* content_editable1 = document->getElementById("contenteditable1");
1993 EXPECT_EQ(content_editable1, document->FocusedElement());
1994 button1->focus();
1995 next_focus =
1996 document->GetPage()->GetFocusController().NextFocusableElementInForm(
1997 button1, mojom::blink::FocusType::kBackward);
1998 EXPECT_EQ(next_focus->GetIdAttribute(), "input1");
1999 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2000 mojom::blink::FocusType::kBackward);
2001 EXPECT_EQ(input1, document->FocusedElement());
2002
2003 Element* anchor1 = document->getElementById("anchor1");
2004 anchor1->focus();
2005 next_previous_flags =
2006 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2007 // No Next/Previous element for elements outside form.
2008 EXPECT_EQ(0, next_previous_flags);
2009 next_focus =
2010 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2011 anchor1, mojom::blink::FocusType::kForward);
2012 EXPECT_EQ(next_focus, nullptr);
2013 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2014 mojom::blink::FocusType::kForward);
2015 // Since anchor is not a form control element, next/previous element will
2016 // be null, hence focus will stay same as it is.
2017 EXPECT_EQ(anchor1, document->FocusedElement());
2018
2019 next_focus =
2020 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2021 anchor1, mojom::blink::FocusType::kBackward);
2022 EXPECT_EQ(next_focus, nullptr);
2023 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2024 mojom::blink::FocusType::kBackward);
2025 EXPECT_EQ(anchor1, document->FocusedElement());
2026
2027 // Navigation of elements which is not part of any forms.
2028 Element* text_area3 = document->getElementById("textarea3");
2029 text_area3->focus();
2030 next_previous_flags =
2031 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2032 // No Next/Previous element for elements outside form.
2033 EXPECT_EQ(default_text_input_flags, next_previous_flags);
2034 next_focus =
2035 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2036 text_area3, mojom::blink::FocusType::kForward);
2037 EXPECT_EQ(next_focus, nullptr);
2038 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2039 mojom::blink::FocusType::kForward);
2040 // No Next/Previous element to this element because it's not part of any
2041 // form. Hence focus won't change wrt NEXT/PREVIOUS.
2042 EXPECT_EQ(text_area3, document->FocusedElement());
2043 next_focus =
2044 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2045 text_area3, mojom::blink::FocusType::kBackward);
2046 EXPECT_EQ(next_focus, nullptr);
2047 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2048 mojom::blink::FocusType::kBackward);
2049 EXPECT_EQ(text_area3, document->FocusedElement());
2050
2051 // Navigation from an element which is part of a form but not an editable
2052 // element.
2053 Element* button2 = document->getElementById("button2");
2054 button2->focus();
2055 next_previous_flags =
2056 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2057 // No Next element for this element, due to last element outside the form.
2058 EXPECT_EQ(kWebTextInputFlagHavePreviousFocusableElement, next_previous_flags);
2059 next_focus =
2060 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2061 button2, mojom::blink::FocusType::kForward);
2062 EXPECT_EQ(next_focus, nullptr);
2063 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2064 mojom::blink::FocusType::kForward);
2065 // No Next element to this element because it's not part of any form.
2066 // Hence focus won't change wrt NEXT.
2067 EXPECT_EQ(button2, document->FocusedElement());
2068 Element* text_area2 = document->getElementById("textarea2");
2069 next_focus =
2070 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2071 button2, mojom::blink::FocusType::kBackward);
2072 EXPECT_EQ(next_focus, text_area2);
2073 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2074 mojom::blink::FocusType::kBackward);
2075 // Since button is a form control element from form1, ensuring focus is set
2076 // at correct position.
2077 EXPECT_EQ(text_area2, document->FocusedElement());
2078
2079 Element* content_editable2 = document->getElementById("contenteditable2");
2080 document->SetFocusedElement(
2081 content_editable2, FocusParams(SelectionBehaviorOnFocus::kNone,
2082 mojom::blink::FocusType::kNone, nullptr));
2083 next_previous_flags =
2084 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2085 // No Next/Previous element for elements outside form.
2086 EXPECT_EQ(0, next_previous_flags);
2087 next_focus =
2088 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2089 content_editable2, mojom::blink::FocusType::kForward);
2090 EXPECT_EQ(next_focus, nullptr);
2091 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2092 mojom::blink::FocusType::kForward);
2093 // No Next/Previous element to this element because it's not part of any
2094 // form. Hence focus won't change wrt NEXT/PREVIOUS.
2095 EXPECT_EQ(content_editable2, document->FocusedElement());
2096 next_focus =
2097 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2098 content_editable2, mojom::blink::FocusType::kBackward);
2099 EXPECT_EQ(next_focus, nullptr);
2100 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2101 mojom::blink::FocusType::kBackward);
2102 EXPECT_EQ(content_editable2, document->FocusedElement());
2103
2104 // Navigation of elements which is having invalid form attribute and hence
2105 // not part of any forms.
2106 Element* text_area4 = document->getElementById("textarea4");
2107 text_area4->focus();
2108 next_previous_flags =
2109 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2110 // No Next/Previous element for elements which is having invalid form
2111 // attribute.
2112 EXPECT_EQ(default_text_input_flags, next_previous_flags);
2113 next_focus =
2114 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2115 text_area4, mojom::blink::FocusType::kForward);
2116 EXPECT_EQ(next_focus, nullptr);
2117 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2118 mojom::blink::FocusType::kForward);
2119 // No Next/Previous element to this element because it's not part of any
2120 // form. Hence focus won't change wrt NEXT/PREVIOUS.
2121 EXPECT_EQ(text_area4, document->FocusedElement());
2122 next_focus =
2123 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2124 text_area4, mojom::blink::FocusType::kBackward);
2125 EXPECT_EQ(next_focus, nullptr);
2126 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2127 mojom::blink::FocusType::kBackward);
2128 EXPECT_EQ(text_area4, document->FocusedElement());
2129
2130 web_view_helper_.Reset();
2131 }
2132
TEST_F(WebViewTest,MoveFocusToNextFocusableElementInFormWithNonEditableNonFormControlElements)2133 TEST_F(
2134 WebViewTest,
2135 MoveFocusToNextFocusableElementInFormWithNonEditableNonFormControlElements) {
2136 const std::string test_file =
2137 "advance_focus_in_form_with_key_event_listeners.html";
2138 RegisterMockedHttpURLLoad(test_file);
2139 WebViewImpl* web_view =
2140 web_view_helper_.InitializeAndLoad(base_url_ + test_file);
2141 web_view->SetInitialFocus(false);
2142 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
2143 WebInputMethodController* active_input_method_controller =
2144 web_view->MainFrameImpl()
2145 ->FrameWidget()
2146 ->GetActiveWebInputMethodController();
2147 const int default_text_input_flags = kWebTextInputFlagNone;
2148
2149 struct FocusedElement {
2150 const char* element_id;
2151 int next_previous_flags;
2152 } focused_elements[] = {
2153 {"textarea5",
2154 default_text_input_flags | kWebTextInputFlagHaveNextFocusableElement},
2155 {"input4", default_text_input_flags |
2156 kWebTextInputFlagHaveNextFocusableElement |
2157 kWebTextInputFlagHavePreviousFocusableElement},
2158 {"contenteditable3", kWebTextInputFlagHaveNextFocusableElement |
2159 kWebTextInputFlagHavePreviousFocusableElement},
2160 {"input5", kWebTextInputFlagHavePreviousFocusableElement},
2161 };
2162
2163 // Forward Navigation in form2 with NEXT
2164 Element* text_area5 = document->getElementById("textarea5");
2165 text_area5->focus();
2166 Element* current_focus = nullptr;
2167 Element* next_focus = nullptr;
2168 int next_previous_flags;
2169 for (size_t i = 0; i < base::size(focused_elements); ++i) {
2170 current_focus = document->getElementById(focused_elements[i].element_id);
2171 EXPECT_EQ(current_focus, document->FocusedElement());
2172 next_previous_flags =
2173 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2174 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
2175 next_focus =
2176 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2177 current_focus, mojom::blink::FocusType::kForward);
2178 if (next_focus) {
2179 EXPECT_EQ(next_focus->GetIdAttribute(),
2180 focused_elements[i + 1].element_id);
2181 }
2182 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2183 mojom::blink::FocusType::kForward);
2184 }
2185 // Now focus will stay on previous focus itself, because it has no next
2186 // element.
2187 EXPECT_EQ(current_focus, document->FocusedElement());
2188
2189 // Backward Navigation in form1 with PREVIOUS
2190 for (size_t i = base::size(focused_elements); i-- > 0;) {
2191 current_focus = document->getElementById(focused_elements[i].element_id);
2192 EXPECT_EQ(current_focus, document->FocusedElement());
2193 next_previous_flags =
2194 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2195 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
2196 next_focus =
2197 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2198 current_focus, mojom::blink::FocusType::kBackward);
2199 if (next_focus) {
2200 EXPECT_EQ(next_focus->GetIdAttribute(),
2201 focused_elements[i - 1].element_id);
2202 }
2203 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2204 mojom::blink::FocusType::kBackward);
2205 }
2206 // Now focus will stay on previous focus itself, because it has no previous
2207 // element.
2208 EXPECT_EQ(current_focus, document->FocusedElement());
2209
2210 // Setting a non editable element as focus in form1, and ensuring editable
2211 // navigation is fine in forward and backward.
2212 Element* anchor2 = document->getElementById("anchor2");
2213 anchor2->focus();
2214 next_previous_flags =
2215 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2216 // No Next/Previous element for non-form control elements inside form.
2217 EXPECT_EQ(0, next_previous_flags);
2218 next_focus =
2219 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2220 anchor2, mojom::blink::FocusType::kForward);
2221 EXPECT_EQ(next_focus, nullptr);
2222 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2223 mojom::blink::FocusType::kForward);
2224 // Since anchor is not a form control element, next/previous element will
2225 // be null, hence focus will stay same as it is.
2226 EXPECT_EQ(anchor2, document->FocusedElement());
2227 next_focus =
2228 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2229 anchor2, mojom::blink::FocusType::kBackward);
2230 EXPECT_EQ(next_focus, nullptr);
2231 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2232 mojom::blink::FocusType::kBackward);
2233 EXPECT_EQ(anchor2, document->FocusedElement());
2234
2235 web_view_helper_.Reset();
2236 }
2237
TEST_F(WebViewTest,MoveFocusToNextFocusableElementInFormWithTabIndexElements)2238 TEST_F(WebViewTest, MoveFocusToNextFocusableElementInFormWithTabIndexElements) {
2239 const std::string test_file =
2240 "advance_focus_in_form_with_tabindex_elements.html";
2241 RegisterMockedHttpURLLoad(test_file);
2242 WebViewImpl* web_view =
2243 web_view_helper_.InitializeAndLoad(base_url_ + test_file);
2244 web_view->SetInitialFocus(false);
2245 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
2246 WebInputMethodController* active_input_method_controller =
2247 web_view->MainFrameImpl()
2248 ->FrameWidget()
2249 ->GetActiveWebInputMethodController();
2250 const int default_text_input_flags = kWebTextInputFlagNone;
2251
2252 struct FocusedElement {
2253 const char* element_id;
2254 int next_previous_flags;
2255 } focused_elements[] = {
2256 {"textarea6",
2257 default_text_input_flags | kWebTextInputFlagHaveNextFocusableElement},
2258 {"input5", default_text_input_flags |
2259 kWebTextInputFlagHaveNextFocusableElement |
2260 kWebTextInputFlagHavePreviousFocusableElement},
2261 {"contenteditable4", kWebTextInputFlagHaveNextFocusableElement |
2262 kWebTextInputFlagHavePreviousFocusableElement},
2263 {"input6", default_text_input_flags |
2264 kWebTextInputFlagHavePreviousFocusableElement},
2265 };
2266
2267 // Forward Navigation in form with NEXT which has tabindex attribute
2268 // which differs visual order.
2269 Element* text_area6 = document->getElementById("textarea6");
2270 text_area6->focus();
2271 Element* current_focus = nullptr;
2272 Element* next_focus = nullptr;
2273 int next_previous_flags;
2274 for (size_t i = 0; i < base::size(focused_elements); ++i) {
2275 current_focus = document->getElementById(focused_elements[i].element_id);
2276 EXPECT_EQ(current_focus, document->FocusedElement());
2277 next_previous_flags =
2278 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2279 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
2280 next_focus =
2281 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2282 current_focus, mojom::blink::FocusType::kForward);
2283 if (next_focus) {
2284 EXPECT_EQ(next_focus->GetIdAttribute(),
2285 focused_elements[i + 1].element_id);
2286 }
2287 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2288 mojom::blink::FocusType::kForward);
2289 }
2290 // No next editable element which is focusable with proper tab index, hence
2291 // staying on previous focus.
2292 EXPECT_EQ(current_focus, document->FocusedElement());
2293
2294 // Backward Navigation in form with PREVIOUS which has tabindex attribute
2295 // which differs visual order.
2296 for (size_t i = base::size(focused_elements); i-- > 0;) {
2297 current_focus = document->getElementById(focused_elements[i].element_id);
2298 EXPECT_EQ(current_focus, document->FocusedElement());
2299 next_previous_flags =
2300 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2301 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
2302 next_focus =
2303 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2304 current_focus, mojom::blink::FocusType::kBackward);
2305 if (next_focus) {
2306 EXPECT_EQ(next_focus->GetIdAttribute(),
2307 focused_elements[i - 1].element_id);
2308 }
2309 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2310 mojom::blink::FocusType::kBackward);
2311 }
2312 // Now focus will stay on previous focus itself, because it has no previous
2313 // element.
2314 EXPECT_EQ(current_focus, document->FocusedElement());
2315
2316 // Setting an element which has invalid tabindex and ensuring it is not
2317 // modifying further navigation.
2318 Element* content_editable5 = document->getElementById("contenteditable5");
2319 content_editable5->focus();
2320 Element* input6 = document->getElementById("input6");
2321 next_focus =
2322 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2323 content_editable5, mojom::blink::FocusType::kForward);
2324 EXPECT_EQ(next_focus, input6);
2325 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2326 mojom::blink::FocusType::kForward);
2327 EXPECT_EQ(input6, document->FocusedElement());
2328 content_editable5->focus();
2329 next_focus =
2330 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2331 content_editable5, mojom::blink::FocusType::kBackward);
2332 EXPECT_EQ(next_focus, text_area6);
2333 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2334 mojom::blink::FocusType::kBackward);
2335 EXPECT_EQ(text_area6, document->FocusedElement());
2336
2337 web_view_helper_.Reset();
2338 }
2339
TEST_F(WebViewTest,MoveFocusToNextFocusableElementInFormWithDisabledAndReadonlyElements)2340 TEST_F(WebViewTest,
2341 MoveFocusToNextFocusableElementInFormWithDisabledAndReadonlyElements) {
2342 const std::string test_file =
2343 "advance_focus_in_form_with_disabled_and_readonly_elements.html";
2344 RegisterMockedHttpURLLoad(test_file);
2345 WebViewImpl* web_view =
2346 web_view_helper_.InitializeAndLoad(base_url_ + test_file);
2347 web_view->SetInitialFocus(false);
2348 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
2349 WebInputMethodController* active_input_method_controller =
2350 web_view->MainFrameImpl()
2351 ->FrameWidget()
2352 ->GetActiveWebInputMethodController();
2353
2354 struct FocusedElement {
2355 const char* element_id;
2356 int next_previous_flags;
2357 } focused_elements[] = {
2358 {"contenteditable6", kWebTextInputFlagHaveNextFocusableElement},
2359 {"contenteditable7", kWebTextInputFlagHavePreviousFocusableElement},
2360 };
2361 // Forward Navigation in form with NEXT which has has disabled/enabled
2362 // elements which will gets skipped during navigation.
2363 Element* content_editable6 = document->getElementById("contenteditable6");
2364 content_editable6->focus();
2365 Element* current_focus = nullptr;
2366 Element* next_focus = nullptr;
2367 int next_previous_flags;
2368 for (size_t i = 0; i < base::size(focused_elements); ++i) {
2369 current_focus = document->getElementById(focused_elements[i].element_id);
2370 EXPECT_EQ(current_focus, document->FocusedElement());
2371 next_previous_flags =
2372 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2373 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
2374 next_focus =
2375 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2376 current_focus, mojom::blink::FocusType::kForward);
2377 if (next_focus) {
2378 EXPECT_EQ(next_focus->GetIdAttribute(),
2379 focused_elements[i + 1].element_id);
2380 }
2381 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2382 mojom::blink::FocusType::kForward);
2383 }
2384 // No next editable element which is focusable, hence staying on previous
2385 // focus.
2386 EXPECT_EQ(current_focus, document->FocusedElement());
2387
2388 // Backward Navigation in form with PREVIOUS which has has
2389 // disabled/enabled elements which will gets skipped during navigation.
2390 for (size_t i = base::size(focused_elements); i-- > 0;) {
2391 current_focus = document->getElementById(focused_elements[i].element_id);
2392 EXPECT_EQ(current_focus, document->FocusedElement());
2393 next_previous_flags =
2394 active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
2395 EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
2396 next_focus =
2397 document->GetPage()->GetFocusController().NextFocusableElementInForm(
2398 current_focus, mojom::blink::FocusType::kBackward);
2399 if (next_focus) {
2400 EXPECT_EQ(next_focus->GetIdAttribute(),
2401 focused_elements[i - 1].element_id);
2402 }
2403 web_view->MainFrameImpl()->GetFrame()->AdvanceFocusInForm(
2404 mojom::blink::FocusType::kBackward);
2405 }
2406 // Now focus will stay on previous focus itself, because it has no previous
2407 // element.
2408 EXPECT_EQ(current_focus, document->FocusedElement());
2409
2410 web_view_helper_.Reset();
2411 }
2412
TEST_F(WebViewTest,ExitingDeviceEmulationResetsPageScale)2413 TEST_F(WebViewTest, ExitingDeviceEmulationResetsPageScale) {
2414 RegisterMockedHttpURLLoad("200-by-300.html");
2415 WebViewImpl* web_view_impl =
2416 web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
2417 web_view_impl->MainFrameWidget()->Resize(WebSize(200, 300));
2418
2419 float page_scale_expected = web_view_impl->PageScaleFactor();
2420
2421 WebDeviceEmulationParams params;
2422 params.screen_position = WebDeviceEmulationParams::kDesktop;
2423 params.device_scale_factor = 0;
2424 params.scale = 1;
2425
2426 web_view_impl->EnableDeviceEmulation(params);
2427
2428 web_view_impl->SetPageScaleFactor(2);
2429
2430 web_view_impl->DisableDeviceEmulation();
2431
2432 EXPECT_EQ(page_scale_expected, web_view_impl->PageScaleFactor());
2433 }
2434
TEST_F(WebViewTest,HistoryResetScrollAndScaleState)2435 TEST_F(WebViewTest, HistoryResetScrollAndScaleState) {
2436 RegisterMockedHttpURLLoad("200-by-300.html");
2437 WebViewImpl* web_view_impl =
2438 web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
2439 web_view_impl->MainFrameWidget()->Resize(WebSize(100, 150));
2440 UpdateAllLifecyclePhases();
2441 EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
2442 EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
2443
2444 // Make the page scale and scroll with the given paremeters.
2445 web_view_impl->SetPageScaleFactor(2.0f);
2446 web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(94, 111));
2447 EXPECT_EQ(2.0f, web_view_impl->PageScaleFactor());
2448 EXPECT_EQ(94, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
2449 EXPECT_EQ(111, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
2450 auto* main_frame_local =
2451 To<LocalFrame>(web_view_impl->GetPage()->MainFrame());
2452 main_frame_local->Loader().SaveScrollState();
2453 EXPECT_EQ(2.0f, main_frame_local->Loader()
2454 .GetDocumentLoader()
2455 ->GetHistoryItem()
2456 ->GetViewState()
2457 ->page_scale_factor_);
2458 EXPECT_EQ(94, main_frame_local->Loader()
2459 .GetDocumentLoader()
2460 ->GetHistoryItem()
2461 ->GetViewState()
2462 ->scroll_offset_.Width());
2463 EXPECT_EQ(111, main_frame_local->Loader()
2464 .GetDocumentLoader()
2465 ->GetHistoryItem()
2466 ->GetViewState()
2467 ->scroll_offset_.Height());
2468
2469 // Confirm that resetting the page state resets the saved scroll position.
2470 web_view_impl->ResetScrollAndScaleState();
2471 EXPECT_EQ(1.0f, web_view_impl->PageScaleFactor());
2472 EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
2473 EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
2474 EXPECT_FALSE(main_frame_local->Loader()
2475 .GetDocumentLoader()
2476 ->GetHistoryItem()
2477 ->GetViewState()
2478 .has_value());
2479 }
2480
TEST_F(WebViewTest,BackForwardRestoreScroll)2481 TEST_F(WebViewTest, BackForwardRestoreScroll) {
2482 RegisterMockedHttpURLLoad("back_forward_restore_scroll.html");
2483 WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(
2484 base_url_ + "back_forward_restore_scroll.html");
2485 web_view_impl->MainFrameWidget()->Resize(WebSize(640, 480));
2486 web_view_impl->MainFrameWidget()->UpdateAllLifecyclePhases(
2487 DocumentUpdateReason::kTest);
2488
2489 // Emulate a user scroll
2490 web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(0, 900));
2491 auto* main_frame_local =
2492 To<LocalFrame>(web_view_impl->GetPage()->MainFrame());
2493 Persistent<HistoryItem> item1 =
2494 main_frame_local->Loader().GetDocumentLoader()->GetHistoryItem();
2495
2496 // Click an anchor
2497 FrameLoadRequest request_a(
2498 main_frame_local->GetDocument(),
2499 ResourceRequest(main_frame_local->GetDocument()->CompleteURL("#a")));
2500 main_frame_local->Loader().StartNavigation(request_a);
2501 Persistent<HistoryItem> item2 =
2502 main_frame_local->Loader().GetDocumentLoader()->GetHistoryItem();
2503
2504 // Go back, then forward, then back again.
2505 main_frame_local->Loader().GetDocumentLoader()->CommitSameDocumentNavigation(
2506 item1->Url(), WebFrameLoadType::kBackForward, item1.Get(),
2507 ClientRedirectPolicy::kNotClientRedirect, nullptr, false, nullptr);
2508 main_frame_local->Loader().GetDocumentLoader()->CommitSameDocumentNavigation(
2509 item2->Url(), WebFrameLoadType::kBackForward, item2.Get(),
2510 ClientRedirectPolicy::kNotClientRedirect, nullptr, false, nullptr);
2511 main_frame_local->Loader().GetDocumentLoader()->CommitSameDocumentNavigation(
2512 item1->Url(), WebFrameLoadType::kBackForward, item1.Get(),
2513 ClientRedirectPolicy::kNotClientRedirect, nullptr, false, nullptr);
2514 web_view_impl->MainFrameWidget()->UpdateAllLifecyclePhases(
2515 DocumentUpdateReason::kTest);
2516
2517 // Click a different anchor
2518 FrameLoadRequest request_b(
2519 main_frame_local->GetDocument(),
2520 ResourceRequest(main_frame_local->GetDocument()->CompleteURL("#b")));
2521 main_frame_local->Loader().StartNavigation(request_b);
2522 Persistent<HistoryItem> item3 =
2523 main_frame_local->Loader().GetDocumentLoader()->GetHistoryItem();
2524 web_view_impl->MainFrameWidget()->UpdateAllLifecyclePhases(
2525 DocumentUpdateReason::kTest);
2526
2527 // Go back, then forward. The scroll position should be properly set on the
2528 // forward navigation.
2529 main_frame_local->Loader().GetDocumentLoader()->CommitSameDocumentNavigation(
2530 item1->Url(), WebFrameLoadType::kBackForward, item1.Get(),
2531 ClientRedirectPolicy::kNotClientRedirect, nullptr, false, nullptr);
2532
2533 main_frame_local->Loader().GetDocumentLoader()->CommitSameDocumentNavigation(
2534 item3->Url(), WebFrameLoadType::kBackForward, item3.Get(),
2535 ClientRedirectPolicy::kNotClientRedirect, nullptr, false, nullptr);
2536 // The scroll offset is only applied via invoking the anchor via the main
2537 // lifecycle, or a forced layout.
2538 // TODO(chrishtr): At the moment, WebLocalFrameImpl::GetScrollOffset() does
2539 // not force a layout. Script-exposed scroll offset-reading methods do,
2540 // however. It seems wrong not to force a layout.
2541 EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
2542 EXPECT_GT(web_view_impl->MainFrameImpl()->GetScrollOffset().height, 2000);
2543 }
2544
2545 // Tests that scroll offset modified during fullscreen is preserved when
2546 // exiting fullscreen.
TEST_F(WebViewTest,FullscreenNoResetScroll)2547 TEST_F(WebViewTest, FullscreenNoResetScroll) {
2548 RegisterMockedHttpURLLoad("fullscreen_style.html");
2549 WebViewImpl* web_view_impl =
2550 web_view_helper_.InitializeAndLoad(base_url_ + "fullscreen_style.html");
2551 web_view_impl->MainFrameWidget()->Resize(WebSize(800, 600));
2552 UpdateAllLifecyclePhases();
2553
2554 // Scroll the page down.
2555 web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(0, 2000));
2556 ASSERT_EQ(2000, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
2557
2558 // Enter fullscreen.
2559 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
2560 Element* element = frame->GetDocument()->documentElement();
2561 LocalFrame::NotifyUserActivation(frame);
2562 Fullscreen::RequestFullscreen(*element);
2563 web_view_impl->MainFrameWidget()->DidEnterFullscreen();
2564 UpdateAllLifecyclePhases();
2565
2566 // Assert the scroll position on the document element doesn't change.
2567 ASSERT_EQ(2000, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
2568
2569 web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(0, 2100));
2570
2571 web_view_impl->MainFrameWidget()->DidExitFullscreen();
2572 UpdateAllLifecyclePhases();
2573
2574 EXPECT_EQ(2100, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
2575 }
2576
2577 // Tests that background color is read from the backdrop on fullscreen.
TEST_F(WebViewTest,FullscreenBackgroundColor)2578 TEST_F(WebViewTest, FullscreenBackgroundColor) {
2579 RegisterMockedHttpURLLoad("fullscreen_style.html");
2580 WebViewImpl* web_view_impl =
2581 web_view_helper_.InitializeAndLoad(base_url_ + "fullscreen_style.html");
2582 web_view_impl->MainFrameWidget()->Resize(WebSize(800, 600));
2583 UpdateAllLifecyclePhases();
2584 EXPECT_EQ(SK_ColorWHITE, web_view_impl->BackgroundColor());
2585
2586 // Enter fullscreen.
2587 LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
2588 Element* element = frame->GetDocument()->getElementById("fullscreenElement");
2589 ASSERT_TRUE(element);
2590 LocalFrame::NotifyUserActivation(frame);
2591 Fullscreen::RequestFullscreen(*element);
2592 web_view_impl->MainFrameWidget()->DidEnterFullscreen();
2593 UpdateAllLifecyclePhases();
2594
2595 EXPECT_EQ(SK_ColorYELLOW, web_view_impl->BackgroundColor());
2596 }
2597
2598 class PrintWebViewClient : public frame_test_helpers::TestWebViewClient {
2599 public:
PrintWebViewClient()2600 PrintWebViewClient() : print_called_(false) {}
2601
2602 // WebViewClient methods
PrintPage(WebLocalFrame *)2603 void PrintPage(WebLocalFrame*) override { print_called_ = true; }
2604
PrintCalled() const2605 bool PrintCalled() const { return print_called_; }
2606
2607 private:
2608 bool print_called_;
2609 };
2610
TEST_F(WebViewTest,PrintWithXHRInFlight)2611 TEST_F(WebViewTest, PrintWithXHRInFlight) {
2612 PrintWebViewClient client;
2613 RegisterMockedHttpURLLoad("print_with_xhr_inflight.html");
2614 WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(
2615 base_url_ + "print_with_xhr_inflight.html", nullptr, &client);
2616
2617 ASSERT_TRUE(To<LocalFrame>(web_view_impl->GetPage()->MainFrame())
2618 ->GetDocument()
2619 ->LoadEventFinished());
2620 EXPECT_TRUE(client.PrintCalled());
2621 web_view_helper_.Reset();
2622 }
2623
DragAndDropURL(WebViewImpl * web_view,const std::string & url)2624 static void DragAndDropURL(WebViewImpl* web_view, const std::string& url) {
2625 WebDragData drag_data;
2626 drag_data.Initialize();
2627
2628 WebDragData::Item item;
2629 item.storage_type = WebDragData::Item::kStorageTypeString;
2630 item.string_type = "text/uri-list";
2631 item.string_data = WebString::FromUTF8(url);
2632 drag_data.AddItem(item);
2633
2634 const gfx::PointF client_point;
2635 const gfx::PointF screen_point;
2636 WebFrameWidget* widget = web_view->MainFrameImpl()->FrameWidget();
2637 widget->DragTargetDragEnter(drag_data, client_point, screen_point,
2638 kWebDragOperationCopy, 0);
2639 widget->DragTargetDrop(drag_data, client_point, screen_point, 0);
2640 frame_test_helpers::PumpPendingRequestsForFrameToLoad(
2641 web_view->MainFrameImpl());
2642 }
2643
TEST_F(WebViewTest,DragDropURL)2644 TEST_F(WebViewTest, DragDropURL) {
2645 RegisterMockedHttpURLLoad("foo.html");
2646 RegisterMockedHttpURLLoad("bar.html");
2647
2648 const std::string foo_url = base_url_ + "foo.html";
2649 const std::string bar_url = base_url_ + "bar.html";
2650
2651 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(foo_url);
2652
2653 ASSERT_TRUE(web_view);
2654
2655 // Drag and drop barUrl and verify that we've navigated to it.
2656 DragAndDropURL(web_view, bar_url);
2657 EXPECT_EQ(bar_url,
2658 web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
2659
2660 // Drag and drop fooUrl and verify that we've navigated back to it.
2661 DragAndDropURL(web_view, foo_url);
2662 EXPECT_EQ(foo_url,
2663 web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
2664
2665 // Disable navigation on drag-and-drop.
2666 web_view->SettingsImpl()->SetNavigateOnDragDrop(false);
2667
2668 // Attempt to drag and drop to barUrl and verify that no navigation has
2669 // occurred.
2670 DragAndDropURL(web_view, bar_url);
2671 EXPECT_EQ(foo_url,
2672 web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
2673 }
2674
TapElement(WebInputEvent::Type type,Element * element)2675 bool WebViewTest::TapElement(WebInputEvent::Type type, Element* element) {
2676 if (!element || !element->GetLayoutObject())
2677 return false;
2678
2679 DCHECK(web_view_helper_.GetWebView());
2680 element->scrollIntoViewIfNeeded();
2681
2682 FloatPoint center(
2683 web_view_helper_.GetWebView()
2684 ->MainFrameImpl()
2685 ->GetFrameView()
2686 ->FrameToScreen(element->GetLayoutObject()->AbsoluteBoundingBoxRect())
2687 .Center());
2688
2689 WebGestureEvent event(type, WebInputEvent::kNoModifiers,
2690 WebInputEvent::GetStaticTimeStampForTests(),
2691 WebGestureDevice::kTouchscreen);
2692 event.SetPositionInWidget(center);
2693
2694 web_view_helper_.GetWebView()->MainFrameWidget()->HandleInputEvent(
2695 WebCoalescedInputEvent(event));
2696 RunPendingTasks();
2697 return true;
2698 }
2699
TapElementById(WebInputEvent::Type type,const WebString & id)2700 bool WebViewTest::TapElementById(WebInputEvent::Type type,
2701 const WebString& id) {
2702 DCHECK(web_view_helper_.GetWebView());
2703 Element* element = static_cast<Element*>(
2704 web_view_helper_.LocalMainFrame()->GetDocument().GetElementById(id));
2705 return TapElement(type, element);
2706 }
2707
PrintICBSizeFromPageSize(const FloatSize & page_size)2708 IntSize WebViewTest::PrintICBSizeFromPageSize(const FloatSize& page_size) {
2709 // The expected layout size comes from the calculation done in
2710 // ResizePageRectsKeepingRatio() which is used from PrintContext::begin() to
2711 // scale the page size.
2712 const float ratio = page_size.Height() / (float)page_size.Width();
2713 const int icb_width =
2714 floor(page_size.Width() * PrintContext::kPrintingMinimumShrinkFactor);
2715 const int icb_height = floor(icb_width * ratio);
2716 return IntSize(icb_width, icb_height);
2717 }
2718
GetExternalDateTimeChooser(WebViewImpl * web_view_impl)2719 ExternalDateTimeChooser* WebViewTest::GetExternalDateTimeChooser(
2720 WebViewImpl* web_view_impl) {
2721 return web_view_impl->GetChromeClient()
2722 .GetExternalDateTimeChooserForTesting();
2723 }
2724
TEST_F(WebViewTest,ClientTapHandling)2725 TEST_F(WebViewTest, ClientTapHandling) {
2726 TapHandlingWebWidgetClient client;
2727 WebView* web_view = web_view_helper_.InitializeAndLoad("about:blank", nullptr,
2728 nullptr, &client);
2729 WebGestureEvent event(WebInputEvent::kGestureTap, WebInputEvent::kNoModifiers,
2730 WebInputEvent::GetStaticTimeStampForTests(),
2731 WebGestureDevice::kTouchscreen);
2732 event.SetPositionInWidget(gfx::PointF(3, 8));
2733 web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
2734 RunPendingTasks();
2735 EXPECT_EQ(3, client.TapX());
2736 EXPECT_EQ(8, client.TapY());
2737 client.Reset();
2738 event.SetType(WebInputEvent::kGestureLongPress);
2739 event.SetPositionInWidget(gfx::PointF(25, 7));
2740 web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
2741 RunPendingTasks();
2742 EXPECT_EQ(25, client.LongpressX());
2743 EXPECT_EQ(7, client.LongpressY());
2744
2745 // Explicitly reset to break dependency on locally scoped client.
2746 web_view_helper_.Reset();
2747 }
2748
TEST_F(WebViewTest,ClientTapHandlingNullWebViewClient)2749 TEST_F(WebViewTest, ClientTapHandlingNullWebViewClient) {
2750 // Note: this test doesn't use WebViewHelper since WebViewHelper creates an
2751 // internal WebViewClient on demand if the supplied WebViewClient is null.
2752 WebViewImpl* web_view = static_cast<WebViewImpl*>(
2753 WebView::Create(nullptr, false,
2754 /*compositing_enabled=*/false, nullptr,
2755 mojo::ScopedInterfaceEndpointHandle()));
2756 frame_test_helpers::TestWebFrameClient web_frame_client;
2757 frame_test_helpers::TestWebWidgetClient web_widget_client;
2758 WebLocalFrame* local_frame = WebLocalFrame::CreateMainFrame(
2759 web_view, &web_frame_client, nullptr, nullptr);
2760 web_frame_client.Bind(local_frame);
2761 blink::WebFrameWidget::CreateForMainFrame(
2762 &web_widget_client, local_frame,
2763 CrossVariantMojoAssociatedRemote<mojom::FrameWidgetHostInterfaceBase>(),
2764 CrossVariantMojoAssociatedReceiver<mojom::FrameWidgetInterfaceBase>(),
2765 CrossVariantMojoAssociatedRemote<mojom::WidgetHostInterfaceBase>(),
2766 CrossVariantMojoAssociatedReceiver<mojom::WidgetInterfaceBase>());
2767
2768 WebGestureEvent event(WebInputEvent::kGestureTap, WebInputEvent::kNoModifiers,
2769 WebInputEvent::GetStaticTimeStampForTests(),
2770 WebGestureDevice::kTouchscreen);
2771 event.SetPositionInWidget(gfx::PointF(3, 8));
2772 EXPECT_EQ(WebInputEventResult::kNotHandled,
2773 web_view->MainFrameWidget()->HandleInputEvent(
2774 WebCoalescedInputEvent(event)));
2775 web_view->Close();
2776 }
2777
TEST_F(WebViewTest,LongPressEmptyDiv)2778 TEST_F(WebViewTest, LongPressEmptyDiv) {
2779 RegisterMockedHttpURLLoad("long_press_empty_div.html");
2780
2781 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
2782 base_url_ + "long_press_empty_div.html");
2783 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(false);
2784 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2785 UpdateAllLifecyclePhases();
2786 RunPendingTasks();
2787
2788 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2789 WebInputEvent::kNoModifiers,
2790 WebInputEvent::GetStaticTimeStampForTests(),
2791 WebGestureDevice::kTouchscreen);
2792 event.SetPositionInWidget(gfx::PointF(250, 150));
2793
2794 EXPECT_EQ(WebInputEventResult::kNotHandled,
2795 web_view->MainFrameWidget()->HandleInputEvent(
2796 WebCoalescedInputEvent(event)));
2797 }
2798
TEST_F(WebViewTest,LongPressEmptyDivAlwaysShow)2799 TEST_F(WebViewTest, LongPressEmptyDivAlwaysShow) {
2800 RegisterMockedHttpURLLoad("long_press_empty_div.html");
2801
2802 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
2803 base_url_ + "long_press_empty_div.html");
2804 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(true);
2805 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2806 UpdateAllLifecyclePhases();
2807 RunPendingTasks();
2808
2809 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2810 WebInputEvent::kNoModifiers,
2811 WebInputEvent::GetStaticTimeStampForTests(),
2812 WebGestureDevice::kTouchscreen);
2813 event.SetPositionInWidget(gfx::PointF(250, 150));
2814
2815 EXPECT_EQ(WebInputEventResult::kHandledSystem,
2816 web_view->MainFrameWidget()->HandleInputEvent(
2817 WebCoalescedInputEvent(event)));
2818 }
2819
TEST_F(WebViewTest,LongPressObject)2820 TEST_F(WebViewTest, LongPressObject) {
2821 RegisterMockedHttpURLLoad("long_press_object.html");
2822
2823 WebViewImpl* web_view =
2824 web_view_helper_.InitializeAndLoad(base_url_ + "long_press_object.html");
2825 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(true);
2826 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2827 UpdateAllLifecyclePhases();
2828 RunPendingTasks();
2829
2830 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2831 WebInputEvent::kNoModifiers,
2832 WebInputEvent::GetStaticTimeStampForTests(),
2833 WebGestureDevice::kTouchscreen);
2834 event.SetPositionInWidget(gfx::PointF(10, 10));
2835
2836 EXPECT_NE(WebInputEventResult::kHandledSystem,
2837 web_view->MainFrameWidget()->HandleInputEvent(
2838 WebCoalescedInputEvent(event)));
2839
2840 auto* element = To<HTMLElement>(static_cast<Node*>(
2841 web_view->MainFrameImpl()->GetDocument().GetElementById("obj")));
2842 EXPECT_FALSE(element->CanStartSelection());
2843 }
2844
TEST_F(WebViewTest,LongPressObjectFallback)2845 TEST_F(WebViewTest, LongPressObjectFallback) {
2846 RegisterMockedHttpURLLoad("long_press_object_fallback.html");
2847
2848 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
2849 base_url_ + "long_press_object_fallback.html");
2850 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(true);
2851 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2852 UpdateAllLifecyclePhases();
2853 RunPendingTasks();
2854
2855 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2856 WebInputEvent::kNoModifiers,
2857 WebInputEvent::GetStaticTimeStampForTests(),
2858 WebGestureDevice::kTouchscreen);
2859 event.SetPositionInWidget(gfx::PointF(10, 10));
2860
2861 EXPECT_EQ(WebInputEventResult::kHandledSystem,
2862 web_view->MainFrameWidget()->HandleInputEvent(
2863 WebCoalescedInputEvent(event)));
2864
2865 auto* element = To<HTMLElement>(static_cast<Node*>(
2866 web_view->MainFrameImpl()->GetDocument().GetElementById("obj")));
2867 EXPECT_TRUE(element->CanStartSelection());
2868 }
2869
TEST_F(WebViewTest,LongPressImage)2870 TEST_F(WebViewTest, LongPressImage) {
2871 RegisterMockedHttpURLLoad("long_press_image.html");
2872
2873 WebViewImpl* web_view =
2874 web_view_helper_.InitializeAndLoad(base_url_ + "long_press_image.html");
2875 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(false);
2876 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2877 UpdateAllLifecyclePhases();
2878 RunPendingTasks();
2879
2880 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2881 WebInputEvent::kNoModifiers,
2882 WebInputEvent::GetStaticTimeStampForTests(),
2883 WebGestureDevice::kTouchscreen);
2884 event.SetPositionInWidget(gfx::PointF(10, 10));
2885
2886 EXPECT_EQ(WebInputEventResult::kHandledSystem,
2887 web_view->MainFrameWidget()->HandleInputEvent(
2888 WebCoalescedInputEvent(event)));
2889 EXPECT_TRUE(
2890 web_view->AsView()
2891 .page->GetContextMenuController()
2892 .ContextMenuNodeForFrame(web_view->MainFrameImpl()->GetFrame()));
2893 }
2894
TEST_F(WebViewTest,LongPressVideo)2895 TEST_F(WebViewTest, LongPressVideo) {
2896 RegisterMockedHttpURLLoad("long_press_video.html");
2897
2898 WebViewImpl* web_view =
2899 web_view_helper_.InitializeAndLoad(base_url_ + "long_press_video.html");
2900 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(false);
2901 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2902 UpdateAllLifecyclePhases();
2903 RunPendingTasks();
2904
2905 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2906 WebInputEvent::kNoModifiers,
2907 WebInputEvent::GetStaticTimeStampForTests(),
2908 WebGestureDevice::kTouchscreen);
2909 event.SetPositionInWidget(gfx::PointF(10, 10));
2910
2911 EXPECT_EQ(WebInputEventResult::kHandledSystem,
2912 web_view->MainFrameWidget()->HandleInputEvent(
2913 WebCoalescedInputEvent(event)));
2914 }
2915
TEST_F(WebViewTest,LongPressLink)2916 TEST_F(WebViewTest, LongPressLink) {
2917 RegisterMockedHttpURLLoad("long_press_link.html");
2918
2919 WebViewImpl* web_view =
2920 web_view_helper_.InitializeAndLoad(base_url_ + "long_press_link.html");
2921 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(false);
2922 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2923 UpdateAllLifecyclePhases();
2924 RunPendingTasks();
2925
2926 WebGestureEvent event(WebInputEvent::kGestureLongPress,
2927 WebInputEvent::kNoModifiers,
2928 WebInputEvent::GetStaticTimeStampForTests(),
2929 WebGestureDevice::kTouchscreen);
2930 event.SetPositionInWidget(gfx::PointF(500, 300));
2931
2932 EXPECT_EQ(WebInputEventResult::kHandledSystem,
2933 web_view->MainFrameWidget()->HandleInputEvent(
2934 WebCoalescedInputEvent(event)));
2935 }
2936
2937 // Tests that we send touchcancel when drag start by long press.
TEST_F(WebViewTest,TouchCancelOnStartDragging)2938 TEST_F(WebViewTest, TouchCancelOnStartDragging) {
2939 RegisterMockedHttpURLLoad("long_press_draggable_div.html");
2940
2941 url_test_helpers::RegisterMockedURLLoad(
2942 ToKURL("http://www.test.com/foo.png"),
2943 test::CoreTestDataPath("white-1x1.png"));
2944 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
2945 base_url_ + "long_press_draggable_div.html");
2946
2947 web_view->SettingsImpl()->SetTouchDragDropEnabled(true);
2948 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2949 UpdateAllLifecyclePhases();
2950 RunPendingTasks();
2951
2952 WebPointerEvent pointer_down(
2953 WebInputEvent::kPointerDown,
2954 WebPointerProperties(1, WebPointerProperties::PointerType::kTouch), 5, 5);
2955 pointer_down.SetPositionInWidget(250, 8);
2956 web_view->MainFrameWidget()->HandleInputEvent(
2957 WebCoalescedInputEvent(pointer_down));
2958 web_view->MainFrameWidget()->DispatchBufferedTouchEvents();
2959
2960 WebString target_id = WebString::FromUTF8("target");
2961
2962 // Send long press to start dragging
2963 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, target_id));
2964 EXPECT_EQ("dragstart", web_view->MainFrameImpl()->GetDocument().Title());
2965
2966 // Check pointer cancel is sent to dom.
2967 WebPointerEvent pointer_cancel(
2968 WebInputEvent::kPointerCancel,
2969 WebPointerProperties(1, WebPointerProperties::PointerType::kTouch), 5, 5);
2970 pointer_cancel.SetPositionInWidget(250, 8);
2971 EXPECT_NE(WebInputEventResult::kHandledSuppressed,
2972 web_view->MainFrameWidget()->HandleInputEvent(
2973 WebCoalescedInputEvent(pointer_cancel)));
2974 web_view->MainFrameWidget()->DispatchBufferedTouchEvents();
2975 EXPECT_EQ("touchcancel", web_view->MainFrameImpl()->GetDocument().Title());
2976 }
2977
TEST_F(WebViewTest,showContextMenuOnLongPressingLinks)2978 TEST_F(WebViewTest, showContextMenuOnLongPressingLinks) {
2979 RegisterMockedHttpURLLoad("long_press_links_and_images.html");
2980
2981 url_test_helpers::RegisterMockedURLLoad(
2982 ToKURL("http://www.test.com/foo.png"),
2983 test::CoreTestDataPath("white-1x1.png"));
2984 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
2985 base_url_ + "long_press_links_and_images.html");
2986
2987 web_view->SettingsImpl()->SetTouchDragDropEnabled(true);
2988 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
2989 UpdateAllLifecyclePhases();
2990 RunPendingTasks();
2991
2992 WebString anchor_tag_id = WebString::FromUTF8("anchorTag");
2993 WebString image_tag_id = WebString::FromUTF8("imageTag");
2994
2995 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, anchor_tag_id));
2996 EXPECT_EQ("anchor contextmenu",
2997 web_view->MainFrameImpl()->GetDocument().Title());
2998
2999 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, image_tag_id));
3000 EXPECT_EQ("image contextmenu",
3001 web_view->MainFrameImpl()->GetDocument().Title());
3002 }
3003
TEST_F(WebViewTest,LongPressEmptyEditableSelection)3004 TEST_F(WebViewTest, LongPressEmptyEditableSelection) {
3005 RegisterMockedHttpURLLoad("long_press_empty_editable_selection.html");
3006
3007 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3008 base_url_ + "long_press_empty_editable_selection.html");
3009 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(false);
3010 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
3011 UpdateAllLifecyclePhases();
3012 RunPendingTasks();
3013
3014 WebGestureEvent event(WebInputEvent::kGestureLongPress,
3015 WebInputEvent::kNoModifiers,
3016 WebInputEvent::GetStaticTimeStampForTests(),
3017 WebGestureDevice::kTouchscreen);
3018 event.SetPositionInWidget(gfx::PointF(10, 10));
3019
3020 EXPECT_EQ(WebInputEventResult::kHandledSystem,
3021 web_view->MainFrameWidget()->HandleInputEvent(
3022 WebCoalescedInputEvent(event)));
3023 }
3024
TEST_F(WebViewTest,LongPressEmptyNonEditableSelection)3025 TEST_F(WebViewTest, LongPressEmptyNonEditableSelection) {
3026 RegisterMockedHttpURLLoad("long_press_image.html");
3027
3028 WebViewImpl* web_view =
3029 web_view_helper_.InitializeAndLoad(base_url_ + "long_press_image.html");
3030 web_view->MainFrameWidget()->Resize(WebSize(500, 500));
3031 UpdateAllLifecyclePhases();
3032 RunPendingTasks();
3033
3034 WebGestureEvent event(WebInputEvent::kGestureLongPress,
3035 WebInputEvent::kNoModifiers,
3036 WebInputEvent::GetStaticTimeStampForTests(),
3037 WebGestureDevice::kTouchscreen);
3038 event.SetPositionInWidget(gfx::PointF(300, 300));
3039 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3040
3041 EXPECT_EQ(WebInputEventResult::kHandledSystem,
3042 web_view->MainFrameWidget()->HandleInputEvent(
3043 WebCoalescedInputEvent(event)));
3044 EXPECT_TRUE(frame->SelectionAsText().IsEmpty());
3045 }
3046
TEST_F(WebViewTest,LongPressSelection)3047 TEST_F(WebViewTest, LongPressSelection) {
3048 RegisterMockedHttpURLLoad("longpress_selection.html");
3049
3050 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3051 base_url_ + "longpress_selection.html");
3052 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
3053 UpdateAllLifecyclePhases();
3054 RunPendingTasks();
3055
3056 WebString target = WebString::FromUTF8("target");
3057 WebString onselectstartfalse = WebString::FromUTF8("onselectstartfalse");
3058 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3059
3060 EXPECT_TRUE(
3061 TapElementById(WebInputEvent::kGestureLongPress, onselectstartfalse));
3062 EXPECT_EQ("", frame->SelectionAsText().Utf8());
3063 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, target));
3064 EXPECT_EQ("testword", frame->SelectionAsText().Utf8());
3065 }
3066
TEST_F(WebViewTest,FinishComposingTextDoesNotDismissHandles)3067 TEST_F(WebViewTest, FinishComposingTextDoesNotDismissHandles) {
3068 RegisterMockedHttpURLLoad("longpress_selection.html");
3069
3070 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3071 base_url_ + "longpress_selection.html");
3072 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
3073 UpdateAllLifecyclePhases();
3074 RunPendingTasks();
3075
3076 WebString target = WebString::FromUTF8("target");
3077 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3078 WebInputMethodController* active_input_method_controller =
3079 frame->FrameWidget()->GetActiveWebInputMethodController();
3080 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureTap, target));
3081 WebVector<WebImeTextSpan> empty_ime_text_spans;
3082 frame->SetEditableSelectionOffsets(8, 8);
3083 EXPECT_TRUE(active_input_method_controller->SetComposition(
3084 "12345", empty_ime_text_spans, WebRange(), 8, 13));
3085 EXPECT_TRUE(frame->GetFrame()->GetInputMethodController().HasComposition());
3086 EXPECT_EQ("", frame->SelectionAsText().Utf8());
3087 EXPECT_FALSE(frame->GetFrame()->Selection().IsHandleVisible());
3088 EXPECT_TRUE(frame->GetFrame()->GetInputMethodController().HasComposition());
3089
3090 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, target));
3091 EXPECT_EQ("testword12345", frame->SelectionAsText().Utf8());
3092 EXPECT_TRUE(frame->GetFrame()->Selection().IsHandleVisible());
3093 EXPECT_TRUE(frame->GetFrame()->GetInputMethodController().HasComposition());
3094
3095 // Check that finishComposingText(KeepSelection) does not dismiss handles.
3096 active_input_method_controller->FinishComposingText(
3097 WebInputMethodController::kKeepSelection);
3098 EXPECT_TRUE(frame->GetFrame()->Selection().IsHandleVisible());
3099 }
3100
3101 #if !defined(OS_MACOSX)
TEST_F(WebViewTest,TouchDoesntSelectEmptyTextarea)3102 TEST_F(WebViewTest, TouchDoesntSelectEmptyTextarea) {
3103 RegisterMockedHttpURLLoad("longpress_textarea.html");
3104
3105 WebViewImpl* web_view =
3106 web_view_helper_.InitializeAndLoad(base_url_ + "longpress_textarea.html");
3107 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
3108 UpdateAllLifecyclePhases();
3109 RunPendingTasks();
3110
3111 WebString blanklinestextbox = WebString::FromUTF8("blanklinestextbox");
3112 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3113
3114 // Long-press on carriage returns.
3115 EXPECT_TRUE(
3116 TapElementById(WebInputEvent::kGestureLongPress, blanklinestextbox));
3117 EXPECT_TRUE(frame->SelectionAsText().IsEmpty());
3118
3119 // Double-tap on carriage returns.
3120 WebGestureEvent event(WebInputEvent::kGestureTap, WebInputEvent::kNoModifiers,
3121 WebInputEvent::GetStaticTimeStampForTests(),
3122 WebGestureDevice::kTouchscreen);
3123 event.SetPositionInWidget(gfx::PointF(100, 25));
3124 event.data.tap.tap_count = 2;
3125
3126 web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
3127 EXPECT_TRUE(frame->SelectionAsText().IsEmpty());
3128
3129 auto* text_area_element = To<HTMLTextAreaElement>(static_cast<Node*>(
3130 web_view->MainFrameImpl()->GetDocument().GetElementById(
3131 blanklinestextbox)));
3132 text_area_element->setValue("hello");
3133
3134 // Long-press past last word of textbox.
3135 EXPECT_TRUE(
3136 TapElementById(WebInputEvent::kGestureLongPress, blanklinestextbox));
3137 EXPECT_TRUE(frame->SelectionAsText().IsEmpty());
3138
3139 // Double-tap past last word of textbox.
3140 web_view->MainFrameWidget()->HandleInputEvent(WebCoalescedInputEvent(event));
3141 EXPECT_TRUE(frame->SelectionAsText().IsEmpty());
3142 }
3143 #endif
3144
TEST_F(WebViewTest,LongPressImageTextarea)3145 TEST_F(WebViewTest, LongPressImageTextarea) {
3146 RegisterMockedHttpURLLoad("longpress_image_contenteditable.html");
3147
3148 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3149 base_url_ + "longpress_image_contenteditable.html");
3150 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
3151 UpdateAllLifecyclePhases();
3152 RunPendingTasks();
3153
3154 WebString image = WebString::FromUTF8("purpleimage");
3155
3156 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, image));
3157 WebRange range = web_view->MainFrameImpl()
3158 ->GetInputMethodController()
3159 ->GetSelectionOffsets();
3160 EXPECT_FALSE(range.IsNull());
3161 EXPECT_EQ(0, range.StartOffset());
3162 EXPECT_EQ(1, range.length());
3163 }
3164
TEST_F(WebViewTest,BlinkCaretAfterLongPress)3165 TEST_F(WebViewTest, BlinkCaretAfterLongPress) {
3166 RegisterMockedHttpURLLoad("blink_caret_on_typing_after_long_press.html");
3167
3168 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3169 base_url_ + "blink_caret_on_typing_after_long_press.html");
3170 web_view->MainFrameWidget()->Resize(WebSize(640, 480));
3171 UpdateAllLifecyclePhases();
3172 RunPendingTasks();
3173
3174 WebString target = WebString::FromUTF8("target");
3175 WebLocalFrameImpl* main_frame = web_view->MainFrameImpl();
3176
3177 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureLongPress, target));
3178 EXPECT_FALSE(main_frame->GetFrame()->Selection().IsCaretBlinkingSuspended());
3179 }
3180
TEST_F(WebViewTest,BlinkCaretOnClosingContextMenu)3181 TEST_F(WebViewTest, BlinkCaretOnClosingContextMenu) {
3182 RegisterMockedHttpURLLoad("form.html");
3183 WebViewImpl* web_view =
3184 web_view_helper_.InitializeAndLoad(base_url_ + "form.html");
3185
3186 web_view->SetInitialFocus(false);
3187 RunPendingTasks();
3188
3189 // We suspend caret blinking when pressing with mouse right button.
3190 // Note that we do not send MouseUp event here since it will be consumed
3191 // by the context menu once it shows up.
3192 WebMouseEvent mouse_event(WebInputEvent::kMouseDown,
3193 WebInputEvent::kNoModifiers,
3194 WebInputEvent::GetStaticTimeStampForTests());
3195
3196 mouse_event.button = WebMouseEvent::Button::kRight;
3197 mouse_event.SetPositionInWidget(1, 1);
3198 mouse_event.click_count = 1;
3199 web_view->MainFrameWidget()->HandleInputEvent(
3200 WebCoalescedInputEvent(mouse_event));
3201 RunPendingTasks();
3202
3203 WebLocalFrameImpl* main_frame = web_view->MainFrameImpl();
3204 EXPECT_TRUE(main_frame->GetFrame()->Selection().IsCaretBlinkingSuspended());
3205
3206 // Caret blinking is still suspended after showing context menu.
3207 web_view->MainFrameWidget()->ShowContextMenu(kMenuSourceMouse);
3208 EXPECT_TRUE(main_frame->GetFrame()->Selection().IsCaretBlinkingSuspended());
3209
3210 // Caret blinking will be resumed only after context menu is closed.
3211 web_view->DidCloseContextMenu();
3212
3213 EXPECT_FALSE(main_frame->GetFrame()->Selection().IsCaretBlinkingSuspended());
3214 }
3215
TEST_F(WebViewTest,SelectionOnReadOnlyInput)3216 TEST_F(WebViewTest, SelectionOnReadOnlyInput) {
3217 RegisterMockedHttpURLLoad("selection_readonly.html");
3218 WebViewImpl* web_view =
3219 web_view_helper_.InitializeAndLoad(base_url_ + "selection_readonly.html");
3220 web_view->MainFrameWidget()->Resize(WebSize(640, 480));
3221 UpdateAllLifecyclePhases();
3222 RunPendingTasks();
3223
3224 std::string test_word = "This text should be selected.";
3225
3226 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3227 EXPECT_EQ(test_word, frame->SelectionAsText().Utf8());
3228
3229 WebRange range = web_view->MainFrameImpl()
3230 ->GetInputMethodController()
3231 ->GetSelectionOffsets();
3232 EXPECT_FALSE(range.IsNull());
3233 EXPECT_EQ(0, range.StartOffset());
3234 EXPECT_EQ(static_cast<int>(test_word.length()), range.length());
3235 }
3236
TEST_F(WebViewTest,KeyDownScrollsHandled)3237 TEST_F(WebViewTest, KeyDownScrollsHandled) {
3238 RegisterMockedHttpURLLoad("content-width-1000.html");
3239
3240 WebViewImpl* web_view =
3241 web_view_helper_.InitializeAndLoad(base_url_ + "content-width-1000.html");
3242 web_view->MainFrameWidget()->Resize(WebSize(100, 100));
3243 UpdateAllLifecyclePhases();
3244 RunPendingTasks();
3245
3246 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
3247 WebInputEvent::kNoModifiers,
3248 WebInputEvent::GetStaticTimeStampForTests());
3249
3250 // RawKeyDown pagedown should be handled.
3251 key_event.windows_key_code = VKEY_NEXT;
3252 EXPECT_EQ(WebInputEventResult::kHandledSystem,
3253 web_view->MainFrameWidget()->HandleInputEvent(
3254 WebCoalescedInputEvent(key_event)));
3255 key_event.SetType(WebInputEvent::kKeyUp);
3256 web_view->MainFrameWidget()->HandleInputEvent(
3257 WebCoalescedInputEvent(key_event));
3258
3259 // Coalesced KeyDown arrow-down should be handled.
3260 key_event.windows_key_code = VKEY_DOWN;
3261 key_event.SetType(WebInputEvent::kKeyDown);
3262 EXPECT_EQ(WebInputEventResult::kHandledSystem,
3263 web_view->MainFrameWidget()->HandleInputEvent(
3264 WebCoalescedInputEvent(key_event)));
3265 key_event.SetType(WebInputEvent::kKeyUp);
3266 web_view->MainFrameWidget()->HandleInputEvent(
3267 WebCoalescedInputEvent(key_event));
3268
3269 // Ctrl-Home should be handled...
3270 key_event.windows_key_code = VKEY_HOME;
3271 key_event.SetModifiers(WebInputEvent::kControlKey);
3272 key_event.SetType(WebInputEvent::kRawKeyDown);
3273 EXPECT_EQ(WebInputEventResult::kNotHandled,
3274 web_view->MainFrameWidget()->HandleInputEvent(
3275 WebCoalescedInputEvent(key_event)));
3276 key_event.SetType(WebInputEvent::kKeyUp);
3277 web_view->MainFrameWidget()->HandleInputEvent(
3278 WebCoalescedInputEvent(key_event));
3279
3280 // But Ctrl-Down should not.
3281 key_event.windows_key_code = VKEY_DOWN;
3282 key_event.SetModifiers(WebInputEvent::kControlKey);
3283 key_event.SetType(WebInputEvent::kRawKeyDown);
3284 EXPECT_EQ(WebInputEventResult::kNotHandled,
3285 web_view->MainFrameWidget()->HandleInputEvent(
3286 WebCoalescedInputEvent(key_event)));
3287 key_event.SetType(WebInputEvent::kKeyUp);
3288 web_view->MainFrameWidget()->HandleInputEvent(
3289 WebCoalescedInputEvent(key_event));
3290
3291 // Shift, meta, and alt should not be handled.
3292 key_event.windows_key_code = VKEY_NEXT;
3293 key_event.SetModifiers(WebInputEvent::kShiftKey);
3294 key_event.SetType(WebInputEvent::kRawKeyDown);
3295 EXPECT_EQ(WebInputEventResult::kNotHandled,
3296 web_view->MainFrameWidget()->HandleInputEvent(
3297 WebCoalescedInputEvent(key_event)));
3298 key_event.SetType(WebInputEvent::kKeyUp);
3299 web_view->MainFrameWidget()->HandleInputEvent(
3300 WebCoalescedInputEvent(key_event));
3301
3302 key_event.windows_key_code = VKEY_NEXT;
3303 key_event.SetModifiers(WebInputEvent::kMetaKey);
3304 key_event.SetType(WebInputEvent::kRawKeyDown);
3305 EXPECT_EQ(WebInputEventResult::kNotHandled,
3306 web_view->MainFrameWidget()->HandleInputEvent(
3307 WebCoalescedInputEvent(key_event)));
3308 key_event.SetType(WebInputEvent::kKeyUp);
3309 web_view->MainFrameWidget()->HandleInputEvent(
3310 WebCoalescedInputEvent(key_event));
3311
3312 key_event.windows_key_code = VKEY_NEXT;
3313 key_event.SetModifiers(WebInputEvent::kAltKey);
3314 key_event.SetType(WebInputEvent::kRawKeyDown);
3315 EXPECT_EQ(WebInputEventResult::kNotHandled,
3316 web_view->MainFrameWidget()->HandleInputEvent(
3317 WebCoalescedInputEvent(key_event)));
3318 key_event.SetType(WebInputEvent::kKeyUp);
3319 web_view->MainFrameWidget()->HandleInputEvent(
3320 WebCoalescedInputEvent(key_event));
3321
3322 // System-key labeled Alt-Down (as in Windows) should do nothing,
3323 // but non-system-key labeled Alt-Down (as in Mac) should be handled
3324 // as a page-down.
3325 key_event.windows_key_code = VKEY_DOWN;
3326 key_event.SetModifiers(WebInputEvent::kAltKey);
3327 key_event.is_system_key = true;
3328 key_event.SetType(WebInputEvent::kRawKeyDown);
3329 EXPECT_EQ(WebInputEventResult::kNotHandled,
3330 web_view->MainFrameWidget()->HandleInputEvent(
3331 WebCoalescedInputEvent(key_event)));
3332 key_event.SetType(WebInputEvent::kKeyUp);
3333 web_view->MainFrameWidget()->HandleInputEvent(
3334 WebCoalescedInputEvent(key_event));
3335
3336 key_event.windows_key_code = VKEY_DOWN;
3337 key_event.SetModifiers(WebInputEvent::kAltKey);
3338 key_event.is_system_key = false;
3339 key_event.SetType(WebInputEvent::kRawKeyDown);
3340 EXPECT_EQ(WebInputEventResult::kHandledSystem,
3341 web_view->MainFrameWidget()->HandleInputEvent(
3342 WebCoalescedInputEvent(key_event)));
3343 key_event.SetType(WebInputEvent::kKeyUp);
3344 web_view->MainFrameWidget()->HandleInputEvent(
3345 WebCoalescedInputEvent(key_event));
3346 }
3347
3348 class MiddleClickAutoscrollWebWidgetClient
3349 : public frame_test_helpers::TestWebWidgetClient {
3350 public:
3351 // WebWidgetClient methods
3352
DidChangeCursor(const ui::Cursor & cursor)3353 void DidChangeCursor(const ui::Cursor& cursor) override {
3354 last_cursor_type_ = cursor.type();
3355 }
3356
GetLastCursorType() const3357 ui::mojom::blink::CursorType GetLastCursorType() const {
3358 return last_cursor_type_;
3359 }
3360
3361 private:
3362 ui::mojom::blink::CursorType last_cursor_type_ =
3363 ui::mojom::blink::CursorType::kPointer;
3364 };
3365
TEST_F(WebViewTest,MiddleClickAutoscrollCursor)3366 TEST_F(WebViewTest, MiddleClickAutoscrollCursor) {
3367 MiddleClickAutoscrollWebWidgetClient client;
3368 ScopedMiddleClickAutoscrollForTest middle_click_autoscroll(true);
3369 RegisterMockedHttpURLLoad("content-width-1000.html");
3370
3371 // We will be changing the size of the page to test each of the panning
3372 // cursor variations. For reference, content-width-1000.html is 1000px wide
3373 // and 2000px tall.
3374 // 1. 100 x 100 - The page will be scrollable in both x and y directions, so
3375 // we expect to see the cursor with arrows in all four directions.
3376 // 2. 1010 x 100 - The page will be scrollable in the y direction, but not x,
3377 // so we expect to see the cursor with only the vertical arrows.
3378 // 3. 100 x 2010 - The page will be scrollable in the x direction, but not y,
3379 // so we expect to see the cursor with only the horizontal arrows.
3380 struct CursorTests {
3381 int resize_width;
3382 int resize_height;
3383 ui::mojom::blink::CursorType expected_cursor;
3384 } cursor_tests[] = {{100, 100, MiddlePanningCursor().type()},
3385 {1010, 100, MiddlePanningVerticalCursor().type()},
3386 {100, 2010, MiddlePanningHorizontalCursor().type()}};
3387
3388 for (const CursorTests current_test : cursor_tests) {
3389 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3390 base_url_ + "content-width-1000.html", nullptr, nullptr, &client);
3391 web_view->MainFrameWidget()->Resize(
3392 WebSize(current_test.resize_width, current_test.resize_height));
3393 UpdateAllLifecyclePhases();
3394 RunPendingTasks();
3395
3396 LocalFrame* local_frame =
3397 To<WebLocalFrameImpl>(web_view->MainFrame())->GetFrame();
3398
3399 // Setup a mock clipboard. On linux, middle click can paste from the
3400 // clipboard, so the input handler below will access the clipboard.
3401 PageTestBase::MockClipboardHostProvider mock_clip_host_provider(
3402 local_frame->GetBrowserInterfaceBroker());
3403
3404 WebMouseEvent mouse_event(WebInputEvent::kMouseDown,
3405 WebInputEvent::kNoModifiers,
3406 WebInputEvent::GetStaticTimeStampForTests());
3407 mouse_event.button = WebMouseEvent::Button::kMiddle;
3408 mouse_event.SetPositionInWidget(1, 1);
3409 mouse_event.click_count = 1;
3410
3411 // Start middle-click autoscroll.
3412 web_view->MainFrameWidget()->HandleInputEvent(
3413 WebCoalescedInputEvent(mouse_event));
3414 mouse_event.SetType(WebInputEvent::kMouseUp);
3415 web_view->MainFrameWidget()->HandleInputEvent(
3416 WebCoalescedInputEvent(mouse_event));
3417
3418 EXPECT_EQ(current_test.expected_cursor, client.GetLastCursorType());
3419
3420 // Even if a plugin tries to change the cursor type, that should be ignored
3421 // during middle-click autoscroll.
3422 web_view->GetChromeClient().SetCursorForPlugin(PointerCursor(),
3423 local_frame);
3424 EXPECT_EQ(current_test.expected_cursor, client.GetLastCursorType());
3425
3426 // End middle-click autoscroll.
3427 mouse_event.SetType(WebInputEvent::kMouseDown);
3428 web_view->MainFrameWidget()->HandleInputEvent(
3429 WebCoalescedInputEvent(mouse_event));
3430 mouse_event.SetType(WebInputEvent::kMouseUp);
3431 web_view->MainFrameWidget()->HandleInputEvent(
3432 WebCoalescedInputEvent(mouse_event));
3433
3434 web_view->GetChromeClient().SetCursorForPlugin(IBeamCursor(), local_frame);
3435 EXPECT_EQ(IBeamCursor().type(), client.GetLastCursorType());
3436 }
3437
3438 // Explicitly reset to break dependency on locally scoped client.
3439 web_view_helper_.Reset();
3440 }
3441
ConfigueCompositingWebView(WebSettings * settings)3442 static void ConfigueCompositingWebView(WebSettings* settings) {
3443 settings->SetPreferCompositingToLCDTextEnabled(true);
3444 }
3445
TEST_F(WebViewTest,ShowPressOnTransformedLink)3446 TEST_F(WebViewTest, ShowPressOnTransformedLink) {
3447 frame_test_helpers::WebViewHelper web_view_helper;
3448 WebViewImpl* web_view_impl =
3449 web_view_helper.InitializeWithSettings(&ConfigueCompositingWebView);
3450
3451 int page_width = 640;
3452 int page_height = 480;
3453 web_view_impl->MainFrameWidget()->Resize(WebSize(page_width, page_height));
3454
3455 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
3456 frame_test_helpers::LoadHTMLString(
3457 web_view_impl->MainFrameImpl(),
3458 "<a href='http://www.test.com' style='position: absolute; left: 20px; "
3459 "top: 20px; width: 200px; transform:translateZ(0);'>A link to "
3460 "highlight</a>",
3461 base_url);
3462
3463 WebGestureEvent event(WebInputEvent::kGestureShowPress,
3464 WebInputEvent::kNoModifiers,
3465 WebInputEvent::GetStaticTimeStampForTests(),
3466 WebGestureDevice::kTouchscreen);
3467 event.SetPositionInWidget(gfx::PointF(20, 20));
3468
3469 // Just make sure we don't hit any asserts.
3470 web_view_impl->MainFrameWidget()->HandleInputEvent(
3471 WebCoalescedInputEvent(event));
3472 }
3473
3474 class MockAutofillClient : public WebAutofillClient {
3475 public:
3476 MockAutofillClient() = default;
3477
3478 ~MockAutofillClient() override = default;
3479
TextFieldDidChange(const WebFormControlElement &)3480 void TextFieldDidChange(const WebFormControlElement&) override {
3481 ++text_changes_;
3482 }
UserGestureObserved()3483 void UserGestureObserved() override { ++user_gesture_notifications_count_; }
3484
ShouldSuppressKeyboard(const WebFormControlElement &)3485 bool ShouldSuppressKeyboard(const WebFormControlElement&) override {
3486 return should_suppress_keyboard_;
3487 }
3488
SetShouldSuppressKeyboard(bool should_suppress_keyboard)3489 void SetShouldSuppressKeyboard(bool should_suppress_keyboard) {
3490 should_suppress_keyboard_ = should_suppress_keyboard;
3491 }
3492
ClearChangeCounts()3493 void ClearChangeCounts() { text_changes_ = 0; }
3494
TextChanges()3495 int TextChanges() { return text_changes_; }
GetUserGestureNotificationsCount()3496 int GetUserGestureNotificationsCount() {
3497 return user_gesture_notifications_count_;
3498 }
3499
3500 private:
3501 int text_changes_ = 0;
3502 int user_gesture_notifications_count_ = 0;
3503 bool should_suppress_keyboard_ = false;
3504 };
3505
TEST_F(WebViewTest,LosingFocusDoesNotTriggerAutofillTextChange)3506 TEST_F(WebViewTest, LosingFocusDoesNotTriggerAutofillTextChange) {
3507 RegisterMockedHttpURLLoad("input_field_populated.html");
3508 MockAutofillClient client;
3509 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3510 base_url_ + "input_field_populated.html");
3511 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3512 frame->SetAutofillClient(&client);
3513 web_view->SetInitialFocus(false);
3514
3515 // Set up a composition that needs to be committed.
3516 WebVector<WebImeTextSpan> empty_ime_text_spans;
3517 frame->SetEditableSelectionOffsets(4, 10);
3518 frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
3519 WebTextInputInfo info = frame->GetInputMethodController()->TextInputInfo();
3520 EXPECT_EQ(4, info.selection_start);
3521 EXPECT_EQ(10, info.selection_end);
3522 EXPECT_EQ(8, info.composition_start);
3523 EXPECT_EQ(12, info.composition_end);
3524
3525 // Clear the focus and track that the subsequent composition commit does not
3526 // trigger a text changed notification for autofill.
3527 client.ClearChangeCounts();
3528 web_view->MainFrameWidget()->SetFocus(false);
3529 EXPECT_EQ(0, client.TextChanges());
3530
3531 frame->SetAutofillClient(nullptr);
3532 }
3533
VerifySelectionAndComposition(WebViewImpl * web_view,int selection_start,int selection_end,int composition_start,int composition_end,const char * fail_message)3534 static void VerifySelectionAndComposition(WebViewImpl* web_view,
3535 int selection_start,
3536 int selection_end,
3537 int composition_start,
3538 int composition_end,
3539 const char* fail_message) {
3540 WebTextInputInfo info =
3541 web_view->MainFrameImpl()->GetInputMethodController()->TextInputInfo();
3542 EXPECT_EQ(selection_start, info.selection_start) << fail_message;
3543 EXPECT_EQ(selection_end, info.selection_end) << fail_message;
3544 EXPECT_EQ(composition_start, info.composition_start) << fail_message;
3545 EXPECT_EQ(composition_end, info.composition_end) << fail_message;
3546 }
3547
TEST_F(WebViewTest,CompositionNotCancelledByBackspace)3548 TEST_F(WebViewTest, CompositionNotCancelledByBackspace) {
3549 RegisterMockedHttpURLLoad("composition_not_cancelled_by_backspace.html");
3550 MockAutofillClient client;
3551 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3552 base_url_ + "composition_not_cancelled_by_backspace.html");
3553 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3554 frame->SetAutofillClient(&client);
3555 web_view->SetInitialFocus(false);
3556
3557 // Test both input elements.
3558 for (int i = 0; i < 2; ++i) {
3559 // Select composition and do sanity check.
3560 WebVector<WebImeTextSpan> empty_ime_text_spans;
3561 frame->SetEditableSelectionOffsets(6, 6);
3562 WebInputMethodController* active_input_method_controller =
3563 frame->FrameWidget()->GetActiveWebInputMethodController();
3564 EXPECT_TRUE(active_input_method_controller->SetComposition(
3565 "fghij", empty_ime_text_spans, WebRange(), 0, 5));
3566 frame->SetEditableSelectionOffsets(11, 11);
3567 VerifySelectionAndComposition(web_view, 11, 11, 6, 11, "initial case");
3568
3569 // Press Backspace and verify composition didn't get cancelled. This is to
3570 // verify the fix for crbug.com/429916.
3571 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
3572 WebInputEvent::kNoModifiers,
3573 WebInputEvent::GetStaticTimeStampForTests());
3574 key_event.dom_key = ui::DomKey::BACKSPACE;
3575 key_event.windows_key_code = VKEY_BACK;
3576 web_view->MainFrameWidget()->HandleInputEvent(
3577 WebCoalescedInputEvent(key_event));
3578
3579 frame->SetEditableSelectionOffsets(6, 6);
3580 EXPECT_TRUE(active_input_method_controller->SetComposition(
3581 "fghi", empty_ime_text_spans, WebRange(), 0, 4));
3582 frame->SetEditableSelectionOffsets(10, 10);
3583 VerifySelectionAndComposition(web_view, 10, 10, 6, 10,
3584 "after pressing Backspace");
3585
3586 key_event.SetType(WebInputEvent::kKeyUp);
3587 web_view->MainFrameWidget()->HandleInputEvent(
3588 WebCoalescedInputEvent(key_event));
3589
3590 web_view->AdvanceFocus(false);
3591 }
3592
3593 frame->SetAutofillClient(nullptr);
3594 }
3595
TEST_F(WebViewTest,FinishComposingTextDoesntTriggerAutofillTextChange)3596 TEST_F(WebViewTest, FinishComposingTextDoesntTriggerAutofillTextChange) {
3597 RegisterMockedHttpURLLoad("input_field_populated.html");
3598 MockAutofillClient client;
3599 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3600 base_url_ + "input_field_populated.html");
3601 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3602 frame->SetAutofillClient(&client);
3603 web_view->SetInitialFocus(false);
3604
3605 WebDocument document = web_view->MainFrameImpl()->GetDocument();
3606 auto* form = To<HTMLFormControlElement>(
3607 static_cast<Element*>(document.GetElementById("sample")));
3608
3609 WebInputMethodController* active_input_method_controller =
3610 frame->FrameWidget()->GetActiveWebInputMethodController();
3611 // Set up a composition that needs to be committed.
3612 std::string composition_text("testingtext");
3613
3614 WebVector<WebImeTextSpan> empty_ime_text_spans;
3615 active_input_method_controller->SetComposition(
3616 WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
3617 WebRange(), 0, composition_text.length());
3618
3619 WebTextInputInfo info = active_input_method_controller->TextInputInfo();
3620 EXPECT_EQ(0, info.selection_start);
3621 EXPECT_EQ((int)composition_text.length(), info.selection_end);
3622 EXPECT_EQ(0, info.composition_start);
3623 EXPECT_EQ((int)composition_text.length(), info.composition_end);
3624
3625 form->SetAutofillState(blink::WebAutofillState::kAutofilled);
3626 client.ClearChangeCounts();
3627
3628 active_input_method_controller->FinishComposingText(
3629 WebInputMethodController::kKeepSelection);
3630 EXPECT_EQ(0, client.TextChanges());
3631
3632 EXPECT_TRUE(form->IsAutofilled());
3633
3634 frame->SetAutofillClient(nullptr);
3635 }
3636
TEST_F(WebViewTest,SetCompositionFromExistingTextDoesntTriggerAutofillTextChange)3637 TEST_F(WebViewTest,
3638 SetCompositionFromExistingTextDoesntTriggerAutofillTextChange) {
3639 RegisterMockedHttpURLLoad("input_field_populated.html");
3640 MockAutofillClient client;
3641 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3642 base_url_ + "input_field_populated.html");
3643 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
3644 frame->SetAutofillClient(&client);
3645 web_view->SetInitialFocus(false);
3646
3647 WebVector<WebImeTextSpan> empty_ime_text_spans;
3648
3649 client.ClearChangeCounts();
3650 frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
3651
3652 WebTextInputInfo info = frame->GetInputMethodController()->TextInputInfo();
3653 EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value.Utf8());
3654 EXPECT_EQ(8, info.composition_start);
3655 EXPECT_EQ(12, info.composition_end);
3656
3657 EXPECT_EQ(0, client.TextChanges());
3658
3659 WebDocument document = web_view->MainFrameImpl()->GetDocument();
3660 EXPECT_EQ(WebString::FromUTF8("none"),
3661 document.GetElementById("inputEvent").FirstChild().NodeValue());
3662
3663 frame->SetAutofillClient(nullptr);
3664 }
3665
3666 class ViewCreatingWebViewClient : public frame_test_helpers::TestWebViewClient {
3667 public:
ViewCreatingWebViewClient()3668 ViewCreatingWebViewClient() : did_focus_called_(false) {}
3669
3670 // WebViewClient methods
CreateView(WebLocalFrame * opener,const WebURLRequest &,const WebWindowFeatures &,const WebString & name,WebNavigationPolicy,mojom::blink::WebSandboxFlags,const FeaturePolicy::FeatureState &,const SessionStorageNamespaceId &)3671 WebView* CreateView(WebLocalFrame* opener,
3672 const WebURLRequest&,
3673 const WebWindowFeatures&,
3674 const WebString& name,
3675 WebNavigationPolicy,
3676 mojom::blink::WebSandboxFlags,
3677 const FeaturePolicy::FeatureState&,
3678 const SessionStorageNamespaceId&) override {
3679 return web_view_helper_.InitializeWithOpener(opener);
3680 }
3681
3682 // WebWidgetClient methods
DidFocus(WebLocalFrame *)3683 void DidFocus(WebLocalFrame*) override { did_focus_called_ = true; }
3684
DidFocusCalled() const3685 bool DidFocusCalled() const { return did_focus_called_; }
CreatedWebView() const3686 WebView* CreatedWebView() const { return web_view_helper_.GetWebView(); }
3687
3688 private:
3689 frame_test_helpers::WebViewHelper web_view_helper_;
3690 bool did_focus_called_;
3691 };
3692
TEST_F(WebViewTest,DoNotFocusCurrentFrameOnNavigateFromLocalFrame)3693 TEST_F(WebViewTest, DoNotFocusCurrentFrameOnNavigateFromLocalFrame) {
3694 ViewCreatingWebViewClient client;
3695 frame_test_helpers::WebViewHelper web_view_helper;
3696 WebViewImpl* web_view_impl = web_view_helper.Initialize(nullptr, &client);
3697
3698 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
3699 frame_test_helpers::LoadHTMLString(
3700 web_view_impl->MainFrameImpl(),
3701 "<html><body><iframe src=\"about:blank\"></iframe></body></html>",
3702 base_url);
3703
3704 // Make a request from a local frame.
3705 WebURLRequest web_url_request_with_target_start(KURL("about:blank"));
3706 LocalFrame* local_frame =
3707 To<WebLocalFrameImpl>(web_view_impl->MainFrame()->FirstChild())
3708 ->GetFrame();
3709 FrameLoadRequest request_with_target_start(
3710 local_frame->GetDocument(),
3711 web_url_request_with_target_start.ToResourceRequest());
3712 local_frame->Tree().FindOrCreateFrameForNavigation(request_with_target_start,
3713 "_top");
3714 EXPECT_FALSE(client.DidFocusCalled());
3715
3716 web_view_helper.Reset(); // Remove dependency on locally scoped client.
3717 }
3718
TEST_F(WebViewTest,FocusExistingFrameOnNavigate)3719 TEST_F(WebViewTest, FocusExistingFrameOnNavigate) {
3720 ViewCreatingWebViewClient client;
3721 frame_test_helpers::WebViewHelper web_view_helper;
3722 WebViewImpl* web_view_impl = web_view_helper.Initialize(nullptr, &client);
3723 WebLocalFrameImpl* frame = web_view_impl->MainFrameImpl();
3724 frame->SetName("_start");
3725
3726 // Make a request that will open a new window
3727 WebURLRequest web_url_request(KURL("about:blank"));
3728 FrameLoadRequest request(nullptr, web_url_request.ToResourceRequest());
3729 To<LocalFrame>(web_view_impl->GetPage()->MainFrame())
3730 ->Tree()
3731 .FindOrCreateFrameForNavigation(request, "_blank");
3732 ASSERT_TRUE(client.CreatedWebView());
3733 EXPECT_FALSE(client.DidFocusCalled());
3734
3735 // Make a request from the new window that will navigate the original window.
3736 // The original window should be focused.
3737 WebURLRequest web_url_request_with_target_start(KURL("about:blank"));
3738 FrameLoadRequest request_with_target_start(
3739 nullptr, web_url_request_with_target_start.ToResourceRequest());
3740 To<LocalFrame>(static_cast<WebViewImpl*>(client.CreatedWebView())
3741 ->GetPage()
3742 ->MainFrame())
3743 ->Tree()
3744 .FindOrCreateFrameForNavigation(request_with_target_start, "_start");
3745 EXPECT_TRUE(client.DidFocusCalled());
3746
3747 web_view_helper.Reset(); // Remove dependency on locally scoped client.
3748 }
3749
3750 class ViewReusingWebViewClient : public frame_test_helpers::TestWebViewClient {
3751 public:
3752 ViewReusingWebViewClient() = default;
3753
3754 // WebViewClient methods
CreateView(WebLocalFrame *,const WebURLRequest &,const WebWindowFeatures &,const WebString & name,WebNavigationPolicy,mojom::blink::WebSandboxFlags,const FeaturePolicy::FeatureState &,const SessionStorageNamespaceId &)3755 WebView* CreateView(WebLocalFrame*,
3756 const WebURLRequest&,
3757 const WebWindowFeatures&,
3758 const WebString& name,
3759 WebNavigationPolicy,
3760 mojom::blink::WebSandboxFlags,
3761 const FeaturePolicy::FeatureState&,
3762 const SessionStorageNamespaceId&) override {
3763 return web_view_;
3764 }
3765
SetWebView(WebView * view)3766 void SetWebView(WebView* view) { web_view_ = view; }
3767
3768 private:
3769 WebView* web_view_ = nullptr;
3770 };
3771
TEST_F(WebViewTest,ReuseExistingWindowOnCreateViewUsesCorrectNavigationPolicy)3772 TEST_F(WebViewTest,
3773 ReuseExistingWindowOnCreateViewUsesCorrectNavigationPolicy) {
3774 ViewReusingWebViewClient view_client;
3775 frame_test_helpers::WebViewHelper web_view_helper;
3776 WebViewImpl* web_view_impl =
3777 web_view_helper.Initialize(nullptr, &view_client);
3778 view_client.SetWebView(web_view_impl);
3779 LocalFrame* frame = To<LocalFrame>(web_view_impl->GetPage()->MainFrame());
3780
3781 // Request a new window, but the WebViewClient will decline to and instead
3782 // return the current window.
3783 WebURLRequest web_url_request(KURL("about:blank"));
3784 FrameLoadRequest request(frame->GetDocument(),
3785 web_url_request.ToResourceRequest());
3786 FrameTree::FindResult result =
3787 frame->Tree().FindOrCreateFrameForNavigation(request, "_blank");
3788 EXPECT_EQ(frame, result.frame);
3789 EXPECT_EQ(kNavigationPolicyCurrentTab, request.GetNavigationPolicy());
3790 }
3791
TEST_F(WebViewTest,DispatchesFocusOutFocusInOnViewToggleFocus)3792 TEST_F(WebViewTest, DispatchesFocusOutFocusInOnViewToggleFocus) {
3793 RegisterMockedHttpURLLoad("focusout_focusin_events.html");
3794 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3795 base_url_ + "focusout_focusin_events.html");
3796
3797 web_view->MainFrameWidget()->SetFocus(true);
3798 web_view->MainFrameWidget()->SetFocus(false);
3799 web_view->MainFrameWidget()->SetFocus(true);
3800
3801 WebElement element =
3802 web_view->MainFrameImpl()->GetDocument().GetElementById("message");
3803 EXPECT_EQ("focusoutfocusin", element.TextContent());
3804 }
3805
TEST_F(WebViewTest,DispatchesDomFocusOutDomFocusInOnViewToggleFocus)3806 TEST_F(WebViewTest, DispatchesDomFocusOutDomFocusInOnViewToggleFocus) {
3807 RegisterMockedHttpURLLoad("domfocusout_domfocusin_events.html");
3808 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
3809 base_url_ + "domfocusout_domfocusin_events.html");
3810
3811 web_view->MainFrameWidget()->SetFocus(true);
3812 web_view->MainFrameWidget()->SetFocus(false);
3813 web_view->MainFrameWidget()->SetFocus(true);
3814
3815 WebElement element =
3816 web_view->MainFrameImpl()->GetDocument().GetElementById("message");
3817 EXPECT_EQ("DOMFocusOutDOMFocusIn", element.TextContent());
3818 }
3819
OpenDateTimeChooser(WebView * web_view,HTMLInputElement * input_element)3820 static void OpenDateTimeChooser(WebView* web_view,
3821 HTMLInputElement* input_element) {
3822 input_element->focus();
3823
3824 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
3825 WebInputEvent::kNoModifiers,
3826 WebInputEvent::GetStaticTimeStampForTests());
3827 key_event.dom_key = ui::DomKey::FromCharacter(' ');
3828 key_event.windows_key_code = VKEY_SPACE;
3829 web_view->MainFrameWidget()->HandleInputEvent(
3830 WebCoalescedInputEvent(key_event));
3831
3832 key_event.SetType(WebInputEvent::kKeyUp);
3833 web_view->MainFrameWidget()->HandleInputEvent(
3834 WebCoalescedInputEvent(key_event));
3835 }
3836
TEST_F(WebViewTest,ChooseValueFromDateTimeChooser)3837 TEST_F(WebViewTest, ChooseValueFromDateTimeChooser) {
3838 ScopedInputMultipleFieldsUIForTest input_multiple_fields_ui(false);
3839 std::string url = RegisterMockedHttpURLLoad("date_time_chooser.html");
3840 WebViewImpl* web_view_impl =
3841 web_view_helper_.InitializeAndLoad(url, nullptr, nullptr);
3842
3843 Document* document =
3844 web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
3845
3846 auto* input_element = To<HTMLInputElement>(document->getElementById("date"));
3847 OpenDateTimeChooser(web_view_impl, input_element);
3848 GetExternalDateTimeChooser(web_view_impl)->ResponseHandler(true, 0);
3849 EXPECT_EQ("1970-01-01", input_element->value());
3850
3851 OpenDateTimeChooser(web_view_impl, input_element);
3852 GetExternalDateTimeChooser(web_view_impl)
3853 ->ResponseHandler(true, std::numeric_limits<double>::quiet_NaN());
3854 EXPECT_EQ("", input_element->value());
3855
3856 input_element =
3857 To<HTMLInputElement>(document->getElementById("datetimelocal"));
3858 OpenDateTimeChooser(web_view_impl, input_element);
3859 GetExternalDateTimeChooser(web_view_impl)->ResponseHandler(true, 0);
3860 EXPECT_EQ("1970-01-01T00:00", input_element->value());
3861
3862 OpenDateTimeChooser(web_view_impl, input_element);
3863 GetExternalDateTimeChooser(web_view_impl)
3864 ->ResponseHandler(true, std::numeric_limits<double>::quiet_NaN());
3865 EXPECT_EQ("", input_element->value());
3866
3867 input_element = To<HTMLInputElement>(document->getElementById("month"));
3868 OpenDateTimeChooser(web_view_impl, input_element);
3869 GetExternalDateTimeChooser(web_view_impl)->ResponseHandler(true, 0);
3870 EXPECT_EQ("1970-01", input_element->value());
3871
3872 OpenDateTimeChooser(web_view_impl, input_element);
3873 GetExternalDateTimeChooser(web_view_impl)
3874 ->ResponseHandler(true, std::numeric_limits<double>::quiet_NaN());
3875 EXPECT_EQ("", input_element->value());
3876
3877 input_element = To<HTMLInputElement>(document->getElementById("time"));
3878 OpenDateTimeChooser(web_view_impl, input_element);
3879 GetExternalDateTimeChooser(web_view_impl)->ResponseHandler(true, 0);
3880 EXPECT_EQ("00:00", input_element->value());
3881
3882 OpenDateTimeChooser(web_view_impl, input_element);
3883 GetExternalDateTimeChooser(web_view_impl)
3884 ->ResponseHandler(true, std::numeric_limits<double>::quiet_NaN());
3885 EXPECT_EQ("", input_element->value());
3886
3887 input_element = To<HTMLInputElement>(document->getElementById("week"));
3888 OpenDateTimeChooser(web_view_impl, input_element);
3889 GetExternalDateTimeChooser(web_view_impl)->ResponseHandler(true, 0);
3890 EXPECT_EQ("1970-W01", input_element->value());
3891
3892 OpenDateTimeChooser(web_view_impl, input_element);
3893 GetExternalDateTimeChooser(web_view_impl)
3894 ->ResponseHandler(true, std::numeric_limits<double>::quiet_NaN());
3895 EXPECT_EQ("", input_element->value());
3896
3897 // Clear the WebViewClient from the webViewHelper to avoid use-after-free in
3898 // the WebViewHelper destructor.
3899 web_view_helper_.Reset();
3900 }
3901
TEST_F(WebViewTest,DispatchesFocusBlurOnViewToggle)3902 TEST_F(WebViewTest, DispatchesFocusBlurOnViewToggle) {
3903 RegisterMockedHttpURLLoad("focus_blur_events.html");
3904 WebViewImpl* web_view =
3905 web_view_helper_.InitializeAndLoad(base_url_ + "focus_blur_events.html");
3906
3907 web_view->MainFrameWidget()->SetFocus(true);
3908 web_view->MainFrameWidget()->SetFocus(false);
3909 web_view->MainFrameWidget()->SetFocus(true);
3910
3911 WebElement element =
3912 web_view->MainFrameImpl()->GetDocument().GetElementById("message");
3913 // Expect not to see duplication of events.
3914 EXPECT_EQ("blurfocus", element.TextContent());
3915 }
3916
3917 class CreateChildCounterFrameClient
3918 : public frame_test_helpers::TestWebFrameClient {
3919 public:
CreateChildCounterFrameClient()3920 CreateChildCounterFrameClient() : count_(0) {}
3921 WebLocalFrame* CreateChildFrame(WebLocalFrame* parent,
3922 WebTreeScopeType,
3923 const WebString& name,
3924 const WebString& fallback_name,
3925 const FramePolicy&,
3926 const WebFrameOwnerProperties&,
3927 FrameOwnerElementType) override;
3928
Count() const3929 int Count() const { return count_; }
3930
3931 private:
3932 int count_;
3933 };
3934
CreateChildFrame(WebLocalFrame * parent,WebTreeScopeType scope,const WebString & name,const WebString & fallback_name,const FramePolicy & frame_policy,const WebFrameOwnerProperties & frame_owner_properties,FrameOwnerElementType frame_owner_element_type)3935 WebLocalFrame* CreateChildCounterFrameClient::CreateChildFrame(
3936 WebLocalFrame* parent,
3937 WebTreeScopeType scope,
3938 const WebString& name,
3939 const WebString& fallback_name,
3940 const FramePolicy& frame_policy,
3941 const WebFrameOwnerProperties& frame_owner_properties,
3942 FrameOwnerElementType frame_owner_element_type) {
3943 ++count_;
3944 return TestWebFrameClient::CreateChildFrame(
3945 parent, scope, name, fallback_name, frame_policy, frame_owner_properties,
3946 frame_owner_element_type);
3947 }
3948
TEST_F(WebViewTest,ChangeDisplayMode)3949 TEST_F(WebViewTest, ChangeDisplayMode) {
3950 RegisterMockedHttpURLLoad("display_mode.html");
3951 WebView* web_view =
3952 web_view_helper_.InitializeAndLoad(base_url_ + "display_mode.html");
3953
3954 std::string content =
3955 WebFrameContentDumper::DumpWebViewAsText(web_view, 21).Utf8();
3956 EXPECT_EQ("regular-ui", content);
3957
3958 web_view->SetDisplayMode(blink::mojom::DisplayMode::kMinimalUi);
3959 content = WebFrameContentDumper::DumpWebViewAsText(web_view, 21).Utf8();
3960 EXPECT_EQ("minimal-ui", content);
3961 web_view_helper_.Reset();
3962 }
3963
TEST_F(WebViewTest,AddFrameInCloseUnload)3964 TEST_F(WebViewTest, AddFrameInCloseUnload) {
3965 CreateChildCounterFrameClient frame_client;
3966 RegisterMockedHttpURLLoad("add_frame_in_unload.html");
3967 web_view_helper_.InitializeAndLoad(base_url_ + "add_frame_in_unload.html",
3968 &frame_client);
3969 web_view_helper_.Reset();
3970 EXPECT_EQ(0, frame_client.Count());
3971 }
3972
TEST_F(WebViewTest,AddFrameInCloseURLUnload)3973 TEST_F(WebViewTest, AddFrameInCloseURLUnload) {
3974 CreateChildCounterFrameClient frame_client;
3975 RegisterMockedHttpURLLoad("add_frame_in_unload.html");
3976 web_view_helper_.InitializeAndLoad(base_url_ + "add_frame_in_unload.html",
3977 &frame_client);
3978 // Dispatch unload event.
3979 web_view_helper_.LocalMainFrame()->GetFrame()->ClosePage(base::DoNothing());
3980 EXPECT_EQ(0, frame_client.Count());
3981 web_view_helper_.Reset();
3982 }
3983
TEST_F(WebViewTest,AddFrameInNavigateUnload)3984 TEST_F(WebViewTest, AddFrameInNavigateUnload) {
3985 CreateChildCounterFrameClient frame_client;
3986 RegisterMockedHttpURLLoad("add_frame_in_unload.html");
3987 web_view_helper_.InitializeAndLoad(base_url_ + "add_frame_in_unload.html",
3988 &frame_client);
3989 frame_test_helpers::LoadFrame(web_view_helper_.GetWebView()->MainFrameImpl(),
3990 "about:blank");
3991 EXPECT_EQ(0, frame_client.Count());
3992 web_view_helper_.Reset();
3993 }
3994
TEST_F(WebViewTest,AddFrameInChildInNavigateUnload)3995 TEST_F(WebViewTest, AddFrameInChildInNavigateUnload) {
3996 CreateChildCounterFrameClient frame_client;
3997 RegisterMockedHttpURLLoad("add_frame_in_unload_wrapper.html");
3998 RegisterMockedHttpURLLoad("add_frame_in_unload.html");
3999 web_view_helper_.InitializeAndLoad(
4000 base_url_ + "add_frame_in_unload_wrapper.html", &frame_client);
4001 frame_test_helpers::LoadFrame(web_view_helper_.GetWebView()->MainFrameImpl(),
4002 "about:blank");
4003 EXPECT_EQ(1, frame_client.Count());
4004 web_view_helper_.Reset();
4005 }
4006
4007 class TouchEventHandlerWebWidgetClient
4008 : public frame_test_helpers::TestWebWidgetClient {
4009 public:
4010 // WebWidgetClient methods
SetHasTouchEventHandlers(bool state)4011 void SetHasTouchEventHandlers(bool state) override {
4012 // Only count the times the state changes.
4013 if (state != has_touch_event_handler_)
4014 has_touch_event_handler_count_[state]++;
4015 has_touch_event_handler_ = state;
4016 }
4017
4018 // Local methods
TouchEventHandlerWebWidgetClient()4019 TouchEventHandlerWebWidgetClient()
4020 : has_touch_event_handler_count_(), has_touch_event_handler_(false) {}
4021
GetAndResetHasTouchEventHandlerCallCount(bool state)4022 int GetAndResetHasTouchEventHandlerCallCount(bool state) {
4023 int value = has_touch_event_handler_count_[state];
4024 has_touch_event_handler_count_[state] = 0;
4025 return value;
4026 }
4027
4028 private:
4029 int has_touch_event_handler_count_[2];
4030 bool has_touch_event_handler_;
4031 };
4032
4033 // This test verifies that WebWidgetClient::SetHasTouchEventHandlers is called
4034 // accordingly for various calls to EventHandlerRegistry::did{Add|Remove|
4035 // RemoveAll}EventHandler(..., TouchEvent). Verifying that those calls are made
4036 // correctly is the job of web_tests/fast/events/event-handler-count.html.
TEST_F(WebViewTest,SetHasTouchEventHandlers)4037 TEST_F(WebViewTest, SetHasTouchEventHandlers) {
4038 TouchEventHandlerWebWidgetClient client;
4039 std::string url = RegisterMockedHttpURLLoad("has_touch_event_handlers.html");
4040 WebViewImpl* web_view_impl =
4041 web_view_helper_.InitializeAndLoad(url, nullptr, nullptr, &client);
4042 const EventHandlerRegistry::EventHandlerClass kTouchEvent =
4043 EventHandlerRegistry::kTouchStartOrMoveEventBlocking;
4044
4045 // The page is initialized with at least one no-handlers call.
4046 // In practice we get two such calls because WebViewHelper::initializeAndLoad
4047 // first initializes an empty frame, and then loads a document into it, so
4048 // there are two FrameLoader::commitProvisionalLoad calls.
4049 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4050 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4051
4052 // Adding the first document handler results in a has-handlers call.
4053 Document* document =
4054 web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
4055 EventHandlerRegistry* registry =
4056 &document->GetFrame()->GetEventHandlerRegistry();
4057 registry->DidAddEventHandler(*document, kTouchEvent);
4058 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4059 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(true));
4060
4061 // Adding another handler has no effect.
4062 registry->DidAddEventHandler(*document, kTouchEvent);
4063 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4064 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4065
4066 // Removing the duplicate handler has no effect.
4067 registry->DidRemoveEventHandler(*document, kTouchEvent);
4068 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4069 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4070
4071 // Removing the final handler results in a no-handlers call.
4072 registry->DidRemoveEventHandler(*document, kTouchEvent);
4073 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(false));
4074 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4075
4076 // Adding a handler on a div results in a has-handlers call.
4077 Element* parent_div = document->getElementById("parentdiv");
4078 DCHECK(parent_div);
4079 registry->DidAddEventHandler(*parent_div, kTouchEvent);
4080 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4081 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(true));
4082
4083 // Adding a duplicate handler on the div, clearing all document handlers
4084 // (of which there are none) and removing the extra handler on the div
4085 // all have no effect.
4086 registry->DidAddEventHandler(*parent_div, kTouchEvent);
4087 registry->DidRemoveAllEventHandlers(*document);
4088 registry->DidRemoveEventHandler(*parent_div, kTouchEvent);
4089 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4090 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4091
4092 // Removing the final handler on the div results in a no-handlers call.
4093 registry->DidRemoveEventHandler(*parent_div, kTouchEvent);
4094 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(false));
4095 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4096
4097 // Adding two handlers then clearing them in a single call results in a
4098 // has-handlers then no-handlers call.
4099 registry->DidAddEventHandler(*parent_div, kTouchEvent);
4100 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4101 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(true));
4102 registry->DidAddEventHandler(*parent_div, kTouchEvent);
4103 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4104 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4105 registry->DidRemoveAllEventHandlers(*parent_div);
4106 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(false));
4107 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4108
4109 // Adding a handler inside of a child iframe results in a has-handlers call.
4110 Element* child_frame = document->getElementById("childframe");
4111 DCHECK(child_frame);
4112 Document* child_document =
4113 To<HTMLIFrameElement>(child_frame)->contentDocument();
4114 Element* child_div = child_document->getElementById("childdiv");
4115 DCHECK(child_div);
4116 registry->DidAddEventHandler(*child_div, kTouchEvent);
4117 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4118 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(true));
4119
4120 // Adding and clearing handlers in the parent doc or elsewhere in the child
4121 // doc has no impact.
4122 registry->DidAddEventHandler(*document, kTouchEvent);
4123 registry->DidAddEventHandler(*child_frame, kTouchEvent);
4124 registry->DidAddEventHandler(*child_document, kTouchEvent);
4125 registry->DidRemoveAllEventHandlers(*document);
4126 registry->DidRemoveAllEventHandlers(*child_frame);
4127 registry->DidRemoveAllEventHandlers(*child_document);
4128 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4129 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4130
4131 // Removing the final handler inside the child frame results in a no-handlers
4132 // call.
4133 registry->DidRemoveAllEventHandlers(*child_div);
4134 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(false));
4135 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4136
4137 // Adding a handler inside the child frame results in a has-handlers call.
4138 registry->DidAddEventHandler(*child_document, kTouchEvent);
4139 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4140 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(true));
4141
4142 // Adding a handler in the parent document and removing the one in the frame
4143 // has no effect.
4144 registry->DidAddEventHandler(*child_frame, kTouchEvent);
4145 registry->DidRemoveEventHandler(*child_document, kTouchEvent);
4146 registry->DidRemoveAllEventHandlers(*child_document);
4147 registry->DidRemoveAllEventHandlers(*document);
4148 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(false));
4149 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4150
4151 // Now removing the handler in the parent document results in a no-handlers
4152 // call.
4153 registry->DidRemoveEventHandler(*child_frame, kTouchEvent);
4154 EXPECT_EQ(1, client.GetAndResetHasTouchEventHandlerCallCount(false));
4155 EXPECT_EQ(0, client.GetAndResetHasTouchEventHandlerCallCount(true));
4156
4157 // Free the webView before the TouchEventHandlerWebViewClient gets freed.
4158 web_view_helper_.Reset();
4159 }
4160
4161 // This test checks that deleting nodes which have only non-JS-registered touch
4162 // handlers also removes them from the event handler registry. Note that this
4163 // is different from detaching and re-attaching the same node, which is covered
4164 // by web tests under fast/events/.
TEST_F(WebViewTest,DeleteElementWithRegisteredHandler)4165 TEST_F(WebViewTest, DeleteElementWithRegisteredHandler) {
4166 std::string url = RegisterMockedHttpURLLoad("simple_div.html");
4167 WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(url);
4168
4169 Persistent<Document> document =
4170 web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
4171 Element* div = document->getElementById("div");
4172 EventHandlerRegistry& registry =
4173 document->GetFrame()->GetEventHandlerRegistry();
4174
4175 registry.DidAddEventHandler(*div, EventHandlerRegistry::kScrollEvent);
4176 EXPECT_TRUE(registry.HasEventHandlers(EventHandlerRegistry::kScrollEvent));
4177
4178 DummyExceptionStateForTesting exception_state;
4179 div->remove(exception_state);
4180
4181 // For oilpan we have to force a GC to ensure the event handlers have been
4182 // removed when checking below. We do a precise GC (collectAllGarbage does not
4183 // scan the stack) to ensure the div element dies. This is also why the
4184 // Document is in a Persistent since we want that to stay around.
4185 ThreadState::Current()->CollectAllGarbageForTesting();
4186
4187 EXPECT_FALSE(registry.HasEventHandlers(EventHandlerRegistry::kScrollEvent));
4188 }
4189
4190 // This test verifies the text input flags are correctly exposed to script.
TEST_F(WebViewTest,TextInputFlags)4191 TEST_F(WebViewTest, TextInputFlags) {
4192 std::string url = RegisterMockedHttpURLLoad("text_input_flags.html");
4193 WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(url);
4194 web_view_impl->SetInitialFocus(false);
4195
4196 WebLocalFrameImpl* frame = web_view_impl->MainFrameImpl();
4197 WebInputMethodController* active_input_method_controller =
4198 frame->GetInputMethodController();
4199 Document* document = frame->GetFrame()->GetDocument();
4200
4201 // (A) <input>
4202 // (A.1) Verifies autocorrect/autocomplete/spellcheck flags are Off and
4203 // autocapitalize is set to none.
4204 auto* input_element = To<HTMLInputElement>(document->getElementById("input"));
4205 document->SetFocusedElement(
4206 input_element, FocusParams(SelectionBehaviorOnFocus::kNone,
4207 mojom::blink::FocusType::kNone, nullptr));
4208 web_view_impl->MainFrameWidget()->SetFocus(true);
4209 WebTextInputInfo info1 = active_input_method_controller->TextInputInfo();
4210 EXPECT_EQ(kWebTextInputFlagAutocompleteOff | kWebTextInputFlagAutocorrectOff |
4211 kWebTextInputFlagSpellcheckOff |
4212 kWebTextInputFlagAutocapitalizeNone,
4213 info1.flags);
4214
4215 // (A.2) Verifies autocorrect/autocomplete/spellcheck flags are On and
4216 // autocapitalize is set to sentences.
4217 input_element = To<HTMLInputElement>(document->getElementById("input2"));
4218 document->SetFocusedElement(
4219 input_element, FocusParams(SelectionBehaviorOnFocus::kNone,
4220 mojom::blink::FocusType::kNone, nullptr));
4221 web_view_impl->MainFrameWidget()->SetFocus(true);
4222 WebTextInputInfo info2 = active_input_method_controller->TextInputInfo();
4223 EXPECT_EQ(kWebTextInputFlagAutocompleteOn | kWebTextInputFlagAutocorrectOn |
4224 kWebTextInputFlagSpellcheckOn |
4225 kWebTextInputFlagAutocapitalizeSentences,
4226 info2.flags);
4227
4228 // (B) <textarea> Verifies the default text input flags are
4229 // WebTextInputFlagAutocapitalizeSentences.
4230 auto* text_area_element =
4231 To<HTMLTextAreaElement>(document->getElementById("textarea"));
4232 document->SetFocusedElement(
4233 text_area_element, FocusParams(SelectionBehaviorOnFocus::kNone,
4234 mojom::blink::FocusType::kNone, nullptr));
4235 web_view_impl->MainFrameWidget()->SetFocus(true);
4236 WebTextInputInfo info3 = active_input_method_controller->TextInputInfo();
4237 EXPECT_EQ(kWebTextInputFlagAutocapitalizeSentences, info3.flags);
4238
4239 // (C) Verifies the WebTextInputInfo's don't equal.
4240 EXPECT_FALSE(info1.Equals(info2));
4241 EXPECT_FALSE(info2.Equals(info3));
4242
4243 // Free the webView before freeing the NonUserInputTextUpdateWebViewClient.
4244 web_view_helper_.Reset();
4245 }
4246
4247 // Check that the WebAutofillClient is correctly notified about first user
4248 // gestures after load, following various input events.
TEST_F(WebViewTest,FirstUserGestureObservedKeyEvent)4249 TEST_F(WebViewTest, FirstUserGestureObservedKeyEvent) {
4250 RegisterMockedHttpURLLoad("form.html");
4251 MockAutofillClient client;
4252 WebViewImpl* web_view =
4253 web_view_helper_.InitializeAndLoad(base_url_ + "form.html");
4254 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4255 frame->SetAutofillClient(&client);
4256 web_view->SetInitialFocus(false);
4257
4258 EXPECT_EQ(0, client.GetUserGestureNotificationsCount());
4259
4260 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
4261 WebInputEvent::kNoModifiers,
4262 WebInputEvent::GetStaticTimeStampForTests());
4263 key_event.dom_key = ui::DomKey::FromCharacter(' ');
4264 key_event.windows_key_code = VKEY_SPACE;
4265 web_view->MainFrameWidget()->HandleInputEvent(
4266 WebCoalescedInputEvent(key_event));
4267 key_event.SetType(WebInputEvent::kKeyUp);
4268 web_view->MainFrameWidget()->HandleInputEvent(
4269 WebCoalescedInputEvent(key_event));
4270
4271 EXPECT_EQ(1, client.GetUserGestureNotificationsCount());
4272 frame->SetAutofillClient(nullptr);
4273 }
4274
TEST_F(WebViewTest,FirstUserGestureObservedMouseEvent)4275 TEST_F(WebViewTest, FirstUserGestureObservedMouseEvent) {
4276 RegisterMockedHttpURLLoad("form.html");
4277 MockAutofillClient client;
4278 WebViewImpl* web_view =
4279 web_view_helper_.InitializeAndLoad(base_url_ + "form.html");
4280 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4281 frame->SetAutofillClient(&client);
4282 web_view->SetInitialFocus(false);
4283
4284 EXPECT_EQ(0, client.GetUserGestureNotificationsCount());
4285
4286 WebMouseEvent mouse_event(WebInputEvent::kMouseDown,
4287 WebInputEvent::kNoModifiers,
4288 WebInputEvent::GetStaticTimeStampForTests());
4289 mouse_event.button = WebMouseEvent::Button::kLeft;
4290 mouse_event.SetPositionInWidget(1, 1);
4291 mouse_event.click_count = 1;
4292 web_view->MainFrameWidget()->HandleInputEvent(
4293 WebCoalescedInputEvent(mouse_event));
4294 mouse_event.SetType(WebInputEvent::kMouseUp);
4295 web_view->MainFrameWidget()->HandleInputEvent(
4296 WebCoalescedInputEvent(mouse_event));
4297
4298 EXPECT_EQ(1, client.GetUserGestureNotificationsCount());
4299 frame->SetAutofillClient(nullptr);
4300 }
4301
TEST_F(WebViewTest,CompositionIsUserGesture)4302 TEST_F(WebViewTest, CompositionIsUserGesture) {
4303 RegisterMockedHttpURLLoad("input_field_populated.html");
4304 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4305 base_url_ + "input_field_populated.html");
4306 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4307 MockAutofillClient client;
4308 frame->SetAutofillClient(&client);
4309 web_view->SetInitialFocus(false);
4310
4311 EXPECT_EQ(0, client.TextChanges());
4312 EXPECT_TRUE(
4313 frame->FrameWidget()->GetActiveWebInputMethodController()->SetComposition(
4314 WebString::FromUTF8(std::string("hello").c_str()),
4315 WebVector<WebImeTextSpan>(), WebRange(), 3, 3));
4316 EXPECT_TRUE(frame->HasTransientUserActivation());
4317 EXPECT_EQ(1, client.TextChanges());
4318 EXPECT_TRUE(frame->HasMarkedText());
4319
4320 frame->SetAutofillClient(nullptr);
4321 }
4322
4323 // Currently, SelectionAsText() is built upon TextIterator, but
4324 // WebFrameContentDumper is built upon TextDumperForTests. Their results can
4325 // be different, making the test fail.
4326 // TODO(crbug.com/781434): Build a selection serializer upon TextDumperForTests.
TEST_F(WebViewTest,DISABLED_CompareSelectAllToContentAsText)4327 TEST_F(WebViewTest, DISABLED_CompareSelectAllToContentAsText) {
4328 RegisterMockedHttpURLLoad("longpress_selection.html");
4329 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4330 base_url_ + "longpress_selection.html");
4331
4332 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4333 frame->ExecuteScript(WebScriptSource(
4334 WebString::FromUTF8("document.execCommand('SelectAll', false, null)")));
4335 std::string actual = frame->SelectionAsText().Utf8();
4336
4337 const int kMaxOutputCharacters = 1024;
4338 std::string expected =
4339 WebFrameContentDumper::DumpWebViewAsText(web_view, kMaxOutputCharacters)
4340 .Utf8();
4341 EXPECT_EQ(expected, actual);
4342 }
4343
TEST_F(WebViewTest,AutoResizeSubtreeLayout)4344 TEST_F(WebViewTest, AutoResizeSubtreeLayout) {
4345 std::string url = RegisterMockedHttpURLLoad("subtree-layout.html");
4346 WebViewImpl* web_view = web_view_helper_.Initialize();
4347
4348 web_view->EnableAutoResizeMode(WebSize(200, 200), WebSize(200, 200));
4349 LoadFrame(web_view->MainFrameImpl(), url);
4350
4351 LocalFrameView* frame_view =
4352 web_view_helper_.LocalMainFrame()->GetFrameView();
4353
4354 // Auto-resizing used to DCHECK(needsLayout()) in LayoutBlockFlow::layout.
4355 // This EXPECT is merely a dummy. The real test is that we don't trigger
4356 // asserts in debug builds.
4357 EXPECT_FALSE(frame_view->NeedsLayout());
4358 }
4359
TEST_F(WebViewTest,PreferredSize)4360 TEST_F(WebViewTest, PreferredSize) {
4361 std::string url = base_url_ + "specify_size.html?100px:100px";
4362 url_test_helpers::RegisterMockedURLLoad(
4363 ToKURL(url), test::CoreTestDataPath("specify_size.html"));
4364 WebView* web_view = web_view_helper_.InitializeAndLoad(url);
4365
4366 WebSize size = web_view->ContentsPreferredMinimumSize();
4367 EXPECT_EQ(100, size.width);
4368 EXPECT_EQ(100, size.height);
4369
4370 web_view->SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
4371 size = web_view->ContentsPreferredMinimumSize();
4372 EXPECT_EQ(200, size.width);
4373 EXPECT_EQ(200, size.height);
4374
4375 // Verify that both width and height are rounded (in this case up)
4376 web_view->SetZoomLevel(PageZoomFactorToZoomLevel(0.9995));
4377 size = web_view->ContentsPreferredMinimumSize();
4378 EXPECT_EQ(100, size.width);
4379 EXPECT_EQ(100, size.height);
4380
4381 // Verify that both width and height are rounded (in this case down)
4382 web_view->SetZoomLevel(PageZoomFactorToZoomLevel(1.0005));
4383 size = web_view->ContentsPreferredMinimumSize();
4384 EXPECT_EQ(100, size.width);
4385 EXPECT_EQ(100, size.height);
4386
4387 url = base_url_ + "specify_size.html?1.5px:1.5px";
4388 url_test_helpers::RegisterMockedURLLoad(
4389 ToKURL(url), test::CoreTestDataPath("specify_size.html"));
4390 web_view = web_view_helper_.InitializeAndLoad(url);
4391
4392 web_view->SetZoomLevel(PageZoomFactorToZoomLevel(1));
4393 size = web_view->ContentsPreferredMinimumSize();
4394 EXPECT_EQ(2, size.width);
4395 EXPECT_EQ(2, size.height);
4396 }
4397
TEST_F(WebViewTest,PreferredMinimumSizeQuirksMode)4398 TEST_F(WebViewTest, PreferredMinimumSizeQuirksMode) {
4399 WebViewImpl* web_view = web_view_helper_.Initialize();
4400 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
4401 frame_test_helpers::LoadHTMLString(
4402 web_view->MainFrameImpl(),
4403 R"HTML(<html>
4404 <body style="margin: 0px;">
4405 <div style="width: 99px; height: 100px; display: inline-block;"></div>
4406 </body>
4407 </html>)HTML",
4408 url_test_helpers::ToKURL("http://example.com/"));
4409
4410 WebSize size = web_view->ContentsPreferredMinimumSize();
4411 EXPECT_EQ(99, size.width);
4412 // When in quirks mode the preferred height stretches to fill the viewport.
4413 EXPECT_EQ(600, size.height);
4414 }
4415
TEST_F(WebViewTest,PreferredSizeWithGrid)4416 TEST_F(WebViewTest, PreferredSizeWithGrid) {
4417 WebViewImpl* web_view = web_view_helper_.Initialize();
4418 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
4419 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
4420 R"HTML(<!DOCTYPE html>
4421 <style>
4422 html { writing-mode: vertical-rl; }
4423 body { margin: 0px; }
4424 </style>
4425 <div style="width: 100px;">
4426 <div style="display: grid; width: 100%;">
4427 <div style="writing-mode: horizontal-tb; height: 100px;"></div>
4428 </div>
4429 </div>
4430 )HTML",
4431 base_url);
4432
4433 WebSize size = web_view->ContentsPreferredMinimumSize();
4434 EXPECT_EQ(100, size.width);
4435 EXPECT_EQ(100, size.height);
4436 }
4437
TEST_F(WebViewTest,PreferredSizeWithGridMinWidth)4438 TEST_F(WebViewTest, PreferredSizeWithGridMinWidth) {
4439 WebViewImpl* web_view = web_view_helper_.Initialize();
4440 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
4441 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
4442 R"HTML(<!DOCTYPE html>
4443 <body style="margin: 0px;">
4444 <div style="display: inline-grid; min-width: 200px;">
4445 <div>item</div>
4446 </div>
4447 </body>
4448 )HTML",
4449 base_url);
4450
4451 WebSize size = web_view->ContentsPreferredMinimumSize();
4452 EXPECT_EQ(200, size.width);
4453 }
4454
TEST_F(WebViewTest,PreferredSizeWithGridMinWidthFlexibleTracks)4455 TEST_F(WebViewTest, PreferredSizeWithGridMinWidthFlexibleTracks) {
4456 WebViewImpl* web_view = web_view_helper_.Initialize();
4457 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
4458 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
4459 R"HTML(<!DOCTYPE html>
4460 <body style="margin: 0px;">
4461 <div style="display: inline-grid; min-width: 200px; grid-template-columns: 1fr;">
4462 <div>item</div>
4463 </div>
4464 </body>
4465 )HTML",
4466 base_url);
4467
4468 WebSize size = web_view->ContentsPreferredMinimumSize();
4469 EXPECT_EQ(200, size.width);
4470 }
4471
4472 #if BUILDFLAG(ENABLE_UNHANDLED_TAP)
4473
4474 // Helps set up any test that uses a mock Mojo implementation.
4475 class MojoTestHelper {
4476 public:
MojoTestHelper(const String & test_file,frame_test_helpers::WebViewHelper & web_view_helper)4477 MojoTestHelper(const String& test_file,
4478 frame_test_helpers::WebViewHelper& web_view_helper)
4479 : web_view_helper_(web_view_helper) {
4480 web_view_ =
4481 web_view_helper.InitializeAndLoad(test_file.Utf8(), &web_frame_client_);
4482 }
4483
~MojoTestHelper()4484 ~MojoTestHelper() {
4485 web_view_helper_.Reset(); // Remove dependency on locally scoped client.
4486 }
4487
WebView() const4488 WebViewImpl* WebView() const { return web_view_; }
4489
4490 private:
4491 WebViewImpl* web_view_;
4492 frame_test_helpers::WebViewHelper& web_view_helper_;
4493 frame_test_helpers::TestWebFrameClient web_frame_client_;
4494 };
4495
4496 // Mock implementation of the UnhandledTapNotifier Mojo receiver, for testing
4497 // the ShowUnhandledTapUIIfNeeded notification.
4498 class MockUnhandledTapNotifierImpl : public mojom::blink::UnhandledTapNotifier {
4499 public:
4500 MockUnhandledTapNotifierImpl() = default;
4501 ~MockUnhandledTapNotifierImpl() override = default;
4502
Bind(mojo::ScopedMessagePipeHandle handle)4503 void Bind(mojo::ScopedMessagePipeHandle handle) {
4504 receiver_.Bind(mojo::PendingReceiver<mojom::blink::UnhandledTapNotifier>(
4505 std::move(handle)));
4506 }
4507
ShowUnhandledTapUIIfNeeded(mojom::blink::UnhandledTapInfoPtr unhandled_tap_info)4508 void ShowUnhandledTapUIIfNeeded(
4509 mojom::blink::UnhandledTapInfoPtr unhandled_tap_info) override {
4510 was_unhandled_tap_ = true;
4511 tapped_position_ = unhandled_tap_info->tapped_position_in_viewport;
4512 element_text_run_length_ = unhandled_tap_info->element_text_run_length;
4513 font_size_ = unhandled_tap_info->font_size_in_pixels;
4514 }
WasUnhandledTap() const4515 bool WasUnhandledTap() const { return was_unhandled_tap_; }
GetTappedXPos() const4516 int GetTappedXPos() const { return tapped_position_.x(); }
GetTappedYPos() const4517 int GetTappedYPos() const { return tapped_position_.y(); }
GetFontSize() const4518 int GetFontSize() const { return font_size_; }
GetElementTextRunLength() const4519 int GetElementTextRunLength() const { return element_text_run_length_; }
Reset()4520 void Reset() {
4521 was_unhandled_tap_ = false;
4522 tapped_position_ = IntPoint();
4523 element_text_run_length_ = 0;
4524 font_size_ = 0;
4525 receiver_.reset();
4526 }
4527
4528 private:
4529 bool was_unhandled_tap_ = false;
4530 gfx::Point tapped_position_;
4531 int element_text_run_length_ = 0;
4532 int font_size_ = 0;
4533
4534 mojo::Receiver<mojom::blink::UnhandledTapNotifier> receiver_{this};
4535 };
4536
4537 // A Test Fixture for testing ShowUnhandledTapUIIfNeeded usages.
4538 class ShowUnhandledTapTest : public WebViewTest {
4539 public:
SetUp()4540 void SetUp() override {
4541 WebViewTest::SetUp();
4542 std::string test_file = "show_unhandled_tap.html";
4543 RegisterMockedHttpURLLoad("Ahem.ttf");
4544 RegisterMockedHttpURLLoad(test_file);
4545
4546 mojo_test_helper_.reset(new MojoTestHelper(
4547 WebString::FromUTF8(base_url_ + test_file), web_view_helper_));
4548
4549 web_view_ = mojo_test_helper_->WebView();
4550 web_view_->MainFrameWidget()->Resize(WebSize(500, 300));
4551 web_view_->MainFrameWidget()->UpdateAllLifecyclePhases(
4552 DocumentUpdateReason::kTest);
4553 RunPendingTasks();
4554
4555 WebLocalFrameImpl* web_local_frame = web_view_->MainFrameImpl();
4556 web_local_frame->GetFrame()
4557 ->GetBrowserInterfaceBroker()
4558 .SetBinderForTesting(
4559 mojom::blink::UnhandledTapNotifier::Name_,
4560 WTF::BindRepeating(&MockUnhandledTapNotifierImpl::Bind,
4561 WTF::Unretained(&mock_notifier_)));
4562 }
4563
TearDown()4564 void TearDown() override {
4565 WebLocalFrameImpl* web_local_frame = web_view_->MainFrameImpl();
4566 web_local_frame->GetFrame()
4567 ->GetBrowserInterfaceBroker()
4568 .SetBinderForTesting(mojom::blink::UnhandledTapNotifier::Name_, {});
4569
4570 WebViewTest::TearDown();
4571 }
4572
4573 protected:
4574 // Tap on the given element by ID.
Tap(const String & element_id)4575 void Tap(const String& element_id) {
4576 mock_notifier_.Reset();
4577 EXPECT_TRUE(TapElementById(WebInputEvent::kGestureTap, element_id));
4578 }
4579
4580 // Set up a test script for the given |operation| with the given |handler|.
SetTestScript(const String & operation,const String & handler)4581 void SetTestScript(const String& operation, const String& handler) {
4582 String test_key = operation + "-" + handler;
4583 web_view_->MainFrameImpl()->ExecuteScript(
4584 WebScriptSource(String("setTest('" + test_key + "');")));
4585 }
4586
4587 // Test each mouse event combination with the given |handler|, and verify the
4588 // |expected| outcome.
TestEachMouseEvent(const String & handler,bool expected)4589 void TestEachMouseEvent(const String& handler, bool expected) {
4590 SetTestScript("mousedown", handler);
4591 Tap("target");
4592 EXPECT_EQ(expected, mock_notifier_.WasUnhandledTap());
4593
4594 SetTestScript("mouseup", handler);
4595 Tap("target");
4596 EXPECT_EQ(expected, mock_notifier_.WasUnhandledTap());
4597
4598 SetTestScript("click", handler);
4599 Tap("target");
4600 EXPECT_EQ(expected, mock_notifier_.WasUnhandledTap());
4601 }
4602
4603 WebViewImpl* web_view_;
4604 MockUnhandledTapNotifierImpl mock_notifier_;
4605
4606 private:
4607 std::unique_ptr<MojoTestHelper> mojo_test_helper_;
4608 };
4609
TEST_F(ShowUnhandledTapTest,ShowUnhandledTapUIIfNeeded)4610 TEST_F(ShowUnhandledTapTest, ShowUnhandledTapUIIfNeeded) {
4611 // Scroll the bottom into view so we can distinguish window coordinates from
4612 // document coordinates.
4613 Tap("bottom");
4614 EXPECT_TRUE(mock_notifier_.WasUnhandledTap());
4615 EXPECT_EQ(64, mock_notifier_.GetTappedXPos());
4616 EXPECT_EQ(278, mock_notifier_.GetTappedYPos());
4617 EXPECT_EQ(16, mock_notifier_.GetFontSize());
4618 EXPECT_EQ(7, mock_notifier_.GetElementTextRunLength());
4619
4620 // Test basic tap handling and notification.
4621 Tap("target");
4622 EXPECT_TRUE(mock_notifier_.WasUnhandledTap());
4623 EXPECT_EQ(144, mock_notifier_.GetTappedXPos());
4624 EXPECT_EQ(82, mock_notifier_.GetTappedYPos());
4625
4626 // Test correct conversion of coordinates to viewport space under pinch-zoom.
4627 constexpr float scale = 1.5f;
4628 constexpr float visual_x = 6.f;
4629 constexpr float visual_y = 10.f;
4630
4631 web_view_->SetPageScaleFactor(scale);
4632 web_view_->SetVisualViewportOffset(gfx::PointF(visual_x, visual_y));
4633
4634 Tap("target");
4635
4636 // Ensure position didn't change as a result of scroll into view.
4637 ASSERT_EQ(visual_x, web_view_->VisualViewportOffset().x());
4638 ASSERT_EQ(visual_y, web_view_->VisualViewportOffset().y());
4639
4640 EXPECT_TRUE(mock_notifier_.WasUnhandledTap());
4641
4642 constexpr float expected_x = 144 * scale - (scale * visual_x);
4643 constexpr float expected_y = 82 * scale - (scale * visual_y);
4644 EXPECT_EQ(expected_x, mock_notifier_.GetTappedXPos());
4645 EXPECT_EQ(expected_y, mock_notifier_.GetTappedYPos());
4646 EXPECT_EQ(16, mock_notifier_.GetFontSize());
4647 EXPECT_EQ(28, mock_notifier_.GetElementTextRunLength());
4648 }
4649
TEST_F(ShowUnhandledTapTest,ShowUnhandledTapUIIfNeededWithMutateDom)4650 TEST_F(ShowUnhandledTapTest, ShowUnhandledTapUIIfNeededWithMutateDom) {
4651 // Test dom mutation.
4652 TestEachMouseEvent("mutateDom", FALSE);
4653
4654 // Test without any DOM mutation.
4655 TestEachMouseEvent("none", TRUE);
4656 }
4657
TEST_F(ShowUnhandledTapTest,ShowUnhandledTapUIIfNeededWithMutateStyle)4658 TEST_F(ShowUnhandledTapTest, ShowUnhandledTapUIIfNeededWithMutateStyle) {
4659 // Test style mutation.
4660 TestEachMouseEvent("mutateStyle", FALSE);
4661
4662 // Test checkbox:indeterminate style mutation.
4663 TestEachMouseEvent("mutateIndeterminate", FALSE);
4664
4665 // Test click div with :active style.
4666 Tap("style_active");
4667 EXPECT_FALSE(mock_notifier_.WasUnhandledTap());
4668 }
4669
TEST_F(ShowUnhandledTapTest,ShowUnhandledTapUIIfNeededWithPreventDefault)4670 TEST_F(ShowUnhandledTapTest, ShowUnhandledTapUIIfNeededWithPreventDefault) {
4671 // Test swallowing.
4672 TestEachMouseEvent("preventDefault", FALSE);
4673
4674 // Test without any preventDefault.
4675 TestEachMouseEvent("none", TRUE);
4676 }
4677
TEST_F(ShowUnhandledTapTest,ShowUnhandledTapUIIfNeededWithNonTriggeringNodes)4678 TEST_F(ShowUnhandledTapTest, ShowUnhandledTapUIIfNeededWithNonTriggeringNodes) {
4679 Tap("image");
4680 EXPECT_FALSE(mock_notifier_.WasUnhandledTap());
4681
4682 Tap("editable");
4683 EXPECT_FALSE(mock_notifier_.WasUnhandledTap());
4684
4685 Tap("focusable");
4686 EXPECT_FALSE(mock_notifier_.WasUnhandledTap());
4687 }
4688
TEST_F(ShowUnhandledTapTest,ShowUnhandledTapUIIfNeededWithTextSizes)4689 TEST_F(ShowUnhandledTapTest, ShowUnhandledTapUIIfNeededWithTextSizes) {
4690 Tap("large");
4691 EXPECT_TRUE(mock_notifier_.WasUnhandledTap());
4692 EXPECT_EQ(20, mock_notifier_.GetFontSize());
4693
4694 Tap("small");
4695 EXPECT_TRUE(mock_notifier_.WasUnhandledTap());
4696 EXPECT_EQ(10, mock_notifier_.GetFontSize());
4697 }
4698
4699 #endif // BUILDFLAG(ENABLE_UNHANDLED_TAP)
4700
4701 #if defined(OS_MACOSX)
TEST_F(WebViewTest,WebSubstringUtil)4702 TEST_F(WebViewTest, WebSubstringUtil) {
4703 RegisterMockedHttpURLLoad("content_editable_populated.html");
4704 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4705 base_url_ + "content_editable_populated.html");
4706 web_view->GetSettings()->SetDefaultFontSize(12);
4707 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
4708 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4709
4710 gfx::Point baseline_point;
4711 NSAttributedString* result = WebSubstringUtil::AttributedSubstringInRange(
4712 frame, 10, 3, &baseline_point);
4713 ASSERT_TRUE(!!result);
4714
4715 gfx::Point point(baseline_point);
4716 result = WebSubstringUtil::AttributedWordAtPoint(frame->FrameWidget(), point,
4717 baseline_point);
4718 ASSERT_TRUE(!!result);
4719
4720 web_view->SetZoomLevel(3);
4721
4722 result = WebSubstringUtil::AttributedSubstringInRange(frame, 5, 5,
4723 &baseline_point);
4724 ASSERT_TRUE(!!result);
4725
4726 point = baseline_point;
4727 result = WebSubstringUtil::AttributedWordAtPoint(frame->FrameWidget(), point,
4728 baseline_point);
4729 ASSERT_TRUE(!!result);
4730 }
4731
TEST_F(WebViewTest,WebSubstringUtilBaselinePoint)4732 TEST_F(WebViewTest, WebSubstringUtilBaselinePoint) {
4733 RegisterMockedHttpURLLoad("content_editable_multiline.html");
4734 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4735 base_url_ + "content_editable_multiline.html");
4736 web_view->GetSettings()->SetDefaultFontSize(12);
4737 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
4738 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4739
4740 gfx::Point old_point;
4741 WebSubstringUtil::AttributedSubstringInRange(frame, 3, 1, &old_point);
4742
4743 gfx::Point new_point;
4744 WebSubstringUtil::AttributedSubstringInRange(frame, 3, 20, &new_point);
4745
4746 EXPECT_EQ(old_point.x(), new_point.x());
4747 EXPECT_EQ(old_point.y(), new_point.y());
4748 }
4749
TEST_F(WebViewTest,WebSubstringUtilPinchZoom)4750 TEST_F(WebViewTest, WebSubstringUtilPinchZoom) {
4751 RegisterMockedHttpURLLoad("content_editable_populated.html");
4752 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4753 base_url_ + "content_editable_populated.html");
4754 web_view->GetSettings()->SetDefaultFontSize(12);
4755 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
4756 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4757 NSAttributedString* result = nil;
4758
4759 gfx::Point baseline_point;
4760 result = WebSubstringUtil::AttributedSubstringInRange(frame, 10, 3,
4761 &baseline_point);
4762 ASSERT_TRUE(!!result);
4763
4764 web_view->SetPageScaleFactor(3);
4765
4766 gfx::Point point_after_zoom;
4767 result = WebSubstringUtil::AttributedSubstringInRange(frame, 10, 3,
4768 &point_after_zoom);
4769 ASSERT_TRUE(!!result);
4770
4771 // We won't have moved by a full factor of 3 because of the translations, but
4772 // we should move by a factor of >2.
4773 EXPECT_LT(2 * baseline_point.x(), point_after_zoom.x());
4774 EXPECT_LT(2 * baseline_point.y(), point_after_zoom.y());
4775 }
4776
TEST_F(WebViewTest,WebSubstringUtilIframe)4777 TEST_F(WebViewTest, WebSubstringUtilIframe) {
4778 RegisterMockedHttpURLLoad("single_iframe.html");
4779 RegisterMockedHttpURLLoad("visible_iframe.html");
4780 WebViewImpl* web_view =
4781 web_view_helper_.InitializeAndLoad(base_url_ + "single_iframe.html");
4782 web_view->GetSettings()->SetDefaultFontSize(12);
4783 web_view->GetSettings()->SetJavaScriptEnabled(true);
4784 web_view->MainFrameWidget()->Resize(WebSize(400, 400));
4785 WebLocalFrameImpl* main_frame = web_view->MainFrameImpl();
4786 WebLocalFrameImpl* child_frame = WebLocalFrameImpl::FromFrame(
4787 To<LocalFrame>(main_frame->GetFrame()->Tree().FirstChild()));
4788
4789 gfx::Point baseline_point;
4790 NSAttributedString* result = WebSubstringUtil::AttributedSubstringInRange(
4791 child_frame, 11, 7, &baseline_point);
4792 ASSERT_NE(result, nullptr);
4793
4794 gfx::Point point(baseline_point);
4795 result = WebSubstringUtil::AttributedWordAtPoint(main_frame->FrameWidget(),
4796 point, baseline_point);
4797 ASSERT_NE(result, nullptr);
4798
4799 int y_before_change = baseline_point.y();
4800
4801 // Now move the <iframe> down by 100px.
4802 main_frame->ExecuteScript(WebScriptSource(
4803 "document.querySelector('iframe').style.marginTop = '100px';"));
4804
4805 point = gfx::Point(point.x(), point.y() + 100);
4806 result = WebSubstringUtil::AttributedWordAtPoint(main_frame->FrameWidget(),
4807 point, baseline_point);
4808 ASSERT_NE(result, nullptr);
4809
4810 EXPECT_EQ(y_before_change, baseline_point.y() - 100);
4811 }
4812
4813 #endif
4814
TEST_F(WebViewTest,ShouldSuppressKeyboardForPasswordField)4815 TEST_F(WebViewTest, ShouldSuppressKeyboardForPasswordField) {
4816 RegisterMockedHttpURLLoad("input_field_password.html");
4817 // Pretend client has fill data for all fields it's queried.
4818 MockAutofillClient client;
4819 client.SetShouldSuppressKeyboard(true);
4820 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4821 base_url_ + "input_field_password.html");
4822 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4823 frame->SetAutofillClient(&client);
4824 // No field is focused.
4825 EXPECT_FALSE(frame->ShouldSuppressKeyboardForFocusedElement());
4826
4827 // Focusing a field should result in treating it autofillable.
4828 web_view->SetInitialFocus(false);
4829 EXPECT_TRUE(frame->ShouldSuppressKeyboardForFocusedElement());
4830
4831 // Pretend that |client| no longer has autofill data available.
4832 client.SetShouldSuppressKeyboard(false);
4833 EXPECT_FALSE(frame->ShouldSuppressKeyboardForFocusedElement());
4834 frame->SetAutofillClient(nullptr);
4835 }
4836
TEST_F(WebViewTest,PasswordFieldEditingIsUserGesture)4837 TEST_F(WebViewTest, PasswordFieldEditingIsUserGesture) {
4838 RegisterMockedHttpURLLoad("input_field_password.html");
4839 MockAutofillClient client;
4840 WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
4841 base_url_ + "input_field_password.html");
4842 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
4843 frame->SetAutofillClient(&client);
4844 web_view->SetInitialFocus(false);
4845
4846 WebVector<WebImeTextSpan> empty_ime_text_spans;
4847
4848 EXPECT_EQ(0, client.TextChanges());
4849 EXPECT_TRUE(
4850 frame->FrameWidget()->GetActiveWebInputMethodController()->CommitText(
4851 WebString::FromUTF8(std::string("hello").c_str()),
4852 empty_ime_text_spans, WebRange(), 0));
4853 EXPECT_TRUE(frame->HasTransientUserActivation());
4854 EXPECT_EQ(1, client.TextChanges());
4855 frame->SetAutofillClient(nullptr);
4856 }
4857
4858 // Verify that a WebView created with a ScopedPagePauser already on the
4859 // stack defers its loads.
TEST_F(WebViewTest,CreatedDuringPagePause)4860 TEST_F(WebViewTest, CreatedDuringPagePause) {
4861 {
4862 WebViewImpl* web_view = web_view_helper_.Initialize();
4863 EXPECT_FALSE(web_view->GetPage()->Paused());
4864 }
4865
4866 {
4867 ScopedPagePauser pauser;
4868 WebViewImpl* web_view = web_view_helper_.Initialize();
4869 EXPECT_TRUE(web_view->GetPage()->Paused());
4870 }
4871 }
4872
4873 // Make sure the SubframeBeforeUnloadUseCounter is only incremented on subframe
4874 // unloads. crbug.com/635029.
TEST_F(WebViewTest,SubframeBeforeUnloadUseCounter)4875 TEST_F(WebViewTest, SubframeBeforeUnloadUseCounter) {
4876 RegisterMockedHttpURLLoad("visible_iframe.html");
4877 RegisterMockedHttpURLLoad("single_iframe.html");
4878 WebViewImpl* web_view =
4879 web_view_helper_.InitializeAndLoad(base_url_ + "single_iframe.html");
4880
4881 WebLocalFrame* frame = web_view_helper_.LocalMainFrame();
4882 Document* document =
4883 To<LocalFrame>(web_view_helper_.GetWebView()->GetPage()->MainFrame())
4884 ->GetDocument();
4885
4886 // Add a beforeunload handler in the main frame. Make sure firing
4887 // beforeunload doesn't increment the subframe use counter.
4888 {
4889 frame->ExecuteScript(
4890 WebScriptSource("addEventListener('beforeunload', function() {});"));
4891 web_view->MainFrameImpl()->DispatchBeforeUnloadEvent(false);
4892 EXPECT_FALSE(
4893 document->IsUseCounted(WebFeature::kSubFrameBeforeUnloadFired));
4894 }
4895
4896 // Add a beforeunload handler in the iframe and dispatch. Make sure we do
4897 // increment the use counter for subframe beforeunloads.
4898 {
4899 frame->ExecuteScript(WebScriptSource(
4900 "document.getElementsByTagName('iframe')[0].contentWindow."
4901 "addEventListener('beforeunload', function() {});"));
4902 To<WebLocalFrameImpl>(
4903 web_view->MainFrame()->FirstChild()->ToWebLocalFrame())
4904 ->DispatchBeforeUnloadEvent(false);
4905
4906 Document* child_document = To<LocalFrame>(web_view_helper_.GetWebView()
4907 ->GetPage()
4908 ->MainFrame()
4909 ->Tree()
4910 .FirstChild())
4911 ->GetDocument();
4912 EXPECT_TRUE(
4913 child_document->IsUseCounted(WebFeature::kSubFrameBeforeUnloadFired));
4914 }
4915 }
4916
4917 // Verify that page loads are deferred until all ScopedPageLoadDeferrers are
4918 // destroyed.
TEST_F(WebViewTest,NestedPagePauses)4919 TEST_F(WebViewTest, NestedPagePauses) {
4920 WebViewImpl* web_view = web_view_helper_.Initialize();
4921 EXPECT_FALSE(web_view->GetPage()->Paused());
4922
4923 {
4924 ScopedPagePauser pauser;
4925 EXPECT_TRUE(web_view->GetPage()->Paused());
4926
4927 {
4928 ScopedPagePauser pauser2;
4929 EXPECT_TRUE(web_view->GetPage()->Paused());
4930 }
4931
4932 EXPECT_TRUE(web_view->GetPage()->Paused());
4933 }
4934
4935 EXPECT_FALSE(web_view->GetPage()->Paused());
4936 }
4937
TEST_F(WebViewTest,ClosingPageIsPaused)4938 TEST_F(WebViewTest, ClosingPageIsPaused) {
4939 WebViewImpl* web_view = web_view_helper_.Initialize();
4940 Page* page = web_view_helper_.GetWebView()->GetPage();
4941 EXPECT_FALSE(page->Paused());
4942
4943 web_view->SetOpenedByDOM();
4944
4945 auto* main_frame = To<LocalFrame>(page->MainFrame());
4946 EXPECT_FALSE(main_frame->DomWindow()->closed());
4947
4948 ScriptState* script_state = ToScriptStateForMainWorld(main_frame);
4949 ScriptState::Scope entered_context_scope(script_state);
4950 v8::Context::BackupIncumbentScope incumbent_context_scope(
4951 script_state->GetContext());
4952
4953 main_frame->DomWindow()->close(script_state->GetIsolate());
4954 // The window should be marked closed...
4955 EXPECT_TRUE(main_frame->DomWindow()->closed());
4956 // EXPECT_TRUE(page->isClosing());
4957 // ...but not yet detached.
4958 EXPECT_TRUE(main_frame->GetPage());
4959
4960 {
4961 ScopedPagePauser pauser;
4962 EXPECT_TRUE(page->Paused());
4963 }
4964 }
4965
TEST_F(WebViewTest,ForceAndResetViewport)4966 TEST_F(WebViewTest, ForceAndResetViewport) {
4967 RegisterMockedHttpURLLoad("200-by-300.html");
4968 WebViewImpl* web_view_impl =
4969 web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
4970 web_view_impl->MainFrameWidget()->Resize(WebSize(100, 150));
4971 SetViewportSize(WebSize(100, 150));
4972 DevToolsEmulator* dev_tools_emulator = web_view_impl->GetDevToolsEmulator();
4973
4974 TransformationMatrix expected_matrix;
4975 expected_matrix.MakeIdentity();
4976 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
4977 {
4978 IntRect visible_rect(1, 2, 3, 4);
4979 dev_tools_emulator->OverrideVisibleRect(IntSize(), &visible_rect);
4980 EXPECT_EQ(IntRect(1, 2, 3, 4), visible_rect); // Was modified.
4981 }
4982
4983 // Override applies transform, sets visible rect, and disables
4984 // visual viewport clipping.
4985 TransformationMatrix matrix =
4986 dev_tools_emulator->ForceViewportForTesting(gfx::PointF(50, 55), 2.f);
4987 expected_matrix.MakeIdentity().Scale(2.f).Translate(-50, -55);
4988 EXPECT_EQ(expected_matrix, matrix);
4989 {
4990 IntRect visible_rect(1, 2, 3, 4);
4991 dev_tools_emulator->OverrideVisibleRect(IntSize(100, 150), &visible_rect);
4992 EXPECT_EQ(IntRect(50, 55, 50, 75), visible_rect);
4993 }
4994
4995 // Setting new override discards previous one.
4996 matrix = dev_tools_emulator->ForceViewportForTesting(gfx::PointF(5.4f, 10.5f),
4997 1.5f);
4998 expected_matrix.MakeIdentity().Scale(1.5f).Translate(-5.4f, -10.5f);
4999 EXPECT_EQ(expected_matrix, matrix);
5000 {
5001 IntRect visible_rect(1, 2, 3, 4);
5002 dev_tools_emulator->OverrideVisibleRect(IntSize(100, 150), &visible_rect);
5003 EXPECT_EQ(IntRect(5, 10, 68, 101), visible_rect); // Was modified.
5004 }
5005
5006 // Clearing override restores original transform, visible rect and
5007 // visual viewport clipping.
5008 matrix = dev_tools_emulator->ResetViewportForTesting();
5009 expected_matrix.MakeIdentity();
5010 EXPECT_EQ(expected_matrix, matrix);
5011 {
5012 IntRect visible_rect(1, 2, 3, 4);
5013 dev_tools_emulator->OverrideVisibleRect(IntSize(), &visible_rect);
5014 EXPECT_EQ(IntRect(1, 2, 3, 4), visible_rect); // Not modified.
5015 }
5016 }
5017
TEST_F(WebViewTest,ViewportOverrideIntegratesDeviceMetricsOffsetAndScale)5018 TEST_F(WebViewTest, ViewportOverrideIntegratesDeviceMetricsOffsetAndScale) {
5019 RegisterMockedHttpURLLoad("200-by-300.html");
5020 WebViewImpl* web_view_impl =
5021 web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
5022 web_view_impl->MainFrameWidget()->Resize(WebSize(100, 150));
5023
5024 TransformationMatrix expected_matrix;
5025 expected_matrix.MakeIdentity();
5026 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5027
5028 WebDeviceEmulationParams emulation_params;
5029 emulation_params.scale = 2.f;
5030 web_view_impl->EnableDeviceEmulation(emulation_params);
5031 expected_matrix.MakeIdentity().Scale(2.f);
5032 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5033
5034 // Device metrics offset and scale are applied before viewport override.
5035 emulation_params.viewport_offset = gfx::PointF(5, 10);
5036 emulation_params.viewport_scale = 1.5f;
5037 web_view_impl->EnableDeviceEmulation(emulation_params);
5038 expected_matrix.MakeIdentity()
5039 .Scale(1.5f)
5040 .Translate(-5, -10)
5041 .Scale(2.f);
5042 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5043 }
5044
TEST_F(WebViewTest,ViewportOverrideAdaptsToScaleAndScroll)5045 TEST_F(WebViewTest, ViewportOverrideAdaptsToScaleAndScroll) {
5046 RegisterMockedHttpURLLoad("200-by-300.html");
5047 WebViewImpl* web_view_impl =
5048 web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
5049 web_view_impl->MainFrameWidget()->Resize(WebSize(100, 150));
5050 SetViewportSize(WebSize(100, 150));
5051 LocalFrameView* frame_view =
5052 web_view_impl->MainFrameImpl()->GetFrame()->View();
5053 DevToolsEmulator* dev_tools_emulator = web_view_impl->GetDevToolsEmulator();
5054
5055 TransformationMatrix expected_matrix;
5056 expected_matrix.MakeIdentity();
5057 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5058
5059 // Initial transform takes current page scale and scroll position into
5060 // account.
5061 web_view_impl->SetPageScaleFactor(1.5f);
5062 frame_view->LayoutViewport()->SetScrollOffset(
5063 ScrollOffset(100, 150), mojom::blink::ScrollType::kProgrammatic,
5064 mojom::blink::ScrollBehavior::kInstant);
5065
5066 WebDeviceEmulationParams emulation_params;
5067 emulation_params.viewport_offset = gfx::PointF(50, 55);
5068 emulation_params.viewport_scale = 2.f;
5069 web_view_impl->EnableDeviceEmulation(emulation_params);
5070 expected_matrix.MakeIdentity()
5071 .Scale(2.f)
5072 .Translate(-50, -55)
5073 .Translate(100, 150)
5074 .Scale(1. / 1.5f);
5075 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5076 // Scale is irrelevant for visible rect.
5077 {
5078 IntRect visible_rect(1, 2, 3, 4);
5079 dev_tools_emulator->OverrideVisibleRect(IntSize(100, 150), &visible_rect);
5080 EXPECT_EQ(IntRect(50 - 100, 55 - 150, 50, 75), visible_rect);
5081 }
5082
5083 // Transform adapts to scroll changes.
5084 frame_view->LayoutViewport()->SetScrollOffset(
5085 ScrollOffset(50, 55), mojom::blink::ScrollType::kProgrammatic,
5086 mojom::blink::ScrollBehavior::kInstant);
5087 expected_matrix.MakeIdentity()
5088 .Scale(2.f)
5089 .Translate(-50, -55)
5090 .Translate(50, 55)
5091 .Scale(1. / 1.5f);
5092 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5093 // Visible rect adapts to scroll change.
5094 {
5095 IntRect visible_rect(1, 2, 3, 4);
5096 dev_tools_emulator->OverrideVisibleRect(IntSize(100, 150), &visible_rect);
5097 EXPECT_EQ(IntRect(50 - 50, 55 - 55, 50, 75), visible_rect);
5098 }
5099
5100 // Transform adapts to page scale changes.
5101 web_view_impl->SetPageScaleFactor(2.f);
5102 expected_matrix.MakeIdentity()
5103 .Scale(2.f)
5104 .Translate(-50, -55)
5105 .Translate(50, 55)
5106 .Scale(1. / 2.f);
5107 EXPECT_EQ(expected_matrix, web_view_impl->GetDeviceEmulationTransform());
5108 // Visible rect doesn't change.
5109 {
5110 IntRect visible_rect(1, 2, 3, 4);
5111 dev_tools_emulator->OverrideVisibleRect(IntSize(100, 150), &visible_rect);
5112 EXPECT_EQ(IntRect(50 - 50, 55 - 55, 50, 75), visible_rect);
5113 }
5114 }
5115
TEST_F(WebViewTest,ResizeForPrintingViewportUnits)5116 TEST_F(WebViewTest, ResizeForPrintingViewportUnits) {
5117 WebViewImpl* web_view = web_view_helper_.Initialize();
5118 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
5119
5120 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5121 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5122 "<style>"
5123 " body { margin: 0px; }"
5124 " #vw { width: 100vw; height: 100vh; }"
5125 "</style>"
5126 "<div id=vw></div>",
5127 base_url);
5128
5129 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
5130 Document* document = frame->GetFrame()->GetDocument();
5131 Element* vw_element = document->getElementById("vw");
5132
5133 EXPECT_EQ(800, vw_element->OffsetWidth());
5134
5135 FloatSize page_size(300, 360);
5136
5137 WebPrintParams print_params;
5138 print_params.print_content_area.width = page_size.Width();
5139 print_params.print_content_area.height = page_size.Height();
5140
5141 IntSize expected_size = PrintICBSizeFromPageSize(page_size);
5142
5143 frame->PrintBegin(print_params, WebNode());
5144
5145 EXPECT_EQ(expected_size.Width(), vw_element->OffsetWidth());
5146 EXPECT_EQ(expected_size.Height(), vw_element->OffsetHeight());
5147
5148 web_view->MainFrameWidget()->Resize(FlooredIntSize(page_size));
5149
5150 EXPECT_EQ(expected_size.Width(), vw_element->OffsetWidth());
5151 EXPECT_EQ(expected_size.Height(), vw_element->OffsetHeight());
5152
5153 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
5154 frame->PrintEnd();
5155
5156 EXPECT_EQ(800, vw_element->OffsetWidth());
5157 }
5158
TEST_F(WebViewTest,WidthMediaQueryWithPageZoomAfterPrinting)5159 TEST_F(WebViewTest, WidthMediaQueryWithPageZoomAfterPrinting) {
5160 WebViewImpl* web_view = web_view_helper_.Initialize();
5161 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
5162 web_view->SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
5163
5164 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5165 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5166 "<style>"
5167 " @media (max-width: 600px) {"
5168 " div { color: green }"
5169 " }"
5170 "</style>"
5171 "<div id=d></div>",
5172 base_url);
5173
5174 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
5175 Document* document = frame->GetFrame()->GetDocument();
5176 Element* div = document->getElementById("d");
5177
5178 EXPECT_EQ(MakeRGB(0, 128, 0), div->GetComputedStyle()->VisitedDependentColor(
5179 GetCSSPropertyColor()));
5180
5181 FloatSize page_size(300, 360);
5182
5183 WebPrintParams print_params;
5184 print_params.print_content_area.width = page_size.Width();
5185 print_params.print_content_area.height = page_size.Height();
5186
5187 frame->PrintBegin(print_params, WebNode());
5188 frame->PrintEnd();
5189
5190 EXPECT_EQ(MakeRGB(0, 128, 0), div->GetComputedStyle()->VisitedDependentColor(
5191 GetCSSPropertyColor()));
5192 }
5193
TEST_F(WebViewTest,ViewportUnitsPrintingWithPageZoom)5194 TEST_F(WebViewTest, ViewportUnitsPrintingWithPageZoom) {
5195 WebViewImpl* web_view = web_view_helper_.Initialize();
5196 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
5197 web_view->SetZoomLevel(PageZoomFactorToZoomLevel(2.0));
5198
5199 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5200 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5201 "<style>"
5202 " body { margin: 0 }"
5203 " #t1 { width: 100% }"
5204 " #t2 { width: 100vw }"
5205 "</style>"
5206 "<div id=t1></div>"
5207 "<div id=t2></div>",
5208 base_url);
5209
5210 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
5211 Document* document = frame->GetFrame()->GetDocument();
5212 Element* t1 = document->getElementById("t1");
5213 Element* t2 = document->getElementById("t2");
5214
5215 EXPECT_EQ(400, t1->OffsetWidth());
5216 EXPECT_EQ(400, t2->OffsetWidth());
5217
5218 FloatSize page_size(600, 720);
5219 int expected_width = PrintICBSizeFromPageSize(page_size).Width();
5220
5221 WebPrintParams print_params;
5222 print_params.print_content_area.width = page_size.Width();
5223 print_params.print_content_area.height = page_size.Height();
5224
5225 frame->PrintBegin(print_params, WebNode());
5226
5227 EXPECT_EQ(expected_width, t1->OffsetWidth());
5228 EXPECT_EQ(expected_width, t2->OffsetWidth());
5229
5230 frame->PrintEnd();
5231 }
5232
TEST_F(WebViewTest,DeviceEmulationResetScrollbars)5233 TEST_F(WebViewTest, DeviceEmulationResetScrollbars) {
5234 WebViewImpl* web_view = web_view_helper_.Initialize();
5235 web_view->MainFrameWidget()->Resize(WebSize(800, 600));
5236
5237 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5238 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5239 "<!doctype html>"
5240 "<meta name='viewport'"
5241 " content='width=device-width'>"
5242 "<style>"
5243 " body {margin: 0px; height:3000px;}"
5244 "</style>",
5245 base_url);
5246 UpdateAllLifecyclePhases();
5247
5248 WebLocalFrameImpl* frame = web_view->MainFrameImpl();
5249 auto* frame_view = frame->GetFrameView();
5250 EXPECT_FALSE(frame_view->VisualViewportSuppliesScrollbars());
5251 EXPECT_NE(nullptr, frame_view->LayoutViewport()->VerticalScrollbar());
5252
5253 WebDeviceEmulationParams params;
5254 params.screen_position = WebDeviceEmulationParams::kMobile;
5255 params.device_scale_factor = 0;
5256 params.scale = 1;
5257
5258 web_view->EnableDeviceEmulation(params);
5259
5260 // The visual viewport should now proivde the scrollbars instead of the view.
5261 EXPECT_TRUE(frame_view->VisualViewportSuppliesScrollbars());
5262 EXPECT_EQ(nullptr, frame_view->LayoutViewport()->VerticalScrollbar());
5263
5264 web_view->DisableDeviceEmulation();
5265
5266 // The view should once again provide the scrollbars.
5267 EXPECT_FALSE(frame_view->VisualViewportSuppliesScrollbars());
5268 EXPECT_NE(nullptr, frame_view->LayoutViewport()->VerticalScrollbar());
5269 }
5270
TEST_F(WebViewTest,SetZoomLevelWhilePluginFocused)5271 TEST_F(WebViewTest, SetZoomLevelWhilePluginFocused) {
5272 class PluginCreatingWebFrameClient
5273 : public frame_test_helpers::TestWebFrameClient {
5274 public:
5275 // WebLocalFrameClient overrides:
5276 WebPlugin* CreatePlugin(const WebPluginParams& params) override {
5277 return new FakeWebPlugin(params);
5278 }
5279 };
5280 PluginCreatingWebFrameClient frame_client;
5281 WebViewImpl* web_view = web_view_helper_.Initialize(&frame_client);
5282 WebURL base_url = url_test_helpers::ToKURL("https://example.com/");
5283 frame_test_helpers::LoadHTMLString(
5284 web_view->MainFrameImpl(),
5285 "<!DOCTYPE html><html><body>"
5286 "<object type='application/x-webkit-test-plugin'></object>"
5287 "</body></html>",
5288 base_url);
5289 // Verify the plugin is loaded.
5290 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5291 auto* plugin_element =
5292 To<HTMLObjectElement>(main_frame->GetDocument()->body()->firstChild());
5293 EXPECT_TRUE(plugin_element->OwnedPlugin());
5294 // Focus the plugin element, and then change the zoom level on the WebView.
5295 plugin_element->focus();
5296 EXPECT_FLOAT_EQ(1.0f, main_frame->PageZoomFactor());
5297 web_view->SetZoomLevel(-1.0);
5298 // Even though the plugin is focused, the entire frame's zoom factor should
5299 // still be updated.
5300 EXPECT_FLOAT_EQ(5.0f / 6.0f, main_frame->PageZoomFactor());
5301 web_view_helper_.Reset(); // Remove dependency on locally scoped client.
5302 }
5303
5304 // Tests that a layout update that detaches a plugin doesn't crash if the
5305 // plugin tries to execute script while being destroyed.
TEST_F(WebViewTest,DetachPluginInLayout)5306 TEST_F(WebViewTest, DetachPluginInLayout) {
5307 class ScriptInDestroyPlugin : public FakeWebPlugin {
5308 public:
5309 ScriptInDestroyPlugin(WebLocalFrame* frame, const WebPluginParams& params)
5310 : FakeWebPlugin(params), frame_(frame) {}
5311
5312 // WebPlugin overrides:
5313 void Destroy() override {
5314 frame_->ExecuteScript(WebScriptSource("console.log('done')"));
5315 // Deletes this.
5316 FakeWebPlugin::Destroy();
5317 }
5318
5319 private:
5320 WebLocalFrame* frame_; // Unowned
5321 };
5322
5323 class PluginCreatingWebFrameClient
5324 : public frame_test_helpers::TestWebFrameClient {
5325 public:
5326 // WebLocalFrameClient overrides:
5327 WebPlugin* CreatePlugin(const WebPluginParams& params) override {
5328 return new ScriptInDestroyPlugin(Frame(), params);
5329 }
5330
5331 void DidAddMessageToConsole(const WebConsoleMessage& message,
5332 const WebString& source_name,
5333 unsigned source_line,
5334 const WebString& stack_trace) override {
5335 message_ = message.text;
5336 }
5337
5338 const String& Message() const { return message_; }
5339
5340 private:
5341 String message_;
5342 };
5343
5344 PluginCreatingWebFrameClient frame_client;
5345 WebViewImpl* web_view = web_view_helper_.Initialize(&frame_client);
5346 WebURL base_url = url_test_helpers::ToKURL("https://example.com/");
5347 frame_test_helpers::LoadHTMLString(
5348 web_view->MainFrameImpl(),
5349 "<!DOCTYPE html><html><body>"
5350 "<object type='application/x-webkit-test-plugin'></object>"
5351 "</body></html>",
5352 base_url);
5353 // Verify the plugin is loaded.
5354 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5355 auto* plugin_element =
5356 To<HTMLObjectElement>(main_frame->GetDocument()->body()->firstChild());
5357 EXPECT_TRUE(plugin_element->OwnedPlugin());
5358
5359 plugin_element->style()->setCSSText(
5360 main_frame->GetDocument()->ToExecutionContext(), "display: none",
5361 ASSERT_NO_EXCEPTION);
5362 EXPECT_TRUE(plugin_element->OwnedPlugin());
5363 UpdateAllLifecyclePhases();
5364 EXPECT_FALSE(plugin_element->OwnedPlugin());
5365 EXPECT_EQ("done", frame_client.Message());
5366 web_view_helper_.Reset(); // Remove dependency on locally scoped client.
5367 }
5368
5369 // Check that first input delay is correctly reported to the document.
TEST_F(WebViewTest,FirstInputDelayReported)5370 TEST_F(WebViewTest, FirstInputDelayReported) {
5371 WebViewImpl* web_view = web_view_helper_.Initialize();
5372 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5373 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5374 "<html><body></body></html>", base_url);
5375
5376 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5377 ASSERT_NE(nullptr, main_frame);
5378
5379 Document* document = main_frame->GetDocument();
5380 ASSERT_NE(nullptr, document);
5381
5382 base::TimeTicks start_time = test_task_runner_->NowTicks();
5383 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5384
5385 InteractiveDetector* interactive_detector =
5386 GetTestInteractiveDetector(*document);
5387
5388 EXPECT_FALSE(interactive_detector->GetFirstInputDelay().has_value());
5389
5390 WebKeyboardEvent key_event1(WebInputEvent::kRawKeyDown,
5391 WebInputEvent::kNoModifiers,
5392 WebInputEvent::GetStaticTimeStampForTests());
5393 key_event1.dom_key = ui::DomKey::FromCharacter(' ');
5394 key_event1.windows_key_code = VKEY_SPACE;
5395 key_event1.SetTimeStamp(test_task_runner_->NowTicks());
5396 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5397 web_view->MainFrameWidget()->HandleInputEvent(
5398 WebCoalescedInputEvent(key_event1));
5399
5400 EXPECT_TRUE(interactive_detector->GetFirstInputDelay().has_value());
5401 EXPECT_NEAR(50,
5402 (*interactive_detector->GetFirstInputDelay()).InMillisecondsF(),
5403 0.01);
5404 EXPECT_EQ(70, (*interactive_detector->GetFirstInputTimestamp() - start_time)
5405 .InMillisecondsF());
5406
5407 // Sending a second event won't change the FirstInputDelay.
5408 WebKeyboardEvent key_event2(WebInputEvent::kRawKeyDown,
5409 WebInputEvent::kNoModifiers,
5410 WebInputEvent::GetStaticTimeStampForTests());
5411 key_event2.dom_key = ui::DomKey::FromCharacter(' ');
5412 key_event2.windows_key_code = VKEY_SPACE;
5413 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(60));
5414 key_event2.SetTimeStamp(test_task_runner_->NowTicks());
5415 web_view->MainFrameWidget()->HandleInputEvent(
5416 WebCoalescedInputEvent(key_event2));
5417
5418 EXPECT_NEAR(50,
5419 (*interactive_detector->GetFirstInputDelay()).InMillisecondsF(),
5420 0.01);
5421 EXPECT_EQ(70, (*interactive_detector->GetFirstInputTimestamp() - start_time)
5422 .InMillisecondsF());
5423 }
5424
5425 // Check that longest input delay is correctly reported to the document.
TEST_F(WebViewTest,LongestInputDelayReported)5426 TEST_F(WebViewTest, LongestInputDelayReported) {
5427 WebViewImpl* web_view = web_view_helper_.Initialize();
5428 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5429 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5430 "<html><body></body></html>", base_url);
5431
5432 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5433 ASSERT_NE(nullptr, main_frame);
5434
5435 Document* document = main_frame->GetDocument();
5436 ASSERT_NE(nullptr, document);
5437
5438 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5439
5440 InteractiveDetector* interactive_detector =
5441 GetTestInteractiveDetector(*document);
5442
5443 EXPECT_FALSE(interactive_detector->GetLongestInputDelay().has_value());
5444
5445 WebKeyboardEvent key_event1(WebInputEvent::kRawKeyDown,
5446 WebInputEvent::kNoModifiers,
5447 WebInputEvent::GetStaticTimeStampForTests());
5448 key_event1.dom_key = ui::DomKey::FromCharacter(' ');
5449 key_event1.windows_key_code = VKEY_SPACE;
5450 key_event1.SetTimeStamp(test_task_runner_->NowTicks());
5451 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5452 web_view->MainFrameWidget()->HandleInputEvent(
5453 WebCoalescedInputEvent(key_event1));
5454
5455 base::TimeTicks longest_input_timestamp = test_task_runner_->NowTicks();
5456
5457 WebKeyboardEvent key_event2(WebInputEvent::kRawKeyDown,
5458 WebInputEvent::kNoModifiers,
5459 WebInputEvent::GetStaticTimeStampForTests());
5460 key_event2.dom_key = ui::DomKey::FromCharacter(' ');
5461 key_event2.windows_key_code = VKEY_SPACE;
5462 key_event2.SetTimeStamp(longest_input_timestamp);
5463 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
5464 web_view->MainFrameWidget()->HandleInputEvent(
5465 WebCoalescedInputEvent(key_event2));
5466
5467 WebKeyboardEvent key_event3(WebInputEvent::kRawKeyDown,
5468 WebInputEvent::kNoModifiers,
5469 WebInputEvent::GetStaticTimeStampForTests());
5470 key_event3.dom_key = ui::DomKey::FromCharacter(' ');
5471 key_event3.windows_key_code = VKEY_SPACE;
5472 key_event3.SetTimeStamp(test_task_runner_->NowTicks());
5473 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5474 web_view->MainFrameWidget()->HandleInputEvent(
5475 WebCoalescedInputEvent(key_event3));
5476
5477 EXPECT_NEAR(100,
5478 (*interactive_detector->GetLongestInputDelay()).InMillisecondsF(),
5479 0.01);
5480 EXPECT_EQ(longest_input_timestamp,
5481 interactive_detector->GetLongestInputTimestamp());
5482 }
5483
TEST_F(WebViewTest,InputDelayReported)5484 TEST_F(WebViewTest, InputDelayReported) {
5485 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5486
5487 WebViewImpl* web_view = web_view_helper_.Initialize();
5488
5489 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5490 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5491 "<html><body></body></html>", base_url,
5492 test_task_runner_->GetMockTickClock());
5493
5494 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5495 ASSERT_NE(nullptr, main_frame);
5496 Document* document = main_frame->GetDocument();
5497 ASSERT_NE(nullptr, document);
5498 GetTestInteractiveDetector(*document);
5499
5500 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5501
5502 HistogramTester histogram_tester;
5503 WebKeyboardEvent key_event1(WebInputEvent::kRawKeyDown,
5504 WebInputEvent::kNoModifiers,
5505 WebInputEvent::GetStaticTimeStampForTests());
5506 key_event1.dom_key = ui::DomKey::FromCharacter(' ');
5507 key_event1.windows_key_code = VKEY_SPACE;
5508 key_event1.SetTimeStamp(test_task_runner_->NowTicks());
5509 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5510 web_view->MainFrameWidget()->HandleInputEvent(
5511 WebCoalescedInputEvent(key_event1));
5512
5513 WebKeyboardEvent key_event2(WebInputEvent::kRawKeyDown,
5514 WebInputEvent::kNoModifiers,
5515 WebInputEvent::GetStaticTimeStampForTests());
5516 key_event2.dom_key = ui::DomKey::FromCharacter(' ');
5517 key_event2.windows_key_code = VKEY_SPACE;
5518 key_event2.SetTimeStamp(test_task_runner_->NowTicks());
5519 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5520 web_view->MainFrameWidget()->HandleInputEvent(
5521 WebCoalescedInputEvent(key_event2));
5522
5523 WebKeyboardEvent key_event3(WebInputEvent::kRawKeyDown,
5524 WebInputEvent::kNoModifiers,
5525 WebInputEvent::GetStaticTimeStampForTests());
5526 key_event3.dom_key = ui::DomKey::FromCharacter(' ');
5527 key_event3.windows_key_code = VKEY_SPACE;
5528 key_event3.SetTimeStamp(test_task_runner_->NowTicks());
5529 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5530 web_view->MainFrameWidget()->HandleInputEvent(
5531 WebCoalescedInputEvent(key_event3));
5532
5533 histogram_tester.ExpectTotalCount("PageLoad.InteractiveTiming.InputDelay3",
5534 3);
5535 histogram_tester.ExpectBucketCount("PageLoad.InteractiveTiming.InputDelay3",
5536 50, 2);
5537 histogram_tester.ExpectBucketCount("PageLoad.InteractiveTiming.InputDelay3",
5538 70, 1);
5539
5540 histogram_tester.ExpectTotalCount(
5541 "PageLoad.InteractiveTiming.InputTimestamp3", 3);
5542 histogram_tester.ExpectBucketCount(
5543 "PageLoad.InteractiveTiming.InputTimestamp3", 70, 1);
5544 histogram_tester.ExpectBucketCount(
5545 "PageLoad.InteractiveTiming.InputTimestamp3", 120, 1);
5546 histogram_tester.ExpectBucketCount(
5547 "PageLoad.InteractiveTiming.InputTimestamp3", 170, 1);
5548 }
5549
5550 // Tests that if the page was backgrounded while an input event was queued,
5551 // we do not count its delay to calculate longest input delay.
TEST_F(WebViewTest,LongestInputDelayPageBackgroundedDuringQueuing)5552 TEST_F(WebViewTest, LongestInputDelayPageBackgroundedDuringQueuing) {
5553 WebViewImpl* web_view = web_view_helper_.Initialize();
5554 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5555 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5556 "<html><body></body></html>", base_url);
5557
5558 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5559 ASSERT_NE(nullptr, main_frame);
5560
5561 Document* document = main_frame->GetDocument();
5562 ASSERT_NE(nullptr, document);
5563
5564 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5565
5566 InteractiveDetector* interactive_detector =
5567 GetTestInteractiveDetector(*document);
5568
5569 EXPECT_FALSE(interactive_detector->GetLongestInputDelay().has_value());
5570
5571 WebKeyboardEvent key_event1(WebInputEvent::kRawKeyDown,
5572 WebInputEvent::kNoModifiers,
5573 WebInputEvent::GetStaticTimeStampForTests());
5574 key_event1.dom_key = ui::DomKey::FromCharacter(' ');
5575 key_event1.windows_key_code = VKEY_SPACE;
5576 base::TimeTicks key_event1_time = test_task_runner_->NowTicks();
5577 key_event1.SetTimeStamp(key_event1_time);
5578 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5579 web_view->MainFrameWidget()->HandleInputEvent(
5580 WebCoalescedInputEvent(key_event1));
5581
5582 WebKeyboardEvent key_event2(WebInputEvent::kRawKeyDown,
5583 WebInputEvent::kNoModifiers,
5584 WebInputEvent::GetStaticTimeStampForTests());
5585 key_event2.dom_key = ui::DomKey::FromCharacter(' ');
5586 key_event2.windows_key_code = VKEY_SPACE;
5587 key_event2.SetTimeStamp(test_task_runner_->NowTicks());
5588 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
5589 web_view->SetVisibilityState(PageVisibilityState::kHidden,
5590 /*initial_state=*/false);
5591 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
5592 web_view->SetVisibilityState(PageVisibilityState::kVisible,
5593 /*initial_state=*/false);
5594 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
5595 // Total input delay is >300ms.
5596 web_view->MainFrameWidget()->HandleInputEvent(
5597 WebCoalescedInputEvent(key_event2));
5598
5599 EXPECT_NEAR(50,
5600 (*interactive_detector->GetLongestInputDelay()).InMillisecondsF(),
5601 0.01);
5602 EXPECT_EQ(key_event1_time, interactive_detector->GetLongestInputTimestamp());
5603 }
5604
5605 // Tests that if the page was backgrounded at navigation start and an input
5606 // event was queued before it was foregrounded, we do not count its delay to
5607 // calculate longest input delay.
TEST_F(WebViewTest,LongestInputDelayPageBackgroundedAtNavStart)5608 TEST_F(WebViewTest, LongestInputDelayPageBackgroundedAtNavStart) {
5609 WebViewImpl* web_view = web_view_helper_.Initialize();
5610 web_view->SetVisibilityState(PageVisibilityState::kHidden,
5611 /*initial_state=*/false);
5612 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5613 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5614 "<html><body></body></html>", base_url);
5615
5616 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5617 ASSERT_NE(nullptr, main_frame);
5618
5619 Document* document = main_frame->GetDocument();
5620 ASSERT_NE(nullptr, document);
5621
5622 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5623
5624 InteractiveDetector* interactive_detector =
5625 GetTestInteractiveDetector(*document);
5626
5627 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
5628 WebInputEvent::kNoModifiers,
5629 WebInputEvent::GetStaticTimeStampForTests());
5630 key_event.dom_key = ui::DomKey::FromCharacter(' ');
5631 key_event.windows_key_code = VKEY_SPACE;
5632 key_event.SetTimeStamp(test_task_runner_->NowTicks());
5633 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
5634 web_view->SetVisibilityState(PageVisibilityState::kVisible,
5635 /*initial_state=*/false);
5636 web_view->MainFrameWidget()->HandleInputEvent(
5637 WebCoalescedInputEvent(key_event));
5638
5639 EXPECT_FALSE(interactive_detector->GetLongestInputDelay().has_value());
5640 }
5641
5642 // Tests page backgrounding outside of input queuing time does not affect
5643 // longest input delay.
TEST_F(WebViewTest,LongestInputDelayPageBackgroundedNotDuringQueuing)5644 TEST_F(WebViewTest, LongestInputDelayPageBackgroundedNotDuringQueuing) {
5645 WebViewImpl* web_view = web_view_helper_.Initialize();
5646 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5647 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5648 "<html><body></body></html>", base_url);
5649
5650 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5651 ASSERT_NE(nullptr, main_frame);
5652
5653 Document* document = main_frame->GetDocument();
5654 ASSERT_NE(nullptr, document);
5655
5656 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5657
5658 InteractiveDetector* interactive_detector =
5659 GetTestInteractiveDetector(*document);
5660
5661 EXPECT_FALSE(interactive_detector->GetLongestInputDelay().has_value());
5662
5663 web_view->SetVisibilityState(PageVisibilityState::kHidden,
5664 /*initial_state=*/false);
5665 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
5666 web_view->SetVisibilityState(PageVisibilityState::kVisible,
5667 /*initial_state=*/false);
5668 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
5669
5670 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
5671 WebInputEvent::kNoModifiers,
5672 WebInputEvent::GetStaticTimeStampForTests());
5673 key_event.dom_key = ui::DomKey::FromCharacter(' ');
5674 key_event.windows_key_code = VKEY_SPACE;
5675 base::TimeTicks key_event_time = test_task_runner_->NowTicks();
5676 key_event.SetTimeStamp(key_event_time);
5677 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5678 web_view->MainFrameWidget()->HandleInputEvent(
5679 WebCoalescedInputEvent(key_event));
5680
5681 EXPECT_NEAR(50,
5682 (*interactive_detector->GetLongestInputDelay()).InMillisecondsF(),
5683 0.01);
5684 EXPECT_EQ(key_event_time, interactive_detector->GetLongestInputTimestamp());
5685 }
5686
5687 // TODO(npm): Improve this test to receive real input sequences and avoid hacks.
5688 // Check that first input delay is correctly reported to the document when the
5689 // first input is a pointer down event, and we receive a pointer up event.
TEST_F(WebViewTest,PointerDownUpFirstInputDelay)5690 TEST_F(WebViewTest, PointerDownUpFirstInputDelay) {
5691 WebViewImpl* web_view = web_view_helper_.Initialize();
5692 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5693 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5694 "<html><body></body></html>", base_url);
5695 // Add an event listener for pointerdown to ensure it is not optimized out
5696 // before reaching the EventDispatcher.
5697 WebLocalFrame* frame = web_view_helper_.LocalMainFrame();
5698 frame->ExecuteScript(
5699 WebScriptSource("addEventListener('pointerdown', function() {});"));
5700
5701 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5702 ASSERT_NE(nullptr, main_frame);
5703
5704 Document* document = main_frame->GetDocument();
5705 ASSERT_NE(nullptr, document);
5706
5707 base::TimeTicks start_time = test_task_runner_->NowTicks();
5708 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(70));
5709
5710 InteractiveDetector* interactive_detector =
5711 GetTestInteractiveDetector(*document);
5712
5713 WebPointerEvent pointer_down(
5714 WebInputEvent::kPointerDown,
5715 WebPointerProperties(1, WebPointerProperties::PointerType::kTouch), 5, 5);
5716 pointer_down.SetTimeStamp(test_task_runner_->NowTicks());
5717 // Set this to the left button, needed for testing to behave properly.
5718 pointer_down.SetModifiers(WebInputEvent::kLeftButtonDown);
5719 pointer_down.button = WebPointerProperties::Button::kLeft;
5720 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(50));
5721 web_view->MainFrameWidget()->HandleInputEvent(
5722 WebCoalescedInputEvent(pointer_down));
5723
5724 // We don't know if this pointer event will result in a scroll or not, so we
5725 // can't report its delay. We don't consider a scroll to be meaningful input.
5726 EXPECT_FALSE(interactive_detector->GetFirstInputDelay().has_value());
5727
5728 // When we receive a pointer up, we report the delay of the pointer down.
5729 WebPointerEvent pointer_up(
5730 WebInputEvent::kPointerUp,
5731 WebPointerProperties(1, WebPointerProperties::PointerType::kTouch), 5, 5);
5732 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(60));
5733 pointer_up.SetTimeStamp(test_task_runner_->NowTicks());
5734 web_view->MainFrameWidget()->HandleInputEvent(
5735 WebCoalescedInputEvent(pointer_up));
5736
5737 EXPECT_NEAR(50,
5738 (*interactive_detector->GetFirstInputDelay()).InMillisecondsF(),
5739 0.01);
5740 EXPECT_EQ(70, (*interactive_detector->GetFirstInputTimestamp() - start_time)
5741 .InMillisecondsF());
5742 }
5743
5744 // We need a way for JS to advance the mock clock. Hook into console.log, so
5745 // that logging advances the clock by |event_handling_delay| seconds.
5746 class MockClockAdvancingWebFrameClient
5747 : public frame_test_helpers::TestWebFrameClient {
5748 public:
MockClockAdvancingWebFrameClient(scoped_refptr<base::TestMockTimeTaskRunner> task_runner,base::TimeDelta event_handling_delay)5749 MockClockAdvancingWebFrameClient(
5750 scoped_refptr<base::TestMockTimeTaskRunner> task_runner,
5751 base::TimeDelta event_handling_delay)
5752 : task_runner_(std::move(task_runner)),
5753 event_handling_delay_(event_handling_delay) {}
5754 // WebLocalFrameClient overrides:
DidAddMessageToConsole(const WebConsoleMessage & message,const WebString & source_name,unsigned source_line,const WebString & stack_trace)5755 void DidAddMessageToConsole(const WebConsoleMessage& message,
5756 const WebString& source_name,
5757 unsigned source_line,
5758 const WebString& stack_trace) override {
5759 task_runner_->FastForwardBy(event_handling_delay_);
5760 }
5761
5762 private:
5763 scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
5764 base::TimeDelta event_handling_delay_;
5765 };
5766
5767 // Check that the input delay is correctly reported to the document.
TEST_F(WebViewTest,FirstInputDelayExcludesProcessingTime)5768 TEST_F(WebViewTest, FirstInputDelayExcludesProcessingTime) {
5769 // Page load timing logic depends on the time not being zero.
5770 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
5771 MockClockAdvancingWebFrameClient frame_client(
5772 test_task_runner_, base::TimeDelta::FromMilliseconds(6000));
5773 WebViewImpl* web_view = web_view_helper_.Initialize(&frame_client);
5774 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5775 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5776 "<html><body></body></html>", base_url,
5777 test_task_runner_->GetMockTickClock());
5778
5779 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5780 ASSERT_NE(nullptr, main_frame);
5781
5782 Document* document = main_frame->GetDocument();
5783 ASSERT_NE(nullptr, document);
5784
5785 WebLocalFrame* frame = web_view_helper_.LocalMainFrame();
5786 // console.log will advance the mock clock.
5787 frame->ExecuteScript(
5788 WebScriptSource("document.addEventListener('keydown', "
5789 "() => {console.log('advancing timer');})"));
5790
5791 InteractiveDetector* interactive_detector =
5792 GetTestInteractiveDetector(*document);
5793
5794 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
5795 WebInputEvent::kNoModifiers,
5796 WebInputEvent::GetStaticTimeStampForTests());
5797 key_event.dom_key = ui::DomKey::FromCharacter(' ');
5798 key_event.windows_key_code = VKEY_SPACE;
5799 key_event.SetTimeStamp(test_task_runner_->NowTicks());
5800
5801 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(5000));
5802
5803 web_view->MainFrameWidget()->HandleInputEvent(
5804 WebCoalescedInputEvent(key_event));
5805
5806 EXPECT_TRUE(interactive_detector->GetFirstInputDelay().has_value());
5807 base::TimeDelta first_input_delay =
5808 *interactive_detector->GetFirstInputDelay();
5809 EXPECT_EQ(5000, first_input_delay.InMillisecondsF());
5810
5811 web_view_helper_.Reset(); // Remove dependency on locally scoped client.
5812 }
5813
5814 // Check that the longest input delay is correctly reported to the document.
TEST_F(WebViewTest,LongestInputDelayExcludesProcessingTime)5815 TEST_F(WebViewTest, LongestInputDelayExcludesProcessingTime) {
5816 // Page load timing logic depends on the time not being zero.
5817 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(1));
5818 MockClockAdvancingWebFrameClient frame_client(
5819 test_task_runner_, base::TimeDelta::FromMilliseconds(6000));
5820 WebViewImpl* web_view = web_view_helper_.Initialize(&frame_client);
5821 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
5822 frame_test_helpers::LoadHTMLString(web_view->MainFrameImpl(),
5823 "<html><body></body></html>", base_url);
5824
5825 LocalFrame* main_frame = web_view->MainFrameImpl()->GetFrame();
5826 ASSERT_NE(nullptr, main_frame);
5827
5828 Document* document = main_frame->GetDocument();
5829 ASSERT_NE(nullptr, document);
5830
5831 WebLocalFrame* frame = web_view_helper_.LocalMainFrame();
5832 // console.log will advance the mock clock.
5833 frame->ExecuteScript(
5834 WebScriptSource("document.addEventListener('keydown', "
5835 "() => {console.log('advancing timer');})"));
5836
5837 InteractiveDetector* interactive_detector =
5838 GetTestInteractiveDetector(*document);
5839
5840 WebKeyboardEvent key_event(WebInputEvent::kRawKeyDown,
5841 WebInputEvent::kNoModifiers,
5842 WebInputEvent::GetStaticTimeStampForTests());
5843 key_event.dom_key = ui::DomKey::FromCharacter(' ');
5844 key_event.windows_key_code = VKEY_SPACE;
5845 key_event.SetTimeStamp(test_task_runner_->NowTicks());
5846
5847 test_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(5000));
5848
5849 web_view->MainFrameWidget()->HandleInputEvent(
5850 WebCoalescedInputEvent(key_event));
5851
5852 base::TimeDelta longest_input_delay =
5853 *interactive_detector->GetLongestInputDelay();
5854 EXPECT_EQ(5000, longest_input_delay.InMillisecondsF());
5855
5856 web_view_helper_.Reset(); // Remove dependency on locally scoped client.
5857 }
5858
TEST_F(WebViewTest,RootLayerAttachment)5859 TEST_F(WebViewTest, RootLayerAttachment) {
5860 WebView* web_view = web_view_helper_.InitializeAndLoad("about:blank");
5861
5862 // Do a lifecycle update that includes compositing but not paint. Hit test
5863 // events are an example of a real case where this occurs
5864 // (see: WebViewTest::ClientTapHandling).
5865 web_view->MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kPrePaint,
5866 DocumentUpdateReason::kTest);
5867
5868 // Layers (including the root layer) should not be attached until the paint
5869 // lifecycle phase.
5870 cc::LayerTreeHost* layer_tree_host = web_view_helper_.GetLayerTreeHost();
5871 EXPECT_FALSE(layer_tree_host->root_layer());
5872
5873 // Do a full lifecycle update and ensure that the root layer has been added.
5874 web_view->MainFrameWidget()->UpdateLifecycle(WebLifecycleUpdate::kAll,
5875 DocumentUpdateReason::kTest);
5876 EXPECT_TRUE(layer_tree_host->root_layer());
5877 }
5878
5879 // Verifies that we emit Blink.UseCounter.FeaturePolicy.PotentialAnimation for
5880 // CSS and JS animations in a document.
TEST_F(WebViewTest,PotentialViolationReportsForLayoutAnimations)5881 TEST_F(WebViewTest, PotentialViolationReportsForLayoutAnimations) {
5882 const char* kHistogramName =
5883 "Blink.UseCounter.FeaturePolicy.PotentialViolation";
5884 WebViewImpl* web_view = web_view_helper_.Initialize();
5885 // A page with non-violating animation does not generate report.
5886 WebURL base_url_no_violation =
5887 url_test_helpers::ToKURL("http://good-css.example.com/");
5888 frame_test_helpers::LoadHTMLString(
5889 web_view->MainFrameImpl(),
5890 "<html><head><style>@keyframes foo {from "
5891 "{color: blue;} to {color: red}}</style></head></html>",
5892 base_url_no_violation);
5893 HistogramTester histogram_tester;
5894 histogram_tester.ExpectTotalCount(kHistogramName, 0);
5895 // Page with 2 potential (CSS) layout-animation violations.
5896 WebURL base_url_css_violations =
5897 url_test_helpers::ToKURL("http://bad-css.example.com/");
5898 frame_test_helpers::LoadHTMLString(
5899 web_view->MainFrameImpl(),
5900 "<html><head><style>@keyframes bar {"
5901 "from{height: 100px;} to {height: 200px;}}"
5902 "@keyframes baz {from{top: 100px;} to {top: 200px;}}"
5903 "</style></head></html>",
5904 base_url_css_violations);
5905 histogram_tester.ExpectTotalCount(kHistogramName, 1);
5906 // Page with a JS layout-animations violation.
5907 WebURL base_url_js_violations =
5908 url_test_helpers::ToKURL("http://js.example.com/");
5909 frame_test_helpers::LoadHTMLString(
5910 web_view->MainFrameImpl(),
5911 "<html><body><div></div><script>document.body.firstChild.animate("
5912 "{top: '100px'});</script></body></html>",
5913 base_url_js_violations);
5914 histogram_tester.ExpectTotalCount(kHistogramName, 2);
5915 }
5916
TEST_F(WebViewTest,ForceDarkModeInvalidatesPaint)5917 TEST_F(WebViewTest, ForceDarkModeInvalidatesPaint) {
5918 WebViewImpl* web_view = web_view_helper_.Initialize();
5919 web_view->MainFrameWidget()->Resize(WebSize(500, 500));
5920 UpdateAllLifecyclePhases();
5921
5922 Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
5923 ASSERT_TRUE(document);
5924 web_view->GetSettings()->SetForceDarkModeEnabled(true);
5925 EXPECT_TRUE(document->GetLayoutView()->ShouldDoFullPaintInvalidation());
5926 }
5927
5928 // Regression test for https://crbug.com/1012068
TEST_F(WebViewTest,LongPressImageAndThenLongTapImage)5929 TEST_F(WebViewTest, LongPressImageAndThenLongTapImage) {
5930 RegisterMockedHttpURLLoad("long_press_image.html");
5931
5932 WebViewImpl* web_view =
5933 web_view_helper_.InitializeAndLoad(base_url_ + "long_press_image.html");
5934 web_view->SettingsImpl()->SetAlwaysShowContextMenuOnTouch(false);
5935 web_view->MainFrameWidget()->Resize(WebSize(500, 300));
5936 UpdateAllLifecyclePhases();
5937 RunPendingTasks();
5938
5939 WebGestureEvent event(WebInputEvent::kGestureLongPress,
5940 WebInputEvent::kNoModifiers,
5941 WebInputEvent::GetStaticTimeStampForTests(),
5942 WebGestureDevice::kTouchscreen);
5943 event.SetPositionInWidget(gfx::PointF(10, 10));
5944
5945 EXPECT_EQ(WebInputEventResult::kHandledSystem,
5946 web_view->MainFrameWidget()->HandleInputEvent(
5947 WebCoalescedInputEvent(event)));
5948 EXPECT_TRUE(
5949 web_view->AsView()
5950 .page->GetContextMenuController()
5951 .ContextMenuNodeForFrame(web_view->MainFrameImpl()->GetFrame()));
5952
5953 WebGestureEvent tap_event(WebInputEvent::kGestureLongTap,
5954 WebInputEvent::kNoModifiers,
5955 WebInputEvent::GetStaticTimeStampForTests(),
5956 WebGestureDevice::kTouchscreen);
5957 tap_event.SetPositionInWidget(gfx::PointF(10, 10));
5958
5959 EXPECT_EQ(WebInputEventResult::kNotHandled,
5960 web_view->MainFrameWidget()->HandleInputEvent(
5961 WebCoalescedInputEvent(tap_event)));
5962 EXPECT_TRUE(
5963 web_view->AsView()
5964 .page->GetContextMenuController()
5965 .ContextMenuNodeForFrame(web_view->MainFrameImpl()->GetFrame()));
5966 }
5967
5968 } // namespace blink
5969