blob: 12e03ae39da2f76ecf951a48761958b3ab0267e7 [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 <limits>
#include <memory>
#include <string>
#include "bindings/core/v8/V8Document.h"
#include "build/build_config.h"
#include "core/dom/Document.h"
#include "core/dom/Element.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/dom/UserGestureIndicator.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/ime/InputMethodController.h"
#include "core/editing/markers/DocumentMarkerController.h"
#include "core/exported/FakeWebPlugin.h"
#include "core/exported/WebSettingsImpl.h"
#include "core/exported/WebViewImpl.h"
#include "core/frame/EventHandlerRegistry.h"
#include "core/frame/FrameTestHelpers.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/Settings.h"
#include "core/frame/VisualViewport.h"
#include "core/frame/WebLocalFrameImpl.h"
#include "core/fullscreen/Fullscreen.h"
#include "core/html/HTMLIFrameElement.h"
#include "core/html/HTMLObjectElement.h"
#include "core/html/forms/HTMLInputElement.h"
#include "core/html/forms/HTMLTextAreaElement.h"
#include "core/inspector/DevToolsEmulator.h"
#include "core/layout/LayoutView.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/FrameLoadRequest.h"
#include "core/loader/InteractiveDetector.h"
#include "core/page/ChromeClient.h"
#include "core/page/FocusController.h"
#include "core/page/Page.h"
#include "core/page/PrintContext.h"
#include "core/page/ScopedPagePauser.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/PaintLayerPainter.h"
#include "core/timing/DOMWindowPerformance.h"
#include "core/timing/Performance.h"
#include "platform/KeyboardCodes.h"
#include "platform/geometry/IntRect.h"
#include "platform/geometry/IntSize.h"
#include "platform/graphics/Color.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/paint/PaintRecordBuilder.h"
#include "platform/scroll/ScrollTypes.h"
#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
#include "platform/testing/URLTestHelpers.h"
#include "platform/testing/UnitTestHelpers.h"
#include "platform/testing/wtf/ScopedMockClock.h"
#include "platform/wtf/PtrUtil.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCoalescedInputEvent.h"
#include "public/platform/WebCursorInfo.h"
#include "public/platform/WebDisplayMode.h"
#include "public/platform/WebDragData.h"
#include "public/platform/WebDragOperation.h"
#include "public/platform/WebFloatPoint.h"
#include "public/platform/WebInputEvent.h"
#include "public/platform/WebKeyboardEvent.h"
#include "public/platform/WebLayerTreeView.h"
#include "public/platform/WebMockClipboard.h"
#include "public/platform/WebSize.h"
#include "public/platform/WebThread.h"
#include "public/platform/WebURLLoaderMockFactory.h"
#include "public/web/WebAutofillClient.h"
#include "public/web/WebConsoleMessage.h"
#include "public/web/WebDateTimeChooserCompletion.h"
#include "public/web/WebDeviceEmulationParams.h"
#include "public/web/WebDocument.h"
#include "public/web/WebElement.h"
#include "public/web/WebFrame.h"
#include "public/web/WebFrameClient.h"
#include "public/web/WebFrameContentDumper.h"
#include "public/web/WebHitTestResult.h"
#include "public/web/WebInputMethodController.h"
#include "public/web/WebPrintParams.h"
#include "public/web/WebScriptSource.h"
#include "public/web/WebSettings.h"
#include "public/web/WebTappedInfo.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/WebKit/common/page/page_visibility_state.mojom-blink.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "v8/include/v8.h"
#if defined(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 {
kNoHorizontalScrollbar,
kVisibleHorizontalScrollbar,
};
enum VerticalScrollbarState {
kNoVerticalScrollbar,
kVisibleVerticalScrollbar,
};
class TestData {
public:
void SetWebView(WebView* web_view) {
web_view_ = static_cast<WebViewImpl*>(web_view);
}
void SetSize(const WebSize& new_size) { size_ = new_size; }
HorizontalScrollbarState GetHorizontalScrollbarState() const {
return web_view_->HasHorizontalScrollbar() ? kVisibleHorizontalScrollbar
: kNoHorizontalScrollbar;
}
VerticalScrollbarState GetVerticalScrollbarState() const {
return web_view_->HasVerticalScrollbar() ? kVisibleVerticalScrollbar
: kNoVerticalScrollbar;
}
int Width() const { return size_.width; }
int Height() const { return size_.height; }
private:
WebSize size_;
WebViewImpl* web_view_;
};
class AutoResizeWebViewClient : public FrameTestHelpers::TestWebViewClient {
public:
// WebViewClient methods
void DidAutoResize(const WebSize& new_size) override {
test_data_.SetSize(new_size);
}
// Local methods
TestData& GetTestData() { return test_data_; }
private:
TestData test_data_;
};
class TapHandlingWebViewClient : public FrameTestHelpers::TestWebViewClient {
public:
// WebViewClient methods
void DidHandleGestureEvent(const WebGestureEvent& event,
bool event_cancelled) override {
if (event.GetType() == WebInputEvent::kGestureTap) {
tap_x_ = event.x;
tap_y_ = event.y;
} else if (event.GetType() == WebInputEvent::kGestureLongPress) {
longpress_x_ = event.x;
longpress_y_ = event.y;
}
}
// Local methods
void Reset() {
tap_x_ = -1;
tap_y_ = -1;
longpress_x_ = -1;
longpress_y_ = -1;
}
int TapX() { return tap_x_; }
int TapY() { return tap_y_; }
int LongpressX() { return longpress_x_; }
int LongpressY() { return longpress_y_; }
private:
int tap_x_;
int tap_y_;
int longpress_x_;
int longpress_y_;
};
class DateTimeChooserWebViewClient
: public FrameTestHelpers::TestWebViewClient {
public:
WebDateTimeChooserCompletion* ChooserCompletion() {
return chooser_completion_;
}
void ClearChooserCompletion() { chooser_completion_ = nullptr; }
// WebViewClient methods
bool OpenDateTimeChooser(
const WebDateTimeChooserParams&,
WebDateTimeChooserCompletion* chooser_completion) override {
chooser_completion_ = chooser_completion;
return true;
}
private:
WebDateTimeChooserCompletion* chooser_completion_;
};
typedef bool TestParamRootLayerScrolling;
class WebViewTest
: public ::testing::Test,
public ::testing::WithParamInterface<TestParamRootLayerScrolling>,
private ScopedRootLayerScrollingForTest {
public:
WebViewTest()
: ScopedRootLayerScrollingForTest(GetParam()),
base_url_("http://www.test.com/") {}
void TearDown() override {
Platform::Current()
->GetURLLoaderMockFactory()
->UnregisterAllURLsAndClearMemoryCache();
}
protected:
void SetViewportSize(const WebSize& size) {
web_view_helper_.SetViewportSize(size);
}
std::string RegisterMockedHttpURLLoad(const std::string& file_name) {
return URLTestHelpers::RegisterMockedURLLoadFromBase(
WebString::FromUTF8(base_url_), testing::CoreTestDataPath(),
WebString::FromUTF8(file_name))
.GetString()
.Utf8();
}
void TestAutoResize(const WebSize& min_auto_resize,
const WebSize& max_auto_resize,
const std::string& page_width,
const std::string& page_height,
int expected_width,
int expected_height,
HorizontalScrollbarState expected_horizontal_state,
VerticalScrollbarState expected_vertical_state);
void TestTextInputType(WebTextInputType expected_type,
const std::string& html_file);
void TestInputMode(WebTextInputMode expected_input_mode,
const std::string& html_file);
bool TapElement(WebInputEvent::Type, Element*);
bool TapElementById(WebInputEvent::Type, const WebString& id);
IntSize PrintICBSizeFromPageSize(const FloatSize& page_size);
std::string base_url_;
FrameTestHelpers::WebViewHelper web_view_helper_;
};
static bool HitTestIsContentEditable(WebView* view, int x, int y) {
WebPoint hit_point(x, y);
WebHitTestResult hit_test_result = view->HitTestResultAt(hit_point);
return hit_test_result.IsContentEditable();
}
static std::string HitTestElementId(WebView* view, int x, int y) {
WebPoint hit_point(x, y);
WebHitTestResult hit_test_result = view->HitTestResultAt(hit_point);
return hit_test_result.GetNode().To<WebElement>().GetAttribute("id").Utf8();
}
INSTANTIATE_TEST_CASE_P(All, WebViewTest, ::testing::Bool());
TEST_P(WebViewTest, HitTestVideo) {
// Test that hit tests on parts of a video element result in hits on the video
// element itself as opposed to its child elements.
std::string url = RegisterMockedHttpURLLoad("video_200x200.html");
WebView* web_view = web_view_helper_.InitializeAndLoad(url);
web_view->Resize(WebSize(200, 200));
// Center of video.
EXPECT_EQ("video", HitTestElementId(web_view, 100, 100));
// Play button.
EXPECT_EQ("video", HitTestElementId(web_view, 10, 195));
// Timeline bar.
EXPECT_EQ("video", HitTestElementId(web_view, 100, 195));
}
TEST_P(WebViewTest, HitTestContentEditableImageMaps) {
std::string url =
RegisterMockedHttpURLLoad("content-editable-image-maps.html");
WebView* web_view = web_view_helper_.InitializeAndLoad(url);
web_view->Resize(WebSize(500, 500));
EXPECT_EQ("areaANotEditable", HitTestElementId(web_view, 25, 25));
EXPECT_FALSE(HitTestIsContentEditable(web_view, 25, 25));
EXPECT_EQ("imageANotEditable", HitTestElementId(web_view, 75, 25));
EXPECT_FALSE(HitTestIsContentEditable(web_view, 75, 25));
EXPECT_EQ("areaBNotEditable", HitTestElementId(web_view, 25, 125));
EXPECT_FALSE(HitTestIsContentEditable(web_view, 25, 125));
EXPECT_EQ("imageBEditable", HitTestElementId(web_view, 75, 125));
EXPECT_TRUE(HitTestIsContentEditable(web_view, 75, 125));
EXPECT_EQ("areaCNotEditable", HitTestElementId(web_view, 25, 225));
EXPECT_FALSE(HitTestIsContentEditable(web_view, 25, 225));
EXPECT_EQ("imageCNotEditable", HitTestElementId(web_view, 75, 225));
EXPECT_FALSE(HitTestIsContentEditable(web_view, 75, 225));
EXPECT_EQ("areaDEditable", HitTestElementId(web_view, 25, 325));
EXPECT_TRUE(HitTestIsContentEditable(web_view, 25, 325));
EXPECT_EQ("imageDNotEditable", HitTestElementId(web_view, 75, 325));
EXPECT_FALSE(HitTestIsContentEditable(web_view, 75, 325));
}
static std::string HitTestAbsoluteUrl(WebView* view, int x, int y) {
WebPoint hit_point(x, y);
WebHitTestResult hit_test_result = view->HitTestResultAt(hit_point);
return hit_test_result.AbsoluteImageURL().GetString().Utf8();
}
static WebElement HitTestUrlElement(WebView* view, int x, int y) {
WebPoint hit_point(x, y);
WebHitTestResult hit_test_result = view->HitTestResultAt(hit_point);
return hit_test_result.UrlElement();
}
TEST_P(WebViewTest, ImageMapUrls) {
std::string url = RegisterMockedHttpURLLoad("image-map.html");
WebView* web_view = web_view_helper_.InitializeAndLoad(url);
web_view->Resize(WebSize(400, 400));
std::string image_url =
"data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=";
EXPECT_EQ("area", HitTestElementId(web_view, 25, 25));
EXPECT_EQ("area",
HitTestUrlElement(web_view, 25, 25).GetAttribute("id").Utf8());
EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 25, 25));
EXPECT_EQ("image", HitTestElementId(web_view, 75, 25));
EXPECT_TRUE(HitTestUrlElement(web_view, 75, 25).IsNull());
EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 75, 25));
}
TEST_P(WebViewTest, BrokenImage) {
URLTestHelpers::RegisterMockedErrorURLLoad(
KURL(ToKURL(base_url_), "non_existent.png"));
std::string url = RegisterMockedHttpURLLoad("image-broken.html");
WebViewImpl* web_view = web_view_helper_.Initialize();
web_view->GetSettings()->SetLoadsImagesAutomatically(true);
LoadFrame(web_view->MainFrameImpl(), url);
web_view->Resize(WebSize(400, 400));
std::string image_url = "http://www.test.com/non_existent.png";
EXPECT_EQ("image", HitTestElementId(web_view, 25, 25));
EXPECT_TRUE(HitTestUrlElement(web_view, 25, 25).IsNull());
EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 25, 25));
}
TEST_P(WebViewTest, BrokenInputImage) {
URLTestHelpers::RegisterMockedErrorURLLoad(
KURL(ToKURL(base_url_), "non_existent.png"));
std::string url = RegisterMockedHttpURLLoad("input-image-broken.html");
WebViewImpl* web_view = web_view_helper_.Initialize();
web_view->GetSettings()->SetLoadsImagesAutomatically(true);
LoadFrame(web_view->MainFrameImpl(), url);
web_view->Resize(WebSize(400, 400));
std::string image_url = "http://www.test.com/non_existent.png";
EXPECT_EQ("image", HitTestElementId(web_view, 25, 25));
EXPECT_TRUE(HitTestUrlElement(web_view, 25, 25).IsNull());
EXPECT_EQ(image_url, HitTestAbsoluteUrl(web_view, 25, 25));
}
TEST_P(WebViewTest, SetBaseBackgroundColor) {
const WebColor kWhite = 0xFFFFFFFF;
const WebColor kBlue = 0xFF0000FF;
const WebColor kDarkCyan = 0xFF227788;
const WebColor kTranslucentPutty = 0x80BFB196;
const WebColor kTransparent = 0x00000000;
WebViewImpl* web_view = web_view_helper_.Initialize();
EXPECT_EQ(kWhite, web_view->BackgroundColor());
web_view->SetBaseBackgroundColor(kBlue);
EXPECT_EQ(kBlue, web_view->BackgroundColor());
WebURL base_url = URLTestHelpers::ToKURL("http://example.com/");
FrameTestHelpers::LoadHTMLString(web_view->MainFrameImpl(),
"<html><head><style>body "
"{background-color:#227788}</style></head></"
"html>",
base_url);
EXPECT_EQ(kDarkCyan, web_view->BackgroundColor());
FrameTestHelpers::LoadHTMLString(web_view->MainFrameImpl(),
"<html><head><style>body "
"{background-color:rgba(255,0,0,0.5)}</"
"style></head></html>",
base_url);
// Expected: red (50% alpha) blended atop base of kBlue.
EXPECT_EQ(0xFF80007F, web_view->BackgroundColor());
web_view->SetBaseBackgroundColor(kTranslucentPutty);
// Expected: red (50% alpha) blended atop kTranslucentPutty. Note the alpha.
EXPECT_EQ(0xBFE93A31, web_view->BackgroundColor());
web_view->SetBaseBackgroundColor(kTransparent);
FrameTestHelpers::LoadHTMLString(web_view->MainFrameImpl(),
"<html><head><style>body "
"{background-color:transparent}</style></"
"head></html>",
base_url);
// Expected: transparent on top of kTransparent will still be transparent.
EXPECT_EQ(kTransparent, web_view->BackgroundColor());
LocalFrame* frame = web_view->MainFrameImpl()->GetFrame();
// The shutdown() calls are a hack to prevent this test
// from violating invariants about frame state during navigation/detach.
frame->GetDocument()->Shutdown();
// Creating a new frame view with the background color having 0 alpha.
frame->CreateView(IntSize(1024, 768), Color::kTransparent);
EXPECT_EQ(kTransparent, frame->View()->BaseBackgroundColor());
frame->View()->Dispose();
const Color transparent_red(100, 0, 0, 0);
frame->CreateView(IntSize(1024, 768), transparent_red);
EXPECT_EQ(transparent_red, frame->View()->BaseBackgroundColor());
frame->View()->Dispose();
}
TEST_P(WebViewTest, SetBaseBackgroundColorBeforeMainFrame) {
// Note: this test doesn't use WebViewHelper since it intentionally runs
// initialization code between WebView and WebLocalFrame creation.
const WebColor kBlue = 0xFF0000FF;
FrameTestHelpers::TestWebViewClient web_view_client;
WebViewImpl* web_view = static_cast<WebViewImpl*>(WebView::Create(
&web_view_client, mojom::PageVisibilityState::kVisible, nullptr));
EXPECT_NE(kBlue, web_view->BackgroundColor());
// webView does not have a frame yet, but we should still be able to set the
// background color.
web_view->SetBaseBackgroundColor(kBlue);
EXPECT_EQ(kBlue, web_view->BackgroundColor());
FrameTestHelpers::TestWebFrameClient web_frame_client;
WebLocalFrame* frame = WebLocalFrame::CreateMainFrame(
web_view, &web_frame_client, nullptr, nullptr);
web_frame_client.Bind(frame);
web_view->Close();
}
TEST_P(WebViewTest, SetBaseBackgroundColorAndBlendWithExistingContent) {
const WebColor kAlphaRed = 0x80FF0000;
const WebColor kAlphaGreen = 0x8000FF00;
const int kWidth = 100;
const int kHeight = 100;
WebViewImpl* web_view = web_view_helper_.Initialize();
// Set WebView background to green with alpha.
web_view->SetBaseBackgroundColor(kAlphaGreen);
web_view->GetSettings()->SetShouldClearDocumentBackground(false);
web_view->Resize(WebSize(kWidth, kHeight));
web_view->UpdateAllLifecyclePhases();
// Set canvas background to red with alpha.
SkBitmap bitmap;
bitmap.allocN32Pixels(kWidth, kHeight);
SkCanvas canvas(bitmap);
canvas.clear(kAlphaRed);
PaintRecordBuilder builder;
// Paint the root of the main frame in the way that CompositedLayerMapping
// would.
LocalFrameView* view = web_view_helper_.LocalMainFrame()->GetFrameView();
PaintLayer* root_layer = view->GetLayoutView()->Layer();
LayoutRect paint_rect(0, 0, kWidth, kHeight);
PaintLayerPaintingInfo painting_info(root_layer, paint_rect,
kGlobalPaintNormalPhase, LayoutSize());
view->GetLayoutView()->GetDocument().Lifecycle().AdvanceTo(
DocumentLifecycle::kInPaint);
PaintLayerPainter(*root_layer)
.PaintLayerContents(builder.Context(), painting_info,
kPaintLayerPaintingCompositingAllPhases);
view->GetLayoutView()->GetDocument().Lifecycle().AdvanceTo(
DocumentLifecycle::kPaintClean);
builder.EndRecording()->Playback(&canvas);
// The result should be a blend of red and green.
SkColor color = bitmap.getColor(kWidth / 2, kHeight / 2);
EXPECT_TRUE(RedChannel(color));
EXPECT_TRUE(GreenChannel(color));
}
TEST_P(WebViewTest, FocusIsInactive) {
RegisterMockedHttpURLLoad("visible_iframe.html");
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + "visible_iframe.html");
web_view->SetFocus(true);
web_view->SetIsActive(true);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
EXPECT_TRUE(frame->GetFrame()->GetDocument()->IsHTMLDocument());
Document* document = frame->GetFrame()->GetDocument();
EXPECT_TRUE(document->hasFocus());
web_view->SetFocus(false);
web_view->SetIsActive(false);
EXPECT_FALSE(document->hasFocus());
web_view->SetFocus(true);
web_view->SetIsActive(true);
EXPECT_TRUE(document->hasFocus());
web_view->SetFocus(true);
web_view->SetIsActive(false);
EXPECT_FALSE(document->hasFocus());
web_view->SetFocus(false);
web_view->SetIsActive(true);
EXPECT_FALSE(document->hasFocus());
}
TEST_P(WebViewTest, ActiveState) {
RegisterMockedHttpURLLoad("visible_iframe.html");
WebView* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + "visible_iframe.html");
ASSERT_TRUE(web_view);
web_view->SetIsActive(true);
EXPECT_TRUE(web_view->IsActive());
web_view->SetIsActive(false);
EXPECT_FALSE(web_view->IsActive());
web_view->SetIsActive(true);
EXPECT_TRUE(web_view->IsActive());
}
TEST_P(WebViewTest, HitTestResultAtWithPageScale) {
std::string url = base_url_ + "specify_size.html?" + "50px" + ":" + "50px";
URLTestHelpers::RegisterMockedURLLoad(
ToKURL(url), testing::CoreTestDataPath("specify_size.html"));
WebView* web_view = web_view_helper_.InitializeAndLoad(url);
web_view->Resize(WebSize(100, 100));
WebPoint hit_point(75, 75);
// Image is at top left quandrant, so should not hit it.
WebHitTestResult negative_result = web_view->HitTestResultAt(hit_point);
EXPECT_FALSE(
negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result.Reset();
// Scale page up 2x so image should occupy the whole viewport.
web_view->SetPageScaleFactor(2.0f);
WebHitTestResult positive_result = web_view->HitTestResultAt(hit_point);
EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
positive_result.Reset();
}
TEST_P(WebViewTest, HitTestResultAtWithPageScaleAndPan) {
std::string url = base_url_ + "specify_size.html?" + "50px" + ":" + "50px";
URLTestHelpers::RegisterMockedURLLoad(
ToKURL(url), testing::CoreTestDataPath("specify_size.html"));
WebViewImpl* web_view = web_view_helper_.Initialize();
LoadFrame(web_view->MainFrameImpl(), url);
web_view->Resize(WebSize(100, 100));
WebPoint hit_point(75, 75);
// Image is at top left quandrant, so should not hit it.
WebHitTestResult negative_result = web_view->HitTestResultAt(hit_point);
EXPECT_FALSE(
negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result.Reset();
// Scale page up 2x so image should occupy the whole viewport.
web_view->SetPageScaleFactor(2.0f);
WebHitTestResult positive_result = web_view->HitTestResultAt(hit_point);
EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
positive_result.Reset();
// Pan around the zoomed in page so the image is not visible in viewport.
web_view->SetVisualViewportOffset(WebFloatPoint(100, 100));
WebHitTestResult negative_result2 = web_view->HitTestResultAt(hit_point);
EXPECT_FALSE(
negative_result2.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result2.Reset();
}
TEST_P(WebViewTest, HitTestResultForTapWithTapArea) {
std::string url = RegisterMockedHttpURLLoad("hit_test.html");
WebView* web_view = web_view_helper_.InitializeAndLoad(url);
web_view->Resize(WebSize(100, 100));
WebPoint hit_point(55, 55);
// Image is at top left quandrant, so should not hit it.
WebHitTestResult negative_result = web_view->HitTestResultAt(hit_point);
EXPECT_FALSE(
negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result.Reset();
// The tap area is 20 by 20 square, centered at 55, 55.
WebSize tap_area(20, 20);
WebHitTestResult positive_result =
web_view->HitTestResultForTap(hit_point, tap_area);
EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
positive_result.Reset();
// Move the hit point the image is just outside the tapped area now.
hit_point = WebPoint(61, 61);
WebHitTestResult negative_result2 =
web_view->HitTestResultForTap(hit_point, tap_area);
EXPECT_FALSE(
negative_result2.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result2.Reset();
}
TEST_P(WebViewTest, HitTestResultForTapWithTapAreaPageScaleAndPan) {
std::string url = RegisterMockedHttpURLLoad("hit_test.html");
WebViewImpl* web_view = web_view_helper_.Initialize();
LoadFrame(web_view->MainFrameImpl(), url);
web_view->Resize(WebSize(100, 100));
WebPoint hit_point(55, 55);
// Image is at top left quandrant, so should not hit it.
WebHitTestResult negative_result = web_view->HitTestResultAt(hit_point);
EXPECT_FALSE(
negative_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result.Reset();
// The tap area is 20 by 20 square, centered at 55, 55.
WebSize tap_area(20, 20);
WebHitTestResult positive_result =
web_view->HitTestResultForTap(hit_point, tap_area);
EXPECT_TRUE(positive_result.GetNode().To<WebElement>().HasHTMLTagName("img"));
positive_result.Reset();
// Zoom in and pan around the page so the image is not visible in viewport.
web_view->SetPageScaleFactor(2.0f);
web_view->SetVisualViewportOffset(WebFloatPoint(100, 100));
WebHitTestResult negative_result2 =
web_view->HitTestResultForTap(hit_point, tap_area);
EXPECT_FALSE(
negative_result2.GetNode().To<WebElement>().HasHTMLTagName("img"));
negative_result2.Reset();
}
void WebViewTest::TestAutoResize(
const WebSize& min_auto_resize,
const WebSize& max_auto_resize,
const std::string& page_width,
const std::string& page_height,
int expected_width,
int expected_height,
HorizontalScrollbarState expected_horizontal_state,
VerticalScrollbarState expected_vertical_state) {
AutoResizeWebViewClient client;
std::string url =
base_url_ + "specify_size.html?" + page_width + ":" + page_height;
URLTestHelpers::RegisterMockedURLLoad(
ToKURL(url), testing::CoreTestDataPath("specify_size.html"));
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(url, nullptr, &client);
client.GetTestData().SetWebView(web_view);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
LocalFrameView* frame_view = frame->GetFrame()->View();
frame_view->UpdateLayout();
EXPECT_FALSE(frame_view->LayoutPending());
EXPECT_FALSE(frame_view->NeedsLayout());
web_view->EnableAutoResizeMode(min_auto_resize, max_auto_resize);
EXPECT_TRUE(frame_view->LayoutPending());
EXPECT_TRUE(frame_view->NeedsLayout());
frame_view->UpdateLayout();
EXPECT_TRUE(frame->GetFrame()->GetDocument()->IsHTMLDocument());
EXPECT_EQ(expected_width, client.GetTestData().Width());
EXPECT_EQ(expected_height, client.GetTestData().Height());
// Android disables main frame scrollbars.
#if !defined(OS_ANDROID)
EXPECT_EQ(expected_horizontal_state,
client.GetTestData().GetHorizontalScrollbarState());
EXPECT_EQ(expected_vertical_state,
client.GetTestData().GetVerticalScrollbarState());
#endif
// Explicitly reset to break dependency on locally scoped client.
web_view_helper_.Reset();
}
TEST_P(WebViewTest, AutoResizeMinimumSize) {
WebSize min_auto_resize(91, 56);
WebSize max_auto_resize(403, 302);
std::string page_width = "91px";
std::string page_height = "56px";
int expected_width = 91;
int expected_height = 56;
TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
expected_width, expected_height, kNoHorizontalScrollbar,
kNoVerticalScrollbar);
}
TEST_P(WebViewTest, AutoResizeHeightOverflowAndFixedWidth) {
WebSize min_auto_resize(90, 95);
WebSize max_auto_resize(90, 100);
std::string page_width = "60px";
std::string page_height = "200px";
int expected_width = 90;
int expected_height = 100;
TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
expected_width, expected_height, kNoHorizontalScrollbar,
kVisibleVerticalScrollbar);
}
TEST_P(WebViewTest, AutoResizeFixedHeightAndWidthOverflow) {
WebSize min_auto_resize(90, 100);
WebSize max_auto_resize(200, 100);
std::string page_width = "300px";
std::string page_height = "80px";
int expected_width = 200;
int expected_height = 100;
TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
expected_width, expected_height, kVisibleHorizontalScrollbar,
kNoVerticalScrollbar);
}
// Next three tests disabled for https://bugs.webkit.org/show_bug.cgi?id=92318 .
// It seems we can run three AutoResize tests, then the next one breaks.
TEST_P(WebViewTest, AutoResizeInBetweenSizes) {
WebSize min_auto_resize(90, 95);
WebSize max_auto_resize(200, 300);
std::string page_width = "100px";
std::string page_height = "200px";
int expected_width = 100;
int expected_height = 200;
TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
expected_width, expected_height, kNoHorizontalScrollbar,
kNoVerticalScrollbar);
}
TEST_P(WebViewTest, AutoResizeOverflowSizes) {
WebSize min_auto_resize(90, 95);
WebSize max_auto_resize(200, 300);
std::string page_width = "300px";
std::string page_height = "400px";
int expected_width = 200;
int expected_height = 300;
TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
expected_width, expected_height, kVisibleHorizontalScrollbar,
kVisibleVerticalScrollbar);
}
TEST_P(WebViewTest, AutoResizeMaxSize) {
WebSize min_auto_resize(90, 95);
WebSize max_auto_resize(200, 300);
std::string page_width = "200px";
std::string page_height = "300px";
int expected_width = 200;
int expected_height = 300;
TestAutoResize(min_auto_resize, max_auto_resize, page_width, page_height,
expected_width, expected_height, kNoHorizontalScrollbar,
kNoVerticalScrollbar);
}
void WebViewTest::TestTextInputType(WebTextInputType expected_type,
const std::string& html_file) {
RegisterMockedHttpURLLoad(html_file);
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + html_file);
WebInputMethodController* controller =
web_view->MainFrameImpl()->GetInputMethodController();
EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputType());
EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputInfo().type);
web_view->SetInitialFocus(false);
EXPECT_EQ(expected_type, controller->TextInputType());
EXPECT_EQ(expected_type, controller->TextInputInfo().type);
web_view->ClearFocusedElement();
EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputType());
EXPECT_EQ(kWebTextInputTypeNone, controller->TextInputInfo().type);
}
TEST_P(WebViewTest, TextInputType) {
TestTextInputType(kWebTextInputTypeText, "input_field_default.html");
TestTextInputType(kWebTextInputTypePassword, "input_field_password.html");
TestTextInputType(kWebTextInputTypeEmail, "input_field_email.html");
TestTextInputType(kWebTextInputTypeSearch, "input_field_search.html");
TestTextInputType(kWebTextInputTypeNumber, "input_field_number.html");
TestTextInputType(kWebTextInputTypeTelephone, "input_field_tel.html");
TestTextInputType(kWebTextInputTypeURL, "input_field_url.html");
}
TEST_P(WebViewTest, TextInputInfoUpdateStyleAndLayout) {
FrameTestHelpers::WebViewHelper web_view_helper;
WebViewImpl* web_view_impl = web_view_helper.Initialize();
WebURL base_url = URLTestHelpers::ToKURL("http://example.com/");
// Here, we need to construct a document that has a special property:
// Adding id="foo" to the <path> element will trigger creation of an SVG
// instance tree for the use <use> element.
// This is significant, because SVG instance trees are actually created lazily
// during Document::updateStyleAndLayout code, thus incrementing the DOM tree
// version and freaking out the EphemeralRange (invalidating it).
FrameTestHelpers::LoadHTMLString(
web_view_impl->MainFrameImpl(),
"<svg height='100%' version='1.1' viewBox='0 0 14 14' width='100%'>"
"<use xmlns:xlink='http://www.w3.org/1999/xlink' xlink:href='#foo'></use>"
"<path d='M 100 100 L 300 100 L 200 300 z' fill='#000'></path>"
"</svg>"
"<input>",
base_url);
web_view_impl->SetInitialFocus(false);
// Add id="foo" to <path>, thus triggering the condition described above.
Document* document =
web_view_impl->MainFrameImpl()->GetFrame()->GetDocument();
document->body()
->QuerySelector("path", ASSERT_NO_EXCEPTION)
->SetIdAttribute("foo");
// This should not DCHECK.
EXPECT_EQ(kWebTextInputTypeText, web_view_impl->MainFrameImpl()
->GetInputMethodController()
->TextInputInfo()
.type);
}
void WebViewTest::TestInputMode(WebTextInputMode expected_input_mode,
const std::string& html_file) {
RegisterMockedHttpURLLoad(html_file);
WebViewImpl* web_view_impl =
web_view_helper_.InitializeAndLoad(base_url_ + html_file);
web_view_impl->SetInitialFocus(false);
EXPECT_EQ(expected_input_mode, web_view_impl->MainFrameImpl()
->GetInputMethodController()
->TextInputInfo()
.input_mode);
}
TEST_P(WebViewTest, InputMode) {
TestInputMode(WebTextInputMode::kWebTextInputModeDefault,
"input_mode_default.html");
TestInputMode(WebTextInputMode::kWebTextInputModeDefault,
"input_mode_default_unknown.html");
TestInputMode(WebTextInputMode::kWebTextInputModeNone,
"input_mode_type_none.html");
TestInputMode(WebTextInputMode::kWebTextInputModeText,
"input_mode_type_text.html");
TestInputMode(WebTextInputMode::kWebTextInputModeTel,
"input_mode_type_tel.html");
TestInputMode(WebTextInputMode::kWebTextInputModeUrl,
"input_mode_type_url.html");
TestInputMode(WebTextInputMode::kWebTextInputModeEmail,
"input_mode_type_email.html");
TestInputMode(WebTextInputMode::kWebTextInputModeNumeric,
"input_mode_type_numeric.html");
TestInputMode(WebTextInputMode::kWebTextInputModeDecimal,
"input_mode_type_decimal.html");
TestInputMode(WebTextInputMode::kWebTextInputModeSearch,
"input_mode_type_search.html");
}
TEST_P(WebViewTest, TextInputInfoWithReplacedElements) {
std::string url = RegisterMockedHttpURLLoad("div_with_image.html");
URLTestHelpers::RegisterMockedURLLoad(
ToKURL("http://www.test.com/foo.png"),
testing::CoreTestDataPath("white-1x1.png"));
WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(url);
web_view_impl->SetInitialFocus(false);
WebTextInputInfo info = web_view_impl->MainFrameImpl()
->GetInputMethodController()
->TextInputInfo();
EXPECT_EQ("foo\xef\xbf\xbc", info.value.Utf8());
}
TEST_P(WebViewTest, SetEditableSelectionOffsetsAndTextInputInfo) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
WebInputMethodController* active_input_method_controller =
frame->GetInputMethodController();
frame->SetEditableSelectionOffsets(5, 13);
EXPECT_EQ("56789abc", frame->SelectionAsText());
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value);
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(13, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
RegisterMockedHttpURLLoad("content_editable_populated.html");
web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "content_editable_populated.html");
web_view->SetInitialFocus(false);
frame = web_view->MainFrameImpl();
active_input_method_controller = frame->GetInputMethodController();
frame->SetEditableSelectionOffsets(8, 19);
EXPECT_EQ("89abcdefghi", frame->SelectionAsText());
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz", info.value);
EXPECT_EQ(8, info.selection_start);
EXPECT_EQ(19, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
// Regression test for crbug.com/663645
TEST_P(WebViewTest, FinishComposingTextDoesNotAssert) {
RegisterMockedHttpURLLoad("input_field_default.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_default.html");
web_view->SetInitialFocus(false);
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
// The test requires non-empty composition.
std::string composition_text("hello");
WebVector<WebImeTextSpan> empty_ime_text_spans;
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 5, 5);
// Do arbitrary change to make layout dirty.
Document& document = *web_view->MainFrameImpl()->GetFrame()->GetDocument();
Element* br = document.createElement("br");
document.body()->AppendChild(br);
// Should not hit assertion when calling
// WebInputMethodController::finishComposingText with non-empty composition
// and dirty layout.
active_input_method_controller->FinishComposingText(
WebInputMethodController::kKeepSelection);
}
TEST_P(WebViewTest, FinishComposingTextCursorPositionChange) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
// Set up a composition that needs to be committed.
std::string composition_text("hello");
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
WebVector<WebImeTextSpan> empty_ime_text_spans;
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 3, 3);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
EXPECT_EQ(3, info.selection_start);
EXPECT_EQ(3, info.selection_end);
EXPECT_EQ(0, info.composition_start);
EXPECT_EQ(5, info.composition_end);
active_input_method_controller->FinishComposingText(
WebInputMethodController::kKeepSelection);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ(3, info.selection_start);
EXPECT_EQ(3, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 3, 3);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helhellolo", std::string(info.value.Utf8().data()));
EXPECT_EQ(6, info.selection_start);
EXPECT_EQ(6, info.selection_end);
EXPECT_EQ(3, info.composition_start);
EXPECT_EQ(8, info.composition_end);
active_input_method_controller->FinishComposingText(
WebInputMethodController::kDoNotKeepSelection);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ(8, info.selection_start);
EXPECT_EQ(8, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
TEST_P(WebViewTest, SetCompositionForNewCaretPositions) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
WebVector<WebImeTextSpan> empty_ime_text_spans;
active_input_method_controller->CommitText("hello", empty_ime_text_spans,
WebRange(), 0);
active_input_method_controller->CommitText("world", empty_ime_text_spans,
WebRange(), -5);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Set up a composition that needs to be committed.
std::string composition_text("ABC");
// Caret is on the left of composing text.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 0, 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret is on the right of composing text.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 3, 3);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(8, info.selection_start);
EXPECT_EQ(8, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret is between composing text and left boundary.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), -2, -2);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(3, info.selection_start);
EXPECT_EQ(3, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret is between composing text and right boundary.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 5, 5);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(10, info.selection_start);
EXPECT_EQ(10, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret is on the left boundary.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), -5, -5);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret is on the right boundary.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 8, 8);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(13, info.selection_start);
EXPECT_EQ(13, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret exceeds the left boundary.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), -100, -100);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
// Caret exceeds the right boundary.
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 100, 100);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloABCworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(13, info.selection_start);
EXPECT_EQ(13, info.selection_end);
EXPECT_EQ(5, info.composition_start);
EXPECT_EQ(8, info.composition_end);
}
TEST_P(WebViewTest, SetCompositionWithEmptyText) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
WebVector<WebImeTextSpan> empty_ime_text_spans;
active_input_method_controller->CommitText("hello", empty_ime_text_spans,
WebRange(), 0);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
active_input_method_controller->SetComposition(
WebString::FromUTF8(""), empty_ime_text_spans, WebRange(), 0, 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
active_input_method_controller->SetComposition(
WebString::FromUTF8(""), empty_ime_text_spans, WebRange(), -2, -2);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
EXPECT_EQ(3, info.selection_start);
EXPECT_EQ(3, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
TEST_P(WebViewTest, CommitTextForNewCaretPositions) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
WebVector<WebImeTextSpan> empty_ime_text_spans;
// Caret is on the left of composing text.
active_input_method_controller->CommitText("ab", empty_ime_text_spans,
WebRange(), -2);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("ab", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Caret is on the right of composing text.
active_input_method_controller->CommitText("c", empty_ime_text_spans,
WebRange(), 1);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("cab", std::string(info.value.Utf8().data()));
EXPECT_EQ(2, info.selection_start);
EXPECT_EQ(2, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Caret is on the left boundary.
active_input_method_controller->CommitText("def", empty_ime_text_spans,
WebRange(), -5);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("cadefb", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Caret is on the right boundary.
active_input_method_controller->CommitText("g", empty_ime_text_spans,
WebRange(), 6);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("gcadefb", std::string(info.value.Utf8().data()));
EXPECT_EQ(7, info.selection_start);
EXPECT_EQ(7, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Caret exceeds the left boundary.
active_input_method_controller->CommitText("hi", empty_ime_text_spans,
WebRange(), -100);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("gcadefbhi", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Caret exceeds the right boundary.
active_input_method_controller->CommitText("jk", empty_ime_text_spans,
WebRange(), 100);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("jkgcadefbhi", std::string(info.value.Utf8().data()));
EXPECT_EQ(11, info.selection_start);
EXPECT_EQ(11, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
TEST_P(WebViewTest, CommitTextWhileComposing) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
WebVector<WebImeTextSpan> empty_ime_text_spans;
active_input_method_controller->SetComposition(
WebString::FromUTF8("abc"), empty_ime_text_spans, WebRange(), 0, 0);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("abc", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
EXPECT_EQ(0, info.composition_start);
EXPECT_EQ(3, info.composition_end);
// Deletes ongoing composition, inserts the specified text and moves the
// caret.
active_input_method_controller->CommitText("hello", empty_ime_text_spans,
WebRange(), -2);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
EXPECT_EQ(3, info.selection_start);
EXPECT_EQ(3, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
active_input_method_controller->SetComposition(
WebString::FromUTF8("abc"), empty_ime_text_spans, WebRange(), 0, 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helabclo", std::string(info.value.Utf8().data()));
EXPECT_EQ(3, info.selection_start);
EXPECT_EQ(3, info.selection_end);
EXPECT_EQ(3, info.composition_start);
EXPECT_EQ(6, info.composition_end);
// Deletes ongoing composition and moves the caret.
active_input_method_controller->CommitText("", empty_ime_text_spans,
WebRange(), 2);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Inserts the specified text and moves the caret.
active_input_method_controller->CommitText("world", empty_ime_text_spans,
WebRange(), -5);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
// Only moves the caret.
active_input_method_controller->CommitText("", empty_ime_text_spans,
WebRange(), 5);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("helloworld", std::string(info.value.Utf8().data()));
EXPECT_EQ(10, info.selection_start);
EXPECT_EQ(10, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
TEST_P(WebViewTest, FinishCompositionDoesNotRevealSelection) {
RegisterMockedHttpURLLoad("form_with_input.html");
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + "form_with_input.html");
web_view->Resize(WebSize(800, 600));
web_view->SetInitialFocus(false);
EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().height);
// Set up a composition from existing text that needs to be committed.
Vector<ImeTextSpan> empty_ime_text_spans;
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
frame->GetFrame()->GetInputMethodController().SetCompositionFromExistingText(
empty_ime_text_spans, 0, 3);
// Scroll the input field out of the viewport.
Element* element = static_cast<Element*>(
web_view->MainFrameImpl()->GetDocument().GetElementById("btn"));
element->scrollIntoView();
float offset_height = web_view->MainFrameImpl()->GetScrollOffset().height;
EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
EXPECT_LT(0, offset_height);
WebTextInputInfo info = frame->GetInputMethodController()->TextInputInfo();
EXPECT_EQ("hello", std::string(info.value.Utf8().data()));
// Verify that the input field is not scrolled back into the viewport.
frame->FrameWidget()
->GetActiveWebInputMethodController()
->FinishComposingText(WebInputMethodController::kDoNotKeepSelection);
EXPECT_EQ(0, web_view->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(offset_height, web_view->MainFrameImpl()->GetScrollOffset().height);
}
TEST_P(WebViewTest, InsertNewLinePlacementAfterFinishComposingText) {
RegisterMockedHttpURLLoad("text_area_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "text_area_populated.html");
web_view->SetInitialFocus(false);
WebVector<WebImeTextSpan> empty_ime_text_spans;
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
WebInputMethodController* active_input_method_controller =
frame->GetInputMethodController();
frame->SetEditableSelectionOffsets(4, 4);
frame->SetCompositionFromExistingText(8, 12, empty_ime_text_spans);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("0123456789abcdefghijklmnopqrstuvwxyz",
std::string(info.value.Utf8().data()));
EXPECT_EQ(4, info.selection_start);
EXPECT_EQ(4, info.selection_end);
EXPECT_EQ(8, info.composition_start);
EXPECT_EQ(12, info.composition_end);
active_input_method_controller->FinishComposingText(
WebInputMethodController::kKeepSelection);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ(4, info.selection_start);
EXPECT_EQ(4, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
std::string composition_text("\n");
active_input_method_controller->CommitText(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
EXPECT_EQ("0123\n456789abcdefghijklmnopqrstuvwxyz",
std::string(info.value.Utf8().data()));
}
TEST_P(WebViewTest, ExtendSelectionAndDelete) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
web_view->SetInitialFocus(false);
frame->SetEditableSelectionOffsets(10, 10);
frame->ExtendSelectionAndDelete(5, 8);
WebInputMethodController* active_input_method_controller =
frame->GetInputMethodController();
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("01234ijklmnopqrstuvwxyz", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
frame->ExtendSelectionAndDelete(10, 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("ijklmnopqrstuvwxyz", std::string(info.value.Utf8().data()));
}
TEST_P(WebViewTest, DeleteSurroundingText) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebView* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
WebLocalFrameImpl* frame = ToWebLocalFrameImpl(web_view->MainFrame());
WebInputMethodController* active_input_method_controller =
frame->GetInputMethodController();
web_view->SetInitialFocus(false);
frame->SetEditableSelectionOffsets(10, 10);
frame->DeleteSurroundingText(5, 8);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("01234ijklmnopqrstuvwxyz", std::string(info.value.Utf8().data()));
EXPECT_EQ(5, info.selection_start);
EXPECT_EQ(5, info.selection_end);
frame->SetEditableSelectionOffsets(5, 10);
frame->DeleteSurroundingText(3, 5);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("01ijklmstuvwxyz", std::string(info.value.Utf8().data()));
EXPECT_EQ(2, info.selection_start);
EXPECT_EQ(7, info.selection_end);
frame->SetEditableSelectionOffsets(5, 5);
frame->DeleteSurroundingText(10, 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("lmstuvwxyz", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
frame->DeleteSurroundingText(0, 20);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
frame->DeleteSurroundingText(10, 10);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("", std::string(info.value.Utf8().data()));
EXPECT_EQ(0, info.selection_start);
EXPECT_EQ(0, info.selection_end);
}
TEST_P(WebViewTest, SetCompositionFromExistingText) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
ime_text_spans[0] =
WebImeTextSpan(WebImeTextSpan::Type::kComposition, 0, 4, 0, false, 0);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
WebInputMethodController* active_input_method_controller =
frame->GetInputMethodController();
frame->SetEditableSelectionOffsets(4, 10);
frame->SetCompositionFromExistingText(8, 12, ime_text_spans);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ(4, info.selection_start);
EXPECT_EQ(10, info.selection_end);
EXPECT_EQ(8, info.composition_start);
EXPECT_EQ(12, info.composition_end);
WebVector<WebImeTextSpan> empty_ime_text_spans;
frame->SetCompositionFromExistingText(0, 0, empty_ime_text_spans);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ(4, info.selection_start);
EXPECT_EQ(10, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
TEST_P(WebViewTest, SetCompositionFromExistingTextInTextArea) {
RegisterMockedHttpURLLoad("text_area_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "text_area_populated.html");
web_view->SetInitialFocus(false);
WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
ime_text_spans[0] =
WebImeTextSpan(WebImeTextSpan::Type::kComposition, 0, 4, 0, false, 0);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
WebInputMethodController* active_input_method_controller =
frame->FrameWidget()->GetActiveWebInputMethodController();
frame->SetEditableSelectionOffsets(27, 27);
std::string new_line_text("\n");
WebVector<WebImeTextSpan> empty_ime_text_spans;
active_input_method_controller->CommitText(
WebString::FromUTF8(new_line_text.c_str()), empty_ime_text_spans,
WebRange(), 0);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz",
std::string(info.value.Utf8().data()));
frame->SetEditableSelectionOffsets(31, 31);
frame->SetCompositionFromExistingText(30, 34, ime_text_spans);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("0123456789abcdefghijklmnopq\nrstuvwxyz",
std::string(info.value.Utf8().data()));
EXPECT_EQ(31, info.selection_start);
EXPECT_EQ(31, info.selection_end);
EXPECT_EQ(30, info.composition_start);
EXPECT_EQ(34, info.composition_end);
std::string composition_text("yolo");
active_input_method_controller->CommitText(
WebString::FromUTF8(composition_text.c_str()), empty_ime_text_spans,
WebRange(), 0);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("0123456789abcdefghijklmnopq\nrsyoloxyz",
std::string(info.value.Utf8().data()));
EXPECT_EQ(34, info.selection_start);
EXPECT_EQ(34, info.selection_end);
EXPECT_EQ(-1, info.composition_start);
EXPECT_EQ(-1, info.composition_end);
}
TEST_P(WebViewTest, SetCompositionFromExistingTextInRichText) {
RegisterMockedHttpURLLoad("content_editable_rich_text.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "content_editable_rich_text.html");
web_view->SetInitialFocus(false);
WebVector<WebImeTextSpan> ime_text_spans(static_cast<size_t>(1));
ime_text_spans[0] =
WebImeTextSpan(WebImeTextSpan::Type::kComposition, 0, 4, 0, false, 0);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
frame->SetEditableSelectionOffsets(1, 1);
WebDocument document = web_view->MainFrameImpl()->GetDocument();
EXPECT_FALSE(document.GetElementById("bold").IsNull());
frame->SetCompositionFromExistingText(0, 4, ime_text_spans);
EXPECT_FALSE(document.GetElementById("bold").IsNull());
}
TEST_P(WebViewTest, SetEditableSelectionOffsetsKeepsComposition) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
web_view->SetInitialFocus(false);
std::string composition_text_first("hello ");
std::string composition_text_second("world");
WebVector<WebImeTextSpan> empty_ime_text_spans;
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
active_input_method_controller->CommitText(
WebString::FromUTF8(composition_text_first.c_str()), empty_ime_text_spans,
WebRange(), 0);
active_input_method_controller->SetComposition(
WebString::FromUTF8(composition_text_second.c_str()),
empty_ime_text_spans, WebRange(), 5, 5);
WebTextInputInfo info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
EXPECT_EQ(11, info.selection_start);
EXPECT_EQ(11, info.selection_end);
EXPECT_EQ(6, info.composition_start);
EXPECT_EQ(11, info.composition_end);
WebLocalFrameImpl* frame = web_view->MainFrameImpl();
frame->SetEditableSelectionOffsets(6, 6);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
EXPECT_EQ(6, info.selection_start);
EXPECT_EQ(6, info.selection_end);
EXPECT_EQ(6, info.composition_start);
EXPECT_EQ(11, info.composition_end);
frame->SetEditableSelectionOffsets(8, 8);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
EXPECT_EQ(8, info.selection_start);
EXPECT_EQ(8, info.selection_end);
EXPECT_EQ(6, info.composition_start);
EXPECT_EQ(11, info.composition_end);
frame->SetEditableSelectionOffsets(11, 11);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
EXPECT_EQ(11, info.selection_start);
EXPECT_EQ(11, info.selection_end);
EXPECT_EQ(6, info.composition_start);
EXPECT_EQ(11, info.composition_end);
frame->SetEditableSelectionOffsets(6, 11);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
EXPECT_EQ(6, info.selection_start);
EXPECT_EQ(11, info.selection_end);
EXPECT_EQ(6, info.composition_start);
EXPECT_EQ(11, info.composition_end);
frame->SetEditableSelectionOffsets(2, 2);
info = active_input_method_controller->TextInputInfo();
EXPECT_EQ("hello world", std::string(info.value.Utf8().data()));
EXPECT_EQ(2, info.selection_start);
EXPECT_EQ(2, info.selection_end);
// Composition range should be reset by browser process or keyboard apps.
EXPECT_EQ(6, info.composition_start);
EXPECT_EQ(11, info.composition_end);
}
TEST_P(WebViewTest, IsSelectionAnchorFirst) {
RegisterMockedHttpURLLoad("input_field_populated.html");
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
base_url_ + "input_field_populated.html");
WebLocalFrame* frame = web_view->MainFrameImpl();
web_view->SetInitialFocus(false);
frame->SetEditableSelectionOffsets(4, 10);
EXPECT_TRUE(frame->IsSelectionAnchorFirst());
WebRect anchor;
WebRect focus;
web_view->SelectionBounds(anchor, focus);
frame->SelectRange(WebPoint(focus.x, focus.y), WebPoint(anchor.x, anchor.y));
EXPECT_FALSE(frame->IsSelectionAnchorFirst());
}
TEST_P(
WebViewTest,
MoveFocusToNextFocusableElementInFormWithKeyEventListenersAndNonEditableElements) {
const std::string test_file =
"advance_focus_in_form_with_key_event_listeners.html";
RegisterMockedHttpURLLoad(test_file);
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + test_file);
web_view->SetInitialFocus(false);
Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
const int default_text_input_flags = kWebTextInputFlagNone;
struct FocusedElement {
AtomicString element_id;
int next_previous_flags;
} focused_elements[] = {
{"input1",
default_text_input_flags | kWebTextInputFlagHaveNextFocusableElement},
{"contenteditable1", kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"input2", default_text_input_flags |
kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"textarea1", default_text_input_flags |
kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"input3", default_text_input_flags |
kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"textarea2", default_text_input_flags |
kWebTextInputFlagHavePreviousFocusableElement},
};
// Forward Navigation in form1 with NEXT
Element* input1 = document->getElementById("input1");
input1->focus();
Element* current_focus = nullptr;
Element* next_focus = nullptr;
int next_previous_flags;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(focused_elements); ++i) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeForward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i + 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
}
// Now focus will stay on previous focus itself, because it has no next
// element.
EXPECT_EQ(current_focus, document->FocusedElement());
// Backward Navigation in form1 with PREVIOUS
for (size_t i = WTF_ARRAY_LENGTH(focused_elements); i-- > 0;) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeBackward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i - 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
}
// Now focus will stay on previous focus itself, because it has no previous
// element.
EXPECT_EQ(current_focus, document->FocusedElement());
// Setting a non editable element as focus in form1, and ensuring editable
// navigation is fine in forward and backward.
Element* button1 = document->getElementById("button1");
button1->focus();
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement,
next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
button1, kWebFocusTypeForward);
EXPECT_EQ(next_focus->GetIdAttribute(), "contenteditable1");
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
Element* content_editable1 = document->getElementById("contenteditable1");
EXPECT_EQ(content_editable1, document->FocusedElement());
button1->focus();
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
button1, kWebFocusTypeBackward);
EXPECT_EQ(next_focus->GetIdAttribute(), "input1");
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(input1, document->FocusedElement());
Element* anchor1 = document->getElementById("anchor1");
anchor1->focus();
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
// No Next/Previous element for elements outside form.
EXPECT_EQ(0, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
anchor1, kWebFocusTypeForward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
// Since anchor is not a form control element, next/previous element will
// be null, hence focus will stay same as it is.
EXPECT_EQ(anchor1, document->FocusedElement());
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
anchor1, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(anchor1, document->FocusedElement());
// Navigation of elements which is not part of any forms.
Element* text_area3 = document->getElementById("textarea3");
text_area3->focus();
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
// No Next/Previous element for elements outside form.
EXPECT_EQ(default_text_input_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
text_area3, kWebFocusTypeForward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
// No Next/Previous element to this element because it's not part of any
// form. Hence focus won't change wrt NEXT/PREVIOUS.
EXPECT_EQ(text_area3, document->FocusedElement());
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
text_area3, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(text_area3, document->FocusedElement());
// Navigation from an element which is part of a form but not an editable
// element.
Element* button2 = document->getElementById("button2");
button2->focus();
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
// No Next element for this element, due to last element outside the form.
EXPECT_EQ(kWebTextInputFlagHavePreviousFocusableElement, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
button2, kWebFocusTypeForward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
// No Next element to this element because it's not part of any form.
// Hence focus won't change wrt NEXT.
EXPECT_EQ(button2, document->FocusedElement());
Element* text_area2 = document->getElementById("textarea2");
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
button2, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, text_area2);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
// Since button is a form control element from form1, ensuring focus is set
// at correct position.
EXPECT_EQ(text_area2, document->FocusedElement());
Element* content_editable2 = document->getElementById("contenteditable2");
document->SetFocusedElement(
content_editable2,
FocusParams(SelectionBehaviorOnFocus::kNone, kWebFocusTypeNone, nullptr));
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
// No Next/Previous element for elements outside form.
EXPECT_EQ(0, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
content_editable2, kWebFocusTypeForward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
// No Next/Previous element to this element because it's not part of any
// form. Hence focus won't change wrt NEXT/PREVIOUS.
EXPECT_EQ(content_editable2, document->FocusedElement());
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
content_editable2, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(content_editable2, document->FocusedElement());
// Navigation of elements which is having invalid form attribute and hence
// not part of any forms.
Element* text_area4 = document->getElementById("textarea4");
text_area4->focus();
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
// No Next/Previous element for elements which is having invalid form
// attribute.
EXPECT_EQ(default_text_input_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
text_area4, kWebFocusTypeForward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
// No Next/Previous element to this element because it's not part of any
// form. Hence focus won't change wrt NEXT/PREVIOUS.
EXPECT_EQ(text_area4, document->FocusedElement());
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
text_area4, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(text_area4, document->FocusedElement());
web_view_helper_.Reset();
}
TEST_P(
WebViewTest,
MoveFocusToNextFocusableElementInFormWithNonEditableNonFormControlElements) {
const std::string test_file =
"advance_focus_in_form_with_key_event_listeners.html";
RegisterMockedHttpURLLoad(test_file);
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + test_file);
web_view->SetInitialFocus(false);
Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
const int default_text_input_flags = kWebTextInputFlagNone;
struct FocusedElement {
const char* element_id;
int next_previous_flags;
} focused_elements[] = {
{"textarea5",
default_text_input_flags | kWebTextInputFlagHaveNextFocusableElement},
{"input4", default_text_input_flags |
kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"contenteditable3", kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"input5", kWebTextInputFlagHavePreviousFocusableElement},
};
// Forward Navigation in form2 with NEXT
Element* text_area5 = document->getElementById("textarea5");
text_area5->focus();
Element* current_focus = nullptr;
Element* next_focus = nullptr;
int next_previous_flags;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(focused_elements); ++i) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeForward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i + 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
}
// Now focus will stay on previous focus itself, because it has no next
// element.
EXPECT_EQ(current_focus, document->FocusedElement());
// Backward Navigation in form1 with PREVIOUS
for (size_t i = WTF_ARRAY_LENGTH(focused_elements); i-- > 0;) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeBackward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i - 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
}
// Now focus will stay on previous focus itself, because it has no previous
// element.
EXPECT_EQ(current_focus, document->FocusedElement());
// Setting a non editable element as focus in form1, and ensuring editable
// navigation is fine in forward and backward.
Element* anchor2 = document->getElementById("anchor2");
anchor2->focus();
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
// No Next/Previous element for non-form control elements inside form.
EXPECT_EQ(0, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
anchor2, kWebFocusTypeForward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
// Since anchor is not a form control element, next/previous element will
// be null, hence focus will stay same as it is.
EXPECT_EQ(anchor2, document->FocusedElement());
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
anchor2, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, nullptr);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(anchor2, document->FocusedElement());
web_view_helper_.Reset();
}
TEST_P(WebViewTest, MoveFocusToNextFocusableElementInFormWithTabIndexElements) {
const std::string test_file =
"advance_focus_in_form_with_tabindex_elements.html";
RegisterMockedHttpURLLoad(test_file);
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + test_file);
web_view->SetInitialFocus(false);
Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
const int default_text_input_flags = kWebTextInputFlagNone;
struct FocusedElement {
const char* element_id;
int next_previous_flags;
} focused_elements[] = {
{"textarea6",
default_text_input_flags | kWebTextInputFlagHaveNextFocusableElement},
{"input5", default_text_input_flags |
kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"contenteditable4", kWebTextInputFlagHaveNextFocusableElement |
kWebTextInputFlagHavePreviousFocusableElement},
{"input6", default_text_input_flags |
kWebTextInputFlagHavePreviousFocusableElement},
};
// Forward Navigation in form with NEXT which has tabindex attribute
// which differs visual order.
Element* text_area6 = document->getElementById("textarea6");
text_area6->focus();
Element* current_focus = nullptr;
Element* next_focus = nullptr;
int next_previous_flags;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(focused_elements); ++i) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeForward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i + 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
}
// No next editable element which is focusable with proper tab index, hence
// staying on previous focus.
EXPECT_EQ(current_focus, document->FocusedElement());
// Backward Navigation in form with PREVIOUS which has tabindex attribute
// which differs visual order.
for (size_t i = WTF_ARRAY_LENGTH(focused_elements); i-- > 0;) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeBackward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i - 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
}
// Now focus will stay on previous focus itself, because it has no previous
// element.
EXPECT_EQ(current_focus, document->FocusedElement());
// Setting an element which has invalid tabindex and ensuring it is not
// modifying further navigation.
Element* content_editable5 = document->getElementById("contenteditable5");
content_editable5->focus();
Element* input6 = document->getElementById("input6");
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
content_editable5, kWebFocusTypeForward);
EXPECT_EQ(next_focus, input6);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
EXPECT_EQ(input6, document->FocusedElement());
content_editable5->focus();
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
content_editable5, kWebFocusTypeBackward);
EXPECT_EQ(next_focus, text_area6);
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
EXPECT_EQ(text_area6, document->FocusedElement());
web_view_helper_.Reset();
}
TEST_P(WebViewTest,
MoveFocusToNextFocusableElementInFormWithDisabledAndReadonlyElements) {
const std::string test_file =
"advance_focus_in_form_with_disabled_and_readonly_elements.html";
RegisterMockedHttpURLLoad(test_file);
WebViewImpl* web_view =
web_view_helper_.InitializeAndLoad(base_url_ + test_file);
web_view->SetInitialFocus(false);
Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
WebInputMethodController* active_input_method_controller =
web_view->MainFrameImpl()
->FrameWidget()
->GetActiveWebInputMethodController();
struct FocusedElement {
const char* element_id;
int next_previous_flags;
} focused_elements[] = {
{"contenteditable6", kWebTextInputFlagHaveNextFocusableElement},
{"contenteditable7", kWebTextInputFlagHavePreviousFocusableElement},
};
// Forward Navigation in form with NEXT which has has disabled/enabled
// elements which will gets skipped during navigation.
Element* content_editable6 = document->getElementById("contenteditable6");
content_editable6->focus();
Element* current_focus = nullptr;
Element* next_focus = nullptr;
int next_previous_flags;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(focused_elements); ++i) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeForward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i + 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeForward);
}
// No next editable element which is focusable, hence staying on previous
// focus.
EXPECT_EQ(current_focus, document->FocusedElement());
// Backward Navigation in form with PREVIOUS which has has
// disabled/enabled elements which will gets skipped during navigation.
for (size_t i = WTF_ARRAY_LENGTH(focused_elements); i-- > 0;) {
current_focus = document->getElementById(focused_elements[i].element_id);
EXPECT_EQ(current_focus, document->FocusedElement());
next_previous_flags =
active_input_method_controller->ComputeWebTextInputNextPreviousFlags();
EXPECT_EQ(focused_elements[i].next_previous_flags, next_previous_flags);
next_focus =
document->GetPage()->GetFocusController().NextFocusableElementInForm(
current_focus, kWebFocusTypeBackward);
if (next_focus) {
EXPECT_EQ(next_focus->GetIdAttribute(),
focused_elements[i - 1].element_id);
}
web_view->MainFrameImpl()->AdvanceFocusInForm(kWebFocusTypeBackward);
}
// Now focus will stay on previous focus itself, because it has no previous
// element.
EXPECT_EQ(current_focus, document->FocusedElement());
web_view_helper_.Reset();
}
TEST_P(WebViewTest, ExitingDeviceEmulationResetsPageScale) {
RegisterMockedHttpURLLoad("200-by-300.html");
WebViewImpl* web_view_impl =
web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
web_view_impl->Resize(WebSize(200, 300));
float page_scale_expected = web_view_impl->PageScaleFactor();
WebDeviceEmulationParams params;
params.screen_position = WebDeviceEmulationParams::kDesktop;
params.device_scale_factor = 0;
params.scale = 1;
web_view_impl->EnableDeviceEmulation(params);
web_view_impl->SetPageScaleFactor(2);
web_view_impl->DisableDeviceEmulation();
EXPECT_EQ(page_scale_expected, web_view_impl->PageScaleFactor());
}
TEST_P(WebViewTest, HistoryResetScrollAndScaleState) {
RegisterMockedHttpURLLoad("200-by-300.html");
WebViewImpl* web_view_impl =
web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
web_view_impl->Resize(WebSize(100, 150));
web_view_impl->UpdateAllLifecyclePhases();
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
// Make the page scale and scroll with the given paremeters.
web_view_impl->SetPageScaleFactor(2.0f);
web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(94, 111));
EXPECT_EQ(2.0f, web_view_impl->PageScaleFactor());
EXPECT_EQ(94, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(111, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
LocalFrame* main_frame_local =
ToLocalFrame(web_view_impl->GetPage()->MainFrame());
main_frame_local->Loader().SaveScrollState();
EXPECT_EQ(2.0f, main_frame_local->Loader()
.GetDocumentLoader()
->GetHistoryItem()
->GetViewState()
->page_scale_factor_);
EXPECT_EQ(94, main_frame_local->Loader()
.GetDocumentLoader()
->GetHistoryItem()
->GetViewState()
->scroll_offset_.Width());
EXPECT_EQ(111, main_frame_local->Loader()
.GetDocumentLoader()
->GetHistoryItem()
->GetViewState()
->scroll_offset_.Height());
// Confirm that resetting the page state resets the saved scroll position.
web_view_impl->ResetScrollAndScaleState();
EXPECT_EQ(1.0f, web_view_impl->PageScaleFactor());
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
EXPECT_EQ(nullptr, main_frame_local->Loader()
.GetDocumentLoader()
->GetHistoryItem()
->GetViewState());
}
TEST_P(WebViewTest, BackForwardRestoreScroll) {
RegisterMockedHttpURLLoad("back_forward_restore_scroll.html");
WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(
base_url_ + "back_forward_restore_scroll.html");
web_view_impl->Resize(WebSize(640, 480));
web_view_impl->UpdateAllLifecyclePhases();
// Emulate a user scroll
web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(0, 900));
LocalFrame* main_frame_local =
ToLocalFrame(web_view_impl->GetPage()->MainFrame());
Persistent<HistoryItem> item1 =
main_frame_local->Loader().GetDocumentLoader()->GetHistoryItem();
// Click an anchor
main_frame_local->Loader().Load(FrameLoadRequest(
main_frame_local->GetDocument(),
ResourceRequest(main_frame_local->GetDocument()->CompleteURL("#a"))));
Persistent<HistoryItem> item2 =
main_frame_local->Loader().GetDocumentLoader()->GetHistoryItem();
// Go back, then forward, then back again.
main_frame_local->Loader().Load(
FrameLoadRequest(nullptr, item1->GenerateResourceRequest(
mojom::FetchCacheMode::kDefault)),
kFrameLoadTypeBackForward, item1.Get(), kHistorySameDocumentLoad);
main_frame_local->Loader().Load(
FrameLoadRequest(nullptr, item2->GenerateResourceRequest(
mojom::FetchCacheMode::kDefault)),
kFrameLoadTypeBackForward, item2.Get(), kHistorySameDocumentLoad);
main_frame_local->Loader().Load(
FrameLoadRequest(nullptr, item1->GenerateResourceRequest(
mojom::FetchCacheMode::kDefault)),
kFrameLoadTypeBackForward, item1.Get(), kHistorySameDocumentLoad);
// Click a different anchor
main_frame_local->Loader().Load(FrameLoadRequest(
main_frame_local->GetDocument(),
ResourceRequest(main_frame_local->GetDocument()->CompleteURL("#b"))));
Persistent<HistoryItem> item3 =
main_frame_local->Loader().GetDocumentLoader()->GetHistoryItem();
// Go back, then forward. The scroll position should be properly set on the
// forward navigation.
main_frame_local->Loader().Load(
FrameLoadRequest(nullptr, item1->GenerateResourceRequest(
mojom::FetchCacheMode::kDefault)),
kFrameLoadTypeBackForward, item1.Get(), kHistorySameDocumentLoad);
main_frame_local->Loader().Load(
FrameLoadRequest(nullptr, item3->GenerateResourceRequest(
mojom::FetchCacheMode::kDefault)),
kFrameLoadTypeBackForward, item3.Get(), kHistorySameDocumentLoad);
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_GT(web_view_impl->MainFrameImpl()->GetScrollOffset().height, 2000);
}
// Tests that we restore scroll and scale *after* the fullscreen styles are
// removed and the page is laid out. http://crbug.com/625683.
TEST_P(WebViewTest, FullscreenResetScrollAndScaleFullscreenStyles) {
RegisterMockedHttpURLLoad("fullscreen_style.html");
WebViewImpl* web_view_impl =
web_view_helper_.InitializeAndLoad(base_url_ + "fullscreen_style.html");
web_view_impl->Resize(WebSize(800, 600));
web_view_impl->UpdateAllLifecyclePhases();
// Scroll the page down.
web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(0, 2000));
ASSERT_EQ(2000, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
// Enter fullscreen.
LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
Element* element = frame->GetDocument()->getElementById("fullscreenElement");
std::unique_ptr<UserGestureIndicator> gesture =
Frame::NotifyUserActivation(frame);
Fullscreen::RequestFullscreen(*element);
web_view_impl->DidEnterFullscreen();
web_view_impl->UpdateAllLifecyclePhases();
// Sanity-check. There should be no scrolling possible.
ASSERT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
ASSERT_EQ(0, web_view_impl->MainFrameImpl()
->GetFrameView()
->MaximumScrollOffset()
.Height());
// Confirm that after exiting and doing a layout, the scroll and scale
// parameters are reset. The page sets display: none on overflowing elements
// while in fullscreen so if we try to restore before the style and layout
// is applied the offsets will be clamped.
web_view_impl->DidExitFullscreen();
EXPECT_TRUE(web_view_impl->MainFrameImpl()->GetFrameView()->NeedsLayout());
web_view_impl->UpdateAllLifecyclePhases();
EXPECT_EQ(2000, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
}
// Tests that exiting and immediately reentering fullscreen doesn't cause the
// scroll and scale restoration to occur when we enter fullscreen again.
TEST_P(WebViewTest, FullscreenResetScrollAndScaleExitAndReenter) {
RegisterMockedHttpURLLoad("fullscreen_style.html");
WebViewImpl* web_view_impl =
web_view_helper_.InitializeAndLoad(base_url_ + "fullscreen_style.html");
web_view_impl->Resize(WebSize(800, 600));
web_view_impl->UpdateAllLifecyclePhases();
// Scroll the page down.
web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(0, 2000));
ASSERT_EQ(2000, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
// Enter fullscreen.
LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
Element* element = frame->GetDocument()->getElementById("fullscreenElement");
std::unique_ptr<UserGestureIndicator> gesture =
Frame::NotifyUserActivation(frame);
Fullscreen::RequestFullscreen(*element);
web_view_impl->DidEnterFullscreen();
web_view_impl->UpdateAllLifecyclePhases();
// Sanity-check. There should be no scrolling possible.
ASSERT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
ASSERT_EQ(0, web_view_impl->MainFrameImpl()
->GetFrameView()
->MaximumScrollOffset()
.Height());
// Exit and, without performing a layout, reenter fullscreen again. We
// shouldn't try to restore the scroll and scale values when we layout to
// enter fullscreen.
web_view_impl->DidExitFullscreen();
Fullscreen::RequestFullscreen(*element);
web_view_impl->DidEnterFullscreen();
web_view_impl->UpdateAllLifecyclePhases();
// Sanity-check. There should be no scrolling possible.
ASSERT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
ASSERT_EQ(0, web_view_impl->MainFrameImpl()
->GetFrameView()
->MaximumScrollOffset()
.Height());
// When we exit now, we should restore the original scroll value.
web_view_impl->DidExitFullscreen();
web_view_impl->UpdateAllLifecyclePhases();
EXPECT_EQ(2000, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
}
TEST_P(WebViewTest, EnterFullscreenResetScrollAndScaleState) {
RegisterMockedHttpURLLoad("200-by-300.html");
WebViewImpl* web_view_impl =
web_view_helper_.InitializeAndLoad(base_url_ + "200-by-300.html");
web_view_impl->Resize(WebSize(100, 150));
web_view_impl->UpdateAllLifecyclePhases();
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(0, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
// Make the page scale and scroll with the given paremeters.
web_view_impl->SetPageScaleFactor(2.0f);
web_view_impl->MainFrameImpl()->SetScrollOffset(WebSize(94, 111));
web_view_impl->SetVisualViewportOffset(WebFloatPoint(12, 20));
EXPECT_EQ(2.0f, web_view_impl->PageScaleFactor());
EXPECT_EQ(94, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(111, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
EXPECT_EQ(12, web_view_impl->VisualViewportOffset().x);
EXPECT_EQ(20, web_view_impl->VisualViewportOffset().y);
LocalFrame* frame = web_view_impl->MainFrameImpl()->GetFrame();
Element* element = frame->GetDocument()->body();
std::unique_ptr<UserGestureIndicator> gesture =
Frame::NotifyUserActivation(frame);
Fullscreen::RequestFullscreen(*element);
web_view_impl->DidEnterFullscreen();
// Page scale factor must be 1.0 during fullscreen for elements to be sized
// properly.
EXPECT_EQ(1.0f, web_view_impl->PageScaleFactor());
// Make sure fullscreen nesting doesn't disrupt scroll/scale saving.
Element* other_element = frame->GetDocument()->getElementById("content");
Fullscreen::RequestFullscreen(*other_element);
// Confirm that exiting fullscreen restores the parameters.
web_view_impl->DidExitFullscreen();
web_view_impl->UpdateAllLifecyclePhases();
EXPECT_EQ(2.0f, web_view_impl->PageScaleFactor());
EXPECT_EQ(94, web_view_impl->MainFrameImpl()->GetScrollOffset().width);
EXPECT_EQ(111, web_view_impl->MainFrameImpl()->GetScrollOffset().height);
EXPECT_EQ(12, web_view_impl->VisualViewportOffset().x);
EXPECT_EQ(20, web_view_impl->VisualViewportOffset().y);
}
class PrintWebViewClient : public FrameTestHelpers::TestWebViewClient {
public:
PrintWebViewClient() : print_called_(false) {}
// WebViewClient methods
void PrintPage(WebLocalFrame*) override { print_called_ = true; }
bool PrintCalled() const { return print_called_; }
private:
bool print_called_;
};
TEST_P(WebViewTest, PrintWithXHRInFlight) {
PrintWebViewClient client;
RegisterMockedHttpURLLoad("print_with_xhr_inflight.html");
WebViewImpl* web_view_impl = web_view_helper_.InitializeAndLoad(
base_url_ + "print_with_xhr_inflight.html", nullptr, &client);
ASSERT_TRUE(ToLocalFrame(web_view_impl->GetPage()->MainFrame())
->GetDocument()
->LoadEventFinished());
EXPECT_TRUE(client.PrintCalled());
web_view_helper_.Reset();
}
static void DragAndDropURL(WebViewImpl* web_view, const std::string& url) {
WebDragData drag_data;
drag_data.Initialize();
WebDragData::Item item;
item.storage_type = WebDragData::Item::kStorageTypeString;
item.string_type = "text/uri-list";
item.string_data = WebString::FromUTF8(url);
drag_data.AddItem(item);
const WebFloatPoint client_point(0, 0);
const WebFloatPoint screen_point(0, 0);
WebFrameWidgetBase* widget = web_view->MainFrameImpl()->FrameWidget();
widget->DragTargetDragEnter(drag_data, client_point, screen_point,
kWebDragOperationCopy, 0);
widget->DragTargetDrop(drag_data, client_point, screen_point, 0);
FrameTestHelpers::PumpPendingRequestsForFrameToLoad(web_view->MainFrame());
}
TEST_P(WebViewTest, DragDropURL) {
RegisterMockedHttpURLLoad("foo.html");
RegisterMockedHttpURLLoad("bar.html");
const std::string foo_url = base_url_ + "foo.html";
const std::string bar_url = base_url_ + "bar.html";
WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(foo_url);
ASSERT_TRUE(web_view);
// Drag and drop barUrl and verify that we've navigated to it.
DragAndDropURL(web_view, bar_url);
EXPECT_EQ(bar_url,
web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
// Drag and drop fooUrl and verify that we've navigated back to it.
DragAndDropURL(web_view, foo_url);
EXPECT_EQ(foo_url,
web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
// Disable navigation on drag-and-drop.
web_view->SettingsImpl()->SetNavigateOnDragDrop(false);
// Attempt to drag and drop to barUrl and verify that no navigation has
// occurred.
DragAndDropURL(web_view, bar_url);
EXPECT_EQ(foo_url,
web_view->MainFrameImpl()->GetDocument().Url().GetString().Utf8());
}
bool WebViewTest::TapElement(WebInputEvent::Type type, Element* element) {
if (!element || !element->GetLayoutObject())
return false;
DCHECK(web_view_helper_.GetWebView());
element->scrollIntoViewIfNeeded();
// TODO(bokan): Technically incorrect, event positions should be in viewport
// space. crbug.com/371902.
IntPoint center =
web_view_helper_.GetWebView()
->MainFrameImpl()
->GetFrameView()
->ContentsToScreen(
element->GetLayoutObject()->AbsoluteBoundingBoxRect())
.Center();
WebGestureEvent event(type, WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
event.source_device = kWebGestureDeviceTouchscreen;
event.x = center.X();
event.y = center.Y();
web_view_helper_.GetWebView()->HandleInputEvent(
WebCoalescedInputEvent(event));
RunPendingTasks();
return true;
}
bool WebViewTest::TapElementById(WebInputEvent::Type type,
const WebString& id) {
DCHECK(web_view_helper_.GetWebView());
Element* element = static_cast<Element*>(
web_view_helper_.LocalMainFrame()->GetDocument().GetElementById(id));
return TapElement(type, element);
}
IntSize WebViewTest::PrintICBSizeFromPageSize(const FloatSize& page_size) {
// The expected layout size comes from the calculation done in
// ResizePageRectsKeepingRatio() which is used from PrintContext::begin() to
// scale the page size.
const float ratio = page_size.Height() / (float)page_size.Width();
const int icb_width =
floor(page_size.Width() * PrintC