blob: ecd8b53df82d2e2e68538feecd0be991b4dc43d2 [file] [log] [blame]
/*
* Copyright (C) 2010 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/WebFrame.h"
#include <stdarg.h>
#include <limits>
#include <map>
#include <memory>
#include <set>
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "bindings/core/v8/V8BindingForCore.h"
#include "bindings/core/v8/V8Node.h"
#include "bindings/core/v8/serialization/SerializedScriptValueFactory.h"
#include "bindings/core/v8/serialization/V8ScriptValueSerializer.h"
#include "build/build_config.h"
#include "core/clipboard/DataTransfer.h"
#include "core/css/StyleSheetContents.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/css/resolver/ViewportStyleResolver.h"
#include "core/dom/Document.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/dom/Range.h"
#include "core/dom/UserGestureIndicator.h"
#include "core/editing/Editor.h"
#include "core/editing/EphemeralRange.h"
#include "core/editing/FrameSelection.h"
#include "core/editing/finder/TextFinder.h"
#include "core/editing/markers/DocumentMarkerController.h"
#include "core/editing/spellcheck/IdleSpellCheckCallback.h"
#include "core/editing/spellcheck/SpellChecker.h"
#include "core/events/MouseEvent.h"
#include "core/exported/WebRemoteFrameImpl.h"
#include "core/exported/WebViewImpl.h"
#include "core/frame/BrowserControls.h"
#include "core/frame/FrameTestHelpers.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/RemoteFrame.h"
#include "core/frame/Settings.h"
#include "core/frame/VisualViewport.h"
#include "core/frame/WebLocalFrameImpl.h"
#include "core/fullscreen/Fullscreen.h"
#include "core/html/HTMLBodyElement.h"
#include "core/html/HTMLIFrameElement.h"
#include "core/html/ImageDocument.h"
#include "core/html/forms/HTMLFormElement.h"
#include "core/html/media/HTMLVideoElement.h"
#include "core/input/EventHandler.h"
#include "core/inspector/DevToolsEmulator.h"
#include "core/layout/HitTestResult.h"
#include "core/layout/LayoutFullScreen.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/DocumentThreadableLoader.h"
#include "core/loader/DocumentThreadableLoaderClient.h"
#include "core/loader/FrameLoadRequest.h"
#include "core/loader/ThreadableLoader.h"
#include "core/page/ChromeClient.h"
#include "core/page/Page.h"
#include "core/page/ScopedPagePauser.h"
#include "core/paint/PaintLayer.h"
#include "core/paint/compositing/CompositedLayerMapping.h"
#include "core/paint/compositing/PaintLayerCompositor.h"
#include "core/testing/NullExecutionContext.h"
#include "core/testing/sim/SimDisplayItemList.h"
#include "core/testing/sim/SimRequest.h"
#include "core/testing/sim/SimTest.h"
#include "platform/Cursor.h"
#include "platform/DragImage.h"
#include "platform/KeyboardCodes.h"
#include "platform/bindings/Microtask.h"
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/compositing/PaintArtifactCompositor.h"
#include "platform/loader/fetch/FetchParameters.h"
#include "platform/loader/fetch/MemoryCache.h"
#include "platform/loader/fetch/ResourceError.h"
#include "platform/loader/fetch/ResourceFetcher.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/scroll/Scrollbar.h"
#include "platform/scroll/ScrollbarTestSuite.h"
#include "platform/testing/HistogramTester.h"
#include "platform/testing/PaintTestConfigurations.h"
#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
#include "platform/testing/URLTestHelpers.h"
#include "platform/testing/UnitTestHelpers.h"
#include "platform/weborigin/KURLHash.h"
#include "platform/weborigin/SchemeRegistry.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/Forward.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/dtoa/utils.h"
#include "public/platform/Platform.h"
#include "public/platform/WebCache.h"
#include "public/platform/WebClipboard.h"
#include "public/platform/WebCoalescedInputEvent.h"
#include "public/platform/WebFloatRect.h"
#include "public/platform/WebKeyboardEvent.h"
#include "public/platform/WebMockClipboard.h"
#include "public/platform/WebSecurityOrigin.h"
#include "public/platform/WebThread.h"
#include "public/platform/WebURL.h"
#include "public/platform/WebURLLoaderClient.h"
#include "public/platform/WebURLLoaderMockFactory.h"
#include "public/platform/WebURLResponse.h"
#include "public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
#include "public/web/WebConsoleMessage.h"
#include "public/web/WebContextMenuData.h"
#include "public/web/WebDeviceEmulationParams.h"
#include "public/web/WebDocument.h"
#include "public/web/WebDocumentLoader.h"
#include "public/web/WebFindOptions.h"
#include "public/web/WebFormElement.h"
#include "public/web/WebFrameClient.h"
#include "public/web/WebFrameContentDumper.h"
#include "public/web/WebFrameWidget.h"
#include "public/web/WebHistoryItem.h"
#include "public/web/WebPrintParams.h"
#include "public/web/WebRange.h"
#include "public/web/WebRemoteFrame.h"
#include "public/web/WebScriptExecutionCallback.h"
#include "public/web/WebScriptSource.h"
#include "public/web/WebSearchableFormData.h"
#include "public/web/WebSecurityPolicy.h"
#include "public/web/WebSelection.h"
#include "public/web/WebSettings.h"
#include "public/web/WebTextCheckClient.h"
#include "public/web/WebTextCheckingCompletion.h"
#include "public/web/WebTextCheckingResult.h"
#include "public/web/WebViewClient.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "v8/include/v8.h"
using blink::URLTestHelpers::ToKURL;
using blink::mojom::SelectionMenuBehavior;
using blink::testing::RunPendingTasks;
using ::testing::ElementsAre;
using ::testing::Mock;
using ::testing::_;
namespace blink {
#if defined(THREAD_SANITIZER)
#define DISABLE_ON_TSAN(test_name) DISABLED_##test_name
#else
#define DISABLE_ON_TSAN(test_name) test_name
#endif // defined(THREAD_SANITIZER)
::std::ostream& operator<<(::std::ostream& os, const WebFloatSize& size) {
return os << "WebFloatSize: [" << size.width << ", " << size.height << "]";
}
::std::ostream& operator<<(::std::ostream& os, const WebFloatPoint& point) {
return os << "WebFloatPoint: [" << point.x << ", " << point.y << "]";
}
const int kTouchPointPadding = 32;
class WebFrameTest : public ::testing::Test {
protected:
WebFrameTest()
: base_url_("http://internal.test/"),
not_base_url_("http://external.test/"),
chrome_url_("chrome://") {}
~WebFrameTest() override {
Platform::Current()
->GetURLLoaderMockFactory()
->UnregisterAllURLsAndClearMemoryCache();
}
void RegisterMockedHttpURLLoad(const std::string& file_name) {
RegisterMockedURLLoadFromBase(base_url_, file_name);
}
void RegisterMockedChromeURLLoad(const std::string& file_name) {
RegisterMockedURLLoadFromBase(chrome_url_, file_name);
}
void RegisterMockedURLLoadFromBase(const std::string& base_url,
const std::string& file_name) {
URLTestHelpers::RegisterMockedURLLoadFromBase(
WebString::FromUTF8(base_url), testing::CoreTestDataPath(),
WebString::FromUTF8(file_name));
}
void RegisterMockedHttpURLLoadWithCSP(const std::string& file_name,
const std::string& csp,
bool report_only = false) {
WebURLResponse response;
response.SetMIMEType("text/html");
response.AddHTTPHeaderField(
report_only ? WebString("Content-Security-Policy-Report-Only")
: WebString("Content-Security-Policy"),
WebString::FromUTF8(csp));
std::string full_string = base_url_ + file_name;
URLTestHelpers::RegisterMockedURLLoadWithCustomResponse(
ToKURL(full_string),
testing::CoreTestDataPath(WebString::FromUTF8(file_name)), response);
}
void RegisterMockedHttpURLLoadWithMimeType(const std::string& file_name,
const std::string& mime_type) {
URLTestHelpers::RegisterMockedURLLoadFromBase(
WebString::FromUTF8(base_url_), testing::CoreTestDataPath(),
WebString::FromUTF8(file_name), WebString::FromUTF8(mime_type));
}
static void ConfigureCompositingWebView(WebSettings* settings) {
settings->SetAcceleratedCompositingEnabled(true);
settings->SetPreferCompositingToLCDTextEnabled(true);
}
static void ConfigureAndroid(WebSettings* settings) {
settings->SetViewportMetaEnabled(true);
settings->SetViewportEnabled(true);
settings->SetMainFrameResizesAreOrientationChanges(true);
settings->SetShrinksViewportContentToFit(true);
settings->SetViewportStyle(WebViewportStyle::kMobile);
}
static void ConfigureLoadsImagesAutomatically(WebSettings* settings) {
settings->SetLoadsImagesAutomatically(true);
}
void InitializeTextSelectionWebView(
const std::string& url,
FrameTestHelpers::WebViewHelper* web_view_helper) {
web_view_helper->InitializeAndLoad(url);
web_view_helper->WebView()->GetSettings()->SetDefaultFontSize(12);
web_view_helper->Resize(WebSize(640, 480));
}
std::unique_ptr<DragImage> NodeImageTestSetup(
FrameTestHelpers::WebViewHelper* web_view_helper,
const std::string& testcase) {
RegisterMockedHttpURLLoad("nodeimage.html");
web_view_helper->InitializeAndLoad(base_url_ + "nodeimage.html");
web_view_helper->Resize(WebSize(640, 480));
LocalFrame* frame =
ToLocalFrame(web_view_helper->WebView()->GetPage()->MainFrame());
DCHECK(frame);
Element* element = frame->GetDocument()->getElementById(testcase.c_str());
return DataTransfer::NodeImage(*frame, *element);
}
void RemoveElementById(WebLocalFrameImpl* frame, const AtomicString& id) {
Element* element = frame->GetFrame()->GetDocument()->getElementById(id);
DCHECK(element);
element->remove();
}
// Both sets the inner html and runs the document lifecycle.
void InitializeWithHTML(LocalFrame& frame, const String& html_content) {
frame.GetDocument()->body()->SetInnerHTMLFromString(html_content);
frame.GetDocument()->View()->UpdateAllLifecyclePhases();
}
WebFrame* LastChild(WebFrame* frame) { return frame->last_child_; }
WebFrame* PreviousSibling(WebFrame* frame) {
return frame->previous_sibling_;
}
void SwapAndVerifyFirstChildConsistency(const char* const message,
WebFrame* parent,
WebFrame* new_child);
void SwapAndVerifyMiddleChildConsistency(const char* const message,
WebFrame* parent,
WebFrame* new_child);
void SwapAndVerifyLastChildConsistency(const char* const message,
WebFrame* parent,
WebFrame* new_child);
void SwapAndVerifySubframeConsistency(const char* const message,
WebFrame* parent,
WebFrame* new_child);
int NumMarkersInRange(const Document* document,
const EphemeralRange& range,
DocumentMarker::MarkerTypes marker_types) {
Node* start_container = range.StartPosition().ComputeContainerNode();
unsigned start_offset = static_cast<unsigned>(
range.StartPosition().ComputeOffsetInContainerNode());
Node* end_container = range.EndPosition().ComputeContainerNode();
unsigned end_offset = static_cast<unsigned>(
range.EndPosition().ComputeOffsetInContainerNode());
int node_count = 0;
for (Node& node : range.Nodes()) {
const DocumentMarkerVector& markers_in_node =
document->Markers().MarkersFor(&node, marker_types);
node_count += std::count_if(
markers_in_node.begin(), markers_in_node.end(),
[start_offset, end_offset, &node, &start_container,
&end_container](const DocumentMarker* marker) {
if (node == start_container && marker->EndOffset() <= start_offset)
return false;
if (node == end_container && marker->StartOffset() >= end_offset)
return false;
return true;
});
}
return node_count;
}
std::string base_url_;
std::string not_base_url_;
std::string chrome_url_;
};
typedef bool TestParamRootLayerScrolling;
class ParameterizedWebFrameTest
: public ::testing::WithParamInterface<TestParamRootLayerScrolling>,
private ScopedRootLayerScrollingForTest,
public WebFrameTest {
public:
ParameterizedWebFrameTest() : ScopedRootLayerScrollingForTest(GetParam()) {}
};
INSTANTIATE_TEST_CASE_P(All, ParameterizedWebFrameTest, ::testing::Bool());
TEST_P(ParameterizedWebFrameTest, ContentText) {
RegisterMockedHttpURLLoad("iframes_test.html");
RegisterMockedHttpURLLoad("visible_iframe.html");
RegisterMockedHttpURLLoad("invisible_iframe.html");
RegisterMockedHttpURLLoad("zero_sized_iframe.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "iframes_test.html");
// Now retrieve the frames text and test it only includes visible elements.
std::string content =
WebFrameContentDumper::DumpWebViewAsText(web_view_helper.WebView(), 1024)
.Utf8();
EXPECT_NE(std::string::npos, content.find(" visible paragraph"));
EXPECT_NE(std::string::npos, content.find(" visible iframe"));
EXPECT_EQ(std::string::npos, content.find(" invisible pararaph"));
EXPECT_EQ(std::string::npos, content.find(" invisible iframe"));
EXPECT_EQ(std::string::npos, content.find("iframe with zero size"));
}
TEST_P(ParameterizedWebFrameTest, FrameForEnteredContext) {
RegisterMockedHttpURLLoad("iframes_test.html");
RegisterMockedHttpURLLoad("visible_iframe.html");
RegisterMockedHttpURLLoad("invisible_iframe.html");
RegisterMockedHttpURLLoad("zero_sized_iframe.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "iframes_test.html");
v8::HandleScope scope(v8::Isolate::GetCurrent());
EXPECT_EQ(web_view_helper.WebView()->MainFrame(),
WebLocalFrame::FrameForContext(web_view_helper.WebView()
->MainFrameImpl()
->MainWorldScriptContext()));
EXPECT_EQ(web_view_helper.WebView()->MainFrame()->FirstChild(),
WebLocalFrame::FrameForContext(web_view_helper.WebView()
->MainFrame()
->FirstChild()
->ToWebLocalFrame()
->MainWorldScriptContext()));
}
class ScriptExecutionCallbackHelper : public WebScriptExecutionCallback {
public:
explicit ScriptExecutionCallbackHelper(v8::Local<v8::Context> context)
: did_complete_(false), bool_value_(false), context_(context) {}
~ScriptExecutionCallbackHelper() override {}
bool DidComplete() const { return did_complete_; }
const String& StringValue() const { return string_value_; }
bool BoolValue() { return bool_value_; }
private:
// WebScriptExecutionCallback:
void Completed(const WebVector<v8::Local<v8::Value>>& values) override {
did_complete_ = true;
if (!values.IsEmpty()) {
if (values[0]->IsString()) {
string_value_ =
ToCoreString(values[0]->ToString(context_).ToLocalChecked());
} else if (values[0]->IsBoolean()) {
bool_value_ = values[0].As<v8::Boolean>()->Value();
}
}
}
bool did_complete_;
String string_value_;
bool bool_value_;
v8::Local<v8::Context> context_;
};
TEST_P(ParameterizedWebFrameTest, RequestExecuteScript) {
RegisterMockedHttpURLLoad("foo.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
v8::HandleScope scope(v8::Isolate::GetCurrent());
ScriptExecutionCallbackHelper callback_helper(
web_view_helper.LocalMainFrame()->MainWorldScriptContext());
web_view_helper.WebView()
->MainFrameImpl()
->RequestExecuteScriptAndReturnValue(
WebScriptSource(WebString("'hello';")), false, &callback_helper);
RunPendingTasks();
EXPECT_TRUE(callback_helper.DidComplete());
EXPECT_EQ("hello", callback_helper.StringValue());
}
TEST_P(ParameterizedWebFrameTest, SuspendedRequestExecuteScript) {
RegisterMockedHttpURLLoad("foo.html");
RegisterMockedHttpURLLoad("bar.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
v8::HandleScope scope(v8::Isolate::GetCurrent());
ScriptExecutionCallbackHelper callback_helper(
web_view_helper.LocalMainFrame()->MainWorldScriptContext());
// Suspend scheduled tasks so the script doesn't run.
web_view_helper.WebView()
->MainFrameImpl()
->GetFrame()
->GetDocument()
->SuspendScheduledTasks();
web_view_helper.WebView()
->MainFrameImpl()
->RequestExecuteScriptAndReturnValue(
WebScriptSource(WebString("'hello';")), false, &callback_helper);
RunPendingTasks();
EXPECT_FALSE(callback_helper.DidComplete());
// If the frame navigates, pending scripts should be removed, but the callback
// should always be ran.
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "bar.html");
EXPECT_TRUE(callback_helper.DidComplete());
EXPECT_EQ(String(), callback_helper.StringValue());
}
TEST_P(ParameterizedWebFrameTest, RequestExecuteV8Function) {
RegisterMockedHttpURLLoad("foo.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(V8String(info.GetIsolate(), "hello"));
};
v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Local<v8::Context> context =
web_view_helper.LocalMainFrame()->MainWorldScriptContext();
ScriptExecutionCallbackHelper callback_helper(context);
v8::Local<v8::Function> function =
v8::Function::New(context, callback).ToLocalChecked();
web_view_helper.WebView()
->MainFrame()
->ToWebLocalFrame()
->RequestExecuteV8Function(context, function,
v8::Undefined(context->GetIsolate()), 0,
nullptr, &callback_helper);
RunPendingTasks();
EXPECT_TRUE(callback_helper.DidComplete());
EXPECT_EQ("hello", callback_helper.StringValue());
}
TEST_P(ParameterizedWebFrameTest, RequestExecuteV8FunctionWhileSuspended) {
RegisterMockedHttpURLLoad("foo.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(V8String(info.GetIsolate(), "hello"));
};
v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Local<v8::Context> context =
web_view_helper.LocalMainFrame()->MainWorldScriptContext();
// Suspend scheduled tasks so the script doesn't run.
WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
main_frame->GetFrame()->GetDocument()->SuspendScheduledTasks();
ScriptExecutionCallbackHelper callback_helper(context);
v8::Local<v8::Function> function =
v8::Function::New(context, callback).ToLocalChecked();
main_frame->RequestExecuteV8Function(context, function,
v8::Undefined(context->GetIsolate()), 0,
nullptr, &callback_helper);
RunPendingTasks();
EXPECT_FALSE(callback_helper.DidComplete());
main_frame->GetFrame()->GetDocument()->ResumeScheduledTasks();
RunPendingTasks();
EXPECT_TRUE(callback_helper.DidComplete());
EXPECT_EQ("hello", callback_helper.StringValue());
}
TEST_P(ParameterizedWebFrameTest,
RequestExecuteV8FunctionWhileSuspendedWithUserGesture) {
RegisterMockedHttpURLLoad("foo.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "foo.html");
auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(v8::Boolean::New(
info.GetIsolate(), UserGestureIndicator::ProcessingUserGesture()));
};
// Suspend scheduled tasks so the script doesn't run.
WebLocalFrameImpl* main_frame = web_view_helper.LocalMainFrame();
main_frame->GetFrame()->GetDocument()->SuspendScheduledTasks();
v8::HandleScope scope(v8::Isolate::GetCurrent());
v8::Local<v8::Context> context =
web_view_helper.LocalMainFrame()->MainWorldScriptContext();
std::unique_ptr<UserGestureIndicator> indicator = Frame::NotifyUserActivation(
main_frame->GetFrame(), UserGestureToken::kNewGesture);
ScriptExecutionCallbackHelper callback_helper(context);
v8::Local<v8::Function> function =
v8::Function::New(context, callback).ToLocalChecked();
main_frame->RequestExecuteV8Function(
main_frame->MainWorldScriptContext(), function,
v8::Undefined(context->GetIsolate()), 0, nullptr, &callback_helper);
RunPendingTasks();
EXPECT_FALSE(callback_helper.DidComplete());
main_frame->GetFrame()->GetDocument()->ResumeScheduledTasks();
RunPendingTasks();
EXPECT_TRUE(callback_helper.DidComplete());
EXPECT_EQ(true, callback_helper.BoolValue());
}
TEST_P(ParameterizedWebFrameTest, IframeScriptRemovesSelf) {
RegisterMockedHttpURLLoad("single_iframe.html");
RegisterMockedHttpURLLoad("visible_iframe.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "single_iframe.html");
v8::HandleScope scope(v8::Isolate::GetCurrent());
ScriptExecutionCallbackHelper callback_helper(
web_view_helper.LocalMainFrame()->MainWorldScriptContext());
web_view_helper.WebView()
->MainFrame()
->FirstChild()
->ToWebLocalFrame()
->RequestExecuteScriptAndReturnValue(
WebScriptSource(WebString(
"var iframe = "
"window.top.document.getElementsByTagName('iframe')[0]; "
"window.top.document.body.removeChild(iframe); 'hello';")),
false, &callback_helper);
RunPendingTasks();
EXPECT_TRUE(callback_helper.DidComplete());
EXPECT_EQ(String(), callback_helper.StringValue());
}
TEST_P(ParameterizedWebFrameTest, FormWithNullFrame) {
RegisterMockedHttpURLLoad("form.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "form.html");
WebVector<WebFormElement> forms;
web_view_helper.LocalMainFrame()->GetDocument().Forms(forms);
web_view_helper.Reset();
EXPECT_EQ(forms.size(), 1U);
// This test passes if this doesn't crash.
WebSearchableFormData searchable_data_form(forms[0]);
}
TEST_P(ParameterizedWebFrameTest, ChromePageJavascript) {
RegisterMockedChromeURLLoad("history.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(chrome_url_ + "history.html");
// Try to run JS against the chrome-style URL.
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
"javascript:document.body.appendChild(document."
"createTextNode('Clobbered'))");
// Now retrieve the frame's text and ensure it was modified by running
// javascript.
std::string content =
WebFrameContentDumper::DumpWebViewAsText(web_view_helper.WebView(), 1024)
.Utf8();
EXPECT_NE(std::string::npos, content.find("Clobbered"));
}
TEST_P(ParameterizedWebFrameTest, ChromePageNoJavascript) {
RegisterMockedChromeURLLoad("history.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(chrome_url_ + "history.html");
// Try to run JS against the chrome-style URL after prohibiting it.
WebSecurityPolicy::RegisterURLSchemeAsNotAllowingJavascriptURLs("chrome");
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
"javascript:document.body.appendChild(document."
"createTextNode('Clobbered'))");
// Now retrieve the frame's text and ensure it wasn't modified by running
// javascript.
std::string content =
WebFrameContentDumper::DumpWebViewAsText(web_view_helper.WebView(), 1024)
.Utf8();
EXPECT_EQ(std::string::npos, content.find("Clobbered"));
}
TEST_P(ParameterizedWebFrameTest, LocationSetHostWithMissingPort) {
std::string file_name = "print-location-href.html";
RegisterMockedHttpURLLoad(file_name);
RegisterMockedURLLoadFromBase("http://internal.test:0/", file_name);
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + file_name);
// Setting host to "hostname:" should be treated as "hostname:0".
FrameTestHelpers::LoadFrame(
web_view_helper.WebView()->MainFrameImpl(),
"javascript:location.host = 'internal.test:'; void 0;");
FrameTestHelpers::LoadFrame(
web_view_helper.WebView()->MainFrameImpl(),
"javascript:document.body.textContent = location.href; void 0;");
std::string content =
WebFrameContentDumper::DumpWebViewAsText(web_view_helper.WebView(), 1024)
.Utf8();
EXPECT_EQ("http://internal.test:0/" + file_name, content);
}
TEST_P(ParameterizedWebFrameTest, LocationSetEmptyPort) {
std::string file_name = "print-location-href.html";
RegisterMockedHttpURLLoad(file_name);
RegisterMockedURLLoadFromBase("http://internal.test:0/", file_name);
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + file_name);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
"javascript:location.port = ''; void 0;");
FrameTestHelpers::LoadFrame(
web_view_helper.WebView()->MainFrameImpl(),
"javascript:document.body.textContent = location.href; void 0;");
std::string content =
WebFrameContentDumper::DumpWebViewAsText(web_view_helper.WebView(), 1024)
.Utf8();
EXPECT_EQ("http://internal.test:0/" + file_name, content);
}
class EvaluateOnLoadWebFrameClient
: public FrameTestHelpers::TestWebFrameClient {
public:
EvaluateOnLoadWebFrameClient() : executing_(false), was_executed_(false) {}
~EvaluateOnLoadWebFrameClient() override {}
// FrameTestHelpers::TestWebFrameClient:
void DidClearWindowObject() override {
EXPECT_FALSE(executing_);
was_executed_ = true;
executing_ = true;
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
Frame()->ExecuteScriptAndReturnValue(
WebScriptSource(WebString("window.someProperty = 42;")));
executing_ = false;
}
bool executing_;
bool was_executed_;
};
TEST_P(ParameterizedWebFrameTest, DidClearWindowObjectIsNotRecursive) {
EvaluateOnLoadWebFrameClient web_frame_client;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad("about:blank", &web_frame_client);
EXPECT_TRUE(web_frame_client.was_executed_);
}
class CSSCallbackWebFrameClient : public FrameTestHelpers::TestWebFrameClient {
public:
CSSCallbackWebFrameClient() : update_count_(0) {}
~CSSCallbackWebFrameClient() override {}
// FrameTestHelpers::TestWebFrameClient:
void DidMatchCSS(
const WebVector<WebString>& newly_matching_selectors,
const WebVector<WebString>& stopped_matching_selectors) override;
std::map<WebLocalFrame*, std::set<std::string>> matched_selectors_;
int update_count_;
};
void CSSCallbackWebFrameClient::DidMatchCSS(
const WebVector<WebString>& newly_matching_selectors,
const WebVector<WebString>& stopped_matching_selectors) {
++update_count_;
std::set<std::string>& frame_selectors = matched_selectors_[Frame()];
for (size_t i = 0; i < newly_matching_selectors.size(); ++i) {
std::string selector = newly_matching_selectors[i].Utf8();
EXPECT_EQ(0U, frame_selectors.count(selector)) << selector;
frame_selectors.insert(selector);
}
for (size_t i = 0; i < stopped_matching_selectors.size(); ++i) {
std::string selector = stopped_matching_selectors[i].Utf8();
EXPECT_EQ(1U, frame_selectors.count(selector)) << selector;
frame_selectors.erase(selector);
}
}
class WebFrameCSSCallbackTest : public ::testing::Test {
protected:
WebFrameCSSCallbackTest() {
frame_ = helper_.InitializeAndLoad("about:blank", &client_)
->MainFrame()
->ToWebLocalFrame();
}
~WebFrameCSSCallbackTest() {
EXPECT_EQ(1U, client_.matched_selectors_.size());
}
WebDocument Doc() const { return frame_->GetDocument(); }
int UpdateCount() const { return client_.update_count_; }
const std::set<std::string>& MatchedSelectors() {
return client_.matched_selectors_[frame_];
}
void LoadHTML(const std::string& html) {
FrameTestHelpers::LoadHTMLString(frame_, html, ToKURL("about:blank"));
}
void ExecuteScript(const WebString& code) {
frame_->ExecuteScript(WebScriptSource(code));
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
}
CSSCallbackWebFrameClient client_;
FrameTestHelpers::WebViewHelper helper_;
WebLocalFrame* frame_;
};
TEST_F(WebFrameCSSCallbackTest, AuthorStyleSheet) {
LoadHTML(
"<style>"
// This stylesheet checks that the internal property and value can't be
// set by a stylesheet, only WebDocument::watchCSSSelectors().
"div.initial_on { -internal-callback: none; }"
"div.initial_off { -internal-callback: -internal-presence; }"
"</style>"
"<div class=\"initial_on\"></div>"
"<div class=\"initial_off\"></div>");
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("div.initial_on"));
frame_->GetDocument().WatchCSSSelectors(WebVector<WebString>(selectors));
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("div.initial_on"));
// Check that adding a watched selector calls back for already-present nodes.
selectors.push_back(WebString::FromUTF8("div.initial_off"));
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
EXPECT_EQ(2, UpdateCount());
EXPECT_THAT(MatchedSelectors(),
ElementsAre("div.initial_off", "div.initial_on"));
// Check that we can turn off callbacks for certain selectors.
Doc().WatchCSSSelectors(WebVector<WebString>());
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
EXPECT_EQ(3, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre());
}
TEST_F(WebFrameCSSCallbackTest, SharedComputedStyle) {
// Check that adding an element calls back when it matches an existing rule.
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("span"));
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
ExecuteScript(
"i1 = document.createElement('span');"
"i1.id = 'first_span';"
"document.body.appendChild(i1)");
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
// Adding a second element that shares a ComputedStyle shouldn't call back.
// We use <span>s to avoid default style rules that can set
// ComputedStyle::unique().
ExecuteScript(
"i2 = document.createElement('span');"
"i2.id = 'second_span';"
"i1 = document.getElementById('first_span');"
"i1.parentNode.insertBefore(i2, i1.nextSibling);");
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
// Removing the first element shouldn't call back.
ExecuteScript(
"i1 = document.getElementById('first_span');"
"i1.parentNode.removeChild(i1);");
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
// But removing the second element *should* call back.
ExecuteScript(
"i2 = document.getElementById('second_span');"
"i2.parentNode.removeChild(i2);");
EXPECT_EQ(2, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre());
}
TEST_F(WebFrameCSSCallbackTest, CatchesAttributeChange) {
LoadHTML("<span></span>");
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("span[attr=\"value\"]"));
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
RunPendingTasks();
EXPECT_EQ(0, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre());
ExecuteScript(
"document.querySelector('span').setAttribute('attr', 'value');");
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span[attr=\"value\"]"));
}
TEST_F(WebFrameCSSCallbackTest, DisplayNone) {
LoadHTML("<div style='display:none'><span></span></div>");
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("span"));
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
RunPendingTasks();
EXPECT_EQ(0, UpdateCount()) << "Don't match elements in display:none trees.";
ExecuteScript(
"d = document.querySelector('div');"
"d.style.display = 'block';");
EXPECT_EQ(1, UpdateCount()) << "Match elements when they become displayed.";
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
ExecuteScript(
"d = document.querySelector('div');"
"d.style.display = 'none';");
EXPECT_EQ(2, UpdateCount())
<< "Unmatch elements when they become undisplayed.";
EXPECT_THAT(MatchedSelectors(), ElementsAre());
ExecuteScript(
"s = document.querySelector('span');"
"s.style.display = 'none';");
EXPECT_EQ(2, UpdateCount())
<< "No effect from no-display'ing a span that's already undisplayed.";
ExecuteScript(
"d = document.querySelector('div');"
"d.style.display = 'block';");
EXPECT_EQ(2, UpdateCount())
<< "No effect from displaying a div whose span is display:none.";
ExecuteScript(
"s = document.querySelector('span');"
"s.style.display = 'inline';");
EXPECT_EQ(3, UpdateCount())
<< "Now the span is visible and produces a callback.";
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
ExecuteScript(
"s = document.querySelector('span');"
"s.style.display = 'none';");
EXPECT_EQ(4, UpdateCount())
<< "Undisplaying the span directly should produce another callback.";
EXPECT_THAT(MatchedSelectors(), ElementsAre());
}
TEST_F(WebFrameCSSCallbackTest, Reparenting) {
LoadHTML(
"<div id='d1'><span></span></div>"
"<div id='d2'></div>");
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("span"));
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
ExecuteScript(
"s = document.querySelector('span');"
"d2 = document.getElementById('d2');"
"d2.appendChild(s);");
EXPECT_EQ(1, UpdateCount()) << "Just moving an element that continues to "
"match shouldn't send a spurious callback.";
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"));
}
TEST_F(WebFrameCSSCallbackTest, MultiSelector) {
LoadHTML("<span></span>");
// Check that selector lists match as the whole list, not as each element
// independently.
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("span"));
selectors.push_back(WebString::FromUTF8("span,p"));
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span", "span, p"));
}
TEST_F(WebFrameCSSCallbackTest, InvalidSelector) {
LoadHTML("<p><span></span></p>");
// Build a list with one valid selector and one invalid.
Vector<WebString> selectors;
selectors.push_back(WebString::FromUTF8("span"));
selectors.push_back(WebString::FromUTF8("[")); // Invalid.
selectors.push_back(WebString::FromUTF8("p span")); // Not compound.
Doc().WatchCSSSelectors(WebVector<WebString>(selectors));
frame_->View()->UpdateAllLifecyclePhases();
RunPendingTasks();
EXPECT_EQ(1, UpdateCount());
EXPECT_THAT(MatchedSelectors(), ElementsAre("span"))
<< "An invalid selector shouldn't prevent other selectors from matching.";
}
TEST_P(ParameterizedWebFrameTest, DispatchMessageEventWithOriginCheck) {
RegisterMockedHttpURLLoad("postmessage_test.html");
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "postmessage_test.html");
// Send a message with the correct origin.
WebSecurityOrigin correct_origin(
WebSecurityOrigin::Create(ToKURL(base_url_)));
WebDocument document = web_view_helper.LocalMainFrame()->GetDocument();
WebSerializedScriptValue data(WebSerializedScriptValue::CreateInvalid());
WebDOMMessageEvent message(data, "http://origin.com");
web_view_helper.WebView()
->MainFrameImpl()
->DispatchMessageEventWithOriginCheck(correct_origin, message);
// Send another message with incorrect origin.
WebSecurityOrigin incorrect_origin(
WebSecurityOrigin::Create(ToKURL(chrome_url_)));
web_view_helper.WebView()
->MainFrameImpl()
->DispatchMessageEventWithOriginCheck(incorrect_origin, message);
// Verify that only the first addition is in the body of the page.
std::string content =
WebFrameContentDumper::DumpWebViewAsText(web_view_helper.WebView(), 1024)
.Utf8();
EXPECT_NE(std::string::npos, content.find("Message 1."));
EXPECT_EQ(std::string::npos, content.find("Message 2."));
}
namespace {
scoped_refptr<SerializedScriptValue> SerializeString(
const StringView& message,
ScriptState* script_state) {
// This is inefficient, but avoids duplicating serialization logic for the
// sake of this test.
NonThrowableExceptionState exception_state;
ScriptState::Scope scope(script_state);
V8ScriptValueSerializer serializer(script_state);
return serializer.Serialize(V8String(script_state->GetIsolate(), message),
exception_state);
}
} // namespace
TEST_P(ParameterizedWebFrameTest, PostMessageThenDetach) {
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad("about:blank");
LocalFrame* frame =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame());
NonThrowableExceptionState exception_state;
scoped_refptr<SerializedScriptValue> message =
SerializeString("message", ToScriptStateForMainWorld(frame));
MessagePortArray message_ports;
frame->DomWindow()->postMessage(message, message_ports, "*",
frame->DomWindow(), exception_state);
web_view_helper.Reset();
EXPECT_FALSE(exception_state.HadException());
// Success is not crashing.
RunPendingTasks();
}
namespace {
class FixedLayoutTestWebViewClient
: public FrameTestHelpers::TestWebViewClient {
public:
FixedLayoutTestWebViewClient() {}
~FixedLayoutTestWebViewClient() override {}
// FrameTestHelpers::TestWebViewClient:
WebScreenInfo GetScreenInfo() override { return screen_info_; }
WebScreenInfo screen_info_;
};
class FakeCompositingWebViewClient : public FixedLayoutTestWebViewClient {};
// Helper function to set autosizing multipliers on a document.
bool SetTextAutosizingMultiplier(Document* document, float multiplier) {
bool multiplier_set = false;
for (LayoutItem layout_item = document->GetLayoutViewItem();
!layout_item.IsNull(); layout_item = layout_item.NextInPreOrder()) {
if (layout_item.Style()) {
layout_item.MutableStyleRef().SetTextAutosizingMultiplier(multiplier);
EXPECT_EQ(multiplier, layout_item.Style()->TextAutosizingMultiplier());
multiplier_set = true;
}
}
return multiplier_set;
}
// Helper function to check autosizing multipliers on a document.
bool CheckTextAutosizingMultiplier(Document* document, float multiplier) {
bool multiplier_checked = false;
for (LayoutItem layout_item = document->GetLayoutViewItem();
!layout_item.IsNull(); layout_item = layout_item.NextInPreOrder()) {
if (layout_item.Style() && layout_item.IsText()) {
EXPECT_EQ(multiplier, layout_item.Style()->TextAutosizingMultiplier());
multiplier_checked = true;
}
}
return multiplier_checked;
}
} // namespace
TEST_P(ParameterizedWebFrameTest,
ChangeInFixedLayoutResetsTextAutosizingMultipliers) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
&client, nullptr, ConfigureAndroid);
Document* document =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->GetDocument();
document->GetSettings()->SetTextAutosizingEnabled(true);
EXPECT_TRUE(document->GetSettings()->TextAutosizingEnabled());
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_TRUE(SetTextAutosizingMultiplier(document, 2));
ViewportDescription description = document->GetViewportDescription();
// Choose a width that's not going match the viewport width of the loaded
// document.
description.min_width = Length(100, blink::kFixed);
description.max_width = Length(100, blink::kFixed);
web_view_helper.WebView()->UpdatePageDefinedViewportConstraints(description);
EXPECT_TRUE(CheckTextAutosizingMultiplier(document, 1));
}
TEST_P(ParameterizedWebFrameTest,
WorkingTextAutosizingMultipliers_VirtualViewport) {
const std::string html_file = "fixed_layout.html";
RegisterMockedHttpURLLoad(html_file);
FixedLayoutTestWebViewClient client;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + html_file, nullptr, &client,
nullptr, ConfigureAndroid);
Document* document =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->GetDocument();
document->GetSettings()->SetTextAutosizingEnabled(true);
EXPECT_TRUE(document->GetSettings()->TextAutosizingEnabled());
web_view_helper.Resize(WebSize(490, 800));
// Multiplier: 980 / 490 = 2.0
EXPECT_TRUE(CheckTextAutosizingMultiplier(document, 2.0));
}
TEST_P(ParameterizedWebFrameTest,
VisualViewportSetSizeInvalidatesTextAutosizingMultipliers) {
RegisterMockedHttpURLLoad("iframe_reload.html");
RegisterMockedHttpURLLoad("visible_iframe.html");
FixedLayoutTestWebViewClient client;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "iframe_reload.html", nullptr,
&client, nullptr, ConfigureAndroid);
LocalFrame* main_frame =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame());
Document* document = main_frame->GetDocument();
LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView();
document->GetSettings()->SetTextAutosizingEnabled(true);
EXPECT_TRUE(document->GetSettings()->TextAutosizingEnabled());
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) {
if (!frame->IsLocalFrame())
continue;
EXPECT_TRUE(
SetTextAutosizingMultiplier(ToLocalFrame(frame)->GetDocument(), 2));
for (LayoutItem layout_item =
ToLocalFrame(frame)->GetDocument()->GetLayoutViewItem();
!layout_item.IsNull(); layout_item = layout_item.NextInPreOrder()) {
if (layout_item.IsText())
EXPECT_FALSE(layout_item.NeedsLayout());
}
}
frame_view->GetPage()->GetVisualViewport().SetSize(IntSize(200, 200));
for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) {
if (!frame->IsLocalFrame())
continue;
for (LayoutItem layout_item =
ToLocalFrame(frame)->GetDocument()->GetLayoutViewItem();
!layout_item.IsNull(); layout_item = layout_item.NextInPreOrder()) {
if (layout_item.IsText())
EXPECT_TRUE(layout_item.NeedsLayout());
}
}
}
TEST_P(ParameterizedWebFrameTest, ZeroHeightPositiveWidthNotIgnored) {
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 1280;
int viewport_height = 0;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(viewport_height, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest,
DeviceScaleFactorUsesDefaultWithoutViewportTag) {
RegisterMockedHttpURLLoad("no_viewport_tag.html");
int viewport_width = 640;
int viewport_height = 480;
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 2;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(
2, web_view_helper.WebView()->GetPage()->DeviceScaleFactorDeprecated());
// Device scale factor should be independent of page scale.
web_view_helper.WebView()->SetDefaultPageScaleLimits(1, 2);
web_view_helper.WebView()->SetPageScaleFactor(0.5);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(1, web_view_helper.WebView()->PageScaleFactor());
// Force the layout to happen before leaving the test.
web_view_helper.WebView()->UpdateAllLifecyclePhases();
}
TEST_P(ParameterizedWebFrameTest, FixedLayoutInitializeAtMinimumScale) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
// Make sure we initialize to minimum scale, even if the window size
// only becomes available after the load begins.
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->SetDefaultPageScaleLimits(0.25f, 5);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "fixed_layout.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
int default_fixed_layout_width = 980;
float minimum_page_scale_factor =
viewport_width / (float)default_fixed_layout_width;
EXPECT_EQ(minimum_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
EXPECT_EQ(minimum_page_scale_factor,
web_view_helper.WebView()->MinimumPageScaleFactor());
// Assume the user has pinch zoomed to page scale factor 2.
float user_pinch_page_scale_factor = 2;
web_view_helper.WebView()->SetPageScaleFactor(user_pinch_page_scale_factor);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
// Make sure we don't reset to initial scale if the page continues to load.
web_view_helper.WebView()->DidCommitLoad(false, false);
web_view_helper.WebView()->DidChangeContentsSize();
EXPECT_EQ(user_pinch_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
// Make sure we don't reset to initial scale if the viewport size changes.
web_view_helper.Resize(WebSize(viewport_width, viewport_height + 100));
EXPECT_EQ(user_pinch_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, WideDocumentInitializeAtMinimumScale) {
RegisterMockedHttpURLLoad("wide_document.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
// Make sure we initialize to minimum scale, even if the window size
// only becomes available after the load begins.
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->SetDefaultPageScaleLimits(0.25f, 5);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "wide_document.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
int wide_document_width = 1500;
float minimum_page_scale_factor = viewport_width / (float)wide_document_width;
EXPECT_EQ(minimum_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
EXPECT_EQ(minimum_page_scale_factor,
web_view_helper.WebView()->MinimumPageScaleFactor());
// Assume the user has pinch zoomed to page scale factor 2.
float user_pinch_page_scale_factor = 2;
web_view_helper.WebView()->SetPageScaleFactor(user_pinch_page_scale_factor);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
// Make sure we don't reset to initial scale if the page continues to load.
web_view_helper.WebView()->DidCommitLoad(false, false);
web_view_helper.WebView()->DidChangeContentsSize();
EXPECT_EQ(user_pinch_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
// Make sure we don't reset to initial scale if the viewport size changes.
web_view_helper.Resize(WebSize(viewport_width, viewport_height + 100));
EXPECT_EQ(user_pinch_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, DelayedViewportInitialScale) {
RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-auto-initial-scale.html", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(0.25f, web_view_helper.WebView()->PageScaleFactor());
Document* document =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->GetDocument();
ViewportDescription description = document->GetViewportDescription();
description.zoom = 2;
document->SetViewportDescription(description);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(2, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, setLoadWithOverviewModeToFalse) {
RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-auto-initial-scale.html", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetLoadWithOverviewMode(false);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
// The page must be displayed at 100% zoom.
EXPECT_EQ(1.0f, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest,
SetLoadWithOverviewModeToFalseAndNoWideViewport) {
RegisterMockedHttpURLLoad("large-div.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetLoadWithOverviewMode(false);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(false);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
// The page must be displayed at 100% zoom, despite that it hosts a wide div
// element.
EXPECT_EQ(1.0f, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, NoWideViewportIgnoresPageViewportWidth) {
RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-auto-initial-scale.html", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(false);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
// The page sets viewport width to 3000, but with UseWideViewport == false is
// must be ignored.
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Width());
EXPECT_EQ(viewport_height, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest,
NoWideViewportIgnoresPageViewportWidthButAccountsScale) {
RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(false);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
// The page sets viewport width to 3000, but with UseWideViewport == false it
// must be ignored while the initial scale specified by the page must be
// accounted.
EXPECT_EQ(viewport_width / 2, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Width());
EXPECT_EQ(viewport_height / 2, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest, WideViewportSetsTo980WithoutViewportTag) {
RegisterMockedHttpURLLoad("no_viewport_tag.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(980, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->LayoutViewportScrollableArea()
->ContentsSize()
.Width());
EXPECT_EQ(980.0 / viewport_width * viewport_height,
web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->LayoutViewportScrollableArea()
->ContentsSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest, WideViewportSetsTo980WithXhtmlMp) {
RegisterMockedHttpURLLoad("viewport/viewport-legacy-xhtmlmp.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
FrameTestHelpers::LoadFrame(
web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "viewport/viewport-legacy-xhtmlmp.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Width());
EXPECT_EQ(viewport_height, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest, NoWideViewportAndHeightInMeta) {
RegisterMockedHttpURLLoad("viewport-height-1000.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "viewport-height-1000.html",
nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(false);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Width());
}
TEST_P(ParameterizedWebFrameTest, WideViewportSetsTo980WithAutoWidth) {
RegisterMockedHttpURLLoad("viewport-2x-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-2x-initial-scale.html", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(980, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Width());
EXPECT_EQ(980.0 / viewport_width * viewport_height, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest,
PageViewportInitialScaleOverridesLoadWithOverviewMode) {
RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetLoadWithOverviewMode(false);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
// The page must be displayed at 200% zoom, as specified in its viewport meta
// tag.
EXPECT_EQ(2.0f, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, setInitialPageScaleFactorPermanently) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
float enforced_page_scale_factor = 2.0f;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetLoadWithOverviewMode(false);
web_view_helper.WebView()->SetInitialPageScaleOverride(
enforced_page_scale_factor);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(enforced_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
int viewport_width = 640;
int viewport_height = 480;
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(enforced_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
web_view_helper.WebView()->SetInitialPageScaleOverride(-1);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(1.0, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest,
PermanentInitialPageScaleFactorOverridesLoadWithOverviewMode) {
RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
float enforced_page_scale_factor = 0.5f;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-auto-initial-scale.html", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetLoadWithOverviewMode(false);
web_view_helper.WebView()->SetInitialPageScaleOverride(
enforced_page_scale_factor);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(enforced_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest,
PermanentInitialPageScaleFactorOverridesPageViewportInitialScale) {
RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
float enforced_page_scale_factor = 0.5f;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()->SetInitialPageScaleOverride(
enforced_page_scale_factor);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(enforced_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest,
SmallPermanentInitialPageScaleFactorIsClobbered) {
const char* pages[] = {
// These pages trigger the clobbering condition. There must be a matching
// item in "pageScaleFactors" array.
"viewport-device-0.5x-initial-scale.html",
"viewport-initial-scale-1.html",
// These ones do not.
"viewport-auto-initial-scale.html",
"viewport-target-densitydpi-device-and-fixed-width.html"};
float page_scale_factors[] = {0.5f, 1.0f};
for (size_t i = 0; i < WTF_ARRAY_LENGTH(pages); ++i)
RegisterMockedHttpURLLoad(pages[i]);
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 400;
int viewport_height = 300;
float enforced_page_scale_factor = 0.75f;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(pages); ++i) {
for (int quirk_enabled = 0; quirk_enabled <= 1; ++quirk_enabled) {
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + pages[i], nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()
->GetSettings()
->SetClobberUserAgentInitialScaleQuirk(quirk_enabled);
web_view_helper.WebView()->SetInitialPageScaleOverride(
enforced_page_scale_factor);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
float expected_page_scale_factor =
quirk_enabled && i < WTF_ARRAY_LENGTH(page_scale_factors)
? page_scale_factors[i]
: enforced_page_scale_factor;
EXPECT_EQ(expected_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
}
}
TEST_P(ParameterizedWebFrameTest,
PermanentInitialPageScaleFactorAffectsLayoutWidth) {
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
float enforced_page_scale_factor = 0.5;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad("about:blank", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(false);
web_view_helper.WebView()->GetSettings()->SetLoadWithOverviewMode(false);
web_view_helper.WebView()->SetInitialPageScaleOverride(
enforced_page_scale_factor);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(viewport_width / enforced_page_scale_factor,
web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->ContentsSize()
.Width());
EXPECT_EQ(enforced_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest,
DocumentElementClientHeightWorksWithWrapContentMode) {
RegisterMockedHttpURLLoad("0-by-0.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "0-by-0.html", nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame();
Document* document = frame->GetDocument();
EXPECT_EQ(viewport_height, document->documentElement()->clientHeight());
EXPECT_EQ(viewport_width, document->documentElement()->clientWidth());
}
TEST_P(ParameterizedWebFrameTest,
SetForceZeroLayoutHeightWorksWithWrapContentMode) {
RegisterMockedHttpURLLoad("0-by-0.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "0-by-0.html", nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(true);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
PaintLayerCompositor* compositor = web_view_helper.WebView()->Compositor();
GraphicsLayer* scroll_container = compositor->ContainerLayer();
if (!scroll_container)
scroll_container = compositor->RootGraphicsLayer();
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
EXPECT_EQ(0.0, scroll_container->Size().Width());
EXPECT_EQ(0.0, scroll_container->Size().Height());
web_view_helper.Resize(WebSize(viewport_width, 0));
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
EXPECT_EQ(viewport_width, scroll_container->Size().Width());
EXPECT_EQ(0.0, scroll_container->Size().Height());
// The flag ForceZeroLayoutHeight will cause the following resize of viewport
// height to be ignored by the outer viewport (the container layer of
// LayerCompositor). The height of the visualViewport, however, is not
// affected.
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_FALSE(web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->NeedsLayout());
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
EXPECT_EQ(viewport_width, scroll_container->Size().Width());
EXPECT_EQ(viewport_height, scroll_container->Size().Height());
LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame();
VisualViewport& visual_viewport = frame->GetPage()->GetVisualViewport();
EXPECT_EQ(viewport_height, visual_viewport.ContainerLayer()->Size().Height());
EXPECT_TRUE(
visual_viewport.ContainerLayer()->PlatformLayer()->MasksToBounds());
EXPECT_FALSE(scroll_container->PlatformLayer()->MasksToBounds());
}
TEST_P(ParameterizedWebFrameTest, SetForceZeroLayoutHeight) {
RegisterMockedHttpURLLoad("200-by-300.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_LE(viewport_height, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(true);
EXPECT_TRUE(web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->NeedsLayout());
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
web_view_helper.Resize(WebSize(viewport_width, viewport_height * 2));
EXPECT_FALSE(web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->NeedsLayout());
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
web_view_helper.Resize(WebSize(viewport_width * 2, viewport_height));
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(false);
EXPECT_LE(viewport_height, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest, ToggleViewportMetaOnOff) {
RegisterMockedHttpURLLoad("viewport-device-width.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "viewport-device-width.html",
nullptr, &client);
WebSettings* settings = web_view_helper.WebView()->GetSettings();
settings->SetViewportMetaEnabled(false);
settings->SetViewportEnabled(true);
settings->SetMainFrameResizesAreOrientationChanges(true);
settings->SetShrinksViewportContentToFit(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
Document* document =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->GetDocument();
EXPECT_FALSE(document->GetViewportDescription().IsLegacyViewportType());
settings->SetViewportMetaEnabled(true);
EXPECT_TRUE(document->GetViewportDescription().IsLegacyViewportType());
settings->SetViewportMetaEnabled(false);
EXPECT_FALSE(document->GetViewportDescription().IsLegacyViewportType());
}
TEST_P(ParameterizedWebFrameTest,
SetForceZeroLayoutHeightWorksWithRelayoutsWhenHeightChanged) {
// this unit test is an attempt to target a real world case where an app could
// 1. call resize(width, 0) and setForceZeroLayoutHeight(true)
// 2. load content (hoping that the viewport height would increase
// as more content is added)
// 3. fail to register touch events aimed at the loaded content
// because the layout is only updated if either width or height is changed
RegisterMockedHttpURLLoad("button.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "button.html", nullptr, &client,
nullptr, ConfigureAndroid);
// set view height to zero so that if the height of the view is not
// successfully updated during later resizes touch events will fail
// (as in not hit content included in the view)
web_view_helper.Resize(WebSize(viewport_width, 0));
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
IntPoint hit_point = IntPoint(30, 30); // button size is 100x100
WebLocalFrameImpl* frame = web_view_helper.LocalMainFrame();
Document* document = frame->GetFrame()->GetDocument();
Element* element = document->getElementById("tap_button");
ASSERT_NE(nullptr, element);
EXPECT_EQ(String("oldValue"), element->innerText());
WebGestureEvent gesture_event(WebInputEvent::kGestureTap,
WebInputEvent::kNoModifiers,
WebInputEvent::kTimeStampForTesting);
gesture_event.SetFrameScale(1);
gesture_event.x = gesture_event.global_x = hit_point.X();
gesture_event.y = gesture_event.global_y = hit_point.Y();
gesture_event.source_device = kWebGestureDeviceTouchscreen;
web_view_helper.WebView()
->MainFrameImpl()
->GetFrame()
->GetEventHandler()
.HandleGestureEvent(gesture_event);
// when pressed, the button changes its own text to "updatedValue"
EXPECT_EQ(String("updatedValue"), element->innerText());
}
TEST_P(ParameterizedWebFrameTest, FrameOwnerPropertiesMargin) {
FrameTestHelpers::WebViewHelper helper;
helper.InitializeRemote();
WebFrameOwnerProperties properties;
properties.margin_width = 11;
properties.margin_height = 22;
WebLocalFrameImpl* local_frame = FrameTestHelpers::CreateLocalChild(
*helper.RemoteMainFrame(), "frameName", properties);
RegisterMockedHttpURLLoad("frame_owner_properties.html");
FrameTestHelpers::LoadFrame(local_frame,
base_url_ + "frame_owner_properties.html");
// Check if the LocalFrame has seen the marginwidth and marginheight
// properties.
Document* child_document = local_frame->GetFrame()->GetDocument();
EXPECT_EQ(11, child_document->FirstBodyElement()->GetIntegralAttribute(
HTMLNames::marginwidthAttr));
EXPECT_EQ(22, child_document->FirstBodyElement()->GetIntegralAttribute(
HTMLNames::marginheightAttr));
LocalFrameView* frame_view = local_frame->GetFrameView();
frame_view->Resize(800, 600);
frame_view->SetNeedsLayout();
frame_view->UpdateAllLifecyclePhases();
// Expect scrollbars to be enabled by default.
EXPECT_NE(nullptr,
frame_view->LayoutViewportScrollableArea()->HorizontalScrollbar());
EXPECT_NE(nullptr,
frame_view->LayoutViewportScrollableArea()->VerticalScrollbar());
}
TEST_P(ParameterizedWebFrameTest, FrameOwnerPropertiesScrolling) {
FrameTestHelpers::WebViewHelper helper;
helper.InitializeRemote();
WebFrameOwnerProperties properties;
// Turn off scrolling in the subframe.
properties.scrolling_mode =
WebFrameOwnerProperties::ScrollingMode::kAlwaysOff;
WebLocalFrameImpl* local_frame = FrameTestHelpers::CreateLocalChild(
*helper.RemoteMainFrame(), "frameName", properties);
RegisterMockedHttpURLLoad("frame_owner_properties.html");
FrameTestHelpers::LoadFrame(local_frame,
base_url_ + "frame_owner_properties.html");
Document* child_document = local_frame->GetFrame()->GetDocument();
EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute(
HTMLNames::marginwidthAttr));
EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute(
HTMLNames::marginheightAttr));
LocalFrameView* frame_view =
static_cast<WebLocalFrameImpl*>(local_frame)->GetFrameView();
EXPECT_EQ(nullptr, frame_view->HorizontalScrollbar());
EXPECT_EQ(nullptr, frame_view->VerticalScrollbar());
}
TEST_P(ParameterizedWebFrameTest,
SetForceZeroLayoutHeightWorksAcrossNavigations) {
RegisterMockedHttpURLLoad("200-by-300.html");
RegisterMockedHttpURLLoad("large-div.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "large-div.html");
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest,
SetForceZeroLayoutHeightWithWideViewportQuirk) {
RegisterMockedHttpURLLoad("200-by-300.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.WebView()->GetSettings()->SetForceZeroLayoutHeight(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(0, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest, WideViewportAndWideContentWithInitialScale) {
RegisterMockedHttpURLLoad("wide_document_width_viewport.html");
RegisterMockedHttpURLLoad("white-1x1.png");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 600;
int viewport_height = 800;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad("about:blank", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.WebView()->GetSettings()->SetViewportMetaLayoutSizeQuirk(
true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "wide_document_width_viewport.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
int wide_document_width = 800;
float minimum_page_scale_factor = viewport_width / (float)wide_document_width;
EXPECT_EQ(minimum_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
EXPECT_EQ(minimum_page_scale_factor,
web_view_helper.WebView()->MinimumPageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, WideViewportQuirkClobbersHeight) {
RegisterMockedHttpURLLoad("viewport-height-1000.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 600;
int viewport_height = 800;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad("about:blank", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(false);
web_view_helper.WebView()->GetSettings()->SetViewportMetaLayoutSizeQuirk(
true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "viewport-height-1000.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(800, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
EXPECT_EQ(1, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, LayoutSize320Quirk) {
RegisterMockedHttpURLLoad("viewport/viewport-30.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 600;
int viewport_height = 800;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad("about:blank", nullptr, &client, nullptr,
ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.WebView()->GetSettings()->SetViewportMetaLayoutSizeQuirk(
true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "viewport/viewport-30.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(600, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(800, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
EXPECT_EQ(1, web_view_helper.WebView()->PageScaleFactor());
// The magic number to snap to device-width is 320, so test that 321 is
// respected.
Document* document =
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->GetDocument();
ViewportDescription description = document->GetViewportDescription();
description.min_width = Length(321, blink::kFixed);
description.max_width = Length(321, blink::kFixed);
document->SetViewportDescription(description);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(321, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
description.min_width = Length(320, blink::kFixed);
description.max_width = Length(320, blink::kFixed);
document->SetViewportDescription(description);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(600, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
description = document->GetViewportDescription();
description.max_height = Length(1000, blink::kFixed);
document->SetViewportDescription(description);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(1000, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
description.max_height = Length(320, blink::kFixed);
document->SetViewportDescription(description);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(800, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height());
}
TEST_P(ParameterizedWebFrameTest, ZeroValuesQuirk) {
RegisterMockedHttpURLLoad("viewport-zero-values.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetViewportMetaZeroValuesQuirk(
true);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()->GetSettings()->SetViewportMetaLayoutSizeQuirk(
true);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "viewport-zero-values.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(1.0f, web_view_helper.WebView()->PageScaleFactor());
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(viewport_width, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(1.0f, web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, OverflowHiddenDisablesScrolling) {
RegisterMockedHttpURLLoad("body-overflow-hidden.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "body-overflow-hidden.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
EXPECT_FALSE(view->UserInputScrollable(kVerticalScrollbar));
EXPECT_FALSE(view->UserInputScrollable(kHorizontalScrollbar));
}
TEST_P(ParameterizedWebFrameTest,
OverflowHiddenDisablesScrollingWithSetCanHaveScrollbars) {
RegisterMockedHttpURLLoad("body-overflow-hidden-short.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "body-overflow-hidden-short.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
EXPECT_FALSE(view->UserInputScrollable(kVerticalScrollbar));
EXPECT_FALSE(view->UserInputScrollable(kHorizontalScrollbar));
web_view_helper.LocalMainFrame()->SetCanHaveScrollbars(true);
EXPECT_FALSE(view->UserInputScrollable(kVerticalScrollbar));
EXPECT_FALSE(view->UserInputScrollable(kHorizontalScrollbar));
}
TEST_P(ParameterizedWebFrameTest, IgnoreOverflowHiddenQuirk) {
RegisterMockedHttpURLLoad("body-overflow-hidden.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client);
web_view_helper.WebView()
->GetSettings()
->SetIgnoreMainFrameOverflowHiddenQuirk(true);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "body-overflow-hidden.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
EXPECT_TRUE(view->LayoutViewportScrollableArea()->UserInputScrollable(
kVerticalScrollbar));
}
TEST_P(ParameterizedWebFrameTest, NonZeroValuesNoQuirk) {
RegisterMockedHttpURLLoad("viewport-nonzero-values.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
float expected_page_scale_factor = 0.5f;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetViewportMetaZeroValuesQuirk(
true);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "viewport-nonzero-values.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
EXPECT_EQ(viewport_width / expected_page_scale_factor,
web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(expected_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
web_view_helper.WebView()->GetSettings()->SetUseWideViewport(true);
web_view_helper.WebView()->UpdateAllLifecyclePhases();
EXPECT_EQ(viewport_width / expected_page_scale_factor,
web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width());
EXPECT_EQ(expected_page_scale_factor,
web_view_helper.WebView()->PageScaleFactor());
}
TEST_P(ParameterizedWebFrameTest, setPageScaleFactorDoesNotLayout) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
// Small viewport to ensure there are always scrollbars.
int viewport_width = 64;
int viewport_height = 48;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
int prev_layout_count =
web_view_helper.LocalMainFrame()->GetFrameView()->LayoutCount();
web_view_helper.WebView()->SetPageScaleFactor(3);
EXPECT_FALSE(web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->NeedsLayout());
EXPECT_EQ(prev_layout_count, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->LayoutCount());
}
TEST_P(ParameterizedWebFrameTest,
setPageScaleFactorWithOverlayScrollbarsDoesNotLayout) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
int prev_layout_count =
web_view_helper.LocalMainFrame()->GetFrameView()->LayoutCount();
web_view_helper.WebView()->SetPageScaleFactor(30);
EXPECT_FALSE(web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->NeedsLayout());
EXPECT_EQ(prev_layout_count, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->LayoutCount());
}
TEST_P(ParameterizedWebFrameTest, pageScaleFactorWrittenToHistoryItem) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
web_view_helper.WebView()->SetPageScaleFactor(3);
EXPECT_EQ(3, ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->Loader()
.GetDocumentLoader()
->GetHistoryItem()
->GetViewState()
->page_scale_factor_);
}
TEST_P(ParameterizedWebFrameTest, initialScaleWrittenToHistoryItem) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.Initialize(nullptr, &client, nullptr, ConfigureAndroid);
web_view_helper.WebView()->SetDefaultPageScaleLimits(0.25f, 5);
FrameTestHelpers::LoadFrame(web_view_helper.WebView()->MainFrameImpl(),
base_url_ + "fixed_layout.html");
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
int default_fixed_layout_width = 980;
float minimum_page_scale_factor =
viewport_width / (float)default_fixed_layout_width;
EXPECT_EQ(minimum_page_scale_factor,
ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->Loader()
.GetDocumentLoader()
->GetHistoryItem()
->GetViewState()
->page_scale_factor_);
}
TEST_P(ParameterizedWebFrameTest, pageScaleFactorDoesntShrinkFrameView) {
RegisterMockedHttpURLLoad("large-div.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
// Small viewport to ensure there are always scrollbars.
int viewport_width = 64;
int viewport_height = 48;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView();
int viewport_width_minus_scrollbar = viewport_width;
int viewport_height_minus_scrollbar = viewport_height;
if (view->VerticalScrollbar() &&
!view->VerticalScrollbar()->IsOverlayScrollbar())
viewport_width_minus_scrollbar -= 15;
if (view->HorizontalScrollbar() &&
!view->HorizontalScrollbar()->IsOverlayScrollbar())
viewport_height_minus_scrollbar -= 15;
web_view_helper.WebView()->SetPageScaleFactor(2);
IntSize unscaled_size = view->VisibleContentSize(kIncludeScrollbars);
EXPECT_EQ(viewport_width, unscaled_size.Width());
EXPECT_EQ(viewport_height, unscaled_size.Height());
IntSize unscaled_size_minus_scrollbar =
view->VisibleContentSize(kExcludeScrollbars);
EXPECT_EQ(viewport_width_minus_scrollbar,
unscaled_size_minus_scrollbar.Width());
EXPECT_EQ(viewport_height_minus_scrollbar,
unscaled_size_minus_scrollbar.Height());
IntSize frame_view_size = view->VisibleContentRect().Size();
EXPECT_EQ(viewport_width_minus_scrollbar, frame_view_size.Width());
EXPECT_EQ(viewport_height_minus_scrollbar, frame_view_size.Height());
}
TEST_P(ParameterizedWebFrameTest, pageScaleFactorDoesNotApplyCssTransform) {
RegisterMockedHttpURLLoad("fixed_layout.html");
FixedLayoutTestWebViewClient client;
client.screen_info_.device_scale_factor = 1;
int viewport_width = 640;
int viewport_height = 480;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr,
&client, nullptr, ConfigureAndroid);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
web_view_helper.WebView()->SetPageScaleFactor(2);
EXPECT_EQ(980, ToLocalFrame(web_view_helper.WebView()->GetPage()->MainFrame())
->ContentLayoutItem()
.DocumentRect()
.Width());
EXPECT_EQ(980, web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->LayoutViewportScrollableArea()
->ContentsSize()
.Width());
}
TEST_P(ParameterizedWebFrameTest, targetDensityDpiHigh) {
RegisterMockedHttpURLLoad("viewport-target-densitydpi-high.html");
FixedLayoutTestWebViewClient client;
// high-dpi = 240
float target_dpi = 240.0f;
float device_scale_factors[] = {1.0f, 4.0f / 3.0f, 2.0f};
int viewport_width = 640;
int viewport_height = 480;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(device_scale_factors); ++i) {
float device_scale_factor = device_scale_factors[i];
float device_dpi = device_scale_factor * 160.0f;
client.screen_info_.device_scale_factor = device_scale_factor;
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-target-densitydpi-high.html", nullptr, &client,
nullptr, ConfigureAndroid);
web_view_helper.WebView()->GetSettings()->SetWideViewportQuirkEnabled(true);
web_view_helper.WebView()
->GetSettings()
->SetSupportDeprecatedTargetDensityDPI(true);
web_view_helper.Resize(WebSize(viewport_width, viewport_height));
// We need to account for the fact that logical pixels are unconditionally
// multiplied by deviceScaleFactor to produce physical pixels.
float density_dpi_scale_ratio =
device_scale_factor * target_dpi / device_dpi;
EXPECT_NEAR(viewport_width * density_dpi_scale_ratio,
web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Width(),
1.0f);
EXPECT_NEAR(viewport_height * density_dpi_scale_ratio,
web_view_helper.WebView()
->MainFrameImpl()
->GetFrameView()
->GetLayoutSize()
.Height(),
1.0f);
EXPECT_NEAR(1.0f / density_dpi_scale_ratio,
web_view_helper.WebView()->PageScaleFactor(), 0.01f);
}
}
TEST_P(ParameterizedWebFrameTest, targetDensityDpiDevice) {
RegisterMockedHttpURLLoad("viewport-target-densitydpi-device.html");
float device_scale_factors[] = {1.0f, 4.0f / 3.0f, 2.0f};
FixedLayoutTestWebViewClient client;
int viewport_width = 640;
int viewport_height = 480;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(device_scale_factors); ++i) {
client.screen_info_.device_scale_factor = device_scale_factors[i];
FrameTestHelpers::WebViewHelper web_view_helper;
web_view_helper.InitializeAndLoad(
base_url_ + "viewport-target-densitydpi-device.html", nullptr, &client,
nullptr, ConfigureAndroid);