| /* |
| * Copyright (C) 2011, 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "public/web/WebView.h" |
| |
| #include "bindings/core/v8/V8Document.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/Element.h" |
| #include "core/editing/FrameSelection.h" |
| #include "core/editing/InputMethodController.h" |
| #include "core/editing/markers/DocumentMarkerController.h" |
| #include "core/frame/EventHandlerRegistry.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/frame/Settings.h" |
| #include "core/frame/VisualViewport.h" |
| #include "core/html/HTMLIFrameElement.h" |
| #include "core/html/HTMLInputElement.h" |
| #include "core/html/HTMLTextAreaElement.h" |
| #include "core/layout/api/LayoutViewItem.h" |
| #include "core/loader/DocumentLoader.h" |
| #include "core/loader/FrameLoadRequest.h" |
| #include "core/page/Page.h" |
| #include "core/page/ScopedPageSuspender.h" |
| #include "core/paint/PaintLayer.h" |
| #include "core/paint/PaintLayerPainter.h" |
| #include "core/timing/DOMWindowPerformance.h" |
| #include "core/timing/Performance.h" |
| #include "platform/KeyboardCodes.h" |
| #include "platform/UserGestureIndicator.h" |
| #include "platform/geometry/IntRect.h" |
| #include "platform/geometry/IntSize.h" |
| #include "platform/graphics/Color.h" |
| #include "platform/graphics/GraphicsContext.h" |
| #include "platform/graphics/paint/SkPictureBuilder.h" |
| #include "platform/scroll/ScrollTypes.h" |
| #include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h" |
| #include "platform/testing/URLTestHelpers.h" |
| #include "platform/testing/UnitTestHelpers.h" |
| #include "public/platform/Platform.h" |
| #include "public/platform/WebDisplayMode.h" |
| #include "public/platform/WebDragData.h" |
| #include "public/platform/WebDragOperation.h" |
| #include "public/platform/WebFloatPoint.h" |
| #include "public/platform/WebInputEvent.h" |
| #include "public/platform/WebLayerTreeView.h" |
| #include "public/platform/WebMockClipboard.h" |
| #include "public/platform/WebSize.h" |
| #include "public/platform/WebThread.h" |
| #include "public/platform/WebURLLoaderMockFactory.h" |
| #include "public/web/WebAutofillClient.h" |
| #include "public/web/WebCache.h" |
| #include "public/web/WebDateTimeChooserCompletion.h" |
| #include "public/web/WebDeviceEmulationParams.h" |
| #include "public/web/WebDocument.h" |
| #include "public/web/WebElement.h" |
| #include "public/web/WebFrame.h" |
| #include "public/web/WebFrameClient.h" |
| #include "public/web/WebFrameContentDumper.h" |
| #include "public/web/WebHitTestResult.h" |
| #include "public/web/WebInputMethodController.h" |
| #include "public/web/WebScriptSource.h" |
| #include "public/web/WebSettings.h" |
| #include "public/web/WebTreeScopeType.h" |
| #include "public/web/WebViewClient.h" |
| #include "public/web/WebWidget.h" |
| #include "public/web/WebWidgetClient.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "web/DevToolsEmulator.h" |
| #include "web/WebInputMethodControllerImpl.h" |
| #include "web/WebLocalFrameImpl.h" |
| #include "web/WebSettingsImpl.h" |
| #include "web/WebViewImpl.h" |
| #include "web/tests/FrameTestHelpers.h" |
| #include "wtf/PtrUtil.h" |
| #include <memory> |
| |
| #if OS(MACOSX) |
| #include "public/web/mac/WebSubstringUtil.h" |
| #endif |
| |
| using blink::FrameTestHelpers::loadFrame; |
| using blink::URLTestHelpers::toKURL; |
| using blink::URLTestHelpers::registerMockedURLLoad; |
| using blink::testing::runPendingTasks; |
| |
| namespace blink { |
| |
| enum HorizontalScrollbarState { |
| NoHorizontalScrollbar, |
| VisibleHorizontalScrollbar, |
| }; |
| |
| enum VerticalScrollbarState { |
| NoVerticalScrollbar, |
| VisibleVerticalScrollbar, |
| }; |
| |
| class TestData { |
| public: |
| void setWebView(WebView* webView) { m_webView = toWebViewImpl(webView); } |
| void setSize(const WebSize& newSize) { m_size = newSize; } |
| HorizontalScrollbarState getHorizontalScrollbarState() const { |
| return m_webView->hasHorizontalScrollbar() ? VisibleHorizontalScrollbar |
| : NoHorizontalScrollbar; |
| } |
| VerticalScrollbarState getVerticalScrollbarState() const { |
| return m_webView->hasVerticalScrollbar() ? VisibleVerticalScrollbar |
| : NoVerticalScrollbar; |
| } |
| int width() const { return m_size.width; } |
| int height() const { return m_size.height; } |
| |
| private: |
| WebSize m_size; |
| WebViewImpl* m_webView; |
| }; |
| |
| class AutoResizeWebViewClient : public FrameTestHelpers::TestWebViewClient { |
| public: |
| // WebViewClient methods |
| void didAutoResize(const WebSize& newSize) override { |
| m_testData.setSize(newSize); |
| } |
| |
| // Local methods |
| TestData& testData() { return m_testData; } |
| |
| private: |
| TestData m_testData; |
| }; |
| |
| class TapHandlingWebViewClient : public FrameTestHelpers::TestWebViewClient { |
| public: |
| // WebViewClient methods |
| void didHandleGestureEvent(const WebGestureEvent& event, |
| bool eventCancelled) override { |
| if (event.type == WebInputEvent::GestureTap) { |
| m_tapX = event.x; |
| m_tapY = event.y; |
| } else if (event.type == WebInputEvent::GestureLongPress) { |
| m_longpressX = event.x; |
| m_longpressY = event.y; |
| } |
| } |
| |
| // Local methods |
| void reset() { |
| m_tapX = -1; |
| m_tapY = -1; |
| m_longpressX = -1; |
| m_longpressY = -1; |
| } |
| int tapX() { return m_tapX; } |
| int tapY() { return m_tapY; } |
| int longpressX() { return m_longpressX; } |
| int longpressY() { return m_longpressY; } |
| |
| private: |
| int m_tapX; |
| int m_tapY; |
| int m_longpressX; |
| int m_longpressY; |
| }; |
| |
| class DateTimeChooserWebViewClient |
| : public FrameTestHelpers::TestWebViewClient { |
| public: |
| WebDateTimeChooserCompletion* chooserCompletion() { |
| return m_chooserCompletion; |
| } |
| |
| void clearChooserCompletion() { m_chooserCompletion = 0; } |
| |
| // WebViewClient methods |
| bool openDateTimeChooser( |
| const WebDateTimeChooserParams&, |
| WebDateTimeChooserCompletion* chooser_completion) override { |
| m_chooserCompletion = chooser_completion; |
| return true; |
| } |
| |
| private: |
| WebDateTimeChooserCompletion* m_chooserCompletion; |
| }; |
| |
| typedef bool TestParamRootLayerScrolling; |
| class WebViewTest |
| : public ::testing::Test, |
| public ::testing::WithParamInterface<TestParamRootLayerScrolling>, |
| private ScopedRootLayerScrollingForTest { |
| public: |
| WebViewTest() |
| : ScopedRootLayerScrollingForTest(GetParam()), |
| m_baseURL("http://www.test.com/") {} |
| |
| void TearDown() override { |
| Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs(); |
| WebCache::clear(); |
| } |
| |
| protected: |
| void registerMockedHttpURLLoad(const std::string& fileName) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8(fileName.c_str())); |
| } |
| |
| void testAutoResize(const WebSize& minAutoResize, |
| const WebSize& maxAutoResize, |
| const std::string& pageWidth, |
| const std::string& pageHeight, |
| int expectedWidth, |
| int expectedHeight, |
| HorizontalScrollbarState expectedHorizontalState, |
| VerticalScrollbarState expectedVerticalState); |
| |
| void testTextInputType(WebTextInputType expectedType, |
| const std::string& htmlFile); |
| void testInputMode(WebTextInputMode expectedInputMode, |
| const std::string& htmlFile); |
| bool tapElement(WebInputEvent::Type, Element*); |
| bool tapElementById(WebInputEvent::Type, const WebString& id); |
| |
| std::string m_baseURL; |
| FrameTestHelpers::WebViewHelper m_webViewHelper; |
| }; |
| |
| static bool hitTestIsContentEditable(WebView* view, int x, int y) { |
| WebPoint hitPoint(x, y); |
| WebHitTestResult hitTestResult = view->hitTestResultAt(hitPoint); |
| return hitTestResult.isContentEditable(); |
| } |
| |
| static std::string hitTestElementId(WebView* view, int x, int y) { |
| WebPoint hitPoint(x, y); |
| WebHitTestResult hitTestResult = view->hitTestResultAt(hitPoint); |
| return hitTestResult.node().to<WebElement>().getAttribute("id").utf8(); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(All, WebViewTest, ::testing::Bool()); |
| |
| TEST_P(WebViewTest, HitTestContentEditableImageMaps) { |
| std::string url = m_baseURL + "content-editable-image-maps.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), |
| "content-editable-image-maps.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url, true, 0); |
| webView->resize(WebSize(500, 500)); |
| |
| EXPECT_EQ("areaANotEditable", hitTestElementId(webView, 25, 25)); |
| EXPECT_FALSE(hitTestIsContentEditable(webView, 25, 25)); |
| EXPECT_EQ("imageANotEditable", hitTestElementId(webView, 75, 25)); |
| EXPECT_FALSE(hitTestIsContentEditable(webView, 75, 25)); |
| |
| EXPECT_EQ("areaBNotEditable", hitTestElementId(webView, 25, 125)); |
| EXPECT_FALSE(hitTestIsContentEditable(webView, 25, 125)); |
| EXPECT_EQ("imageBEditable", hitTestElementId(webView, 75, 125)); |
| EXPECT_TRUE(hitTestIsContentEditable(webView, 75, 125)); |
| |
| EXPECT_EQ("areaCNotEditable", hitTestElementId(webView, 25, 225)); |
| EXPECT_FALSE(hitTestIsContentEditable(webView, 25, 225)); |
| EXPECT_EQ("imageCNotEditable", hitTestElementId(webView, 75, 225)); |
| EXPECT_FALSE(hitTestIsContentEditable(webView, 75, 225)); |
| |
| EXPECT_EQ("areaDEditable", hitTestElementId(webView, 25, 325)); |
| EXPECT_TRUE(hitTestIsContentEditable(webView, 25, 325)); |
| EXPECT_EQ("imageDNotEditable", hitTestElementId(webView, 75, 325)); |
| EXPECT_FALSE(hitTestIsContentEditable(webView, 75, 325)); |
| } |
| |
| static std::string hitTestAbsoluteUrl(WebView* view, int x, int y) { |
| WebPoint hitPoint(x, y); |
| WebHitTestResult hitTestResult = view->hitTestResultAt(hitPoint); |
| return hitTestResult.absoluteImageURL().string().utf8(); |
| } |
| |
| static WebElement hitTestUrlElement(WebView* view, int x, int y) { |
| WebPoint hitPoint(x, y); |
| WebHitTestResult hitTestResult = view->hitTestResultAt(hitPoint); |
| return hitTestResult.urlElement(); |
| } |
| |
| TEST_P(WebViewTest, ImageMapUrls) { |
| std::string url = m_baseURL + "image-map.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "image-map.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url, true, 0); |
| webView->resize(WebSize(400, 400)); |
| |
| std::string imageUrl = |
| "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="; |
| |
| EXPECT_EQ("area", hitTestElementId(webView, 25, 25)); |
| EXPECT_EQ("area", |
| hitTestUrlElement(webView, 25, 25).getAttribute("id").utf8()); |
| EXPECT_EQ(imageUrl, hitTestAbsoluteUrl(webView, 25, 25)); |
| |
| EXPECT_EQ("image", hitTestElementId(webView, 75, 25)); |
| EXPECT_TRUE(hitTestUrlElement(webView, 75, 25).isNull()); |
| EXPECT_EQ(imageUrl, hitTestAbsoluteUrl(webView, 75, 25)); |
| } |
| |
| TEST_P(WebViewTest, BrokenImage) { |
| URLTestHelpers::registerMockedErrorURLLoad( |
| KURL(toKURL(m_baseURL), "non_existent.png")); |
| std::string url = m_baseURL + "image-broken.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "image-broken.html"); |
| |
| WebView* webView = m_webViewHelper.initialize(); |
| webView->settings()->setLoadsImagesAutomatically(true); |
| loadFrame(webView->mainFrame(), url); |
| webView->resize(WebSize(400, 400)); |
| |
| std::string imageUrl = "http://www.test.com/non_existent.png"; |
| |
| EXPECT_EQ("image", hitTestElementId(webView, 25, 25)); |
| EXPECT_TRUE(hitTestUrlElement(webView, 25, 25).isNull()); |
| EXPECT_EQ(imageUrl, hitTestAbsoluteUrl(webView, 25, 25)); |
| } |
| |
| TEST_P(WebViewTest, BrokenInputImage) { |
| URLTestHelpers::registerMockedErrorURLLoad( |
| KURL(toKURL(m_baseURL), "non_existent.png")); |
| std::string url = m_baseURL + "input-image-broken.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "input-image-broken.html"); |
| |
| WebView* webView = m_webViewHelper.initialize(); |
| webView->settings()->setLoadsImagesAutomatically(true); |
| loadFrame(webView->mainFrame(), url); |
| webView->resize(WebSize(400, 400)); |
| |
| std::string imageUrl = "http://www.test.com/non_existent.png"; |
| |
| EXPECT_EQ("image", hitTestElementId(webView, 25, 25)); |
| EXPECT_TRUE(hitTestUrlElement(webView, 25, 25).isNull()); |
| EXPECT_EQ(imageUrl, hitTestAbsoluteUrl(webView, 25, 25)); |
| } |
| |
| TEST_P(WebViewTest, SetBaseBackgroundColor) { |
| const WebColor kWhite = 0xFFFFFFFF; |
| const WebColor kBlue = 0xFF0000FF; |
| const WebColor kDarkCyan = 0xFF227788; |
| const WebColor kTranslucentPutty = 0x80BFB196; |
| const WebColor kTransparent = 0x00000000; |
| |
| WebViewImpl* webView = m_webViewHelper.initialize(); |
| EXPECT_EQ(kWhite, webView->backgroundColor()); |
| |
| webView->setBaseBackgroundColor(kBlue); |
| EXPECT_EQ(kBlue, webView->backgroundColor()); |
| |
| WebURL baseURL = URLTestHelpers::toKURL("http://example.com/"); |
| FrameTestHelpers::loadHTMLString(webView->mainFrame(), |
| "<html><head><style>body " |
| "{background-color:#227788}</style></head></" |
| "html>", |
| baseURL); |
| EXPECT_EQ(kDarkCyan, webView->backgroundColor()); |
| |
| FrameTestHelpers::loadHTMLString(webView->mainFrame(), |
| "<html><head><style>body " |
| "{background-color:rgba(255,0,0,0.5)}</" |
| "style></head></html>", |
| baseURL); |
| // Expected: red (50% alpha) blended atop base of kBlue. |
| EXPECT_EQ(0xFF7F0080, webView->backgroundColor()); |
| |
| webView->setBaseBackgroundColor(kTranslucentPutty); |
| // Expected: red (50% alpha) blended atop kTranslucentPutty. Note the alpha. |
| EXPECT_EQ(0xBFE93B32, webView->backgroundColor()); |
| |
| webView->setBaseBackgroundColor(kTransparent); |
| FrameTestHelpers::loadHTMLString(webView->mainFrame(), |
| "<html><head><style>body " |
| "{background-color:transparent}</style></" |
| "head></html>", |
| baseURL); |
| // Expected: transparent on top of kTransparent will still be transparent. |
| EXPECT_EQ(kTransparent, webView->backgroundColor()); |
| |
| LocalFrame* frame = webView->mainFrameImpl()->frame(); |
| // The shutdown() calls are a hack to prevent this test |
| // from violating invariants about frame state during navigation/detach. |
| frame->document()->shutdown(); |
| |
| // Creating a new frame view with the background color having 0 alpha. |
| frame->createView(IntSize(1024, 768), Color::transparent, true); |
| EXPECT_EQ(kTransparent, frame->view()->baseBackgroundColor()); |
| frame->view()->dispose(); |
| |
| const Color transparentRed(100, 0, 0, 0); |
| frame->createView(IntSize(1024, 768), transparentRed, true); |
| EXPECT_EQ(transparentRed, frame->view()->baseBackgroundColor()); |
| frame->view()->dispose(); |
| } |
| |
| TEST_P(WebViewTest, SetBaseBackgroundColorBeforeMainFrame) { |
| const WebColor kBlue = 0xFF0000FF; |
| FrameTestHelpers::TestWebViewClient webViewClient; |
| WebViewImpl* webView = |
| WebViewImpl::create(&webViewClient, WebPageVisibilityStateVisible); |
| EXPECT_NE(kBlue, webView->backgroundColor()); |
| // webView does not have a frame yet, but we should still be able to set the |
| // background color. |
| webView->setBaseBackgroundColor(kBlue); |
| EXPECT_EQ(kBlue, webView->backgroundColor()); |
| FrameTestHelpers::TestWebFrameClient webFrameClient; |
| WebLocalFrame* frame = |
| WebLocalFrame::create(WebTreeScopeType::Document, &webFrameClient); |
| webView->setMainFrame(frame); |
| webView->close(); |
| } |
| |
| TEST_P(WebViewTest, SetBaseBackgroundColorAndBlendWithExistingContent) { |
| const WebColor kAlphaRed = 0x80FF0000; |
| const WebColor kAlphaGreen = 0x8000FF00; |
| const int kWidth = 100; |
| const int kHeight = 100; |
| |
| WebViewImpl* webView = m_webViewHelper.initialize(); |
| |
| // Set WebView background to green with alpha. |
| webView->setBaseBackgroundColor(kAlphaGreen); |
| webView->settings()->setShouldClearDocumentBackground(false); |
| webView->resize(WebSize(kWidth, kHeight)); |
| webView->updateAllLifecyclePhases(); |
| |
| // Set canvas background to red with alpha. |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(kWidth, kHeight); |
| SkCanvas canvas(bitmap); |
| canvas.clear(kAlphaRed); |
| |
| SkPictureBuilder pictureBuilder(FloatRect(0, 0, kWidth, kHeight)); |
| |
| // Paint the root of the main frame in the way that CompositedLayerMapping |
| // would. |
| FrameView* view = m_webViewHelper.webView()->mainFrameImpl()->frameView(); |
| PaintLayer* rootLayer = view->layoutViewItem().layer(); |
| LayoutRect paintRect(0, 0, kWidth, kHeight); |
| PaintLayerPaintingInfo paintingInfo(rootLayer, paintRect, |
| GlobalPaintNormalPhase, LayoutSize()); |
| PaintLayerPainter(*rootLayer) |
| .paintLayerContents(pictureBuilder.context(), paintingInfo, |
| PaintLayerPaintingCompositingAllPhases); |
| |
| pictureBuilder.endRecording()->playback(&canvas); |
| |
| // The result should be a blend of red and green. |
| SkColor color = bitmap.getColor(kWidth / 2, kHeight / 2); |
| EXPECT_TRUE(redChannel(color)); |
| EXPECT_TRUE(greenChannel(color)); |
| } |
| |
| TEST_P(WebViewTest, FocusIsInactive) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), "visible_iframe.html"); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "visible_iframe.html"); |
| |
| webView->setFocus(true); |
| webView->setIsActive(true); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| EXPECT_TRUE(frame->frame()->document()->isHTMLDocument()); |
| |
| Document* document = frame->frame()->document(); |
| EXPECT_TRUE(document->hasFocus()); |
| webView->setFocus(false); |
| webView->setIsActive(false); |
| EXPECT_FALSE(document->hasFocus()); |
| webView->setFocus(true); |
| webView->setIsActive(true); |
| EXPECT_TRUE(document->hasFocus()); |
| webView->setFocus(true); |
| webView->setIsActive(false); |
| EXPECT_FALSE(document->hasFocus()); |
| webView->setFocus(false); |
| webView->setIsActive(true); |
| EXPECT_FALSE(document->hasFocus()); |
| } |
| |
| TEST_P(WebViewTest, ActiveState) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), "visible_iframe.html"); |
| WebView* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "visible_iframe.html"); |
| |
| ASSERT_TRUE(webView); |
| |
| webView->setIsActive(true); |
| EXPECT_TRUE(webView->isActive()); |
| |
| webView->setIsActive(false); |
| EXPECT_FALSE(webView->isActive()); |
| |
| webView->setIsActive(true); |
| EXPECT_TRUE(webView->isActive()); |
| } |
| |
| TEST_P(WebViewTest, HitTestResultAtWithPageScale) { |
| std::string url = m_baseURL + "specify_size.html?" + "50px" + ":" + "50px"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "specify_size.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url, true, 0); |
| webView->resize(WebSize(100, 100)); |
| WebPoint hitPoint(75, 75); |
| |
| // Image is at top left quandrant, so should not hit it. |
| WebHitTestResult negativeResult = webView->hitTestResultAt(hitPoint); |
| EXPECT_FALSE(negativeResult.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult.reset(); |
| |
| // Scale page up 2x so image should occupy the whole viewport. |
| webView->setPageScaleFactor(2.0f); |
| WebHitTestResult positiveResult = webView->hitTestResultAt(hitPoint); |
| EXPECT_TRUE(positiveResult.node().to<WebElement>().hasHTMLTagName("img")); |
| positiveResult.reset(); |
| } |
| |
| TEST_P(WebViewTest, HitTestResultAtWithPageScaleAndPan) { |
| std::string url = m_baseURL + "specify_size.html?" + "50px" + ":" + "50px"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "specify_size.html"); |
| WebView* webView = m_webViewHelper.initialize(true); |
| loadFrame(webView->mainFrame(), url); |
| webView->resize(WebSize(100, 100)); |
| WebPoint hitPoint(75, 75); |
| |
| // Image is at top left quandrant, so should not hit it. |
| WebHitTestResult negativeResult = webView->hitTestResultAt(hitPoint); |
| EXPECT_FALSE(negativeResult.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult.reset(); |
| |
| // Scale page up 2x so image should occupy the whole viewport. |
| webView->setPageScaleFactor(2.0f); |
| WebHitTestResult positiveResult = webView->hitTestResultAt(hitPoint); |
| EXPECT_TRUE(positiveResult.node().to<WebElement>().hasHTMLTagName("img")); |
| positiveResult.reset(); |
| |
| // Pan around the zoomed in page so the image is not visible in viewport. |
| webView->setVisualViewportOffset(WebFloatPoint(100, 100)); |
| WebHitTestResult negativeResult2 = webView->hitTestResultAt(hitPoint); |
| EXPECT_FALSE(negativeResult2.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult2.reset(); |
| } |
| |
| TEST_P(WebViewTest, HitTestResultForTapWithTapArea) { |
| std::string url = m_baseURL + "hit_test.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "hit_test.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url, true, 0); |
| webView->resize(WebSize(100, 100)); |
| WebPoint hitPoint(55, 55); |
| |
| // Image is at top left quandrant, so should not hit it. |
| WebHitTestResult negativeResult = webView->hitTestResultAt(hitPoint); |
| EXPECT_FALSE(negativeResult.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult.reset(); |
| |
| // The tap area is 20 by 20 square, centered at 55, 55. |
| WebSize tapArea(20, 20); |
| WebHitTestResult positiveResult = |
| webView->hitTestResultForTap(hitPoint, tapArea); |
| EXPECT_TRUE(positiveResult.node().to<WebElement>().hasHTMLTagName("img")); |
| positiveResult.reset(); |
| |
| // Move the hit point the image is just outside the tapped area now. |
| hitPoint = WebPoint(61, 61); |
| WebHitTestResult negativeResult2 = |
| webView->hitTestResultForTap(hitPoint, tapArea); |
| EXPECT_FALSE(negativeResult2.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult2.reset(); |
| } |
| |
| TEST_P(WebViewTest, HitTestResultForTapWithTapAreaPageScaleAndPan) { |
| std::string url = m_baseURL + "hit_test.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "hit_test.html"); |
| WebView* webView = m_webViewHelper.initialize(true); |
| loadFrame(webView->mainFrame(), url); |
| webView->resize(WebSize(100, 100)); |
| WebPoint hitPoint(55, 55); |
| |
| // Image is at top left quandrant, so should not hit it. |
| WebHitTestResult negativeResult = webView->hitTestResultAt(hitPoint); |
| EXPECT_FALSE(negativeResult.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult.reset(); |
| |
| // The tap area is 20 by 20 square, centered at 55, 55. |
| WebSize tapArea(20, 20); |
| WebHitTestResult positiveResult = |
| webView->hitTestResultForTap(hitPoint, tapArea); |
| EXPECT_TRUE(positiveResult.node().to<WebElement>().hasHTMLTagName("img")); |
| positiveResult.reset(); |
| |
| // Zoom in and pan around the page so the image is not visible in viewport. |
| webView->setPageScaleFactor(2.0f); |
| webView->setVisualViewportOffset(WebFloatPoint(100, 100)); |
| WebHitTestResult negativeResult2 = |
| webView->hitTestResultForTap(hitPoint, tapArea); |
| EXPECT_FALSE(negativeResult2.node().to<WebElement>().hasHTMLTagName("img")); |
| negativeResult2.reset(); |
| } |
| |
| void WebViewTest::testAutoResize( |
| const WebSize& minAutoResize, |
| const WebSize& maxAutoResize, |
| const std::string& pageWidth, |
| const std::string& pageHeight, |
| int expectedWidth, |
| int expectedHeight, |
| HorizontalScrollbarState expectedHorizontalState, |
| VerticalScrollbarState expectedVerticalState) { |
| AutoResizeWebViewClient client; |
| std::string url = |
| m_baseURL + "specify_size.html?" + pageWidth + ":" + pageHeight; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "specify_size.html"); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(url, true, 0, &client); |
| client.testData().setWebView(webView); |
| |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| FrameView* frameView = frame->frame()->view(); |
| frameView->layout(); |
| EXPECT_FALSE(frameView->layoutPending()); |
| EXPECT_FALSE(frameView->needsLayout()); |
| |
| webView->enableAutoResizeMode(minAutoResize, maxAutoResize); |
| EXPECT_TRUE(frameView->layoutPending()); |
| EXPECT_TRUE(frameView->needsLayout()); |
| frameView->layout(); |
| |
| EXPECT_TRUE(frame->frame()->document()->isHTMLDocument()); |
| |
| EXPECT_EQ(expectedWidth, client.testData().width()); |
| EXPECT_EQ(expectedHeight, client.testData().height()); |
| |
| // Android disables main frame scrollbars. |
| #if !OS(ANDROID) |
| EXPECT_EQ(expectedHorizontalState, |
| client.testData().getHorizontalScrollbarState()); |
| EXPECT_EQ(expectedVerticalState, |
| client.testData().getVerticalScrollbarState()); |
| #endif |
| |
| // Explicitly reset to break dependency on locally scoped client. |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, AutoResizeMinimumSize) { |
| WebSize minAutoResize(91, 56); |
| WebSize maxAutoResize(403, 302); |
| std::string pageWidth = "91px"; |
| std::string pageHeight = "56px"; |
| int expectedWidth = 91; |
| int expectedHeight = 56; |
| testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, |
| expectedWidth, expectedHeight, NoHorizontalScrollbar, |
| NoVerticalScrollbar); |
| } |
| |
| TEST_P(WebViewTest, AutoResizeHeightOverflowAndFixedWidth) { |
| WebSize minAutoResize(90, 95); |
| WebSize maxAutoResize(90, 100); |
| std::string pageWidth = "60px"; |
| std::string pageHeight = "200px"; |
| int expectedWidth = 90; |
| int expectedHeight = 100; |
| testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, |
| expectedWidth, expectedHeight, NoHorizontalScrollbar, |
| VisibleVerticalScrollbar); |
| } |
| |
| TEST_P(WebViewTest, AutoResizeFixedHeightAndWidthOverflow) { |
| WebSize minAutoResize(90, 100); |
| WebSize maxAutoResize(200, 100); |
| std::string pageWidth = "300px"; |
| std::string pageHeight = "80px"; |
| int expectedWidth = 200; |
| int expectedHeight = 100; |
| testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, |
| expectedWidth, expectedHeight, VisibleHorizontalScrollbar, |
| NoVerticalScrollbar); |
| } |
| |
| // Next three tests disabled for https://bugs.webkit.org/show_bug.cgi?id=92318 . |
| // It seems we can run three AutoResize tests, then the next one breaks. |
| TEST_P(WebViewTest, AutoResizeInBetweenSizes) { |
| WebSize minAutoResize(90, 95); |
| WebSize maxAutoResize(200, 300); |
| std::string pageWidth = "100px"; |
| std::string pageHeight = "200px"; |
| int expectedWidth = 100; |
| int expectedHeight = 200; |
| testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, |
| expectedWidth, expectedHeight, NoHorizontalScrollbar, |
| NoVerticalScrollbar); |
| } |
| |
| TEST_P(WebViewTest, AutoResizeOverflowSizes) { |
| WebSize minAutoResize(90, 95); |
| WebSize maxAutoResize(200, 300); |
| std::string pageWidth = "300px"; |
| std::string pageHeight = "400px"; |
| int expectedWidth = 200; |
| int expectedHeight = 300; |
| testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, |
| expectedWidth, expectedHeight, VisibleHorizontalScrollbar, |
| VisibleVerticalScrollbar); |
| } |
| |
| TEST_P(WebViewTest, AutoResizeMaxSize) { |
| WebSize minAutoResize(90, 95); |
| WebSize maxAutoResize(200, 300); |
| std::string pageWidth = "200px"; |
| std::string pageHeight = "300px"; |
| int expectedWidth = 200; |
| int expectedHeight = 300; |
| testAutoResize(minAutoResize, maxAutoResize, pageWidth, pageHeight, |
| expectedWidth, expectedHeight, NoHorizontalScrollbar, |
| NoVerticalScrollbar); |
| } |
| |
| void WebViewTest::testTextInputType(WebTextInputType expectedType, |
| const std::string& htmlFile) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8(htmlFile.c_str())); |
| WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + htmlFile); |
| EXPECT_EQ(WebTextInputTypeNone, webView->textInputType()); |
| EXPECT_EQ(WebTextInputTypeNone, webView->textInputInfo().type); |
| webView->setInitialFocus(false); |
| EXPECT_EQ(expectedType, webView->textInputType()); |
| EXPECT_EQ(expectedType, webView->textInputInfo().type); |
| webView->clearFocusedElement(); |
| EXPECT_EQ(WebTextInputTypeNone, webView->textInputType()); |
| EXPECT_EQ(WebTextInputTypeNone, webView->textInputInfo().type); |
| } |
| |
| TEST_P(WebViewTest, TextInputType) { |
| testTextInputType(WebTextInputTypeText, "input_field_default.html"); |
| testTextInputType(WebTextInputTypePassword, "input_field_password.html"); |
| testTextInputType(WebTextInputTypeEmail, "input_field_email.html"); |
| testTextInputType(WebTextInputTypeSearch, "input_field_search.html"); |
| testTextInputType(WebTextInputTypeNumber, "input_field_number.html"); |
| testTextInputType(WebTextInputTypeTelephone, "input_field_tel.html"); |
| testTextInputType(WebTextInputTypeURL, "input_field_url.html"); |
| } |
| |
| TEST_P(WebViewTest, TextInputInfoUpdateStyleAndLayout) { |
| FrameTestHelpers::TestWebViewClient client; |
| FrameTestHelpers::WebViewHelper m_webViewHelper; |
| WebViewImpl* webViewImpl = m_webViewHelper.initialize(true, 0, &client); |
| |
| WebURL baseURL = URLTestHelpers::toKURL("http://example.com/"); |
| // Here, we need to construct a document that has a special property: |
| // Adding id="foo" to the <path> element will trigger creation of an SVG |
| // instance tree for the use <use> element. |
| // This is significant, because SVG instance trees are actually created lazily |
| // during Document::updateStyleAndLayout code, thus incrementing the DOM tree |
| // version and freaking out the EphemeralRange (invalidating it). |
| FrameTestHelpers::loadHTMLString( |
| webViewImpl->mainFrame(), |
| "<svg height='100%' version='1.1' viewBox='0 0 14 14' width='100%'>" |
| "<use xmlns:xlink='http://www.w3.org/1999/xlink' xlink:href='#foo'></use>" |
| "<path d='M 100 100 L 300 100 L 200 300 z' fill='#000'></path>" |
| "</svg>" |
| "<input>", |
| baseURL); |
| webViewImpl->setInitialFocus(false); |
| |
| // Add id="foo" to <path>, thus triggering the condition described above. |
| Document* document = webViewImpl->mainFrameImpl()->frame()->document(); |
| document->body() |
| ->querySelector("path", ASSERT_NO_EXCEPTION) |
| ->setIdAttribute("foo"); |
| |
| // This should not DCHECK. |
| EXPECT_EQ(WebTextInputTypeText, webViewImpl->textInputInfo().type); |
| } |
| |
| void WebViewTest::testInputMode(WebTextInputMode expectedInputMode, |
| const std::string& htmlFile) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8(htmlFile.c_str())); |
| WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + htmlFile); |
| webView->setInitialFocus(false); |
| EXPECT_EQ(expectedInputMode, webView->textInputInfo().inputMode); |
| } |
| |
| TEST_P(WebViewTest, InputMode) { |
| testInputMode(WebTextInputMode::kWebTextInputModeDefault, |
| "input_mode_default.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeDefault, |
| "input_mode_default_unknown.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeVerbatim, |
| "input_mode_default_verbatim.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeVerbatim, |
| "input_mode_type_text_verbatim.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeVerbatim, |
| "input_mode_type_search_verbatim.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeDefault, |
| "input_mode_type_url_verbatim.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeLatin, |
| "input_mode_type_latin.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeLatinName, |
| "input_mode_type_latin_name.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeLatinProse, |
| "input_mode_type_latin_prose.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeFullWidthLatin, |
| "input_mode_type_full_width_latin.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeKana, |
| "input_mode_type_kana.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeKanaName, |
| "input_mode_type_kana_name.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeKataKana, |
| "input_mode_type_kata_kana.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeNumeric, |
| "input_mode_type_numeric.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeTel, |
| "input_mode_type_tel.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeEmail, |
| "input_mode_type_email.html"); |
| testInputMode(WebTextInputMode::kWebTextInputModeUrl, |
| "input_mode_type_url.html"); |
| } |
| |
| TEST_P(WebViewTest, TextInputInfoWithReplacedElements) { |
| std::string url = m_baseURL + "div_with_image.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "div_with_image.html"); |
| URLTestHelpers::registerMockedURLLoad(toKURL("http://www.test.com/foo.png"), |
| "white-1x1.png"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url); |
| webView->setInitialFocus(false); |
| WebTextInputInfo info = webView->textInputInfo(); |
| |
| EXPECT_EQ("foo\xef\xbf\xbc", info.value.utf8()); |
| } |
| |
| TEST_P(WebViewTest, SetEditableSelectionOffsetsAndTextInputInfo) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setEditableSelectionOffsets(5, 13); |
| EXPECT_EQ("56789abc", frame->selectionAsText()); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(13, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("content_editable_populated.html")); |
| webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "content_editable_populated.html"); |
| webView->setInitialFocus(false); |
| frame = webView->mainFrameImpl(); |
| frame->setEditableSelectionOffsets(8, 19); |
| EXPECT_EQ("89abcdefghi", frame->selectionAsText()); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value); |
| EXPECT_EQ(8, info.selectionStart); |
| EXPECT_EQ(19, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| // Regression test for crbug.com/663645 |
| TEST_P(WebViewTest, FinishComposingTextDoesNotAssert) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_default.html")); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "input_field_default.html"); |
| webView->setInitialFocus(false); |
| |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| |
| // The test requires non-empty composition. |
| std::string compositionText("hello"); |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 5, 5); |
| |
| // Do arbitrary change to make layout dirty. |
| Document& document = *webView->mainFrameImpl()->frame()->document(); |
| Element* br = document.createElement("br"); |
| document.body()->appendChild(br); |
| |
| // Should not hit assertion when calling |
| // WebInputMethodController::finishComposingText with non-empty composition |
| // and dirty layout. |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::KeepSelection); |
| } |
| |
| TEST_P(WebViewTest, FinishComposingTextCursorPositionChange) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| |
| // Set up a composition that needs to be committed. |
| std::string compositionText("hello"); |
| |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 3, 3); |
| |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| EXPECT_EQ(3, info.selectionStart); |
| EXPECT_EQ(3, info.selectionEnd); |
| EXPECT_EQ(0, info.compositionStart); |
| EXPECT_EQ(5, info.compositionEnd); |
| |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::KeepSelection); |
| info = webView->textInputInfo(); |
| EXPECT_EQ(3, info.selectionStart); |
| EXPECT_EQ(3, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 3, 3); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helhellolo", std::string(info.value.utf8().data())); |
| EXPECT_EQ(6, info.selectionStart); |
| EXPECT_EQ(6, info.selectionEnd); |
| EXPECT_EQ(3, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::DoNotKeepSelection); |
| info = webView->textInputInfo(); |
| EXPECT_EQ(8, info.selectionStart); |
| EXPECT_EQ(8, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, SetCompositionForNewCaretPositions) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| |
| activeInputMethodController->commitText("hello", 0); |
| activeInputMethodController->commitText("world", -5); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("helloworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| // Set up a composition that needs to be committed. |
| std::string compositionText("ABC"); |
| |
| // Caret is on the left of composing text. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 0, 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret is on the right of composing text. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 3, 3); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(8, info.selectionStart); |
| EXPECT_EQ(8, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret is between composing text and left boundary. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, -2, -2); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(3, info.selectionStart); |
| EXPECT_EQ(3, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret is between composing text and right boundary. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 5, 5); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(10, info.selectionStart); |
| EXPECT_EQ(10, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret is on the left boundary. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, -5, -5); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret is on the right boundary. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 8, 8); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(13, info.selectionStart); |
| EXPECT_EQ(13, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret exceeds the left boundary. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, -100, |
| -100); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| |
| // Caret exceeds the right boundary. |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 100, 100); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloABCworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(13, info.selectionStart); |
| EXPECT_EQ(13, info.selectionEnd); |
| EXPECT_EQ(5, info.compositionStart); |
| EXPECT_EQ(8, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, SetCompositionWithEmptyText) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| |
| activeInputMethodController->commitText("hello", 0); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| |
| activeInputMethodController->setComposition(WebString::fromUTF8(""), |
| emptyUnderlines, 0, 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| activeInputMethodController->setComposition(WebString::fromUTF8(""), |
| emptyUnderlines, -2, -2); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| EXPECT_EQ(3, info.selectionStart); |
| EXPECT_EQ(3, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, CommitTextForNewCaretPositions) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| |
| // Caret is on the left of composing text. |
| activeInputMethodController->commitText("ab", -2); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("ab", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Caret is on the right of composing text. |
| activeInputMethodController->commitText("c", 1); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("cab", std::string(info.value.utf8().data())); |
| EXPECT_EQ(2, info.selectionStart); |
| EXPECT_EQ(2, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Caret is on the left boundary. |
| activeInputMethodController->commitText("def", -5); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("cadefb", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Caret is on the right boundary. |
| activeInputMethodController->commitText("g", 6); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("gcadefb", std::string(info.value.utf8().data())); |
| EXPECT_EQ(7, info.selectionStart); |
| EXPECT_EQ(7, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Caret exceeds the left boundary. |
| activeInputMethodController->commitText("hi", -100); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("gcadefbhi", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Caret exceeds the right boundary. |
| activeInputMethodController->commitText("jk", 100); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("jkgcadefbhi", std::string(info.value.utf8().data())); |
| EXPECT_EQ(11, info.selectionStart); |
| EXPECT_EQ(11, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, CommitTextWhileComposing) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| activeInputMethodController->setComposition(WebString::fromUTF8("abc"), |
| emptyUnderlines, 0, 0); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("abc", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| EXPECT_EQ(0, info.compositionStart); |
| EXPECT_EQ(3, info.compositionEnd); |
| |
| // Deletes ongoing composition, inserts the specified text and moves the |
| // caret. |
| activeInputMethodController->commitText("hello", -2); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| EXPECT_EQ(3, info.selectionStart); |
| EXPECT_EQ(3, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| activeInputMethodController->setComposition(WebString::fromUTF8("abc"), |
| emptyUnderlines, 0, 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helabclo", std::string(info.value.utf8().data())); |
| EXPECT_EQ(3, info.selectionStart); |
| EXPECT_EQ(3, info.selectionEnd); |
| EXPECT_EQ(3, info.compositionStart); |
| EXPECT_EQ(6, info.compositionEnd); |
| |
| // Deletes ongoing composition and moves the caret. |
| activeInputMethodController->commitText("", 2); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Inserts the specified text and moves the caret. |
| activeInputMethodController->commitText("world", -5); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| // Only moves the caret. |
| activeInputMethodController->commitText("", 5); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("helloworld", std::string(info.value.utf8().data())); |
| EXPECT_EQ(10, info.selectionStart); |
| EXPECT_EQ(10, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, FinishCompositionDoesNotRevealSelection) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("form_with_input.html")); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "form_with_input.html"); |
| webView->resize(WebSize(800, 600)); |
| webView->setInitialFocus(false); |
| EXPECT_EQ(0, webView->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(0, webView->mainFrame()->scrollOffset().height); |
| |
| // Set up a composition from existing text that needs to be committed. |
| Vector<CompositionUnderline> emptyUnderlines; |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->frame()->inputMethodController().setCompositionFromExistingText( |
| emptyUnderlines, 3, 3); |
| |
| // Scroll the input field out of the viewport. |
| Element* element = static_cast<Element*>( |
| webView->mainFrame()->document().getElementById("btn")); |
| element->scrollIntoView(); |
| float offsetHeight = webView->mainFrame()->scrollOffset().height; |
| EXPECT_EQ(0, webView->mainFrame()->scrollOffset().width); |
| EXPECT_LT(0, offsetHeight); |
| |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("hello", std::string(info.value.utf8().data())); |
| |
| // Verify that the input field is not scrolled back into the viewport. |
| frame->frameWidget() |
| ->getActiveWebInputMethodController() |
| ->finishComposingText(WebInputMethodController::DoNotKeepSelection); |
| EXPECT_EQ(0, webView->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(offsetHeight, webView->mainFrame()->scrollOffset().height); |
| } |
| |
| TEST_P(WebViewTest, InsertNewLinePlacementAfterFinishComposingText) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("text_area_populated.html")); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "text_area_populated.html"); |
| webView->setInitialFocus(false); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setEditableSelectionOffsets(4, 4); |
| frame->setCompositionFromExistingText(8, 12, emptyUnderlines); |
| |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", |
| std::string(info.value.utf8().data())); |
| EXPECT_EQ(4, info.selectionStart); |
| EXPECT_EQ(4, info.selectionEnd); |
| EXPECT_EQ(8, info.compositionStart); |
| EXPECT_EQ(12, info.compositionEnd); |
| |
| WebInputMethodController* activeInputMethodController = |
| frame->frameWidget()->getActiveWebInputMethodController(); |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::KeepSelection); |
| info = webView->textInputInfo(); |
| EXPECT_EQ(4, info.selectionStart); |
| EXPECT_EQ(4, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| |
| std::string compositionText("\n"); |
| activeInputMethodController->commitText( |
| WebString::fromUTF8(compositionText.c_str()), 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| EXPECT_EQ("0123\n456789abcdefghijklmnopqrstuvwxyz", |
| std::string(info.value.utf8().data())); |
| } |
| |
| TEST_P(WebViewTest, ExtendSelectionAndDelete) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| webView->setInitialFocus(false); |
| frame->setEditableSelectionOffsets(10, 10); |
| frame->extendSelectionAndDelete(5, 8); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("01234ijklmnopqrstuvwxyz", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| frame->extendSelectionAndDelete(10, 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("ijklmnopqrstuvwxyz", std::string(info.value.utf8().data())); |
| } |
| |
| TEST_P(WebViewTest, DeleteSurroundingText) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame()); |
| webView->setInitialFocus(false); |
| |
| frame->setEditableSelectionOffsets(10, 10); |
| frame->deleteSurroundingText(5, 8); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("01234ijklmnopqrstuvwxyz", std::string(info.value.utf8().data())); |
| EXPECT_EQ(5, info.selectionStart); |
| EXPECT_EQ(5, info.selectionEnd); |
| |
| frame->setEditableSelectionOffsets(5, 10); |
| frame->deleteSurroundingText(3, 5); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("01ijklmstuvwxyz", std::string(info.value.utf8().data())); |
| EXPECT_EQ(2, info.selectionStart); |
| EXPECT_EQ(7, info.selectionEnd); |
| |
| frame->setEditableSelectionOffsets(5, 5); |
| frame->deleteSurroundingText(10, 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("lmstuvwxyz", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| |
| frame->deleteSurroundingText(0, 20); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| |
| frame->deleteSurroundingText(10, 10); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("", std::string(info.value.utf8().data())); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ(0, info.selectionEnd); |
| } |
| |
| TEST_P(WebViewTest, SetCompositionFromExistingText) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1)); |
| underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setEditableSelectionOffsets(4, 10); |
| frame->setCompositionFromExistingText(8, 12, underlines); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ(4, info.selectionStart); |
| EXPECT_EQ(10, info.selectionEnd); |
| EXPECT_EQ(8, info.compositionStart); |
| EXPECT_EQ(12, info.compositionEnd); |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| frame->setCompositionFromExistingText(0, 0, emptyUnderlines); |
| info = webView->textInputInfo(); |
| EXPECT_EQ(4, info.selectionStart); |
| EXPECT_EQ(10, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, SetCompositionFromExistingTextInTextArea) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("text_area_populated.html")); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "text_area_populated.html"); |
| webView->setInitialFocus(false); |
| WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1)); |
| underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| WebInputMethodController* activeInputMethodController = |
| frame->frameWidget()->getActiveWebInputMethodController(); |
| frame->setEditableSelectionOffsets(27, 27); |
| std::string newLineText("\n"); |
| activeInputMethodController->commitText( |
| WebString::fromUTF8(newLineText.c_str()), 0); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz", |
| std::string(info.value.utf8().data())); |
| |
| frame->setEditableSelectionOffsets(31, 31); |
| frame->setCompositionFromExistingText(30, 34, underlines); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz", |
| std::string(info.value.utf8().data())); |
| EXPECT_EQ(31, info.selectionStart); |
| EXPECT_EQ(31, info.selectionEnd); |
| EXPECT_EQ(30, info.compositionStart); |
| EXPECT_EQ(34, info.compositionEnd); |
| |
| std::string compositionText("yolo"); |
| activeInputMethodController->commitText( |
| WebString::fromUTF8(compositionText.c_str()), 0); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopq\nrsyoloxyz", |
| std::string(info.value.utf8().data())); |
| EXPECT_EQ(34, info.selectionStart); |
| EXPECT_EQ(34, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, SetCompositionFromExistingTextInRichText) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("content_editable_rich_text.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "content_editable_rich_text.html"); |
| webView->setInitialFocus(false); |
| WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1)); |
| underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setEditableSelectionOffsets(1, 1); |
| WebDocument document = webView->mainFrame()->document(); |
| EXPECT_FALSE(document.getElementById("bold").isNull()); |
| frame->setCompositionFromExistingText(0, 4, underlines); |
| EXPECT_FALSE(document.getElementById("bold").isNull()); |
| } |
| |
| TEST_P(WebViewTest, SetEditableSelectionOffsetsKeepsComposition) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| webView->setInitialFocus(false); |
| |
| std::string compositionTextFirst("hello "); |
| std::string compositionTextSecond("world"); |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| WebInputMethodController* activeInputMethodController = |
| webView->mainFrameImpl() |
| ->frameWidget() |
| ->getActiveWebInputMethodController(); |
| activeInputMethodController->commitText( |
| WebString::fromUTF8(compositionTextFirst.c_str()), 0); |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionTextSecond.c_str()), emptyUnderlines, 5, |
| 5); |
| |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("hello world", std::string(info.value.utf8().data())); |
| EXPECT_EQ(11, info.selectionStart); |
| EXPECT_EQ(11, info.selectionEnd); |
| EXPECT_EQ(6, info.compositionStart); |
| EXPECT_EQ(11, info.compositionEnd); |
| |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setEditableSelectionOffsets(6, 6); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello world", std::string(info.value.utf8().data())); |
| EXPECT_EQ(6, info.selectionStart); |
| EXPECT_EQ(6, info.selectionEnd); |
| EXPECT_EQ(6, info.compositionStart); |
| EXPECT_EQ(11, info.compositionEnd); |
| |
| frame->setEditableSelectionOffsets(8, 8); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello world", std::string(info.value.utf8().data())); |
| EXPECT_EQ(8, info.selectionStart); |
| EXPECT_EQ(8, info.selectionEnd); |
| EXPECT_EQ(6, info.compositionStart); |
| EXPECT_EQ(11, info.compositionEnd); |
| |
| frame->setEditableSelectionOffsets(11, 11); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello world", std::string(info.value.utf8().data())); |
| EXPECT_EQ(11, info.selectionStart); |
| EXPECT_EQ(11, info.selectionEnd); |
| EXPECT_EQ(6, info.compositionStart); |
| EXPECT_EQ(11, info.compositionEnd); |
| |
| frame->setEditableSelectionOffsets(6, 11); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello world", std::string(info.value.utf8().data())); |
| EXPECT_EQ(6, info.selectionStart); |
| EXPECT_EQ(11, info.selectionEnd); |
| EXPECT_EQ(6, info.compositionStart); |
| EXPECT_EQ(11, info.compositionEnd); |
| |
| frame->setEditableSelectionOffsets(2, 2); |
| info = webView->textInputInfo(); |
| EXPECT_EQ("hello world", std::string(info.value.utf8().data())); |
| EXPECT_EQ(2, info.selectionStart); |
| EXPECT_EQ(2, info.selectionEnd); |
| EXPECT_EQ(-1, info.compositionStart); |
| EXPECT_EQ(-1, info.compositionEnd); |
| } |
| |
| TEST_P(WebViewTest, IsSelectionAnchorFirst) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| WebLocalFrame* frame = webView->mainFrameImpl(); |
| |
| webView->setInitialFocus(false); |
| frame->setEditableSelectionOffsets(4, 10); |
| EXPECT_TRUE(webView->isSelectionAnchorFirst()); |
| WebRect anchor; |
| WebRect focus; |
| webView->selectionBounds(anchor, focus); |
| frame->selectRange(WebPoint(focus.x, focus.y), WebPoint(anchor.x, anchor.y)); |
| EXPECT_FALSE(webView->isSelectionAnchorFirst()); |
| } |
| |
| TEST_P(WebViewTest, ExitingDeviceEmulationResetsPageScale) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("200-by-300.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html"); |
| webViewImpl->resize(WebSize(200, 300)); |
| |
| float pageScaleExpected = webViewImpl->pageScaleFactor(); |
| |
| WebDeviceEmulationParams params; |
| params.screenPosition = WebDeviceEmulationParams::Desktop; |
| params.deviceScaleFactor = 0; |
| params.fitToView = false; |
| params.offset = WebFloatPoint(); |
| params.scale = 1; |
| |
| webViewImpl->enableDeviceEmulation(params); |
| |
| webViewImpl->setPageScaleFactor(2); |
| |
| webViewImpl->disableDeviceEmulation(); |
| |
| EXPECT_EQ(pageScaleExpected, webViewImpl->pageScaleFactor()); |
| } |
| |
| TEST_P(WebViewTest, HistoryResetScrollAndScaleState) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("200-by-300.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html"); |
| webViewImpl->resize(WebSize(100, 150)); |
| webViewImpl->updateAllLifecyclePhases(); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().height); |
| |
| // Make the page scale and scroll with the given paremeters. |
| webViewImpl->setPageScaleFactor(2.0f); |
| webViewImpl->mainFrame()->setScrollOffset(WebSize(94, 111)); |
| EXPECT_EQ(2.0f, webViewImpl->pageScaleFactor()); |
| EXPECT_EQ(94, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(111, webViewImpl->mainFrame()->scrollOffset().height); |
| LocalFrame* mainFrameLocal = toLocalFrame(webViewImpl->page()->mainFrame()); |
| mainFrameLocal->loader().saveScrollState(); |
| EXPECT_EQ(2.0f, mainFrameLocal->loader().currentItem()->pageScaleFactor()); |
| EXPECT_EQ(94, mainFrameLocal->loader().currentItem()->scrollOffset().width()); |
| EXPECT_EQ(111, |
| mainFrameLocal->loader().currentItem()->scrollOffset().height()); |
| |
| // Confirm that resetting the page state resets the saved scroll position. |
| webViewImpl->resetScrollAndScaleState(); |
| EXPECT_EQ(1.0f, webViewImpl->pageScaleFactor()); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().height); |
| EXPECT_EQ(1.0f, mainFrameLocal->loader().currentItem()->pageScaleFactor()); |
| EXPECT_EQ(0, mainFrameLocal->loader().currentItem()->scrollOffset().width()); |
| EXPECT_EQ(0, mainFrameLocal->loader().currentItem()->scrollOffset().height()); |
| } |
| |
| TEST_P(WebViewTest, BackForwardRestoreScroll) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("back_forward_restore_scroll.html")); |
| WebViewImpl* webViewImpl = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "back_forward_restore_scroll.html"); |
| webViewImpl->resize(WebSize(640, 480)); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| // Emulate a user scroll |
| webViewImpl->mainFrame()->setScrollOffset(WebSize(0, 900)); |
| LocalFrame* mainFrameLocal = toLocalFrame(webViewImpl->page()->mainFrame()); |
| Persistent<HistoryItem> item1 = mainFrameLocal->loader().currentItem(); |
| |
| // Click an anchor |
| mainFrameLocal->loader().load(FrameLoadRequest( |
| mainFrameLocal->document(), |
| ResourceRequest(mainFrameLocal->document()->completeURL("#a")))); |
| Persistent<HistoryItem> item2 = mainFrameLocal->loader().currentItem(); |
| |
| // Go back, then forward, then back again. |
| mainFrameLocal->loader().load( |
| FrameLoadRequest( |
| nullptr, FrameLoader::resourceRequestFromHistoryItem( |
| item1.get(), WebCachePolicy::UseProtocolCachePolicy)), |
| FrameLoadTypeBackForward, item1.get(), HistorySameDocumentLoad); |
| mainFrameLocal->loader().load( |
| FrameLoadRequest( |
| nullptr, FrameLoader::resourceRequestFromHistoryItem( |
| item2.get(), WebCachePolicy::UseProtocolCachePolicy)), |
| FrameLoadTypeBackForward, item2.get(), HistorySameDocumentLoad); |
| mainFrameLocal->loader().load( |
| FrameLoadRequest( |
| nullptr, FrameLoader::resourceRequestFromHistoryItem( |
| item1.get(), WebCachePolicy::UseProtocolCachePolicy)), |
| FrameLoadTypeBackForward, item1.get(), HistorySameDocumentLoad); |
| |
| // Click a different anchor |
| mainFrameLocal->loader().load(FrameLoadRequest( |
| mainFrameLocal->document(), |
| ResourceRequest(mainFrameLocal->document()->completeURL("#b")))); |
| Persistent<HistoryItem> item3 = mainFrameLocal->loader().currentItem(); |
| |
| // Go back, then forward. The scroll position should be properly set on the |
| // forward navigation. |
| mainFrameLocal->loader().load( |
| FrameLoadRequest( |
| nullptr, FrameLoader::resourceRequestFromHistoryItem( |
| item1.get(), WebCachePolicy::UseProtocolCachePolicy)), |
| FrameLoadTypeBackForward, item1.get(), HistorySameDocumentLoad); |
| mainFrameLocal->loader().load( |
| FrameLoadRequest( |
| nullptr, FrameLoader::resourceRequestFromHistoryItem( |
| item3.get(), WebCachePolicy::UseProtocolCachePolicy)), |
| FrameLoadTypeBackForward, item3.get(), HistorySameDocumentLoad); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_GT(webViewImpl->mainFrame()->scrollOffset().height, 2000); |
| } |
| |
| // Tests that we restore scroll and scale *after* the fullscreen styles are |
| // removed and the page is laid out. http://crbug.com/625683. |
| TEST_P(WebViewTest, FullscreenResetScrollAndScaleFullscreenStyles) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("fullscreen_style.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "fullscreen_style.html"); |
| webViewImpl->resize(WebSize(800, 600)); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| // Scroll the page down. |
| webViewImpl->mainFrame()->setScrollOffset(WebSize(0, 2000)); |
| ASSERT_EQ(2000, webViewImpl->mainFrame()->scrollOffset().height); |
| |
| // Enter fullscreen. |
| Element* element = static_cast<Element*>( |
| webViewImpl->mainFrame()->document().getElementById("fullscreenElement")); |
| webViewImpl->enterFullscreenForElement(element); |
| webViewImpl->didEnterFullscreen(); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| // Sanity-check. There should be no scrolling possible. |
| ASSERT_EQ(0, webViewImpl->mainFrame()->scrollOffset().height); |
| ASSERT_EQ(0, webViewImpl->mainFrameImpl() |
| ->frameView() |
| ->maximumScrollOffset() |
| .height()); |
| |
| // Confirm that after exiting and doing a layout, the scroll and scale |
| // parameters are reset. The page sets display: none on overflowing elements |
| // while in fullscreen so if we try to restore before the style and layout |
| // is applied the offsets will be clamped. |
| webViewImpl->didExitFullscreen(); |
| EXPECT_TRUE(webViewImpl->mainFrameImpl()->frameView()->needsLayout()); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| EXPECT_EQ(2000, webViewImpl->mainFrame()->scrollOffset().height); |
| } |
| |
| // Tests that exiting and immediately reentering fullscreen doesn't cause the |
| // scroll and scale restoration to occur when we enter fullscreen again. |
| TEST_P(WebViewTest, FullscreenResetScrollAndScaleExitAndReenter) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("fullscreen_style.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "fullscreen_style.html"); |
| webViewImpl->resize(WebSize(800, 600)); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| // Scroll the page down. |
| webViewImpl->mainFrame()->setScrollOffset(WebSize(0, 2000)); |
| ASSERT_EQ(2000, webViewImpl->mainFrame()->scrollOffset().height); |
| |
| // Enter fullscreen. |
| Element* element = static_cast<Element*>( |
| webViewImpl->mainFrame()->document().getElementById("fullscreenElement")); |
| webViewImpl->enterFullscreenForElement(element); |
| webViewImpl->didEnterFullscreen(); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| // Sanity-check. There should be no scrolling possible. |
| ASSERT_EQ(0, webViewImpl->mainFrame()->scrollOffset().height); |
| ASSERT_EQ(0, webViewImpl->mainFrameImpl() |
| ->frameView() |
| ->maximumScrollOffset() |
| .height()); |
| |
| // Exit and, without performing a layout, reenter fullscreen again. We |
| // shouldn't try to restore the scroll and scale values when we layout to |
| // enter fullscreen. |
| webViewImpl->exitFullscreen(element->document().frame()); |
| webViewImpl->didExitFullscreen(); |
| webViewImpl->enterFullscreenForElement(element); |
| webViewImpl->didEnterFullscreen(); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| // Sanity-check. There should be no scrolling possible. |
| ASSERT_EQ(0, webViewImpl->mainFrame()->scrollOffset().height); |
| ASSERT_EQ(0, webViewImpl->mainFrameImpl() |
| ->frameView() |
| ->maximumScrollOffset() |
| .height()); |
| |
| // When we exit now, we should restore the original scroll value. |
| webViewImpl->exitFullscreen(element->document().frame()); |
| webViewImpl->didExitFullscreen(); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| EXPECT_EQ(2000, webViewImpl->mainFrame()->scrollOffset().height); |
| } |
| |
| TEST_P(WebViewTest, EnterFullscreenResetScrollAndScaleState) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("200-by-300.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html"); |
| webViewImpl->resize(WebSize(100, 150)); |
| webViewImpl->updateAllLifecyclePhases(); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(0, webViewImpl->mainFrame()->scrollOffset().height); |
| |
| // Make the page scale and scroll with the given paremeters. |
| webViewImpl->setPageScaleFactor(2.0f); |
| webViewImpl->mainFrame()->setScrollOffset(WebSize(94, 111)); |
| webViewImpl->setVisualViewportOffset(WebFloatPoint(12, 20)); |
| EXPECT_EQ(2.0f, webViewImpl->pageScaleFactor()); |
| EXPECT_EQ(94, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(111, webViewImpl->mainFrame()->scrollOffset().height); |
| EXPECT_EQ(12, webViewImpl->visualViewportOffset().x); |
| EXPECT_EQ(20, webViewImpl->visualViewportOffset().y); |
| |
| Element* element = |
| static_cast<Element*>(webViewImpl->mainFrame()->document().body()); |
| webViewImpl->enterFullscreenForElement(element); |
| webViewImpl->didEnterFullscreen(); |
| |
| // Page scale factor must be 1.0 during fullscreen for elements to be sized |
| // properly. |
| EXPECT_EQ(1.0f, webViewImpl->pageScaleFactor()); |
| |
| // Make sure fullscreen nesting doesn't disrupt scroll/scale saving. |
| Element* otherElement = |
| static_cast<Element*>(webViewImpl->mainFrame()->document().head()); |
| webViewImpl->enterFullscreenForElement(otherElement); |
| |
| // Confirm that exiting fullscreen restores the parameters. |
| webViewImpl->exitFullscreen(element->document().frame()); |
| webViewImpl->didExitFullscreen(); |
| webViewImpl->updateAllLifecyclePhases(); |
| |
| EXPECT_EQ(2.0f, webViewImpl->pageScaleFactor()); |
| EXPECT_EQ(94, webViewImpl->mainFrame()->scrollOffset().width); |
| EXPECT_EQ(111, webViewImpl->mainFrame()->scrollOffset().height); |
| EXPECT_EQ(12, webViewImpl->visualViewportOffset().x); |
| EXPECT_EQ(20, webViewImpl->visualViewportOffset().y); |
| } |
| |
| class PrintWebViewClient : public FrameTestHelpers::TestWebViewClient { |
| public: |
| PrintWebViewClient() : m_printCalled(false) {} |
| |
| // WebViewClient methods |
| void printPage(WebLocalFrame*) override { m_printCalled = true; } |
| |
| bool printCalled() const { return m_printCalled; } |
| |
| private: |
| bool m_printCalled; |
| }; |
| |
| TEST_P(WebViewTest, PrintWithXHRInFlight) { |
| PrintWebViewClient client; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("print_with_xhr_inflight.html")); |
| WebViewImpl* webViewImpl = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "print_with_xhr_inflight.html", true, 0, &client); |
| |
| ASSERT_TRUE(toLocalFrame(webViewImpl->page()->mainFrame()) |
| ->document() |
| ->loadEventFinished()); |
| EXPECT_TRUE(client.printCalled()); |
| m_webViewHelper.reset(); |
| } |
| |
| static void DragAndDropURL(WebViewImpl* webView, const std::string& url) { |
| WebDragData dragData; |
| dragData.initialize(); |
| |
| WebDragData::Item item; |
| item.storageType = WebDragData::Item::StorageTypeString; |
| item.stringType = "text/uri-list"; |
| item.stringData = WebString::fromUTF8(url); |
| dragData.addItem(item); |
| |
| const WebPoint clientPoint(0, 0); |
| const WebPoint screenPoint(0, 0); |
| WebFrameWidgetBase* widget = webView->mainFrameImpl()->frameWidget(); |
| widget->dragTargetDragEnter(dragData, clientPoint, screenPoint, |
| WebDragOperationCopy, 0); |
| widget->dragTargetDrop(dragData, clientPoint, screenPoint, 0); |
| FrameTestHelpers::pumpPendingRequestsForFrameToLoad(webView->mainFrame()); |
| } |
| |
| TEST_P(WebViewTest, DragDropURL) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), "foo.html"); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), "bar.html"); |
| |
| const std::string fooUrl = m_baseURL + "foo.html"; |
| const std::string barUrl = m_baseURL + "bar.html"; |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad(fooUrl); |
| |
| ASSERT_TRUE(webView); |
| |
| // Drag and drop barUrl and verify that we've navigated to it. |
| DragAndDropURL(webView, barUrl); |
| EXPECT_EQ(barUrl, webView->mainFrame()->document().url().string().utf8()); |
| |
| // Drag and drop fooUrl and verify that we've navigated back to it. |
| DragAndDropURL(webView, fooUrl); |
| EXPECT_EQ(fooUrl, webView->mainFrame()->document().url().string().utf8()); |
| |
| // Disable navigation on drag-and-drop. |
| webView->settingsImpl()->setNavigateOnDragDrop(false); |
| |
| // Attempt to drag and drop to barUrl and verify that no navigation has |
| // occurred. |
| DragAndDropURL(webView, barUrl); |
| EXPECT_EQ(fooUrl, webView->mainFrame()->document().url().string().utf8()); |
| } |
| |
| class ContentDetectorClient : public FrameTestHelpers::TestWebViewClient { |
| public: |
| ContentDetectorClient() { reset(); } |
| |
| WebURL detectContentIntentAt(const WebHitTestResult& hitTest) override { |
| m_contentDetectionRequested = true; |
| return m_contentDetectionResult; |
| } |
| |
| void scheduleContentIntent(const WebURL& url, bool isMainFrame) override { |
| m_scheduledIntentURL = url; |
| m_wasInMainFrame = isMainFrame; |
| } |
| |
| void cancelScheduledContentIntents() override { |
| m_pendingIntentsCancelled = true; |
| } |
| |
| void reset() { |
| m_contentDetectionRequested = false; |
| m_pendingIntentsCancelled = false; |
| m_scheduledIntentURL = WebURL(); |
| m_wasInMainFrame = false; |
| m_contentDetectionResult = WebURL(); |
| } |
| |
| bool contentDetectionRequested() const { return m_contentDetectionRequested; } |
| bool pendingIntentsCancelled() const { return m_pendingIntentsCancelled; } |
| const WebURL& scheduledIntentURL() const { return m_scheduledIntentURL; } |
| bool wasInMainFrame() const { return m_wasInMainFrame; } |
| void setContentDetectionResult(const WebURL& result) { |
| m_contentDetectionResult = result; |
| } |
| |
| private: |
| bool m_contentDetectionRequested; |
| bool m_pendingIntentsCancelled; |
| WebURL m_scheduledIntentURL; |
| bool m_wasInMainFrame; |
| WebURL m_contentDetectionResult; |
| }; |
| |
| bool WebViewTest::tapElement(WebInputEvent::Type type, Element* element) { |
| if (!element || !element->layoutObject()) |
| return false; |
| |
| DCHECK(m_webViewHelper.webView()); |
| element->scrollIntoViewIfNeeded(); |
| |
| // TODO(bokan): Technically incorrect, event positions should be in viewport |
| // space. crbug.com/371902. |
| IntPoint center = |
| m_webViewHelper.webView() |
| ->mainFrameImpl() |
| ->frameView() |
| ->contentsToScreen(element->layoutObject()->absoluteBoundingBoxRect()) |
| .center(); |
| |
| WebGestureEvent event; |
| event.type = type; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = center.x(); |
| event.y = center.y(); |
| |
| m_webViewHelper.webView()->handleInputEvent(event); |
| runPendingTasks(); |
| return true; |
| } |
| |
| bool WebViewTest::tapElementById(WebInputEvent::Type type, |
| const WebString& id) { |
| DCHECK(m_webViewHelper.webView()); |
| Element* element = static_cast<Element*>( |
| m_webViewHelper.webView()->mainFrame()->document().getElementById(id)); |
| return tapElement(type, element); |
| } |
| |
| TEST_P(WebViewTest, DetectContentAroundPosition) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("content_listeners.html")); |
| |
| ContentDetectorClient client; |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "content_listeners.html", true, 0, &client); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString clickListener = WebString::fromUTF8("clickListener"); |
| WebString touchstartListener = WebString::fromUTF8("touchstartListener"); |
| WebString mousedownListener = WebString::fromUTF8("mousedownListener"); |
| WebString noListener = WebString::fromUTF8("noListener"); |
| WebString link = WebString::fromUTF8("link"); |
| |
| // Ensure content detection is not requested for nodes listening to click, |
| // mouse or touch events when we do simple taps. |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, clickListener)); |
| EXPECT_FALSE(client.contentDetectionRequested()); |
| client.reset(); |
| |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, touchstartListener)); |
| EXPECT_FALSE(client.contentDetectionRequested()); |
| client.reset(); |
| |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, mousedownListener)); |
| EXPECT_FALSE(client.contentDetectionRequested()); |
| client.reset(); |
| |
| // Content detection should work normally without these event listeners. |
| // The click listener in the body should be ignored as a special case. |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, noListener)); |
| EXPECT_TRUE(client.contentDetectionRequested()); |
| EXPECT_FALSE(client.scheduledIntentURL().isValid()); |
| |
| WebURL intentURL = toKURL(m_baseURL); |
| client.setContentDetectionResult(intentURL); |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, noListener)); |
| EXPECT_TRUE(client.scheduledIntentURL() == intentURL); |
| EXPECT_TRUE(client.wasInMainFrame()); |
| |
| // Tapping elsewhere should cancel the scheduled intent. |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureTap; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| webView->handleInputEvent(event); |
| runPendingTasks(); |
| EXPECT_TRUE(client.pendingIntentsCancelled()); |
| |
| // Explicitly reset to break dependency on locally scoped client. |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, ContentDetectionInIframe) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("content_listeners_iframe.html")); |
| |
| ContentDetectorClient client; |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "content_listeners_iframe.html", true, 0, &client); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString noListener = WebString::fromUTF8("noListener"); |
| WebString frameName = WebString::fromUTF8("innerFrame"); |
| |
| WebURL intentURL = toKURL(m_baseURL); |
| client.setContentDetectionResult(intentURL); |
| Element* element = static_cast<Element*>( |
| webView->findFrameByName(frameName)->document().getElementById( |
| noListener)); |
| EXPECT_TRUE(tapElement(WebInputEvent::GestureTap, element)); |
| EXPECT_TRUE(client.scheduledIntentURL() == intentURL); |
| EXPECT_FALSE(client.wasInMainFrame()); |
| |
| // Explicitly reset to break dependency on locally scoped client. |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, ClientTapHandling) { |
| TapHandlingWebViewClient client; |
| client.reset(); |
| WebView* webView = |
| m_webViewHelper.initializeAndLoad("about:blank", true, 0, &client); |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureTap; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 3; |
| event.y = 8; |
| webView->handleInputEvent(event); |
| runPendingTasks(); |
| EXPECT_EQ(3, client.tapX()); |
| EXPECT_EQ(8, client.tapY()); |
| client.reset(); |
| event.type = WebInputEvent::GestureLongPress; |
| event.x = 25; |
| event.y = 7; |
| webView->handleInputEvent(event); |
| runPendingTasks(); |
| EXPECT_EQ(25, client.longpressX()); |
| EXPECT_EQ(7, client.longpressY()); |
| |
| // Explicitly reset to break dependency on locally scoped client. |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, ClientTapHandlingNullWebViewClient) { |
| WebViewImpl* webView = |
| WebViewImpl::create(nullptr, WebPageVisibilityStateVisible); |
| FrameTestHelpers::TestWebFrameClient webFrameClient; |
| FrameTestHelpers::TestWebWidgetClient webWidgetClient; |
| WebLocalFrame* localFrame = |
| WebLocalFrame::create(WebTreeScopeType::Document, &webFrameClient); |
| webView->setMainFrame(localFrame); |
| |
| // TODO(dcheng): The main frame widget currently has a special case. |
| // Eliminate this once WebView is no longer a WebWidget. |
| blink::WebFrameWidget::create(&webWidgetClient, webView, localFrame); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureTap; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 3; |
| event.y = 8; |
| EXPECT_EQ(WebInputEventResult::NotHandled, webView->handleInputEvent(event)); |
| webView->close(); |
| } |
| |
| TEST_P(WebViewTest, LongPressEmptyDiv) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_empty_div.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_empty_div.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(false); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 250; |
| event.y = 150; |
| |
| EXPECT_EQ(WebInputEventResult::NotHandled, webView->handleInputEvent(event)); |
| } |
| |
| TEST_P(WebViewTest, LongPressEmptyDivAlwaysShow) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_empty_div.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_empty_div.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 250; |
| event.y = 150; |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| } |
| |
| TEST_P(WebViewTest, LongPressObject) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_object.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_object.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 10; |
| event.y = 10; |
| |
| EXPECT_NE(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| |
| HTMLElement* element = |
| toHTMLElement(webView->mainFrame()->document().getElementById("obj")); |
| EXPECT_FALSE(element->canStartSelection()); |
| } |
| |
| TEST_P(WebViewTest, LongPressObjectFallback) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_object_fallback.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_object_fallback.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 10; |
| event.y = 10; |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| |
| HTMLElement* element = |
| toHTMLElement(webView->mainFrame()->document().getElementById("obj")); |
| EXPECT_TRUE(element->canStartSelection()); |
| } |
| |
| TEST_P(WebViewTest, LongPressImage) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_image.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_image.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(false); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 10; |
| event.y = 10; |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| } |
| |
| TEST_P(WebViewTest, LongPressVideo) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_video.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_video.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(false); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 10; |
| event.y = 10; |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| } |
| |
| TEST_P(WebViewTest, LongPressLink) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_link.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_link.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(false); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 500; |
| event.y = 300; |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| } |
| |
| TEST_P(WebViewTest, showContextMenuOnLongPressingLinks) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_links_and_images.html")); |
| |
| URLTestHelpers::registerMockedURLLoad(toKURL("http://www.test.com/foo.png"), |
| "white-1x1.png"); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_links_and_images.html", true); |
| |
| webView->settingsImpl()->setTouchDragDropEnabled(true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString anchorTagId = WebString::fromUTF8("anchorTag"); |
| WebString imageTagId = WebString::fromUTF8("imageTag"); |
| |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureLongPress, anchorTagId)); |
| EXPECT_STREQ("anchor contextmenu", |
| webView->mainFrame()->document().title().utf8().data()); |
| |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureLongPress, imageTagId)); |
| EXPECT_STREQ("image contextmenu", |
| webView->mainFrame()->document().title().utf8().data()); |
| } |
| |
| TEST_P(WebViewTest, LongPressEmptyEditableSelection) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_empty_editable_selection.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_empty_editable_selection.html", true); |
| webView->settingsImpl()->setAlwaysShowContextMenuOnTouch(false); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 10; |
| event.y = 10; |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| } |
| |
| TEST_P(WebViewTest, LongPressEmptyNonEditableSelection) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("long_press_image.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "long_press_image.html", true); |
| webView->resize(WebSize(500, 500)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureLongPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 300; |
| event.y = 300; |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(event)); |
| EXPECT_TRUE(frame->selectionAsText().isEmpty()); |
| } |
| |
| TEST_P(WebViewTest, LongPressSelection) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("longpress_selection.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "longpress_selection.html", true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString target = WebString::fromUTF8("target"); |
| WebString onselectstartfalse = WebString::fromUTF8("onselectstartfalse"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureLongPress, onselectstartfalse)); |
| EXPECT_EQ("", std::string(frame->selectionAsText().utf8().data())); |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureLongPress, target)); |
| EXPECT_EQ("testword", std::string(frame->selectionAsText().utf8().data())); |
| } |
| |
| #if !OS(MACOSX) |
| TEST_P(WebViewTest, TouchDoesntSelectEmptyTextarea) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("longpress_textarea.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "longpress_textarea.html", true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString blanklinestextbox = WebString::fromUTF8("blanklinestextbox"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| |
| // Long-press on carriage returns. |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureLongPress, blanklinestextbox)); |
| EXPECT_TRUE(frame->selectionAsText().isEmpty()); |
| |
| // Double-tap on carriage returns. |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureTap; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 100; |
| event.y = 25; |
| event.data.tap.tapCount = 2; |
| |
| webView->handleInputEvent(event); |
| EXPECT_TRUE(frame->selectionAsText().isEmpty()); |
| |
| HTMLTextAreaElement* textAreaElement = toHTMLTextAreaElement( |
| webView->mainFrame()->document().getElementById(blanklinestextbox)); |
| textAreaElement->setValue("hello"); |
| |
| // Long-press past last word of textbox. |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureLongPress, blanklinestextbox)); |
| EXPECT_TRUE(frame->selectionAsText().isEmpty()); |
| |
| // Double-tap past last word of textbox. |
| webView->handleInputEvent(event); |
| EXPECT_TRUE(frame->selectionAsText().isEmpty()); |
| } |
| #endif |
| |
| TEST_P(WebViewTest, LongPressImageTextarea) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("longpress_image_contenteditable.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "longpress_image_contenteditable.html", true); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString image = WebString::fromUTF8("purpleimage"); |
| |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureLongPress, image)); |
| WebRange range = webView->caretOrSelectionRange(); |
| EXPECT_FALSE(range.isNull()); |
| EXPECT_EQ(0, range.startOffset()); |
| EXPECT_EQ(1, range.length()); |
| } |
| |
| TEST_P(WebViewTest, BlinkCaretAfterLongPress) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("blink_caret_on_typing_after_long_press.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "blink_caret_on_typing_after_long_press.html", true); |
| webView->resize(WebSize(640, 480)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebString target = WebString::fromUTF8("target"); |
| WebLocalFrameImpl* mainFrame = webView->mainFrameImpl(); |
| |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureLongPress, target)); |
| EXPECT_FALSE(mainFrame->frame()->selection().isCaretBlinkingSuspended()); |
| } |
| |
| TEST_P(WebViewTest, BlinkCaretOnClosingContextMenu) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("form.html")); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "form.html", true); |
| |
| webView->setInitialFocus(false); |
| runPendingTasks(); |
| |
| // We suspend caret blinking when pressing with mouse right button. |
| // Note that we do not send MouseUp event here since it will be consumed |
| // by the context menu once it shows up. |
| WebMouseEvent mouseEvent; |
| mouseEvent.button = WebMouseEvent::Button::Right; |
| mouseEvent.x = 1; |
| mouseEvent.y = 1; |
| mouseEvent.clickCount = 1; |
| mouseEvent.type = WebInputEvent::MouseDown; |
| webView->handleInputEvent(mouseEvent); |
| runPendingTasks(); |
| |
| WebLocalFrameImpl* mainFrame = webView->mainFrameImpl(); |
| EXPECT_TRUE(mainFrame->frame()->selection().isCaretBlinkingSuspended()); |
| |
| // Caret blinking is still suspended after showing context menu. |
| webView->showContextMenu(); |
| EXPECT_TRUE(mainFrame->frame()->selection().isCaretBlinkingSuspended()); |
| |
| // Caret blinking will be resumed only after context menu is closed. |
| webView->didCloseContextMenu(); |
| |
| EXPECT_FALSE(mainFrame->frame()->selection().isCaretBlinkingSuspended()); |
| } |
| |
| TEST_P(WebViewTest, SelectionOnReadOnlyInput) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("selection_readonly.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "selection_readonly.html", true); |
| webView->resize(WebSize(640, 480)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| std::string testWord = "This text should be selected."; |
| |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| EXPECT_EQ(testWord, std::string(frame->selectionAsText().utf8().data())); |
| |
| WebRange range = webView->caretOrSelectionRange(); |
| EXPECT_FALSE(range.isNull()); |
| EXPECT_EQ(0, range.startOffset()); |
| EXPECT_EQ(static_cast<int>(testWord.length()), range.length()); |
| } |
| |
| TEST_P(WebViewTest, KeyDownScrollsHandled) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("content-width-1000.html")); |
| |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "content-width-1000.html", true); |
| webView->resize(WebSize(100, 100)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| WebKeyboardEvent keyEvent; |
| |
| // RawKeyDown pagedown should be handled. |
| keyEvent.windowsKeyCode = VKEY_NEXT; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| // Coalesced KeyDown arrow-down should be handled. |
| keyEvent.windowsKeyCode = VKEY_DOWN; |
| keyEvent.type = WebInputEvent::KeyDown; |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| // Ctrl-Home should be handled... |
| keyEvent.windowsKeyCode = VKEY_HOME; |
| keyEvent.modifiers = WebInputEvent::ControlKey; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::NotHandled, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| // But Ctrl-Down should not. |
| keyEvent.windowsKeyCode = VKEY_DOWN; |
| keyEvent.modifiers = WebInputEvent::ControlKey; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::NotHandled, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| // Shift, meta, and alt should not be handled. |
| keyEvent.windowsKeyCode = VKEY_NEXT; |
| keyEvent.modifiers = WebInputEvent::ShiftKey; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::NotHandled, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| keyEvent.windowsKeyCode = VKEY_NEXT; |
| keyEvent.modifiers = WebInputEvent::MetaKey; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::NotHandled, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| keyEvent.windowsKeyCode = VKEY_NEXT; |
| keyEvent.modifiers = WebInputEvent::AltKey; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::NotHandled, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| // System-key labeled Alt-Down (as in Windows) should do nothing, |
| // but non-system-key labeled Alt-Down (as in Mac) should be handled |
| // as a page-down. |
| keyEvent.windowsKeyCode = VKEY_DOWN; |
| keyEvent.modifiers = WebInputEvent::AltKey; |
| keyEvent.isSystemKey = true; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::NotHandled, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| keyEvent.windowsKeyCode = VKEY_DOWN; |
| keyEvent.modifiers = WebInputEvent::AltKey; |
| keyEvent.isSystemKey = false; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| EXPECT_EQ(WebInputEventResult::HandledSystem, |
| webView->handleInputEvent(keyEvent)); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| } |
| |
| static void configueCompositingWebView(WebSettings* settings) { |
| settings->setAcceleratedCompositingEnabled(true); |
| settings->setPreferCompositingToLCDTextEnabled(true); |
| } |
| |
| TEST_P(WebViewTest, ShowPressOnTransformedLink) { |
| std::unique_ptr<FrameTestHelpers::TestWebViewClient> |
| fakeCompositingWebViewClient = |
| makeUnique<FrameTestHelpers::TestWebViewClient>(); |
| FrameTestHelpers::WebViewHelper webViewHelper; |
| WebViewImpl* webViewImpl = webViewHelper.initialize( |
| true, nullptr, fakeCompositingWebViewClient.get(), nullptr, |
| &configueCompositingWebView); |
| |
| int pageWidth = 640; |
| int pageHeight = 480; |
| webViewImpl->resize(WebSize(pageWidth, pageHeight)); |
| |
| WebURL baseURL = URLTestHelpers::toKURL("http://example.com/"); |
| FrameTestHelpers::loadHTMLString( |
| webViewImpl->mainFrame(), |
| "<a href='http://www.test.com' style='position: absolute; left: 20px; " |
| "top: 20px; width: 200px; transform:translateZ(0);'>A link to " |
| "highlight</a>", |
| baseURL); |
| |
| WebGestureEvent event; |
| event.type = WebInputEvent::GestureShowPress; |
| event.sourceDevice = WebGestureDeviceTouchscreen; |
| event.x = 20; |
| event.y = 20; |
| |
| // Just make sure we don't hit any asserts. |
| webViewImpl->handleInputEvent(event); |
| } |
| |
| class MockAutofillClient : public WebAutofillClient { |
| public: |
| MockAutofillClient() |
| : m_ignoreTextChanges(false), |
| m_textChangesFromUserGesture(0), |
| m_textChangesWhileIgnored(0), |
| m_textChangesWhileNotIgnored(0), |
| m_userGestureNotificationsCount(0) {} |
| |
| ~MockAutofillClient() override {} |
| |
| void setIgnoreTextChanges(bool ignore) override { |
| m_ignoreTextChanges = ignore; |
| } |
| void textFieldDidChange(const WebFormControlElement&) override { |
| if (m_ignoreTextChanges) |
| ++m_textChangesWhileIgnored; |
| else |
| ++m_textChangesWhileNotIgnored; |
| |
| if (UserGestureIndicator::processingUserGesture()) |
| ++m_textChangesFromUserGesture; |
| } |
| void firstUserGestureObserved() override { |
| ++m_userGestureNotificationsCount; |
| } |
| |
| void clearChangeCounts() { |
| m_textChangesWhileIgnored = 0; |
| m_textChangesWhileNotIgnored = 0; |
| } |
| |
| int textChangesFromUserGesture() { return m_textChangesFromUserGesture; } |
| int textChangesWhileIgnored() { return m_textChangesWhileIgnored; } |
| int textChangesWhileNotIgnored() { return m_textChangesWhileNotIgnored; } |
| int getUserGestureNotificationsCount() { |
| return m_userGestureNotificationsCount; |
| } |
| |
| private: |
| bool m_ignoreTextChanges; |
| int m_textChangesFromUserGesture; |
| int m_textChangesWhileIgnored; |
| int m_textChangesWhileNotIgnored; |
| int m_userGestureNotificationsCount; |
| }; |
| |
| TEST_P(WebViewTest, LosingFocusDoesNotTriggerAutofillTextChange) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| // Set up a composition that needs to be committed. |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| frame->setEditableSelectionOffsets(4, 10); |
| frame->setCompositionFromExistingText(8, 12, emptyUnderlines); |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ(4, info.selectionStart); |
| EXPECT_EQ(10, info.selectionEnd); |
| EXPECT_EQ(8, info.compositionStart); |
| EXPECT_EQ(12, info.compositionEnd); |
| |
| // Clear the focus and track that the subsequent composition commit does not |
| // trigger a text changed notification for autofill. |
| client.clearChangeCounts(); |
| webView->setFocus(false); |
| EXPECT_EQ(0, client.textChangesWhileNotIgnored()); |
| |
| frame->setAutofillClient(0); |
| } |
| |
| static void verifySelectionAndComposition(WebView* webView, |
| int selectionStart, |
| int selectionEnd, |
| int compositionStart, |
| int compositionEnd, |
| const char* failMessage) { |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ(selectionStart, info.selectionStart) << failMessage; |
| EXPECT_EQ(selectionEnd, info.selectionEnd) << failMessage; |
| EXPECT_EQ(compositionStart, info.compositionStart) << failMessage; |
| EXPECT_EQ(compositionEnd, info.compositionEnd) << failMessage; |
| } |
| |
| TEST_P(WebViewTest, CompositionNotCancelledByBackspace) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("composition_not_cancelled_by_backspace.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "composition_not_cancelled_by_backspace.html"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| // Test both input elements. |
| for (int i = 0; i < 2; ++i) { |
| // Select composition and do sanity check. |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| frame->setEditableSelectionOffsets(6, 6); |
| WebInputMethodController* activeInputMethodController = |
| frame->frameWidget()->getActiveWebInputMethodController(); |
| EXPECT_TRUE(activeInputMethodController->setComposition( |
| "fghij", emptyUnderlines, 0, 5)); |
| frame->setEditableSelectionOffsets(11, 11); |
| verifySelectionAndComposition(webView, 11, 11, 6, 11, "initial case"); |
| |
| // Press Backspace and verify composition didn't get cancelled. This is to |
| // verify the fix for crbug.com/429916. |
| WebKeyboardEvent keyEvent; |
| keyEvent.domKey = Platform::current()->domKeyEnumFromString("\b"); |
| keyEvent.windowsKeyCode = VKEY_BACK; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| webView->handleInputEvent(keyEvent); |
| |
| frame->setEditableSelectionOffsets(6, 6); |
| EXPECT_TRUE(activeInputMethodController->setComposition( |
| "fghi", emptyUnderlines, 0, 4)); |
| frame->setEditableSelectionOffsets(10, 10); |
| verifySelectionAndComposition(webView, 10, 10, 6, 10, |
| "after pressing Backspace"); |
| |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| webView->advanceFocus(false); |
| } |
| |
| frame->setAutofillClient(0); |
| } |
| |
| TEST_P(WebViewTest, FinishComposingTextTriggersAutofillTextChange) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| WebInputMethodController* activeInputMethodController = |
| frame->frameWidget()->getActiveWebInputMethodController(); |
| // Set up a composition that needs to be committed. |
| std::string compositionText("testingtext"); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| activeInputMethodController->setComposition( |
| WebString::fromUTF8(compositionText.c_str()), emptyUnderlines, 0, |
| compositionText.length()); |
| |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ(0, info.selectionStart); |
| EXPECT_EQ((int)compositionText.length(), info.selectionEnd); |
| EXPECT_EQ(0, info.compositionStart); |
| EXPECT_EQ((int)compositionText.length(), info.compositionEnd); |
| |
| client.clearChangeCounts(); |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::KeepSelection); |
| EXPECT_EQ(0, client.textChangesWhileIgnored()); |
| EXPECT_EQ(1, client.textChangesWhileNotIgnored()); |
| |
| frame->setAutofillClient(0); |
| } |
| |
| TEST_P(WebViewTest, SetCompositionFromExistingTextTriggersAutofillTextChange) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html", true); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| |
| client.clearChangeCounts(); |
| frame->setCompositionFromExistingText(8, 12, emptyUnderlines); |
| |
| WebTextInputInfo info = webView->textInputInfo(); |
| EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", |
| std::string(info.value.utf8().data())); |
| EXPECT_EQ(8, info.compositionStart); |
| EXPECT_EQ(12, info.compositionEnd); |
| |
| EXPECT_EQ(0, client.textChangesWhileIgnored()); |
| EXPECT_EQ(0, client.textChangesWhileNotIgnored()); |
| |
| WebDocument document = webView->mainFrame()->document(); |
| EXPECT_EQ(WebString::fromUTF8("none"), |
| document.getElementById("inputEvent").firstChild().nodeValue()); |
| |
| frame->setAutofillClient(0); |
| } |
| |
| class ViewCreatingWebViewClient : public FrameTestHelpers::TestWebViewClient { |
| public: |
| ViewCreatingWebViewClient() : m_didFocusCalled(false) {} |
| |
| // WebViewClient methods |
| WebView* createView(WebLocalFrame* opener, |
| const WebURLRequest&, |
| const WebWindowFeatures&, |
| const WebString& name, |
| WebNavigationPolicy, |
| bool) override { |
| return m_webViewHelper.initializeWithOpener(opener, true); |
| } |
| |
| // WebWidgetClient methods |
| void didFocus() override { m_didFocusCalled = true; } |
| |
| bool didFocusCalled() const { return m_didFocusCalled; } |
| WebView* createdWebView() const { return m_webViewHelper.webView(); } |
| |
| private: |
| FrameTestHelpers::WebViewHelper m_webViewHelper; |
| bool m_didFocusCalled; |
| }; |
| |
| TEST_P(WebViewTest, DoNotFocusCurrentFrameOnNavigateFromLocalFrame) { |
| ViewCreatingWebViewClient client; |
| FrameTestHelpers::WebViewHelper m_webViewHelper; |
| WebViewImpl* webViewImpl = m_webViewHelper.initialize(true, 0, &client); |
| webViewImpl->page()->settings().setJavaScriptCanOpenWindowsAutomatically( |
| true); |
| |
| WebURL baseURL = URLTestHelpers::toKURL("http://example.com/"); |
| FrameTestHelpers::loadHTMLString( |
| webViewImpl->mainFrame(), |
| "<html><body><iframe src=\"about:blank\"></iframe></body></html>", |
| baseURL); |
| |
| // Make a request from a local frame. |
| WebURLRequest webURLRequestWithTargetStart; |
| LocalFrame* localFrame = |
| toWebLocalFrameImpl(webViewImpl->mainFrame()->firstChild())->frame(); |
| FrameLoadRequest requestWithTargetStart( |
| localFrame->document(), webURLRequestWithTargetStart.toResourceRequest(), |
| "_top"); |
| localFrame->loader().load(requestWithTargetStart); |
| EXPECT_FALSE(client.didFocusCalled()); |
| |
| m_webViewHelper.reset(); // Remove dependency on locally scoped client. |
| } |
| |
| TEST_P(WebViewTest, FocusExistingFrameOnNavigate) { |
| ViewCreatingWebViewClient client; |
| FrameTestHelpers::WebViewHelper m_webViewHelper; |
| WebViewImpl* webViewImpl = m_webViewHelper.initialize(true, 0, &client); |
| webViewImpl->page()->settings().setJavaScriptCanOpenWindowsAutomatically( |
| true); |
| WebLocalFrameImpl* frame = webViewImpl->mainFrameImpl(); |
| frame->setName("_start"); |
| |
| // Make a request that will open a new window |
| WebURLRequest webURLRequest; |
| FrameLoadRequest request(0, webURLRequest.toResourceRequest(), "_blank"); |
| toLocalFrame(webViewImpl->page()->mainFrame())->loader().load(request); |
| ASSERT_TRUE(client.createdWebView()); |
| EXPECT_FALSE(client.didFocusCalled()); |
| |
| // Make a request from the new window that will navigate the original window. |
| // The original window should be focused. |
| WebURLRequest webURLRequestWithTargetStart; |
| FrameLoadRequest requestWithTargetStart( |
| 0, webURLRequestWithTargetStart.toResourceRequest(), "_start"); |
| toLocalFrame(toWebViewImpl(client.createdWebView())->page()->mainFrame()) |
| ->loader() |
| .load(requestWithTargetStart); |
| EXPECT_TRUE(client.didFocusCalled()); |
| |
| m_webViewHelper.reset(); // Remove dependency on locally scoped client. |
| } |
| |
| TEST_P(WebViewTest, DispatchesFocusOutFocusInOnViewToggleFocus) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), "focusout_focusin_events.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "focusout_focusin_events.html", true, 0); |
| |
| webView->setFocus(true); |
| webView->setFocus(false); |
| webView->setFocus(true); |
| |
| WebElement element = |
| webView->mainFrame()->document().getElementById("message"); |
| EXPECT_STREQ("focusoutfocusin", element.textContent().utf8().data()); |
| } |
| |
| TEST_P(WebViewTest, DispatchesDomFocusOutDomFocusInOnViewToggleFocus) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| "domfocusout_domfocusin_events.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "domfocusout_domfocusin_events.html", true, 0); |
| |
| webView->setFocus(true); |
| webView->setFocus(false); |
| webView->setFocus(true); |
| |
| WebElement element = |
| webView->mainFrame()->document().getElementById("message"); |
| EXPECT_STREQ("DOMFocusOutDOMFocusIn", element.textContent().utf8().data()); |
| } |
| |
| static void openDateTimeChooser(WebView* webView, |
| HTMLInputElement* inputElement) { |
| inputElement->focus(); |
| |
| WebKeyboardEvent keyEvent; |
| keyEvent.domKey = Platform::current()->domKeyEnumFromString(" "); |
| keyEvent.windowsKeyCode = VKEY_SPACE; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| webView->handleInputEvent(keyEvent); |
| |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| } |
| |
| // TODO(crbug.com/605112) This test is crashing on Android (Nexus 4) bot. |
| #if OS(ANDROID) |
| TEST_P(WebViewTest, DISABLED_ChooseValueFromDateTimeChooser) { |
| #else |
| TEST_P(WebViewTest, ChooseValueFromDateTimeChooser) { |
| #endif |
| bool originalMultipleFieldsFlag = |
| RuntimeEnabledFeatures::inputMultipleFieldsUIEnabled(); |
| RuntimeEnabledFeatures::setInputMultipleFieldsUIEnabled(false); |
| DateTimeChooserWebViewClient client; |
| std::string url = m_baseURL + "date_time_chooser.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "date_time_chooser.html"); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(url, true, 0, &client); |
| |
| Document* document = webViewImpl->mainFrameImpl()->frame()->document(); |
| |
| HTMLInputElement* inputElement; |
| |
| inputElement = toHTMLInputElement(document->getElementById("date")); |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue(0); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("1970-01-01", inputElement->value().utf8().data()); |
| |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue( |
| std::numeric_limits<double>::quiet_NaN()); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("", inputElement->value().utf8().data()); |
| |
| inputElement = toHTMLInputElement(document->getElementById("datetimelocal")); |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue(0); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("1970-01-01T00:00", inputElement->value().utf8().data()); |
| |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue( |
| std::numeric_limits<double>::quiet_NaN()); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("", inputElement->value().utf8().data()); |
| |
| inputElement = toHTMLInputElement(document->getElementById("month")); |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue(0); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("1970-01", inputElement->value().utf8().data()); |
| |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue( |
| std::numeric_limits<double>::quiet_NaN()); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("", inputElement->value().utf8().data()); |
| |
| inputElement = toHTMLInputElement(document->getElementById("time")); |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue(0); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("00:00", inputElement->value().utf8().data()); |
| |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue( |
| std::numeric_limits<double>::quiet_NaN()); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("", inputElement->value().utf8().data()); |
| |
| inputElement = toHTMLInputElement(document->getElementById("week")); |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue(0); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("1970-W01", inputElement->value().utf8().data()); |
| |
| openDateTimeChooser(webViewImpl, inputElement); |
| client.chooserCompletion()->didChooseValue( |
| std::numeric_limits<double>::quiet_NaN()); |
| client.clearChooserCompletion(); |
| EXPECT_STREQ("", inputElement->value().utf8().data()); |
| |
| // Clear the WebViewClient from the webViewHelper to avoid use-after-free in |
| // the WebViewHelper destructor. |
| m_webViewHelper.reset(); |
| RuntimeEnabledFeatures::setInputMultipleFieldsUIEnabled( |
| originalMultipleFieldsFlag); |
| } |
| |
| TEST_P(WebViewTest, DispatchesFocusBlurOnViewToggle) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), "focus_blur_events.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "focus_blur_events.html", true, 0); |
| |
| webView->setFocus(true); |
| webView->setFocus(false); |
| webView->setFocus(true); |
| |
| WebElement element = |
| webView->mainFrame()->document().getElementById("message"); |
| // Expect not to see duplication of events. |
| EXPECT_STREQ("blurfocus", element.textContent().utf8().data()); |
| } |
| |
| TEST_P(WebViewTest, SmartClipData) { |
| static const char kExpectedClipText[] = "\nPrice 10,000,000won"; |
| static const char kExpectedClipHtml[] = |
| "<div id=\"div4\" style=\"padding: 10px; margin: 10px; border: 2px " |
| "solid skyblue; float: left; width: 190px; height: 30px; " |
| "color: rgb(0, 0, 0); font-family: myahem; font-size: 8px; font-style: " |
| "normal; font-variant-ligatures: normal; font-variant-caps: normal; " |
| "font-weight: normal; letter-spacing: " |
| "normal; orphans: 2; text-align: start; " |
| "text-indent: 0px; text-transform: none; white-space: normal; widows: " |
| "2; word-spacing: 0px; -webkit-text-stroke-width: 0px; " |
| "text-decoration-style: initial; text-decoration-color: initial;\">Air " |
| "conditioner</div><div id=\"div5\" style=\"padding: 10px; margin: " |
| "10px; border: 2px solid skyblue; float: left; width: " |
| "190px; height: 30px; color: rgb(0, 0, 0); font-family: myahem; " |
| "font-size: 8px; font-style: normal; font-variant-ligatures: normal; " |
| "font-variant-caps: normal; font-weight: normal; " |
| "letter-spacing: normal; orphans: 2; " |
| "text-align: start; text-indent: 0px; text-transform: " |
| "none; white-space: normal; widows: 2; word-spacing: 0px; " |
| "-webkit-text-stroke-width: 0px; text-decoration-style: initial; " |
| "text-decoration-color: initial;\">Price 10,000,000won</div>"; |
| WebString clipText; |
| WebString clipHtml; |
| WebRect clipRect; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("smartclip.html")); |
| WebView* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "smartclip.html"); |
| webView->resize(WebSize(500, 500)); |
| webView->updateAllLifecyclePhases(); |
| WebRect cropRect(300, 125, 152, 50); |
| webView->extractSmartClipData(cropRect, clipText, clipHtml, clipRect); |
| EXPECT_STREQ(kExpectedClipText, clipText.utf8().c_str()); |
| EXPECT_STREQ(kExpectedClipHtml, clipHtml.utf8().c_str()); |
| } |
| |
| TEST_P(WebViewTest, SmartClipDataWithPinchZoom) { |
| static const char kExpectedClipText[] = "\nPrice 10,000,000won"; |
| static const char kExpectedClipHtml[] = |
| "<div id=\"div4\" style=\"padding: 10px; margin: 10px; border: 2px " |
| "solid skyblue; float: left; width: 190px; height: 30px; " |
| "color: rgb(0, 0, 0); font-family: myahem; font-size: 8px; font-style: " |
| "normal; font-variant-ligatures: normal; font-variant-caps: normal; " |
| "font-weight: normal; letter-spacing: " |
| "normal; orphans: 2; text-align: start; " |
| "text-indent: 0px; text-transform: none; white-space: normal; widows: " |
| "2; word-spacing: 0px; -webkit-text-stroke-width: 0px; " |
| "text-decoration-style: initial; text-decoration-color: initial;\">Air " |
| "conditioner</div><div id=\"div5\" style=\"padding: 10px; margin: " |
| "10px; border: 2px solid skyblue; float: left; width: " |
| "190px; height: 30px; color: rgb(0, 0, 0); font-family: myahem; " |
| "font-size: 8px; font-style: normal; font-variant-ligatures: normal; " |
| "font-variant-caps: normal; font-weight: normal; letter-spacing: normal; " |
| "orphans: 2; text-align: start; text-indent: 0px; " |
| "text-transform: none; white-space: normal; widows: 2; " |
| "word-spacing: 0px; -webkit-text-stroke-width: 0px;" |
| " text-decoration-style: initial; text-decoration-color: initial;\">" |
| "Price 10,000,000won</div>"; |
| WebString clipText; |
| WebString clipHtml; |
| WebRect clipRect; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("smartclip.html")); |
| WebView* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "smartclip.html"); |
| webView->resize(WebSize(500, 500)); |
| webView->updateAllLifecyclePhases(); |
| webView->setPageScaleFactor(1.5); |
| webView->setVisualViewportOffset(WebFloatPoint(167, 100)); |
| WebRect cropRect(200, 38, 228, 75); |
| webView->extractSmartClipData(cropRect, clipText, clipHtml, clipRect); |
| EXPECT_STREQ(kExpectedClipText, clipText.utf8().c_str()); |
| EXPECT_STREQ(kExpectedClipHtml, clipHtml.utf8().c_str()); |
| } |
| |
| TEST_P(WebViewTest, SmartClipReturnsEmptyStringsWhenUserSelectIsNone) { |
| WebString clipText; |
| WebString clipHtml; |
| WebRect clipRect; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("smartclip_user_select_none.html")); |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "smartclip_user_select_none.html"); |
| webView->resize(WebSize(500, 500)); |
| webView->updateAllLifecyclePhases(); |
| WebRect cropRect(0, 0, 100, 100); |
| webView->extractSmartClipData(cropRect, clipText, clipHtml, clipRect); |
| EXPECT_STREQ("", clipText.utf8().c_str()); |
| EXPECT_STREQ("", clipHtml.utf8().c_str()); |
| } |
| |
| TEST_P(WebViewTest, SmartClipDoesNotCrashPositionReversed) { |
| WebString clipText; |
| WebString clipHtml; |
| WebRect clipRect; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("smartclip_reversed_positions.html")); |
| WebView* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "smartclip_reversed_positions.html"); |
| webView->resize(WebSize(500, 500)); |
| webView->updateAllLifecyclePhases(); |
| // Left upper corner of the rect will be end position in the DOM hierarchy. |
| WebRect cropRect(30, 110, 400, 250); |
| // This should not still crash. See crbug.com/589082 for more details. |
| webView->extractSmartClipData(cropRect, clipText, clipHtml, clipRect); |
| } |
| |
| class CreateChildCounterFrameClient |
| : public FrameTestHelpers::TestWebFrameClient { |
| public: |
| CreateChildCounterFrameClient() : m_count(0) {} |
| WebLocalFrame* createChildFrame(WebLocalFrame* parent, |
| WebTreeScopeType, |
| const WebString& name, |
| const WebString& uniqueName, |
| WebSandboxFlags, |
| const WebFrameOwnerProperties&) override; |
| |
| int count() const { return m_count; } |
| |
| private: |
| int m_count; |
| }; |
| |
| WebLocalFrame* CreateChildCounterFrameClient::createChildFrame( |
| WebLocalFrame* parent, |
| WebTreeScopeType scope, |
| const WebString& name, |
| const WebString& uniqueName, |
| WebSandboxFlags sandboxFlags, |
| const WebFrameOwnerProperties& frameOwnerProperties) { |
| ++m_count; |
| return TestWebFrameClient::createChildFrame( |
| parent, scope, name, uniqueName, sandboxFlags, frameOwnerProperties); |
| } |
| |
| TEST_P(WebViewTest, ChangeDisplayMode) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("display_mode.html")); |
| WebView* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "display_mode.html", true); |
| |
| std::string content = |
| WebFrameContentDumper::dumpWebViewAsText(webView, 21).utf8(); |
| EXPECT_EQ("regular-ui", content); |
| |
| webView->setDisplayMode(WebDisplayModeMinimalUi); |
| content = WebFrameContentDumper::dumpWebViewAsText(webView, 21).utf8(); |
| EXPECT_EQ("minimal-ui", content); |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, AddFrameInCloseUnload) { |
| CreateChildCounterFrameClient frameClient; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("add_frame_in_unload.html")); |
| m_webViewHelper.initializeAndLoad(m_baseURL + "add_frame_in_unload.html", |
| true, &frameClient); |
| m_webViewHelper.reset(); |
| EXPECT_EQ(0, frameClient.count()); |
| } |
| |
| TEST_P(WebViewTest, AddFrameInCloseURLUnload) { |
| CreateChildCounterFrameClient frameClient; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("add_frame_in_unload.html")); |
| m_webViewHelper.initializeAndLoad(m_baseURL + "add_frame_in_unload.html", |
| true, &frameClient); |
| m_webViewHelper.webView()->mainFrame()->dispatchUnloadEvent(); |
| EXPECT_EQ(0, frameClient.count()); |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, AddFrameInNavigateUnload) { |
| CreateChildCounterFrameClient frameClient; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("add_frame_in_unload.html")); |
| m_webViewHelper.initializeAndLoad(m_baseURL + "add_frame_in_unload.html", |
| true, &frameClient); |
| FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), |
| "about:blank"); |
| EXPECT_EQ(0, frameClient.count()); |
| m_webViewHelper.reset(); |
| } |
| |
| TEST_P(WebViewTest, AddFrameInChildInNavigateUnload) { |
| CreateChildCounterFrameClient frameClient; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("add_frame_in_unload_wrapper.html")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("add_frame_in_unload.html")); |
| m_webViewHelper.initializeAndLoad( |
| m_baseURL + "add_frame_in_unload_wrapper.html", true, &frameClient); |
| FrameTestHelpers::loadFrame(m_webViewHelper.webView()->mainFrame(), |
| "about:blank"); |
| EXPECT_EQ(1, frameClient.count()); |
| m_webViewHelper.reset(); |
| } |
| |
| class TouchEventHandlerWebViewClient |
| : public FrameTestHelpers::TestWebViewClient { |
| public: |
| // WebWidgetClient methods |
| void hasTouchEventHandlers(bool state) override { |
| m_hasTouchEventHandlerCount[state]++; |
| } |
| |
| // Local methods |
| TouchEventHandlerWebViewClient() : m_hasTouchEventHandlerCount() {} |
| |
| int getAndResetHasTouchEventHandlerCallCount(bool state) { |
| int value = m_hasTouchEventHandlerCount[state]; |
| m_hasTouchEventHandlerCount[state] = 0; |
| return value; |
| } |
| |
| private: |
| int m_hasTouchEventHandlerCount[2]; |
| }; |
| |
| // This test verifies that WebWidgetClient::hasTouchEventHandlers is called |
| // accordingly for various calls to EventHandlerRegistry::did{Add|Remove| |
| // RemoveAll}EventHandler(..., TouchEvent). Verifying that those calls are made |
| // correctly is the job of LayoutTests/fast/events/event-handler-count.html. |
| TEST_P(WebViewTest, HasTouchEventHandlers) { |
| TouchEventHandlerWebViewClient client; |
| std::string url = m_baseURL + "has_touch_event_handlers.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), |
| "has_touch_event_handlers.html"); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(url, true, 0, &client); |
| const EventHandlerRegistry::EventHandlerClass touchEvent = |
| EventHandlerRegistry::TouchStartOrMoveEventBlocking; |
| |
| // The page is initialized with at least one no-handlers call. |
| // In practice we get two such calls because WebViewHelper::initializeAndLoad |
| // first initializes and empty frame, and then loads a document into it, so |
| // there are two FrameLoader::commitProvisionalLoad calls. |
| EXPECT_GE(client.getAndResetHasTouchEventHandlerCallCount(false), 1); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding the first document handler results in a has-handlers call. |
| Document* document = webViewImpl->mainFrameImpl()->frame()->document(); |
| EventHandlerRegistry* registry = |
| &document->frameHost()->eventHandlerRegistry(); |
| registry->didAddEventHandler(*document, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding another handler has no effect. |
| registry->didAddEventHandler(*document, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Removing the duplicate handler has no effect. |
| registry->didRemoveEventHandler(*document, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Removing the final handler results in a no-handlers call. |
| registry->didRemoveEventHandler(*document, touchEvent); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding a handler on a div results in a has-handlers call. |
| Element* parentDiv = document->getElementById("parentdiv"); |
| DCHECK(parentDiv); |
| registry->didAddEventHandler(*parentDiv, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding a duplicate handler on the div, clearing all document handlers |
| // (of which there are none) and removing the extra handler on the div |
| // all have no effect. |
| registry->didAddEventHandler(*parentDiv, touchEvent); |
| registry->didRemoveAllEventHandlers(*document); |
| registry->didRemoveEventHandler(*parentDiv, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Removing the final handler on the div results in a no-handlers call. |
| registry->didRemoveEventHandler(*parentDiv, touchEvent); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding two handlers then clearing them in a single call results in a |
| // has-handlers then no-handlers call. |
| registry->didAddEventHandler(*parentDiv, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| registry->didAddEventHandler(*parentDiv, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| registry->didRemoveAllEventHandlers(*parentDiv); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding a handler inside of a child iframe results in a has-handlers call. |
| Element* childFrame = document->getElementById("childframe"); |
| DCHECK(childFrame); |
| Document* childDocument = toHTMLIFrameElement(childFrame)->contentDocument(); |
| Element* childDiv = childDocument->getElementById("childdiv"); |
| DCHECK(childDiv); |
| registry->didAddEventHandler(*childDiv, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding and clearing handlers in the parent doc or elsewhere in the child |
| // doc has no impact. |
| registry->didAddEventHandler(*document, touchEvent); |
| registry->didAddEventHandler(*childFrame, touchEvent); |
| registry->didAddEventHandler(*childDocument, touchEvent); |
| registry->didRemoveAllEventHandlers(*document); |
| registry->didRemoveAllEventHandlers(*childFrame); |
| registry->didRemoveAllEventHandlers(*childDocument); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Removing the final handler inside the child frame results in a no-handlers |
| // call. |
| registry->didRemoveAllEventHandlers(*childDiv); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding a handler inside the child frame results in a has-handlers call. |
| registry->didAddEventHandler(*childDocument, touchEvent); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Adding a handler in the parent document and removing the one in the frame |
| // has no effect. |
| registry->didAddEventHandler(*childFrame, touchEvent); |
| registry->didRemoveEventHandler(*childDocument, touchEvent); |
| registry->didRemoveAllEventHandlers(*childDocument); |
| registry->didRemoveAllEventHandlers(*document); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Now removing the handler in the parent document results in a no-handlers |
| // call. |
| registry->didRemoveEventHandler(*childFrame, touchEvent); |
| EXPECT_EQ(1, client.getAndResetHasTouchEventHandlerCallCount(false)); |
| EXPECT_EQ(0, client.getAndResetHasTouchEventHandlerCallCount(true)); |
| |
| // Free the webView before the TouchEventHandlerWebViewClient gets freed. |
| m_webViewHelper.reset(); |
| } |
| |
| // This test checks that deleting nodes which have only non-JS-registered touch |
| // handlers also removes them from the event handler registry. Note that this |
| // is different from detaching and re-attaching the same node, which is covered |
| // by layout tests under fast/events/. |
| TEST_P(WebViewTest, DeleteElementWithRegisteredHandler) { |
| std::string url = m_baseURL + "simple_div.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "simple_div.html"); |
| WebViewImpl* webViewImpl = m_webViewHelper.initializeAndLoad(url, true); |
| |
| Persistent<Document> document = |
| webViewImpl->mainFrameImpl()->frame()->document(); |
| Element* div = document->getElementById("div"); |
| EventHandlerRegistry& registry = |
| document->frameHost()->eventHandlerRegistry(); |
| |
| registry.didAddEventHandler(*div, EventHandlerRegistry::ScrollEvent); |
| EXPECT_TRUE(registry.hasEventHandlers(EventHandlerRegistry::ScrollEvent)); |
| |
| TrackExceptionState exceptionState; |
| div->remove(exceptionState); |
| |
| // For oilpan we have to force a GC to ensure the event handlers have been |
| // removed when checking below. We do a precise GC (collectAllGarbage does not |
| // scan the stack) to ensure the div element dies. This is also why the |
| // Document is in a Persistent since we want that to stay around. |
| ThreadState::current()->collectAllGarbage(); |
| |
| EXPECT_FALSE(registry.hasEventHandlers(EventHandlerRegistry::ScrollEvent)); |
| } |
| |
| class NonUserInputTextUpdateWebWidgetClient |
| : public FrameTestHelpers::TestWebWidgetClient { |
| public: |
| NonUserInputTextUpdateWebWidgetClient() : m_textIsUpdated(false) {} |
| |
| // WebWidgetClient methods |
| void didUpdateTextOfFocusedElementByNonUserInput() override { |
| m_textIsUpdated = true; |
| } |
| |
| void reset() { m_textIsUpdated = false; } |
| |
| bool textIsUpdated() const { return m_textIsUpdated; } |
| |
| private: |
| int m_textIsUpdated; |
| }; |
| |
| // This test verifies the text input flags are correctly exposed to script. |
| TEST_P(WebViewTest, TextInputFlags) { |
| std::string url = m_baseURL + "text_input_flags.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "text_input_flags.html"); |
| WebViewImpl* webViewImpl = m_webViewHelper.initializeAndLoad(url, true); |
| webViewImpl->setInitialFocus(false); |
| |
| WebLocalFrameImpl* frame = webViewImpl->mainFrameImpl(); |
| Document* document = frame->frame()->document(); |
| |
| // (A) <input> |
| // (A.1) Verifies autocorrect/autocomplete/spellcheck flags are Off and |
| // autocapitalize is set to none. |
| HTMLInputElement* inputElement = |
| toHTMLInputElement(document->getElementById("input")); |
| document->setFocusedElement( |
| inputElement, |
| FocusParams(SelectionBehaviorOnFocus::None, WebFocusTypeNone, nullptr)); |
| webViewImpl->setFocus(true); |
| WebTextInputInfo info1 = webViewImpl->textInputInfo(); |
| EXPECT_EQ(WebTextInputFlagAutocompleteOff | WebTextInputFlagAutocorrectOff | |
| WebTextInputFlagSpellcheckOff | |
| WebTextInputFlagAutocapitalizeNone, |
| info1.flags); |
| |
| // (A.2) Verifies autocorrect/autocomplete/spellcheck flags are On and |
| // autocapitalize is set to sentences. |
| inputElement = toHTMLInputElement(document->getElementById("input2")); |
| document->setFocusedElement( |
| inputElement, |
| FocusParams(SelectionBehaviorOnFocus::None, WebFocusTypeNone, nullptr)); |
| webViewImpl->setFocus(true); |
| WebTextInputInfo info2 = webViewImpl->textInputInfo(); |
| EXPECT_EQ(WebTextInputFlagAutocompleteOn | WebTextInputFlagAutocorrectOn | |
| WebTextInputFlagSpellcheckOn | |
| WebTextInputFlagAutocapitalizeSentences, |
| info2.flags); |
| |
| // (B) <textarea> Verifies the default text input flags are |
| // WebTextInputFlagAutocapitalizeSentences. |
| HTMLTextAreaElement* textAreaElement = |
| toHTMLTextAreaElement(document->getElementById("textarea")); |
| document->setFocusedElement( |
| textAreaElement, |
| FocusParams(SelectionBehaviorOnFocus::None, WebFocusTypeNone, nullptr)); |
| webViewImpl->setFocus(true); |
| WebTextInputInfo info3 = webViewImpl->textInputInfo(); |
| EXPECT_EQ(WebTextInputFlagAutocapitalizeSentences, info3.flags); |
| |
| // (C) Verifies the WebTextInputInfo's don't equal. |
| EXPECT_FALSE(info1.equals(info2)); |
| EXPECT_FALSE(info2.equals(info3)); |
| |
| // Free the webView before freeing the NonUserInputTextUpdateWebViewClient. |
| m_webViewHelper.reset(); |
| } |
| |
| // This test verifies that |
| // WebWidgetClient::didUpdateTextOfFocusedElementByNonUserInput is called iff |
| // value of a focused element is modified via script. |
| TEST_P(WebViewTest, NonUserInputTextUpdate) { |
| NonUserInputTextUpdateWebWidgetClient client; |
| std::string url = m_baseURL + "non_user_input_text_update.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), |
| "non_user_input_text_update.html"); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(url, true, nullptr, nullptr, &client); |
| webViewImpl->setInitialFocus(false); |
| |
| WebLocalFrameImpl* frame = webViewImpl->mainFrameImpl(); |
| Document* document = frame->frame()->document(); |
| WebInputMethodController* activeInputMethodController = |
| frame->frameWidget()->getActiveWebInputMethodController(); |
| |
| // (A) <input> |
| // (A.1) Focused and value is changed by script. |
| client.reset(); |
| EXPECT_FALSE(client.textIsUpdated()); |
| |
| HTMLInputElement* inputElement = |
| toHTMLInputElement(document->getElementById("input")); |
| document->setFocusedElement( |
| inputElement, |
| FocusParams(SelectionBehaviorOnFocus::None, WebFocusTypeNone, nullptr)); |
| webViewImpl->setFocus(true); |
| EXPECT_EQ(document->focusedElement(), static_cast<Element*>(inputElement)); |
| |
| // Emulate value change from script. |
| inputElement->setValue("testA"); |
| EXPECT_TRUE(client.textIsUpdated()); |
| WebTextInputInfo info = webViewImpl->textInputInfo(); |
| EXPECT_EQ("testA", std::string(info.value.utf8().data())); |
| |
| // (A.2) Focused and user input modifies value. |
| client.reset(); |
| EXPECT_FALSE(client.textIsUpdated()); |
| |
| WebVector<WebCompositionUnderline> emptyUnderlines; |
| activeInputMethodController->setComposition(WebString::fromUTF8("2"), |
| emptyUnderlines, 1, 1); |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::KeepSelection); |
| EXPECT_FALSE(client.textIsUpdated()); |
| info = webViewImpl->textInputInfo(); |
| EXPECT_EQ("testA2", std::string(info.value.utf8().data())); |
| |
| // (A.3) Unfocused and value is changed by script. |
| client.reset(); |
| EXPECT_FALSE(client.textIsUpdated()); |
| document->clearFocusedElement(); |
| webViewImpl->setFocus(false); |
| EXPECT_NE(document->focusedElement(), static_cast<Element*>(inputElement)); |
| inputElement->setValue("testA3"); |
| EXPECT_FALSE(client.textIsUpdated()); |
| |
| // (B) <textarea> |
| // (B.1) Focused and value is changed by script. |
| client.reset(); |
| EXPECT_FALSE(client.textIsUpdated()); |
| HTMLTextAreaElement* textAreaElement = |
| toHTMLTextAreaElement(document->getElementById("textarea")); |
| document->setFocusedElement( |
| textAreaElement, |
| FocusParams(SelectionBehaviorOnFocus::None, WebFocusTypeNone, nullptr)); |
| webViewImpl->setFocus(true); |
| EXPECT_EQ(document->focusedElement(), static_cast<Element*>(textAreaElement)); |
| textAreaElement->setValue("testB"); |
| EXPECT_TRUE(client.textIsUpdated()); |
| info = webViewImpl->textInputInfo(); |
| EXPECT_EQ("testB", std::string(info.value.utf8().data())); |
| |
| // (B.2) Focused and user input modifies value. |
| client.reset(); |
| EXPECT_FALSE(client.textIsUpdated()); |
| activeInputMethodController->setComposition(WebString::fromUTF8("2"), |
| emptyUnderlines, 1, 1); |
| activeInputMethodController->finishComposingText( |
| WebInputMethodController::KeepSelection); |
| info = webViewImpl->textInputInfo(); |
| EXPECT_EQ("testB2", std::string(info.value.utf8().data())); |
| |
| // (B.3) Unfocused and value is changed by script. |
| client.reset(); |
| EXPECT_FALSE(client.textIsUpdated()); |
| document->clearFocusedElement(); |
| webViewImpl->setFocus(false); |
| EXPECT_NE(document->focusedElement(), static_cast<Element*>(textAreaElement)); |
| inputElement->setValue("testB3"); |
| EXPECT_FALSE(client.textIsUpdated()); |
| |
| // Free the webView before freeing the NonUserInputTextUpdateWebViewClient. |
| m_webViewHelper.reset(); |
| } |
| |
| // Check that the WebAutofillClient is correctly notified about first user |
| // gestures after load, following various input events. |
| TEST_P(WebViewTest, FirstUserGestureObservedKeyEvent) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("form.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "form.html", true); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| EXPECT_EQ(0, client.getUserGestureNotificationsCount()); |
| |
| WebKeyboardEvent keyEvent; |
| keyEvent.domKey = Platform::current()->domKeyEnumFromString(" "); |
| keyEvent.windowsKeyCode = VKEY_SPACE; |
| keyEvent.type = WebInputEvent::RawKeyDown; |
| webView->handleInputEvent(keyEvent); |
| keyEvent.type = WebInputEvent::KeyUp; |
| webView->handleInputEvent(keyEvent); |
| |
| EXPECT_EQ(1, client.getUserGestureNotificationsCount()); |
| frame->setAutofillClient(0); |
| } |
| |
| TEST_P(WebViewTest, FirstUserGestureObservedMouseEvent) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("form.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "form.html", true); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| EXPECT_EQ(0, client.getUserGestureNotificationsCount()); |
| |
| WebMouseEvent mouseEvent; |
| mouseEvent.button = WebMouseEvent::Button::Left; |
| mouseEvent.x = 1; |
| mouseEvent.y = 1; |
| mouseEvent.clickCount = 1; |
| mouseEvent.type = WebInputEvent::MouseDown; |
| webView->handleInputEvent(mouseEvent); |
| mouseEvent.type = WebInputEvent::MouseUp; |
| webView->handleInputEvent(mouseEvent); |
| |
| EXPECT_EQ(1, client.getUserGestureNotificationsCount()); |
| frame->setAutofillClient(0); |
| } |
| |
| TEST_P(WebViewTest, FirstUserGestureObservedGestureTap) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("longpress_selection.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "longpress_selection.html", true); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| EXPECT_EQ(0, client.getUserGestureNotificationsCount()); |
| |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("target"))); |
| |
| EXPECT_EQ(1, client.getUserGestureNotificationsCount()); |
| frame->setAutofillClient(0); |
| } |
| |
| TEST_P(WebViewTest, CompositionIsUserGesture) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_populated.html"); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| MockAutofillClient client; |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| EXPECT_TRUE( |
| frame->frameWidget()->getActiveWebInputMethodController()->setComposition( |
| WebString::fromUTF8(std::string("hello").c_str()), |
| WebVector<WebCompositionUnderline>(), 3, 3)); |
| EXPECT_EQ(1, client.textChangesFromUserGesture()); |
| EXPECT_FALSE(UserGestureIndicator::processingUserGesture()); |
| EXPECT_TRUE(frame->hasMarkedText()); |
| |
| frame->setAutofillClient(0); |
| } |
| |
| TEST_P(WebViewTest, CompareSelectAllToContentAsText) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("longpress_selection.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "longpress_selection.html", true); |
| |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->executeScript(WebScriptSource( |
| WebString::fromUTF8("document.execCommand('SelectAll', false, null)"))); |
| std::string actual = frame->selectionAsText().utf8(); |
| |
| const int kMaxOutputCharacters = 1024; |
| std::string expected = |
| WebFrameContentDumper::dumpWebViewAsText(webView, kMaxOutputCharacters) |
| .utf8(); |
| EXPECT_EQ(expected, actual); |
| } |
| |
| TEST_P(WebViewTest, AutoResizeSubtreeLayout) { |
| std::string url = m_baseURL + "subtree-layout.html"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "subtree-layout.html"); |
| WebView* webView = m_webViewHelper.initialize(true); |
| |
| webView->enableAutoResizeMode(WebSize(200, 200), WebSize(200, 200)); |
| loadFrame(webView->mainFrame(), url); |
| |
| FrameView* frameView = |
| m_webViewHelper.webView()->mainFrameImpl()->frameView(); |
| |
| // Auto-resizing used to DCHECK(needsLayout()) in LayoutBlockFlow::layout. |
| // This EXPECT is merely a dummy. The real test is that we don't trigger |
| // asserts in debug builds. |
| EXPECT_FALSE(frameView->needsLayout()); |
| }; |
| |
| TEST_P(WebViewTest, PreferredSize) { |
| std::string url = m_baseURL + "specify_size.html?100px:100px"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "specify_size.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url, true); |
| |
| WebSize size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(100, size.width); |
| EXPECT_EQ(100, size.height); |
| |
| webView->setZoomLevel(WebView::zoomFactorToZoomLevel(2.0)); |
| size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(200, size.width); |
| EXPECT_EQ(200, size.height); |
| |
| // Verify that both width and height are rounded (in this case up) |
| webView->setZoomLevel(WebView::zoomFactorToZoomLevel(0.9995)); |
| size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(100, size.width); |
| EXPECT_EQ(100, size.height); |
| |
| // Verify that both width and height are rounded (in this case down) |
| webView->setZoomLevel(WebView::zoomFactorToZoomLevel(1.0005)); |
| size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(100, size.width); |
| EXPECT_EQ(100, size.height); |
| |
| url = m_baseURL + "specify_size.html?1.5px:1.5px"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "specify_size.html"); |
| webView = m_webViewHelper.initializeAndLoad(url, true); |
| |
| webView->setZoomLevel(WebView::zoomFactorToZoomLevel(1)); |
| size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(2, size.width); |
| EXPECT_EQ(2, size.height); |
| } |
| |
| TEST_P(WebViewTest, PreferredSizeDirtyLayout) { |
| std::string url = m_baseURL + "specify_size.html?100px:100px"; |
| URLTestHelpers::registerMockedURLLoad(toKURL(url), "specify_size.html"); |
| WebView* webView = m_webViewHelper.initializeAndLoad(url, true); |
| WebElement documentElement = |
| webView->mainFrame()->document().documentElement(); |
| |
| WebSize size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(100, size.width); |
| EXPECT_EQ(100, size.height); |
| |
| documentElement.setAttribute("style", "display: none"); |
| |
| size = webView->contentsPreferredMinimumSize(); |
| EXPECT_EQ(0, size.width); |
| EXPECT_EQ(0, size.height); |
| } |
| |
| class UnhandledTapWebViewClient : public FrameTestHelpers::TestWebViewClient { |
| public: |
| void showUnhandledTapUIIfNeeded(const WebPoint& tappedPosition, |
| const WebNode& tappedNode, |
| bool pageChanged) override { |
| m_wasCalled = true; |
| m_tappedPosition = tappedPosition; |
| m_tappedNode = tappedNode; |
| m_pageChanged = pageChanged; |
| } |
| bool getWasCalled() const { return m_wasCalled; } |
| int getTappedXPos() const { return m_tappedPosition.x(); } |
| int getTappedYPos() const { return m_tappedPosition.y(); } |
| bool isTappedNodeNull() const { return m_tappedNode.isNull(); } |
| const WebNode& getWebNode() const { return m_tappedNode; } |
| bool getPageChanged() const { return m_pageChanged; } |
| void reset() { |
| m_wasCalled = false; |
| m_tappedPosition = IntPoint(); |
| m_tappedNode = WebNode(); |
| m_pageChanged = false; |
| } |
| |
| private: |
| bool m_wasCalled = false; |
| IntPoint m_tappedPosition; |
| WebNode m_tappedNode; |
| bool m_pageChanged = false; |
| }; |
| |
| TEST_P(WebViewTest, ShowUnhandledTapUIIfNeeded) { |
| std::string testFile = "show_unhandled_tap.html"; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(testFile)); |
| UnhandledTapWebViewClient client; |
| WebView* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| |
| // Scroll the bottom into view so we can distinguish window coordinates from |
| // document coordinates. |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("bottom"))); |
| EXPECT_TRUE(client.getWasCalled()); |
| EXPECT_EQ(64, client.getTappedXPos()); |
| EXPECT_EQ(278, client.getTappedYPos()); |
| EXPECT_FALSE(client.isTappedNodeNull()); |
| EXPECT_TRUE(client.getWebNode().isTextNode()); |
| |
| // Test basic tap handling and notification. |
| client.reset(); |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("target"))); |
| EXPECT_TRUE(client.getWasCalled()); |
| EXPECT_EQ(144, client.getTappedXPos()); |
| EXPECT_EQ(82, client.getTappedYPos()); |
| EXPECT_FALSE(client.isTappedNodeNull()); |
| EXPECT_TRUE(client.getWebNode().isTextNode()); |
| // Make sure the returned text node has the parent element that was our |
| // target. |
| EXPECT_EQ(webView->mainFrame()->document().getElementById("target"), |
| client.getWebNode().parentNode()); |
| |
| // Test correct conversion of coordinates to viewport space under pinch-zoom. |
| webView->setPageScaleFactor(2); |
| webView->setVisualViewportOffset(WebFloatPoint(50, 20)); |
| client.reset(); |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("target"))); |
| EXPECT_TRUE(client.getWasCalled()); |
| EXPECT_EQ(188, client.getTappedXPos()); |
| EXPECT_EQ(124, client.getTappedYPos()); |
| |
| m_webViewHelper.reset(); // Remove dependency on locally scoped client. |
| } |
| |
| #define TEST_EACH_MOUSEEVENT(handler, EXPECT) \ |
| frame->executeScript(WebScriptSource("setTest('mousedown-" handler "');")); \ |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, \ |
| WebString::fromUTF8("target"))); \ |
| EXPECT_##EXPECT(client.getPageChanged()); \ |
| client.reset(); \ |
| frame->executeScript(WebScriptSource("setTest('mouseup-" handler "');")); \ |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, \ |
| WebString::fromUTF8("target"))); \ |
| EXPECT_##EXPECT(client.getPageChanged()); \ |
| client.reset(); \ |
| frame->executeScript(WebScriptSource("setTest('mousemove-" handler "');")); \ |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, \ |
| WebString::fromUTF8("target"))); \ |
| EXPECT_##EXPECT(client.getPageChanged()); \ |
| client.reset(); \ |
| frame->executeScript(WebScriptSource("setTest('click-" handler "');")); \ |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, \ |
| WebString::fromUTF8("target"))); \ |
| EXPECT_##EXPECT(client.getPageChanged()); |
| |
| TEST_P(WebViewTest, ShowUnhandledTapUIIfNeededWithMutateDom) { |
| std::string testFile = "show_unhandled_tap.html"; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(testFile)); |
| UnhandledTapWebViewClient client; |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| |
| // Test dom mutation. |
| TEST_EACH_MOUSEEVENT("mutateDom", TRUE); |
| |
| // Test without any DOM mutation. |
| client.reset(); |
| frame->executeScript(WebScriptSource("setTest('none');")); |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("target"))); |
| EXPECT_FALSE(client.getPageChanged()); |
| |
| m_webViewHelper.reset(); // Remove dependency on locally scoped client. |
| } |
| |
| TEST_P(WebViewTest, ShowUnhandledTapUIIfNeededWithMutateStyle) { |
| std::string testFile = "show_unhandled_tap.html"; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(testFile)); |
| UnhandledTapWebViewClient client; |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| |
| // Test style mutation. |
| TEST_EACH_MOUSEEVENT("mutateStyle", TRUE); |
| |
| // Test checkbox:indeterminate style mutation. |
| TEST_EACH_MOUSEEVENT("mutateIndeterminate", TRUE); |
| |
| // Test click div with :active style but it is not covered for now. |
| client.reset(); |
| EXPECT_TRUE(tapElementById(WebInputEvent::GestureTap, |
| WebString::fromUTF8("style_active"))); |
| EXPECT_FALSE(client.getPageChanged()); |
| |
| // Test without any style mutation. |
| client.reset(); |
| frame->executeScript(WebScriptSource("setTest('none');")); |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("target"))); |
| EXPECT_FALSE(client.getPageChanged()); |
| |
| m_webViewHelper.reset(); // Remove dependency on locally scoped client. |
| } |
| |
| TEST_P(WebViewTest, ShowUnhandledTapUIIfNeededWithPreventDefault) { |
| std::string testFile = "show_unhandled_tap.html"; |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("Ahem.ttf")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8(testFile)); |
| UnhandledTapWebViewClient client; |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client); |
| webView->resize(WebSize(500, 300)); |
| webView->updateAllLifecyclePhases(); |
| runPendingTasks(); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| |
| // Testswallowing. |
| TEST_EACH_MOUSEEVENT("preventDefault", FALSE); |
| |
| // Test without any preventDefault. |
| client.reset(); |
| frame->executeScript(WebScriptSource("setTest('none');")); |
| EXPECT_TRUE( |
| tapElementById(WebInputEvent::GestureTap, WebString::fromUTF8("target"))); |
| EXPECT_TRUE(client.getWasCalled()); |
| |
| m_webViewHelper.reset(); // Remove dependency on locally scoped client. |
| } |
| |
| TEST_P(WebViewTest, StopLoadingIfJavaScriptURLReturnsNoStringResult) { |
| ViewCreatingWebViewClient client; |
| FrameTestHelpers::WebViewHelper mainWebView; |
| mainWebView.initializeAndLoad("about:blank", true, 0, &client); |
| mainWebView.webView() |
| ->page() |
| ->settings() |
| .setJavaScriptCanOpenWindowsAutomatically(true); |
| |
| WebFrame* frame = mainWebView.webView()->mainFrame(); |
| v8::HandleScope scope(v8::Isolate::GetCurrent()); |
| v8::Local<v8::Value> v8Value = |
| frame->executeScriptAndReturnValue(WebScriptSource( |
| "var win = window.open('javascript:false'); win.document")); |
| ASSERT_TRUE(v8Value->IsObject()); |
| Document* document = |
| V8Document::toImplWithTypeCheck(v8::Isolate::GetCurrent(), v8Value); |
| ASSERT_TRUE(document); |
| EXPECT_FALSE(document->frame()->isLoading()); |
| } |
| |
| #if OS(MACOSX) |
| TEST_P(WebViewTest, WebSubstringUtil) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("content_editable_populated.html")); |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "content_editable_populated.html"); |
| webView->settings()->setDefaultFontSize(12); |
| webView->resize(WebSize(400, 400)); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| FrameView* frameView = frame->frame()->view(); |
| |
| WebPoint baselinePoint; |
| NSAttributedString* result = WebSubstringUtil::attributedSubstringInRange( |
| frame, 10, 3, &baselinePoint); |
| ASSERT_TRUE(!!result); |
| |
| WebPoint point(baselinePoint.x, frameView->height() - baselinePoint.y); |
| result = WebSubstringUtil::attributedWordAtPoint(frame->frameWidget(), point, |
| baselinePoint); |
| ASSERT_TRUE(!!result); |
| |
| webView->setZoomLevel(3); |
| |
| result = |
| WebSubstringUtil::attributedSubstringInRange(frame, 5, 5, &baselinePoint); |
| ASSERT_TRUE(!!result); |
| |
| point = WebPoint(baselinePoint.x, frameView->height() - baselinePoint.y); |
| result = WebSubstringUtil::attributedWordAtPoint(frame->frameWidget(), point, |
| baselinePoint); |
| ASSERT_TRUE(!!result); |
| } |
| |
| TEST_P(WebViewTest, WebSubstringUtilIframe) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("single_iframe.html")); |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("visible_iframe.html")); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "single_iframe.html"); |
| webView->settings()->setDefaultFontSize(12); |
| webView->settings()->setJavaScriptEnabled(true); |
| webView->resize(WebSize(400, 400)); |
| WebLocalFrameImpl* mainFrame = webView->mainFrameImpl(); |
| WebLocalFrameImpl* childFrame = WebLocalFrameImpl::fromFrame( |
| toLocalFrame(mainFrame->frame()->tree().firstChild())); |
| |
| WebPoint baselinePoint; |
| NSAttributedString* result = WebSubstringUtil::attributedSubstringInRange( |
| childFrame, 11, 7, &baselinePoint); |
| ASSERT_NE(result, nullptr); |
| |
| WebPoint point(baselinePoint.x, |
| mainFrame->frameView()->height() - baselinePoint.y); |
| result = WebSubstringUtil::attributedWordAtPoint(mainFrame->frameWidget(), |
| point, baselinePoint); |
| ASSERT_NE(result, nullptr); |
| |
| int yBeforeChange = baselinePoint.y; |
| |
| // Now move the <iframe> down by 100px. |
| mainFrame->executeScript(WebScriptSource( |
| "document.querySelector('iframe').style.marginTop = '100px';")); |
| |
| point = WebPoint(point.x, point.y + 100); |
| result = WebSubstringUtil::attributedWordAtPoint(mainFrame->frameWidget(), |
| point, baselinePoint); |
| ASSERT_NE(result, nullptr); |
| |
| EXPECT_EQ(yBeforeChange, baselinePoint.y + 100); |
| } |
| |
| #endif |
| |
| TEST_P(WebViewTest, PasswordFieldEditingIsUserGesture) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("input_field_password.html")); |
| MockAutofillClient client; |
| WebViewImpl* webView = m_webViewHelper.initializeAndLoad( |
| m_baseURL + "input_field_password.html", true); |
| WebLocalFrameImpl* frame = webView->mainFrameImpl(); |
| frame->setAutofillClient(&client); |
| webView->setInitialFocus(false); |
| |
| EXPECT_TRUE( |
| frame->frameWidget()->getActiveWebInputMethodController()->commitText( |
| WebString::fromUTF8(std::string("hello").c_str()), 0)); |
| EXPECT_EQ(1, client.textChangesFromUserGesture()); |
| EXPECT_FALSE(UserGestureIndicator::processingUserGesture()); |
| frame->setAutofillClient(0); |
| } |
| |
| // Verify that a WebView created with a ScopedPageSuspender already on the |
| // stack defers its loads. |
| TEST_P(WebViewTest, CreatedDuringPageSuspension) { |
| { |
| WebViewImpl* webView = m_webViewHelper.initialize(); |
| EXPECT_FALSE(webView->page()->suspended()); |
| } |
| |
| { |
| ScopedPageSuspender suspender; |
| WebViewImpl* webView = m_webViewHelper.initialize(); |
| EXPECT_TRUE(webView->page()->suspended()); |
| } |
| } |
| |
| // Make sure the SubframeBeforeUnloadUseCounter is only incremented on subframe |
| // unloads. crbug.com/635029. |
| TEST_P(WebViewTest, SubframeBeforeUnloadUseCounter) { |
| registerMockedHttpURLLoad("visible_iframe.html"); |
| registerMockedHttpURLLoad("single_iframe.html"); |
| WebViewImpl* webView = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "single_iframe.html", true); |
| |
| WebFrame* frame = m_webViewHelper.webView()->mainFrame(); |
| Document* document = |
| toLocalFrame(m_webViewHelper.webView()->page()->mainFrame())->document(); |
| |
| // Add a beforeunload handler in the main frame. Make sure firing |
| // beforeunload doesn't increment the subframe use counter. |
| { |
| frame->executeScript( |
| WebScriptSource("addEventListener('beforeunload', function() {});")); |
| webView->mainFrame()->toWebLocalFrame()->dispatchBeforeUnloadEvent(false); |
| EXPECT_FALSE(UseCounter::isCounted(*document, |
| UseCounter::SubFrameBeforeUnloadFired)); |
| } |
| |
| // Add a beforeunload handler in the iframe and dispatch. Make sure we do |
| // increment the use counter for subframe beforeunloads. |
| { |
| frame->executeScript(WebScriptSource( |
| "document.getElementsByTagName('iframe')[0].contentWindow." |
| "addEventListener('beforeunload', function() {});")); |
| webView->mainFrame() |
| ->firstChild() |
| ->toWebLocalFrame() |
| ->dispatchBeforeUnloadEvent(false); |
| |
| Document* childDocument = |
| toLocalFrame( |
| m_webViewHelper.webView()->page()->mainFrame()->tree().firstChild()) |
| ->document(); |
| EXPECT_TRUE(UseCounter::isCounted(*childDocument, |
| UseCounter::SubFrameBeforeUnloadFired)); |
| } |
| } |
| |
| // Verify that page loads are deferred until all ScopedPageLoadDeferrers are |
| // destroyed. |
| TEST_P(WebViewTest, NestedPageSuspensions) { |
| WebViewImpl* webView = m_webViewHelper.initialize(); |
| EXPECT_FALSE(webView->page()->suspended()); |
| |
| { |
| ScopedPageSuspender suspender; |
| EXPECT_TRUE(webView->page()->suspended()); |
| |
| { |
| ScopedPageSuspender suspender2; |
| EXPECT_TRUE(webView->page()->suspended()); |
| } |
| |
| EXPECT_TRUE(webView->page()->suspended()); |
| } |
| |
| EXPECT_FALSE(webView->page()->suspended()); |
| } |
| |
| TEST_P(WebViewTest, ClosingPageIsSuspended) { |
| WebViewImpl* webView = m_webViewHelper.initialize(); |
| Page* page = m_webViewHelper.webView()->page(); |
| EXPECT_FALSE(page->suspended()); |
| |
| webView->setOpenedByDOM(); |
| |
| LocalFrame* mainFrame = toLocalFrame(page->mainFrame()); |
| EXPECT_FALSE(mainFrame->domWindow()->closed()); |
| |
| mainFrame->domWindow()->close(nullptr); |
| // The window should be marked closed... |
| EXPECT_TRUE(mainFrame->domWindow()->closed()); |
| // EXPECT_TRUE(page->isClosing()); |
| // ...but not yet detached. |
| EXPECT_TRUE(mainFrame->host()); |
| |
| { |
| ScopedPageSuspender suspender; |
| EXPECT_TRUE(page->suspended()); |
| } |
| } |
| |
| TEST_P(WebViewTest, ForceAndResetViewport) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("200-by-300.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html"); |
| webViewImpl->resize(WebSize(100, 150)); |
| webViewImpl->layerTreeView()->setViewportSize(WebSize(100, 150)); |
| VisualViewport* visualViewport = |
| &webViewImpl->page()->frameHost().visualViewport(); |
| DevToolsEmulator* devToolsEmulator = webViewImpl->devToolsEmulator(); |
| |
| TransformationMatrix expectedMatrix; |
| expectedMatrix.makeIdentity(); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| EXPECT_FALSE(devToolsEmulator->visibleContentRectForPainting()); |
| EXPECT_TRUE(visualViewport->containerLayer()->masksToBounds()); |
| |
| // Override applies transform, sets visibleContentRect, and disables |
| // visual viewport clipping. |
| devToolsEmulator->forceViewport(WebFloatPoint(50, 55), 2.f); |
| expectedMatrix.makeIdentity().scale(2.f).translate(-50, -55); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| EXPECT_EQ(IntRect(50, 55, 50, 75), |
| *devToolsEmulator->visibleContentRectForPainting()); |
| EXPECT_FALSE(visualViewport->containerLayer()->masksToBounds()); |
| |
| // Setting new override discards previous one. |
| devToolsEmulator->forceViewport(WebFloatPoint(5.4f, 10.5f), 1.5f); |
| expectedMatrix.makeIdentity().scale(1.5f).translate(-5.4f, -10.5f); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| EXPECT_EQ(IntRect(5, 10, 68, 101), |
| *devToolsEmulator->visibleContentRectForPainting()); |
| EXPECT_FALSE(visualViewport->containerLayer()->masksToBounds()); |
| |
| // Clearing override restores original transform, visibleContentRect and |
| // visual viewport clipping. |
| devToolsEmulator->resetViewport(); |
| expectedMatrix.makeIdentity(); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| EXPECT_FALSE(devToolsEmulator->visibleContentRectForPainting()); |
| EXPECT_TRUE(visualViewport->containerLayer()->masksToBounds()); |
| } |
| |
| TEST_P(WebViewTest, ViewportOverrideIntegratesDeviceMetricsOffsetAndScale) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("200-by-300.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html"); |
| webViewImpl->resize(WebSize(100, 150)); |
| |
| TransformationMatrix expectedMatrix; |
| expectedMatrix.makeIdentity(); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| |
| WebDeviceEmulationParams emulationParams; |
| emulationParams.offset = WebFloatPoint(50, 50); |
| emulationParams.scale = 2.f; |
| webViewImpl->enableDeviceEmulation(emulationParams); |
| expectedMatrix.makeIdentity().translate(50, 50).scale(2.f); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| |
| // Device metrics offset and scale are applied before viewport override. |
| webViewImpl->devToolsEmulator()->forceViewport(WebFloatPoint(5, 10), 1.5f); |
| expectedMatrix.makeIdentity() |
| .scale(1.5f) |
| .translate(-5, -10) |
| .translate(50, 50) |
| .scale(2.f); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| } |
| |
| TEST_P(WebViewTest, ViewportOverrideAdaptsToScaleAndScroll) { |
| URLTestHelpers::registerMockedURLFromBaseURL( |
| WebString::fromUTF8(m_baseURL.c_str()), |
| WebString::fromUTF8("200-by-300.html")); |
| WebViewImpl* webViewImpl = |
| m_webViewHelper.initializeAndLoad(m_baseURL + "200-by-300.html"); |
| webViewImpl->resize(WebSize(100, 150)); |
| webViewImpl->layerTreeView()->setViewportSize(WebSize(100, 150)); |
| FrameView* frameView = webViewImpl->mainFrameImpl()->frame()->view(); |
| DevToolsEmulator* devToolsEmulator = webViewImpl->devToolsEmulator(); |
| |
| TransformationMatrix expectedMatrix; |
| expectedMatrix.makeIdentity(); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| |
| // Initial transform takes current page scale and scroll position into |
| // account. |
| webViewImpl->setPageScaleFactor(1.5f); |
| frameView->layoutViewportScrollableArea()->setScrollOffset( |
| ScrollOffset(100, 150), ProgrammaticScroll, ScrollBehaviorInstant); |
| devToolsEmulator->forceViewport(WebFloatPoint(50, 55), 2.f); |
| expectedMatrix.makeIdentity() |
| .scale(2.f) |
| .translate(-50, -55) |
| .translate(100, 150) |
| .scale(1. / 1.5f); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| // Page scroll and scale are irrelevant for visibleContentRect. |
| EXPECT_EQ(IntRect(50, 55, 50, 75), |
| *devToolsEmulator->visibleContentRectForPainting()); |
| |
| // Transform adapts to scroll changes. |
| frameView->layoutViewportScrollableArea()->setScrollOffset( |
| ScrollOffset(50, 55), ProgrammaticScroll, ScrollBehaviorInstant); |
| expectedMatrix.makeIdentity() |
| .scale(2.f) |
| .translate(-50, -55) |
| .translate(50, 55) |
| .scale(1. / 1.5f); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| // visibleContentRect doesn't change. |
| EXPECT_EQ(IntRect(50, 55, 50, 75), |
| *devToolsEmulator->visibleContentRectForPainting()); |
| |
| // Transform adapts to page scale changes. |
| webViewImpl->setPageScaleFactor(2.f); |
| expectedMatrix.makeIdentity() |
| .scale(2.f) |
| .translate(-50, -55) |
| .translate(50, 55) |
| .scale(1. / 2.f); |
| EXPECT_EQ(expectedMatrix, |
| webViewImpl->getDeviceEmulationTransformForTesting()); |
| // visibleContentRect doesn't change. |
| EXPECT_EQ(IntRect(50, 55, 50, 75), |
| *devToolsEmulator->visibleContentRectForPainting()); |
| } |
| |
| } // namespace blink |