| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
| * |
| * 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/ChromeClientImpl.h" |
| |
| #include "bindings/core/v8/ScriptController.h" |
| #include "core/HTMLNames.h" |
| #include "core/dom/AXObjectCache.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/Fullscreen.h" |
| #include "core/dom/Node.h" |
| #include "core/events/UIEventWithKeyState.h" |
| #include "core/frame/FrameHost.h" |
| #include "core/frame/FrameView.h" |
| #include "core/frame/Settings.h" |
| #include "core/frame/VisualViewport.h" |
| #include "core/html/HTMLInputElement.h" |
| #include "core/html/forms/ColorChooser.h" |
| #include "core/html/forms/ColorChooserClient.h" |
| #include "core/html/forms/DateTimeChooser.h" |
| #include "core/layout/HitTestResult.h" |
| #include "core/layout/LayoutPart.h" |
| #include "core/layout/compositing/CompositedSelection.h" |
| #include "core/loader/DocumentLoader.h" |
| #include "core/loader/FrameLoadRequest.h" |
| #include "core/page/Page.h" |
| #include "core/page/PopupOpeningObserver.h" |
| #include "modules/accessibility/AXObject.h" |
| #include "modules/audio_output_devices/AudioOutputDeviceClient.h" |
| #include "modules/bluetooth/BluetoothSupplement.h" |
| #include "modules/installedapp/InstalledAppController.h" |
| #include "modules/mediastream/UserMediaController.h" |
| #include "modules/presentation/PresentationController.h" |
| #include "modules/push_messaging/PushController.h" |
| #include "modules/screen_orientation/ScreenOrientationController.h" |
| #include "modules/vr/VRController.h" |
| #include "platform/Cursor.h" |
| #include "platform/FileChooser.h" |
| #include "platform/Histogram.h" |
| #include "platform/KeyboardCodes.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| #include "platform/exported/WrappedResourceRequest.h" |
| #include "platform/geometry/IntRect.h" |
| #include "platform/graphics/GraphicsLayer.h" |
| #include "platform/weborigin/SecurityOrigin.h" |
| #include "public/platform/WebCursorInfo.h" |
| #include "public/platform/WebFloatRect.h" |
| #include "public/platform/WebFrameScheduler.h" |
| #include "public/platform/WebInputEvent.h" |
| #include "public/platform/WebRect.h" |
| #include "public/platform/WebURLRequest.h" |
| #include "public/platform/WebViewScheduler.h" |
| #include "public/web/WebAXObject.h" |
| #include "public/web/WebAutofillClient.h" |
| #include "public/web/WebColorChooser.h" |
| #include "public/web/WebColorSuggestion.h" |
| #include "public/web/WebConsoleMessage.h" |
| #include "public/web/WebFrameClient.h" |
| #include "public/web/WebInputElement.h" |
| #include "public/web/WebKit.h" |
| #include "public/web/WebNode.h" |
| #include "public/web/WebPageImportanceSignals.h" |
| #include "public/web/WebPlugin.h" |
| #include "public/web/WebPopupMenuInfo.h" |
| #include "public/web/WebSelection.h" |
| #include "public/web/WebSettings.h" |
| #include "public/web/WebTextDirection.h" |
| #include "public/web/WebTouchAction.h" |
| #include "public/web/WebUserGestureIndicator.h" |
| #include "public/web/WebUserGestureToken.h" |
| #include "public/web/WebViewClient.h" |
| #include "public/web/WebWindowFeatures.h" |
| #include "web/AudioOutputDeviceClientImpl.h" |
| #include "web/ColorChooserPopupUIController.h" |
| #include "web/ColorChooserUIController.h" |
| #include "web/DateTimeChooserImpl.h" |
| #include "web/DevToolsEmulator.h" |
| #include "web/ExternalDateTimeChooser.h" |
| #include "web/ExternalPopupMenu.h" |
| #include "web/IndexedDBClientImpl.h" |
| #include "web/LocalFileSystemClient.h" |
| #include "web/NavigatorContentUtilsClientImpl.h" |
| #include "web/PopupMenuImpl.h" |
| #include "web/WebFileChooserCompletionImpl.h" |
| #include "web/WebFrameWidgetImpl.h" |
| #include "web/WebInputEventConversion.h" |
| #include "web/WebLocalFrameImpl.h" |
| #include "web/WebPluginContainerImpl.h" |
| #include "web/WebSettingsImpl.h" |
| #include "web/WebViewImpl.h" |
| #include "wtf/Optional.h" |
| #include "wtf/PtrUtil.h" |
| #include "wtf/text/CString.h" |
| #include "wtf/text/CharacterNames.h" |
| #include "wtf/text/StringBuilder.h" |
| #include "wtf/text/StringConcatenate.h" |
| #include <memory> |
| |
| namespace blink { |
| |
| namespace { |
| |
| const char* dialogTypeToString(ChromeClient::DialogType dialogType) { |
| switch (dialogType) { |
| case ChromeClient::AlertDialog: |
| return "alert"; |
| case ChromeClient::ConfirmDialog: |
| return "confirm"; |
| case ChromeClient::PromptDialog: |
| return "prompt"; |
| case ChromeClient::HTMLDialog: |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| return ""; |
| } |
| |
| const char* dismissalTypeToString(Document::PageDismissalType dismissalType) { |
| switch (dismissalType) { |
| case Document::BeforeUnloadDismissal: |
| return "beforeunload"; |
| case Document::PageHideDismissal: |
| return "pagehide"; |
| case Document::UnloadVisibilityChangeDismissal: |
| return "visibilitychange"; |
| case Document::UnloadDismissal: |
| return "unload"; |
| case Document::NoDismissal: |
| NOTREACHED(); |
| } |
| NOTREACHED(); |
| return ""; |
| } |
| |
| } // namespace |
| |
| class CompositorAnimationTimeline; |
| |
| // Converts a AXObjectCache::AXNotification to a WebAXEvent |
| static WebAXEvent toWebAXEvent(AXObjectCache::AXNotification notification) { |
| // These enums have the same values; enforced in AssertMatchingEnums.cpp. |
| return static_cast<WebAXEvent>(notification); |
| } |
| |
| ChromeClientImpl::ChromeClientImpl(WebViewImpl* webView) |
| : m_webView(webView), |
| m_cursorOverridden(false), |
| m_didRequestNonEmptyToolTip(false) {} |
| |
| ChromeClientImpl::~ChromeClientImpl() {} |
| |
| ChromeClientImpl* ChromeClientImpl::create(WebViewImpl* webView) { |
| return new ChromeClientImpl(webView); |
| } |
| |
| void* ChromeClientImpl::webView() const { |
| return static_cast<void*>(m_webView); |
| } |
| |
| void ChromeClientImpl::chromeDestroyed() { |
| // Our lifetime is bound to the WebViewImpl. |
| } |
| |
| void ChromeClientImpl::setWindowRect(const IntRect& r, LocalFrame& frame) { |
| DCHECK_EQ(&frame, m_webView->mainFrameImpl()->frame()); |
| WebWidgetClient* client = |
| WebLocalFrameImpl::fromFrame(&frame)->frameWidget()->client(); |
| client->setWindowRect(r); |
| } |
| |
| IntRect ChromeClientImpl::rootWindowRect() { |
| WebRect rect; |
| if (m_webView->client()) { |
| rect = m_webView->client()->rootWindowRect(); |
| } else { |
| // These numbers will be fairly wrong. The window's x/y coordinates will |
| // be the top left corner of the screen and the size will be the content |
| // size instead of the window size. |
| rect.width = m_webView->size().width; |
| rect.height = m_webView->size().height; |
| } |
| return IntRect(rect); |
| } |
| |
| IntRect ChromeClientImpl::pageRect() { |
| // We hide the details of the window's border thickness from the web page by |
| // simple re-using the window position here. So, from the point-of-view of |
| // the web page, the window has no border. |
| return rootWindowRect(); |
| } |
| |
| void ChromeClientImpl::focus() { |
| if (m_webView->client()) |
| m_webView->client()->didFocus(); |
| } |
| |
| bool ChromeClientImpl::canTakeFocus(WebFocusType) { |
| // For now the browser can always take focus if we're not running layout |
| // tests. |
| return !layoutTestMode(); |
| } |
| |
| void ChromeClientImpl::takeFocus(WebFocusType type) { |
| if (!m_webView->client()) |
| return; |
| if (type == WebFocusTypeBackward) |
| m_webView->client()->focusPrevious(); |
| else |
| m_webView->client()->focusNext(); |
| } |
| |
| void ChromeClientImpl::focusedNodeChanged(Node* fromNode, Node* toNode) { |
| if (!m_webView->client()) |
| return; |
| |
| m_webView->client()->focusedNodeChanged(WebNode(fromNode), WebNode(toNode)); |
| |
| WebURL focusURL; |
| if (toNode && toNode->isElementNode() && toElement(toNode)->isLiveLink() && |
| toNode->shouldHaveFocusAppearance()) |
| focusURL = toElement(toNode)->hrefURL(); |
| m_webView->client()->setKeyboardFocusURL(focusURL); |
| } |
| |
| bool ChromeClientImpl::hadFormInteraction() const { |
| return m_webView->pageImportanceSignals() && |
| m_webView->pageImportanceSignals()->hadFormInteraction(); |
| } |
| |
| void ChromeClientImpl::startDragging(LocalFrame* frame, |
| const WebDragData& dragData, |
| WebDragOperationsMask mask, |
| const WebImage& dragImage, |
| const WebPoint& dragImageOffset) { |
| WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(frame); |
| WebReferrerPolicy policy = webFrame->document().referrerPolicy(); |
| webFrame->localRoot()->frameWidget()->startDragging( |
| policy, dragData, mask, dragImage, dragImageOffset); |
| } |
| |
| bool ChromeClientImpl::acceptsLoadDrops() const { |
| return !m_webView->client() || m_webView->client()->acceptsLoadDrops(); |
| } |
| |
| namespace { |
| |
| void updatePolicyForEvent(const WebInputEvent* inputEvent, |
| NavigationPolicy* policy) { |
| if (!inputEvent) |
| return; |
| |
| unsigned short buttonNumber = 0; |
| if (inputEvent->type == WebInputEvent::MouseUp) { |
| const WebMouseEvent* mouseEvent = |
| static_cast<const WebMouseEvent*>(inputEvent); |
| |
| switch (mouseEvent->button) { |
| case WebMouseEvent::Button::Left: |
| buttonNumber = 0; |
| break; |
| case WebMouseEvent::Button::Middle: |
| buttonNumber = 1; |
| break; |
| case WebMouseEvent::Button::Right: |
| buttonNumber = 2; |
| break; |
| default: |
| return; |
| } |
| } else if ((WebInputEvent::isKeyboardEventType(inputEvent->type) && |
| static_cast<const WebKeyboardEvent*>(inputEvent) |
| ->windowsKeyCode == VKEY_RETURN) || |
| WebInputEvent::isGestureEventType(inputEvent->type)) { |
| // Keyboard and gesture events can simulate mouse events. |
| buttonNumber = 0; |
| } else { |
| return; |
| } |
| |
| bool ctrl = inputEvent->modifiers & WebInputEvent::ControlKey; |
| bool shift = inputEvent->modifiers & WebInputEvent::ShiftKey; |
| bool alt = inputEvent->modifiers & WebInputEvent::AltKey; |
| bool meta = inputEvent->modifiers & WebInputEvent::MetaKey; |
| |
| NavigationPolicy userPolicy = *policy; |
| navigationPolicyFromMouseEvent(buttonNumber, ctrl, shift, alt, meta, |
| &userPolicy); |
| |
| // When the input event suggests a download, but the navigation was initiated |
| // by script, we should not override it. |
| if (userPolicy == NavigationPolicyDownload && |
| *policy != NavigationPolicyIgnore) |
| return; |
| |
| // User and app agree that we want a new window; let the app override the |
| // decorations. |
| if (userPolicy == NavigationPolicyNewWindow && |
| *policy == NavigationPolicyNewPopup) |
| return; |
| *policy = userPolicy; |
| } |
| |
| WebNavigationPolicy getNavigationPolicy(const WindowFeatures& features) { |
| // If our default configuration was modified by a script or wasn't |
| // created by a user gesture, then show as a popup. Else, let this |
| // new window be opened as a toplevel window. |
| bool asPopup = !features.toolBarVisible || !features.statusBarVisible || |
| !features.scrollbarsVisible || !features.menuBarVisible || |
| !features.resizable; |
| |
| NavigationPolicy policy = NavigationPolicyNewForegroundTab; |
| if (asPopup) |
| policy = NavigationPolicyNewPopup; |
| updatePolicyForEvent(WebViewImpl::currentInputEvent(), &policy); |
| |
| return static_cast<WebNavigationPolicy>(policy); |
| } |
| |
| WebNavigationPolicy effectiveNavigationPolicy(NavigationPolicy navigationPolicy, |
| const WindowFeatures& features) { |
| WebNavigationPolicy policy = |
| static_cast<WebNavigationPolicy>(navigationPolicy); |
| if (policy == WebNavigationPolicyIgnore) |
| return getNavigationPolicy(features); |
| if (policy == WebNavigationPolicyNewBackgroundTab && |
| getNavigationPolicy(features) != WebNavigationPolicyNewBackgroundTab && |
| !UIEventWithKeyState::newTabModifierSetFromIsolatedWorld()) |
| return WebNavigationPolicyNewForegroundTab; |
| |
| return policy; |
| } |
| |
| } // namespace |
| |
| Page* ChromeClientImpl::createWindow(LocalFrame* frame, |
| const FrameLoadRequest& r, |
| const WindowFeatures& features, |
| NavigationPolicy navigationPolicy) { |
| if (!m_webView->client()) |
| return nullptr; |
| |
| if (!frame->page() || frame->page()->suspended()) |
| return nullptr; |
| |
| WebNavigationPolicy policy = |
| effectiveNavigationPolicy(navigationPolicy, features); |
| DCHECK(frame->document()); |
| Fullscreen::fullyExitFullscreen(*frame->document()); |
| |
| WebViewImpl* newView = toWebViewImpl(m_webView->client()->createView( |
| WebLocalFrameImpl::fromFrame(frame), |
| WrappedResourceRequest(r.resourceRequest()), features, r.frameName(), |
| policy, r.getShouldSetOpener() == NeverSetOpener || features.noopener)); |
| if (!newView) |
| return nullptr; |
| return newView->page(); |
| } |
| |
| void ChromeClientImpl::didOverscroll(const FloatSize& overscrollDelta, |
| const FloatSize& accumulatedOverscroll, |
| const FloatPoint& positionInViewport, |
| const FloatSize& velocityInViewport) { |
| if (!m_webView->client()) |
| return; |
| |
| m_webView->client()->didOverscroll(overscrollDelta, accumulatedOverscroll, |
| positionInViewport, velocityInViewport); |
| } |
| |
| void ChromeClientImpl::show(NavigationPolicy navigationPolicy) { |
| if (m_webView->client()) |
| m_webView->client()->show( |
| effectiveNavigationPolicy(navigationPolicy, m_windowFeatures)); |
| } |
| |
| void ChromeClientImpl::setToolbarsVisible(bool value) { |
| m_windowFeatures.toolBarVisible = value; |
| } |
| |
| bool ChromeClientImpl::toolbarsVisible() { |
| return m_windowFeatures.toolBarVisible; |
| } |
| |
| void ChromeClientImpl::setStatusbarVisible(bool value) { |
| m_windowFeatures.statusBarVisible = value; |
| } |
| |
| bool ChromeClientImpl::statusbarVisible() { |
| return m_windowFeatures.statusBarVisible; |
| } |
| |
| void ChromeClientImpl::setScrollbarsVisible(bool value) { |
| m_windowFeatures.scrollbarsVisible = value; |
| if (WebLocalFrameImpl* webFrame = toWebLocalFrameImpl(m_webView->mainFrame())) |
| webFrame->setCanHaveScrollbars(value); |
| } |
| |
| bool ChromeClientImpl::scrollbarsVisible() { |
| return m_windowFeatures.scrollbarsVisible; |
| } |
| |
| void ChromeClientImpl::setMenubarVisible(bool value) { |
| m_windowFeatures.menuBarVisible = value; |
| } |
| |
| bool ChromeClientImpl::menubarVisible() { |
| return m_windowFeatures.menuBarVisible; |
| } |
| |
| void ChromeClientImpl::setResizable(bool value) { |
| m_windowFeatures.resizable = value; |
| } |
| |
| bool ChromeClientImpl::shouldReportDetailedMessageForSource( |
| LocalFrame& localFrame, |
| const String& url) { |
| WebLocalFrameImpl* webframe = |
| WebLocalFrameImpl::fromFrame(localFrame.localFrameRoot()); |
| return webframe && webframe->client() && |
| webframe->client()->shouldReportDetailedMessageForSource(url); |
| } |
| |
| void ChromeClientImpl::addMessageToConsole(LocalFrame* localFrame, |
| MessageSource source, |
| MessageLevel level, |
| const String& message, |
| unsigned lineNumber, |
| const String& sourceID, |
| const String& stackTrace) { |
| WebLocalFrameImpl* frame = WebLocalFrameImpl::fromFrame(localFrame); |
| if (frame && frame->client()) { |
| frame->client()->didAddMessageToConsole( |
| WebConsoleMessage(static_cast<WebConsoleMessage::Level>(level), |
| message), |
| sourceID, lineNumber, stackTrace); |
| } |
| } |
| |
| bool ChromeClientImpl::canOpenBeforeUnloadConfirmPanel() { |
| return !!m_webView->client(); |
| } |
| |
| bool ChromeClientImpl::openBeforeUnloadConfirmPanelDelegate(LocalFrame* frame, |
| bool isReload) { |
| notifyPopupOpeningObservers(); |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| return webframe->client() && |
| webframe->client()->runModalBeforeUnloadDialog(isReload); |
| } |
| |
| void ChromeClientImpl::closeWindowSoon() { |
| if (m_webView->client()) |
| m_webView->client()->closeWidgetSoon(); |
| } |
| |
| // Although a LocalFrame is passed in, we don't actually use it, since we |
| // already know our own m_webView. |
| bool ChromeClientImpl::openJavaScriptAlertDelegate(LocalFrame* frame, |
| const String& message) { |
| notifyPopupOpeningObservers(); |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| if (webframe->client()) { |
| if (WebUserGestureIndicator::isProcessingUserGesture()) |
| WebUserGestureIndicator::currentUserGestureToken().setJavascriptPrompt(); |
| webframe->client()->runModalAlertDialog(message); |
| return true; |
| } |
| return false; |
| } |
| |
| // See comments for openJavaScriptAlertDelegate(). |
| bool ChromeClientImpl::openJavaScriptConfirmDelegate(LocalFrame* frame, |
| const String& message) { |
| notifyPopupOpeningObservers(); |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| if (webframe->client()) { |
| if (WebUserGestureIndicator::isProcessingUserGesture()) |
| WebUserGestureIndicator::currentUserGestureToken().setJavascriptPrompt(); |
| return webframe->client()->runModalConfirmDialog(message); |
| } |
| return false; |
| } |
| |
| // See comments for openJavaScriptAlertDelegate(). |
| bool ChromeClientImpl::openJavaScriptPromptDelegate(LocalFrame* frame, |
| const String& message, |
| const String& defaultValue, |
| String& result) { |
| notifyPopupOpeningObservers(); |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| if (webframe->client()) { |
| if (WebUserGestureIndicator::isProcessingUserGesture()) |
| WebUserGestureIndicator::currentUserGestureToken().setJavascriptPrompt(); |
| WebString actualValue; |
| bool ok = webframe->client()->runModalPromptDialog(message, defaultValue, |
| &actualValue); |
| if (ok) |
| result = actualValue; |
| return ok; |
| } |
| return false; |
| } |
| |
| void ChromeClientImpl::setStatusbarText(const String& message) { |
| if (m_webView->client()) |
| m_webView->client()->setStatusText(message); |
| } |
| |
| bool ChromeClientImpl::tabsToLinks() { |
| return m_webView->tabsToLinks(); |
| } |
| |
| void ChromeClientImpl::invalidateRect(const IntRect& updateRect) { |
| if (!updateRect.isEmpty()) |
| m_webView->invalidateRect(updateRect); |
| } |
| |
| void ChromeClientImpl::scheduleAnimation(Widget* widget) { |
| DCHECK(widget->isFrameView()); |
| FrameView* view = toFrameView(widget); |
| LocalFrame* frame = view->frame().localFrameRoot(); |
| |
| // If the frame is still being created, it might not yet have a WebWidget. |
| // FIXME: Is this the right thing to do? Is there a way to avoid having |
| // a local frame root that doesn't have a WebWidget? During initialization |
| // there is no content to draw so this call serves no purpose. |
| if (WebLocalFrameImpl::fromFrame(frame) && |
| WebLocalFrameImpl::fromFrame(frame)->frameWidget()) |
| WebLocalFrameImpl::fromFrame(frame)->frameWidget()->scheduleAnimation(); |
| } |
| |
| IntRect ChromeClientImpl::viewportToScreen(const IntRect& rectInViewport, |
| const Widget* widget) const { |
| WebRect screenRect(rectInViewport); |
| |
| DCHECK(widget->isFrameView()); |
| const FrameView* view = toFrameView(widget); |
| LocalFrame* frame = view->frame().localFrameRoot(); |
| |
| WebWidgetClient* client = |
| WebLocalFrameImpl::fromFrame(frame)->frameWidget()->client(); |
| |
| if (client) { |
| client->convertViewportToWindow(&screenRect); |
| WebRect viewRect = client->viewRect(); |
| screenRect.x += viewRect.x; |
| screenRect.y += viewRect.y; |
| } |
| |
| return screenRect; |
| } |
| |
| float ChromeClientImpl::windowToViewportScalar(const float scalarValue) const { |
| if (!m_webView->client()) |
| return scalarValue; |
| WebFloatRect viewportRect(0, 0, scalarValue, 0); |
| m_webView->client()->convertWindowToViewport(&viewportRect); |
| return viewportRect.width; |
| } |
| |
| WebScreenInfo ChromeClientImpl::screenInfo() const { |
| return m_webView->client() ? m_webView->client()->screenInfo() |
| : WebScreenInfo(); |
| } |
| |
| WTF::Optional<IntRect> ChromeClientImpl::visibleContentRectForPainting() const { |
| return m_webView->devToolsEmulator()->visibleContentRectForPainting(); |
| } |
| |
| void ChromeClientImpl::contentsSizeChanged(LocalFrame* frame, |
| const IntSize& size) const { |
| m_webView->didChangeContentsSize(); |
| |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| webframe->didChangeContentsSize(size); |
| } |
| |
| void ChromeClientImpl::pageScaleFactorChanged() const { |
| m_webView->pageScaleFactorChanged(); |
| } |
| |
| void ChromeClientImpl::mainFrameScrollOffsetChanged() const { |
| m_webView->mainFrameScrollOffsetChanged(); |
| } |
| |
| float ChromeClientImpl::clampPageScaleFactorToLimits(float scale) const { |
| return m_webView->clampPageScaleFactorToLimits(scale); |
| } |
| |
| void ChromeClientImpl::layoutUpdated(LocalFrame* frame) const { |
| m_webView->layoutUpdated(WebLocalFrameImpl::fromFrame(frame)); |
| } |
| |
| void ChromeClientImpl::showMouseOverURL(const HitTestResult& result) { |
| if (!m_webView->client()) |
| return; |
| |
| WebURL url; |
| |
| // Ignore URL if hitTest include scrollbar since we might have both a |
| // scrollbar and an element in the case of overlay scrollbars. |
| if (!result.scrollbar()) { |
| // Find out if the mouse is over a link, and if so, let our UI know... |
| if (result.isLiveLink() && |
| !result.absoluteLinkURL().getString().isEmpty()) { |
| url = result.absoluteLinkURL(); |
| } else if (result.innerNode() && |
| (isHTMLObjectElement(*result.innerNode()) || |
| isHTMLEmbedElement(*result.innerNode()))) { |
| LayoutObject* object = result.innerNode()->layoutObject(); |
| if (object && object->isLayoutPart()) { |
| Widget* widget = toLayoutPart(object)->widget(); |
| if (widget && widget->isPluginContainer()) { |
| WebPluginContainerImpl* plugin = toWebPluginContainerImpl(widget); |
| url = plugin->plugin()->linkAtPosition( |
| result.roundedPointInInnerNodeFrame()); |
| } |
| } |
| } |
| } |
| |
| m_webView->client()->setMouseOverURL(url); |
| } |
| |
| void ChromeClientImpl::setToolTip(LocalFrame& frame, |
| const String& tooltipText, |
| TextDirection dir) { |
| WebLocalFrameImpl* webFrame = |
| WebLocalFrameImpl::fromFrame(&frame)->localRoot(); |
| if (!tooltipText.isEmpty()) { |
| webFrame->frameWidget()->client()->setToolTipText(tooltipText, |
| toWebTextDirection(dir)); |
| m_didRequestNonEmptyToolTip = true; |
| } else if (m_didRequestNonEmptyToolTip) { |
| // WebWidgetClient::setToolTipText will send an IPC message. We'd like to |
| // reduce the number of setToolTipText calls. |
| webFrame->frameWidget()->client()->setToolTipText(tooltipText, |
| toWebTextDirection(dir)); |
| m_didRequestNonEmptyToolTip = false; |
| } |
| } |
| |
| void ChromeClientImpl::dispatchViewportPropertiesDidChange( |
| const ViewportDescription& description) const { |
| m_webView->updatePageDefinedViewportConstraints(description); |
| } |
| |
| void ChromeClientImpl::printDelegate(LocalFrame* frame) { |
| if (m_webView->client()) |
| m_webView->client()->printPage(WebLocalFrameImpl::fromFrame(frame)); |
| } |
| |
| ColorChooser* ChromeClientImpl::openColorChooser( |
| LocalFrame* frame, |
| ColorChooserClient* chooserClient, |
| const Color&) { |
| notifyPopupOpeningObservers(); |
| ColorChooserUIController* controller = nullptr; |
| if (RuntimeEnabledFeatures::pagePopupEnabled()) |
| controller = |
| ColorChooserPopupUIController::create(frame, this, chooserClient); |
| else |
| controller = ColorChooserUIController::create(frame, chooserClient); |
| controller->openUI(); |
| return controller; |
| } |
| |
| DateTimeChooser* ChromeClientImpl::openDateTimeChooser( |
| DateTimeChooserClient* pickerClient, |
| const DateTimeChooserParameters& parameters) { |
| notifyPopupOpeningObservers(); |
| if (RuntimeEnabledFeatures::inputMultipleFieldsUIEnabled()) |
| return DateTimeChooserImpl::create(this, pickerClient, parameters); |
| return ExternalDateTimeChooser::create(this, m_webView->client(), |
| pickerClient, parameters); |
| } |
| |
| void ChromeClientImpl::openFileChooser(LocalFrame* frame, |
| PassRefPtr<FileChooser> fileChooser) { |
| notifyPopupOpeningObservers(); |
| WebFrameClient* client = WebLocalFrameImpl::fromFrame(frame)->client(); |
| if (!client) |
| return; |
| |
| WebFileChooserParams params; |
| params.multiSelect = fileChooser->settings().allowsMultipleFiles; |
| params.directory = fileChooser->settings().allowsDirectoryUpload; |
| params.acceptTypes = fileChooser->settings().acceptTypes(); |
| params.selectedFiles = fileChooser->settings().selectedFiles; |
| params.useMediaCapture = fileChooser->settings().useMediaCapture; |
| params.needLocalPath = fileChooser->settings().allowsDirectoryUpload; |
| params.requestor = frame->document()->url(); |
| |
| WebFileChooserCompletionImpl* chooserCompletion = |
| new WebFileChooserCompletionImpl(std::move(fileChooser)); |
| if (client->runFileChooser(params, chooserCompletion)) |
| return; |
| // Choosing failed, so do callback with an empty list. |
| chooserCompletion->didChooseFile(WebVector<WebString>()); |
| } |
| |
| void ChromeClientImpl::enumerateChosenDirectory(FileChooser* fileChooser) { |
| WebViewClient* client = m_webView->client(); |
| if (!client) |
| return; |
| |
| WebFileChooserCompletionImpl* chooserCompletion = |
| new WebFileChooserCompletionImpl(fileChooser); |
| |
| DCHECK(fileChooser); |
| DCHECK(fileChooser->settings().selectedFiles.size()); |
| |
| // If the enumeration can't happen, call the callback with an empty list. |
| if (!client->enumerateChosenDirectory( |
| fileChooser->settings().selectedFiles[0], chooserCompletion)) |
| chooserCompletion->didChooseFile(WebVector<WebString>()); |
| } |
| |
| Cursor ChromeClientImpl::lastSetCursorForTesting() const { |
| return m_lastSetMouseCursorForTesting; |
| } |
| |
| void ChromeClientImpl::setCursor(const Cursor& cursor, LocalFrame* localFrame) { |
| m_lastSetMouseCursorForTesting = cursor; |
| setCursor(WebCursorInfo(cursor), localFrame); |
| } |
| |
| void ChromeClientImpl::setCursor(const WebCursorInfo& cursor, |
| LocalFrame* localFrame) { |
| if (m_cursorOverridden) |
| return; |
| |
| #if OS(MACOSX) |
| // On Mac the mousemove event propagates to both the popup and main window. |
| // If a popup is open we don't want the main window to change the cursor. |
| if (m_webView->hasOpenedPopup()) |
| return; |
| #endif |
| |
| LocalFrame* localRoot = localFrame->localFrameRoot(); |
| if (WebFrameWidgetBase* widget = |
| WebLocalFrameImpl::fromFrame(localRoot)->frameWidget()) |
| widget->client()->didChangeCursor(cursor); |
| } |
| |
| void ChromeClientImpl::setCursorForPlugin(const WebCursorInfo& cursor, |
| LocalFrame* localFrame) { |
| setCursor(cursor, localFrame); |
| } |
| |
| void ChromeClientImpl::setCursorOverridden(bool overridden) { |
| m_cursorOverridden = overridden; |
| } |
| |
| void ChromeClientImpl::postAccessibilityNotification( |
| AXObject* obj, |
| AXObjectCache::AXNotification notification) { |
| // Alert assistive technology about the accessibility object notification. |
| if (!obj || !obj->getDocument()) |
| return; |
| |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame( |
| obj->getDocument()->axObjectCacheOwner().frame()); |
| if (webframe && webframe->client()) |
| webframe->client()->postAccessibilityEvent(WebAXObject(obj), |
| toWebAXEvent(notification)); |
| } |
| |
| String ChromeClientImpl::acceptLanguages() { |
| return m_webView->client()->acceptLanguages(); |
| } |
| |
| void ChromeClientImpl::attachRootGraphicsLayer(GraphicsLayer* rootLayer, |
| LocalFrame* localFrame) { |
| DCHECK(!RuntimeEnabledFeatures::slimmingPaintV2Enabled()); |
| WebLocalFrameImpl* webFrame = |
| WebLocalFrameImpl::fromFrame(localFrame)->localRoot(); |
| |
| // This method can be called while the frame is being detached. In that |
| // case, the rootLayer is null, and the widget is already destroyed. |
| DCHECK(webFrame->frameWidget() || !rootLayer); |
| if (webFrame->frameWidget()) |
| webFrame->frameWidget()->setRootGraphicsLayer(rootLayer); |
| } |
| |
| void ChromeClientImpl::attachRootLayer(WebLayer* rootLayer, |
| LocalFrame* localFrame) { |
| WebLocalFrameImpl* webFrame = |
| WebLocalFrameImpl::fromFrame(localFrame)->localRoot(); |
| |
| // This method can be called while the frame is being detached. In that |
| // case, the rootLayer is null, and the widget is already destroyed. |
| DCHECK(webFrame->frameWidget() || !rootLayer); |
| if (webFrame->frameWidget()) |
| webFrame->frameWidget()->setRootLayer(rootLayer); |
| } |
| |
| void ChromeClientImpl::attachCompositorAnimationTimeline( |
| CompositorAnimationTimeline* compositorTimeline, |
| LocalFrame* localFrame) { |
| WebLocalFrameImpl* webFrame = |
| WebLocalFrameImpl::fromFrame(localFrame)->localRoot(); |
| webFrame->frameWidget()->attachCompositorAnimationTimeline( |
| compositorTimeline); |
| } |
| |
| void ChromeClientImpl::detachCompositorAnimationTimeline( |
| CompositorAnimationTimeline* compositorTimeline, |
| LocalFrame* localFrame) { |
| WebLocalFrameImpl* webFrame = |
| WebLocalFrameImpl::fromFrame(localFrame)->localRoot(); |
| |
| // This method can be called when the frame is being detached, after the |
| // widget is destroyed. |
| if (webFrame->frameWidget()) |
| webFrame->frameWidget()->detachCompositorAnimationTimeline( |
| compositorTimeline); |
| } |
| |
| void ChromeClientImpl::enterFullscreenForElement(Element* element) { |
| m_webView->enterFullscreenForElement(element); |
| } |
| |
| void ChromeClientImpl::exitFullscreen(LocalFrame* frame) { |
| m_webView->exitFullscreen(frame); |
| } |
| |
| void ChromeClientImpl::clearCompositedSelection(LocalFrame* frame) { |
| LocalFrame* localRoot = frame->localFrameRoot(); |
| auto client = |
| WebLocalFrameImpl::fromFrame(localRoot)->frameWidget()->client(); |
| if (!client) |
| return; |
| |
| auto layerTreeView = client->layerTreeView(); |
| if (layerTreeView) |
| layerTreeView->clearSelection(); |
| } |
| |
| void ChromeClientImpl::updateCompositedSelection( |
| LocalFrame* frame, |
| const CompositedSelection& selection) { |
| LocalFrame* localRoot = frame->localFrameRoot(); |
| WebWidgetClient* client = |
| WebLocalFrameImpl::fromFrame(localRoot)->frameWidget()->client(); |
| if (!client) |
| return; |
| |
| WebLayerTreeView* layerTreeView = client->layerTreeView(); |
| if (layerTreeView) |
| layerTreeView->registerSelection(WebSelection(selection)); |
| } |
| |
| bool ChromeClientImpl::hasOpenedPopup() const { |
| return m_webView->hasOpenedPopup(); |
| } |
| |
| PopupMenu* ChromeClientImpl::openPopupMenu(LocalFrame& frame, |
| HTMLSelectElement& select) { |
| notifyPopupOpeningObservers(); |
| if (WebViewImpl::useExternalPopupMenus()) |
| return new ExternalPopupMenu(frame, select, *m_webView); |
| |
| DCHECK(RuntimeEnabledFeatures::pagePopupEnabled()); |
| return PopupMenuImpl::create(this, select); |
| } |
| |
| PagePopup* ChromeClientImpl::openPagePopup(PagePopupClient* client) { |
| return m_webView->openPagePopup(client); |
| } |
| |
| void ChromeClientImpl::closePagePopup(PagePopup* popup) { |
| m_webView->closePagePopup(popup); |
| } |
| |
| DOMWindow* ChromeClientImpl::pagePopupWindowForTesting() const { |
| return m_webView->pagePopupWindow(); |
| } |
| |
| bool ChromeClientImpl::shouldOpenModalDialogDuringPageDismissal( |
| const DialogType& dialogType, |
| const String& dialogMessage, |
| Document::PageDismissalType dismissalType) const { |
| String message = String("Blocked ") + dialogTypeToString(dialogType) + "('" + |
| dialogMessage + "') during " + |
| dismissalTypeToString(dismissalType) + "."; |
| m_webView->mainFrame()->addMessageToConsole( |
| WebConsoleMessage(WebConsoleMessage::LevelError, message)); |
| |
| return false; |
| } |
| |
| void ChromeClientImpl::setEventListenerProperties( |
| WebEventListenerClass eventClass, |
| WebEventListenerProperties properties) { |
| if (WebLayerTreeView* treeView = m_webView->layerTreeView()) { |
| treeView->setEventListenerProperties(eventClass, properties); |
| if (eventClass == WebEventListenerClass::TouchStartOrMove) { |
| m_webView->hasTouchEventHandlers( |
| properties != WebEventListenerProperties::Nothing || |
| eventListenerProperties(WebEventListenerClass::TouchEndOrCancel) != |
| WebEventListenerProperties::Nothing); |
| } else if (eventClass == WebEventListenerClass::TouchEndOrCancel) { |
| m_webView->hasTouchEventHandlers( |
| properties != WebEventListenerProperties::Nothing || |
| eventListenerProperties(WebEventListenerClass::TouchStartOrMove) != |
| WebEventListenerProperties::Nothing); |
| } |
| } else { |
| m_webView->hasTouchEventHandlers(true); |
| } |
| } |
| |
| void ChromeClientImpl::beginLifecycleUpdates() { |
| if (WebLayerTreeView* treeView = m_webView->layerTreeView()) { |
| treeView->setDeferCommits(false); |
| treeView->setNeedsBeginFrame(); |
| } |
| } |
| |
| WebEventListenerProperties ChromeClientImpl::eventListenerProperties( |
| WebEventListenerClass eventClass) const { |
| if (WebLayerTreeView* treeView = m_webView->layerTreeView()) |
| return treeView->eventListenerProperties(eventClass); |
| return WebEventListenerProperties::Nothing; |
| } |
| |
| void ChromeClientImpl::setHasScrollEventHandlers(bool hasEventHandlers) { |
| if (WebLayerTreeView* treeView = m_webView->layerTreeView()) |
| treeView->setHaveScrollEventHandlers(hasEventHandlers); |
| } |
| |
| bool ChromeClientImpl::hasScrollEventHandlers() const { |
| if (WebLayerTreeView* treeView = m_webView->layerTreeView()) |
| return treeView->haveScrollEventHandlers(); |
| return false; |
| } |
| |
| void ChromeClientImpl::setTouchAction(TouchAction touchAction) { |
| if (WebViewClient* client = m_webView->client()) |
| client->setTouchAction(static_cast<WebTouchAction>(touchAction)); |
| } |
| |
| bool ChromeClientImpl::requestPointerLock(LocalFrame* frame) { |
| LocalFrame* localRoot = frame->localFrameRoot(); |
| return WebLocalFrameImpl::fromFrame(localRoot) |
| ->frameWidget() |
| ->client() |
| ->requestPointerLock(); |
| } |
| |
| void ChromeClientImpl::requestPointerUnlock(LocalFrame* frame) { |
| LocalFrame* localRoot = frame->localFrameRoot(); |
| return WebLocalFrameImpl::fromFrame(localRoot) |
| ->frameWidget() |
| ->client() |
| ->requestPointerUnlock(); |
| } |
| |
| void ChromeClientImpl::annotatedRegionsChanged() { |
| if (WebViewClient* client = m_webView->client()) |
| client->draggableRegionsChanged(); |
| } |
| |
| void ChromeClientImpl::didAssociateFormControlsAfterLoad(LocalFrame* frame) { |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->didAssociateFormControlsDynamically(); |
| } |
| |
| void ChromeClientImpl::didCancelCompositionOnSelectionChange() { |
| if (m_webView->client()) |
| m_webView->client()->didCancelCompositionOnSelectionChange(); |
| } |
| |
| void ChromeClientImpl::willSetInputMethodState() { |
| if (m_webView->client()) |
| m_webView->client()->resetInputMethod(); |
| } |
| |
| void ChromeClientImpl::didUpdateTextOfFocusedElementByNonUserInput( |
| LocalFrame& frame) { |
| WebLocalFrameImpl* webFrame = |
| WebLocalFrameImpl::fromFrame(frame.localFrameRoot()); |
| webFrame->frameWidget() |
| ->client() |
| ->didUpdateTextOfFocusedElementByNonUserInput(); |
| } |
| |
| void ChromeClientImpl::showImeIfNeeded() { |
| if (m_webView->client()) |
| m_webView->client()->showImeIfNeeded(); |
| } |
| |
| void ChromeClientImpl::showUnhandledTapUIIfNeeded( |
| IntPoint tappedPositionInViewport, |
| Node* tappedNode, |
| bool pageChanged) { |
| if (m_webView->client()) |
| m_webView->client()->showUnhandledTapUIIfNeeded( |
| WebPoint(tappedPositionInViewport), WebNode(tappedNode), pageChanged); |
| } |
| |
| void ChromeClientImpl::onMouseDown(Node* mouseDownNode) { |
| if (m_webView->client()) |
| m_webView->client()->onMouseDown(WebNode(mouseDownNode)); |
| } |
| |
| void ChromeClientImpl::handleKeyboardEventOnTextField( |
| HTMLInputElement& inputElement, |
| KeyboardEvent& event) { |
| WebLocalFrameImpl* webframe = |
| WebLocalFrameImpl::fromFrame(inputElement.document().frame()); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->textFieldDidReceiveKeyDown( |
| WebInputElement(&inputElement), WebKeyboardEventBuilder(event)); |
| } |
| |
| void ChromeClientImpl::didChangeValueInTextField( |
| HTMLFormControlElement& element) { |
| WebLocalFrameImpl* webframe = |
| WebLocalFrameImpl::fromFrame(element.document().frame()); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->textFieldDidChange( |
| WebFormControlElement(&element)); |
| |
| m_webView->pageImportanceSignals()->setHadFormInteraction(); |
| } |
| |
| void ChromeClientImpl::didEndEditingOnTextField( |
| HTMLInputElement& inputElement) { |
| WebLocalFrameImpl* webframe = |
| WebLocalFrameImpl::fromFrame(inputElement.document().frame()); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->textFieldDidEndEditing( |
| WebInputElement(&inputElement)); |
| } |
| |
| void ChromeClientImpl::openTextDataListChooser(HTMLInputElement& input) { |
| notifyPopupOpeningObservers(); |
| WebLocalFrameImpl* webframe = |
| WebLocalFrameImpl::fromFrame(input.document().frame()); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->openTextDataListChooser( |
| WebInputElement(&input)); |
| } |
| |
| void ChromeClientImpl::textFieldDataListChanged(HTMLInputElement& input) { |
| WebLocalFrameImpl* webframe = |
| WebLocalFrameImpl::fromFrame(input.document().frame()); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->dataListOptionsChanged(WebInputElement(&input)); |
| } |
| |
| void ChromeClientImpl::ajaxSucceeded(LocalFrame* frame) { |
| WebLocalFrameImpl* webframe = WebLocalFrameImpl::fromFrame(frame); |
| if (webframe->autofillClient()) |
| webframe->autofillClient()->ajaxSucceeded(); |
| } |
| |
| void ChromeClientImpl::registerViewportLayers() const { |
| if (m_webView->rootGraphicsLayer() && m_webView->layerTreeView()) |
| m_webView->registerViewportLayersWithCompositor(); |
| } |
| |
| void ChromeClientImpl::didUpdateBrowserControls() const { |
| m_webView->didUpdateBrowserControls(); |
| } |
| |
| CompositorProxyClient* ChromeClientImpl::createCompositorProxyClient( |
| LocalFrame* frame) { |
| WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(frame); |
| return webFrame->localRoot()->frameWidget()->createCompositorProxyClient(); |
| } |
| |
| void ChromeClientImpl::registerPopupOpeningObserver( |
| PopupOpeningObserver* observer) { |
| DCHECK(observer); |
| m_popupOpeningObservers.append(observer); |
| } |
| |
| void ChromeClientImpl::unregisterPopupOpeningObserver( |
| PopupOpeningObserver* observer) { |
| size_t index = m_popupOpeningObservers.find(observer); |
| DCHECK_NE(index, kNotFound); |
| m_popupOpeningObservers.remove(index); |
| } |
| |
| void ChromeClientImpl::notifyPopupOpeningObservers() const { |
| const Vector<PopupOpeningObserver*> observers(m_popupOpeningObservers); |
| for (const auto& observer : observers) |
| observer->willOpenPopup(); |
| } |
| |
| FloatSize ChromeClientImpl::elasticOverscroll() const { |
| return m_webView->elasticOverscroll(); |
| } |
| |
| void ChromeClientImpl::didObserveNonGetFetchFromScript() const { |
| if (m_webView->pageImportanceSignals()) |
| m_webView->pageImportanceSignals()->setIssuedNonGetFetchFromScript(); |
| } |
| |
| std::unique_ptr<WebFrameScheduler> ChromeClientImpl::createFrameScheduler( |
| BlameContext* blameContext) { |
| return wrapUnique( |
| m_webView->scheduler()->createFrameScheduler(blameContext).release()); |
| } |
| |
| double ChromeClientImpl::lastFrameTimeMonotonic() const { |
| return m_webView->lastFrameTimeMonotonic(); |
| } |
| |
| void ChromeClientImpl::installSupplements(LocalFrame& frame) { |
| WebLocalFrameImpl* webFrame = WebLocalFrameImpl::fromFrame(&frame); |
| WebFrameClient* client = webFrame->client(); |
| DCHECK(client); |
| providePushControllerTo(frame, client->pushClient()); |
| provideUserMediaTo(frame, |
| UserMediaClientImpl::create(client->userMediaClient())); |
| provideIndexedDBClientTo(frame, IndexedDBClientImpl::create()); |
| provideLocalFileSystemTo(frame, LocalFileSystemClient::create()); |
| provideNavigatorContentUtilsTo( |
| frame, NavigatorContentUtilsClientImpl::create(webFrame)); |
| |
| if (RuntimeEnabledFeatures::webBluetoothEnabled()) |
| BluetoothSupplement::provideTo(frame, client->bluetooth()); |
| |
| ScreenOrientationController::provideTo(frame, |
| client->webScreenOrientationClient()); |
| if (RuntimeEnabledFeatures::presentationEnabled()) |
| PresentationController::provideTo(frame, client->presentationClient()); |
| if (RuntimeEnabledFeatures::audioOutputDevicesEnabled()) |
| provideAudioOutputDeviceClientTo(frame, |
| AudioOutputDeviceClientImpl::create()); |
| if (RuntimeEnabledFeatures::installedAppEnabled()) |
| InstalledAppController::provideTo(frame, client->installedAppClient()); |
| } |
| |
| } // namespace blink |