blob: 0f615b439c3cc5aea57640926c505b4496fc04e4 [file] [log] [blame]
/*
* 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/HTMLDocument.h"
#include "core/html/HTMLIFrameElement.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/HTMLTextAreaElement.h"
#include "core/layout/LayoutView.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/FrameLoadRequest.h"
#include "core/page/Page.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PaintLayerPainter.h"
#include "core/timing/DOMWindowPerformance.h"
#include "core/timing/Performance.h"
#include "core/timing/PerformanceCompositeTiming.h"
#include "platform/KeyboardCodes.h"
#include "platform/UserGestureIndicator.h"
#include "platform/geometry/IntSize.h"
#include "platform/graphics/Color.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/paint/SkPictureBuilder.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/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/WebContentDetectionResult.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/WebInputEvent.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/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;
};
class WebViewTest : public ::testing::Test {
public:
WebViewTest()
: 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(const WebString& 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();
}
TEST_F(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_F(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_F(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_F(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_F(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 detach() and dispose() calls are a hack to prevent this test
// from violating invariants about frame state during navigation/detach.
frame->document()->detach();
// 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_F(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());
WebLocalFrame* frame = WebLocalFrame::create(WebTreeScopeType::Document, nullptr);
webView->setMainFrame(frame);
webView->close();
frame->close();
}
TEST_F(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.webViewImpl()->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_F(WebViewTest, FocusIsInactive)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), "visible_iframe.html");
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "visible_iframe.html");
webView->setFocus(true);
webView->setIsActive(true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
EXPECT_TRUE(frame->frame()->document()->isHTMLDocument());
HTMLDocument* document = toHTMLDocument(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_F(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_F(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_F(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_F(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_F(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");
WebView* webView = m_webViewHelper.initializeAndLoad(url, true, 0, &client);
client.testData().setWebView(webView);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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
m_webViewHelper.reset(); // Explicitly reset to break dependency on locally scoped client.
}
TEST_F(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_F(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_F(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_F(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_F(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_F(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_F(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");
}
void WebViewTest::testInputMode(const WebString& 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_F(WebViewTest, InputMode)
{
testInputMode(WebString(), "input_mode_default.html");
testInputMode(WebString("unknown"), "input_mode_default_unknown.html");
testInputMode(WebString("verbatim"), "input_mode_default_verbatim.html");
testInputMode(WebString("verbatim"), "input_mode_type_text_verbatim.html");
testInputMode(WebString("verbatim"), "input_mode_type_search_verbatim.html");
testInputMode(WebString(), "input_mode_type_url_verbatim.html");
testInputMode(WebString("verbatim"), "input_mode_textarea_verbatim.html");
}
TEST_F(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_F(WebViewTest, SetEditableSelectionOffsetsAndTextInputInfo)
{
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");
webView->setInitialFocus(false);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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 = toWebLocalFrameImpl(webView->mainFrame());
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);
}
TEST_F(WebViewTest, ConfirmCompositionCursorPositionChange)
{
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");
webView->setInitialFocus(false);
// Set up a composition that needs to be committed.
std::string compositionText("hello");
WebVector<WebCompositionUnderline> emptyUnderlines;
webView->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);
webView->confirmComposition(WebWidget::KeepSelection);
info = webView->textInputInfo();
EXPECT_EQ(3, info.selectionStart);
EXPECT_EQ(3, info.selectionEnd);
EXPECT_EQ(-1, info.compositionStart);
EXPECT_EQ(-1, info.compositionEnd);
webView->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);
webView->confirmComposition(WebWidget::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_F(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 = toWebLocalFrameImpl(webView->mainFrame());
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.
webView->confirmComposition(WebWidget::DoNotKeepSelection);
EXPECT_EQ(0, webView->mainFrame()->scrollOffset().width);
EXPECT_EQ(offsetHeight, webView->mainFrame()->scrollOffset().height);
}
TEST_F(WebViewTest, InsertNewLinePlacementAfterConfirmComposition)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("text_area_populated.html"));
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "text_area_populated.html");
webView->setInitialFocus(false);
WebVector<WebCompositionUnderline> emptyUnderlines;
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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);
webView->confirmComposition(WebWidget::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");
webView->confirmComposition(WebString::fromUTF8(compositionText.c_str()));
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_F(WebViewTest, ExtendSelectionAndDelete)
{
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->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_F(WebViewTest, SetCompositionFromExistingText)
{
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");
webView->setInitialFocus(false);
WebVector<WebCompositionUnderline> underlines(static_cast<size_t>(1));
underlines[0] = WebCompositionUnderline(0, 4, 0, false, 0);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, SetCompositionFromExistingTextInTextArea)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("text_area_populated.html"));
WebView* 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 = toWebLocalFrameImpl(webView->mainFrame());
frame->setEditableSelectionOffsets(27, 27);
std::string newLineText("\n");
webView->confirmComposition(WebString::fromUTF8(newLineText.c_str()));
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");
webView->confirmComposition(WebString::fromUTF8(compositionText.c_str()));
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_F(WebViewTest, SetCompositionFromExistingTextInRichText)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("content_editable_rich_text.html"));
WebView* 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 = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, SetEditableSelectionOffsetsKeepsComposition)
{
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");
webView->setInitialFocus(false);
std::string compositionTextFirst("hello ");
std::string compositionTextSecond("world");
WebVector<WebCompositionUnderline> emptyUnderlines;
webView->confirmComposition(WebString::fromUTF8(compositionTextFirst.c_str()));
webView->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 = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, IsSelectionAnchorFirst)
{
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");
WebFrame* frame = webView->mainFrame();
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_F(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_F(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()->scrollPoint().x());
EXPECT_EQ(111, mainFrameLocal->loader().currentItem()->scrollPoint().y());
// 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()->scrollPoint().x());
EXPECT_EQ(0, mainFrameLocal->loader().currentItem()->scrollPoint().y());
}
TEST_F(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);
}
TEST_F(WebViewTest, EnterFullscreenResetScrollAndScaleState)
{
FrameTestHelpers::TestWebViewClient client;
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", true, 0, &client);
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->didExitFullScreen();
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);
m_webViewHelper.reset(); // Explicitly reset to break dependency on locally scoped client.
}
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_F(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);
webView->dragTargetDragEnter(dragData, clientPoint, screenPoint, WebDragOperationCopy, 0);
webView->dragTargetDrop(dragData, clientPoint, screenPoint, 0);
FrameTestHelpers::pumpPendingRequestsForFrameToLoad(webView->mainFrame());
}
TEST_F(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(); }
WebContentDetectionResult detectContentAround(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 = WebContentDetectionResult();
}
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 WebContentDetectionResult& result) { m_contentDetectionResult = result; }
private:
bool m_contentDetectionRequested;
bool m_pendingIntentsCancelled;
WebURL m_scheduledIntentURL;
bool m_wasInMainFrame;
WebContentDetectionResult 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.webViewImpl()->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_F(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(WebContentDetectionResult(WebRange(), WebString(), 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());
m_webViewHelper.reset(); // Explicitly reset to break dependency on locally scoped client.
}
TEST_F(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(WebContentDetectionResult(WebRange(), WebString(), 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());
m_webViewHelper.reset(); // Explicitly reset to break dependency on locally scoped client.
}
TEST_F(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());
m_webViewHelper.reset(); // Explicitly reset to break dependency on locally scoped client.
}
TEST_F(WebViewTest, ClientTapHandlingNullWebViewClient)
{
WebViewImpl* webView = WebViewImpl::create(nullptr, WebPageVisibilityStateVisible);
WebLocalFrame* localFrame = WebLocalFrame::create(WebTreeScopeType::Document, nullptr);
webView->setMainFrame(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();
// Explicitly close as the frame as no frame client to do so on frameDetached().
localFrame->close();
}
TEST_F(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_F(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_F(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_F(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_F(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_F(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_F(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_F(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_F(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 = toWebLocalFrameImpl(webView->mainFrame());
EXPECT_EQ(WebInputEventResult::HandledSystem, webView->handleInputEvent(event));
EXPECT_TRUE(frame->selectionAsText().isEmpty());
}
TEST_F(WebViewTest, LongPressSelection)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("longpress_selection.html"));
WebView* 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 = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, TouchDoesntSelectEmptyTextarea)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("longpress_textarea.html"));
WebView* 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 = toWebLocalFrameImpl(webView->mainFrame());
// 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_F(WebViewTest, LongPressImageTextarea)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("longpress_image_contenteditable.html"));
WebView* 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));
size_t location, length;
EXPECT_TRUE(toWebViewImpl(webView)->caretOrSelectionRange(&location, &length));
EXPECT_EQ(0UL, location);
EXPECT_EQ(1UL, length);
}
TEST_F(WebViewTest, BlinkCaretAfterLongPress)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("blink_caret_on_typing_after_long_press.html"));
WebView* 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 = toWebLocalFrameImpl(webView->mainFrame());
EXPECT_TRUE(tapElementById(WebInputEvent::GestureLongPress, target));
EXPECT_FALSE(mainFrame->frame()->selection().isCaretBlinkingSuspended());
}
TEST_F(WebViewTest, BlinkCaretOnClosingContextMenu)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("form.html"));
WebView* 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::ButtonRight;
mouseEvent.x = 1;
mouseEvent.y = 1;
mouseEvent.clickCount = 1;
mouseEvent.type = WebInputEvent::MouseDown;
webView->handleInputEvent(mouseEvent);
runPendingTasks();
WebLocalFrameImpl* mainFrame = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, SelectionOnReadOnlyInput)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("selection_readonly.html"));
WebView* 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 = toWebLocalFrameImpl(webView->mainFrame());
EXPECT_EQ(testWord, std::string(frame->selectionAsText().utf8().data()));
size_t location;
size_t length;
EXPECT_TRUE(toWebViewImpl(webView)->caretOrSelectionRange(&location, &length));
EXPECT_EQ(location, 0UL);
EXPECT_EQ(length, testWord.length());
}
static void configueCompositingWebView(WebSettings* settings)
{
settings->setAcceleratedCompositingEnabled(true);
settings->setPreferCompositingToLCDTextEnabled(true);
}
TEST_F(WebViewTest, ShowPressOnTransformedLink)
{
std::unique_ptr<FrameTestHelpers::TestWebViewClient> fakeCompositingWebViewClient = wrapUnique(new 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_F(WebViewTest, LosingFocusDoesNotTriggerAutofillTextChange)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("input_field_populated.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "input_field_populated.html");
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, CompositionNotCancelledByBackspace)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("composition_not_cancelled_by_backspace.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "composition_not_cancelled_by_backspace.html");
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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);
EXPECT_TRUE(webView->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(webView->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_F(WebViewTest, ConfirmCompositionTriggersAutofillTextChange)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("input_field_populated.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "input_field_populated.html");
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
frame->setAutofillClient(&client);
webView->setInitialFocus(false);
// Set up a composition that needs to be committed.
std::string compositionText("testingtext");
WebVector<WebCompositionUnderline> emptyUnderlines;
webView->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();
webView->confirmComposition();
EXPECT_EQ(0, client.textChangesWhileIgnored());
EXPECT_EQ(1, client.textChangesWhileNotIgnored());
frame->setAutofillClient(0);
}
TEST_F(WebViewTest, SetCompositionFromExistingTextTriggersAutofillTextChange)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("input_field_populated.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "input_field_populated.html", true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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_F(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;
webURLRequestWithTargetStart.initialize();
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_F(WebViewTest, FocusExistingFrameOnNavigate)
{
ViewCreatingWebViewClient client;
FrameTestHelpers::WebViewHelper m_webViewHelper;
WebViewImpl* webViewImpl = m_webViewHelper.initialize(true, 0, &client);
webViewImpl->page()->settings().setJavaScriptCanOpenWindowsAutomatically(true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webViewImpl->mainFrame());
frame->setName("_start");
// Make a request that will open a new window
WebURLRequest webURLRequest;
webURLRequest.initialize();
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;
webURLRequestWithTargetStart.initialize();
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_F(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_F(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)
#define MAYBE_ChooseValueFromDateTimeChooser DISABLED_ChooseValueFromDateTimeChooser
#else
#define MAYBE_ChooseValueFromDateTimeChooser ChooseValueFromDateTimeChooser
#endif
TEST_F(WebViewTest, MAYBE_ChooseValueFromDateTimeChooser)
{
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_F(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_F(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_F(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_F(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_F(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) { }
WebFrame* createChildFrame(WebLocalFrame* parent, WebTreeScopeType, const WebString& name, const WebString& uniqueName, WebSandboxFlags, const WebFrameOwnerProperties&) override;
int count() const { return m_count; }
private:
int m_count;
};
WebFrame* 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_F(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_F(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_F(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.webViewImpl()->mainFrame()->dispatchUnloadEvent();
EXPECT_EQ(0, frameClient.count());
m_webViewHelper.reset();
}
TEST_F(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_F(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_F(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_F(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.
ThreadHeap::collectAllGarbage();
EXPECT_FALSE(registry.hasEventHandlers(EventHandlerRegistry::ScrollEvent));
}
class NonUserInputTextUpdateWebViewClient : public FrameTestHelpers::TestWebViewClient {
public:
NonUserInputTextUpdateWebViewClient() : 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_F(WebViewTest, TextInputFlags)
{
NonUserInputTextUpdateWebViewClient client;
std::string url = m_baseURL + "text_input_flags.html";
URLTestHelpers::registerMockedURLLoad(toKURL(url), "text_input_flags.html");
WebViewImpl* webViewImpl = m_webViewHelper.initializeAndLoad(url, true, 0, &client);
webViewImpl->setInitialFocus(false);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webViewImpl->mainFrame());
HTMLDocument* document = toHTMLDocument(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_F(WebViewTest, NonUserInputTextUpdate)
{
NonUserInputTextUpdateWebViewClient 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, 0, &client);
webViewImpl->setInitialFocus(false);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webViewImpl->mainFrame());
HTMLDocument* document = toHTMLDocument(frame->frame()->document());
// (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;
webViewImpl->setComposition(WebString::fromUTF8("2"), emptyUnderlines, 1, 1);
webViewImpl->confirmComposition(WebWidget::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());
webViewImpl->setComposition(WebString::fromUTF8("2"), emptyUnderlines, 1, 1);
webViewImpl->confirmComposition(WebWidget::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_F(WebViewTest, FirstUserGestureObservedKeyEvent)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("form.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "form.html", true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, FirstUserGestureObservedMouseEvent)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("form.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "form.html", true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
frame->setAutofillClient(&client);
webView->setInitialFocus(false);
EXPECT_EQ(0, client.getUserGestureNotificationsCount());
WebMouseEvent mouseEvent;
mouseEvent.button = WebMouseEvent::ButtonLeft;
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_F(WebViewTest, FirstUserGestureObservedGestureTap)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("longpress_selection.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "longpress_selection.html", true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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_F(WebViewTest, CompositionIsUserGesture)
{
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());
MockAutofillClient client;
frame->setAutofillClient(&client);
webView->setInitialFocus(false);
EXPECT_TRUE(webView->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_F(WebViewTest, CompareSelectAllToContentAsText)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("longpress_selection.html"));
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "longpress_selection.html", true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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_F(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.webViewImpl()->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_F(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_F(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_F(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_F(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;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client);
webView->resize(WebSize(500, 300));
webView->updateAllLifecyclePhases();
runPendingTasks();
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
// 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_F(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;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client);
webView->resize(WebSize(500, 300));
webView->updateAllLifecyclePhases();
runPendingTasks();
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
// 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_F(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;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + testFile, true, 0, &client);
webView->resize(WebSize(500, 300));
webView->updateAllLifecyclePhases();
runPendingTasks();
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
// 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_F(WebViewTest, StopLoadingIfJavaScriptURLReturnsNoStringResult)
{
ViewCreatingWebViewClient client;
FrameTestHelpers::WebViewHelper mainWebView;
mainWebView.initializeAndLoad("about:blank", true, 0, &client);
mainWebView.webViewImpl()->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_F(WebViewTest, WebSubstringUtil)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("content_editable_populated.html"));
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "content_editable_populated.html");
webView->settings()->setDefaultFontSize(12);
webView->resize(WebSize(400, 400));
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
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(webView, 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(webView, point, baselinePoint);
ASSERT_TRUE(!!result);
}
#endif
TEST_F(WebViewTest, PasswordFieldEditingIsUserGesture)
{
URLTestHelpers::registerMockedURLFromBaseURL(WebString::fromUTF8(m_baseURL.c_str()), WebString::fromUTF8("input_field_password.html"));
MockAutofillClient client;
WebView* webView = m_webViewHelper.initializeAndLoad(m_baseURL + "input_field_password.html", true);
WebLocalFrameImpl* frame = toWebLocalFrameImpl(webView->mainFrame());
frame->setAutofillClient(&client);
webView->setInitialFocus(false);
EXPECT_TRUE(webView->confirmComposition(WebString::fromUTF8(std::string("hello").c_str())));
EXPECT_EQ(1, client.textChangesFromUserGesture());
EXPECT_FALSE(UserGestureIndicator::processingUserGesture());
frame->setAutofillClient(0);
}
} // namespace blink