/* * Copyright (C) 2011, 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "core/exported/WebViewImpl.h" #include #include #include #include "base/memory/scoped_refptr.h" #include "build/build_config.h" #include "core/CSSValueKeywords.h" #include "core/CoreInitializer.h" #include "core/clipboard/DataObject.h" #include "core/dom/ContextFeaturesClientImpl.h" #include "core/dom/Document.h" #include "core/dom/LayoutTreeBuilderTraversal.h" #include "core/dom/Text.h" #include "core/dom/UserGestureIndicator.h" #include "core/editing/EditingUtilities.h" #include "core/editing/Editor.h" #include "core/editing/EphemeralRange.h" #include "core/editing/FrameSelection.h" #include "core/editing/SelectionTemplate.h" #include "core/editing/ime/InputMethodController.h" #include "core/editing/iterators/TextIterator.h" #include "core/editing/serializers/HTMLInterchange.h" #include "core/editing/serializers/Serialization.h" #include "core/events/CurrentInputEvent.h" #include "core/events/KeyboardEvent.h" #include "core/events/UIEventWithKeyState.h" #include "core/events/WebInputEventConversion.h" #include "core/events/WheelEvent.h" #include "core/exported/WebDevToolsAgentImpl.h" #include "core/exported/WebPluginContainerImpl.h" #include "core/exported/WebRemoteFrameImpl.h" #include "core/exported/WebSettingsImpl.h" #include "core/frame/BrowserControls.h" #include "core/frame/EventHandlerRegistry.h" #include "core/frame/FullscreenController.h" #include "core/frame/LocalFrame.h" #include "core/frame/LocalFrameClient.h" #include "core/frame/LocalFrameView.h" #include "core/frame/PageScaleConstraintsSet.h" #include "core/frame/RemoteFrame.h" #include "core/frame/ResizeViewportAnchor.h" #include "core/frame/RotationViewportAnchor.h" #include "core/frame/Settings.h" #include "core/frame/UseCounter.h" #include "core/frame/VisualViewport.h" #include "core/frame/WebLocalFrameImpl.h" #include "core/fullscreen/Fullscreen.h" #include "core/html/HTMLPlugInElement.h" #include "core/html/PluginDocument.h" #include "core/html/forms/HTMLTextAreaElement.h" #include "core/html/media/HTMLMediaElement.h" #include "core/html_names.h" #include "core/input/ContextMenuAllowedScope.h" #include "core/input/EventHandler.h" #include "core/input/TouchActionUtil.h" #include "core/inspector/DevToolsEmulator.h" #include "core/layout/LayoutEmbeddedContent.h" #include "core/layout/LayoutView.h" #include "core/layout/TextAutosizer.h" #include "core/loader/DocumentLoader.h" #include "core/loader/FrameLoadRequest.h" #include "core/loader/FrameLoader.h" #include "core/loader/FrameLoaderStateMachine.h" #include "core/loader/InteractiveDetector.h" #include "core/loader/PrerendererClient.h" #include "core/page/ChromeClientImpl.h" #include "core/page/ContextMenuController.h" #include "core/page/ContextMenuProvider.h" #include "core/page/FocusController.h" #include "core/page/FrameTree.h" #include "core/page/Page.h" #include "core/page/PageLifecycleState.h" #include "core/page/PageOverlay.h" #include "core/page/PagePopupClient.h" #include "core/page/PointerLockController.h" #include "core/page/TouchDisambiguation.h" #include "core/page/ValidationMessageClientImpl.h" #include "core/page/scrolling/TopDocumentRootScrollerController.h" #include "core/paint/FirstMeaningfulPaintDetector.h" #include "core/paint/LinkHighlightImpl.h" #include "core/paint/PaintLayer.h" #include "core/paint/compositing/PaintLayerCompositor.h" #include "core/timing/DOMWindowPerformance.h" #include "core/timing/Performance.h" #include "platform/ContextMenu.h" #include "platform/ContextMenuItem.h" #include "platform/Cursor.h" #include "platform/Histogram.h" #include "platform/KeyboardCodes.h" #include "platform/animation/CompositorAnimationHost.h" #include "platform/exported/WebActiveGestureAnimation.h" #include "platform/fonts/FontCache.h" #include "platform/geometry/FloatRect.h" #include "platform/graphics/Color.h" #include "platform/graphics/CompositorMutatorClient.h" #include "platform/graphics/CompositorMutatorImpl.h" #include "platform/graphics/FirstPaintInvalidationTracking.h" #include "platform/graphics/GraphicsContext.h" #include "platform/graphics/Image.h" #include "platform/graphics/gpu/DrawingBuffer.h" #include "platform/graphics/paint/DrawingRecorder.h" #include "platform/image-decoders/ImageDecoder.h" #include "platform/instrumentation/tracing/TraceEvent.h" #include "platform/loader/fetch/UniqueIdentifier.h" #include "platform/runtime_enabled_features.h" #include "platform/scheduler/child/web_scheduler.h" #include "platform/scheduler/renderer/web_view_scheduler.h" #include "platform/scroll/ScrollbarTheme.h" #include "platform/weborigin/SchemeRegistry.h" #include "platform/wtf/AutoReset.h" #include "platform/wtf/PtrUtil.h" #include "platform/wtf/Time.h" #include "public/platform/Platform.h" #include "public/platform/WebCompositeAndReadbackAsyncCallback.h" #include "public/platform/WebCompositorSupport.h" #include "public/platform/WebFloatPoint.h" #include "public/platform/WebGestureCurve.h" #include "public/platform/WebImage.h" #include "public/platform/WebInputEvent.h" #include "public/platform/WebLayerTreeView.h" #include "public/platform/WebMenuSourceType.h" #include "public/platform/WebScrollIntoViewParams.h" #include "public/platform/WebTextInputInfo.h" #include "public/platform/WebURLRequest.h" #include "public/platform/WebVector.h" #include "public/web/WebAutofillClient.h" #include "public/web/WebConsoleMessage.h" #include "public/web/WebElement.h" #include "public/web/WebFrame.h" #include "public/web/WebFrameClient.h" #include "public/web/WebFrameWidget.h" #include "public/web/WebHitTestResult.h" #include "public/web/WebInputElement.h" #include "public/web/WebMeaningfulLayout.h" #include "public/web/WebMediaPlayerAction.h" #include "public/web/WebNode.h" #include "public/web/WebPlugin.h" #include "public/web/WebPluginAction.h" #include "public/web/WebRange.h" #include "public/web/WebScopedUserGesture.h" #include "public/web/WebSelection.h" #include "public/web/WebViewClient.h" #include "public/web/WebWindowFeatures.h" #include "third_party/WebKit/common/page/page_visibility_state.mojom-blink.h" #if defined(WTF_USE_DEFAULT_RENDER_THEME) #include "core/layout/LayoutThemeDefault.h" #endif // Get rid of WTF's pow define so we can use std::pow. #undef pow #include // for std::pow // The following constants control parameters for automated scaling of webpages // (such as due to a double tap gesture or find in page etc.). These are // experimentally determined. static const int touchPointPadding = 32; static const int nonUserInitiatedPointPadding = 11; static const float minScaleDifference = 0.01f; static const float doubleTapZoomContentDefaultMargin = 5; static const float doubleTapZoomContentMinimumMargin = 2; static const double doubleTapZoomAnimationDurationInSeconds = 0.25; static const float doubleTapZoomAlreadyLegibleRatio = 1.2f; static const double multipleTargetsZoomAnimationDurationInSeconds = 0.25; static const double findInPageAnimationDurationInSeconds = 0; // Constants for snapping to minimum zoom. static const double maximumZoomForSnapToMinimum = 1.05; static const double snapToMiminimumZoomAnimationDurationInSeconds = 0.2; // Constants for viewport anchoring on resize. static const float viewportAnchorCoordX = 0.5f; static const float viewportAnchorCoordY = 0; // Constants for zooming in on a focused text field. static const double scrollAndScaleAnimationDurationInSeconds = 0.2; static const int minReadableCaretHeight = 16; static const int minReadableCaretHeightForTextArea = 13; static const float minScaleChangeToTriggerZoom = 1.5f; static const float leftBoxRatio = 0.3f; static const int caretPadding = 10; namespace blink { // Change the text zoom level by kTextSizeMultiplierRatio each time the user // zooms text in or out (ie., change by 20%). The min and max values limit // text zoom to half and 3x the original text size. These three values match // those in Apple's port in WebKit/WebKit/WebView/WebView.mm const double WebView::kTextSizeMultiplierRatio = 1.2; const double WebView::kMinTextSizeMultiplier = 0.5; const double WebView::kMaxTextSizeMultiplier = 3.0; // Used to defer all page activity in cases where the embedder wishes to run // a nested event loop. Using a stack enables nesting of message loop // invocations. static Vector>& PagePauserStack() { DEFINE_STATIC_LOCAL(Vector>, pauser_stack, ()); return pauser_stack; } void WebView::WillEnterModalLoop() { PagePauserStack().push_back(std::make_unique()); } void WebView::DidExitModalLoop() { DCHECK(PagePauserStack().size()); PagePauserStack().pop_back(); } // static HashSet& WebViewImpl::AllInstances() { DEFINE_STATIC_LOCAL(HashSet, all_instances, ()); return all_instances; } static bool g_should_use_external_popup_menus = false; void WebView::SetUseExternalPopupMenus(bool use_external_popup_menus) { g_should_use_external_popup_menus = use_external_popup_menus; } bool WebViewImpl::UseExternalPopupMenus() { return g_should_use_external_popup_menus; } namespace { class EmptyEventListener final : public EventListener { public: static EmptyEventListener* Create() { return new EmptyEventListener(); } bool operator==(const EventListener& other) const override { return this == &other; } private: EmptyEventListener() : EventListener(kCPPEventListenerType) {} void handleEvent(ExecutionContext* execution_context, Event*) override {} }; class ColorOverlay final : public PageOverlay::Delegate { public: explicit ColorOverlay(WebColor color) : color_(color) {} private: void PaintPageOverlay(const PageOverlay& page_overlay, GraphicsContext& graphics_context, const WebSize& size) const override { if (DrawingRecorder::UseCachedDrawingIfPossible( graphics_context, page_overlay, DisplayItem::kPageOverlay)) return; FloatRect rect(0, 0, size.width, size.height); DrawingRecorder recorder(graphics_context, page_overlay, DisplayItem::kPageOverlay); graphics_context.FillRect(rect, color_); } WebColor color_; }; } // namespace // WebView ---------------------------------------------------------------- WebView* WebView::Create(WebViewClient* client, mojom::PageVisibilityState visibility_state, WebView* opener) { return WebViewImpl::Create(client, visibility_state, static_cast(opener)); } WebViewImpl* WebViewImpl::Create(WebViewClient* client, mojom::PageVisibilityState visibility_state, WebViewImpl* opener) { // Pass the WebViewImpl's self-reference to the caller. auto web_view = base::AdoptRef(new WebViewImpl(client, visibility_state, opener)); web_view->AddRef(); return web_view.get(); } void WebView::UpdateVisitedLinkState(unsigned long long link_hash) { Page::VisitedStateChanged(link_hash); } void WebView::ResetVisitedLinkState(bool invalidate_visited_link_hashes) { Page::AllVisitedStateChanged(invalidate_visited_link_hashes); } void WebViewImpl::SetPrerendererClient( WebPrerendererClient* prerenderer_client) { DCHECK(page_); ProvidePrerendererClientTo(*page_, new PrerendererClient(*page_, prerenderer_client)); } WebViewImpl::WebViewImpl(WebViewClient* client, mojom::PageVisibilityState visibility_state, WebViewImpl* opener) : client_(client), chrome_client_(ChromeClientImpl::Create(this)), should_auto_resize_(false), zoom_level_(0), minimum_zoom_level_(ZoomFactorToZoomLevel(kMinTextSizeMultiplier)), maximum_zoom_level_(ZoomFactorToZoomLevel(kMaxTextSizeMultiplier)), zoom_factor_for_device_scale_factor_(0.f), maximum_legible_scale_(1), double_tap_zoom_page_scale_factor_(0), double_tap_zoom_pending_(false), enable_fake_page_scale_animation_for_testing_(false), fake_page_scale_animation_page_scale_factor_(0), fake_page_scale_animation_use_anchor_(false), compositor_device_scale_factor_override_(0), suppress_next_keypress_event_(false), ime_accept_events_(true), dev_tools_emulator_(nullptr), tabs_to_links_(false), layer_tree_view_(nullptr), root_layer_(nullptr), root_graphics_layer_(nullptr), visual_viewport_container_layer_(nullptr), matches_heuristics_for_gpu_rasterization_(false), fullscreen_controller_(FullscreenController::Create(this)), base_background_color_(Color::kWhite), base_background_color_override_enabled_(false), base_background_color_override_(Color::kTransparent), background_color_override_enabled_(false), background_color_override_(Color::kTransparent), zoom_factor_override_(0), should_dispatch_first_visually_non_empty_layout_(false), should_dispatch_first_layout_after_finished_parsing_(false), should_dispatch_first_layout_after_finished_loading_(false), display_mode_(kWebDisplayModeBrowser), elastic_overscroll_(FloatSize()), mutator_(nullptr), scheduler_(Platform::Current() ->CurrentThread() ->Scheduler() ->CreateWebViewScheduler(this, this)), override_compositor_visibility_(false) { Page::PageClients page_clients; page_clients.chrome_client = chrome_client_.Get(); page_ = Page::CreateOrdinary(page_clients, opener ? opener->GetPage() : nullptr); CoreInitializer::GetInstance().ProvideModulesToPage(*page_, client_); page_->SetValidationMessageClient(ValidationMessageClientImpl::Create(*this)); SetVisibilityState(visibility_state, true); InitializeLayerTreeView(); dev_tools_emulator_ = DevToolsEmulator::Create(this); AllInstances().insert(this); page_importance_signals_.SetObserver(client); resize_viewport_anchor_ = new ResizeViewportAnchor(*page_); } WebViewImpl::~WebViewImpl() { DCHECK(!page_); // Each highlight uses m_owningWebViewImpl->m_linkHighlightsTimeline // in destructor. m_linkHighlightsTimeline might be destroyed earlier // than m_linkHighlights. DCHECK(link_highlights_.IsEmpty()); } ValidationMessageClient* WebViewImpl::GetValidationMessageClient() const { return page_ ? &page_->GetValidationMessageClient() : nullptr; } WebDevToolsAgentImpl* WebViewImpl::MainFrameDevToolsAgentImpl() { WebLocalFrameImpl* main_frame = MainFrameImpl(); return main_frame ? main_frame->DevToolsAgentImpl() : nullptr; } WebLocalFrameImpl* WebViewImpl::MainFrameImpl() const { return page_ && page_->MainFrame() && page_->MainFrame()->IsLocalFrame() ? WebLocalFrameImpl::FromFrame(page_->DeprecatedLocalMainFrame()) : nullptr; } bool WebViewImpl::TabKeyCyclesThroughElements() const { DCHECK(page_); return page_->TabKeyCyclesThroughElements(); } void WebViewImpl::SetTabKeyCyclesThroughElements(bool value) { if (page_) page_->SetTabKeyCyclesThroughElements(value); } void WebViewImpl::HandleMouseLeave(LocalFrame& main_frame, const WebMouseEvent& event) { client_->SetMouseOverURL(WebURL()); PageWidgetEventHandler::HandleMouseLeave(main_frame, event); } void WebViewImpl::HandleMouseDown(LocalFrame& main_frame, const WebMouseEvent& event) { // If there is a popup open, close it as the user is clicking on the page // (outside of the popup). We also save it so we can prevent a click on an // element from immediately reopening the same popup. scoped_refptr page_popup; if (event.button == WebMouseEvent::Button::kLeft) { page_popup = page_popup_; HidePopups(); DCHECK(!page_popup_); } // Take capture on a mouse down on a plugin so we can send it mouse events. // If the hit node is a plugin but a scrollbar is over it don't start mouse // capture because it will interfere with the scrollbar receiving events. LayoutPoint point(event.PositionInWidget()); if (event.button == WebMouseEvent::Button::kLeft && page_->MainFrame()->IsLocalFrame()) { point = page_->DeprecatedLocalMainFrame()->View()->RootFrameToContents(point); HitTestResult result(page_->DeprecatedLocalMainFrame() ->GetEventHandler() .HitTestResultAtPoint(point)); result.SetToShadowHostIfInRestrictedShadowRoot(); Node* hit_node = result.InnerNodeOrImageMapImage(); if (!result.GetScrollbar() && hit_node && hit_node->GetLayoutObject() && hit_node->GetLayoutObject()->IsEmbeddedObject()) { mouse_capture_node_ = hit_node; TRACE_EVENT_ASYNC_BEGIN0("input", "capturing mouse", this); } } PageWidgetEventHandler::HandleMouseDown(main_frame, event); if (event.button == WebMouseEvent::Button::kLeft && mouse_capture_node_) { mouse_capture_gesture_token_ = main_frame.GetEventHandler().TakeLastMouseDownGestureToken(); } if (page_popup_ && page_popup && page_popup_->HasSamePopupClient(page_popup.get())) { // That click triggered a page popup that is the same as the one we just // closed. It needs to be closed. CancelPagePopup(); } // Dispatch the contextmenu event regardless of if the click was swallowed. if (!GetPage()->GetSettings().GetShowContextMenuOnMouseUp()) { #if defined(OS_MACOSX) if (event.button == WebMouseEvent::Button::kRight || (event.button == WebMouseEvent::Button::kLeft && event.GetModifiers() & WebMouseEvent::kControlKey)) MouseContextMenu(event); #else if (event.button == WebMouseEvent::Button::kRight) MouseContextMenu(event); #endif } } void WebViewImpl::SetDisplayMode(WebDisplayMode mode) { display_mode_ = mode; if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView()) return; MainFrameImpl()->GetFrameView()->SetDisplayMode(mode); } void WebViewImpl::MouseContextMenu(const WebMouseEvent& event) { if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView()) return; page_->GetContextMenuController().ClearContextMenu(); WebMouseEvent transformed_event = TransformWebMouseEvent(MainFrameImpl()->GetFrameView(), event); transformed_event.menu_source_type = kMenuSourceMouse; LayoutPoint position_in_root_frame(transformed_event.PositionInRootFrame()); // Find the right target frame. See issue 1186900. HitTestResult result = HitTestResultForRootFramePos(position_in_root_frame); Frame* target_frame; if (result.InnerNodeOrImageMapImage()) target_frame = result.InnerNodeOrImageMapImage()->GetDocument().GetFrame(); else target_frame = page_->GetFocusController().FocusedOrMainFrame(); if (!target_frame->IsLocalFrame()) return; LocalFrame* target_local_frame = ToLocalFrame(target_frame); { ContextMenuAllowedScope scope; target_local_frame->GetEventHandler().SendContextMenuEvent( transformed_event, nullptr); } // Actually showing the context menu is handled by the ContextMenuController // implementation... } void WebViewImpl::HandleMouseUp(LocalFrame& main_frame, const WebMouseEvent& event) { PageWidgetEventHandler::HandleMouseUp(main_frame, event); if (GetPage()->GetSettings().GetShowContextMenuOnMouseUp()) { // Dispatch the contextmenu event regardless of if the click was swallowed. // On Mac/Linux, we handle it on mouse down, not up. if (event.button == WebMouseEvent::Button::kRight) MouseContextMenu(event); } } WebInputEventResult WebViewImpl::HandleMouseWheel( LocalFrame& main_frame, const WebMouseWheelEvent& event) { // Halt an in-progress fling on a wheel tick. if (!event.has_precise_scrolling_deltas) { if (WebFrameWidgetBase* widget = MainFrameImpl()->FrameWidget()) widget->EndActiveFlingAnimation(); } HidePopups(); return PageWidgetEventHandler::HandleMouseWheel(main_frame, event); } WebInputEventResult WebViewImpl::HandleGestureEvent( const WebGestureEvent& event) { if (!client_ || !client_->CanHandleGestureEvent()) { return WebInputEventResult::kNotHandled; } WebInputEventResult event_result = WebInputEventResult::kNotHandled; bool event_cancelled = false; // for disambiguation // Special handling for slow-path fling gestures. switch (event.GetType()) { case WebInputEvent::kGestureFlingStart: case WebInputEvent::kGestureFlingCancel: { if (WebFrameWidgetBase* widget = MainFrameImpl()->FrameWidget()) event_result = widget->HandleGestureFlingEvent(event); client_->DidHandleGestureEvent(event, event_cancelled); return event_result; } default: break; } WebGestureEvent scaled_event = TransformWebGestureEvent(MainFrameImpl()->GetFrameView(), event); // Special handling for double tap and scroll events as we don't want to // hit test for them. switch (event.GetType()) { case WebInputEvent::kGestureDoubleTap: if (web_settings_->DoubleTapToZoomEnabled() && MinimumPageScaleFactor() != MaximumPageScaleFactor()) { AnimateDoubleTapZoom( FlooredIntPoint(scaled_event.PositionInRootFrame())); } // GestureDoubleTap is currently only used by Android for zooming. For // WebCore, GestureTap with tap count = 2 is used instead. So we drop // GestureDoubleTap here. event_result = WebInputEventResult::kHandledSystem; client_->DidHandleGestureEvent(event, event_cancelled); return event_result; case WebInputEvent::kGestureScrollBegin: case WebInputEvent::kGestureScrollEnd: case WebInputEvent::kGestureScrollUpdate: case WebInputEvent::kGestureFlingStart: // Scrolling-related gesture events invoke EventHandler recursively for // each frame down the chain, doing a single-frame hit-test per frame. // This matches handleWheelEvent. Perhaps we could simplify things by // rewriting scroll handling to work inner frame out, and then unify with // other gesture events. event_result = MainFrameImpl() ->GetFrame() ->GetEventHandler() .HandleGestureScrollEvent(scaled_event); client_->DidHandleGestureEvent(event, event_cancelled); return event_result; case WebInputEvent::kGesturePinchBegin: case WebInputEvent::kGesturePinchEnd: case WebInputEvent::kGesturePinchUpdate: return WebInputEventResult::kNotHandled; default: break; } // Hit test across all frames and do touch adjustment as necessary for the // event type. GestureEventWithHitTestResults targeted_event = page_->DeprecatedLocalMainFrame()->GetEventHandler().TargetGestureEvent( scaled_event); // Handle link highlighting outside the main switch to avoid getting lost in // the complicated set of cases handled below. switch (event.GetType()) { case WebInputEvent::kGestureShowPress: // Queue a highlight animation, then hand off to regular handler. EnableTapHighlightAtPoint(targeted_event); break; case WebInputEvent::kGestureTapCancel: case WebInputEvent::kGestureTap: case WebInputEvent::kGestureLongPress: for (size_t i = 0; i < link_highlights_.size(); ++i) link_highlights_[i]->StartHighlightAnimationIfNeeded(); break; default: break; } switch (event.GetType()) { case WebInputEvent::kGestureTap: { // Don't trigger a disambiguation popup on sites designed for mobile // devices. Instead, assume that the page has been designed with big // enough buttons and links. Don't trigger a disambiguation popup when // screencasting, since it's implemented outside of compositor pipeline // and is not being screencasted itself. This leads to bad user // experience. WebDevToolsAgentImpl* dev_tools = MainFrameDevToolsAgentImpl(); VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); bool screencast_enabled = dev_tools && dev_tools->ScreencastEnabled(); if (event.data.tap.width > 0 && !visual_viewport.ShouldDisableDesktopWorkarounds() && !screencast_enabled) { IntRect bounding_box(visual_viewport.ViewportToRootFrame( IntRect(event.x - event.data.tap.width / 2, event.y - event.data.tap.height / 2, event.data.tap.width, event.data.tap.height))); // TODO(bokan): We shouldn't pass details of the VisualViewport offset // to render_view_impl. crbug.com/459591 WebSize visual_viewport_offset = FlooredIntSize(visual_viewport.GetScrollOffset()); if (web_settings_->MultiTargetTapNotificationEnabled()) { Vector good_targets; HeapVector> highlight_nodes; FindGoodTouchTargets(bounding_box, MainFrameImpl()->GetFrame(), good_targets, highlight_nodes); // FIXME: replace touch adjustment code when numberOfGoodTargets == 1? // Single candidate case is currently handled by: // https://bugs.webkit.org/show_bug.cgi?id=85101 if (good_targets.size() >= 2 && client_ && client_->DidTapMultipleTargets(visual_viewport_offset, bounding_box, good_targets)) { // Stash the position of the node that would've been used absent // disambiguation, for UMA purposes. last_tap_disambiguation_best_candidate_position_ = targeted_event.GetHitTestResult().RoundedPointInMainFrame() - RoundedIntSize(targeted_event.GetHitTestResult().LocalPoint()); EnableTapHighlights(highlight_nodes); for (size_t i = 0; i < link_highlights_.size(); ++i) link_highlights_[i]->StartHighlightAnimationIfNeeded(); event_result = WebInputEventResult::kHandledSystem; event_cancelled = true; break; } } } { ContextMenuAllowedScope scope; event_result = MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureEvent( targeted_event); } if (page_popup_ && last_hidden_page_popup_ && page_popup_->HasSamePopupClient(last_hidden_page_popup_.get())) { // The tap triggered a page popup that is the same as the one we just // closed. It needs to be closed. CancelPagePopup(); } last_hidden_page_popup_ = nullptr; break; } case WebInputEvent::kGestureTwoFingerTap: case WebInputEvent::kGestureLongPress: case WebInputEvent::kGestureLongTap: { if (!MainFrameImpl() || !MainFrameImpl()->GetFrameView()) break; page_->GetContextMenuController().ClearContextMenu(); { ContextMenuAllowedScope scope; event_result = MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureEvent( targeted_event); } break; } case WebInputEvent::kGestureTapDown: { // Touch pinch zoom and scroll on the page (outside of a popup) must hide // the popup. In case of a touch scroll or pinch zoom, this function is // called with GestureTapDown rather than a GSB/GSU/GSE or GPB/GPU/GPE. // When we close a popup because of a GestureTapDown, we also save it so // we can prevent the following GestureTap from immediately reopening the // same popup. last_hidden_page_popup_ = page_popup_; HidePopups(); DCHECK(!page_popup_); event_result = MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureEvent( targeted_event); break; } case WebInputEvent::kGestureTapCancel: { last_hidden_page_popup_ = nullptr; event_result = MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureEvent( targeted_event); break; } case WebInputEvent::kGestureShowPress: { event_result = MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureEvent( targeted_event); break; } case WebInputEvent::kGestureTapUnconfirmed: { event_result = MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureEvent( targeted_event); break; } default: { NOTREACHED(); } } client_->DidHandleGestureEvent(event, event_cancelled); return event_result; } namespace { // This enum is used to back a histogram, and should therefore be treated as // append-only. enum TapDisambiguationResult { kUmaTapDisambiguationOther = 0, kUmaTapDisambiguationBackButton = 1, kUmaTapDisambiguationTappedOutside = 2, kUmaTapDisambiguationTappedInsideDeprecated = 3, kUmaTapDisambiguationTappedInsideSameNode = 4, kUmaTapDisambiguationTappedInsideDifferentNode = 5, kUmaTapDisambiguationCount = 6, }; void RecordTapDisambiguation(TapDisambiguationResult result) { UMA_HISTOGRAM_ENUMERATION("Touchscreen.TapDisambiguation", result, kUmaTapDisambiguationCount); } } // namespace void WebViewImpl::ResolveTapDisambiguation(double timestamp_seconds, WebPoint tap_viewport_offset, bool is_long_press) { WebGestureEvent event(is_long_press ? WebInputEvent::kGestureLongPress : WebInputEvent::kGestureTap, WebInputEvent::kNoModifiers, timestamp_seconds); event.x = tap_viewport_offset.x; event.y = tap_viewport_offset.y; event.source_device = blink::kWebGestureDeviceTouchscreen; { // Compute UMA stat about whether the node selected by disambiguation UI was // different from the one preferred by the regular hit-testing + adjustment // logic. WebGestureEvent scaled_event = TransformWebGestureEvent(MainFrameImpl()->GetFrameView(), event); GestureEventWithHitTestResults targeted_event = page_->DeprecatedLocalMainFrame()->GetEventHandler().TargetGestureEvent( scaled_event); WebPoint node_position = targeted_event.GetHitTestResult().RoundedPointInMainFrame() - RoundedIntSize(targeted_event.GetHitTestResult().LocalPoint()); TapDisambiguationResult result = (node_position == last_tap_disambiguation_best_candidate_position_) ? kUmaTapDisambiguationTappedInsideSameNode : kUmaTapDisambiguationTappedInsideDifferentNode; RecordTapDisambiguation(result); } HandleGestureEvent(event); } WebInputEventResult WebViewImpl::HandleSyntheticWheelFromTouchpadPinchEvent( const WebGestureEvent& pinch_event) { DCHECK_EQ(pinch_event.GetType(), WebInputEvent::kGesturePinchUpdate); // For pinch gesture events, match typical trackpad behavior on Windows by // sending fake wheel events with the ctrl modifier set when we see trackpad // pinch gestures. Ideally we'd someday get a platform 'pinch' event and // send that instead. WebMouseWheelEvent wheel_event( WebInputEvent::kMouseWheel, pinch_event.GetModifiers() | WebInputEvent::kControlKey, pinch_event.TimeStampSeconds()); wheel_event.SetPositionInWidget(pinch_event.x, pinch_event.y); wheel_event.SetPositionInScreen(pinch_event.global_x, pinch_event.global_y); wheel_event.delta_x = 0; // The function to convert scales to deltaY values is designed to be // compatible with websites existing use of wheel events, and with existing // Windows trackpad behavior. In particular, we want: // - deltas should accumulate via addition: f(s1*s2)==f(s1)+f(s2) // - deltas should invert via negation: f(1/s) == -f(s) // - zoom in should be positive: f(s) > 0 iff s > 1 // - magnitude roughly matches wheels: f(2) > 25 && f(2) < 100 // - a formula that's relatively easy to use from JavaScript // Note that 'wheel' event deltaY values have their sign inverted. So to // convert a wheel deltaY back to a scale use Math.exp(-deltaY/100). DCHECK_GT(pinch_event.data.pinch_update.scale, 0); wheel_event.delta_y = 100.0f * log(pinch_event.data.pinch_update.scale); wheel_event.has_precise_scrolling_deltas = true; wheel_event.wheel_ticks_x = 0; wheel_event.wheel_ticks_y = pinch_event.data.pinch_update.scale > 1 ? 1 : -1; return HandleInputEvent(blink::WebCoalescedInputEvent(wheel_event)); } bool WebViewImpl::StartPageScaleAnimation(const IntPoint& target_position, bool use_anchor, float new_scale, double duration_in_seconds) { VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); WebPoint clamped_point = target_position; if (!use_anchor) { clamped_point = visual_viewport.ClampDocumentOffsetAtScale(target_position, new_scale); if (!duration_in_seconds) { SetPageScaleFactor(new_scale); LocalFrameView* view = MainFrameImpl()->GetFrameView(); if (view && view->GetScrollableArea()) { view->GetScrollableArea()->SetScrollOffset( ScrollOffset(clamped_point.x, clamped_point.y), kProgrammaticScroll); } return false; } } if (use_anchor && new_scale == PageScaleFactor()) return false; if (enable_fake_page_scale_animation_for_testing_) { fake_page_scale_animation_target_position_ = target_position; fake_page_scale_animation_use_anchor_ = use_anchor; fake_page_scale_animation_page_scale_factor_ = new_scale; } else { if (!layer_tree_view_) return false; layer_tree_view_->StartPageScaleAnimation(target_position, use_anchor, new_scale, duration_in_seconds); } return true; } void WebViewImpl::EnableFakePageScaleAnimationForTesting(bool enable) { enable_fake_page_scale_animation_for_testing_ = enable; fake_page_scale_animation_target_position_ = IntPoint(); fake_page_scale_animation_use_anchor_ = false; fake_page_scale_animation_page_scale_factor_ = 0; } void WebViewImpl::SetShowFPSCounter(bool show) { if (layer_tree_view_) { TRACE_EVENT0("blink", "WebViewImpl::setShowFPSCounter"); layer_tree_view_->SetShowFPSCounter(show); } } void WebViewImpl::SetShowPaintRects(bool show) { if (layer_tree_view_) { TRACE_EVENT0("blink", "WebViewImpl::setShowPaintRects"); layer_tree_view_->SetShowPaintRects(show); } FirstPaintInvalidationTracking::SetEnabledForShowPaintRects(show); } void WebViewImpl::SetShowDebugBorders(bool show) { if (layer_tree_view_) layer_tree_view_->SetShowDebugBorders(show); } void WebViewImpl::SetShowScrollBottleneckRects(bool show) { if (layer_tree_view_) layer_tree_view_->SetShowScrollBottleneckRects(show); } void WebViewImpl::AcceptLanguagesChanged() { if (client_) FontCache::AcceptLanguagesChanged(client_->AcceptLanguages()); if (!GetPage()) return; GetPage()->AcceptLanguagesChanged(); } void WebViewImpl::ReportIntervention(const WebString& message) { if (!MainFrameImpl()) return; WebConsoleMessage console_message(WebConsoleMessage::kLevelWarning, message); MainFrameImpl()->AddMessageToConsole(console_message); } void WebViewImpl::RequestBeginMainFrameNotExpected(bool new_state) { if (layer_tree_view_) { layer_tree_view_->RequestBeginMainFrameNotExpected(new_state); } } void WebViewImpl::SetPageStopped(bool stopped) { if (!GetPage()) return; GetPage()->SetLifecycleState(stopped ? PageLifecycleState::kStopped : GetPage()->IsPageVisible() ? PageLifecycleState::kActive : PageLifecycleState::kHidden); } WebInputEventResult WebViewImpl::HandleKeyEvent(const WebKeyboardEvent& event) { DCHECK((event.GetType() == WebInputEvent::kRawKeyDown) || (event.GetType() == WebInputEvent::kKeyDown) || (event.GetType() == WebInputEvent::kKeyUp)); TRACE_EVENT2("input", "WebViewImpl::handleKeyEvent", "type", WebInputEvent::GetName(event.GetType()), "text", String(event.text).Utf8()); // Halt an in-progress fling on a key event. if (WebFrameWidgetBase* widget = MainFrameImpl()->FrameWidget()) widget->EndActiveFlingAnimation(); // Please refer to the comments explaining the m_suppressNextKeypressEvent // member. // The m_suppressNextKeypressEvent is set if the KeyDown is handled by // Webkit. A keyDown event is typically associated with a keyPress(char) // event and a keyUp event. We reset this flag here as this is a new keyDown // event. suppress_next_keypress_event_ = false; // If there is a popup, it should be the one processing the event, not the // page. if (page_popup_) { page_popup_->HandleKeyEvent(event); // We need to ignore the next Char event after this otherwise pressing // enter when selecting an item in the popup will go to the page. if (WebInputEvent::kRawKeyDown == event.GetType()) suppress_next_keypress_event_ = true; return WebInputEventResult::kHandledSystem; } Frame* focused_frame = FocusedCoreFrame(); if (!focused_frame || !focused_frame->IsLocalFrame()) return WebInputEventResult::kNotHandled; LocalFrame* frame = ToLocalFrame(focused_frame); WebInputEventResult result = frame->GetEventHandler().KeyEvent(event); if (result != WebInputEventResult::kNotHandled) { if (WebInputEvent::kRawKeyDown == event.GetType()) { // Suppress the next keypress event unless the focused node is a plugin // node. (Flash needs these keypress events to handle non-US keyboards.) Element* element = FocusedElement(); if (element && element->GetLayoutObject() && element->GetLayoutObject()->IsEmbeddedObject()) { if (event.windows_key_code == VKEY_TAB) { // If the plugin supports keyboard focus then we should not send a tab // keypress event. WebPluginContainerImpl* plugin_view = ToLayoutEmbeddedContent(element->GetLayoutObject())->Plugin(); if (plugin_view && plugin_view->SupportsKeyboardFocus()) { suppress_next_keypress_event_ = true; } } } else { suppress_next_keypress_event_ = true; } } return result; } #if !defined(OS_MACOSX) const WebInputEvent::Type kContextMenuKeyTriggeringEventType = #if defined(OS_WIN) WebInputEvent::kKeyUp; #else WebInputEvent::kRawKeyDown; #endif const WebInputEvent::Type kShiftF10TriggeringEventType = WebInputEvent::kRawKeyDown; bool is_unmodified_menu_key = !(event.GetModifiers() & WebInputEvent::kInputModifiers) && event.windows_key_code == VKEY_APPS; bool is_shift_f10 = (event.GetModifiers() & WebInputEvent::kInputModifiers) == WebInputEvent::kShiftKey && event.windows_key_code == VKEY_F10; if ((is_unmodified_menu_key && event.GetType() == kContextMenuKeyTriggeringEventType) || (is_shift_f10 && event.GetType() == kShiftF10TriggeringEventType)) { SendContextMenuEvent(); return WebInputEventResult::kHandledSystem; } #endif // !defined(OS_MACOSX) return WebInputEventResult::kNotHandled; } WebInputEventResult WebViewImpl::HandleCharEvent( const WebKeyboardEvent& event) { DCHECK_EQ(event.GetType(), WebInputEvent::kChar); TRACE_EVENT1("input", "WebViewImpl::handleCharEvent", "text", String(event.text).Utf8()); // Please refer to the comments explaining the m_suppressNextKeypressEvent // member. The m_suppressNextKeypressEvent is set if the KeyDown is // handled by Webkit. A keyDown event is typically associated with a // keyPress(char) event and a keyUp event. We reset this flag here as it // only applies to the current keyPress event. bool suppress = suppress_next_keypress_event_; suppress_next_keypress_event_ = false; // If there is a popup, it should be the one processing the event, not the // page. if (page_popup_) return page_popup_->HandleKeyEvent(event); LocalFrame* frame = ToLocalFrame(FocusedCoreFrame()); if (!frame) { return suppress ? WebInputEventResult::kHandledSuppressed : WebInputEventResult::kNotHandled; } EventHandler& handler = frame->GetEventHandler(); if (!event.IsCharacterKey()) return WebInputEventResult::kHandledSuppressed; // Accesskeys are triggered by char events and can't be suppressed. if (handler.HandleAccessKey(event)) return WebInputEventResult::kHandledSystem; // Safari 3.1 does not pass off windows system key messages (WM_SYSCHAR) to // the eventHandler::keyEvent. We mimic this behavior on all platforms since // for now we are converting other platform's key events to windows key // events. if (event.is_system_key) return WebInputEventResult::kNotHandled; if (suppress) return WebInputEventResult::kHandledSuppressed; WebInputEventResult result = handler.KeyEvent(event); if (result != WebInputEventResult::kNotHandled) return result; return WebInputEventResult::kNotHandled; } WebRect WebViewImpl::ComputeBlockBound(const WebPoint& point_in_root_frame, bool ignore_clipping) { if (!MainFrameImpl()) return WebRect(); // Use the point-based hit test to find the node. LayoutPoint point = MainFrameImpl()->GetFrameView()->RootFrameToContents( LayoutPoint(point_in_root_frame)); HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kReadOnly | HitTestRequest::kActive | (ignore_clipping ? HitTestRequest::kIgnoreClipping : 0); HitTestResult result = MainFrameImpl()->GetFrame()->GetEventHandler().HitTestResultAtPoint( point, hit_type); result.SetToShadowHostIfInRestrictedShadowRoot(); Node* node = result.InnerNodeOrImageMapImage(); if (!node) return WebRect(); // Find the block type node based on the hit node. // FIXME: This wants to walk flat tree with // LayoutTreeBuilderTraversal::parent(). while (node && (!node->GetLayoutObject() || node->GetLayoutObject()->IsInline())) node = LayoutTreeBuilderTraversal::Parent(*node); // Return the bounding box in the root frame's coordinate space. if (node) { IntRect point_in_root_frame = node->Node::PixelSnappedBoundingBox(); LocalFrame* frame = node->GetDocument().GetFrame(); return frame->View()->ContentsToRootFrame(point_in_root_frame); } return WebRect(); } WebRect WebViewImpl::WidenRectWithinPageBounds(const WebRect& source, int target_margin, int minimum_margin) { WebSize max_size; IntSize scroll_offset; if (MainFrame()) { // TODO(lukasza): https://crbug.com/734209: The DCHECK below holds now, but // only because all of the callers don't support OOPIFs and exit early if // the main frame is not local. DCHECK(MainFrame()->IsWebLocalFrame()); max_size = MainFrame()->ToWebLocalFrame()->ContentsSize(); scroll_offset = MainFrame()->ToWebLocalFrame()->GetScrollOffset(); } int left_margin = target_margin; int right_margin = target_margin; const int absolute_source_x = source.x + scroll_offset.Width(); if (left_margin > absolute_source_x) { left_margin = absolute_source_x; right_margin = std::max(left_margin, minimum_margin); } const int maximum_right_margin = max_size.width - (source.width + absolute_source_x); if (right_margin > maximum_right_margin) { right_margin = maximum_right_margin; left_margin = std::min(left_margin, std::max(right_margin, minimum_margin)); } const int new_width = source.width + left_margin + right_margin; const int new_x = source.x - left_margin; DCHECK_GE(new_width, 0); DCHECK_LE(scroll_offset.Width() + new_x + new_width, max_size.width); return WebRect(new_x, source.y, new_width, source.height); } float WebViewImpl::MaximumLegiblePageScale() const { // Pages should be as legible as on desktop when at dpi scale, so no // need to zoom in further when automatically determining zoom level // (after double tap, find in page, etc), though the user should still // be allowed to manually pinch zoom in further if they desire. if (GetPage()) { return maximum_legible_scale_ * GetPage()->GetSettings().GetAccessibilityFontScaleFactor(); } return maximum_legible_scale_; } void WebViewImpl::ComputeScaleAndScrollForBlockRect( const WebPoint& hit_point_in_root_frame, const WebRect& block_rect_in_root_frame, float padding, float default_scale_when_already_legible, float& scale, WebPoint& scroll) { scale = PageScaleFactor(); scroll.x = scroll.y = 0; WebRect rect = block_rect_in_root_frame; if (!rect.IsEmpty()) { float default_margin = doubleTapZoomContentDefaultMargin; float minimum_margin = doubleTapZoomContentMinimumMargin; // We want the margins to have the same physical size, which means we // need to express them in post-scale size. To do that we'd need to know // the scale we're scaling to, but that depends on the margins. Instead // we express them as a fraction of the target rectangle: this will be // correct if we end up fully zooming to it, and won't matter if we // don't. rect = WidenRectWithinPageBounds( rect, static_cast(default_margin * rect.width / size_.width), static_cast(minimum_margin * rect.width / size_.width)); // Fit block to screen, respecting limits. scale = static_cast(size_.width) / rect.width; scale = std::min(scale, MaximumLegiblePageScale()); if (PageScaleFactor() < default_scale_when_already_legible) scale = std::max(scale, default_scale_when_already_legible); scale = ClampPageScaleFactorToLimits(scale); } // FIXME: If this is being called for auto zoom during find in page, // then if the user manually zooms in it'd be nice to preserve the // relative increase in zoom they caused (if they zoom out then it's ok // to zoom them back in again). This isn't compatible with our current // double-tap zoom strategy (fitting the containing block to the screen) // though. float screen_width = size_.width / scale; float screen_height = size_.height / scale; // Scroll to vertically align the block. if (rect.height < screen_height) { // Vertically center short blocks. rect.y -= 0.5 * (screen_height - rect.height); } else { // Ensure position we're zooming to (+ padding) isn't off the bottom of // the screen. rect.y = std::max( rect.y, hit_point_in_root_frame.y + padding - screen_height); } // Otherwise top align the block. // Do the same thing for horizontal alignment. if (rect.width < screen_width) { rect.x -= 0.5 * (screen_width - rect.width); } else { rect.x = std::max( rect.x, hit_point_in_root_frame.x + padding - screen_width); } scroll.x = rect.x; scroll.y = rect.y; scale = ClampPageScaleFactorToLimits(scale); scroll = MainFrameImpl()->GetFrameView()->RootFrameToContents(scroll); scroll = GetPage()->GetVisualViewport().ClampDocumentOffsetAtScale(scroll, scale); } static Node* FindCursorDefiningAncestor(Node* node, LocalFrame* frame) { // Go up the tree to find the node that defines a mouse cursor style while (node) { if (node->GetLayoutObject()) { ECursor cursor = node->GetLayoutObject()->Style()->Cursor(); if (cursor != ECursor::kAuto || frame->GetEventHandler().UseHandCursor(node, node->IsLink())) break; } node = LayoutTreeBuilderTraversal::Parent(*node); } return node; } static bool ShowsHandCursor(Node* node, LocalFrame* frame) { if (!node || !node->GetLayoutObject()) return false; ECursor cursor = node->GetLayoutObject()->Style()->Cursor(); return cursor == ECursor::kPointer || (cursor == ECursor::kAuto && frame->GetEventHandler().UseHandCursor(node, node->IsLink())); } Node* WebViewImpl::BestTapNode( const GestureEventWithHitTestResults& targeted_tap_event) { TRACE_EVENT0("input", "WebViewImpl::bestTapNode"); if (!page_ || !page_->MainFrame()) return nullptr; Node* best_touch_node = targeted_tap_event.GetHitTestResult().InnerNode(); if (!best_touch_node) return nullptr; // We might hit something like an image map that has no layoutObject on it // Walk up the tree until we have a node with an attached layoutObject while (!best_touch_node->GetLayoutObject()) { best_touch_node = LayoutTreeBuilderTraversal::Parent(*best_touch_node); if (!best_touch_node) return nullptr; } // Editable nodes should not be highlighted (e.g., ) if (HasEditableStyle(*best_touch_node)) return nullptr; Node* cursor_defining_ancestor = FindCursorDefiningAncestor( best_touch_node, page_->DeprecatedLocalMainFrame()); // We show a highlight on tap only when the current node shows a hand cursor if (!cursor_defining_ancestor || !ShowsHandCursor(cursor_defining_ancestor, page_->DeprecatedLocalMainFrame())) { return nullptr; } // We should pick the largest enclosing node with hand cursor set. We do this // by first jumping up to cursorDefiningAncestor (which is already known to // have hand cursor set). Then we locate the next cursor-defining ancestor up // in the the tree and repeat the jumps as long as the node has hand cursor // set. do { best_touch_node = cursor_defining_ancestor; cursor_defining_ancestor = FindCursorDefiningAncestor( LayoutTreeBuilderTraversal::Parent(*best_touch_node), page_->DeprecatedLocalMainFrame()); } while (cursor_defining_ancestor && ShowsHandCursor(cursor_defining_ancestor, page_->DeprecatedLocalMainFrame())); return best_touch_node; } void WebViewImpl::EnableTapHighlightAtPoint( const GestureEventWithHitTestResults& targeted_tap_event) { Node* touch_node = BestTapNode(targeted_tap_event); HeapVector> highlight_nodes; highlight_nodes.push_back(touch_node); EnableTapHighlights(highlight_nodes); } void WebViewImpl::EnableTapHighlights( HeapVector>& highlight_nodes) { if (highlight_nodes.IsEmpty()) return; // Always clear any existing highlight when this is invoked, even if we // don't get a new target to highlight. link_highlights_.clear(); for (size_t i = 0; i < highlight_nodes.size(); ++i) { Node* node = highlight_nodes[i]; if (!node || !node->GetLayoutObject()) continue; Color highlight_color = node->GetLayoutObject()->Style()->TapHighlightColor(); // Safari documentation for -webkit-tap-highlight-color says if the // specified color has 0 alpha, then tap highlighting is disabled. // http://developer.apple.com/library/safari/#documentation/appleapplications/reference/safaricssref/articles/standardcssproperties.html if (!highlight_color.Alpha()) continue; link_highlights_.push_back(LinkHighlightImpl::Create(node, this)); } UpdateAllLifecyclePhases(); } void WebViewImpl::AnimateDoubleTapZoom(const IntPoint& point_in_root_frame) { // TODO(lukasza): https://crbug.com/734209: Add OOPIF support. if (!MainFrameImpl()) return; WebRect block_bounds = ComputeBlockBound(point_in_root_frame, false); float scale; WebPoint scroll; ComputeScaleAndScrollForBlockRect( point_in_root_frame, block_bounds, touchPointPadding, MinimumPageScaleFactor() * doubleTapZoomAlreadyLegibleRatio, scale, scroll); bool still_at_previous_double_tap_scale = (PageScaleFactor() == double_tap_zoom_page_scale_factor_ && double_tap_zoom_page_scale_factor_ != MinimumPageScaleFactor()) || double_tap_zoom_pending_; bool scale_unchanged = fabs(PageScaleFactor() - scale) < minScaleDifference; bool should_zoom_out = block_bounds.IsEmpty() || scale_unchanged || still_at_previous_double_tap_scale; bool is_animating; if (should_zoom_out) { scale = MinimumPageScaleFactor(); IntPoint target_position = MainFrameImpl()->GetFrameView()->RootFrameToContents( point_in_root_frame); is_animating = StartPageScaleAnimation( target_position, true, scale, doubleTapZoomAnimationDurationInSeconds); } else { is_animating = StartPageScaleAnimation( scroll, false, scale, doubleTapZoomAnimationDurationInSeconds); } // TODO(dglazkov): The only reason why we're using isAnimating and not just // checking for m_layerTreeView->hasPendingPageScaleAnimation() is because of // fake page scale animation plumbing for testing, which doesn't actually // initiate a page scale animation. if (is_animating) { double_tap_zoom_page_scale_factor_ = scale; double_tap_zoom_pending_ = true; } } void WebViewImpl::ZoomToFindInPageRect(const WebRect& rect_in_root_frame) { // TODO(lukasza): https://crbug.com/734209: Add OOPIF support. if (!MainFrameImpl()) return; WebRect block_bounds = ComputeBlockBound( WebPoint(rect_in_root_frame.x + rect_in_root_frame.width / 2, rect_in_root_frame.y + rect_in_root_frame.height / 2), true); if (block_bounds.IsEmpty()) { // Keep current scale (no need to scroll as x,y will normally already // be visible). FIXME: Revisit this if it isn't always true. return; } float scale; WebPoint scroll; ComputeScaleAndScrollForBlockRect( WebPoint(rect_in_root_frame.x, rect_in_root_frame.y), block_bounds, nonUserInitiatedPointPadding, MinimumPageScaleFactor(), scale, scroll); StartPageScaleAnimation(scroll, false, scale, findInPageAnimationDurationInSeconds); } bool WebViewImpl::ZoomToMultipleTargetsRect(const WebRect& rect_in_root_frame) { // TODO(lukasza): https://crbug.com/734209: Add OOPIF support. if (!MainFrameImpl()) return false; float scale; WebPoint scroll; ComputeScaleAndScrollForBlockRect( WebPoint(rect_in_root_frame.x, rect_in_root_frame.y), rect_in_root_frame, nonUserInitiatedPointPadding, MinimumPageScaleFactor(), scale, scroll); if (scale <= PageScaleFactor()) return false; StartPageScaleAnimation(scroll, false, scale, multipleTargetsZoomAnimationDurationInSeconds); return true; } #if !defined(OS_MACOSX) // Mac has no way to open a context menu based on a keyboard event. WebInputEventResult WebViewImpl::SendContextMenuEvent() { // The contextMenuController() holds onto the last context menu that was // popped up on the page until a new one is created. We need to clear // this menu before propagating the event through the DOM so that we can // detect if we create a new menu for this event, since we won't create // a new menu if the DOM swallows the event and the defaultEventHandler does // not run. GetPage()->GetContextMenuController().ClearContextMenu(); { ContextMenuAllowedScope scope; Frame* focused_frame = GetPage()->GetFocusController().FocusedOrMainFrame(); if (!focused_frame->IsLocalFrame()) return WebInputEventResult::kNotHandled; // Firefox reveal focus based on "keydown" event but not "contextmenu" // event, we match FF. if (Element* focused_element = ToLocalFrame(focused_frame)->GetDocument()->FocusedElement()) focused_element->scrollIntoViewIfNeeded(); return ToLocalFrame(focused_frame) ->GetEventHandler() .ShowNonLocatedContextMenu(nullptr, kMenuSourceKeyboard); } } #else WebInputEventResult WebViewImpl::SendContextMenuEvent() { return WebInputEventResult::kNotHandled; } #endif void WebViewImpl::ShowContextMenuAtPoint(float x, float y, ContextMenuProvider* menu_provider) { if (!GetPage()->MainFrame()->IsLocalFrame()) return; { ContextMenuAllowedScope scope; GetPage()->GetContextMenuController().ClearContextMenu(); GetPage()->GetContextMenuController().ShowContextMenuAtPoint( GetPage()->DeprecatedLocalMainFrame(), x, y, menu_provider); } } void WebViewImpl::ShowContextMenuForElement(WebElement element) { if (!GetPage()) return; GetPage()->GetContextMenuController().ClearContextMenu(); { ContextMenuAllowedScope scope; if (LocalFrame* focused_frame = ToLocalFrame( GetPage()->GetFocusController().FocusedOrMainFrame())) { focused_frame->GetEventHandler().ShowNonLocatedContextMenu( element.Unwrap()); } } } PagePopup* WebViewImpl::OpenPagePopup(PagePopupClient* client) { DCHECK(client); if (HasOpenedPopup()) HidePopups(); DCHECK(!page_popup_); WebLocalFrameImpl* frame = WebLocalFrameImpl::FromFrame( client->OwnerElement().GetDocument().GetFrame()->LocalFrameRoot()); WebWidget* popup_widget = client_->CreatePopup(frame, kWebPopupTypePage); // CreatePopup returns nullptr if this renderer process is about to die. if (!popup_widget) return nullptr; page_popup_ = ToWebPagePopupImpl(popup_widget); if (!page_popup_->Initialize(this, client)) { page_popup_->ClosePopup(); page_popup_ = nullptr; } EnablePopupMouseWheelEventListener(frame); return page_popup_.get(); } void WebViewImpl::ClosePagePopup(PagePopup* popup) { DCHECK(popup); WebPagePopupImpl* popup_impl = ToWebPagePopupImpl(popup); DCHECK_EQ(page_popup_.get(), popup_impl); if (page_popup_.get() != popup_impl) return; page_popup_->ClosePopup(); } void WebViewImpl::CleanupPagePopup() { page_popup_ = nullptr; DisablePopupMouseWheelEventListener(); } void WebViewImpl::CancelPagePopup() { if (page_popup_) page_popup_->Cancel(); } void WebViewImpl::EnablePopupMouseWheelEventListener( WebLocalFrameImpl* local_root) { DCHECK(!popup_mouse_wheel_event_listener_); Document* document = local_root->GetDocument(); DCHECK(document); // We register an empty event listener, EmptyEventListener, so that mouse // wheel events get sent to the WebView. popup_mouse_wheel_event_listener_ = EmptyEventListener::Create(); document->addEventListener(EventTypeNames::mousewheel, popup_mouse_wheel_event_listener_, false); local_root_with_empty_mouse_wheel_listener_ = local_root; } void WebViewImpl::DisablePopupMouseWheelEventListener() { // TODO(kenrb): Concerns the same as in enablePopupMouseWheelEventListener. // See https://crbug.com/566130 DCHECK(popup_mouse_wheel_event_listener_); Document* document = local_root_with_empty_mouse_wheel_listener_->GetDocument(); DCHECK(document); // Document may have already removed the event listener, for instance, due // to a navigation, but remove it anyway. document->removeEventListener(EventTypeNames::mousewheel, popup_mouse_wheel_event_listener_.Release(), false); local_root_with_empty_mouse_wheel_listener_ = nullptr; } LocalDOMWindow* WebViewImpl::PagePopupWindow() const { return page_popup_ ? page_popup_->Window() : nullptr; } Frame* WebViewImpl::FocusedCoreFrame() const { return page_ ? page_->GetFocusController().FocusedOrMainFrame() : nullptr; } // WebWidget ------------------------------------------------------------------ void WebViewImpl::Close() { DCHECK(AllInstances().Contains(this)); AllInstances().erase(this); if (page_) { // Initiate shutdown for the entire frameset. This will cause a lot of // notifications to be sent. page_->WillBeDestroyed(); page_.Clear(); } // Reset the delegate to prevent notifications being sent as we're being // deleted. client_ = nullptr; Release(); // Balances a reference acquired in WebView::Create } WebSize WebViewImpl::Size() { return size_; } void WebViewImpl::ResizeVisualViewport(const WebSize& new_size) { GetPage()->GetVisualViewport().SetSize(new_size); GetPage()->GetVisualViewport().ClampToBoundaries(); } void WebViewImpl::UpdateICBAndResizeViewport() { // We'll keep the initial containing block size from changing when the top // controls hide so that the ICB will always be the same size as the // viewport with the browser controls shown. IntSize icb_size = size_; if (GetBrowserControls().PermittedState() == kWebBrowserControlsBoth && !GetBrowserControls().ShrinkViewport()) { icb_size.Expand(0, -GetBrowserControls().TotalHeight()); } GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(icb_size); UpdatePageDefinedViewportConstraints( MainFrameImpl()->GetFrame()->GetDocument()->GetViewportDescription()); UpdateMainFrameLayoutSize(); GetPage()->GetVisualViewport().SetSize(size_); if (MainFrameImpl()->GetFrameView()) { MainFrameImpl()->GetFrameView()->SetInitialViewportSize(icb_size); if (!MainFrameImpl()->GetFrameView()->NeedsLayout()) resize_viewport_anchor_->ResizeFrameView(MainFrameSize()); } } void WebViewImpl::UpdateBrowserControlsState(WebBrowserControlsState constraint, WebBrowserControlsState current, bool animate) { WebBrowserControlsState old_permitted_state = GetBrowserControls().PermittedState(); GetBrowserControls().UpdateConstraintsAndState(constraint, current, animate); // If the controls are going from a locked hidden to unlocked state, or vice // versa, the ICB size needs to change but we can't rely on getting a // WebViewImpl::resize since the top controls shown state may not have // changed. if ((old_permitted_state == kWebBrowserControlsHidden && constraint == kWebBrowserControlsBoth) || (old_permitted_state == kWebBrowserControlsBoth && constraint == kWebBrowserControlsHidden)) { UpdateICBAndResizeViewport(); } if (layer_tree_view_) layer_tree_view_->UpdateBrowserControlsState(constraint, current, animate); } void WebViewImpl::DidUpdateBrowserControls() { if (layer_tree_view_) { layer_tree_view_->SetBrowserControlsShownRatio( GetBrowserControls().ShownRatio()); layer_tree_view_->SetBrowserControlsHeight( GetBrowserControls().TopHeight(), GetBrowserControls().BottomHeight(), GetBrowserControls().ShrinkViewport()); } WebLocalFrameImpl* main_frame = MainFrameImpl(); if (!main_frame) return; LocalFrameView* view = main_frame->GetFrameView(); if (!view) return; VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); { // This object will save the current visual viewport offset w.r.t. the // document and restore it when the object goes out of scope. It's // needed since the browser controls adjustment will change the maximum // scroll offset and we may need to reposition them to keep the user's // apparent position unchanged. ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_); visual_viewport.SetBrowserControlsAdjustment( GetBrowserControls().UnreportedSizeAdjustment()); } } void WebViewImpl::SetOverscrollBehavior( const WebOverscrollBehavior& overscroll_behavior) { if (layer_tree_view_) layer_tree_view_->SetOverscrollBehavior(overscroll_behavior); } BrowserControls& WebViewImpl::GetBrowserControls() { return GetPage()->GetBrowserControls(); } void WebViewImpl::ResizeViewWhileAnchored(float top_controls_height, float bottom_controls_height, bool browser_controls_shrink_layout) { DCHECK(MainFrameImpl()); GetBrowserControls().SetHeight(top_controls_height, bottom_controls_height, browser_controls_shrink_layout); { // Avoids unnecessary invalidations while various bits of state in // TextAutosizer are updated. TextAutosizer::DeferUpdatePageInfo defer_update_page_info(GetPage()); LocalFrameView* frame_view = MainFrameImpl()->GetFrameView(); IntRect old_rect = frame_view->FrameRect(); UpdateICBAndResizeViewport(); IntRect new_rect = frame_view->FrameRect(); frame_view->MarkViewportConstrainedObjectsForLayout( old_rect.Width() != new_rect.Width(), old_rect.Height() != new_rect.Height()); } fullscreen_controller_->UpdateSize(); // Update lifecyle phases immediately to recalculate the minimum scale limit // for rotation anchoring, and to make sure that no lifecycle states are // stale if this WebView is embedded in another one. UpdateAllLifecyclePhases(); } void WebViewImpl::ResizeWithBrowserControls( const WebSize& new_size, float top_controls_height, float bottom_controls_height, bool browser_controls_shrink_layout) { if (should_auto_resize_) return; if (size_ == new_size && GetBrowserControls().TopHeight() == top_controls_height && GetBrowserControls().BottomHeight() == bottom_controls_height && GetBrowserControls().ShrinkViewport() == browser_controls_shrink_layout) return; if (GetPage()->MainFrame() && !GetPage()->MainFrame()->IsLocalFrame()) { // Viewport resize for a remote main frame does not require any // particular action, but the state needs to reflect the correct size // so that it can be used for initalization if the main frame gets // swapped to a LocalFrame at a later time. size_ = new_size; GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(size_); GetPage()->GetVisualViewport().SetSize(size_); GetPage()->GetBrowserControls().SetHeight(top_controls_height, bottom_controls_height, browser_controls_shrink_layout); return; } WebLocalFrameImpl* main_frame = MainFrameImpl(); if (!main_frame) return; LocalFrameView* view = main_frame->GetFrameView(); if (!view) return; VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); bool is_rotation = GetPage()->GetSettings().GetMainFrameResizesAreOrientationChanges() && size_.width && ContentsSize().Width() && new_size.width != size_.width && !fullscreen_controller_->IsFullscreenOrTransitioning(); size_ = new_size; FloatSize viewport_anchor_coords(viewportAnchorCoordX, viewportAnchorCoordY); if (is_rotation) { RotationViewportAnchor anchor(*view, visual_viewport, viewport_anchor_coords, GetPageScaleConstraintsSet()); ResizeViewWhileAnchored(top_controls_height, bottom_controls_height, browser_controls_shrink_layout); } else { ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_); ResizeViewWhileAnchored(top_controls_height, bottom_controls_height, browser_controls_shrink_layout); } SendResizeEventAndRepaint(); } void WebViewImpl::Resize(const WebSize& new_size) { if (should_auto_resize_ || size_ == new_size) return; ResizeWithBrowserControls(new_size, GetBrowserControls().TopHeight(), GetBrowserControls().BottomHeight(), GetBrowserControls().ShrinkViewport()); } void WebViewImpl::DidEnterFullscreen() { fullscreen_controller_->DidEnterFullscreen(); } void WebViewImpl::DidExitFullscreen() { fullscreen_controller_->DidExitFullscreen(); } void WebViewImpl::DidUpdateFullscreenSize() { fullscreen_controller_->UpdateSize(); } void WebViewImpl::SetSuppressFrameRequestsWorkaroundFor704763Only( bool suppress_frame_requests) { page_->Animator().SetSuppressFrameRequestsWorkaroundFor704763Only( suppress_frame_requests); } void WebViewImpl::BeginFrame(double last_frame_time_monotonic) { TRACE_EVENT1("blink", "WebViewImpl::beginFrame", "frameTime", last_frame_time_monotonic); DCHECK(last_frame_time_monotonic); if (!MainFrameImpl()) return; if (WebFrameWidgetBase* widget = MainFrameImpl()->FrameWidget()) widget->UpdateGestureAnimation(last_frame_time_monotonic); DocumentLifecycle::AllowThrottlingScope throttling_scope( MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle()); PageWidgetDelegate::Animate(*page_, last_frame_time_monotonic); if (auto* client = GetValidationMessageClient()) client->LayoutOverlay(); } void WebViewImpl::UpdateLifecycle(LifecycleUpdate requested_update) { TRACE_EVENT0("blink", "WebViewImpl::updateAllLifecyclePhases"); if (!MainFrameImpl()) return; DocumentLifecycle::AllowThrottlingScope throttling_scope( MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle()); PageWidgetDelegate::UpdateLifecycle(*page_, *MainFrameImpl()->GetFrame(), requested_update); UpdateLayerTreeBackgroundColor(); if (requested_update == LifecycleUpdate::kPrePaint) return; if (auto* client = GetValidationMessageClient()) client->PaintOverlay(); if (WebDevToolsAgentImpl* devtools = MainFrameDevToolsAgentImpl()) devtools->PaintOverlay(); if (page_color_overlay_) page_color_overlay_->GetGraphicsLayer()->Paint(nullptr); // TODO(chrishtr): link highlights don't currently paint themselves, it's // still driven by cc. Fix this. for (size_t i = 0; i < link_highlights_.size(); ++i) link_highlights_[i]->UpdateGeometry(); if (LocalFrameView* view = MainFrameImpl()->GetFrameView()) { LocalFrame* frame = MainFrameImpl()->GetFrame(); WebWidgetClient* client = WebLocalFrameImpl::FromFrame(frame)->FrameWidget()->Client(); if (should_dispatch_first_visually_non_empty_layout_ && view->IsVisuallyNonEmpty()) { should_dispatch_first_visually_non_empty_layout_ = false; // TODO(esprehn): Move users of this callback to something // better, the heuristic for "visually non-empty" is bad. client->DidMeaningfulLayout(WebMeaningfulLayout::kVisuallyNonEmpty); } if (should_dispatch_first_layout_after_finished_parsing_ && frame->GetDocument()->HasFinishedParsing()) { should_dispatch_first_layout_after_finished_parsing_ = false; client->DidMeaningfulLayout(WebMeaningfulLayout::kFinishedParsing); } if (should_dispatch_first_layout_after_finished_loading_ && frame->GetDocument()->IsLoadCompleted()) { should_dispatch_first_layout_after_finished_loading_ = false; client->DidMeaningfulLayout(WebMeaningfulLayout::kFinishedLoading); } } } void WebViewImpl::Paint(WebCanvas* canvas, const WebRect& rect) { // This should only be used when compositing is not being used for this // WebView, and it is painting into the recording of its parent. DCHECK(!IsAcceleratedCompositingActive()); PageWidgetDelegate::Paint(*page_, canvas, rect, *page_->DeprecatedLocalMainFrame()); } #if defined(OS_ANDROID) void WebViewImpl::PaintIgnoringCompositing(WebCanvas* canvas, const WebRect& rect) { // This is called on a composited WebViewImpl, but we will ignore it, // producing all possible content of the WebViewImpl into the WebCanvas. DCHECK(IsAcceleratedCompositingActive()); PageWidgetDelegate::PaintIgnoringCompositing( *page_, canvas, rect, *page_->DeprecatedLocalMainFrame()); } #endif void WebViewImpl::LayoutAndPaintAsync( WebLayoutAndPaintAsyncCallback* callback) { layer_tree_view_->LayoutAndPaintAsync(callback); } void WebViewImpl::CompositeAndReadbackAsync( WebCompositeAndReadbackAsyncCallback* callback) { layer_tree_view_->CompositeAndReadbackAsync(callback); } void WebViewImpl::ThemeChanged() { if (!GetPage()) return; if (!GetPage()->MainFrame()->IsLocalFrame()) return; LocalFrameView* view = GetPage()->DeprecatedLocalMainFrame()->View(); WebRect damaged_rect(0, 0, size_.width, size_.height); view->InvalidateRect(damaged_rect); } void WebViewImpl::EnterFullscreen(LocalFrame& frame) { fullscreen_controller_->EnterFullscreen(frame); } void WebViewImpl::ExitFullscreen(LocalFrame& frame) { fullscreen_controller_->ExitFullscreen(frame); } void WebViewImpl::FullscreenElementChanged(Element* old_element, Element* new_element) { fullscreen_controller_->FullscreenElementChanged(old_element, new_element); } bool WebViewImpl::HasHorizontalScrollbar() { return MainFrameImpl() ->GetFrameView() ->LayoutViewportScrollableArea() ->HorizontalScrollbar(); } bool WebViewImpl::HasVerticalScrollbar() { return MainFrameImpl() ->GetFrameView() ->LayoutViewportScrollableArea() ->VerticalScrollbar(); } WebInputEventResult WebViewImpl::DispatchBufferedTouchEvents() { if (!MainFrameImpl()) return WebInputEventResult::kNotHandled; if (WebDevToolsAgentImpl* devtools = MainFrameDevToolsAgentImpl()) devtools->DispatchBufferedTouchEvents(); return MainFrameImpl() ->GetFrame() ->GetEventHandler() .DispatchBufferedTouchEvents(); } WebInputEventResult WebViewImpl::HandleInputEvent( const WebCoalescedInputEvent& coalesced_event) { return HandleInputEventIncludingTouch(coalesced_event); } WebInputEventResult WebViewImpl::HandleInputEventInternal( const WebCoalescedInputEvent& coalesced_event) { const WebInputEvent& input_event = coalesced_event.Event(); // TODO(dcheng): The fact that this is getting called when there is no local // main frame is problematic and probably indicates a bug in the input event // routing code. if (!MainFrameImpl()) return WebInputEventResult::kNotHandled; DCHECK(!WebInputEvent::IsTouchEventType(input_event.GetType())); GetPage()->GetVisualViewport().StartTrackingPinchStats(); TRACE_EVENT1("input,rail", "WebViewImpl::handleInputEvent", "type", WebInputEvent::GetName(input_event.GetType())); // If a drag-and-drop operation is in progress, ignore input events. if (MainFrameImpl()->FrameWidget()->DoingDragAndDrop()) return WebInputEventResult::kHandledSuppressed; if (dev_tools_emulator_->HandleInputEvent(input_event)) return WebInputEventResult::kHandledSuppressed; if (WebDevToolsAgentImpl* devtools = MainFrameDevToolsAgentImpl()) { if (devtools->HandleInputEvent(input_event)) return WebInputEventResult::kHandledSuppressed; } // Report the event to be NOT processed by WebKit, so that the browser can // handle it appropriately. if (WebFrameWidgetBase::IgnoreInputEvents()) return WebInputEventResult::kNotHandled; AutoReset current_event_change( &CurrentInputEvent::current_input_event_, &input_event); UIEventWithKeyState::ClearNewTabModifierSetFromIsolatedWorld(); bool is_pointer_locked = false; if (WebFrameWidgetBase* widget = MainFrameImpl()->FrameWidget()) { if (WebWidgetClient* client = widget->Client()) is_pointer_locked = client->IsPointerLocked(); } if (is_pointer_locked && WebInputEvent::IsMouseEventType(input_event.GetType())) { MainFrameImpl()->FrameWidget()->PointerLockMouseEvent(coalesced_event); return WebInputEventResult::kHandledSystem; } Document& main_frame_document = *MainFrameImpl()->GetFrame()->GetDocument(); if (input_event.GetType() != WebInputEvent::kMouseMove) { FirstMeaningfulPaintDetector::From(main_frame_document).NotifyInputEvent(); } if (input_event.GetType() != WebInputEvent::kMouseMove && input_event.GetType() != WebInputEvent::kMouseEnter && input_event.GetType() != WebInputEvent::kMouseLeave) { InteractiveDetector* interactive_detector( InteractiveDetector::From(main_frame_document)); if (interactive_detector) { interactive_detector->OnInvalidatingInputEvent( input_event.TimeStampSeconds()); } } if (mouse_capture_node_ && WebInputEvent::IsMouseEventType(input_event.GetType())) { TRACE_EVENT1("input", "captured mouse event", "type", input_event.GetType()); // Save m_mouseCaptureNode since mouseCaptureLost() will clear it. Node* node = mouse_capture_node_; // Not all platforms call mouseCaptureLost() directly. if (input_event.GetType() == WebInputEvent::kMouseUp) MouseCaptureLost(); std::unique_ptr gesture_indicator; AtomicString event_type; switch (input_event.GetType()) { case WebInputEvent::kMouseEnter: event_type = EventTypeNames::mouseover; break; case WebInputEvent::kMouseMove: event_type = EventTypeNames::mousemove; break; case WebInputEvent::kMouseLeave: event_type = EventTypeNames::mouseout; break; case WebInputEvent::kMouseDown: event_type = EventTypeNames::mousedown; gesture_indicator = Frame::NotifyUserActivation( node->GetDocument().GetFrame(), UserGestureToken::kNewGesture); mouse_capture_gesture_token_ = gesture_indicator->CurrentToken(); break; case WebInputEvent::kMouseUp: event_type = EventTypeNames::mouseup; gesture_indicator = WTF::WrapUnique( new UserGestureIndicator(std::move(mouse_capture_gesture_token_))); break; default: NOTREACHED(); } WebMouseEvent transformed_event = TransformWebMouseEvent(MainFrameImpl()->GetFrameView(), static_cast(input_event)); node->DispatchMouseEvent(transformed_event, event_type, transformed_event.click_count); return WebInputEventResult::kHandledSystem; } // FIXME: This should take in the intended frame, not the local frame root. WebInputEventResult result = PageWidgetDelegate::HandleInputEvent( *this, coalesced_event, MainFrameImpl()->GetFrame()); if (result != WebInputEventResult::kNotHandled) return result; // Unhandled pinch events should adjust the scale. if (input_event.GetType() == WebInputEvent::kGesturePinchUpdate) { const WebGestureEvent& pinch_event = static_cast(input_event); // For touchpad gestures synthesize a Windows-like wheel event // to send to any handlers that may exist. Not necessary for touchscreen // as touch events would have already been sent for the gesture. if (pinch_event.source_device == kWebGestureDeviceTouchpad) { result = HandleSyntheticWheelFromTouchpadPinchEvent(pinch_event); if (result != WebInputEventResult::kNotHandled) return result; } if (pinch_event.data.pinch_update.zoom_disabled) return WebInputEventResult::kNotHandled; if (GetPage()->GetVisualViewport().MagnifyScaleAroundAnchor( pinch_event.data.pinch_update.scale, FloatPoint(pinch_event.x, pinch_event.y))) return WebInputEventResult::kHandledSystem; } // If the page is close to minimum scale at pinch end, snap to minimum. if (input_event.GetType() == WebInputEvent::kGesturePinchEnd) { const WebGestureEvent& pinch_event = static_cast(input_event); float min_scale = MinimumPageScaleFactor(); if (pinch_event.source_device == kWebGestureDeviceTouchpad && PageScaleFactor() <= min_scale * maximumZoomForSnapToMinimum) { IntPoint target_position = MainFrameImpl()->GetFrameView()->ViewportToContents( IntPoint(pinch_event.x, pinch_event.y)); StartPageScaleAnimation(target_position, true, min_scale, snapToMiminimumZoomAnimationDurationInSeconds); } } return WebInputEventResult::kNotHandled; } void WebViewImpl::SetCursorVisibilityState(bool is_visible) { if (page_) page_->SetIsCursorVisible(is_visible); } void WebViewImpl::MouseCaptureLost() { TRACE_EVENT_ASYNC_END0("input", "capturing mouse", this); mouse_capture_node_ = nullptr; } void WebViewImpl::SetFocus(bool enable) { page_->GetFocusController().SetFocused(enable); if (enable) { page_->GetFocusController().SetActive(true); LocalFrame* focused_frame = page_->GetFocusController().FocusedFrame(); if (focused_frame) { Element* element = focused_frame->GetDocument()->FocusedElement(); if (element && focused_frame->Selection() .ComputeVisibleSelectionInDOMTreeDeprecated() .IsNone()) { // If the selection was cleared while the WebView was not // focused, then the focus element shows with a focus ring but // no caret and does respond to keyboard inputs. focused_frame->GetDocument()->UpdateStyleAndLayoutTree(); if (element->IsTextControl()) { element->UpdateFocusAppearance(SelectionBehaviorOnFocus::kRestore); } else if (HasEditableStyle(*element)) { // updateFocusAppearance() selects all the text of // contentseditable DIVs. So we set the selection explicitly // instead. Note that this has the side effect of moving the // caret back to the beginning of the text. Position position(element, 0); focused_frame->Selection().SetSelectionAndEndTyping( SelectionInDOMTree::Builder().Collapse(position).Build()); } } } ime_accept_events_ = true; } else { HidePopups(); // Clear focus on the currently focused frame if any. if (!page_) return; LocalFrame* frame = page_->MainFrame() && page_->MainFrame()->IsLocalFrame() ? page_->DeprecatedLocalMainFrame() : nullptr; if (!frame) return; LocalFrame* focused_frame = FocusedLocalFrameInWidget(); if (focused_frame) { // Finish an ongoing composition to delete the composition node. if (focused_frame->GetInputMethodController().HasComposition()) { // TODO(editing-dev): The use of // updateStyleAndLayoutIgnorePendingStylesheets needs to be audited. // See http://crbug.com/590369 for more details. focused_frame->GetDocument() ->UpdateStyleAndLayoutIgnorePendingStylesheets(); focused_frame->GetInputMethodController().FinishComposingText( InputMethodController::kKeepSelection); } ime_accept_events_ = false; } } } bool WebViewImpl::SelectionBounds(WebRect& anchor_web, WebRect& focus_web) const { const Frame* frame = FocusedCoreFrame(); if (!frame || !frame->IsLocalFrame()) return false; const LocalFrame* local_frame = ToLocalFrame(frame); if (!local_frame) return false; LocalFrameView* frame_view = local_frame->View(); if (!frame_view) return false; IntRect anchor; IntRect focus; if (!local_frame->Selection().ComputeAbsoluteBounds(anchor, focus)) return false; VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); anchor_web = visual_viewport.RootFrameToViewport( frame_view->AbsoluteToRootFrame(anchor)); focus_web = visual_viewport.RootFrameToViewport( frame_view->AbsoluteToRootFrame(focus)); return true; } WebColor WebViewImpl::BackgroundColor() const { if (background_color_override_enabled_) return background_color_override_; if (!page_) return BaseBackgroundColor().Rgb(); if (!page_->MainFrame()) return BaseBackgroundColor().Rgb(); if (!page_->MainFrame()->IsLocalFrame()) return BaseBackgroundColor().Rgb(); LocalFrameView* view = page_->DeprecatedLocalMainFrame()->View(); if (!view) return BaseBackgroundColor().Rgb(); return view->DocumentBackgroundColor().Rgb(); } WebPagePopupImpl* WebViewImpl::GetPagePopup() const { return page_popup_.get(); } bool WebViewImpl::IsAcceleratedCompositingActive() const { return root_layer_; } void WebViewImpl::WillCloseLayerTreeView() { if (link_highlights_timeline_) { link_highlights_.clear(); DetachCompositorAnimationTimeline(link_highlights_timeline_.get()); link_highlights_timeline_.reset(); } if (layer_tree_view_) GetPage()->WillCloseLayerTreeView(*layer_tree_view_, nullptr); SetRootLayer(nullptr); animation_host_ = nullptr; mutator_ = nullptr; layer_tree_view_ = nullptr; } void WebViewImpl::DidAcquirePointerLock() { if (MainFrameImpl()) MainFrameImpl()->FrameWidget()->DidAcquirePointerLock(); } void WebViewImpl::DidNotAcquirePointerLock() { if (MainFrameImpl()) MainFrameImpl()->FrameWidget()->DidNotAcquirePointerLock(); } void WebViewImpl::DidLosePointerLock() { // Make sure that the main frame wasn't swapped-out when the pointer lock is // lost. There's a race that can happen when a pointer lock is requested, but // the browser swaps out the main frame while the pointer lock request is in // progress. This won't be needed once the main frame is refactored to not use // the WebViewImpl as its WebWidget. if (MainFrameImpl()) MainFrameImpl()->FrameWidget()->DidLosePointerLock(); } // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as // well. This code needs to be refactored (http://crbug.com/629721). bool WebViewImpl::GetCompositionCharacterBounds(WebVector& bounds) { WebInputMethodController* controller = GetActiveWebInputMethodController(); if (!controller) return false; WebRange range = controller->CompositionRange(); if (range.IsEmpty()) return false; WebLocalFrame* frame = FocusedFrame(); // Only consider frames whose local root is the main frame. For other // local frames which have different local roots, the corresponding // WebFrameWidget will handle this task. if (frame->LocalRoot() != MainFrameImpl()) return false; size_t character_count = range.length(); size_t offset = range.StartOffset(); WebVector result(character_count); WebRect webrect; for (size_t i = 0; i < character_count; ++i) { if (!frame->FirstRectForCharacterRange(offset + i, 1, webrect)) { DLOG(ERROR) << "Could not retrieve character rectangle at " << i; return false; } result[i] = webrect; } bounds.Swap(result); return true; } // WebView -------------------------------------------------------------------- WebSettingsImpl* WebViewImpl::SettingsImpl() { if (!web_settings_) { web_settings_ = WTF::WrapUnique( new WebSettingsImpl(&page_->GetSettings(), dev_tools_emulator_.Get())); } DCHECK(web_settings_); return web_settings_.get(); } WebSettings* WebViewImpl::GetSettings() { return SettingsImpl(); } WebString WebViewImpl::PageEncoding() const { if (!page_) return WebString(); if (!page_->MainFrame()->IsLocalFrame()) return WebString(); // FIXME: Is this check needed? if (!page_->DeprecatedLocalMainFrame()->GetDocument()->Loader()) return WebString(); return page_->DeprecatedLocalMainFrame()->GetDocument()->EncodingName(); } WebFrame* WebViewImpl::MainFrame() { return WebFrame::FromFrame(page_ ? page_->MainFrame() : nullptr); } WebLocalFrame* WebViewImpl::FocusedFrame() { Frame* frame = FocusedCoreFrame(); // TODO(yabinh): focusedCoreFrame() should always return a local frame, and // the following check should be unnecessary. // See crbug.com/625068 if (!frame || !frame->IsLocalFrame()) return nullptr; return WebLocalFrameImpl::FromFrame(ToLocalFrame(frame)); } void WebViewImpl::SetFocusedFrame(WebFrame* frame) { if (!frame) { // Clears the focused frame if any. Frame* focused_frame = FocusedCoreFrame(); if (focused_frame && focused_frame->IsLocalFrame()) ToLocalFrame(focused_frame)->Selection().SetFrameIsFocused(false); return; } LocalFrame* core_frame = ToWebLocalFrameImpl(frame)->GetFrame(); core_frame->GetPage()->GetFocusController().SetFocusedFrame(core_frame); } void WebViewImpl::FocusDocumentView(WebFrame* frame) { // This is currently only used when replicating focus changes for // cross-process frames, and |notifyEmbedder| is disabled to avoid sending // duplicate frameFocused updates from FocusController to the browser // process, which already knows the latest focused frame. GetPage()->GetFocusController().FocusDocumentView( WebFrame::ToCoreFrame(*frame), false /* notifyEmbedder */); } void WebViewImpl::SetInitialFocus(bool reverse) { if (!page_) return; Frame* frame = GetPage()->GetFocusController().FocusedOrMainFrame(); if (frame->IsLocalFrame()) { if (Document* document = ToLocalFrame(frame)->GetDocument()) document->ClearFocusedElement(); } GetPage()->GetFocusController().SetInitialFocus( reverse ? kWebFocusTypeBackward : kWebFocusTypeForward); } void WebViewImpl::ClearFocusedElement() { Frame* frame = FocusedCoreFrame(); if (!frame || !frame->IsLocalFrame()) return; LocalFrame* local_frame = ToLocalFrame(frame); Document* document = local_frame->GetDocument(); if (!document) return; Element* old_focused_element = document->FocusedElement(); document->ClearFocusedElement(); if (!old_focused_element) return; // If a text field has focus, we need to make sure the selection controller // knows to remove selection from it. Otherwise, the text field is still // processing keyboard events even though focus has been moved to the page and // keystrokes get eaten as a result. document->UpdateStyleAndLayoutTree(); if (HasEditableStyle(*old_focused_element) || old_focused_element->IsTextControl()) local_frame->Selection().Clear(); } // TODO(dglazkov): Remove and replace with Node:hasEditableStyle. // http://crbug.com/612560 static bool IsElementEditable(const Element* element) { element->GetDocument().UpdateStyleAndLayoutTree(); if (HasEditableStyle(*element)) return true; if (element->IsTextControl()) { if (!ToTextControlElement(element)->IsDisabledOrReadOnly()) return true; } return EqualIgnoringASCIICase(element->getAttribute(HTMLNames::roleAttr), "textbox"); } bool WebViewImpl::ScrollFocusedEditableElementIntoView() { Frame* frame = GetPage()->MainFrame(); if (!frame) return false; Element* element = FocusedElement(); if (!element || !IsElementEditable(element)) return false; if (frame->IsRemoteFrame()) { // The rest of the logic here is not implemented for OOPIFs. For now instead // of implementing the logic below (which involves finding the scale and // scrolling point), editable elements inside OOPIFs are scrolled into view // instead. However, a common logic should be implemented for both OOPIFs // and in-process frames to assure a consistent behavior across all frame // types (https://crbug.com/784982). LayoutObject* layout_object = element->GetLayoutObject(); if (!layout_object) return false; layout_object->ScrollRectToVisible(element->BoundingBox(), WebScrollIntoViewParams()); return true; } if (!frame->View()) return false; element->GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); bool zoom_in_to_legible_scale = web_settings_->AutoZoomFocusedNodeToLegibleScale() && !GetPage()->GetVisualViewport().ShouldDisableDesktopWorkarounds(); if (zoom_in_to_legible_scale) { // When deciding whether to zoom in on a focused text box, we should // decide not to zoom in if the user won't be able to zoom out. e.g if the // textbox is within a touch-action: none container the user can't zoom // back out. TouchAction action = TouchActionUtil::ComputeEffectiveTouchAction(*element); if (!(action & TouchAction::kTouchActionPinchZoom)) zoom_in_to_legible_scale = false; } float scale; IntPoint scroll; bool need_animation; ComputeScaleAndScrollForFocusedNode(element, zoom_in_to_legible_scale, scale, scroll, need_animation); if (need_animation) { StartPageScaleAnimation(scroll, false, scale, scrollAndScaleAnimationDurationInSeconds); } return true; } void WebViewImpl::SmoothScroll(int target_x, int target_y, long duration_ms) { IntPoint target_position(target_x, target_y); StartPageScaleAnimation(target_position, false, PageScaleFactor(), (double)duration_ms / 1000); } void WebViewImpl::ComputeScaleAndScrollForFocusedNode( Node* focused_node, bool zoom_in_to_legible_scale, float& new_scale, IntPoint& new_scroll, bool& need_animation) { VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); LocalFrameView* main_frame_view = MainFrameImpl()->GetFrameView(); WebRect caret_in_viewport, unused_end; SelectionBounds(caret_in_viewport, unused_end); // 'caretInDocument' is rect encompassing the blinking cursor relative to the // root document. IntRect caret_in_document = main_frame_view->RootFrameToDocument( visual_viewport.ViewportToRootFrame(caret_in_viewport)); LocalFrameView* textbox_view = focused_node->GetDocument().View(); IntRect textbox_rect_in_document = main_frame_view->RootFrameToDocument(textbox_view->AbsoluteToRootFrame( PixelSnappedIntRect(focused_node->Node::BoundingBox()))); if (!zoom_in_to_legible_scale) { new_scale = PageScaleFactor(); } else { // Pick a scale which is reasonably readable. This is the scale at which // the caret height will become minReadableCaretHeightForNode (adjusted // for dpi and font scale factor). const int min_readable_caret_height_for_node = textbox_rect_in_document.Height() >= 2 * caret_in_document.Height() ? minReadableCaretHeightForTextArea : minReadableCaretHeight; new_scale = ClampPageScaleFactorToLimits( MaximumLegiblePageScale() * min_readable_caret_height_for_node / caret_in_document.Height()); new_scale = std::max(new_scale, PageScaleFactor()); } const float delta_scale = new_scale / PageScaleFactor(); need_animation = false; // If we are at less than the target zoom level, zoom in. if (delta_scale > minScaleChangeToTriggerZoom) need_animation = true; else new_scale = PageScaleFactor(); // If the caret is offscreen, then animate. if (!visual_viewport.VisibleRectInDocument().Contains(caret_in_document)) need_animation = true; // If the box is partially offscreen and it's possible to bring it fully // onscreen, then animate. if (visual_viewport.VisibleRect().Width() >= textbox_rect_in_document.Width() && visual_viewport.VisibleRect().Height() >= textbox_rect_in_document.Height() && !visual_viewport.VisibleRectInDocument().Contains( textbox_rect_in_document)) need_animation = true; if (!need_animation) return; FloatSize target_viewport_size(visual_viewport.Size()); target_viewport_size.Scale(1 / new_scale); if (textbox_rect_in_document.Width() <= target_viewport_size.Width()) { // Field is narrower than screen. Try to leave padding on left so field's // label is visible, but it's more important to ensure entire field is // onscreen. int ideal_left_padding = target_viewport_size.Width() * leftBoxRatio; int max_left_padding_keeping_box_onscreen = target_viewport_size.Width() - textbox_rect_in_document.Width(); new_scroll.SetX(textbox_rect_in_document.X() - std::min(ideal_left_padding, max_left_padding_keeping_box_onscreen)); } else { // Field is wider than screen. Try to left-align field, unless caret would // be offscreen, in which case right-align the caret. new_scroll.SetX(std::max(textbox_rect_in_document.X(), caret_in_document.X() + caret_in_document.Width() + caretPadding - target_viewport_size.Width())); } if (textbox_rect_in_document.Height() <= target_viewport_size.Height()) { // Field is shorter than screen. Vertically center it. new_scroll.SetY( textbox_rect_in_document.Y() - (target_viewport_size.Height() - textbox_rect_in_document.Height()) / 2); } else { // Field is taller than screen. Try to top align field, unless caret would // be offscreen, in which case bottom-align the caret. new_scroll.SetY( std::max(textbox_rect_in_document.Y(), caret_in_document.Y() + caret_in_document.Height() + caretPadding - target_viewport_size.Height())); } } void WebViewImpl::AdvanceFocus(bool reverse) { GetPage()->GetFocusController().AdvanceFocus(reverse ? kWebFocusTypeBackward : kWebFocusTypeForward); } void WebViewImpl::AdvanceFocusAcrossFrames(WebFocusType type, WebRemoteFrame* from, WebLocalFrame* to) { // TODO(alexmos): Pass in proper with sourceCapabilities. GetPage()->GetFocusController().AdvanceFocusAcrossFrames( type, ToWebRemoteFrameImpl(from)->GetFrame(), ToWebLocalFrameImpl(to)->GetFrame()); } double WebViewImpl::ZoomLevel() { return zoom_level_; } void WebViewImpl::PropagateZoomFactorToLocalFrameRoots(Frame* frame, float zoom_factor) { if (frame->IsLocalRoot()) { LocalFrame* local_frame = ToLocalFrame(frame); if (Document* document = local_frame->GetDocument()) { if (!document->IsPluginDocument() || !ToPluginDocument(document)->GetPluginView()) { local_frame->SetPageZoomFactor(zoom_factor); } } } for (Frame* child = frame->Tree().FirstChild(); child; child = child->Tree().NextSibling()) PropagateZoomFactorToLocalFrameRoots(child, zoom_factor); } double WebViewImpl::SetZoomLevel(double zoom_level) { if (zoom_level < minimum_zoom_level_) zoom_level_ = minimum_zoom_level_; else if (zoom_level > maximum_zoom_level_) zoom_level_ = maximum_zoom_level_; else zoom_level_ = zoom_level; float zoom_factor = zoom_factor_override_ ? zoom_factor_override_ : static_cast(ZoomLevelToZoomFactor(zoom_level_)); if (zoom_factor_for_device_scale_factor_) { if (compositor_device_scale_factor_override_) { // Adjust the page's DSF so that DevicePixelRatio becomes // m_zoomFactorForDeviceScaleFactor. GetPage()->SetDeviceScaleFactorDeprecated( zoom_factor_for_device_scale_factor_ / compositor_device_scale_factor_override_); zoom_factor *= compositor_device_scale_factor_override_; } else { GetPage()->SetDeviceScaleFactorDeprecated(1.f); zoom_factor *= zoom_factor_for_device_scale_factor_; } } PropagateZoomFactorToLocalFrameRoots(page_->MainFrame(), zoom_factor); return zoom_level_; } void WebViewImpl::ZoomLimitsChanged(double minimum_zoom_level, double maximum_zoom_level) { minimum_zoom_level_ = minimum_zoom_level; maximum_zoom_level_ = maximum_zoom_level; client_->ZoomLimitsChanged(minimum_zoom_level_, maximum_zoom_level_); } float WebViewImpl::TextZoomFactor() { return MainFrameImpl()->GetFrame()->TextZoomFactor(); } float WebViewImpl::SetTextZoomFactor(float text_zoom_factor) { LocalFrame* frame = MainFrameImpl()->GetFrame(); if (frame->GetWebPluginContainer()) return 1; frame->SetTextZoomFactor(text_zoom_factor); return text_zoom_factor; } double WebView::ZoomLevelToZoomFactor(double zoom_level) { return pow(kTextSizeMultiplierRatio, zoom_level); } double WebView::ZoomFactorToZoomLevel(double factor) { // Since factor = 1.2^level, level = log(factor) / log(1.2) return log(factor) / log(kTextSizeMultiplierRatio); } float WebViewImpl::PageScaleFactor() const { if (!GetPage()) return 1; return GetPage()->GetVisualViewport().Scale(); } float WebViewImpl::ClampPageScaleFactorToLimits(float scale_factor) const { return GetPageScaleConstraintsSet().FinalConstraints().ClampToConstraints( scale_factor); } void WebViewImpl::SetVisualViewportOffset(const WebFloatPoint& offset) { DCHECK(GetPage()); GetPage()->GetVisualViewport().SetLocation(offset); } WebFloatPoint WebViewImpl::VisualViewportOffset() const { DCHECK(GetPage()); return GetPage()->GetVisualViewport().VisibleRect().Location(); } WebFloatSize WebViewImpl::VisualViewportSize() const { DCHECK(GetPage()); return GetPage()->GetVisualViewport().VisibleRect().Size(); } void WebViewImpl::ScrollAndRescaleViewports( float scale_factor, const IntPoint& main_frame_origin, const FloatPoint& visual_viewport_origin) { if (!GetPage()) return; if (!MainFrameImpl()) return; LocalFrameView* view = MainFrameImpl()->GetFrameView(); if (!view) return; // Order is important: visual viewport location is clamped based on // main frame scroll position and visual viewport scale. view->SetScrollOffset(ToScrollOffset(main_frame_origin), kProgrammaticScroll); SetPageScaleFactor(scale_factor); GetPage()->GetVisualViewport().SetLocation(visual_viewport_origin); } void WebViewImpl::SetPageScaleFactorAndLocation(float scale_factor, const FloatPoint& location) { DCHECK(GetPage()); GetPage()->GetVisualViewport().SetScaleAndLocation( ClampPageScaleFactorToLimits(scale_factor), location); } void WebViewImpl::SetPageScaleFactor(float scale_factor) { DCHECK(GetPage()); scale_factor = ClampPageScaleFactorToLimits(scale_factor); if (scale_factor == PageScaleFactor()) return; GetPage()->GetVisualViewport().SetScale(scale_factor); } void WebViewImpl::SetDeviceScaleFactor(float scale_factor) { if (!GetPage()) return; if (GetPage()->DeviceScaleFactorDeprecated() == scale_factor) return; GetPage()->SetDeviceScaleFactorDeprecated(scale_factor); if (layer_tree_view_) UpdateLayerTreeDeviceScaleFactor(); } void WebViewImpl::SetZoomFactorForDeviceScaleFactor( float zoom_factor_for_device_scale_factor) { if (zoom_factor_for_device_scale_factor_ == zoom_factor_for_device_scale_factor) { return; } zoom_factor_for_device_scale_factor_ = zoom_factor_for_device_scale_factor; if (!layer_tree_view_) return; SetZoomLevel(zoom_level_); } void WebViewImpl::EnableAutoResizeMode(const WebSize& min_size, const WebSize& max_size) { should_auto_resize_ = true; min_auto_size_ = min_size; max_auto_size_ = max_size; ConfigureAutoResizeMode(); } void WebViewImpl::DisableAutoResizeMode() { should_auto_resize_ = false; ConfigureAutoResizeMode(); } void WebViewImpl::SetDefaultPageScaleLimits(float min_scale, float max_scale) { return GetPage()->SetDefaultPageScaleLimits(min_scale, max_scale); } void WebViewImpl::SetInitialPageScaleOverride( float initial_page_scale_factor_override) { PageScaleConstraints constraints = GetPageScaleConstraintsSet().UserAgentConstraints(); constraints.initial_scale = initial_page_scale_factor_override; if (constraints == GetPageScaleConstraintsSet().UserAgentConstraints()) return; GetPageScaleConstraintsSet().SetNeedsReset(true); GetPage()->SetUserAgentPageScaleConstraints(constraints); } void WebViewImpl::SetMaximumLegibleScale(float maximum_legible_scale) { maximum_legible_scale_ = maximum_legible_scale; } void WebViewImpl::SetIgnoreViewportTagScaleLimits(bool ignore) { PageScaleConstraints constraints = GetPageScaleConstraintsSet().UserAgentConstraints(); if (ignore) { constraints.minimum_scale = GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale; constraints.maximum_scale = GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale; } else { constraints.minimum_scale = -1; constraints.maximum_scale = -1; } GetPage()->SetUserAgentPageScaleConstraints(constraints); } IntSize WebViewImpl::MainFrameSize() { // The frame size should match the viewport size at minimum scale, since the // viewport must always be contained by the frame. FloatSize frame_size(size_); frame_size.Scale(1 / MinimumPageScaleFactor()); return ExpandedIntSize(frame_size); } PageScaleConstraintsSet& WebViewImpl::GetPageScaleConstraintsSet() const { return GetPage()->GetPageScaleConstraintsSet(); } void WebViewImpl::RefreshPageScaleFactorAfterLayout() { if (!MainFrame() || !GetPage() || !GetPage()->MainFrame() || !GetPage()->MainFrame()->IsLocalFrame() || !GetPage()->DeprecatedLocalMainFrame()->View()) return; LocalFrameView* view = GetPage()->DeprecatedLocalMainFrame()->View(); UpdatePageDefinedViewportConstraints( MainFrameImpl()->GetFrame()->GetDocument()->GetViewportDescription()); GetPageScaleConstraintsSet().ComputeFinalConstraints(); int vertical_scrollbar_width = 0; if (view->VerticalScrollbar() && !view->VerticalScrollbar()->IsOverlayScrollbar()) vertical_scrollbar_width = view->VerticalScrollbar()->Width(); GetPageScaleConstraintsSet().AdjustFinalConstraintsToContentsSize( ContentsSize(), vertical_scrollbar_width, GetSettings()->ShrinksViewportContentToFit()); float new_page_scale_factor = PageScaleFactor(); if (GetPageScaleConstraintsSet().NeedsReset() && GetPageScaleConstraintsSet().FinalConstraints().initial_scale != -1) { new_page_scale_factor = GetPageScaleConstraintsSet().FinalConstraints().initial_scale; GetPageScaleConstraintsSet().SetNeedsReset(false); } SetPageScaleFactor(new_page_scale_factor); UpdateLayerTreeViewport(); } void WebViewImpl::UpdatePageDefinedViewportConstraints( const ViewportDescription& description) { if (!GetPage() || (!size_.width && !size_.height) || !GetPage()->MainFrame()->IsLocalFrame()) return; if (!GetSettings()->ViewportEnabled()) { GetPageScaleConstraintsSet().ClearPageDefinedConstraints(); UpdateMainFrameLayoutSize(); // If we don't support mobile viewports, allow GPU rasterization. matches_heuristics_for_gpu_rasterization_ = true; if (layer_tree_view_) { layer_tree_view_->HeuristicsForGpuRasterizationUpdated( matches_heuristics_for_gpu_rasterization_); } return; } Document* document = GetPage()->DeprecatedLocalMainFrame()->GetDocument(); matches_heuristics_for_gpu_rasterization_ = description.MatchesHeuristicsForGpuRasterization(); if (layer_tree_view_) { layer_tree_view_->HeuristicsForGpuRasterizationUpdated( matches_heuristics_for_gpu_rasterization_); } Length default_min_width = document->ViewportDefaultMinWidth(); if (default_min_width.IsAuto()) default_min_width = Length(kExtendToZoom); ViewportDescription adjusted_description = description; if (SettingsImpl()->ViewportMetaLayoutSizeQuirk() && adjusted_description.type == ViewportDescription::kViewportMeta) { const int kLegacyWidthSnappingMagicNumber = 320; if (adjusted_description.max_width.IsFixed() && adjusted_description.max_width.Value() <= kLegacyWidthSnappingMagicNumber) adjusted_description.max_width = Length(kDeviceWidth); if (adjusted_description.max_height.IsFixed() && adjusted_description.max_height.Value() <= size_.height) adjusted_description.max_height = Length(kDeviceHeight); adjusted_description.min_width = adjusted_description.max_width; adjusted_description.min_height = adjusted_description.max_height; } float old_initial_scale = GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale; GetPageScaleConstraintsSet().UpdatePageDefinedConstraints( adjusted_description, default_min_width); if (SettingsImpl()->ClobberUserAgentInitialScaleQuirk() && GetPageScaleConstraintsSet().UserAgentConstraints().initial_scale != -1 && GetPageScaleConstraintsSet().UserAgentConstraints().initial_scale * DeviceScaleFactor() <= 1) { if (description.max_width == Length(kDeviceWidth) || (description.max_width.GetType() == kAuto && GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale == 1.0f)) SetInitialPageScaleOverride(-1); } Settings& page_settings = GetPage()->GetSettings(); GetPageScaleConstraintsSet().AdjustForAndroidWebViewQuirks( adjusted_description, default_min_width.IntValue(), DeviceScaleFactor(), SettingsImpl()->SupportDeprecatedTargetDensityDPI(), page_settings.GetWideViewportQuirkEnabled(), page_settings.GetUseWideViewport(), page_settings.GetLoadWithOverviewMode(), SettingsImpl()->ViewportMetaNonUserScalableQuirk()); float new_initial_scale = GetPageScaleConstraintsSet().PageDefinedConstraints().initial_scale; if (old_initial_scale != new_initial_scale && new_initial_scale != -1) { GetPageScaleConstraintsSet().SetNeedsReset(true); if (MainFrameImpl() && MainFrameImpl()->GetFrameView()) MainFrameImpl()->GetFrameView()->SetNeedsLayout(); } if (LocalFrame* frame = GetPage()->DeprecatedLocalMainFrame()) { if (TextAutosizer* text_autosizer = frame->GetDocument()->GetTextAutosizer()) text_autosizer->UpdatePageInfoInAllFrames(); } UpdateMainFrameLayoutSize(); } void WebViewImpl::UpdateMainFrameLayoutSize() { if (should_auto_resize_ || !MainFrameImpl()) return; LocalFrameView* view = MainFrameImpl()->GetFrameView(); if (!view) return; WebSize layout_size = size_; if (GetSettings()->ViewportEnabled()) layout_size = GetPageScaleConstraintsSet().GetLayoutSize(); if (GetPage()->GetSettings().GetForceZeroLayoutHeight()) layout_size.height = 0; view->SetLayoutSize(layout_size); } IntSize WebViewImpl::ContentsSize() const { if (!GetPage()->MainFrame()->IsLocalFrame()) return IntSize(); auto* layout_view = GetPage()->DeprecatedLocalMainFrame()->ContentLayoutObject(); if (!layout_view) return IntSize(); return layout_view->DocumentRect().Size(); } WebSize WebViewImpl::ContentsPreferredMinimumSize() { if (MainFrameImpl()) { MainFrameImpl() ->GetFrame() ->View() ->UpdateLifecycleToCompositingCleanPlusScrolling(); } Document* document = page_->MainFrame()->IsLocalFrame() ? page_->DeprecatedLocalMainFrame()->GetDocument() : nullptr; if (!document || !document->GetLayoutView() || !document->documentElement() || !document->documentElement()->GetLayoutBox()) return WebSize(); // Needed for computing MinPreferredWidth. FontCachePurgePreventer fontCachePurgePreventer; int width_scaled = document->GetLayoutView() ->MinPreferredLogicalWidth() .Round(); // Already accounts for zoom. int height_scaled = document->documentElement()->GetLayoutBox()->ScrollHeight().Round(); return IntSize(width_scaled, height_scaled); } float WebViewImpl::DefaultMinimumPageScaleFactor() const { return GetPageScaleConstraintsSet().DefaultConstraints().minimum_scale; } float WebViewImpl::DefaultMaximumPageScaleFactor() const { return GetPageScaleConstraintsSet().DefaultConstraints().maximum_scale; } float WebViewImpl::MinimumPageScaleFactor() const { return GetPageScaleConstraintsSet().FinalConstraints().minimum_scale; } float WebViewImpl::MaximumPageScaleFactor() const { return GetPageScaleConstraintsSet().FinalConstraints().maximum_scale; } void WebViewImpl::ResetScaleStateImmediately() { GetPageScaleConstraintsSet().SetNeedsReset(true); } void WebViewImpl::ResetScrollAndScaleState() { GetPage()->GetVisualViewport().Reset(); if (!GetPage()->MainFrame()->IsLocalFrame()) return; if (LocalFrameView* frame_view = ToLocalFrame(GetPage()->MainFrame())->View()) { ScrollableArea* scrollable_area = frame_view->LayoutViewportScrollableArea(); if (!scrollable_area->GetScrollOffset().IsZero()) scrollable_area->SetScrollOffset(ScrollOffset(), kProgrammaticScroll); } if (Document* document = ToLocalFrame(GetPage()->MainFrame())->GetDocument()) { if (DocumentLoader* loader = document->Loader()) { if (HistoryItem* item = loader->GetHistoryItem()) item->ClearViewState(); } } GetPageScaleConstraintsSet().SetNeedsReset(true); } void WebViewImpl::PerformMediaPlayerAction(const WebMediaPlayerAction& action, const WebPoint& location) { HitTestResult result = HitTestResultForRootFramePos( page_->GetVisualViewport().ViewportToRootFrame(location)); Node* node = result.InnerNode(); if (!IsHTMLVideoElement(*node) && !IsHTMLAudioElement(*node)) return; HTMLMediaElement* media_element = ToHTMLMediaElement(node); switch (action.type) { case WebMediaPlayerAction::kPlay: if (action.enable) media_element->Play(); else media_element->pause(); break; case WebMediaPlayerAction::kMute: media_element->setMuted(action.enable); break; case WebMediaPlayerAction::kLoop: media_element->SetLoop(action.enable); break; case WebMediaPlayerAction::kControls: media_element->SetBooleanAttribute(HTMLNames::controlsAttr, action.enable); break; default: NOTREACHED(); } } void WebViewImpl::PerformPluginAction(const WebPluginAction& action, const WebPoint& location) { // FIXME: Location is probably in viewport coordinates HitTestResult result = HitTestResultForRootFramePos(LayoutPoint(location)); Node* node = result.InnerNode(); if (!IsHTMLObjectElement(*node) && !IsHTMLEmbedElement(*node)) return; LayoutObject* object = node->GetLayoutObject(); if (object && object->IsLayoutEmbeddedContent()) { WebPluginContainerImpl* plugin_view = ToLayoutEmbeddedContent(object)->Plugin(); if (plugin_view) { switch (action.type) { case WebPluginAction::kRotate90Clockwise: plugin_view->Plugin()->RotateView( WebPlugin::kRotationType90Clockwise); break; case WebPluginAction::kRotate90Counterclockwise: plugin_view->Plugin()->RotateView( WebPlugin::kRotationType90Counterclockwise); break; default: NOTREACHED(); } } } } void WebViewImpl::AudioStateChanged(bool is_audio_playing) { scheduler_->AudioStateChanged(is_audio_playing); } WebHitTestResult WebViewImpl::HitTestResultAt(const WebPoint& point) { return CoreHitTestResultAt(point); } HitTestResult WebViewImpl::CoreHitTestResultAt( const WebPoint& point_in_viewport) { DocumentLifecycle::AllowThrottlingScope throttling_scope( MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle()); LocalFrameView* view = MainFrameImpl()->GetFrameView(); LayoutPoint point_in_root_frame = view->ContentsToFrame( view->ViewportToContents(LayoutPoint(point_in_viewport))); return HitTestResultForRootFramePos(point_in_root_frame); } void WebViewImpl::SendResizeEventAndRepaint() { // FIXME: This is wrong. The LocalFrameView is responsible sending a // resizeEvent as part of layout. Layout is also responsible for sending // invalidations to the embedder. This method and all callers may be wrong. -- // eseidel. if (MainFrameImpl()->GetFrameView()) { // Enqueues the resize event. MainFrameImpl()->GetFrame()->GetDocument()->EnqueueResizeEvent(); } if (client_) { if (layer_tree_view_) { UpdateLayerTreeViewport(); } else { WebRect damaged_rect(0, 0, size_.width, size_.height); client_->WidgetClient()->DidInvalidateRect(damaged_rect); } } } void WebViewImpl::ConfigureAutoResizeMode() { if (!MainFrameImpl() || !MainFrameImpl()->GetFrame() || !MainFrameImpl()->GetFrame()->View()) return; if (should_auto_resize_) { MainFrameImpl()->GetFrame()->View()->EnableAutoSizeMode(min_auto_size_, max_auto_size_); } else { MainFrameImpl()->GetFrame()->View()->DisableAutoSizeMode(); } } unsigned long WebViewImpl::CreateUniqueIdentifierForRequest() { return CreateUniqueIdentifier(); } void WebViewImpl::SetCompositorDeviceScaleFactorOverride( float device_scale_factor) { if (compositor_device_scale_factor_override_ == device_scale_factor) return; compositor_device_scale_factor_override_ = device_scale_factor; if (zoom_factor_for_device_scale_factor_) { SetZoomLevel(ZoomLevel()); return; } if (GetPage() && layer_tree_view_) UpdateLayerTreeDeviceScaleFactor(); } void WebViewImpl::SetDeviceEmulationTransform( const TransformationMatrix& transform) { if (transform == device_emulation_transform_) return; device_emulation_transform_ = transform; UpdateDeviceEmulationTransform(); } TransformationMatrix WebViewImpl::GetDeviceEmulationTransformForTesting() const { return device_emulation_transform_; } void WebViewImpl::EnableDeviceEmulation( const WebDeviceEmulationParams& params) { dev_tools_emulator_->EnableDeviceEmulation(params); } void WebViewImpl::DisableDeviceEmulation() { dev_tools_emulator_->DisableDeviceEmulation(); } void WebViewImpl::PerformCustomContextMenuAction(unsigned action) { if (!page_) return; ContextMenu* menu = page_->GetContextMenuController().GetContextMenu(); if (!menu) return; const ContextMenuItem* item = menu->ItemWithAction( static_cast(kContextMenuItemBaseCustomTag + action)); if (item) page_->GetContextMenuController().ContextMenuItemSelected(item); page_->GetContextMenuController().ClearContextMenu(); } void WebViewImpl::ShowContextMenu(WebMenuSourceType source_type) { if (!MainFrameImpl()) return; // If MainFrameImpl() is non-null, then FrameWidget() will also be non-null. DCHECK(MainFrameImpl()->FrameWidget()); MainFrameImpl()->FrameWidget()->ShowContextMenu(source_type); } void WebViewImpl::DidCloseContextMenu() { LocalFrame* frame = page_->GetFocusController().FocusedFrame(); if (frame) frame->Selection().SetCaretBlinkingSuspended(false); } void WebViewImpl::HidePopups() { CancelPagePopup(); } WebInputMethodController* WebViewImpl::GetActiveWebInputMethodController() const { WebLocalFrameImpl* local_frame = WebLocalFrameImpl::FromFrame(FocusedLocalFrameInWidget()); return local_frame ? local_frame->GetInputMethodController() : nullptr; } Color WebViewImpl::BaseBackgroundColor() const { return base_background_color_override_enabled_ ? base_background_color_override_ : base_background_color_; } void WebViewImpl::SetBaseBackgroundColor(WebColor color) { if (base_background_color_ == color) return; base_background_color_ = color; UpdateBaseBackgroundColor(); } void WebViewImpl::SetBaseBackgroundColorOverride(WebColor color) { if (base_background_color_override_enabled_ && base_background_color_override_ == color) { return; } base_background_color_override_enabled_ = true; base_background_color_override_ = color; if (MainFrameImpl()) { // Force lifecycle update to ensure we're good to call // LocalFrameView::setBaseBackgroundColor(). MainFrameImpl() ->GetFrame() ->View() ->UpdateLifecycleToCompositingCleanPlusScrolling(); } UpdateBaseBackgroundColor(); } void WebViewImpl::ClearBaseBackgroundColorOverride() { if (!base_background_color_override_enabled_) return; base_background_color_override_enabled_ = false; if (MainFrameImpl()) { // Force lifecycle update to ensure we're good to call // LocalFrameView::setBaseBackgroundColor(). MainFrameImpl() ->GetFrame() ->View() ->UpdateLifecycleToCompositingCleanPlusScrolling(); } UpdateBaseBackgroundColor(); } void WebViewImpl::UpdateBaseBackgroundColor() { Color color = BaseBackgroundColor(); if (page_->MainFrame() && page_->MainFrame()->IsLocalFrame()) { LocalFrameView* view = page_->DeprecatedLocalMainFrame()->View(); view->SetBaseBackgroundColor(color); view->UpdateBaseBackgroundColorRecursively(color); } } void WebViewImpl::SetIsActive(bool active) { if (GetPage()) GetPage()->GetFocusController().SetActive(active); } bool WebViewImpl::IsActive() const { return GetPage() ? GetPage()->GetFocusController().IsActive() : false; } void WebViewImpl::SetDomainRelaxationForbidden(bool forbidden, const WebString& scheme) { SchemeRegistry::SetDomainRelaxationForbiddenForURLScheme(forbidden, String(scheme)); } void WebViewImpl::SetWindowFeatures(const WebWindowFeatures& features) { page_->SetWindowFeatures(features); } void WebViewImpl::SetOpenedByDOM() { page_->SetOpenedByDOM(); } void WebViewImpl::SetSelectionColors(unsigned active_background_color, unsigned active_foreground_color, unsigned inactive_background_color, unsigned inactive_foreground_color) { #if defined(WTF_USE_DEFAULT_RENDER_THEME) LayoutThemeDefault::SetSelectionColors( active_background_color, active_foreground_color, inactive_background_color, inactive_foreground_color); LayoutTheme::GetTheme().PlatformColorsDidChange(); #endif } void WebViewImpl::DidCommitLoad(bool is_new_navigation, bool is_navigation_within_page) { if (!is_navigation_within_page) { should_dispatch_first_visually_non_empty_layout_ = true; should_dispatch_first_layout_after_finished_parsing_ = true; should_dispatch_first_layout_after_finished_loading_ = true; if (is_new_navigation) { GetPageScaleConstraintsSet().SetNeedsReset(true); page_importance_signals_.OnCommitLoad(); } } // Give the visual viewport's scroll layer its initial size. GetPage()->GetVisualViewport().MainFrameDidChangeSize(); // Make sure link highlight from previous page is cleared. link_highlights_.clear(); if (!MainFrameImpl()) return; if (WebFrameWidgetBase* widget = MainFrameImpl()->FrameWidget()) widget->EndActiveFlingAnimation(); } void WebViewImpl::ResizeAfterLayout() { DCHECK(MainFrameImpl()); if (!client_ || !client_->CanUpdateLayout()) return; if (should_auto_resize_) { LocalFrameView* view = MainFrameImpl()->GetFrame()->View(); WebSize frame_size = view->FrameRect().Size(); if (frame_size != size_) { size_ = frame_size; GetPage()->GetVisualViewport().SetSize(size_); GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(size_); view->SetInitialViewportSize(size_); client_->DidAutoResize(size_); SendResizeEventAndRepaint(); } } if (GetPageScaleConstraintsSet().ConstraintsDirty()) RefreshPageScaleFactorAfterLayout(); resize_viewport_anchor_->ResizeFrameView(MainFrameSize()); } void WebViewImpl::LayoutUpdated() { DCHECK(MainFrameImpl()); if (!client_) return; UpdatePageOverlays(); fullscreen_controller_->DidUpdateLayout(); client_->DidUpdateLayout(); } void WebViewImpl::DidChangeContentsSize() { GetPageScaleConstraintsSet().DidChangeContentsSize(ContentsSize(), PageScaleFactor()); } void WebViewImpl::PageScaleFactorChanged() { GetPageScaleConstraintsSet().SetNeedsReset(false); UpdateLayerTreeViewport(); client_->PageScaleFactorChanged(); dev_tools_emulator_->MainFrameScrollOrScaleChanged(); } void WebViewImpl::MainFrameScrollOffsetChanged() { dev_tools_emulator_->MainFrameScrollOrScaleChanged(); } void WebViewImpl::SetBackgroundColorOverride(WebColor color) { background_color_override_enabled_ = true; background_color_override_ = color; UpdateLayerTreeBackgroundColor(); } void WebViewImpl::ClearBackgroundColorOverride() { background_color_override_enabled_ = false; UpdateLayerTreeBackgroundColor(); } void WebViewImpl::SetZoomFactorOverride(float zoom_factor) { zoom_factor_override_ = zoom_factor; SetZoomLevel(ZoomLevel()); } void WebViewImpl::SetPageOverlayColor(WebColor color) { if (page_color_overlay_) page_color_overlay_.reset(); if (color == Color::kTransparent) return; page_color_overlay_ = PageOverlay::Create( MainFrameImpl(), std::make_unique(color)); // Run compositing update before calling updatePageOverlays. MainFrameImpl() ->GetFrameView() ->UpdateLifecycleToCompositingCleanPlusScrolling(); UpdatePageOverlays(); } WebPageImportanceSignals* WebViewImpl::PageImportanceSignals() { return &page_importance_signals_; } Element* WebViewImpl::FocusedElement() const { LocalFrame* frame = page_->GetFocusController().FocusedFrame(); if (!frame) return nullptr; Document* document = frame->GetDocument(); if (!document) return nullptr; return document->FocusedElement(); } HitTestResult WebViewImpl::HitTestResultForRootFramePos( const LayoutPoint& pos_in_root_frame) { if (!page_->MainFrame()->IsLocalFrame()) return HitTestResult(); LayoutPoint doc_point( page_->DeprecatedLocalMainFrame()->View()->RootFrameToContents( pos_in_root_frame)); HitTestResult result = page_->DeprecatedLocalMainFrame()->GetEventHandler().HitTestResultAtPoint( doc_point, HitTestRequest::kReadOnly | HitTestRequest::kActive); result.SetToShadowHostIfInRestrictedShadowRoot(); return result; } WebHitTestResult WebViewImpl::HitTestResultForTap( const WebPoint& tap_point_window_pos, const WebSize& tap_area) { if (!page_->MainFrame()->IsLocalFrame()) return HitTestResult(); WebGestureEvent tap_event(WebInputEvent::kGestureTap, WebInputEvent::kNoModifiers, WTF::CurrentTimeTicksInSeconds()); tap_event.x = tap_point_window_pos.x; tap_event.y = tap_point_window_pos.y; // GestureTap is only ever from a touchscreen. tap_event.source_device = kWebGestureDeviceTouchscreen; tap_event.data.tap.tap_count = 1; tap_event.data.tap.width = tap_area.width; tap_event.data.tap.height = tap_area.height; WebGestureEvent scaled_event = TransformWebGestureEvent(MainFrameImpl()->GetFrameView(), tap_event); HitTestResult result = page_->DeprecatedLocalMainFrame() ->GetEventHandler() .HitTestResultForGestureEvent( scaled_event, HitTestRequest::kReadOnly | HitTestRequest::kActive) .GetHitTestResult(); result.SetToShadowHostIfInRestrictedShadowRoot(); return result; } void WebViewImpl::SetTabsToLinks(bool enable) { tabs_to_links_ = enable; } bool WebViewImpl::TabsToLinks() const { return tabs_to_links_; } void WebViewImpl::RegisterViewportLayersWithCompositor() { DCHECK(layer_tree_view_); if (!GetPage()->MainFrame() || !GetPage()->MainFrame()->IsLocalFrame()) return; Document* document = GetPage()->DeprecatedLocalMainFrame()->GetDocument(); DCHECK(document); // Get the outer viewport scroll layers. GraphicsLayer* layout_viewport_container_layer = GetPage()->GlobalRootScrollerController().RootContainerLayer(); WebLayer* layout_viewport_container_web_layer = layout_viewport_container_layer ? layout_viewport_container_layer->PlatformLayer() : nullptr; GraphicsLayer* layout_viewport_scroll_layer = GetPage()->GlobalRootScrollerController().RootScrollerLayer(); WebLayer* layout_viewport_scroll_web_layer = layout_viewport_scroll_layer ? layout_viewport_scroll_layer->PlatformLayer() : nullptr; VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); // TODO(bokan): This was moved here from when registerViewportLayers was a // part of VisualViewport and maybe doesn't belong here. See comment inside // the mehtod. visual_viewport.SetScrollLayerOnScrollbars(layout_viewport_scroll_web_layer); WebLayerTreeView::ViewportLayers viewport_layers; viewport_layers.overscroll_elasticity = visual_viewport.OverscrollElasticityLayer()->PlatformLayer(); viewport_layers.page_scale = visual_viewport.PageScaleLayer()->PlatformLayer(); viewport_layers.inner_viewport_container = visual_viewport.ContainerLayer()->PlatformLayer(); viewport_layers.outer_viewport_container = layout_viewport_container_web_layer; viewport_layers.inner_viewport_scroll = visual_viewport.ScrollLayer()->PlatformLayer(); viewport_layers.outer_viewport_scroll = layout_viewport_scroll_web_layer; layer_tree_view_->RegisterViewportLayers(viewport_layers); } void WebViewImpl::SetRootGraphicsLayer(GraphicsLayer* graphics_layer) { if (!layer_tree_view_) return; // In SPv2, setRootLayer is used instead. DCHECK(!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()); VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); visual_viewport.AttachLayerTree(graphics_layer); if (graphics_layer) { root_graphics_layer_ = visual_viewport.RootGraphicsLayer(); visual_viewport_container_layer_ = visual_viewport.ContainerLayer(); root_layer_ = root_graphics_layer_->PlatformLayer(); UpdateDeviceEmulationTransform(); layer_tree_view_->SetRootLayer(*root_layer_); // We register viewport layers here since there may not be a layer // tree view prior to this point. RegisterViewportLayersWithCompositor(); // TODO(enne): Work around page visibility changes not being // propagated to the WebView in some circumstances. This needs to // be refreshed here when setting a new root layer to avoid being // stuck in a presumed incorrectly invisible state. layer_tree_view_->SetVisible(GetPage()->IsPageVisible()); } else { root_graphics_layer_ = nullptr; visual_viewport_container_layer_ = nullptr; root_layer_ = nullptr; // This means that we're transitioning to a new page. Suppress // commits until Blink generates invalidations so we don't // attempt to paint too early in the next page load. layer_tree_view_->SetDeferCommits(true); layer_tree_view_->ClearRootLayer(); layer_tree_view_->ClearViewportLayers(); if (WebDevToolsAgentImpl* dev_tools = MainFrameDevToolsAgentImpl()) dev_tools->RootLayerCleared(); } } void WebViewImpl::SetRootLayer(WebLayer* layer) { if (!layer_tree_view_) return; if (layer) { root_layer_ = layer; layer_tree_view_->SetRootLayer(*root_layer_); layer_tree_view_->SetVisible(GetPage()->IsPageVisible()); } else { root_layer_ = nullptr; // This means that we're transitioning to a new page. Suppress // commits until Blink generates invalidations so we don't // attempt to paint too early in the next page load. layer_tree_view_->SetDeferCommits(true); layer_tree_view_->ClearRootLayer(); layer_tree_view_->ClearViewportLayers(); if (WebDevToolsAgentImpl* dev_tools = MainFrameDevToolsAgentImpl()) dev_tools->RootLayerCleared(); } } void WebViewImpl::InvalidateRect(const IntRect& rect) { if (layer_tree_view_) { UpdateLayerTreeViewport(); } else if (client_) { // This is only for WebViewPlugin. client_->WidgetClient()->DidInvalidateRect(rect); } } PaintLayerCompositor* WebViewImpl::Compositor() const { WebLocalFrameImpl* frame = MainFrameImpl(); if (!frame) return nullptr; Document* document = frame->GetFrame()->GetDocument(); if (!document || !document->GetLayoutView()) return nullptr; return document->GetLayoutView()->Compositor(); } GraphicsLayer* WebViewImpl::RootGraphicsLayer() { return root_graphics_layer_; } void WebViewImpl::ScheduleAnimationForWidget() { if (layer_tree_view_) { layer_tree_view_->SetNeedsBeginFrame(); return; } if (client_) client_->WidgetClient()->ScheduleAnimation(); } void WebViewImpl::AttachCompositorAnimationTimeline( CompositorAnimationTimeline* timeline) { if (animation_host_) animation_host_->AddTimeline(*timeline); } void WebViewImpl::DetachCompositorAnimationTimeline( CompositorAnimationTimeline* timeline) { if (animation_host_) animation_host_->RemoveTimeline(*timeline); } void WebViewImpl::InitializeLayerTreeView() { if (client_) { layer_tree_view_ = client_->InitializeLayerTreeView(); if (layer_tree_view_ && layer_tree_view_->CompositorAnimationHost()) { animation_host_ = std::make_unique( layer_tree_view_->CompositorAnimationHost()); } } if (WebDevToolsAgentImpl* dev_tools = MainFrameDevToolsAgentImpl()) dev_tools->LayerTreeViewChanged(layer_tree_view_); page_->GetSettings().SetAcceleratedCompositingEnabled(layer_tree_view_); if (layer_tree_view_) { page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr); // We don't yet have a page loaded at this point of the initialization of // WebViewImpl, so don't allow cc to commit any frames Blink might // try to create in the meantime. layer_tree_view_->SetDeferCommits(true); } // FIXME: only unittests, click to play, Android printing, and printing (for // headers and footers) make this assert necessary. We should make them not // hit this code and then delete allowsBrokenNullLayerTreeView. DCHECK(layer_tree_view_ || !client_ || client_->WidgetClient()->AllowsBrokenNullLayerTreeView()); if (Platform::Current()->IsThreadedAnimationEnabled() && layer_tree_view_) { link_highlights_timeline_ = CompositorAnimationTimeline::Create(); AttachCompositorAnimationTimeline(link_highlights_timeline_.get()); } } void WebViewImpl::ApplyViewportDeltas( const WebFloatSize& visual_viewport_delta, // TODO(bokan): This parameter is to be removed but requires adjusting many // callsites. const WebFloatSize&, const WebFloatSize& elastic_overscroll_delta, float page_scale_delta, float browser_controls_shown_ratio_delta) { VisualViewport& visual_viewport = GetPage()->GetVisualViewport(); // Store the desired offsets the visual viewport before setting the top // controls ratio since doing so will change the bounds and move the // viewports to keep the offsets valid. The compositor may have already // done that so we don't want to double apply the deltas here. FloatPoint visual_viewport_offset = visual_viewport.VisibleRect().Location(); visual_viewport_offset.Move(visual_viewport_delta.width, visual_viewport_delta.height); GetBrowserControls().SetShownRatio(GetBrowserControls().ShownRatio() + browser_controls_shown_ratio_delta); SetPageScaleFactorAndLocation(PageScaleFactor() * page_scale_delta, visual_viewport_offset); if (page_scale_delta != 1) { double_tap_zoom_pending_ = false; visual_viewport.UserDidChangeScale(); } elastic_overscroll_ += elastic_overscroll_delta; if (MainFrameImpl() && MainFrameImpl()->GetFrameView()) MainFrameImpl()->GetFrameView()->DidUpdateElasticOverscroll(); } void WebViewImpl::RecordWheelAndTouchScrollingCount( bool has_scrolled_by_wheel, bool has_scrolled_by_touch) { if (!MainFrameImpl()) return; if (has_scrolled_by_wheel) UseCounter::Count(MainFrameImpl()->GetFrame(), WebFeature::kScrollByWheel); if (has_scrolled_by_touch) UseCounter::Count(MainFrameImpl()->GetFrame(), WebFeature::kScrollByTouch); } void WebViewImpl::UpdateLayerTreeViewport() { if (!GetPage() || !layer_tree_view_) return; layer_tree_view_->SetPageScaleFactorAndLimits( PageScaleFactor(), MinimumPageScaleFactor(), MaximumPageScaleFactor()); } void WebViewImpl::UpdateLayerTreeBackgroundColor() { if (!layer_tree_view_) return; layer_tree_view_->SetBackgroundColor(BackgroundColor()); } void WebViewImpl::UpdateLayerTreeDeviceScaleFactor() { DCHECK(GetPage()); DCHECK(layer_tree_view_); float device_scale_factor = compositor_device_scale_factor_override_ ? compositor_device_scale_factor_override_ : GetPage()->DeviceScaleFactorDeprecated(); layer_tree_view_->SetDeviceScaleFactor(device_scale_factor); } void WebViewImpl::UpdateDeviceEmulationTransform() { if (!visual_viewport_container_layer_) return; // When the device emulation transform is updated, to avoid incorrect // scales and fuzzy raster from the compositor, force all content to // pick ideal raster scales. visual_viewport_container_layer_->SetTransform(device_emulation_transform_); layer_tree_view_->ForceRecalculateRasterScales(); } WebViewScheduler* WebViewImpl::Scheduler() const { return scheduler_.get(); } void WebViewImpl::SetVisibilityState( mojom::PageVisibilityState visibility_state, bool is_initial_state) { if (GetPage()) { page_->SetVisibilityState(visibility_state, is_initial_state); } bool visible = visibility_state == mojom::PageVisibilityState::kVisible; if (layer_tree_view_ && !override_compositor_visibility_) layer_tree_view_->SetVisible(visible); scheduler_->SetPageVisible(visible); } void WebViewImpl::SetCompositorVisibility(bool is_visible) { if (!is_visible) override_compositor_visibility_ = true; else override_compositor_visibility_ = false; if (layer_tree_view_) layer_tree_view_->SetVisible(is_visible); } void WebViewImpl::ForceNextWebGLContextCreationToFail() { CoreInitializer::GetInstance().ForceNextWebGLContextCreationToFail(); } void WebViewImpl::ForceNextDrawingBufferCreationToFail() { DrawingBuffer::ForceNextDrawingBufferCreationToFail(); } CompositorMutatorImpl& WebViewImpl::Mutator() { return *CompositorMutator(); } CompositorMutatorImpl* WebViewImpl::CompositorMutator() { if (!mutator_) { std::unique_ptr mutator_client = CompositorMutatorImpl::CreateClient(); mutator_ = static_cast(mutator_client->Mutator()); layer_tree_view_->SetMutatorClient(std::move(mutator_client)); } return mutator_; } void WebViewImpl::UpdatePageOverlays() { if (page_color_overlay_) page_color_overlay_->Update(); if (auto* client = GetValidationMessageClient()) client->LayoutOverlay(); if (WebDevToolsAgentImpl* devtools = MainFrameDevToolsAgentImpl()) devtools->LayoutOverlay(); } float WebViewImpl::DeviceScaleFactor() const { // TODO(oshima): Investigate if this should return the ScreenInfo's scale // factor rather than page's scale factor, which can be 1 in use-zoom-for-dsf // mode. if (!GetPage()) return 1; return GetPage()->DeviceScaleFactorDeprecated(); } LocalFrame* WebViewImpl::FocusedLocalFrameInWidget() const { if (!MainFrameImpl()) return nullptr; LocalFrame* focused_frame = ToLocalFrame(FocusedCoreFrame()); if (focused_frame->LocalFrameRoot() != MainFrameImpl()->GetFrame()) return nullptr; return focused_frame; } LocalFrame* WebViewImpl::FocusedLocalFrameAvailableForIme() const { return ime_accept_events_ ? FocusedLocalFrameInWidget() : nullptr; } } // namespace blink