| /* |
| * 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 "web/WebViewImpl.h" |
| |
| #include <memory> |
| #include "core/CSSValueKeywords.h" |
| #include "core/HTMLNames.h" |
| #include "core/clipboard/DataObject.h" |
| #include "core/dom/ContextFeaturesClientImpl.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/DocumentUserGestureToken.h" |
| #include "core/dom/Fullscreen.h" |
| #include "core/dom/LayoutTreeBuilderTraversal.h" |
| #include "core/dom/Text.h" |
| #include "core/editing/EditingUtilities.h" |
| #include "core/editing/Editor.h" |
| #include "core/editing/FrameSelection.h" |
| #include "core/editing/InputMethodController.h" |
| #include "core/editing/iterators/TextIterator.h" |
| #include "core/editing/serializers/HTMLInterchange.h" |
| #include "core/editing/serializers/Serialization.h" |
| #include "core/events/KeyboardEvent.h" |
| #include "core/events/UIEventWithKeyState.h" |
| #include "core/events/WheelEvent.h" |
| #include "core/frame/BrowserControls.h" |
| #include "core/frame/EventHandlerRegistry.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/frame/LocalFrameClient.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/html/HTMLMediaElement.h" |
| #include "core/html/HTMLPlugInElement.h" |
| #include "core/html/HTMLTextAreaElement.h" |
| #include "core/input/EventHandler.h" |
| #include "core/input/TouchActionUtil.h" |
| #include "core/layout/LayoutPart.h" |
| #include "core/layout/TextAutosizer.h" |
| #include "core/layout/api/LayoutViewItem.h" |
| #include "core/layout/compositing/PaintLayerCompositor.h" |
| #include "core/loader/FrameLoadRequest.h" |
| #include "core/loader/FrameLoader.h" |
| #include "core/loader/FrameLoaderStateMachine.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/PagePopupClient.h" |
| #include "core/page/PointerLockController.h" |
| #include "core/page/ScopedPageSuspender.h" |
| #include "core/page/TouchDisambiguation.h" |
| #include "core/page/scrolling/TopDocumentRootScrollerController.h" |
| #include "core/paint/PaintLayer.h" |
| #include "core/timing/DOMWindowPerformance.h" |
| #include "core/timing/Performance.h" |
| #include "modules/accessibility/AXObject.h" |
| #include "modules/accessibility/AXObjectCacheImpl.h" |
| #include "modules/credentialmanager/CredentialManagerClient.h" |
| #include "modules/encryptedmedia/MediaKeysController.h" |
| #include "modules/storage/StorageNamespaceController.h" |
| #include "modules/webdatabase/DatabaseClient.h" |
| #include "modules/webgl/WebGLRenderingContext.h" |
| #include "platform/ContextMenu.h" |
| #include "platform/ContextMenuItem.h" |
| #include "platform/Cursor.h" |
| #include "platform/Histogram.h" |
| #include "platform/KeyboardCodes.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/UserGestureIndicator.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/FirstPaintInvalidationTracking.h" |
| #include "platform/graphics/GraphicsContext.h" |
| #include "platform/graphics/Image.h" |
| #include "platform/graphics/ImageBuffer.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/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/CurrentTime.h" |
| #include "platform/wtf/PtrUtil.h" |
| #include "platform/wtf/RefPtr.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/WebLayerTreeView.h" |
| #include "public/platform/WebTextInputInfo.h" |
| #include "public/platform/WebURLRequest.h" |
| #include "public/platform/WebVector.h" |
| #include "public/web/WebAXObject.h" |
| #include "public/web/WebActiveWheelFlingParameters.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 "web/AnimationWorkletProxyClientImpl.h" |
| #include "web/CompositorMutatorImpl.h" |
| #include "web/CompositorWorkerProxyClientImpl.h" |
| #include "web/ContextMenuAllowedScope.h" |
| #include "web/DedicatedWorkerMessagingProxyProviderImpl.h" |
| #include "web/DevToolsEmulator.h" |
| #include "web/FullscreenController.h" |
| #include "web/InspectorOverlayAgent.h" |
| #include "web/LinkHighlightImpl.h" |
| #include "web/PageOverlay.h" |
| #include "web/PrerendererClientImpl.h" |
| #include "web/SpeechRecognitionClientProxy.h" |
| #include "web/StorageQuotaClientImpl.h" |
| #include "web/ValidationMessageClientImpl.h" |
| #include "web/WebDevToolsAgentImpl.h" |
| #include "web/WebInputEventConversion.h" |
| #include "web/WebInputMethodControllerImpl.h" |
| #include "web/WebLocalFrameImpl.h" |
| #include "web/WebPluginContainerImpl.h" |
| #include "web/WebRemoteFrameImpl.h" |
| #include "web/WebSettingsImpl.h" |
| |
| #if 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 <cmath> // 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 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<std::unique_ptr<ScopedPageSuspender>>& PageSuspenderStack() { |
| DEFINE_STATIC_LOCAL(Vector<std::unique_ptr<ScopedPageSuspender>>, |
| suspender_stack, ()); |
| return suspender_stack; |
| } |
| |
| static bool g_should_use_external_popup_menus = false; |
| |
| 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 drawing_recorder(graphics_context, page_overlay, |
| DisplayItem::kPageOverlay, rect); |
| graphics_context.FillRect(rect, color_); |
| } |
| |
| WebColor color_; |
| }; |
| |
| } // namespace |
| |
| // WebView ---------------------------------------------------------------- |
| |
| WebView* WebView::Create(WebViewClient* client, |
| WebPageVisibilityState visibility_state) { |
| // Pass the WebViewImpl's self-reference to the caller. |
| return WebViewImpl::Create(client, visibility_state); |
| } |
| |
| WebViewImpl* WebViewImpl::Create(WebViewClient* client, |
| WebPageVisibilityState visibility_state) { |
| // Pass the WebViewImpl's self-reference to the caller. |
| return AdoptRef(new WebViewImpl(client, visibility_state)).LeakRef(); |
| } |
| |
| const WebInputEvent* WebViewBase::CurrentInputEvent() { |
| return WebViewImpl::CurrentInputEvent(); |
| } |
| |
| void WebView::SetUseExternalPopupMenus(bool use_external_popup_menus) { |
| g_should_use_external_popup_menus = use_external_popup_menus; |
| } |
| |
| 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 WebView::WillEnterModalLoop() { |
| PageSuspenderStack().push_back(WTF::MakeUnique<ScopedPageSuspender>()); |
| } |
| |
| void WebView::DidExitModalLoop() { |
| DCHECK(PageSuspenderStack().size()); |
| PageSuspenderStack().pop_back(); |
| } |
| |
| void WebViewImpl::SetMainFrame(WebFrame* frame) { |
| WebFrame::InitializeCoreFrame(*frame, *GetPage()); |
| } |
| |
| void WebViewImpl::SetCredentialManagerClient( |
| WebCredentialManagerClient* web_credential_manager_client) { |
| DCHECK(page_); |
| ProvideCredentialManagerClientTo( |
| *page_, new CredentialManagerClient(web_credential_manager_client)); |
| } |
| |
| void WebViewImpl::SetPrerendererClient( |
| WebPrerendererClient* prerenderer_client) { |
| DCHECK(page_); |
| ProvidePrerendererClientTo( |
| *page_, new PrerendererClientImpl(*page_, prerenderer_client)); |
| } |
| |
| void WebViewImpl::SetSpellCheckClient(WebSpellCheckClient* spell_check_client) { |
| spell_check_client_ = spell_check_client; |
| } |
| |
| // static |
| HashSet<WebViewBase*>& WebViewBase::AllInstances() { |
| DEFINE_STATIC_LOCAL(HashSet<WebViewBase*>, all_instances, ()); |
| return all_instances; |
| } |
| |
| WebViewImpl::WebViewImpl(WebViewClient* client, |
| WebPageVisibilityState visibility_state) |
| : client_(client), |
| spell_check_client_(nullptr), |
| chrome_client_impl_(ChromeClientImpl::Create(this)), |
| context_menu_client_impl_(this), |
| editor_client_impl_(this), |
| spell_checker_client_impl_(this), |
| storage_client_impl_(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), |
| fling_modifier_(0), |
| fling_source_device_(kWebGestureDeviceUninitialized), |
| 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_(WTF::WrapUnique(Platform::Current() |
| ->CurrentThread() |
| ->Scheduler() |
| ->CreateWebViewScheduler(this, this) |
| .release())), |
| last_frame_time_monotonic_(0), |
| override_compositor_visibility_(false) { |
| Page::PageClients page_clients; |
| page_clients.chrome_client = chrome_client_impl_.Get(); |
| page_clients.context_menu_client = &context_menu_client_impl_; |
| page_clients.editor_client = &editor_client_impl_; |
| page_clients.spell_checker_client = &spell_checker_client_impl_; |
| |
| page_ = Page::CreateOrdinary(page_clients); |
| MediaKeysController::ProvideMediaKeysTo(*page_, &media_keys_client_impl_); |
| ProvideSpeechRecognitionTo( |
| *page_, SpeechRecognitionClientProxy::Create( |
| client ? client->SpeechRecognizer() : nullptr)); |
| ProvideContextFeaturesTo(*page_, ContextFeaturesClientImpl::Create()); |
| ProvideDatabaseClientTo(*page_, new DatabaseClient); |
| |
| ProvideStorageQuotaClientTo(*page_, StorageQuotaClientImpl::Create()); |
| page_->SetValidationMessageClient(ValidationMessageClientImpl::Create(*this)); |
| ProvideDedicatedWorkerMessagingProxyProviderTo( |
| *page_, DedicatedWorkerMessagingProxyProviderImpl::Create(*page_)); |
| StorageNamespaceController::ProvideStorageNamespaceTo(*page_, |
| &storage_client_impl_); |
| |
| 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()); |
| } |
| |
| WebDevToolsAgentImpl* WebViewImpl::MainFrameDevToolsAgentImpl() { |
| WebLocalFrameImpl* main_frame = MainFrameImpl(); |
| return main_frame ? main_frame->DevToolsAgentImpl() : nullptr; |
| } |
| |
| InspectorOverlayAgent* WebViewImpl::GetInspectorOverlay() { |
| if (WebDevToolsAgentImpl* devtools = MainFrameDevToolsAgentImpl()) |
| return devtools->OverlayAgent(); |
| return 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. |
| RefPtr<WebPagePopupImpl> 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. |
| IntPoint point(event.PositionInWidget().x, event.PositionInWidget().y); |
| 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 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); |
| IntPoint position_in_root_frame = |
| FlooredIntPoint(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 ContextMenuClient |
| // 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) |
| EndActiveFlingAnimation(); |
| |
| HidePopups(); |
| return PageWidgetEventHandler::HandleMouseWheel(main_frame, event); |
| } |
| |
| WebGestureEvent WebViewImpl::CreateGestureScrollEventFromFling( |
| WebInputEvent::Type type, |
| WebGestureDevice source_device) const { |
| WebGestureEvent gesture_event(type, fling_modifier_, |
| WTF::MonotonicallyIncreasingTime()); |
| gesture_event.source_device = source_device; |
| gesture_event.x = position_on_fling_start_.x; |
| gesture_event.y = position_on_fling_start_.y; |
| gesture_event.global_x = global_position_on_fling_start_.x; |
| gesture_event.global_y = global_position_on_fling_start_.y; |
| return gesture_event; |
| } |
| |
| bool WebViewImpl::ScrollBy(const WebFloatSize& delta, |
| const WebFloatSize& velocity) { |
| DCHECK_NE(fling_source_device_, kWebGestureDeviceUninitialized); |
| if (!page_ || !page_->MainFrame() || !page_->MainFrame()->IsLocalFrame() || |
| !page_->DeprecatedLocalMainFrame()->View()) |
| return false; |
| |
| if (fling_source_device_ == kWebGestureDeviceTouchpad) { |
| bool enable_touchpad_scroll_latching = |
| RuntimeEnabledFeatures::touchpadAndWheelScrollLatchingEnabled(); |
| WebMouseWheelEvent synthetic_wheel(WebInputEvent::kMouseWheel, |
| fling_modifier_, |
| WTF::MonotonicallyIncreasingTime()); |
| const float kTickDivisor = WheelEvent::kTickMultiplier; |
| |
| synthetic_wheel.delta_x = delta.width; |
| synthetic_wheel.delta_y = delta.height; |
| synthetic_wheel.wheel_ticks_x = delta.width / kTickDivisor; |
| synthetic_wheel.wheel_ticks_y = delta.height / kTickDivisor; |
| synthetic_wheel.has_precise_scrolling_deltas = true; |
| synthetic_wheel.SetPositionInWidget(position_on_fling_start_.x, |
| position_on_fling_start_.y); |
| synthetic_wheel.SetPositionInScreen(global_position_on_fling_start_.x, |
| global_position_on_fling_start_.y); |
| |
| if (HandleMouseWheel(*page_->DeprecatedLocalMainFrame(), synthetic_wheel) != |
| WebInputEventResult::kNotHandled) |
| return true; |
| |
| if (!enable_touchpad_scroll_latching) { |
| WebGestureEvent synthetic_scroll_begin = |
| CreateGestureScrollEventFromFling(WebInputEvent::kGestureScrollBegin, |
| kWebGestureDeviceTouchpad); |
| synthetic_scroll_begin.data.scroll_begin.delta_x_hint = delta.width; |
| synthetic_scroll_begin.data.scroll_begin.delta_y_hint = delta.height; |
| synthetic_scroll_begin.data.scroll_begin.inertial_phase = |
| WebGestureEvent::kMomentumPhase; |
| HandleGestureEvent(synthetic_scroll_begin); |
| } |
| |
| WebGestureEvent synthetic_scroll_update = CreateGestureScrollEventFromFling( |
| WebInputEvent::kGestureScrollUpdate, kWebGestureDeviceTouchpad); |
| synthetic_scroll_update.data.scroll_update.delta_x = delta.width; |
| synthetic_scroll_update.data.scroll_update.delta_y = delta.height; |
| synthetic_scroll_update.data.scroll_update.velocity_x = velocity.width; |
| synthetic_scroll_update.data.scroll_update.velocity_y = velocity.height; |
| synthetic_scroll_update.data.scroll_update.inertial_phase = |
| WebGestureEvent::kMomentumPhase; |
| bool scroll_update_handled = HandleGestureEvent(synthetic_scroll_update) != |
| WebInputEventResult::kNotHandled; |
| |
| if (!enable_touchpad_scroll_latching) { |
| WebGestureEvent synthetic_scroll_end = CreateGestureScrollEventFromFling( |
| WebInputEvent::kGestureScrollEnd, kWebGestureDeviceTouchpad); |
| synthetic_scroll_end.data.scroll_end.inertial_phase = |
| WebGestureEvent::kMomentumPhase; |
| HandleGestureEvent(synthetic_scroll_end); |
| } |
| |
| return scroll_update_handled; |
| } else { |
| WebGestureEvent synthetic_gesture_event = CreateGestureScrollEventFromFling( |
| WebInputEvent::kGestureScrollUpdate, kWebGestureDeviceTouchscreen); |
| synthetic_gesture_event.data.scroll_update.prevent_propagation = true; |
| synthetic_gesture_event.data.scroll_update.delta_x = delta.width; |
| synthetic_gesture_event.data.scroll_update.delta_y = delta.height; |
| synthetic_gesture_event.data.scroll_update.velocity_x = velocity.width; |
| synthetic_gesture_event.data.scroll_update.velocity_y = velocity.height; |
| synthetic_gesture_event.data.scroll_update.inertial_phase = |
| WebGestureEvent::kMomentumPhase; |
| |
| return HandleGestureEvent(synthetic_gesture_event) != |
| WebInputEventResult::kNotHandled; |
| } |
| } |
| |
| 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: { |
| if (MainFrameImpl() |
| ->GetFrame() |
| ->GetEventHandler() |
| .IsScrollbarHandlingGestures()) |
| break; |
| EndActiveFlingAnimation(); |
| position_on_fling_start_ = WebPoint(event.x, event.y); |
| global_position_on_fling_start_ = |
| WebPoint(event.global_x, event.global_y); |
| fling_modifier_ = event.GetModifiers(); |
| fling_source_device_ = event.source_device; |
| DCHECK_NE(fling_source_device_, kWebGestureDeviceUninitialized); |
| std::unique_ptr<WebGestureCurve> fling_curve = |
| Platform::Current()->CreateFlingAnimationCurve( |
| event.source_device, |
| WebFloatPoint(event.data.fling_start.velocity_x, |
| event.data.fling_start.velocity_y), |
| WebSize()); |
| DCHECK(fling_curve); |
| gesture_animation_ = WebActiveGestureAnimation::CreateAtAnimationStart( |
| std::move(fling_curve), this); |
| MainFrameImpl()->FrameWidget()->ScheduleAnimation(); |
| event_result = WebInputEventResult::kHandledSystem; |
| |
| WebGestureEvent scaled_event = |
| TransformWebGestureEvent(MainFrameImpl()->GetFrameView(), event); |
| // Plugins may need to see GestureFlingStart to balance |
| // GestureScrollBegin (since the former replaces GestureScrollEnd when |
| // transitioning to a fling). |
| // TODO(dtapuska): Why isn't the response used? |
| MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureScrollEvent( |
| scaled_event); |
| |
| client_->DidHandleGestureEvent(event, event_cancelled); |
| return WebInputEventResult::kHandledSystem; |
| } |
| case WebInputEvent::kGestureFlingCancel: |
| if (EndActiveFlingAnimation()) |
| event_result = WebInputEventResult::kHandledSuppressed; |
| |
| 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<IntRect> good_targets; |
| HeapVector<Member<Node>> 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; |
| } |
| } |
| } |
| |
| 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)); |
| } |
| |
| void WebViewImpl::TransferActiveWheelFlingAnimation( |
| const WebActiveWheelFlingParameters& parameters) { |
| TRACE_EVENT0("blink", "WebViewImpl::transferActiveWheelFlingAnimation"); |
| DCHECK(!gesture_animation_); |
| position_on_fling_start_ = parameters.point; |
| global_position_on_fling_start_ = parameters.global_point; |
| fling_modifier_ = parameters.modifiers; |
| std::unique_ptr<WebGestureCurve> curve = |
| Platform::Current()->CreateFlingAnimationCurve( |
| parameters.source_device, WebFloatPoint(parameters.delta), |
| parameters.cumulative_scroll); |
| DCHECK(curve); |
| gesture_animation_ = WebActiveGestureAnimation::CreateWithTimeOffset( |
| std::move(curve), this, parameters.start_time); |
| DCHECK_NE(parameters.source_device, kWebGestureDeviceUninitialized); |
| fling_source_device_ = parameters.source_device; |
| MainFrameImpl()->FrameWidget()->ScheduleAnimation(); |
| } |
| |
| bool WebViewImpl::EndActiveFlingAnimation() { |
| if (gesture_animation_) { |
| gesture_animation_.reset(); |
| fling_source_device_ = kWebGestureDeviceUninitialized; |
| if (layer_tree_view_) |
| layer_tree_view_->DidStopFlinging(); |
| return true; |
| } |
| return false; |
| } |
| |
| 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); |
| |
| FrameView* 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; |
| } |
| |
| 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); |
| } |
| |
| float WebViewImpl::ExpensiveBackgroundThrottlingCPUBudget() { |
| return SettingsImpl()->ExpensiveBackgroundThrottlingCPUBudget(); |
| } |
| |
| float WebViewImpl::ExpensiveBackgroundThrottlingInitialBudget() { |
| return SettingsImpl()->ExpensiveBackgroundThrottlingInitialBudget(); |
| } |
| |
| float WebViewImpl::ExpensiveBackgroundThrottlingMaxBudget() { |
| return SettingsImpl()->ExpensiveBackgroundThrottlingMaxBudget(); |
| } |
| |
| float WebViewImpl::ExpensiveBackgroundThrottlingMaxDelay() { |
| return SettingsImpl()->ExpensiveBackgroundThrottlingMaxDelay(); |
| } |
| |
| 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. |
| 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. |
| PluginView* plugin_view = |
| ToLayoutPart(element->GetLayoutObject())->Plugin(); |
| if (plugin_view && plugin_view->IsPluginContainer()) { |
| WebPluginContainerImpl* plugin = |
| ToWebPluginContainerImpl(plugin_view); |
| if (plugin && plugin->SupportsKeyboardFocus()) |
| suppress_next_keypress_event_ = true; |
| } |
| } |
| } else { |
| suppress_next_keypress_event_ = true; |
| } |
| } |
| return result; |
| } |
| |
| #if !OS(MACOSX) |
| const WebInputEvent::Type kContextMenuKeyTriggeringEventType = |
| #if 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(event); |
| return WebInputEventResult::kHandledSystem; |
| } |
| #endif // !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. |
| IntPoint point = MainFrameImpl()->GetFrameView()->RootFrameToContents( |
| IntPoint(point_in_root_frame.x, point_in_root_frame.y)); |
| 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; |
| if (MainFrame()) |
| max_size = MainFrame()->ContentsSize(); |
| IntSize scroll_offset; |
| if (MainFrame()) |
| scroll_offset = MainFrame()->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<int>(default_margin * rect.width / size_.width), |
| static_cast<int>(minimum_margin * rect.width / size_.width)); |
| // Fit block to screen, respecting limits. |
| scale = static_cast<float>(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<float>( |
| 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<float>( |
| 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., <input>) |
| 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<Member<Node>> highlight_nodes; |
| highlight_nodes.push_back(touch_node); |
| |
| EnableTapHighlights(highlight_nodes); |
| } |
| |
| void WebViewImpl::EnableTapHighlights( |
| HeapVector<Member<Node>>& 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) { |
| 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) { |
| 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) { |
| 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; |
| } |
| |
| bool WebViewImpl::HasTouchEventHandlersAt(const WebPoint& point) { |
| // FIXME: Implement this. Note that the point must be divided by |
| // pageScaleFactor. |
| return true; |
| } |
| |
| #if !OS(MACOSX) |
| // Mac has no way to open a context menu based on a keyboard event. |
| WebInputEventResult WebViewImpl::SendContextMenuEvent( |
| const WebKeyboardEvent& event) { |
| // 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() |
| .SendContextMenuEventForKey(nullptr); |
| } |
| } |
| #else |
| WebInputEventResult WebViewImpl::SendContextMenuEvent( |
| const WebKeyboardEvent& event) { |
| 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().SendContextMenuEventForKey( |
| element.Unwrap<Element>()); |
| } |
| } |
| |
| PagePopup* WebViewImpl::OpenPagePopup(PagePopupClient* client) { |
| DCHECK(client); |
| if (HasOpenedPopup()) |
| HidePopups(); |
| DCHECK(!page_popup_); |
| |
| WebWidget* popup_widget = client_->CreatePopupMenu(kWebPopupTypePage); |
| // createPopupMenu 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(WebLocalFrameImpl::FromFrame( |
| client->OwnerElement().GetDocument().GetFrame()->LocalFrameRoot())); |
| 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; |
| } |
| |
| WebViewBase* WebViewBase::FromPage(Page* page) { |
| return WebViewImpl::FromPage(page); |
| } |
| |
| WebViewImpl* WebViewImpl::FromPage(Page* page) { |
| return page ? static_cast<WebViewImpl*>(page->GetChromeClient().WebView()) |
| : 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; |
| |
| Deref(); // Balances ref() 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 (RuntimeEnabledFeatures::inertTopControlsEnabled() && |
| GetBrowserControls().PermittedState() == kWebBrowserControlsBoth && |
| !GetBrowserControls().ShrinkViewport()) |
| icb_size.Expand(0, -GetBrowserControls().Height()); |
| |
| 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()) |
| ResizeFrameView(MainFrameImpl()); |
| } |
| } |
| |
| 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().Height(), GetBrowserControls().ShrinkViewport()); |
| } |
| |
| WebLocalFrameImpl* main_frame = MainFrameImpl(); |
| if (!main_frame) |
| return; |
| |
| FrameView* 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_); |
| |
| float browser_controls_viewport_adjustment = |
| GetBrowserControls().LayoutHeight() - |
| GetBrowserControls().ContentOffset(); |
| visual_viewport.SetBrowserControlsAdjustment( |
| browser_controls_viewport_adjustment); |
| } |
| } |
| |
| BrowserControls& WebViewImpl::GetBrowserControls() { |
| return GetPage()->GetBrowserControls(); |
| } |
| |
| void WebViewImpl::ResizeViewWhileAnchored(float browser_controls_height, |
| bool browser_controls_shrink_layout) { |
| DCHECK(MainFrameImpl()); |
| |
| GetBrowserControls().SetHeight(browser_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()); |
| UpdateICBAndResizeViewport(); |
| } |
| |
| 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 browser_controls_height, |
| bool browser_controls_shrink_layout) { |
| if (should_auto_resize_) |
| return; |
| |
| if (size_ == new_size && |
| GetBrowserControls().Height() == browser_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_); |
| return; |
| } |
| |
| WebLocalFrameImpl* main_frame = MainFrameImpl(); |
| if (!main_frame) |
| return; |
| |
| FrameView* 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(browser_controls_height, |
| browser_controls_shrink_layout); |
| } else { |
| ResizeViewportAnchor::ResizeScope resize_scope(*resize_viewport_anchor_); |
| ResizeViewWhileAnchored(browser_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().Height(), |
| 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); |
| |
| // Create synthetic wheel events as necessary for fling. |
| if (gesture_animation_) { |
| if (gesture_animation_->Animate(last_frame_time_monotonic)) |
| MainFrameImpl()->FrameWidget()->ScheduleAnimation(); |
| else { |
| DCHECK_NE(fling_source_device_, kWebGestureDeviceUninitialized); |
| WebGestureDevice last_fling_source_device = fling_source_device_; |
| EndActiveFlingAnimation(); |
| |
| WebGestureEvent end_scroll_event = CreateGestureScrollEventFromFling( |
| WebInputEvent::kGestureScrollEnd, last_fling_source_device); |
| MainFrameImpl()->GetFrame()->GetEventHandler().HandleGestureScrollEnd( |
| end_scroll_event); |
| } |
| } |
| |
| if (!MainFrameImpl()) |
| return; |
| |
| last_frame_time_monotonic_ = last_frame_time_monotonic; |
| |
| DocumentLifecycle::AllowThrottlingScope throttling_scope( |
| MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle()); |
| PageWidgetDelegate::Animate(*page_, last_frame_time_monotonic); |
| } |
| |
| void WebViewImpl::UpdateAllLifecyclePhases() { |
| TRACE_EVENT0("blink", "WebViewImpl::updateAllLifecyclePhases"); |
| if (!MainFrameImpl()) |
| return; |
| |
| DocumentLifecycle::AllowThrottlingScope throttling_scope( |
| MainFrameImpl()->GetFrame()->GetDocument()->Lifecycle()); |
| UpdateLayerTreeBackgroundColor(); |
| |
| PageWidgetDelegate::UpdateAllLifecyclePhases(*page_, |
| *MainFrameImpl()->GetFrame()); |
| |
| if (InspectorOverlayAgent* overlay = GetInspectorOverlay()) { |
| overlay->UpdateAllLifecyclePhases(); |
| // TODO(chrishtr): integrate paint into the overlay's lifecycle. |
| if (overlay->GetPageOverlay() && |
| overlay->GetPageOverlay()->GetGraphicsLayer()) |
| overlay->GetPageOverlay()->GetGraphicsLayer()->Paint(nullptr); |
| } |
| 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 (FrameView* 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()); |
| |
| double paint_start = CurrentTime(); |
| PageWidgetDelegate::Paint(*page_, canvas, rect, |
| *page_->DeprecatedLocalMainFrame()); |
| double paint_end = CurrentTime(); |
| double pixels_per_sec = |
| (rect.width * rect.height) / (paint_end - paint_start); |
| DEFINE_STATIC_LOCAL(CustomCountHistogram, software_paint_duration_histogram, |
| ("Renderer4.SoftwarePaintDurationMS", 0, 120, 30)); |
| software_paint_duration_histogram.Count((paint_end - paint_start) * 1000); |
| DEFINE_STATIC_LOCAL(CustomCountHistogram, software_paint_rate_histogram, |
| ("Renderer4.SoftwarePaintMegapixPerSecond", 10, 210, 30)); |
| software_paint_rate_histogram.Count(pixels_per_sec / 1000000); |
| } |
| |
| #if 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; |
| FrameView* 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* from_element, |
| Element* to_element) { |
| fullscreen_controller_->FullscreenElementChanged(from_element, to_element); |
| } |
| |
| bool WebViewImpl::HasHorizontalScrollbar() { |
| return MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutViewportScrollableArea() |
| ->HorizontalScrollbar(); |
| } |
| |
| bool WebViewImpl::HasVerticalScrollbar() { |
| return MainFrameImpl() |
| ->GetFrameView() |
| ->LayoutViewportScrollableArea() |
| ->VerticalScrollbar(); |
| } |
| |
| const WebInputEvent* WebViewImpl::current_input_event_ = nullptr; |
| |
| WebInputEventResult WebViewImpl::HandleInputEvent( |
| 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; |
| |
| 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 (InspectorOverlayAgent* overlay = GetInspectorOverlay()) { |
| if (overlay->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<const WebInputEvent*> current_event_change(¤t_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(input_event); |
| return WebInputEventResult::kHandledSystem; |
| } |
| |
| 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<UserGestureIndicator> gesture_indicator; |
| |
| AtomicString event_type; |
| switch (input_event.GetType()) { |
| 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 = WTF::WrapUnique( |
| new UserGestureIndicator(DocumentUserGestureToken::Create( |
| &node->GetDocument(), UserGestureToken::kNewGesture))); |
| mouse_capture_gesture_token_ = gesture_indicator->CurrentToken(); |
| break; |
| case WebInputEvent::kMouseUp: |
| event_type = EventTypeNames::mouseup; |
| gesture_indicator = WTF::WrapUnique( |
| new UserGestureIndicator(mouse_capture_gesture_token_.Release())); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| WebMouseEvent transformed_event = |
| TransformWebMouseEvent(MainFrameImpl()->GetFrameView(), |
| static_cast<const WebMouseEvent&>(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<const WebGestureEvent&>(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; |
| } |
| |
| 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().SetSelection( |
| 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; |
| } |
| } |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| WebRange WebViewImpl::CompositionRange() { |
| LocalFrame* focused = FocusedLocalFrameAvailableForIme(); |
| if (!focused) |
| return WebRange(); |
| |
| const EphemeralRange range = |
| focused->GetInputMethodController().CompositionEphemeralRange(); |
| if (range.IsNull()) |
| return WebRange(); |
| |
| Element* editable = |
| focused->Selection().RootEditableElementOrDocumentElement(); |
| DCHECK(editable); |
| |
| // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| // needs to be audited. See http://crbug.com/590369 for more details. |
| editable->GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets(); |
| |
| return PlainTextRange::Create(*editable, range); |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| bool WebViewImpl::SelectionBounds(WebRect& anchor, WebRect& focus) const { |
| const Frame* frame = FocusedCoreFrame(); |
| if (!frame || !frame->IsLocalFrame()) |
| return false; |
| |
| const LocalFrame* local_frame = ToLocalFrame(frame); |
| if (!local_frame) |
| return false; |
| FrameSelection& selection = local_frame->Selection(); |
| if (!selection.IsAvailable() || selection.GetSelectionInDOMTree().IsNone()) |
| return false; |
| |
| // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| // needs to be audited. See http://crbug.com/590369 for more details. |
| local_frame->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); |
| if (selection.ComputeVisibleSelectionInDOMTree().IsNone()) { |
| // plugins/mouse-capture-inside-shadow.html reaches here. |
| return false; |
| } |
| |
| DocumentLifecycle::DisallowTransitionScope disallow_transition( |
| local_frame->GetDocument()->Lifecycle()); |
| |
| if (selection.ComputeVisibleSelectionInDOMTreeDeprecated().IsCaret()) { |
| anchor = focus = selection.AbsoluteCaretBounds(); |
| } else { |
| const EphemeralRange selected_range = |
| selection.ComputeVisibleSelectionInDOMTreeDeprecated() |
| .ToNormalizedEphemeralRange(); |
| if (selected_range.IsNull()) |
| return false; |
| anchor = local_frame->GetEditor().FirstRectForRange( |
| EphemeralRange(selected_range.StartPosition())); |
| focus = local_frame->GetEditor().FirstRectForRange( |
| EphemeralRange(selected_range.EndPosition())); |
| } |
| |
| anchor = local_frame->View()->ContentsToViewport(anchor); |
| focus = local_frame->View()->ContentsToViewport(focus); |
| |
| if (!selection.ComputeVisibleSelectionInDOMTreeDeprecated().IsBaseFirst()) |
| std::swap(anchor, focus); |
| return true; |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| WebPlugin* WebViewImpl::FocusedPluginIfInputMethodSupported(LocalFrame* frame) { |
| WebPluginContainerImpl* container = |
| WebLocalFrameImpl::CurrentPluginContainer(frame); |
| if (container && container->SupportsInputMethod()) |
| return container->Plugin(); |
| return nullptr; |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| bool WebViewImpl::SelectionTextDirection(WebTextDirection& start, |
| WebTextDirection& end) const { |
| const LocalFrame* frame = FocusedLocalFrameInWidget(); |
| if (!frame) |
| return false; |
| |
| const FrameSelection& selection = frame->Selection(); |
| if (!selection.IsAvailable()) { |
| // plugins/mouse-capture-inside-shadow.html reaches here. |
| return false; |
| } |
| |
| // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| // needs to be audited. See http://crbug.com/590369 for more details. |
| frame->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); |
| |
| if (selection.ComputeVisibleSelectionInDOMTree() |
| .ToNormalizedEphemeralRange() |
| .IsNull()) |
| return false; |
| start = ToWebTextDirection( |
| PrimaryDirectionOf(*selection.ComputeVisibleSelectionInDOMTreeDeprecated() |
| .Start() |
| .AnchorNode())); |
| end = ToWebTextDirection( |
| PrimaryDirectionOf(*selection.ComputeVisibleSelectionInDOMTreeDeprecated() |
| .end() |
| .AnchorNode())); |
| return true; |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| bool WebViewImpl::IsSelectionAnchorFirst() const { |
| const LocalFrame* frame = FocusedLocalFrameInWidget(); |
| if (!frame) |
| return false; |
| |
| FrameSelection& selection = frame->Selection(); |
| if (!selection.IsAvailable()) { |
| // plugins/mouse-capture-inside-shadow.html reaches here. |
| return false; |
| } |
| return selection.ComputeVisibleSelectionInDOMTreeDeprecated().IsBaseFirst(); |
| } |
| |
| 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(); |
| FrameView* view = page_->DeprecatedLocalMainFrame()->View(); |
| if (!view) |
| return BaseBackgroundColor().Rgb(); |
| return view->DocumentBackgroundColor().Rgb(); |
| } |
| |
| WebPagePopupImpl* WebViewImpl::GetPagePopup() const { |
| return page_popup_.Get(); |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| WebRange WebViewImpl::CaretOrSelectionRange() { |
| const LocalFrame* focused = FocusedLocalFrameInWidget(); |
| if (!focused) |
| return WebRange(); |
| |
| // TODO(editing-dev): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| // needs to be audited. See http://crbug.com/590369 for more details. |
| focused->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets(); |
| |
| return focused->GetInputMethodController().GetSelectionOffsets(); |
| } |
| |
| // TODO(ekaramad):This method is almost duplicated in WebFrameWidgetImpl as |
| // well. This code needs to be refactored (http://crbug.com/629721). |
| void WebViewImpl::SetTextDirection(WebTextDirection direction) { |
| // The Editor::setBaseWritingDirection() function checks if we can change |
| // the text direction of the selected node and updates its DOM "dir" |
| // attribute and its CSS "direction" property. |
| // So, we just call the function as Safari does. |
| const LocalFrame* focused = FocusedLocalFrameInWidget(); |
| if (!focused) |
| return; |
| |
| Editor& editor = focused->GetEditor(); |
| if (!editor.CanEdit()) |
| return; |
| |
| switch (direction) { |
| case kWebTextDirectionDefault: |
| editor.SetBaseWritingDirection(NaturalWritingDirection); |
| break; |
| |
| case kWebTextDirectionLeftToRight: |
| editor.SetBaseWritingDirection(LeftToRightWritingDirection); |
| break; |
| |
| case kWebTextDirectionRightToLeft: |
| editor.SetBaseWritingDirection(RightToLeftWritingDirection); |
| break; |
| |
| default: |
| NOTIMPLEMENTED(); |
| break; |
| } |
| } |
| |
| 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<WebRect>& bounds) { |
| WebRange range = 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<WebRect> 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); |
| } |
| |
| WebFrame* WebViewImpl::FindFrameByName(const WebString& name, |
| WebFrame* relative_to_frame) { |
| // FIXME: Either this should only deal with WebLocalFrames or it should move |
| // to WebFrame. |
| if (!relative_to_frame) |
| relative_to_frame = MainFrame(); |
| Frame* frame = ToWebLocalFrameImpl(relative_to_frame)->GetFrame(); |
| frame = frame->Tree().Find(name); |
| if (!frame || !frame->IsLocalFrame()) |
| return nullptr; |
| return WebLocalFrameImpl::FromFrame(ToLocalFrame(frame)); |
| } |
| |
| 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().SetFocused(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::ScrollFocusedEditableElementIntoRect( |
| const WebRect& rect_in_viewport) { |
| LocalFrame* frame = |
| GetPage()->MainFrame() && GetPage()->MainFrame()->IsLocalFrame() |
| ? GetPage()->DeprecatedLocalMainFrame() |
| : nullptr; |
| Element* element = FocusedElement(); |
| if (!frame || !frame->View() || !element) |
| return false; |
| |
| if (!IsElementEditable(element)) |
| 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 & 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(); |
| |
| 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 = MainFrameImpl()->GetFrameView()->FrameToContents( |
| visual_viewport.ViewportToRootFrame(caret_in_viewport)); |
| IntRect textbox_rect_in_document = |
| MainFrameImpl()->GetFrameView()->FrameToContents( |
| focused_node->GetDocument().View()->ContentsToRootFrame( |
| 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<int>(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<int>(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<int>(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 (!WebLocalFrameImpl::PluginContainerFromFrame(local_frame)) |
| 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<float>(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 (WebLocalFrameImpl::PluginContainerFromFrame(frame)) |
| 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; |
| |
| FrameView* 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; |
| |
| GetPage()->SetDeviceScaleFactorDeprecated(scale_factor); |
| |
| if (layer_tree_view_) |
| UpdateLayerTreeDeviceScaleFactor(); |
| } |
| |
| void WebViewImpl::SetZoomFactorForDeviceScaleFactor( |
| float zoom_factor_for_device_scale_factor) { |
| zoom_factor_for_device_scale_factor_ = zoom_factor_for_device_scale_factor; |
| if (!layer_tree_view_) |
| return; |
| SetZoomLevel(zoom_level_); |
| } |
| |
| void WebViewImpl::SetDeviceColorProfile(const gfx::ICCProfile& color_profile) { |
| ColorBehavior::SetGlobalTargetColorProfile(color_profile); |
| } |
| |
| 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; |
| FrameView* 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(); |
| |
| // Changes to page-scale during layout may require an additional frame. |
| // We can't update the lifecycle here because we may be in the middle of |
| // layout in the caller of this method. |
| // TODO(chrishtr): clean all this up. All layout should happen in one |
| // lifecycle run (crbug.com/578239). |
| if (MainFrameImpl()->GetFrameView()->NeedsLayout()) |
| MainFrameImpl()->FrameWidget()->ScheduleAnimation(); |
| } |
| |
| 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; |
| |
| FrameView* 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(); |
| LayoutViewItem root = |
| GetPage()->DeprecatedLocalMainFrame()->ContentLayoutItem(); |
| if (root.IsNull()) |
| return IntSize(); |
| return root.DocumentRect().Size(); |
| } |
| |
| WebSize WebViewImpl::ContentsPreferredMinimumSize() { |
| if (MainFrameImpl()) |
| MainFrameImpl() |
| ->GetFrame() |
| ->View() |
| ->UpdateLifecycleToCompositingCleanPlusScrolling(); |
| |
| Document* document = page_->MainFrame()->IsLocalFrame() |
| ? page_->DeprecatedLocalMainFrame()->GetDocument() |
| : nullptr; |
| if (!document || document->GetLayoutViewItem().IsNull() || |
| !document->documentElement() || |
| !document->documentElement()->GetLayoutBox()) |
| return WebSize(); |
| |
| int width_scaled = document->GetLayoutViewItem() |
| .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 (FrameView* frame_view = ToLocalFrame(GetPage()->MainFrame())->View()) { |
| ScrollableArea* scrollable_area = |
| frame_view->LayoutViewportScrollableArea(); |
| |
| if (!scrollable_area->GetScrollOffset().IsZero()) |
| scrollable_area->SetScrollOffset(ScrollOffset(), kProgrammaticScroll); |
| } |
| |
| GetPageScaleConstraintsSet().SetNeedsReset(true); |
| } |
| |
| void WebViewImpl::PerformMediaPlayerAction(const WebMediaPlayerAction& action, |
| const WebPoint& location) { |
| HitTestResult result = HitTestResultForViewportPos(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(location); |
| Node* node = result.InnerNode(); |
| if (!isHTMLObjectElement(*node) && !isHTMLEmbedElement(*node)) |
| return; |
| |
| LayoutObject* object = node->GetLayoutObject(); |
| if (object && object->IsLayoutPart()) { |
| PluginView* plugin_view = ToLayoutPart(object)->Plugin(); |
| if (plugin_view && plugin_view->IsPluginContainer()) { |
| WebPluginContainerImpl* plugin = ToWebPluginContainerImpl(plugin_view); |
| switch (action.type) { |
| case WebPluginAction::kRotate90Clockwise: |
| plugin->Plugin()->RotateView(WebPlugin::kRotationType90Clockwise); |
| break; |
| case WebPluginAction::kRotate90Counterclockwise: |
| plugin->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()); |
| FrameView* view = MainFrameImpl()->GetFrameView(); |
| IntPoint point_in_root_frame = |
| view->ContentsToFrame(view->ViewportToContents(point_in_viewport)); |
| return HitTestResultForRootFramePos(point_in_root_frame); |
| } |
| |
| void WebViewImpl::SendResizeEventAndRepaint() { |
| // FIXME: This is wrong. The FrameView 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(); |
| } |
| |
| WebAXObject WebViewImpl::AccessibilityObject() { |
| if (!MainFrameImpl()) |
| return WebAXObject(); |
| |
| Document* document = MainFrameImpl()->GetFrame()->GetDocument(); |
| return WebAXObject(ToAXObjectCacheImpl(document->AxObjectCache())->Root()); |
| } |
| |
| void WebViewImpl::PerformCustomContextMenuAction(unsigned action) { |
| if (!page_) |
| return; |
| ContextMenu* menu = page_->GetContextMenuController().GetContextMenu(); |
| if (!menu) |
| return; |
| const ContextMenuItem* item = menu->ItemWithAction( |
| static_cast<ContextMenuAction>(kContextMenuItemBaseCustomTag + action)); |
| if (item) |
| page_->GetContextMenuController().ContextMenuItemSelected(item); |
| page_->GetContextMenuController().ClearContextMenu(); |
| } |
| |
| void WebViewImpl::ShowContextMenu() { |
| if (!GetPage()) |
| return; |
| |
| GetPage()->GetContextMenuController().ClearContextMenu(); |
| { |
| ContextMenuAllowedScope scope; |
| if (LocalFrame* focused_frame = |
| ToLocalFrame(GetPage()->GetFocusController().FocusedOrMainFrame())) |
| focused_frame->GetEventHandler().SendContextMenuEventForKey(nullptr); |
| } |
| } |
| |
| void WebViewImpl::DidCloseContextMenu() { |
| LocalFrame* frame = page_->GetFocusController().FocusedFrame(); |
| if (frame) |
| frame->Selection().SetCaretBlinkingSuspended(false); |
| } |
| |
| void WebViewImpl::HidePopups() { |
| CancelPagePopup(); |
| } |
| |
| WebInputMethodControllerImpl* WebViewImpl::GetActiveWebInputMethodController() |
| const { |
| return WebInputMethodControllerImpl::FromFrame(FocusedLocalFrameInWidget()); |
| } |
| |
| 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 |
| // FrameView::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 |
| // FrameView::setBaseBackgroundColor(). |
| MainFrameImpl() |
| ->GetFrame() |
| ->View() |
| ->UpdateLifecycleToCompositingCleanPlusScrolling(); |
| } |
| UpdateBaseBackgroundColor(); |
| } |
| |
| void WebViewImpl::UpdateBaseBackgroundColor() { |
| Color color = BaseBackgroundColor(); |
| if (page_->MainFrame() && page_->MainFrame()->IsLocalFrame()) { |
| FrameView* 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_->GetChromeClient().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 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(); |
| EndActiveFlingAnimation(); |
| } |
| |
| void WebViewImpl::ResizeFrameView(WebLocalFrameImpl* webframe) { |
| FrameView* view = webframe->GetFrame()->View(); |
| if (webframe == MainFrame()) |
| resize_viewport_anchor_->ResizeFrameView(MainFrameSize()); |
| else |
| view->Resize(webframe->GetFrameView()->Size()); |
| } |
| |
| void WebViewImpl::ResizeAfterLayout(WebLocalFrameImpl* webframe) { |
| LocalFrame* frame = webframe->GetFrame(); |
| if (!client_ || !client_->CanUpdateLayout() || !frame->IsMainFrame()) |
| return; |
| |
| if (should_auto_resize_) { |
| WebSize frame_size = frame->View()->FrameRect().Size(); |
| if (frame_size != size_) { |
| size_ = frame_size; |
| |
| GetPage()->GetVisualViewport().SetSize(size_); |
| GetPageScaleConstraintsSet().DidChangeInitialContainingBlockSize(size_); |
| frame->View()->SetInitialViewportSize(size_); |
| |
| client_->DidAutoResize(size_); |
| SendResizeEventAndRepaint(); |
| } |
| } |
| |
| if (GetPageScaleConstraintsSet().ConstraintsDirty()) |
| RefreshPageScaleFactorAfterLayout(); |
| |
| ResizeFrameView(webframe); |
| } |
| |
| void WebViewImpl::LayoutUpdated(WebLocalFrameImpl* webframe) { |
| LocalFrame* frame = webframe->GetFrame(); |
| if (!client_ || !client_->CanUpdateLayout() || !frame->IsMainFrame()) |
| return; |
| |
| ResizeAfterLayout(webframe); |
| |
| // Relayout immediately to avoid violating the rule that needsLayout() |
| // isn't set at the end of a layout. |
| FrameView* view = frame->View(); |
| if (view->NeedsLayout()) |
| view->UpdateLayout(); |
| |
| 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(); |
| } |
| |
| bool WebViewBase::UseExternalPopupMenus() { |
| return WebViewImpl::UseExternalPopupMenus(); |
| } |
| |
| bool WebViewImpl::UseExternalPopupMenus() { |
| return g_should_use_external_popup_menus; |
| } |
| |
| 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(), WTF::MakeUnique<ColorOverlay>(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::HitTestResultForViewportPos( |
| const IntPoint& pos_in_viewport) { |
| IntPoint root_frame_point( |
| page_->GetVisualViewport().ViewportToRootFrame(pos_in_viewport)); |
| return HitTestResultForRootFramePos(root_frame_point); |
| } |
| |
| HitTestResult WebViewImpl::HitTestResultForRootFramePos( |
| const IntPoint& pos_in_root_frame) { |
| if (!page_->MainFrame()->IsLocalFrame()) |
| return HitTestResult(); |
| IntPoint 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::MonotonicallyIncreasingTime()); |
| 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 layer. |
| GraphicsLayer* layout_viewport_scroll_layer = |
| GetPage()->GlobalRootScrollerController().RootScrollerLayer(); |
| WebLayer* layout_viewport_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_web_layer); |
| |
| layer_tree_view_->RegisterViewportLayers( |
| visual_viewport.OverscrollElasticityLayer()->PlatformLayer(), |
| visual_viewport.PageScaleLayer()->PlatformLayer(), |
| visual_viewport.ScrollLayer()->PlatformLayer(), |
| layout_viewport_web_layer); |
| } |
| |
| 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->GetLayoutViewItem().IsNull()) |
| return nullptr; |
| |
| return document->GetLayoutViewItem().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_ = WTF::MakeUnique<CompositorAnimationHost>( |
| 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 (!GetPage() || !GetPage()->MainFrame()) |
| return; |
| |
| if (has_scrolled_by_wheel) |
| UseCounter::Count(GetPage()->MainFrame(), UseCounter::kScrollByWheel); |
| if (has_scrolled_by_touch) |
| UseCounter::Count(GetPage()->MainFrame(), UseCounter::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(WebPageVisibilityState visibility_state, |
| bool is_initial_state) { |
| DCHECK(visibility_state == kWebPageVisibilityStateVisible || |
| visibility_state == kWebPageVisibilityStateHidden || |
| visibility_state == kWebPageVisibilityStatePrerender); |
| |
| if (GetPage()) |
| page_->SetVisibilityState( |
| static_cast<PageVisibilityState>(static_cast<int>(visibility_state)), |
| is_initial_state); |
| |
| bool visible = visibility_state == kWebPageVisibilityStateVisible; |
| 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() { |
| WebGLRenderingContext::ForceNextWebGLContextCreationToFail(); |
| } |
| |
| void WebViewImpl::ForceNextDrawingBufferCreationToFail() { |
| DrawingBuffer::ForceNextDrawingBufferCreationToFail(); |
| } |
| |
| CompositorMutatorImpl& WebViewImpl::Mutator() { |
| if (!mutator_) { |
| std::unique_ptr<CompositorMutatorClient> mutator_client = |
| CompositorMutatorImpl::CreateClient(); |
| mutator_ = static_cast<CompositorMutatorImpl*>(mutator_client->Mutator()); |
| layer_tree_view_->SetMutatorClient(std::move(mutator_client)); |
| } |
| |
| return *mutator_; |
| } |
| |
| CompositorWorkerProxyClient* WebViewImpl::CreateCompositorWorkerProxyClient() { |
| return new CompositorWorkerProxyClientImpl(&Mutator()); |
| } |
| |
| AnimationWorkletProxyClient* WebViewImpl::CreateAnimationWorkletProxyClient() { |
| return new AnimationWorkletProxyClientImpl(&Mutator()); |
| } |
| |
| void WebViewImpl::UpdatePageOverlays() { |
| if (page_color_overlay_) |
| page_color_overlay_->Update(); |
| if (InspectorOverlayAgent* overlay = GetInspectorOverlay()) { |
| PageOverlay* inspector_page_overlay = overlay->GetPageOverlay(); |
| if (inspector_page_overlay) |
| inspector_page_overlay->Update(); |
| } |
| } |
| |
| 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 |