| /* |
| * 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 "third_party/blink/public/web/web_frame.h" |
| |
| #include <initializer_list> |
| #include <limits> |
| #include <memory> |
| |
| #include "base/callback_helpers.h" |
| #include "base/optional.h" |
| #include "base/stl_util.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/unguessable_token.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "cc/input/overscroll_behavior.h" |
| #include "cc/layers/picture_layer.h" |
| #include "cc/paint/paint_op_buffer.h" |
| #include "cc/trees/layer_tree_host.h" |
| #include "cc/trees/scroll_node.h" |
| #include "mojo/public/cpp/bindings/pending_remote.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "mojo/public/cpp/system/data_pipe_drainer.h" |
| #include "mojo/public/cpp/system/data_pipe_utils.h" |
| #include "skia/public/mojom/skcolor.mojom-blink.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/browser_interface_broker_proxy.h" |
| #include "third_party/blink/public/common/context_menu_data/context_menu_data.h" |
| #include "third_party/blink/public/common/context_menu_data/edit_flags.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/input/web_coalesced_input_event.h" |
| #include "third_party/blink/public/common/input/web_keyboard_event.h" |
| #include "third_party/blink/public/common/loader/referrer_utils.h" |
| #include "third_party/blink/public/common/messaging/transferable_message.h" |
| #include "third_party/blink/public/common/page/launching_process_state.h" |
| #include "third_party/blink/public/common/widget/device_emulation_params.h" |
| #include "third_party/blink/public/mojom/blob/blob.mojom-blink.h" |
| #include "third_party/blink/public/mojom/blob/data_element.mojom-blink.h" |
| #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h" |
| #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" |
| #include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom-blink.h" |
| #include "third_party/blink/public/mojom/frame/viewport_intersection_state.mojom-blink.h" |
| #include "third_party/blink/public/mojom/page_state/page_state.mojom-blink.h" |
| #include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h" |
| #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom-blink.h" |
| #include "third_party/blink/public/platform/web_cache.h" |
| #include "third_party/blink/public/platform/web_security_origin.h" |
| #include "third_party/blink/public/platform/web_url.h" |
| #include "third_party/blink/public/platform/web_url_loader_client.h" |
| #include "third_party/blink/public/platform/web_url_loader_mock_factory.h" |
| #include "third_party/blink/public/platform/web_url_response.h" |
| #include "third_party/blink/public/test/test_web_frame_content_dumper.h" |
| #include "third_party/blink/public/web/web_console_message.h" |
| #include "third_party/blink/public/web/web_document.h" |
| #include "third_party/blink/public/web/web_document_loader.h" |
| #include "third_party/blink/public/web/web_form_element.h" |
| #include "third_party/blink/public/web/web_frame_widget.h" |
| #include "third_party/blink/public/web/web_history_item.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_local_frame_client.h" |
| #include "third_party/blink/public/web/web_navigation_timings.h" |
| #include "third_party/blink/public/web/web_print_page_description.h" |
| #include "third_party/blink/public/web/web_print_params.h" |
| #include "third_party/blink/public/web/web_range.h" |
| #include "third_party/blink/public/web/web_remote_frame.h" |
| #include "third_party/blink/public/web/web_script_execution_callback.h" |
| #include "third_party/blink/public/web/web_script_source.h" |
| #include "third_party/blink/public/web/web_searchable_form_data.h" |
| #include "third_party/blink/public/web/web_security_policy.h" |
| #include "third_party/blink/public/web/web_settings.h" |
| #include "third_party/blink/public/web/web_text_check_client.h" |
| #include "third_party/blink/public/web/web_text_checking_completion.h" |
| #include "third_party/blink/public/web/web_text_checking_result.h" |
| #include "third_party/blink/public/web/web_view_client.h" |
| #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" |
| #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value_factory.h" |
| #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_node.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_pointer_event_init.h" |
| #include "third_party/blink/renderer/core/clipboard/data_transfer.h" |
| #include "third_party/blink/renderer/core/clipboard/system_clipboard.h" |
| #include "third_party/blink/renderer/core/css/css_page_rule.h" |
| #include "third_party/blink/renderer/core/css/media_values.h" |
| #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" |
| #include "third_party/blink/renderer/core/css/resolver/viewport_style_resolver.h" |
| #include "third_party/blink/renderer/core/css/style_sheet_contents.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/events/native_event_listener.h" |
| #include "third_party/blink/renderer/core/dom/node_computed_style.h" |
| #include "third_party/blink/renderer/core/dom/range.h" |
| #include "third_party/blink/renderer/core/editing/editor.h" |
| #include "third_party/blink/renderer/core/editing/ephemeral_range.h" |
| #include "third_party/blink/renderer/core/editing/finder/text_finder.h" |
| #include "third_party/blink/renderer/core/editing/frame_selection.h" |
| #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h" |
| #include "third_party/blink/renderer/core/editing/selection_template.h" |
| #include "third_party/blink/renderer/core/editing/spellcheck/idle_spell_check_controller.h" |
| #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h" |
| #include "third_party/blink/renderer/core/event_type_names.h" |
| #include "third_party/blink/renderer/core/events/message_event.h" |
| #include "third_party/blink/renderer/core/events/mouse_event.h" |
| #include "third_party/blink/renderer/core/exported/web_view_impl.h" |
| #include "third_party/blink/renderer/core/frame/browser_controls.h" |
| #include "third_party/blink/renderer/core/frame/event_handler_registry.h" |
| #include "third_party/blink/renderer/core/frame/find_in_page.h" |
| #include "third_party/blink/renderer/core/frame/frame_test_helpers.h" |
| #include "third_party/blink/renderer/core/frame/local_dom_window.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/remote_frame.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/frame/viewport_data.h" |
| #include "third_party/blink/renderer/core/frame/visual_viewport.h" |
| #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" |
| #include "third_party/blink/renderer/core/frame/web_remote_frame_impl.h" |
| #include "third_party/blink/renderer/core/fullscreen/fullscreen.h" |
| #include "third_party/blink/renderer/core/geometry/dom_rect.h" |
| #include "third_party/blink/renderer/core/html/forms/html_form_element.h" |
| #include "third_party/blink/renderer/core/html/forms/html_input_element.h" |
| #include "third_party/blink/renderer/core/html/html_body_element.h" |
| #include "third_party/blink/renderer/core/html/html_iframe_element.h" |
| #include "third_party/blink/renderer/core/html/image_document.h" |
| #include "third_party/blink/renderer/core/html/media/html_video_element.h" |
| #include "third_party/blink/renderer/core/input/event_handler.h" |
| #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h" |
| #include "third_party/blink/renderer/core/layout/hit_test_result.h" |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| #include "third_party/blink/renderer/core/loader/document_loader.h" |
| #include "third_party/blink/renderer/core/loader/frame_load_request.h" |
| #include "third_party/blink/renderer/core/messaging/blink_transferable_message.h" |
| #include "third_party/blink/renderer/core/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/page/drag_image.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/page/scoped_page_pauser.h" |
| #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h" |
| #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer.h" |
| #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" |
| #include "third_party/blink/renderer/core/scroll/scrollbar.h" |
| #include "third_party/blink/renderer/core/scroll/scrollbar_test_suite.h" |
| #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" |
| #include "third_party/blink/renderer/core/testing/fake_local_frame_host.h" |
| #include "third_party/blink/renderer/core/testing/fake_remote_frame_host.h" |
| #include "third_party/blink/renderer/core/testing/fake_remote_main_frame_host.h" |
| #include "third_party/blink/renderer/core/testing/mock_clipboard_host.h" |
| #include "third_party/blink/renderer/core/testing/mock_policy_container_host.h" |
| #include "third_party/blink/renderer/core/testing/null_execution_context.h" |
| #include "third_party/blink/renderer/core/testing/page_test_base.h" |
| #include "third_party/blink/renderer/core/testing/scoped_fake_plugin_registry.h" |
| #include "third_party/blink/renderer/core/testing/sim/sim_request.h" |
| #include "third_party/blink/renderer/core/testing/sim/sim_test.h" |
| #include "third_party/blink/renderer/platform/bindings/microtask.h" |
| #include "third_party/blink/renderer/platform/blob/testing/fake_blob.h" |
| #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_layer.h" |
| #include "third_party/blink/renderer/platform/keyboard_codes.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/raw_resource.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h" |
| #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/thread.h" |
| #include "third_party/blink/renderer/platform/testing/find_cc_layer.h" |
| #include "third_party/blink/renderer/platform/testing/histogram_tester.h" |
| #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" |
| #include "third_party/blink/renderer/platform/testing/url_test_helpers.h" |
| #include "third_party/blink/renderer/platform/weborigin/kurl_hash.h" |
| #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" |
| #include "third_party/blink/renderer/platform/weborigin/security_origin.h" |
| #include "third_party/blink/renderer/platform/wtf/forward.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_map.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_set.h" |
| #include "ui/base/ime/mojom/text_input_state.mojom-blink.h" |
| #include "ui/base/mojom/ui_base_types.mojom-shared.h" |
| #include "ui/events/keycodes/dom/dom_key.h" |
| #include "ui/gfx/transform.h" |
| #include "v8/include/v8.h" |
| |
| using blink::mojom::SelectionMenuBehavior; |
| using blink::test::RunPendingTasks; |
| using blink::url_test_helpers::ToKURL; |
| using testing::_; |
| using testing::ElementsAre; |
| using testing::Mock; |
| |
| namespace blink { |
| |
| namespace { |
| |
| const cc::ScrollNode* GetScrollNode(const cc::Layer* layer) { |
| return layer->layer_tree_host() |
| ->property_trees() |
| ->scroll_tree.FindNodeFromElementId(layer->element_id()); |
| } |
| |
| std::string GetHTMLStringForReferrerPolicy(const std::string& meta_policy, |
| const std::string& referrer_policy) { |
| std::string meta_tag = |
| meta_policy.empty() |
| ? "" |
| : base::StringPrintf("<meta name='referrer' content='%s'>", |
| meta_policy.c_str()); |
| std::string referrer_policy_attr = |
| referrer_policy.empty() |
| ? "" |
| : base::StringPrintf("referrerpolicy='%s'", referrer_policy.c_str()); |
| return base::StringPrintf( |
| "<!DOCTYPE html>" |
| "%s" |
| "<a id='dl' href='download_test' download='foo' %s>Click me</a>" |
| "<script>" |
| "(function () {" |
| " var evt = document.createEvent('MouseEvent');" |
| " evt.initMouseEvent('click', true, true);" |
| " document.getElementById('dl').dispatchEvent(evt);" |
| "})();" |
| "</script>", |
| meta_tag.c_str(), referrer_policy_attr.c_str()); |
| } |
| } // namespace |
| |
| const int kTouchPointPadding = 32; |
| |
| const cc::OverscrollBehavior kOverscrollBehaviorAuto = |
| cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kAuto); |
| |
| const cc::OverscrollBehavior kOverscrollBehaviorContain = |
| cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kContain); |
| |
| const cc::OverscrollBehavior kOverscrollBehaviorNone = |
| cc::OverscrollBehavior(cc::OverscrollBehavior::Type::kNone); |
| |
| class WebFrameTest : public testing::Test { |
| protected: |
| WebFrameTest() |
| : base_url_("http://internal.test/"), |
| not_base_url_("http://external.test/"), |
| chrome_url_("chrome://") {} |
| |
| ~WebFrameTest() override { |
| url_test_helpers::UnregisterAllURLsAndClearMemoryCache(); |
| } |
| |
| void DisableRendererSchedulerThrottling() { |
| // Make sure that the RendererScheduler is foregrounded to avoid getting |
| // throttled. |
| if (kLaunchingProcessIsBackgrounded) { |
| ThreadScheduler::Current() |
| ->GetWebMainThreadSchedulerForTest() |
| ->SetRendererBackgrounded(false); |
| } |
| } |
| |
| void RegisterMockedHttpURLLoad(const std::string& file_name) { |
| // TODO(crbug.com/751425): We should use the mock functionality |
| // via the WebViewHelper instance in each test case. |
| RegisterMockedURLLoadFromBase(base_url_, file_name); |
| } |
| |
| void RegisterMockedChromeURLLoad(const std::string& file_name) { |
| // TODO(crbug.com/751425): We should use the mock functionality |
| // via the WebViewHelper instance in each test case. |
| RegisterMockedURLLoadFromBase(chrome_url_, file_name); |
| } |
| |
| void RegisterMockedURLLoadFromBase(const std::string& base_url, |
| const std::string& file_name) { |
| // TODO(crbug.com/751425): We should use the mock functionality |
| // via the WebViewHelper instance in each test case. |
| url_test_helpers::RegisterMockedURLLoadFromBase( |
| WebString::FromUTF8(base_url), test::CoreTestDataPath(), |
| WebString::FromUTF8(file_name)); |
| } |
| |
| void RegisterMockedURLLoadWithCustomResponse(const WebURL& full_url, |
| const WebString& file_path, |
| WebURLResponse response) { |
| url_test_helpers::RegisterMockedURLLoadWithCustomResponse( |
| full_url, file_path, response); |
| } |
| |
| void RegisterMockedHttpURLLoadWithCSP(const std::string& file_name, |
| const std::string& csp, |
| bool report_only = false) { |
| std::string full_string = base_url_ + file_name; |
| KURL url = ToKURL(full_string); |
| WebURLResponse response = WebURLResponse(url); |
| response.SetMimeType("text/html"); |
| response.AddHttpHeaderField( |
| report_only ? WebString("Content-Security-Policy-Report-Only") |
| : WebString("Content-Security-Policy"), |
| WebString::FromUTF8(csp)); |
| RegisterMockedURLLoadWithCustomResponse( |
| url, test::CoreTestDataPath(WebString::FromUTF8(file_name)), response); |
| } |
| |
| void RegisterMockedHttpURLLoadWithMimeType(const std::string& file_name, |
| const std::string& mime_type) { |
| // TODO(crbug.com/751425): We should use the mock functionality |
| // via the WebViewHelper instance in each test case. |
| url_test_helpers::RegisterMockedURLLoadFromBase( |
| WebString::FromUTF8(base_url_), test::CoreTestDataPath(), |
| WebString::FromUTF8(file_name), WebString::FromUTF8(mime_type)); |
| } |
| |
| static void ConfigureCompositingWebView(WebSettings* settings) { |
| settings->SetPreferCompositingToLCDTextEnabled(true); |
| } |
| |
| static void ConfigureAndroid(WebSettings* settings) { |
| settings->SetViewportMetaEnabled(true); |
| settings->SetViewportEnabled(true); |
| settings->SetMainFrameResizesAreOrientationChanges(true); |
| settings->SetShrinksViewportContentToFit(true); |
| settings->SetViewportStyle(mojom::blink::ViewportStyle::kMobile); |
| } |
| |
| static void ConfigureLoadsImagesAutomatically(WebSettings* settings) { |
| settings->SetLoadsImagesAutomatically(true); |
| } |
| |
| void InitializeTextSelectionWebView( |
| const std::string& url, |
| frame_test_helpers::WebViewHelper* web_view_helper) { |
| web_view_helper->InitializeAndLoad(url); |
| web_view_helper->GetWebView()->GetSettings()->SetDefaultFontSize(12); |
| web_view_helper->GetWebView()->MainFrameWidget()->SetFocus(true); |
| web_view_helper->Resize(gfx::Size(640, 480)); |
| } |
| |
| std::unique_ptr<DragImage> NodeImageTestSetup( |
| frame_test_helpers::WebViewHelper* web_view_helper, |
| const std::string& testcase) { |
| RegisterMockedHttpURLLoad("nodeimage.html"); |
| web_view_helper->InitializeAndLoad(base_url_ + "nodeimage.html"); |
| web_view_helper->Resize(gfx::Size(640, 480)); |
| auto* frame = |
| To<LocalFrame>(web_view_helper->GetWebView()->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()->setInnerHTML(html_content); |
| frame.GetDocument()->View()->UpdateAllLifecyclePhasesForTest(); |
| } |
| |
| 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(To<Text>(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; |
| } |
| |
| void UpdateAllLifecyclePhases(WebViewImpl* web_view) { |
| web_view->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| } |
| |
| static void GetElementAndCaretBoundsForFocusedEditableElement( |
| frame_test_helpers::WebViewHelper& helper, |
| IntRect& element_bounds, |
| IntRect& caret_bounds) { |
| Element* element = helper.GetWebView()->FocusedElement(); |
| gfx::Rect caret_in_viewport, unused; |
| helper.GetWebView()->MainFrameViewWidget()->CalculateSelectionBounds( |
| caret_in_viewport, unused); |
| caret_bounds = |
| helper.GetWebView()->GetPage()->GetVisualViewport().ViewportToRootFrame( |
| IntRect(caret_in_viewport)); |
| element_bounds = element->GetDocument().View()->ConvertToRootFrame( |
| PixelSnappedIntRect(element->Node::BoundingBox())); |
| } |
| |
| std::string base_url_; |
| std::string not_base_url_; |
| std::string chrome_url_; |
| }; |
| |
| TEST_F(WebFrameTest, ContentText) { |
| RegisterMockedHttpURLLoad("iframes_test.html"); |
| RegisterMockedHttpURLLoad("visible_iframe.html"); |
| RegisterMockedHttpURLLoad("invisible_iframe.html"); |
| RegisterMockedHttpURLLoad("zero_sized_iframe.html"); |
| |
| frame_test_helpers::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 = TestWebFrameContentDumper::DumpWebViewAsText( |
| web_view_helper.GetWebView(), 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_F(WebFrameTest, FrameForEnteredContext) { |
| RegisterMockedHttpURLLoad("iframes_test.html"); |
| RegisterMockedHttpURLLoad("visible_iframe.html"); |
| RegisterMockedHttpURLLoad("invisible_iframe.html"); |
| RegisterMockedHttpURLLoad("zero_sized_iframe.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "iframes_test.html"); |
| |
| v8::HandleScope scope(v8::Isolate::GetCurrent()); |
| EXPECT_EQ(web_view_helper.GetWebView()->MainFrame(), |
| WebLocalFrame::FrameForContext(web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->MainWorldScriptContext())); |
| EXPECT_EQ(web_view_helper.GetWebView()->MainFrame()->FirstChild(), |
| WebLocalFrame::FrameForContext(web_view_helper.GetWebView() |
| ->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 = default; |
| |
| 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.empty()) { |
| 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_F(WebFrameTest, RequestExecuteScript) { |
| RegisterMockedHttpURLLoad("foo.html"); |
| |
| frame_test_helpers::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.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue( |
| WebScriptSource(WebString("'hello';")), false, &callback_helper); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_EQ("hello", callback_helper.StringValue()); |
| } |
| |
| TEST_F(WebFrameTest, SuspendedRequestExecuteScript) { |
| RegisterMockedHttpURLLoad("foo.html"); |
| |
| frame_test_helpers::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.GetWebView()->GetPage()->SetPaused(true); |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue( |
| WebScriptSource(WebString("'hello';")), false, &callback_helper); |
| RunPendingTasks(); |
| EXPECT_FALSE(callback_helper.DidComplete()); |
| |
| web_view_helper.Reset(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_EQ(String(), callback_helper.StringValue()); |
| } |
| |
| TEST_F(WebFrameTest, RequestExecuteV8Function) { |
| RegisterMockedHttpURLLoad("foo.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "foo.html"); |
| |
| auto callback = [](const v8::FunctionCallbackInfo<v8::Value>& info) { |
| EXPECT_EQ(2, info.Length()); |
| EXPECT_TRUE(info[0]->IsUndefined()); |
| info.GetReturnValue().Set(info[1]); |
| }; |
| |
| v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
| v8::HandleScope scope(isolate); |
| 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(); |
| v8::Local<v8::Value> args[] = {v8::Undefined(isolate), |
| V8String(isolate, "hello")}; |
| web_view_helper.GetWebView() |
| ->MainFrame() |
| ->ToWebLocalFrame() |
| ->RequestExecuteV8Function(context, function, v8::Undefined(isolate), |
| base::size(args), args, &callback_helper); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_EQ("hello", callback_helper.StringValue()); |
| } |
| |
| TEST_F(WebFrameTest, RequestExecuteV8FunctionWhileSuspended) { |
| DisableRendererSchedulerThrottling(); |
| RegisterMockedHttpURLLoad("foo.html"); |
| |
| frame_test_helpers::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(); |
| web_view_helper.GetWebView()->GetPage()->SetPaused(true); |
| |
| 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()); |
| |
| web_view_helper.GetWebView()->GetPage()->SetPaused(false); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_EQ("hello", callback_helper.StringValue()); |
| } |
| |
| TEST_F(WebFrameTest, RequestExecuteV8FunctionWhileSuspendedWithUserGesture) { |
| DisableRendererSchedulerThrottling(); |
| RegisterMockedHttpURLLoad("foo.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "foo.html"); |
| |
| v8::HandleScope scope(v8::Isolate::GetCurrent()); |
| |
| // Suspend scheduled tasks so the script doesn't run. |
| web_view_helper.GetWebView()->GetPage()->SetPaused(true); |
| LocalFrame::NotifyUserActivation( |
| web_view_helper.LocalMainFrame()->GetFrame(), |
| mojom::UserActivationNotificationType::kTest); |
| ScriptExecutionCallbackHelper callback_helper( |
| web_view_helper.LocalMainFrame()->MainWorldScriptContext()); |
| WebScriptSource script_source("navigator.userActivation.isActive;"); |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue(script_source, false, |
| &callback_helper); |
| RunPendingTasks(); |
| EXPECT_FALSE(callback_helper.DidComplete()); |
| |
| web_view_helper.GetWebView()->GetPage()->SetPaused(false); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_EQ(true, callback_helper.BoolValue()); |
| } |
| |
| TEST_F(WebFrameTest, IframeScriptRemovesSelf) { |
| RegisterMockedHttpURLLoad("single_iframe.html"); |
| RegisterMockedHttpURLLoad("visible_iframe.html"); |
| |
| frame_test_helpers::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.GetWebView() |
| ->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()); |
| } |
| |
| namespace { |
| |
| class CapabilityDelegationMessageListener final : public NativeEventListener { |
| public: |
| void Invoke(ExecutionContext*, Event* event) override { |
| delegate_payment_request_ = |
| static_cast<MessageEvent*>(event)->delegatePaymentRequest(); |
| } |
| |
| bool DelegatePaymentRequest() { |
| bool value = delegate_payment_request_.value(); |
| delegate_payment_request_.reset(); |
| return value; |
| } |
| |
| private: |
| base::Optional<bool> delegate_payment_request_; |
| }; |
| |
| } // namespace |
| |
| TEST_F(WebFrameTest, CapabilityDelegationMessageEventTest) { |
| RuntimeEnabledFeatures::SetCapabilityDelegationPaymentRequestEnabled(true); |
| |
| RegisterMockedHttpURLLoad("single_iframe.html"); |
| RegisterMockedHttpURLLoad("visible_iframe.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "single_iframe.html"); |
| |
| auto* main_frame = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()); |
| auto* child_frame = To<LocalFrame>(main_frame->FirstChild()); |
| DCHECK(main_frame); |
| DCHECK(child_frame); |
| |
| auto* message_event_listener = |
| MakeGarbageCollected<CapabilityDelegationMessageListener>(); |
| child_frame->GetDocument()->domWindow()->addEventListener( |
| event_type_names::kMessage, message_event_listener); |
| |
| v8::HandleScope scope(v8::Isolate::GetCurrent()); |
| ScriptExecutionCallbackHelper callback_helper( |
| web_view_helper.LocalMainFrame()->MainWorldScriptContext()); |
| |
| WebScriptSource post_message_wo_payment_request( |
| WebString("window.frames[0].postMessage('0', {targetOrigin: '*'});")); |
| WebScriptSource post_message_w_payment_request( |
| WebString("window.frames[0].postMessage(" |
| "'1', {targetOrigin: '*', createToken: 'paymentrequest'});")); |
| |
| // The delegation info is not passed through a postMessage that is sent |
| // without either user activation or the delegation option. |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue(post_message_wo_payment_request, |
| false, &callback_helper); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_FALSE(message_event_listener->DelegatePaymentRequest()); |
| |
| // The delegation info is not passed through a postMessage that is sent |
| // without user activation but with the delegation option. |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue(post_message_w_payment_request, |
| false, &callback_helper); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_FALSE(message_event_listener->DelegatePaymentRequest()); |
| |
| // The delegation info is not passed through a postMessage that is sent with |
| // user activation but without the delegation option. |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue(post_message_wo_payment_request, |
| true, &callback_helper); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_FALSE(message_event_listener->DelegatePaymentRequest()); |
| |
| // The delegation info is passed through a postMessage that is sent with both |
| // user activation and the delegation option. |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->RequestExecuteScriptAndReturnValue(post_message_w_payment_request, true, |
| &callback_helper); |
| RunPendingTasks(); |
| EXPECT_TRUE(callback_helper.DidComplete()); |
| EXPECT_TRUE(message_event_listener->DelegatePaymentRequest()); |
| } |
| |
| TEST_F(WebFrameTest, FormWithNullFrame) { |
| RegisterMockedHttpURLLoad("form.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "form.html"); |
| |
| WebVector<WebFormElement> forms = |
| web_view_helper.LocalMainFrame()->GetDocument().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_F(WebFrameTest, ChromePageJavascript) { |
| RegisterMockedChromeURLLoad("history.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(chrome_url_ + "history.html"); |
| |
| // Try to run JS against the chrome-style URL. |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->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 = TestWebFrameContentDumper::DumpWebViewAsText( |
| web_view_helper.GetWebView(), 1024) |
| .Utf8(); |
| EXPECT_NE(std::string::npos, content.find("Clobbered")); |
| } |
| |
| TEST_F(WebFrameTest, ChromePageNoJavascript) { |
| RegisterMockedChromeURLLoad("history.html"); |
| |
| frame_test_helpers::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"); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->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 = TestWebFrameContentDumper::DumpWebViewAsText( |
| web_view_helper.GetWebView(), 1024) |
| .Utf8(); |
| EXPECT_EQ(std::string::npos, content.find("Clobbered")); |
| } |
| |
| TEST_F(WebFrameTest, LocationSetHostWithMissingPort) { |
| std::string file_name = "print-location-href.html"; |
| RegisterMockedHttpURLLoad(file_name); |
| // TODO(crbug.com/751425): We should use the mock functionality |
| // via the WebViewHelper instance in each test case. |
| RegisterMockedURLLoadFromBase("http://internal.test:0/", file_name); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + file_name); |
| |
| // Setting host to "hostname:" should be treated as "hostname:0". |
| frame_test_helpers::LoadFrame( |
| web_view_helper.GetWebView()->MainFrameImpl(), |
| "javascript:location.host = 'internal.test:'; void 0;"); |
| |
| frame_test_helpers::LoadFrame( |
| web_view_helper.GetWebView()->MainFrameImpl(), |
| "javascript:document.body.textContent = location.href; void 0;"); |
| |
| std::string content = TestWebFrameContentDumper::DumpWebViewAsText( |
| web_view_helper.GetWebView(), 1024) |
| .Utf8(); |
| EXPECT_EQ("http://internal.test/" + file_name, content); |
| } |
| |
| TEST_F(WebFrameTest, LocationSetEmptyPort) { |
| std::string file_name = "print-location-href.html"; |
| RegisterMockedHttpURLLoad(file_name); |
| // TODO(crbug.com/751425): We should use the mock functionality |
| // via the WebViewHelper instance in each test case. |
| RegisterMockedURLLoadFromBase("http://internal.test:0/", file_name); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + file_name); |
| |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| "javascript:location.port = ''; void 0;"); |
| |
| frame_test_helpers::LoadFrame( |
| web_view_helper.GetWebView()->MainFrameImpl(), |
| "javascript:document.body.textContent = location.href; void 0;"); |
| |
| std::string content = TestWebFrameContentDumper::DumpWebViewAsText( |
| web_view_helper.GetWebView(), 1024) |
| .Utf8(); |
| EXPECT_EQ("http://internal.test/" + file_name, content); |
| } |
| |
| class EvaluateOnLoadWebFrameClient |
| : public frame_test_helpers::TestWebFrameClient { |
| public: |
| EvaluateOnLoadWebFrameClient() : executing_(false), was_executed_(false) {} |
| ~EvaluateOnLoadWebFrameClient() override = default; |
| |
| // frame_test_helpers::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_F(WebFrameTest, DidClearWindowObjectIsNotRecursive) { |
| EvaluateOnLoadWebFrameClient web_frame_client; |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad("about:blank", &web_frame_client); |
| EXPECT_TRUE(web_frame_client.was_executed_); |
| } |
| |
| class CSSCallbackWebFrameClient |
| : public frame_test_helpers::TestWebFrameClient { |
| public: |
| CSSCallbackWebFrameClient() : update_count_(0) {} |
| ~CSSCallbackWebFrameClient() override = default; |
| |
| // frame_test_helpers::TestWebFrameClient: |
| void DidMatchCSS( |
| const WebVector<WebString>& newly_matching_selectors, |
| const WebVector<WebString>& stopped_matching_selectors) override; |
| |
| HashSet<String>& MatchedSelectors() { |
| auto it = matched_selectors_.find(Frame()); |
| if (it != matched_selectors_.end()) |
| return it->value; |
| |
| auto add_result = matched_selectors_.insert(Frame(), HashSet<String>()); |
| return add_result.stored_value->value; |
| } |
| |
| HashMap<WebLocalFrame*, HashSet<String>> matched_selectors_; |
| int update_count_; |
| }; |
| |
| void CSSCallbackWebFrameClient::DidMatchCSS( |
| const WebVector<WebString>& newly_matching_selectors, |
| const WebVector<WebString>& stopped_matching_selectors) { |
| ++update_count_; |
| |
| HashSet<String>& frame_selectors = MatchedSelectors(); |
| for (size_t i = 0; i < newly_matching_selectors.size(); ++i) { |
| String selector = newly_matching_selectors[i]; |
| EXPECT_TRUE(frame_selectors.find(selector) == frame_selectors.end()) |
| << selector; |
| frame_selectors.insert(selector); |
| } |
| for (size_t i = 0; i < stopped_matching_selectors.size(); ++i) { |
| String selector = stopped_matching_selectors[i]; |
| EXPECT_TRUE(frame_selectors.find(selector) != frame_selectors.end()) |
| << selector; |
| frame_selectors.erase(selector); |
| EXPECT_TRUE(frame_selectors.find(selector) == frame_selectors.end()) |
| << selector; |
| } |
| } |
| |
| class WebFrameCSSCallbackTest : public testing::Test { |
| protected: |
| WebFrameCSSCallbackTest() { |
| frame_ = helper_.InitializeAndLoad("about:blank", &client_) |
| ->MainFrame() |
| ->ToWebLocalFrame(); |
| } |
| |
| ~WebFrameCSSCallbackTest() override { |
| EXPECT_EQ(1U, client_.matched_selectors_.size()); |
| } |
| |
| WebDocument Doc() const { return frame_->GetDocument(); } |
| |
| int UpdateCount() const { return client_.update_count_; } |
| |
| const HashSet<String>& MatchedSelectors() { |
| auto it = client_.matched_selectors_.find(frame_); |
| if (it != client_.matched_selectors_.end()) |
| return it->value; |
| |
| auto add_result = |
| client_.matched_selectors_.insert(frame_, HashSet<String>()); |
| return add_result.stored_value->value; |
| } |
| |
| void LoadHTML(const std::string& html) { |
| frame_test_helpers::LoadHTMLString(frame_, html, ToKURL("about:blank")); |
| } |
| |
| void ExecuteScript(const WebString& code) { |
| frame_->ExecuteScript(WebScriptSource(code)); |
| frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| RunPendingTasks(); |
| } |
| |
| CSSCallbackWebFrameClient client_; |
| frame_test_helpers::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()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| 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()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| 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()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| 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, DisplayContents) { |
| LoadHTML("<div style='display:contents'><span></span></div>"); |
| |
| Vector<WebString> selectors(1u, WebString::FromUTF8("span")); |
| Doc().WatchCSSSelectors(WebVector<WebString>(selectors)); |
| frame_->View()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| RunPendingTasks(); |
| |
| EXPECT_EQ(1, UpdateCount()) << "Match elements in display:contents trees."; |
| EXPECT_THAT(MatchedSelectors(), ElementsAre("span")); |
| |
| ExecuteScript( |
| "s = document.querySelector('span');" |
| "s.style.display = 'contents';"); |
| EXPECT_EQ(1, UpdateCount()) << "Match elements which are display:contents."; |
| EXPECT_THAT(MatchedSelectors(), ElementsAre("span")); |
| |
| ExecuteScript( |
| "d = document.querySelector('div');" |
| "d.style.display = 'block';"); |
| EXPECT_EQ(1, UpdateCount()) |
| << "Still match display:contents after parent becomes display:block."; |
| EXPECT_THAT(MatchedSelectors(), ElementsAre("span")); |
| |
| ExecuteScript( |
| "d = document.querySelector('div');" |
| "d.style.display = 'none';"); |
| EXPECT_EQ(2, UpdateCount()) |
| << "No longer matched when parent becomes display:none."; |
| 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()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| 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()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| 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()->MainFrameWidget()->UpdateAllLifecyclePhases( |
| DocumentUpdateReason::kTest); |
| RunPendingTasks(); |
| |
| EXPECT_EQ(1, UpdateCount()); |
| EXPECT_THAT(MatchedSelectors(), ElementsAre("span")) |
| << "An invalid selector shouldn't prevent other selectors from matching."; |
| } |
| |
| TEST_F(WebFrameTest, PostMessageEvent) { |
| RegisterMockedHttpURLLoad("postmessage_test.html"); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "postmessage_test.html"); |
| |
| auto* frame = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()); |
| |
| scoped_refptr<SerializedScriptValue> data = SerializedScriptValue::Create(); |
| MessageEvent* message_event = MessageEvent::Create( |
| /*ports=*/nullptr, std::move(data), "http://origin.com"); |
| |
| // Send a message with the correct origin. |
| scoped_refptr<SecurityOrigin> correct_origin = |
| SecurityOrigin::Create(ToKURL(base_url_)); |
| frame->PostMessageEvent( |
| base::nullopt, g_empty_string, correct_origin->ToString(), |
| BlinkTransferableMessage::FromMessageEvent(message_event)); |
| |
| // Send another message with incorrect origin. |
| scoped_refptr<SecurityOrigin> incorrect_origin = |
| SecurityOrigin::Create(ToKURL(chrome_url_)); |
| frame->PostMessageEvent( |
| base::nullopt, g_empty_string, incorrect_origin->ToString(), |
| BlinkTransferableMessage::FromMessageEvent(message_event)); |
| |
| // Verify that only the first addition is in the body of the page. |
| std::string content = TestWebFrameContentDumper::DumpWebViewAsText( |
| web_view_helper.GetWebView(), 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_F(WebFrameTest, PostMessageThenDetach) { |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad("about:blank"); |
| |
| auto* frame = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()); |
| NonThrowableExceptionState exception_state; |
| scoped_refptr<SerializedScriptValue> message = |
| SerializeString("message", ToScriptStateForMainWorld(frame)); |
| MessagePortArray message_ports; |
| frame->DomWindow()->PostMessageForTesting( |
| message, message_ports, "*", frame->DomWindow(), exception_state); |
| web_view_helper.Reset(); |
| EXPECT_FALSE(exception_state.HadException()); |
| |
| // Success is not crashing. |
| RunPendingTasks(); |
| } |
| |
| namespace { |
| |
| // Helper function to set autosizing multipliers on a document. |
| bool SetTextAutosizingMultiplier(Document* document, float multiplier) { |
| bool multiplier_set = false; |
| for (LayoutObject* layout_object = document->GetLayoutView(); layout_object; |
| layout_object = layout_object->NextInPreOrder()) { |
| if (layout_object->Style()) { |
| scoped_refptr<ComputedStyle> modified_style = |
| ComputedStyle::Clone(layout_object->StyleRef()); |
| modified_style->SetTextAutosizingMultiplier(multiplier); |
| EXPECT_EQ(multiplier, modified_style->TextAutosizingMultiplier()); |
| layout_object->SetModifiedStyleOutsideStyleRecalc( |
| std::move(modified_style), LayoutObject::ApplyStyleChanges::kNo); |
| 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 (LayoutObject* layout_object = document->GetLayoutView(); layout_object; |
| layout_object = layout_object->NextInPreOrder()) { |
| if (layout_object->Style() && layout_object->IsText()) { |
| EXPECT_EQ(multiplier, layout_object->Style()->TextAutosizingMultiplier()); |
| multiplier_checked = true; |
| } |
| } |
| return multiplier_checked; |
| } |
| |
| void UpdateScreenInfoAndResizeView( |
| frame_test_helpers::WebViewHelper* web_view_helper, |
| const ScreenInfo& screen_info) { |
| web_view_helper->GetWebView()->MainFrameViewWidget()->UpdateScreenInfo( |
| screen_info); |
| web_view_helper->Resize(screen_info.rect.size()); |
| } |
| |
| void UpdateScreenInfoAndResizeView( |
| frame_test_helpers::WebViewHelper* web_view_helper, |
| int viewport_width, |
| int viewport_height) { |
| ScreenInfo screen_info = |
| web_view_helper->GetMainFrameWidget()->GetOriginalScreenInfo(); |
| screen_info.rect = gfx::Rect(viewport_width, viewport_height); |
| UpdateScreenInfoAndResizeView(web_view_helper, screen_info); |
| } |
| |
| } // namespace |
| |
| TEST_F(WebFrameTest, ChangeInFixedLayoutResetsTextAutosizingMultipliers) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr, |
| nullptr, ConfigureAndroid); |
| |
| Document* document = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->GetDocument(); |
| document->GetSettings()->SetTextAutosizingEnabled(true); |
| EXPECT_TRUE(document->GetSettings()->GetTextAutosizingEnabled()); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_TRUE(SetTextAutosizingMultiplier(document, 2)); |
| |
| ViewportDescription description = |
| document->GetViewportData().GetViewportDescription(); |
| // Choose a width that's not going match the viewport width of the loaded |
| // document. |
| description.min_width = Length::Fixed(100); |
| description.max_width = Length::Fixed(100); |
| web_view_helper.GetWebView()->UpdatePageDefinedViewportConstraints( |
| description); |
| |
| EXPECT_TRUE(CheckTextAutosizingMultiplier(document, 1)); |
| } |
| |
| TEST_F(WebFrameTest, WorkingTextAutosizingMultipliers_VirtualViewport) { |
| const std::string html_file = "fixed_layout.html"; |
| RegisterMockedHttpURLLoad(html_file); |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + html_file, nullptr, nullptr, |
| ConfigureAndroid); |
| |
| Document* document = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->GetDocument(); |
| document->GetSettings()->SetTextAutosizingEnabled(true); |
| EXPECT_TRUE(document->GetSettings()->GetTextAutosizingEnabled()); |
| |
| web_view_helper.Resize(gfx::Size(490, 800)); |
| |
| // Multiplier: 980 / 490 = 2.0 |
| EXPECT_TRUE(CheckTextAutosizingMultiplier(document, 2.0)); |
| } |
| |
| TEST_F(WebFrameTest, |
| VisualViewportSetSizeInvalidatesTextAutosizingMultipliers) { |
| RegisterMockedHttpURLLoad("iframe_reload.html"); |
| RegisterMockedHttpURLLoad("visible_iframe.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "iframe_reload.html", nullptr, |
| nullptr, ConfigureAndroid); |
| |
| auto* main_frame = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()); |
| Document* document = main_frame->GetDocument(); |
| LocalFrameView* frame_view = web_view_helper.LocalMainFrame()->GetFrameView(); |
| document->GetSettings()->SetTextAutosizingEnabled(true); |
| EXPECT_TRUE(document->GetSettings()->GetTextAutosizingEnabled()); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) { |
| auto* local_frame = DynamicTo<LocalFrame>(frame); |
| if (!local_frame) |
| continue; |
| EXPECT_TRUE(SetTextAutosizingMultiplier(local_frame->GetDocument(), 2)); |
| for (LayoutObject* layout_object = |
| local_frame->GetDocument()->GetLayoutView(); |
| layout_object; layout_object = layout_object->NextInPreOrder()) { |
| if (layout_object->IsText()) |
| EXPECT_FALSE(layout_object->NeedsLayout()); |
| } |
| } |
| |
| frame_view->GetPage()->GetVisualViewport().SetSize(IntSize(200, 200)); |
| |
| for (Frame* frame = main_frame; frame; frame = frame->Tree().TraverseNext()) { |
| auto* local_frame = DynamicTo<LocalFrame>(frame); |
| if (!local_frame) |
| continue; |
| for (LayoutObject* layout_object = |
| local_frame->GetDocument()->GetLayoutView(); |
| !layout_object; layout_object = layout_object->NextInPreOrder()) { |
| if (layout_object->IsText()) |
| EXPECT_TRUE(layout_object->NeedsLayout()); |
| } |
| } |
| } |
| |
| TEST_F(WebFrameTest, ZeroHeightPositiveWidthNotIgnored) { |
| int viewport_width = 1280; |
| int viewport_height = 0; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(viewport_width, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width()); |
| EXPECT_EQ(viewport_height, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, DeviceScaleFactorUsesDefaultWithoutViewportTag) { |
| RegisterMockedHttpURLLoad("no_viewport_tag.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(2.f); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ( |
| 2, |
| web_view_helper.GetWebView()->GetPage()->DeviceScaleFactorDeprecated()); |
| |
| // Device scale factor should be independent of page scale. |
| web_view_helper.GetWebView()->SetDefaultPageScaleLimits(1, 2); |
| web_view_helper.GetWebView()->SetPageScaleFactor(0.5); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| EXPECT_EQ(1, web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| // Force the layout to happen before leaving the test. |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| } |
| |
| TEST_F(WebFrameTest, FixedLayoutInitializeAtMinimumScale) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| 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. |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "fixed_layout.html"); |
| web_view_helper.Resize(gfx::Size(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.GetWebView()->PageScaleFactor()); |
| EXPECT_EQ(minimum_page_scale_factor, |
| web_view_helper.GetWebView()->MinimumPageScaleFactor()); |
| |
| // Assume the user has pinch zoomed to page scale factor 2. |
| float user_pinch_page_scale_factor = 2; |
| web_view_helper.GetWebView()->SetPageScaleFactor( |
| user_pinch_page_scale_factor); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| |
| // Make sure we don't reset to initial scale if the page continues to load. |
| web_view_helper.GetWebView()->DidCommitLoad(false, false); |
| web_view_helper.GetWebView()->DidChangeContentsSize(); |
| EXPECT_EQ(user_pinch_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| // Make sure we don't reset to initial scale if the viewport size changes. |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height + 100)); |
| EXPECT_EQ(user_pinch_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, WideDocumentInitializeAtMinimumScale) { |
| RegisterMockedHttpURLLoad("wide_document.html"); |
| |
| 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. |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "wide_document.html"); |
| web_view_helper.Resize(gfx::Size(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.GetWebView()->PageScaleFactor()); |
| EXPECT_EQ(minimum_page_scale_factor, |
| web_view_helper.GetWebView()->MinimumPageScaleFactor()); |
| |
| // Assume the user has pinch zoomed to page scale factor 2. |
| float user_pinch_page_scale_factor = 2; |
| web_view_helper.GetWebView()->SetPageScaleFactor( |
| user_pinch_page_scale_factor); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| |
| // Make sure we don't reset to initial scale if the page continues to load. |
| web_view_helper.GetWebView()->DidCommitLoad(false, false); |
| web_view_helper.GetWebView()->DidChangeContentsSize(); |
| EXPECT_EQ(user_pinch_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| // Make sure we don't reset to initial scale if the viewport size changes. |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height + 100)); |
| EXPECT_EQ(user_pinch_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, DelayedViewportInitialScale) { |
| RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(0.25f, web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| ViewportData& viewport = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->GetDocument() |
| ->GetViewportData(); |
| ViewportDescription description = viewport.GetViewportDescription(); |
| description.zoom = 2; |
| viewport.SetViewportDescription(description); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| EXPECT_EQ(2, web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, setLoadWithOverviewModeToFalse) { |
| RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| // The page must be displayed at 100% zoom. |
| EXPECT_EQ(1.0f, web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, SetLoadWithOverviewModeToFalseAndNoWideViewport) { |
| RegisterMockedHttpURLLoad("large-div.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(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.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidth) { |
| RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(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.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Width()); |
| EXPECT_EQ(viewport_height, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, NoWideViewportIgnoresPageViewportWidthButAccountsScale) { |
| RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(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.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Width()); |
| EXPECT_EQ(viewport_height / 2, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, WideViewportSetsTo980WithoutViewportTag) { |
| RegisterMockedHttpURLLoad("no_viewport_tag.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "no_viewport_tag.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(980, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutViewport() |
| ->ContentsSize() |
| .Width()); |
| EXPECT_EQ(980.0 / viewport_width * viewport_height, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutViewport() |
| ->ContentsSize() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, WideViewportSetsTo980WithXhtmlMp) { |
| RegisterMockedHttpURLLoad("viewport/viewport-legacy-xhtmlmp.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| frame_test_helpers::LoadFrame( |
| web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "viewport/viewport-legacy-xhtmlmp.html"); |
| |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| EXPECT_EQ(viewport_width, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Width()); |
| EXPECT_EQ(viewport_height, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, NoWideViewportAndHeightInMeta) { |
| RegisterMockedHttpURLLoad("viewport-height-1000.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "viewport-height-1000.html", |
| nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(viewport_width, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Width()); |
| } |
| |
| TEST_F(WebFrameTest, WideViewportSetsTo980WithAutoWidth) { |
| RegisterMockedHttpURLLoad("viewport-2x-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-2x-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(980, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Width()); |
| EXPECT_EQ(980.0 / viewport_width * viewport_height, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, PageViewportInitialScaleOverridesLoadWithOverviewMode) { |
| RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false); |
| web_view_helper.Resize(gfx::Size(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.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, setInitialPageScaleFactorPermanently) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| float enforced_page_scale_factor = 2.0f; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false); |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride( |
| enforced_page_scale_factor); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| |
| EXPECT_EQ(enforced_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(enforced_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride(-1); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| EXPECT_EQ(1.0, web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, |
| PermanentInitialPageScaleFactorOverridesLoadWithOverviewMode) { |
| RegisterMockedHttpURLLoad("viewport-auto-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| float enforced_page_scale_factor = 0.5f; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-auto-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false); |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride( |
| enforced_page_scale_factor); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(enforced_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, |
| PermanentInitialPageScaleFactorOverridesPageViewportInitialScale) { |
| RegisterMockedHttpURLLoad("viewport-wide-2x-initial-scale.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| float enforced_page_scale_factor = 0.5f; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-wide-2x-initial-scale.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride( |
| enforced_page_scale_factor); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(enforced_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, 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 < base::size(pages); ++i) |
| RegisterMockedHttpURLLoad(pages[i]); |
| |
| int viewport_width = 400; |
| int viewport_height = 300; |
| float enforced_page_scale_factor = 0.75f; |
| |
| for (size_t i = 0; i < base::size(pages); ++i) { |
| for (int quirk_enabled = 0; quirk_enabled <= 1; ++quirk_enabled) { |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + pages[i], nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetClobberUserAgentInitialScaleQuirk(quirk_enabled); |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride( |
| enforced_page_scale_factor); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| float expected_page_scale_factor = |
| quirk_enabled && i < base::size(page_scale_factors) |
| ? page_scale_factors[i] |
| : enforced_page_scale_factor; |
| EXPECT_EQ(expected_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| } |
| } |
| |
| TEST_F(WebFrameTest, PermanentInitialPageScaleFactorAffectsLayoutWidth) { |
| int viewport_width = 640; |
| int viewport_height = 480; |
| float enforced_page_scale_factor = 0.5; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad("about:blank", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.GetWebView()->GetSettings()->SetLoadWithOverviewMode(false); |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride( |
| enforced_page_scale_factor); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(viewport_width / enforced_page_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->Size() |
| .Width()); |
| EXPECT_EQ(enforced_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, DocumentElementClientHeightWorksWithWrapContentMode) { |
| RegisterMockedHttpURLLoad("0-by-0.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| |
| web_view_helper.InitializeAndLoad(base_url_ + "0-by-0.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true); |
| web_view_helper.Resize(gfx::Size(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_F(WebFrameTest, SetForceZeroLayoutHeightWorksWithWrapContentMode) { |
| RegisterMockedHttpURLLoad("0-by-0.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| |
| web_view_helper.InitializeAndLoad(base_url_ + "0-by-0.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| |
| LocalFrameView* frame_view = |
| web_view_helper.GetWebView()->MainFrameImpl()->GetFrameView(); |
| GraphicsLayer* scroll_container = |
| frame_view->GetLayoutView()->Compositor()->RootGraphicsLayer(); |
| |
| EXPECT_EQ(IntSize(), frame_view->GetLayoutSize()); |
| EXPECT_EQ(gfx::Size(), scroll_container->Size()); |
| |
| web_view_helper.Resize(gfx::Size(viewport_width, 0)); |
| EXPECT_EQ(IntSize(viewport_width, 0), frame_view->GetLayoutSize()); |
| EXPECT_EQ(gfx::Size(viewport_width, 0), scroll_container->Size()); |
| |
| // 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(gfx::Size(viewport_width, viewport_height)); |
| EXPECT_FALSE(frame_view->NeedsLayout()); |
| EXPECT_EQ(IntSize(viewport_width, 0), frame_view->GetLayoutSize()); |
| EXPECT_EQ(gfx::Size(viewport_width, viewport_height), |
| scroll_container->Size()); |
| |
| LocalFrame* frame = web_view_helper.LocalMainFrame()->GetFrame(); |
| VisualViewport& visual_viewport = frame->GetPage()->GetVisualViewport(); |
| auto* scroll_node = visual_viewport.GetScrollTranslationNode()->ScrollNode(); |
| EXPECT_EQ(IntRect(0, 0, viewport_width, viewport_height), |
| scroll_node->ContainerRect()); |
| EXPECT_EQ(IntSize(viewport_width, viewport_height), |
| scroll_node->ContentsSize()); |
| } |
| |
| TEST_F(WebFrameTest, SetForceZeroLayoutHeight) { |
| RegisterMockedHttpURLLoad("200-by-300.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| |
| web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_LE(viewport_height, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true); |
| EXPECT_TRUE(web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->NeedsLayout()); |
| |
| EXPECT_EQ(0, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height * 2)); |
| EXPECT_FALSE(web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->NeedsLayout()); |
| EXPECT_EQ(0, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| |
| web_view_helper.Resize(gfx::Size(viewport_width * 2, viewport_height)); |
| EXPECT_EQ(0, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(false); |
| EXPECT_LE(viewport_height, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, ToggleViewportMetaOnOff) { |
| RegisterMockedHttpURLLoad("viewport-device-width.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "viewport-device-width.html", |
| nullptr, nullptr); |
| WebSettings* settings = web_view_helper.GetWebView()->GetSettings(); |
| settings->SetViewportMetaEnabled(false); |
| settings->SetViewportEnabled(true); |
| settings->SetMainFrameResizesAreOrientationChanges(true); |
| settings->SetShrinksViewportContentToFit(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| ViewportData& viewport = |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->GetDocument() |
| ->GetViewportData(); |
| EXPECT_FALSE(viewport.GetViewportDescription().IsLegacyViewportType()); |
| |
| settings->SetViewportMetaEnabled(true); |
| EXPECT_TRUE(viewport.GetViewportDescription().IsLegacyViewportType()); |
| |
| settings->SetViewportMetaEnabled(false); |
| EXPECT_FALSE(viewport.GetViewportDescription().IsLegacyViewportType()); |
| } |
| |
| TEST_F(WebFrameTest, |
| 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"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| |
| web_view_helper.InitializeAndLoad(base_url_ + "button.html", nullptr, 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(gfx::Size(viewport_width, 0)); |
| |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| FloatPoint hit_point = FloatPoint(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::Type::kGestureTap, |
| WebInputEvent::kNoModifiers, |
| WebInputEvent::GetStaticTimeStampForTests(), |
| WebGestureDevice::kTouchscreen); |
| gesture_event.SetFrameScale(1); |
| gesture_event.SetPositionInWidget(hit_point); |
| gesture_event.SetPositionInScreen(hit_point); |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrame() |
| ->GetEventHandler() |
| .HandleGestureEvent(gesture_event); |
| // when pressed, the button changes its own text to "updatedValue" |
| EXPECT_EQ(String("updatedValue"), element->innerText()); |
| } |
| |
| TEST_F(WebFrameTest, FrameOwnerPropertiesMargin) { |
| frame_test_helpers::WebViewHelper helper; |
| helper.InitializeRemote(); |
| |
| WebFrameOwnerProperties properties; |
| properties.margin_width = 11; |
| properties.margin_height = 22; |
| WebLocalFrameImpl* local_frame = helper.CreateLocalChild( |
| *helper.RemoteMainFrame(), "frameName", properties); |
| |
| RegisterMockedHttpURLLoad("frame_owner_properties.html"); |
| frame_test_helpers::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( |
| html_names::kMarginwidthAttr)); |
| EXPECT_EQ(22, child_document->FirstBodyElement()->GetIntegralAttribute( |
| html_names::kMarginheightAttr)); |
| |
| LocalFrameView* frame_view = local_frame->GetFrameView(); |
| frame_view->Resize(800, 600); |
| frame_view->SetNeedsLayout(); |
| frame_view->UpdateAllLifecyclePhasesForTest(); |
| // Expect scrollbars to be enabled by default. |
| EXPECT_NE(nullptr, frame_view->LayoutViewport()->HorizontalScrollbar()); |
| EXPECT_NE(nullptr, frame_view->LayoutViewport()->VerticalScrollbar()); |
| } |
| |
| TEST_F(WebFrameTest, FrameOwnerPropertiesScrolling) { |
| frame_test_helpers::WebViewHelper helper; |
| helper.InitializeRemote(); |
| |
| WebFrameOwnerProperties properties; |
| // Turn off scrolling in the subframe. |
| properties.scrollbar_mode = mojom::blink::ScrollbarMode::kAlwaysOff; |
| WebLocalFrameImpl* local_frame = helper.CreateLocalChild( |
| *helper.RemoteMainFrame(), "frameName", properties); |
| |
| RegisterMockedHttpURLLoad("frame_owner_properties.html"); |
| frame_test_helpers::LoadFrame(local_frame, |
| base_url_ + "frame_owner_properties.html"); |
| |
| Document* child_document = local_frame->GetFrame()->GetDocument(); |
| EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute( |
| html_names::kMarginwidthAttr)); |
| EXPECT_EQ(0, child_document->FirstBodyElement()->GetIntegralAttribute( |
| html_names::kMarginheightAttr)); |
| |
| LocalFrameView* frame_view = local_frame->GetFrameView(); |
| EXPECT_EQ(nullptr, frame_view->LayoutViewport()->HorizontalScrollbar()); |
| EXPECT_EQ(nullptr, frame_view->LayoutViewport()->VerticalScrollbar()); |
| } |
| |
| TEST_F(WebFrameTest, SetForceZeroLayoutHeightWorksAcrossNavigations) { |
| RegisterMockedHttpURLLoad("200-by-300.html"); |
| RegisterMockedHttpURLLoad("large-div.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| |
| web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "large-div.html"); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| |
| EXPECT_EQ(0, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, SetForceZeroLayoutHeightWithWideViewportQuirk) { |
| RegisterMockedHttpURLLoad("200-by-300.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| |
| web_view_helper.InitializeAndLoad(base_url_ + "200-by-300.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| web_view_helper.GetWebView()->GetSettings()->SetForceZeroLayoutHeight(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(0, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| } |
| |
| TEST_F(WebFrameTest, WideViewportQuirkClobbersHeight) { |
| RegisterMockedHttpURLLoad("viewport-height-1000.html"); |
| |
| int viewport_width = 600; |
| int viewport_height = 800; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad("about:blank", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "viewport-height-1000.html"); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(800, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height()); |
| EXPECT_EQ(1, web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, OverflowHiddenDisablesScrolling) { |
| RegisterMockedHttpURLLoad("body-overflow-hidden.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "body-overflow-hidden.html"); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView(); |
| EXPECT_FALSE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar)); |
| EXPECT_FALSE( |
| view->LayoutViewport()->UserInputScrollable(kHorizontalScrollbar)); |
| } |
| |
| TEST_F(WebFrameTest, OverflowHiddenDisablesScrollingWithSetCanHaveScrollbars) { |
| RegisterMockedHttpURLLoad("body-overflow-hidden-short.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "body-overflow-hidden-short.html"); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView(); |
| EXPECT_FALSE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar)); |
| EXPECT_FALSE( |
| view->LayoutViewport()->UserInputScrollable(kHorizontalScrollbar)); |
| |
| web_view_helper.LocalMainFrame()->GetFrameView()->SetCanHaveScrollbars(true); |
| EXPECT_FALSE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar)); |
| EXPECT_FALSE( |
| view->LayoutViewport()->UserInputScrollable(kHorizontalScrollbar)); |
| } |
| |
| TEST_F(WebFrameTest, IgnoreOverflowHiddenQuirk) { |
| RegisterMockedHttpURLLoad("body-overflow-hidden.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetIgnoreMainFrameOverflowHiddenQuirk(true); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "body-overflow-hidden.html"); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| LocalFrameView* view = web_view_helper.LocalMainFrame()->GetFrameView(); |
| EXPECT_TRUE(view->LayoutViewport()->UserInputScrollable(kVerticalScrollbar)); |
| } |
| |
| TEST_F(WebFrameTest, NonZeroValuesNoQuirk) { |
| RegisterMockedHttpURLLoad("viewport-nonzero-values.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| float expected_page_scale_factor = 0.5f; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->GetSettings()->SetViewportMetaZeroValuesQuirk( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "viewport-nonzero-values.html"); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_EQ(viewport_width / expected_page_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width()); |
| EXPECT_EQ(expected_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| UpdateAllLifecyclePhases(web_view_helper.GetWebView()); |
| EXPECT_EQ(viewport_width / expected_page_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width()); |
| EXPECT_EQ(expected_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor()); |
| } |
| |
| TEST_F(WebFrameTest, setPageScaleFactorDoesNotLayout) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| // Small viewport to ensure there are always scrollbars. |
| int viewport_width = 64; |
| int viewport_height = 48; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| unsigned prev_layout_count = |
| web_view_helper.LocalMainFrame()->GetFrameView()->LayoutCountForTesting(); |
| web_view_helper.GetWebView()->SetPageScaleFactor(3); |
| EXPECT_FALSE(web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->NeedsLayout()); |
| EXPECT_EQ(prev_layout_count, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutCountForTesting()); |
| } |
| |
| TEST_F(WebFrameTest, setPageScaleFactorWithOverlayScrollbarsDoesNotLayout) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| unsigned prev_layout_count = |
| web_view_helper.LocalMainFrame()->GetFrameView()->LayoutCountForTesting(); |
| web_view_helper.GetWebView()->SetPageScaleFactor(30); |
| EXPECT_FALSE(web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->NeedsLayout()); |
| EXPECT_EQ(prev_layout_count, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutCountForTesting()); |
| } |
| |
| TEST_F(WebFrameTest, pageScaleFactorWrittenToHistoryItem) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| web_view_helper.GetWebView()->SetPageScaleFactor(3); |
| EXPECT_EQ(3, |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->Loader() |
| .GetDocumentLoader() |
| ->GetHistoryItem() |
| ->GetViewState() |
| ->page_scale_factor_); |
| } |
| |
| TEST_F(WebFrameTest, initialScaleWrittenToHistoryItem) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.Initialize(nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5); |
| frame_test_helpers::LoadFrame(web_view_helper.GetWebView()->MainFrameImpl(), |
| base_url_ + "fixed_layout.html"); |
| web_view_helper.Resize(gfx::Size(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, |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->Loader() |
| .GetDocumentLoader() |
| ->GetHistoryItem() |
| ->GetViewState() |
| ->page_scale_factor_); |
| } |
| |
| TEST_F(WebFrameTest, pageScaleFactorDoesntShrinkFrameView) { |
| RegisterMockedHttpURLLoad("large-div.html"); |
| |
| // Small viewport to ensure there are always scrollbars. |
| int viewport_width = 64; |
| int viewport_height = 48; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(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->LayoutViewport()->VerticalScrollbar() && |
| !view->LayoutViewport()->VerticalScrollbar()->IsOverlayScrollbar()) |
| viewport_width_minus_scrollbar -= 15; |
| |
| if (view->LayoutViewport()->HorizontalScrollbar() && |
| !view->LayoutViewport()->HorizontalScrollbar()->IsOverlayScrollbar()) |
| viewport_height_minus_scrollbar -= 15; |
| |
| web_view_helper.GetWebView()->SetPageScaleFactor(2); |
| |
| IntSize unscaled_size = view->Size(); |
| EXPECT_EQ(viewport_width, unscaled_size.Width()); |
| EXPECT_EQ(viewport_height, unscaled_size.Height()); |
| |
| IntSize unscaled_size_minus_scrollbar = view->Size(); |
| 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->Size(); |
| EXPECT_EQ(viewport_width_minus_scrollbar, frame_view_size.Width()); |
| EXPECT_EQ(viewport_height_minus_scrollbar, frame_view_size.Height()); |
| } |
| |
| TEST_F(WebFrameTest, pageScaleFactorDoesNotApplyCssTransform) { |
| RegisterMockedHttpURLLoad("fixed_layout.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "fixed_layout.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| web_view_helper.GetWebView()->SetPageScaleFactor(2); |
| |
| EXPECT_EQ(980, |
| To<LocalFrame>(web_view_helper.GetWebView()->GetPage()->MainFrame()) |
| ->ContentLayoutObject() |
| ->DocumentRect() |
| .Width()); |
| EXPECT_EQ(980, web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutViewport() |
| ->ContentsSize() |
| .Width()); |
| } |
| |
| TEST_F(WebFrameTest, targetDensityDpiHigh) { |
| RegisterMockedHttpURLLoad("viewport-target-densitydpi-high.html"); |
| |
| // 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 < base::size(device_scale_factors); ++i) { |
| float device_scale_factor = device_scale_factors[i]; |
| float device_dpi = device_scale_factor * 160.0f; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-target-densitydpi-high.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(device_scale_factor); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetSupportDeprecatedTargetDensityDPI(true); |
| web_view_helper.Resize(gfx::Size(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.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height * density_dpi_scale_ratio, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f / density_dpi_scale_ratio, |
| web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| } |
| |
| TEST_F(WebFrameTest, targetDensityDpiDevice) { |
| RegisterMockedHttpURLLoad("viewport-target-densitydpi-device.html"); |
| |
| 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 < base::size(device_scale_factors); ++i) { |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-target-densitydpi-device.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(device_scale_factors[i]); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetSupportDeprecatedTargetDensityDPI(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width * device_scale_factors[i], |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height * device_scale_factors[i], |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f / device_scale_factors[i], |
| web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| } |
| |
| TEST_F(WebFrameTest, targetDensityDpiDeviceAndFixedWidth) { |
| RegisterMockedHttpURLLoad( |
| "viewport-target-densitydpi-device-and-fixed-width.html"); |
| |
| 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 < base::size(device_scale_factors); ++i) { |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-target-densitydpi-device-and-fixed-width.html", |
| nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(device_scale_factors[i]); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetSupportDeprecatedTargetDensityDPI(true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| } |
| |
| TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOne) { |
| RegisterMockedHttpURLLoad("viewport-initial-scale-less-than-1.html"); |
| |
| float device_scale_factor = 1.33f; |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-initial-scale-less-than-1.html", nullptr, nullptr, |
| ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(device_scale_factor); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetSupportDeprecatedTargetDensityDPI(true); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width * device_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height * device_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f / device_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| |
| TEST_F(WebFrameTest, NoWideViewportAndScaleLessThanOneWithDeviceWidth) { |
| RegisterMockedHttpURLLoad( |
| "viewport-initial-scale-less-than-1-device-width.html"); |
| |
| float device_scale_factor = 1.33f; |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-initial-scale-less-than-1-device-width.html", |
| nullptr, nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(device_scale_factor); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetSupportDeprecatedTargetDensityDPI(true); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| const float kPageZoom = 0.25f; |
| EXPECT_NEAR(viewport_width * device_scale_factor / kPageZoom, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height * device_scale_factor / kPageZoom, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f / device_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| |
| TEST_F(WebFrameTest, NoWideViewportAndNoViewportWithInitialPageScaleOverride) { |
| RegisterMockedHttpURLLoad("large-div.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| float enforced_page_scale_factor = 5.0f; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad(base_url_ + "large-div.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView()->SetDefaultPageScaleLimits(0.25f, 5); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.GetWebView()->SetInitialPageScaleOverride( |
| enforced_page_scale_factor); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width / enforced_page_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height / enforced_page_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(enforced_page_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| |
| TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScale) { |
| RegisterMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-initial-scale-and-user-scalable-no.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetViewportMetaNonUserScalableQuirk(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| |
| TEST_F(WebFrameTest, |
| NoUserScalableQuirkIgnoresViewportScaleForNonWideViewport) { |
| RegisterMockedHttpURLLoad("viewport-initial-scale-and-user-scalable-no.html"); |
| |
| float device_scale_factor = 1.33f; |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-initial-scale-and-user-scalable-no.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->MainFrameWidget() |
| ->SetDeviceScaleFactorForTesting(device_scale_factor); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetSupportDeprecatedTargetDensityDPI(true); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetViewportMetaNonUserScalableQuirk(true); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(false); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width * device_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height * device_scale_factor, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f / device_scale_factor, |
| web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| |
| TEST_F(WebFrameTest, NoUserScalableQuirkIgnoresViewportScaleForWideViewport) { |
| RegisterMockedHttpURLLoad("viewport-2x-initial-scale-non-user-scalable.html"); |
| |
| int viewport_width = 640; |
| int viewport_height = 480; |
| |
| frame_test_helpers::WebViewHelper web_view_helper; |
| web_view_helper.InitializeAndLoad( |
| base_url_ + "viewport-2x-initial-scale-non-user-scalable.html", nullptr, |
| nullptr, ConfigureAndroid); |
| web_view_helper.GetWebView() |
| ->GetSettings() |
| ->SetViewportMetaNonUserScalableQuirk(true); |
| web_view_helper.GetWebView()->GetSettings()->SetWideViewportQuirkEnabled( |
| true); |
| web_view_helper.GetWebView()->GetSettings()->SetUseWideViewport(true); |
| web_view_helper.Resize(gfx::Size(viewport_width, viewport_height)); |
| |
| EXPECT_NEAR(viewport_width, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Width(), |
| 1.0f); |
| EXPECT_NEAR(viewport_height, |
| web_view_helper.GetWebView() |
| ->MainFrameImpl() |
| ->GetFrameView() |
| ->GetLayoutSize() |
| .Height(), |
| 1.0f); |
| EXPECT_NEAR(1.0f, web_view_helper.GetWebView()->PageScaleFactor(), 0.01f); |
| } |
| |
|