From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- Source/WebCore/page/AbstractView.idl | 35 - Source/WebCore/page/ActivityState.h | 54 + Source/WebCore/page/ActivityStateChangeObserver.h | 41 + Source/WebCore/page/AdjustViewSizeOrNot.h | 5 +- Source/WebCore/page/AlternativeTextClient.h | 26 +- Source/WebCore/page/AutoscrollController.cpp | 20 +- Source/WebCore/page/AutoscrollController.h | 16 +- Source/WebCore/page/BarProp.cpp | 2 +- Source/WebCore/page/BarProp.h | 11 +- Source/WebCore/page/BarProp.idl | 2 +- Source/WebCore/page/Base64Utilities.cpp | 57 + Source/WebCore/page/Base64Utilities.h | 38 + Source/WebCore/page/CaptionUserPreferences.cpp | 147 +- Source/WebCore/page/CaptionUserPreferences.h | 38 +- .../WebCore/page/CaptionUserPreferencesMediaAF.cpp | 1000 +++++++ .../WebCore/page/CaptionUserPreferencesMediaAF.h | 97 + Source/WebCore/page/Chrome.cpp | 255 +- Source/WebCore/page/Chrome.h | 119 +- Source/WebCore/page/ChromeClient.h | 265 +- Source/WebCore/page/Console.cpp | 266 -- Source/WebCore/page/Console.h | 85 - Source/WebCore/page/Console.idl | 61 - Source/WebCore/page/ConsoleTypes.h | 53 - Source/WebCore/page/ContentSecurityPolicy.cpp | 1919 ------------ Source/WebCore/page/ContentSecurityPolicy.h | 151 - Source/WebCore/page/ContextMenuClient.h | 28 +- Source/WebCore/page/ContextMenuContext.cpp | 48 + Source/WebCore/page/ContextMenuContext.h | 62 + Source/WebCore/page/ContextMenuController.cpp | 466 +-- Source/WebCore/page/ContextMenuController.h | 111 +- Source/WebCore/page/ContextMenuProvider.h | 11 +- Source/WebCore/page/Crypto.cpp | 56 +- Source/WebCore/page/Crypto.h | 32 +- Source/WebCore/page/Crypto.idl | 10 +- Source/WebCore/page/DOMSecurityPolicy.cpp | 177 -- Source/WebCore/page/DOMSecurityPolicy.h | 73 - Source/WebCore/page/DOMSecurityPolicy.idl | 46 - Source/WebCore/page/DOMSelection.cpp | 383 +-- Source/WebCore/page/DOMSelection.h | 135 +- Source/WebCore/page/DOMSelection.idl | 75 +- Source/WebCore/page/DOMTimer.cpp | 384 ++- Source/WebCore/page/DOMTimer.h | 92 +- Source/WebCore/page/DOMWindow.cpp | 1319 ++++---- Source/WebCore/page/DOMWindow.h | 704 ++--- Source/WebCore/page/DOMWindow.idl | 340 +-- Source/WebCore/page/DOMWindowExtension.cpp | 20 +- Source/WebCore/page/DOMWindowExtension.h | 24 +- Source/WebCore/page/DOMWindowProperty.cpp | 28 +- Source/WebCore/page/DOMWindowProperty.h | 11 +- Source/WebCore/page/DatabaseProvider.cpp | 35 + Source/WebCore/page/DatabaseProvider.h | 47 + Source/WebCore/page/DebugPageOverlays.cpp | 292 ++ Source/WebCore/page/DebugPageOverlays.h | 102 + Source/WebCore/page/DeviceClient.h | 6 +- Source/WebCore/page/DeviceController.cpp | 29 +- Source/WebCore/page/DeviceController.h | 16 +- Source/WebCore/page/DiagnosticLoggingClient.h | 59 + Source/WebCore/page/DiagnosticLoggingKeys.cpp | 662 ++++- Source/WebCore/page/DiagnosticLoggingKeys.h | 144 +- Source/WebCore/page/DiagnosticLoggingResultType.h | 36 + Source/WebCore/page/DragActions.h | 14 +- Source/WebCore/page/DragClient.h | 29 +- Source/WebCore/page/DragController.cpp | 600 ++-- Source/WebCore/page/DragController.h | 73 +- Source/WebCore/page/DragSession.h | 48 - Source/WebCore/page/DragState.h | 13 +- Source/WebCore/page/EditorClient.h | 52 +- Source/WebCore/page/EventHandler.cpp | 2246 ++++++++------ Source/WebCore/page/EventHandler.h | 422 +-- Source/WebCore/page/EventSource.cpp | 366 ++- Source/WebCore/page/EventSource.h | 95 +- Source/WebCore/page/EventSource.idl | 33 +- Source/WebCore/page/FeatureObserver.cpp | 78 - Source/WebCore/page/FeatureObserver.h | 139 - Source/WebCore/page/FocusController.cpp | 650 ++-- Source/WebCore/page/FocusController.h | 99 +- Source/WebCore/page/FocusDirection.h | 31 +- Source/WebCore/page/Frame.cpp | 468 ++- Source/WebCore/page/Frame.h | 645 ++-- Source/WebCore/page/FrameDestructionObserver.cpp | 6 +- Source/WebCore/page/FrameDestructionObserver.h | 17 +- Source/WebCore/page/FrameSnapshotting.cpp | 41 +- Source/WebCore/page/FrameSnapshotting.h | 13 +- Source/WebCore/page/FrameTree.cpp | 186 +- Source/WebCore/page/FrameTree.h | 172 +- Source/WebCore/page/FrameView.cpp | 3137 +++++++++++++------- Source/WebCore/page/FrameView.h | 599 ++-- Source/WebCore/page/GestureTapHighlighter.cpp | 272 -- Source/WebCore/page/GestureTapHighlighter.h | 46 - Source/WebCore/page/GlobalCrypto.idl | 30 + Source/WebCore/page/GlobalPerformance.idl | 33 + Source/WebCore/page/GroupSettings.cpp | 53 - Source/WebCore/page/GroupSettings.h | 57 - Source/WebCore/page/History.cpp | 141 +- Source/WebCore/page/History.h | 52 +- Source/WebCore/page/History.idl | 18 +- Source/WebCore/page/IntersectionObserver.cpp | 69 + Source/WebCore/page/IntersectionObserver.h | 75 + Source/WebCore/page/IntersectionObserver.idl | 52 + Source/WebCore/page/IntersectionObserverCallback.h | 46 + .../WebCore/page/IntersectionObserverCallback.idl | 30 + Source/WebCore/page/IntersectionObserverEntry.cpp | 47 + Source/WebCore/page/IntersectionObserverEntry.h | 79 + Source/WebCore/page/IntersectionObserverEntry.idl | 52 + Source/WebCore/page/LayerFlushThrottleState.h | 38 + Source/WebCore/page/LayoutMilestones.h | 18 +- Source/WebCore/page/Location.cpp | 120 +- Source/WebCore/page/Location.h | 39 +- Source/WebCore/page/Location.idl | 53 +- Source/WebCore/page/MainFrame.cpp | 132 +- Source/WebCore/page/MainFrame.h | 113 +- Source/WebCore/page/MediaCanStartListener.h | 11 +- Source/WebCore/page/MediaProducer.h | 65 + Source/WebCore/page/MemoryRelease.cpp | 226 ++ Source/WebCore/page/MemoryRelease.h | 40 + .../WebCore/page/MouseEventWithHitTestResults.cpp | 2 +- Source/WebCore/page/MouseEventWithHitTestResults.h | 7 +- Source/WebCore/page/Navigator.cpp | 46 +- Source/WebCore/page/Navigator.h | 29 +- Source/WebCore/page/Navigator.idl | 16 +- Source/WebCore/page/NavigatorBase.cpp | 88 +- Source/WebCore/page/NavigatorBase.h | 47 +- .../WebCore/page/NavigatorConcurrentHardware.idl | 33 + Source/WebCore/page/NavigatorID.idl | 41 + Source/WebCore/page/NavigatorLanguage.idl | 34 + Source/WebCore/page/NavigatorOnLine.idl | 33 + Source/WebCore/page/OriginAccessEntry.cpp | 16 +- Source/WebCore/page/OriginAccessEntry.h | 19 +- Source/WebCore/page/Page.cpp | 1395 ++++++--- Source/WebCore/page/Page.h | 573 ++-- Source/WebCore/page/PageActivityAssertionToken.cpp | 52 - Source/WebCore/page/PageActivityAssertionToken.h | 49 - Source/WebCore/page/PageConfiguration.cpp | 56 + Source/WebCore/page/PageConfiguration.h | 94 + Source/WebCore/page/PageConsole.cpp | 225 -- Source/WebCore/page/PageConsole.h | 70 - Source/WebCore/page/PageConsoleClient.cpp | 216 ++ Source/WebCore/page/PageConsoleClient.h | 74 + Source/WebCore/page/PageDebuggable.cpp | 44 +- Source/WebCore/page/PageDebuggable.h | 36 +- Source/WebCore/page/PageGroup.cpp | 229 +- Source/WebCore/page/PageGroup.h | 112 +- Source/WebCore/page/PageGroupLoadDeferrer.cpp | 17 +- Source/WebCore/page/PageGroupLoadDeferrer.h | 6 +- Source/WebCore/page/PageOverlay.cpp | 292 ++ Source/WebCore/page/PageOverlay.h | 152 + Source/WebCore/page/PageOverlayController.cpp | 413 +++ Source/WebCore/page/PageOverlayController.h | 105 + Source/WebCore/page/PageSerializer.cpp | 152 +- Source/WebCore/page/PageSerializer.h | 24 +- Source/WebCore/page/PageThrottler.cpp | 178 -- Source/WebCore/page/PageThrottler.h | 81 - Source/WebCore/page/PageVisibilityState.cpp | 59 - Source/WebCore/page/PageVisibilityState.h | 19 +- Source/WebCore/page/Performance.cpp | 229 +- Source/WebCore/page/Performance.h | 109 +- Source/WebCore/page/Performance.idl | 50 +- Source/WebCore/page/PerformanceEntry.cpp | 37 +- Source/WebCore/page/PerformanceEntry.h | 43 +- Source/WebCore/page/PerformanceEntry.idl | 13 +- Source/WebCore/page/PerformanceEntryList.cpp | 78 - Source/WebCore/page/PerformanceEntryList.h | 68 - Source/WebCore/page/PerformanceEntryList.idl | 41 - Source/WebCore/page/PerformanceLogging.cpp | 109 + Source/WebCore/page/PerformanceLogging.h | 60 + Source/WebCore/page/PerformanceMark.h | 26 +- Source/WebCore/page/PerformanceMark.idl | 6 +- Source/WebCore/page/PerformanceMeasure.h | 26 +- Source/WebCore/page/PerformanceMeasure.idl | 6 +- Source/WebCore/page/PerformanceMonitor.cpp | 243 ++ Source/WebCore/page/PerformanceMonitor.h | 67 + Source/WebCore/page/PerformanceNavigation.cpp | 10 +- Source/WebCore/page/PerformanceNavigation.h | 12 +- Source/WebCore/page/PerformanceObserver.cpp | 107 + Source/WebCore/page/PerformanceObserver.h | 74 + Source/WebCore/page/PerformanceObserver.idl | 45 + Source/WebCore/page/PerformanceObserverCallback.h | 45 + .../WebCore/page/PerformanceObserverCallback.idl | 30 + .../WebCore/page/PerformanceObserverEntryList.cpp | 79 + Source/WebCore/page/PerformanceObserverEntryList.h | 55 + .../WebCore/page/PerformanceObserverEntryList.idl | 39 + Source/WebCore/page/PerformanceResourceTiming.cpp | 129 +- Source/WebCore/page/PerformanceResourceTiming.h | 45 +- Source/WebCore/page/PerformanceResourceTiming.idl | 34 +- Source/WebCore/page/PerformanceTiming.cpp | 173 +- Source/WebCore/page/PerformanceTiming.h | 25 +- Source/WebCore/page/PerformanceTiming.idl | 5 +- Source/WebCore/page/PerformanceUserTiming.cpp | 194 +- Source/WebCore/page/PerformanceUserTiming.h | 44 +- Source/WebCore/page/PlugInClient.h | 9 +- Source/WebCore/page/PointerLockController.cpp | 103 +- Source/WebCore/page/PointerLockController.h | 35 +- Source/WebCore/page/PopupOpeningObserver.h | 6 +- Source/WebCore/page/PrintContext.cpp | 153 +- Source/WebCore/page/PrintContext.h | 61 +- Source/WebCore/page/ResourceUsageData.cpp | 60 + Source/WebCore/page/ResourceUsageData.h | 77 + Source/WebCore/page/ResourceUsageOverlay.cpp | 130 + Source/WebCore/page/ResourceUsageOverlay.h | 95 + Source/WebCore/page/ResourceUsageThread.cpp | 131 + Source/WebCore/page/ResourceUsageThread.h | 90 + Source/WebCore/page/RuntimeEnabledFeatures.cpp | 103 + Source/WebCore/page/RuntimeEnabledFeatures.h | 300 ++ Source/WebCore/page/Screen.cpp | 6 +- Source/WebCore/page/Screen.h | 39 +- Source/WebCore/page/Screen.idl | 2 +- Source/WebCore/page/ScrollToOptions.h | 40 + Source/WebCore/page/ScrollToOptions.idl | 33 + Source/WebCore/page/SecurityOrigin.cpp | 328 +- Source/WebCore/page/SecurityOrigin.h | 108 +- Source/WebCore/page/SecurityOriginData.cpp | 147 + Source/WebCore/page/SecurityOriginData.h | 137 + Source/WebCore/page/SecurityOriginHash.h | 11 +- Source/WebCore/page/SecurityPolicy.cpp | 49 +- Source/WebCore/page/SecurityPolicy.h | 24 +- Source/WebCore/page/SessionID.h | 74 + Source/WebCore/page/Settings.cpp | 427 ++- Source/WebCore/page/Settings.h | 450 +-- Source/WebCore/page/Settings.in | 141 +- Source/WebCore/page/SocketProvider.cpp | 42 + Source/WebCore/page/SocketProvider.h | 48 + Source/WebCore/page/SpatialNavigation.cpp | 80 +- Source/WebCore/page/SpatialNavigation.h | 12 +- Source/WebCore/page/SpeechInput.cpp | 132 - Source/WebCore/page/SpeechInput.h | 91 - Source/WebCore/page/SpeechInputClient.h | 76 - Source/WebCore/page/SpeechInputEvent.cpp | 72 - Source/WebCore/page/SpeechInputEvent.h | 61 - Source/WebCore/page/SpeechInputEvent.idl | 31 - Source/WebCore/page/SpeechInputListener.h | 67 - Source/WebCore/page/SpeechInputResult.cpp | 61 - Source/WebCore/page/SpeechInputResult.h | 60 - Source/WebCore/page/SpeechInputResult.idl | 34 - Source/WebCore/page/SpeechInputResultList.cpp | 55 - Source/WebCore/page/SpeechInputResultList.h | 57 - Source/WebCore/page/SpeechInputResultList.idl | 34 - Source/WebCore/page/SuspendableTimer.cpp | 8 +- Source/WebCore/page/SuspendableTimer.h | 32 +- Source/WebCore/page/TextIndicator.cpp | 245 ++ Source/WebCore/page/TextIndicator.h | 129 + Source/WebCore/page/UserContentController.cpp | 109 +- Source/WebCore/page/UserContentController.h | 65 +- Source/WebCore/page/UserContentProvider.cpp | 120 + Source/WebCore/page/UserContentProvider.h | 107 + Source/WebCore/page/UserContentTypes.h | 12 +- Source/WebCore/page/UserContentURLPattern.cpp | 33 +- Source/WebCore/page/UserContentURLPattern.h | 14 +- Source/WebCore/page/UserMessageHandler.cpp | 60 + Source/WebCore/page/UserMessageHandler.h | 57 + Source/WebCore/page/UserMessageHandler.idl | 30 + .../WebCore/page/UserMessageHandlerDescriptor.cpp | 62 + Source/WebCore/page/UserMessageHandlerDescriptor.h | 58 + .../page/UserMessageHandlerDescriptorTypes.h | 44 + .../WebCore/page/UserMessageHandlersNamespace.cpp | 96 + Source/WebCore/page/UserMessageHandlersNamespace.h | 68 + .../WebCore/page/UserMessageHandlersNamespace.idl | 30 + Source/WebCore/page/UserScript.h | 15 +- Source/WebCore/page/UserScriptTypes.h | 9 +- Source/WebCore/page/UserStyleSheet.h | 15 +- Source/WebCore/page/UserStyleSheetTypes.h | 9 +- Source/WebCore/page/ValidationMessageClient.h | 15 +- Source/WebCore/page/ViewState.h | 48 - Source/WebCore/page/ViewportConfiguration.cpp | 488 +++ Source/WebCore/page/ViewportConfiguration.h | 133 + Source/WebCore/page/VisitedLinkProvider.cpp | 39 - Source/WebCore/page/VisitedLinkProvider.h | 53 - Source/WebCore/page/VisitedLinkStore.cpp | 68 + Source/WebCore/page/VisitedLinkStore.h | 57 + Source/WebCore/page/WebCoreKeyboardUIMode.h | 25 +- Source/WebCore/page/WebKitNamespace.cpp | 53 + Source/WebCore/page/WebKitNamespace.h | 59 + Source/WebCore/page/WebKitNamespace.idl | 31 + Source/WebCore/page/WebKitPoint.h | 60 +- Source/WebCore/page/WebKitPoint.idl | 12 +- Source/WebCore/page/WheelEventDeltaFilter.cpp | 132 + Source/WebCore/page/WheelEventDeltaFilter.h | 71 + Source/WebCore/page/WheelEventTestTrigger.cpp | 133 + Source/WebCore/page/WheelEventTestTrigger.h | 65 + Source/WebCore/page/WindowBase64.idl | 33 - Source/WebCore/page/WindowEventHandlers.idl | 52 + Source/WebCore/page/WindowFeatures.cpp | 290 +- Source/WebCore/page/WindowFeatures.h | 71 +- .../WebCore/page/WindowFocusAllowedIndicator.cpp | 2 +- Source/WebCore/page/WindowFocusAllowedIndicator.h | 8 +- Source/WebCore/page/WindowOrWorkerGlobalScope.idl | 40 + Source/WebCore/page/WindowTimers.idl | 36 - Source/WebCore/page/WorkerNavigator.cpp | 8 +- Source/WebCore/page/WorkerNavigator.h | 19 +- Source/WebCore/page/WorkerNavigator.idl | 15 +- Source/WebCore/page/animation/AnimationBase.cpp | 618 ++-- Source/WebCore/page/animation/AnimationBase.h | 196 +- .../WebCore/page/animation/AnimationController.cpp | 645 ---- .../WebCore/page/animation/AnimationController.h | 109 - .../page/animation/AnimationControllerPrivate.h | 154 - .../page/animation/CSSAnimationController.cpp | 805 +++++ .../page/animation/CSSAnimationController.h | 118 + .../page/animation/CSSAnimationControllerPrivate.h | 163 + .../page/animation/CSSPropertyAnimation.cpp | 824 +++-- .../WebCore/page/animation/CSSPropertyAnimation.h | 9 +- .../WebCore/page/animation/CompositeAnimation.cpp | 444 +-- Source/WebCore/page/animation/CompositeAnimation.h | 47 +- .../WebCore/page/animation/ImplicitAnimation.cpp | 165 +- Source/WebCore/page/animation/ImplicitAnimation.h | 49 +- .../WebCore/page/animation/KeyframeAnimation.cpp | 342 ++- Source/WebCore/page/animation/KeyframeAnimation.h | 69 +- Source/WebCore/page/csp/ContentSecurityPolicy.cpp | 881 ++++++ Source/WebCore/page/csp/ContentSecurityPolicy.h | 220 ++ .../page/csp/ContentSecurityPolicyDirective.cpp | 38 + .../page/csp/ContentSecurityPolicyDirective.h | 57 + .../csp/ContentSecurityPolicyDirectiveList.cpp | 511 ++++ .../page/csp/ContentSecurityPolicyDirectiveList.h | 132 + .../csp/ContentSecurityPolicyDirectiveNames.cpp | 54 + .../page/csp/ContentSecurityPolicyDirectiveNames.h | 54 + .../WebCore/page/csp/ContentSecurityPolicyHash.h | 66 + .../ContentSecurityPolicyMediaListDirective.cpp | 118 + .../csp/ContentSecurityPolicyMediaListDirective.h | 49 + .../csp/ContentSecurityPolicyResponseHeaders.cpp | 62 + .../csp/ContentSecurityPolicyResponseHeaders.h | 57 + .../page/csp/ContentSecurityPolicySource.cpp | 113 + .../WebCore/page/csp/ContentSecurityPolicySource.h | 60 + .../page/csp/ContentSecurityPolicySourceList.cpp | 520 ++++ .../page/csp/ContentSecurityPolicySourceList.h | 86 + .../ContentSecurityPolicySourceListDirective.cpp | 60 + .../csp/ContentSecurityPolicySourceListDirective.h | 53 + Source/WebCore/page/gtk/DragControllerGtk.cpp | 31 +- Source/WebCore/page/gtk/EventHandlerGtk.cpp | 41 +- .../page/linux/ResourceUsageOverlayLinux.cpp | 163 + .../page/linux/ResourceUsageThreadLinux.cpp | 171 ++ Source/WebCore/page/make_settings.pl | 26 +- .../page/scrolling/AsyncScrollingCoordinator.cpp | 707 +++++ .../page/scrolling/AsyncScrollingCoordinator.h | 170 ++ .../page/scrolling/AxisScrollSnapOffsets.cpp | 346 +++ .../WebCore/page/scrolling/AxisScrollSnapOffsets.h | 50 + .../WebCore/page/scrolling/ScrollLatchingState.cpp | 70 + .../WebCore/page/scrolling/ScrollLatchingState.h | 70 + .../WebCore/page/scrolling/ScrollSnapOffsetsInfo.h | 51 + .../page/scrolling/ScrollingConstraints.cpp | 18 + .../WebCore/page/scrolling/ScrollingConstraints.h | 21 +- .../page/scrolling/ScrollingCoordinator.cpp | 345 ++- .../WebCore/page/scrolling/ScrollingCoordinator.h | 146 +- .../page/scrolling/ScrollingMomentumCalculator.cpp | 235 ++ .../page/scrolling/ScrollingMomentumCalculator.h | 90 + .../page/scrolling/ScrollingStateFixedNode.cpp | 35 +- .../page/scrolling/ScrollingStateFixedNode.h | 19 +- .../scrolling/ScrollingStateFrameScrollingNode.cpp | 329 ++ .../scrolling/ScrollingStateFrameScrollingNode.h | 177 ++ .../WebCore/page/scrolling/ScrollingStateNode.cpp | 72 +- Source/WebCore/page/scrolling/ScrollingStateNode.h | 107 +- .../ScrollingStateOverflowScrollingNode.cpp | 85 + .../ScrollingStateOverflowScrollingNode.h | 63 + .../page/scrolling/ScrollingStateScrollingNode.cpp | 220 +- .../page/scrolling/ScrollingStateScrollingNode.h | 163 +- .../page/scrolling/ScrollingStateStickyNode.cpp | 152 + .../page/scrolling/ScrollingStateStickyNode.h | 69 + .../WebCore/page/scrolling/ScrollingStateTree.cpp | 191 +- Source/WebCore/page/scrolling/ScrollingStateTree.h | 56 +- Source/WebCore/page/scrolling/ScrollingThread.cpp | 120 + Source/WebCore/page/scrolling/ScrollingThread.h | 93 + Source/WebCore/page/scrolling/ScrollingTree.cpp | 410 +++ Source/WebCore/page/scrolling/ScrollingTree.h | 200 ++ .../scrolling/ScrollingTreeFrameScrollingNode.cpp | 157 + .../scrolling/ScrollingTreeFrameScrollingNode.h | 103 + .../WebCore/page/scrolling/ScrollingTreeNode.cpp | 95 + Source/WebCore/page/scrolling/ScrollingTreeNode.h | 94 + .../ScrollingTreeOverflowScrollingNode.cpp | 47 + .../scrolling/ScrollingTreeOverflowScrollingNode.h | 46 + .../page/scrolling/ScrollingTreeScrollingNode.cpp | 165 + .../page/scrolling/ScrollingTreeScrollingNode.h | 119 + .../page/scrolling/ThreadedScrollingTree.cpp | 163 + .../WebCore/page/scrolling/ThreadedScrollingTree.h | 79 + .../ScrollingCoordinatorCoordinatedGraphics.cpp | 125 + .../ScrollingCoordinatorCoordinatedGraphics.h | 60 + .../ScrollingStateNodeCoordinatedGraphics.cpp | 49 + 373 files changed, 34880 insertions(+), 18202 deletions(-) delete mode 100644 Source/WebCore/page/AbstractView.idl create mode 100644 Source/WebCore/page/ActivityState.h create mode 100644 Source/WebCore/page/ActivityStateChangeObserver.h create mode 100644 Source/WebCore/page/Base64Utilities.cpp create mode 100644 Source/WebCore/page/Base64Utilities.h create mode 100644 Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp create mode 100644 Source/WebCore/page/CaptionUserPreferencesMediaAF.h delete mode 100644 Source/WebCore/page/Console.cpp delete mode 100644 Source/WebCore/page/Console.h delete mode 100644 Source/WebCore/page/Console.idl delete mode 100644 Source/WebCore/page/ConsoleTypes.h delete mode 100644 Source/WebCore/page/ContentSecurityPolicy.cpp delete mode 100644 Source/WebCore/page/ContentSecurityPolicy.h create mode 100644 Source/WebCore/page/ContextMenuContext.cpp create mode 100644 Source/WebCore/page/ContextMenuContext.h delete mode 100644 Source/WebCore/page/DOMSecurityPolicy.cpp delete mode 100644 Source/WebCore/page/DOMSecurityPolicy.h delete mode 100644 Source/WebCore/page/DOMSecurityPolicy.idl create mode 100644 Source/WebCore/page/DatabaseProvider.cpp create mode 100644 Source/WebCore/page/DatabaseProvider.h create mode 100644 Source/WebCore/page/DebugPageOverlays.cpp create mode 100644 Source/WebCore/page/DebugPageOverlays.h create mode 100644 Source/WebCore/page/DiagnosticLoggingClient.h create mode 100644 Source/WebCore/page/DiagnosticLoggingResultType.h delete mode 100644 Source/WebCore/page/DragSession.h delete mode 100644 Source/WebCore/page/FeatureObserver.cpp delete mode 100644 Source/WebCore/page/FeatureObserver.h delete mode 100644 Source/WebCore/page/GestureTapHighlighter.cpp delete mode 100644 Source/WebCore/page/GestureTapHighlighter.h create mode 100644 Source/WebCore/page/GlobalCrypto.idl create mode 100644 Source/WebCore/page/GlobalPerformance.idl delete mode 100644 Source/WebCore/page/GroupSettings.cpp delete mode 100644 Source/WebCore/page/GroupSettings.h create mode 100644 Source/WebCore/page/IntersectionObserver.cpp create mode 100644 Source/WebCore/page/IntersectionObserver.h create mode 100644 Source/WebCore/page/IntersectionObserver.idl create mode 100644 Source/WebCore/page/IntersectionObserverCallback.h create mode 100644 Source/WebCore/page/IntersectionObserverCallback.idl create mode 100644 Source/WebCore/page/IntersectionObserverEntry.cpp create mode 100644 Source/WebCore/page/IntersectionObserverEntry.h create mode 100644 Source/WebCore/page/IntersectionObserverEntry.idl create mode 100644 Source/WebCore/page/LayerFlushThrottleState.h create mode 100644 Source/WebCore/page/MediaProducer.h create mode 100644 Source/WebCore/page/MemoryRelease.cpp create mode 100644 Source/WebCore/page/MemoryRelease.h create mode 100644 Source/WebCore/page/NavigatorConcurrentHardware.idl create mode 100644 Source/WebCore/page/NavigatorID.idl create mode 100644 Source/WebCore/page/NavigatorLanguage.idl create mode 100644 Source/WebCore/page/NavigatorOnLine.idl delete mode 100644 Source/WebCore/page/PageActivityAssertionToken.cpp delete mode 100644 Source/WebCore/page/PageActivityAssertionToken.h create mode 100644 Source/WebCore/page/PageConfiguration.cpp create mode 100644 Source/WebCore/page/PageConfiguration.h delete mode 100644 Source/WebCore/page/PageConsole.cpp delete mode 100644 Source/WebCore/page/PageConsole.h create mode 100644 Source/WebCore/page/PageConsoleClient.cpp create mode 100644 Source/WebCore/page/PageConsoleClient.h create mode 100644 Source/WebCore/page/PageOverlay.cpp create mode 100644 Source/WebCore/page/PageOverlay.h create mode 100644 Source/WebCore/page/PageOverlayController.cpp create mode 100644 Source/WebCore/page/PageOverlayController.h delete mode 100644 Source/WebCore/page/PageThrottler.cpp delete mode 100644 Source/WebCore/page/PageThrottler.h delete mode 100644 Source/WebCore/page/PageVisibilityState.cpp delete mode 100644 Source/WebCore/page/PerformanceEntryList.cpp delete mode 100644 Source/WebCore/page/PerformanceEntryList.h delete mode 100644 Source/WebCore/page/PerformanceEntryList.idl create mode 100644 Source/WebCore/page/PerformanceLogging.cpp create mode 100644 Source/WebCore/page/PerformanceLogging.h create mode 100644 Source/WebCore/page/PerformanceMonitor.cpp create mode 100644 Source/WebCore/page/PerformanceMonitor.h create mode 100644 Source/WebCore/page/PerformanceObserver.cpp create mode 100644 Source/WebCore/page/PerformanceObserver.h create mode 100644 Source/WebCore/page/PerformanceObserver.idl create mode 100644 Source/WebCore/page/PerformanceObserverCallback.h create mode 100644 Source/WebCore/page/PerformanceObserverCallback.idl create mode 100644 Source/WebCore/page/PerformanceObserverEntryList.cpp create mode 100644 Source/WebCore/page/PerformanceObserverEntryList.h create mode 100644 Source/WebCore/page/PerformanceObserverEntryList.idl create mode 100644 Source/WebCore/page/ResourceUsageData.cpp create mode 100644 Source/WebCore/page/ResourceUsageData.h create mode 100644 Source/WebCore/page/ResourceUsageOverlay.cpp create mode 100644 Source/WebCore/page/ResourceUsageOverlay.h create mode 100644 Source/WebCore/page/ResourceUsageThread.cpp create mode 100644 Source/WebCore/page/ResourceUsageThread.h create mode 100644 Source/WebCore/page/RuntimeEnabledFeatures.cpp create mode 100644 Source/WebCore/page/RuntimeEnabledFeatures.h create mode 100644 Source/WebCore/page/ScrollToOptions.h create mode 100644 Source/WebCore/page/ScrollToOptions.idl create mode 100644 Source/WebCore/page/SecurityOriginData.cpp create mode 100644 Source/WebCore/page/SecurityOriginData.h create mode 100644 Source/WebCore/page/SessionID.h create mode 100644 Source/WebCore/page/SocketProvider.cpp create mode 100644 Source/WebCore/page/SocketProvider.h delete mode 100644 Source/WebCore/page/SpeechInput.cpp delete mode 100644 Source/WebCore/page/SpeechInput.h delete mode 100644 Source/WebCore/page/SpeechInputClient.h delete mode 100644 Source/WebCore/page/SpeechInputEvent.cpp delete mode 100644 Source/WebCore/page/SpeechInputEvent.h delete mode 100644 Source/WebCore/page/SpeechInputEvent.idl delete mode 100644 Source/WebCore/page/SpeechInputListener.h delete mode 100644 Source/WebCore/page/SpeechInputResult.cpp delete mode 100644 Source/WebCore/page/SpeechInputResult.h delete mode 100644 Source/WebCore/page/SpeechInputResult.idl delete mode 100644 Source/WebCore/page/SpeechInputResultList.cpp delete mode 100644 Source/WebCore/page/SpeechInputResultList.h delete mode 100644 Source/WebCore/page/SpeechInputResultList.idl create mode 100644 Source/WebCore/page/TextIndicator.cpp create mode 100644 Source/WebCore/page/TextIndicator.h create mode 100644 Source/WebCore/page/UserContentProvider.cpp create mode 100644 Source/WebCore/page/UserContentProvider.h create mode 100644 Source/WebCore/page/UserMessageHandler.cpp create mode 100644 Source/WebCore/page/UserMessageHandler.h create mode 100644 Source/WebCore/page/UserMessageHandler.idl create mode 100644 Source/WebCore/page/UserMessageHandlerDescriptor.cpp create mode 100644 Source/WebCore/page/UserMessageHandlerDescriptor.h create mode 100644 Source/WebCore/page/UserMessageHandlerDescriptorTypes.h create mode 100644 Source/WebCore/page/UserMessageHandlersNamespace.cpp create mode 100644 Source/WebCore/page/UserMessageHandlersNamespace.h create mode 100644 Source/WebCore/page/UserMessageHandlersNamespace.idl delete mode 100644 Source/WebCore/page/ViewState.h create mode 100644 Source/WebCore/page/ViewportConfiguration.cpp create mode 100644 Source/WebCore/page/ViewportConfiguration.h delete mode 100644 Source/WebCore/page/VisitedLinkProvider.cpp delete mode 100644 Source/WebCore/page/VisitedLinkProvider.h create mode 100644 Source/WebCore/page/VisitedLinkStore.cpp create mode 100644 Source/WebCore/page/VisitedLinkStore.h create mode 100644 Source/WebCore/page/WebKitNamespace.cpp create mode 100644 Source/WebCore/page/WebKitNamespace.h create mode 100644 Source/WebCore/page/WebKitNamespace.idl create mode 100644 Source/WebCore/page/WheelEventDeltaFilter.cpp create mode 100644 Source/WebCore/page/WheelEventDeltaFilter.h create mode 100644 Source/WebCore/page/WheelEventTestTrigger.cpp create mode 100644 Source/WebCore/page/WheelEventTestTrigger.h delete mode 100644 Source/WebCore/page/WindowBase64.idl create mode 100644 Source/WebCore/page/WindowEventHandlers.idl create mode 100644 Source/WebCore/page/WindowOrWorkerGlobalScope.idl delete mode 100644 Source/WebCore/page/WindowTimers.idl delete mode 100644 Source/WebCore/page/animation/AnimationController.cpp delete mode 100644 Source/WebCore/page/animation/AnimationController.h delete mode 100644 Source/WebCore/page/animation/AnimationControllerPrivate.h create mode 100644 Source/WebCore/page/animation/CSSAnimationController.cpp create mode 100644 Source/WebCore/page/animation/CSSAnimationController.h create mode 100644 Source/WebCore/page/animation/CSSAnimationControllerPrivate.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicy.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicy.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyDirective.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyHash.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicySource.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicySource.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicySourceList.h create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp create mode 100644 Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h create mode 100644 Source/WebCore/page/linux/ResourceUsageOverlayLinux.cpp create mode 100644 Source/WebCore/page/linux/ResourceUsageThreadLinux.cpp create mode 100644 Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp create mode 100644 Source/WebCore/page/scrolling/AsyncScrollingCoordinator.h create mode 100644 Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp create mode 100644 Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h create mode 100644 Source/WebCore/page/scrolling/ScrollLatchingState.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollLatchingState.h create mode 100644 Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h create mode 100644 Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h create mode 100644 Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.h create mode 100644 Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.h create mode 100644 Source/WebCore/page/scrolling/ScrollingStateStickyNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingStateStickyNode.h create mode 100644 Source/WebCore/page/scrolling/ScrollingThread.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingThread.h create mode 100644 Source/WebCore/page/scrolling/ScrollingTree.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingTree.h create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.h create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeNode.h create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp create mode 100644 Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h create mode 100644 Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp create mode 100644 Source/WebCore/page/scrolling/ThreadedScrollingTree.h create mode 100644 Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.cpp create mode 100644 Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.h create mode 100644 Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingStateNodeCoordinatedGraphics.cpp (limited to 'Source/WebCore/page') diff --git a/Source/WebCore/page/AbstractView.idl b/Source/WebCore/page/AbstractView.idl deleted file mode 100644 index 4c39566ea..000000000 --- a/Source/WebCore/page/AbstractView.idl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Samuel Weinig - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. - */ - -// Introduced in DOM Level 2: -[ - NoInterfaceObject, - ObjCCustomImplementation, -] interface AbstractView { - readonly attribute Document document; - readonly attribute StyleMedia styleMedia; -}; - diff --git a/Source/WebCore/page/ActivityState.h b/Source/WebCore/page/ActivityState.h new file mode 100644 index 000000000..0e534cc66 --- /dev/null +++ b/Source/WebCore/page/ActivityState.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +namespace WebCore { + +struct ActivityState { + enum { + WindowIsActive = 1 << 0, + IsFocused = 1 << 1, + IsVisible = 1 << 2, + IsVisibleOrOccluded = 1 << 3, + IsInWindow = 1 << 4, + IsVisuallyIdle = 1 << 5, + IsAudible = 1 << 6, + IsLoading = 1 << 7, + }; + + typedef unsigned Flags; + + static const Flags NoFlags = 0; + static const Flags AllFlags = WindowIsActive | IsFocused | IsVisible | IsVisibleOrOccluded | IsInWindow | IsVisuallyIdle | IsAudible | IsLoading; +}; + +enum class ActivityStateForCPUSampling { + NonVisible, + VisibleNonActive, + VisibleAndActive +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/ActivityStateChangeObserver.h b/Source/WebCore/page/ActivityStateChangeObserver.h new file mode 100644 index 000000000..33437cce6 --- /dev/null +++ b/Source/WebCore/page/ActivityStateChangeObserver.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +#include "ActivityState.h" + +namespace WebCore { + +class ActivityStateChangeObserver { +public: + virtual ~ActivityStateChangeObserver() + { + } + + virtual void activityStateDidChange(ActivityState::Flags oldActivityState, ActivityState::Flags newActivityState) = 0; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/AdjustViewSizeOrNot.h b/Source/WebCore/page/AdjustViewSizeOrNot.h index 0137ad910..f8df1a4dc 100644 --- a/Source/WebCore/page/AdjustViewSizeOrNot.h +++ b/Source/WebCore/page/AdjustViewSizeOrNot.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AdjustViewSizeOrNot_h -#define AdjustViewSizeOrNot_h +#pragma once namespace WebCore { @@ -34,5 +33,3 @@ enum AdjustViewSizeOrNot { }; } // namespace WebCore - -#endif // AdjustViewSizeOrNot_h diff --git a/Source/WebCore/page/AlternativeTextClient.h b/Source/WebCore/page/AlternativeTextClient.h index a7e991d03..351be76d7 100644 --- a/Source/WebCore/page/AlternativeTextClient.h +++ b/Source/WebCore/page/AlternativeTextClient.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,18 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AlternativeTextClient_h -#define AlternativeTextClient_h +#pragma once #include "FloatRect.h" #include "TextChecking.h" #include #include -#if !PLATFORM(IOS) && PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#if PLATFORM(MAC) // Some platforms provide UI for suggesting alternative dictation text. -#define WTF_USE_DICTATION_ALTERNATIVES 1 -#endif // !PLATFORM(IOS) && PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 +#define USE_DICTATION_ALTERNATIVES 1 +#endif namespace WebCore { @@ -51,9 +50,10 @@ enum AlternativeTextType { AlternativeTextTypeDictationAlternatives }; -enum AutocorrectionResponseType { - AutocorrectionEdited, - AutocorrectionReverted +enum class AutocorrectionResponse { + Edited, + Reverted, + Accepted }; class AlternativeTextClient { @@ -64,7 +64,7 @@ public: virtual void showCorrectionAlternative(AlternativeTextType, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacmentString, const Vector& alternativeReplacementStrings) = 0; virtual void dismissAlternative(ReasonForDismissingAlternativeText) = 0; virtual String dismissAlternativeSoon(ReasonForDismissingAlternativeText) = 0; - virtual void recordAutocorrectionResponse(AutocorrectionResponseType, const String& replacedString, const String& replacementString) = 0; + virtual void recordAutocorrectionResponse(AutocorrectionResponse, const String& replacedString, const String& replacementString) = 0; #endif #if USE(DICTATION_ALTERNATIVES) virtual void showDictationAlternativeUI(const WebCore::FloatRect& boundingBoxOfDictatedText, uint64_t dictationContext) = 0; @@ -73,6 +73,4 @@ public: #endif }; -} - -#endif // AlternativeTextClient_h +} // namespace WebCore diff --git a/Source/WebCore/page/AutoscrollController.cpp b/Source/WebCore/page/AutoscrollController.cpp index aec9affe5..a7c7a58e7 100644 --- a/Source/WebCore/page/AutoscrollController.cpp +++ b/Source/WebCore/page/AutoscrollController.cpp @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -42,7 +42,7 @@ namespace WebCore { // Delay time in second for start autoscroll if pointer is in border edge of scrollable element. -static double autoscrollDelay = 0.2; +static const double autoscrollDelay = 0.2; // When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth static const double autoscrollInterval = 0.05; @@ -56,8 +56,8 @@ static Frame* getMainFrame(Frame* frame) #endif AutoscrollController::AutoscrollController() - : m_autoscrollTimer(this, &AutoscrollController::autoscrollTimerFired) - , m_autoscrollRenderer(0) + : m_autoscrollTimer(*this, &AutoscrollController::autoscrollTimerFired) + , m_autoscrollRenderer(nullptr) , m_autoscrollType(NoAutoscroll) , m_dragAndDropAutoscrollStartTime(0) { @@ -90,7 +90,7 @@ void AutoscrollController::stopAutoscrollTimer(bool rendererIsBeingDestroyed) { RenderBox* scrollable = m_autoscrollRenderer; m_autoscrollTimer.stop(); - m_autoscrollRenderer = 0; + m_autoscrollRenderer = nullptr; if (!scrollable) return; @@ -135,9 +135,9 @@ void AutoscrollController::updateAutoscrollRenderer() renderer = nodeAtPoint->renderer(); #endif - while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) + while (renderer && !(is(*renderer) && downcast(*renderer).canAutoscroll())) renderer = renderer->parent(); - m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0; + m_autoscrollRenderer = is(renderer) ? downcast(renderer) : nullptr; } void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime) @@ -231,7 +231,7 @@ bool AutoscrollController::panScrollInProgress() const } #endif -void AutoscrollController::autoscrollTimerFired(Timer&) +void AutoscrollController::autoscrollTimerFired() { if (!m_autoscrollRenderer) { stopAutoscrollTimer(); @@ -252,7 +252,7 @@ void AutoscrollController::autoscrollTimerFired(Timer&) #if ENABLE(DRAG_SUPPORT) frame.eventHandler().updateSelectionForMouseDrag(); #endif - m_autoscrollRenderer->autoscroll(frame.eventHandler().lastKnownMousePosition()); + m_autoscrollRenderer->autoscroll(frame.eventHandler().effectiveMousePositionForSelectionAutoscroll()); break; } case NoAutoscroll: diff --git a/Source/WebCore/page/AutoscrollController.h b/Source/WebCore/page/AutoscrollController.h index 2c34cb62a..3cd9dbf33 100644 --- a/Source/WebCore/page/AutoscrollController.h +++ b/Source/WebCore/page/AutoscrollController.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AutoscrollController_h -#define AutoscrollController_h +#pragma once #include "IntPoint.h" #include "Timer.h" @@ -49,8 +48,9 @@ enum AutoscrollType { #endif }; -// AutscrollController handels autoscroll and pan scroll for EventHandler. +// AutscrollController handles autoscroll and pan scroll for EventHandler. class AutoscrollController { + WTF_MAKE_FAST_ALLOCATED; public: AutoscrollController(); RenderBox* autoscrollRenderer() const; @@ -69,13 +69,13 @@ public: #endif private: - void autoscrollTimerFired(Timer&); + void autoscrollTimerFired(); void startAutoscrollTimer(); #if ENABLE(PAN_SCROLLING) void updatePanScrollState(FrameView*, const IntPoint&); #endif - Timer m_autoscrollTimer; + Timer m_autoscrollTimer; RenderBox* m_autoscrollRenderer; AutoscrollType m_autoscrollType; IntPoint m_dragAndDropAutoscrollReferencePosition; @@ -86,5 +86,3 @@ private: }; } // namespace WebCore - -#endif // AutoscrollController_h diff --git a/Source/WebCore/page/BarProp.cpp b/Source/WebCore/page/BarProp.cpp index 3be4fb397..9446634e3 100644 --- a/Source/WebCore/page/BarProp.cpp +++ b/Source/WebCore/page/BarProp.cpp @@ -10,7 +10,7 @@ * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * diff --git a/Source/WebCore/page/BarProp.h b/Source/WebCore/page/BarProp.h index 36830ea6a..78d7d1e73 100644 --- a/Source/WebCore/page/BarProp.h +++ b/Source/WebCore/page/BarProp.h @@ -10,7 +10,7 @@ * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -26,12 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef BarProp_h -#define BarProp_h +#pragma once #include "DOMWindowProperty.h" #include "ScriptWrappable.h" -#include +#include #include namespace WebCore { @@ -42,7 +41,7 @@ class BarProp : public ScriptWrappable, public RefCounted, public DOMWi public: enum Type { Locationbar, Menubar, Personalbar, Scrollbars, Statusbar, Toolbar }; - static PassRefPtr create(Frame* frame, Type type) { return adoptRef(new BarProp(frame, type)); } + static Ref create(Frame* frame, Type type) { return adoptRef(*new BarProp(frame, type)); } Type type() const; bool visible() const; @@ -53,5 +52,3 @@ private: }; } // namespace WebCore - -#endif // BarProp_h diff --git a/Source/WebCore/page/BarProp.idl b/Source/WebCore/page/BarProp.idl index 188a24f70..d7ae23932 100644 --- a/Source/WebCore/page/BarProp.idl +++ b/Source/WebCore/page/BarProp.idl @@ -10,7 +10,7 @@ * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * diff --git a/Source/WebCore/page/Base64Utilities.cpp b/Source/WebCore/page/Base64Utilities.cpp new file mode 100644 index 000000000..244a1f143 --- /dev/null +++ b/Source/WebCore/page/Base64Utilities.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "Base64Utilities.h" + +#include "ExceptionCode.h" +#include + +namespace WebCore { + +ExceptionOr Base64Utilities::btoa(const String& stringToEncode) +{ + if (stringToEncode.isNull()) + return String(); + + if (!stringToEncode.containsOnlyLatin1()) + return Exception { INVALID_CHARACTER_ERR }; + + return base64Encode(stringToEncode.latin1()); +} + +ExceptionOr Base64Utilities::atob(const String& encodedString) +{ + if (encodedString.isNull()) + return String(); + + Vector out; + if (!base64Decode(encodedString, out, Base64ValidatePadding | Base64IgnoreSpacesAndNewLines)) + return Exception { INVALID_CHARACTER_ERR }; + + return String(out.data(), out.size()); +} + +} diff --git a/Source/WebCore/page/Base64Utilities.h b/Source/WebCore/page/Base64Utilities.h new file mode 100644 index 000000000..baf9a81c4 --- /dev/null +++ b/Source/WebCore/page/Base64Utilities.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +#include "ExceptionOr.h" + +namespace WebCore { + +class Base64Utilities { +public: + static ExceptionOr btoa(const String&); + static ExceptionOr atob(const String&); +}; + +} diff --git a/Source/WebCore/page/CaptionUserPreferences.cpp b/Source/WebCore/page/CaptionUserPreferences.cpp index adb570ea7..d2c391a5d 100644 --- a/Source/WebCore/page/CaptionUserPreferences.cpp +++ b/Source/WebCore/page/CaptionUserPreferences.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,25 +24,31 @@ */ #include "config.h" +#include "CaptionUserPreferences.h" #if ENABLE(VIDEO_TRACK) -#include "CaptionUserPreferences.h" +#include "AudioTrackList.h" #include "DOMWrapperWorld.h" #include "Page.h" #include "PageGroup.h" #include "Settings.h" #include "TextTrackList.h" +#include "UserContentController.h" +#include "UserContentTypes.h" +#include "UserStyleSheet.h" #include "UserStyleSheetTypes.h" +#include +#include +#include +#include namespace WebCore { CaptionUserPreferences::CaptionUserPreferences(PageGroup& group) : m_pageGroup(group) , m_displayMode(ForcedOnly) - , m_timer(this, &CaptionUserPreferences::timerFired) - , m_testingMode(false) - , m_havePreferences(false) + , m_timer(*this, &CaptionUserPreferences::timerFired) { } @@ -50,13 +56,27 @@ CaptionUserPreferences::~CaptionUserPreferences() { } -void CaptionUserPreferences::timerFired(Timer&) +void CaptionUserPreferences::timerFired() { captionPreferencesChanged(); } +void CaptionUserPreferences::beginBlockingNotifications() +{ + ++m_blockNotificationsCounter; +} + +void CaptionUserPreferences::endBlockingNotifications() +{ + ASSERT(m_blockNotificationsCounter); + --m_blockNotificationsCounter; +} + void CaptionUserPreferences::notify() { + if (m_blockNotificationsCounter) + return; + m_havePreferences = true; if (!m_timer.isActive()) m_timer.startOneShot(0); @@ -77,9 +97,17 @@ void CaptionUserPreferences::setCaptionDisplayMode(CaptionUserPreferences::Capti notify(); } +Page* CaptionUserPreferences::currentPage() const +{ + if (m_pageGroup.pages().isEmpty()) + return nullptr; + + return *(m_pageGroup.pages().begin()); +} + bool CaptionUserPreferences::userPrefersCaptions() const { - Page* page = *(m_pageGroup.pages().begin()); + Page* page = currentPage(); if (!page) return false; @@ -88,7 +116,7 @@ bool CaptionUserPreferences::userPrefersCaptions() const void CaptionUserPreferences::setUserPrefersCaptions(bool preference) { - Page* page = *(m_pageGroup.pages().begin()); + Page* page = currentPage(); if (!page) return; @@ -98,7 +126,7 @@ void CaptionUserPreferences::setUserPrefersCaptions(bool preference) bool CaptionUserPreferences::userPrefersSubtitles() const { - Page* page = *(pageGroup().pages().begin()); + Page* page = currentPage(); if (!page) return false; @@ -107,7 +135,7 @@ bool CaptionUserPreferences::userPrefersSubtitles() const void CaptionUserPreferences::setUserPrefersSubtitles(bool preference) { - Page* page = *(m_pageGroup.pages().begin()); + Page* page = currentPage(); if (!page) return; @@ -117,7 +145,7 @@ void CaptionUserPreferences::setUserPrefersSubtitles(bool preference) bool CaptionUserPreferences::userPrefersTextDescriptions() const { - Page* page = *(m_pageGroup.pages().begin()); + Page* page = currentPage(); if (!page) return false; @@ -126,7 +154,7 @@ bool CaptionUserPreferences::userPrefersTextDescriptions() const void CaptionUserPreferences::setUserPrefersTextDescriptions(bool preference) { - Page* page = *(m_pageGroup.pages().begin()); + Page* page = currentPage(); if (!page) return; @@ -154,6 +182,20 @@ void CaptionUserPreferences::setPreferredLanguage(const String& language) notify(); } +void CaptionUserPreferences::setPreferredAudioCharacteristic(const String& characteristic) +{ + m_userPreferredAudioCharacteristic = characteristic; + notify(); +} + +Vector CaptionUserPreferences::preferredAudioCharacteristics() const +{ + Vector characteristics; + if (!m_userPreferredAudioCharacteristic.isEmpty()) + characteristics.append(m_userPreferredAudioCharacteristic); + return characteristics; +} + static String trackDisplayName(TextTrack* track) { if (track == TextTrack::captionMenuOffItem()) @@ -161,11 +203,11 @@ static String trackDisplayName(TextTrack* track) if (track == TextTrack::captionMenuAutomaticItem()) return textTrackAutomaticMenuItemText(); - if (track->label().isEmpty() && track->language().isEmpty()) + if (track->label().isEmpty() && track->validBCP47Language().isEmpty()) return textTrackNoLabelText(); if (!track->label().isEmpty()) return track->label(); - return track->language(); + return track->validBCP47Language(); } String CaptionUserPreferences::displayNameForTrack(TextTrack* track) const @@ -181,12 +223,12 @@ Vector> CaptionUserPreferences::sortedTrackListForMenu(TextTra for (unsigned i = 0, length = trackList->length(); i < length; ++i) { TextTrack* track = trackList->item(i); - const AtomicString& kind = track->kind(); - if (kind == TextTrack::captionsKeyword() || kind == TextTrack::descriptionsKeyword() || kind == TextTrack::subtitlesKeyword()) + auto kind = track->kind(); + if (kind == TextTrack::Kind::Captions || kind == TextTrack::Kind::Descriptions || kind == TextTrack::Kind::Subtitles) tracksForMenu.append(track); } - std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](const RefPtr& a, const RefPtr& b) { + std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](auto& a, auto& b) { return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0; }); @@ -196,57 +238,76 @@ Vector> CaptionUserPreferences::sortedTrackListForMenu(TextTra return tracksForMenu; } -int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track, HTMLMediaElement*) const +static String trackDisplayName(AudioTrack* track) { - int trackScore = 0; + if (track->label().isEmpty() && track->validBCP47Language().isEmpty()) + return audioTrackNoLabelText(); + if (!track->label().isEmpty()) + return track->label(); + return track->validBCP47Language(); +} - if (track->kind() != TextTrack::captionsKeyword() && track->kind() != TextTrack::subtitlesKeyword()) - return trackScore; +String CaptionUserPreferences::displayNameForTrack(AudioTrack* track) const +{ + return trackDisplayName(track); +} + +Vector> CaptionUserPreferences::sortedTrackListForMenu(AudioTrackList* trackList) +{ + ASSERT(trackList); + + Vector> tracksForMenu; + + for (unsigned i = 0, length = trackList->length(); i < length; ++i) { + AudioTrack* track = trackList->item(i); + tracksForMenu.append(track); + } + + std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](auto& a, auto& b) { + return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0; + }); + + return tracksForMenu; +} + +int CaptionUserPreferences::textTrackSelectionScore(TextTrack* track, HTMLMediaElement*) const +{ + if (track->kind() != TextTrack::Kind::Captions && track->kind() != TextTrack::Kind::Subtitles) + return 0; if (!userPrefersSubtitles() && !userPrefersCaptions()) - return trackScore; - - if (track->kind() == TextTrack::subtitlesKeyword() && userPrefersSubtitles()) - trackScore = 1; - else if (track->kind() == TextTrack::captionsKeyword() && userPrefersCaptions()) - trackScore = 1; + return 0; - return trackScore + textTrackLanguageSelectionScore(track, preferredLanguages()); + return textTrackLanguageSelectionScore(track, preferredLanguages()) + 1; } int CaptionUserPreferences::textTrackLanguageSelectionScore(TextTrack* track, const Vector& preferredLanguages) const { - if (track->language().isEmpty()) + if (track->validBCP47Language().isEmpty()) return 0; - size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->language(), preferredLanguages); + bool exactMatch; + size_t languageMatchIndex = indexOfBestMatchingLanguageInList(track->validBCP47Language(), preferredLanguages, exactMatch); if (languageMatchIndex >= preferredLanguages.size()) return 0; // Matching a track language is more important than matching track type, so this multiplier must be // greater than the maximum value returned by textTrackSelectionScore. - return (preferredLanguages.size() - languageMatchIndex) * 10; + int bonus = exactMatch ? 1 : 0; + return (preferredLanguages.size() + bonus - languageMatchIndex) * 10; } void CaptionUserPreferences::setCaptionsStyleSheetOverride(const String& override) { m_captionsStyleSheetOverride = override; - updateCaptionStyleSheetOveride(); + updateCaptionStyleSheetOverride(); } -void CaptionUserPreferences::updateCaptionStyleSheetOveride() +void CaptionUserPreferences::updateCaptionStyleSheetOverride() { - // Identify our override style sheet with a unique URL - a new scheme and a UUID. - DEFINE_STATIC_LOCAL(URL, captionsStyleSheetURL, (ParsedURLString, "user-captions-override:01F6AF12-C3B0-4F70-AF5E-A3E00234DC23")); - - m_pageGroup.removeUserStyleSheetFromWorld(mainThreadNormalWorld(), captionsStyleSheetURL); - String captionsOverrideStyleSheet = captionsStyleSheetOverride(); - if (captionsOverrideStyleSheet.isEmpty()) - return; - - m_pageGroup.addUserStyleSheetToWorld(mainThreadNormalWorld(), captionsOverrideStyleSheet, captionsStyleSheetURL, Vector(), - Vector(), InjectInAllFrames, UserStyleAuthorLevel, InjectInExistingDocuments); + for (auto& page : m_pageGroup.pages()) + page->setCaptionUserPreferencesStyleSheet(captionsOverrideStyleSheet); } String CaptionUserPreferences::primaryAudioTrackLanguageOverride() const diff --git a/Source/WebCore/page/CaptionUserPreferences.h b/Source/WebCore/page/CaptionUserPreferences.h index 3e5a3b9c4..8feea7da2 100644 --- a/Source/WebCore/page/CaptionUserPreferences.h +++ b/Source/WebCore/page/CaptionUserPreferences.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,22 +23,22 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CaptionUserPreferences_h -#define CaptionUserPreferences_h +#pragma once #if ENABLE(VIDEO_TRACK) +#include "AudioTrack.h" #include "Language.h" #include "LocalizedStrings.h" #include "TextTrack.h" #include "Timer.h" -#include #include namespace WebCore { class HTMLMediaElement; class PageGroup; +class AudioTrackList; class TextTrackList; class CaptionUserPreferences { @@ -49,7 +49,8 @@ public: enum CaptionDisplayMode { Automatic, ForcedOnly, - AlwaysOn + AlwaysOn, + Manual, }; virtual CaptionDisplayMode captionDisplayMode() const; virtual void setCaptionDisplayMode(CaptionDisplayMode); @@ -78,35 +79,44 @@ public: virtual void setPreferredLanguage(const String&); virtual Vector preferredLanguages() const; + virtual void setPreferredAudioCharacteristic(const String&); + virtual Vector preferredAudioCharacteristics() const; + virtual String displayNameForTrack(TextTrack*) const; virtual Vector> sortedTrackListForMenu(TextTrackList*); + virtual String displayNameForTrack(AudioTrack*) const; + virtual Vector> sortedTrackListForMenu(AudioTrackList*); + void setPrimaryAudioTrackLanguageOverride(const String& language) { m_primaryAudioTrackLanguageOverride = language; } String primaryAudioTrackLanguageOverride() const; virtual bool testingMode() const { return m_testingMode; } - virtual void setTestingMode(bool override) { m_testingMode = override; } + void setTestingMode(bool override) { m_testingMode = override; } PageGroup& pageGroup() const { return m_pageGroup; } protected: - void updateCaptionStyleSheetOveride(); + void updateCaptionStyleSheetOverride(); + void beginBlockingNotifications(); + void endBlockingNotifications(); private: - void timerFired(Timer&); + void timerFired(); void notify(); + Page* currentPage() const; PageGroup& m_pageGroup; - CaptionDisplayMode m_displayMode; - Timer m_timer; + mutable CaptionDisplayMode m_displayMode; + Timer m_timer; String m_userPreferredLanguage; + String m_userPreferredAudioCharacteristic; String m_captionsStyleSheetOverride; String m_primaryAudioTrackLanguageOverride; - bool m_testingMode; - bool m_havePreferences; + unsigned m_blockNotificationsCounter { 0 }; + bool m_testingMode { false }; + bool m_havePreferences { false }; }; } #endif - -#endif diff --git a/Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp b/Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp new file mode 100644 index 000000000..117143bbc --- /dev/null +++ b/Source/WebCore/page/CaptionUserPreferencesMediaAF.cpp @@ -0,0 +1,1000 @@ +/* + * Copyright (C) 2012-2015 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "config.h" + +#if ENABLE(VIDEO_TRACK) + +#if !USE(DIRECT2D) + +#include "CaptionUserPreferencesMediaAF.h" + +#include "AudioTrackList.h" +#include "FloatConversion.h" +#include "HTMLMediaElement.h" +#include "URL.h" +#include "Language.h" +#include "LocalizedStrings.h" +#include "Logging.h" +#include "MediaControlElements.h" +#include "SoftLinking.h" +#include "TextTrackList.h" +#include "UserStyleSheetTypes.h" +#include "VTTCue.h" +#include +#include +#include +#include +#include + +#if PLATFORM(IOS) +#import "WebCoreThreadRun.h" +#endif + +#if COMPILER(MSVC) +// See https://msdn.microsoft.com/en-us/library/35bhkfb6.aspx +#pragma warning(disable: 4273) +#endif + +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) +#include +#include + +#include "MediaAccessibilitySoftLink.h" + +#if PLATFORM(WIN) + +#ifdef DEBUG_ALL +#define SOFT_LINK_AVF_FRAMEWORK(Lib) SOFT_LINK_DEBUG_LIBRARY(Lib) +#else +#define SOFT_LINK_AVF_FRAMEWORK(Lib) SOFT_LINK_LIBRARY(Lib) +#endif + +#define SOFT_LINK_AVF(Lib, Name, Type) SOFT_LINK_DLL_IMPORT(Lib, Name, Type) +#define SOFT_LINK_AVF_POINTER(Lib, Name, Type) SOFT_LINK_VARIABLE_DLL_IMPORT_OPTIONAL(Lib, Name, Type) +#define SOFT_LINK_AVF_FRAMEWORK_IMPORT(Lib, Fun, ReturnType, Arguments, Signature) SOFT_LINK_DLL_IMPORT(Lib, Fun, ReturnType, __cdecl, Arguments, Signature) +#define SOFT_LINK_AVF_FRAMEWORK_IMPORT_OPTIONAL(Lib, Fun, ReturnType, Arguments) SOFT_LINK_DLL_IMPORT_OPTIONAL(Lib, Fun, ReturnType, __cdecl, Arguments) + +// CoreText only needs to be soft-linked on Windows. +SOFT_LINK_AVF_FRAMEWORK(CoreText) +SOFT_LINK_AVF_FRAMEWORK_IMPORT(CoreText, CTFontDescriptorCopyAttribute, CFTypeRef, (CTFontDescriptorRef descriptor, CFStringRef attribute), (descriptor, attribute)); +SOFT_LINK_AVF_POINTER(CoreText, kCTFontNameAttribute, CFStringRef) +#define kCTFontNameAttribute getkCTFontNameAttribute() + +#define CTFontDescriptorCopyAttribute softLink_CTFontDescriptorCopyAttribute + +SOFT_LINK_AVF_FRAMEWORK(CoreMedia) +SOFT_LINK_AVF_FRAMEWORK_IMPORT_OPTIONAL(CoreMedia, MTEnableCaption2015Behavior, Boolean, ()) + +#else + +SOFT_LINK_FRAMEWORK(MediaToolbox) +SOFT_LINK_OPTIONAL(MediaToolbox, MTEnableCaption2015Behavior, Boolean, (), ()) + +#endif // PLATFORM(WIN) + +#endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + +namespace WebCore { + +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) +static void userCaptionPreferencesChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void *, CFDictionaryRef) +{ +#if !PLATFORM(IOS) + static_cast(observer)->captionPreferencesChanged(); +#else + WebThreadRun(^{ + static_cast(observer)->captionPreferencesChanged(); + }); +#endif +} +#endif + +CaptionUserPreferencesMediaAF::CaptionUserPreferencesMediaAF(PageGroup& group) + : CaptionUserPreferences(group) +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + , m_updateStyleSheetTimer(*this, &CaptionUserPreferencesMediaAF::updateTimerFired) + , m_listeningForPreferenceChanges(false) +#endif +{ + static bool initialized; + if (!initialized) { + initialized = true; + + MTEnableCaption2015BehaviorPtrType function = MTEnableCaption2015BehaviorPtr(); + if (!function || !function()) + return; + + beginBlockingNotifications(); + CaptionUserPreferences::setCaptionDisplayMode(Manual); + setUserPrefersCaptions(false); + setUserPrefersSubtitles(false); + setUserPrefersTextDescriptions(false); + endBlockingNotifications(); + } +} + +CaptionUserPreferencesMediaAF::~CaptionUserPreferencesMediaAF() +{ +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + if (kMAXCaptionAppearanceSettingsChangedNotification) + CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, kMAXCaptionAppearanceSettingsChangedNotification, 0); + if (kMAAudibleMediaSettingsChangedNotification) + CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), this, kMAAudibleMediaSettingsChangedNotification, 0); +#endif +} + +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + +CaptionUserPreferences::CaptionDisplayMode CaptionUserPreferencesMediaAF::captionDisplayMode() const +{ + CaptionDisplayMode internalMode = CaptionUserPreferences::captionDisplayMode(); + if (internalMode == Manual || testingMode() || !MediaAccessibilityLibrary()) + return internalMode; + + MACaptionAppearanceDisplayType displayType = MACaptionAppearanceGetDisplayType(kMACaptionAppearanceDomainUser); + switch (displayType) { + case kMACaptionAppearanceDisplayTypeForcedOnly: + return ForcedOnly; + + case kMACaptionAppearanceDisplayTypeAutomatic: + return Automatic; + + case kMACaptionAppearanceDisplayTypeAlwaysOn: + return AlwaysOn; + } + + ASSERT_NOT_REACHED(); + return ForcedOnly; +} + +void CaptionUserPreferencesMediaAF::setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode mode) +{ + if (testingMode() || !MediaAccessibilityLibrary()) { + CaptionUserPreferences::setCaptionDisplayMode(mode); + return; + } + + if (captionDisplayMode() == Manual) + return; + + MACaptionAppearanceDisplayType displayType = kMACaptionAppearanceDisplayTypeForcedOnly; + switch (mode) { + case Automatic: + displayType = kMACaptionAppearanceDisplayTypeAutomatic; + break; + case ForcedOnly: + displayType = kMACaptionAppearanceDisplayTypeForcedOnly; + break; + case AlwaysOn: + displayType = kMACaptionAppearanceDisplayTypeAlwaysOn; + break; + default: + ASSERT_NOT_REACHED(); + break; + } + + MACaptionAppearanceSetDisplayType(kMACaptionAppearanceDomainUser, displayType); +} + +bool CaptionUserPreferencesMediaAF::userPrefersCaptions() const +{ + bool captionSetting = CaptionUserPreferences::userPrefersCaptions(); + if (captionSetting || testingMode() || !MediaAccessibilityLibrary()) + return captionSetting; + + RetainPtr captioningMediaCharacteristics = adoptCF(MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser)); + return captioningMediaCharacteristics && CFArrayGetCount(captioningMediaCharacteristics.get()); +} + +bool CaptionUserPreferencesMediaAF::userPrefersSubtitles() const +{ + bool subtitlesSetting = CaptionUserPreferences::userPrefersSubtitles(); + if (subtitlesSetting || testingMode() || !MediaAccessibilityLibrary()) + return subtitlesSetting; + + RetainPtr captioningMediaCharacteristics = adoptCF(MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser)); + return !(captioningMediaCharacteristics && CFArrayGetCount(captioningMediaCharacteristics.get())); +} + +void CaptionUserPreferencesMediaAF::updateTimerFired() +{ + updateCaptionStyleSheetOverride(); +} + +void CaptionUserPreferencesMediaAF::setInterestedInCaptionPreferenceChanges() +{ + if (m_listeningForPreferenceChanges) + return; + + if (!MediaAccessibilityLibrary()) + return; + + if (!kMAXCaptionAppearanceSettingsChangedNotification && !canLoad_MediaAccessibility_kMAAudibleMediaSettingsChangedNotification()) + return; + + m_listeningForPreferenceChanges = true; + m_registeringForNotification = true; + + if (kMAXCaptionAppearanceSettingsChangedNotification) + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, userCaptionPreferencesChangedNotificationCallback, kMAXCaptionAppearanceSettingsChangedNotification, 0, CFNotificationSuspensionBehaviorCoalesce); + if (canLoad_MediaAccessibility_kMAAudibleMediaSettingsChangedNotification()) + CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, userCaptionPreferencesChangedNotificationCallback, kMAAudibleMediaSettingsChangedNotification, 0, CFNotificationSuspensionBehaviorCoalesce); + m_registeringForNotification = false; + + // Generating and registering the caption stylesheet can be expensive and this method is called indirectly when the parser creates an audio or + // video element, so do it after a brief pause. + m_updateStyleSheetTimer.startOneShot(0); +} + +void CaptionUserPreferencesMediaAF::captionPreferencesChanged() +{ + if (m_registeringForNotification) + return; + + if (m_listeningForPreferenceChanges) + updateCaptionStyleSheetOverride(); + + CaptionUserPreferences::captionPreferencesChanged(); +} + +String CaptionUserPreferencesMediaAF::captionsWindowCSS() const +{ + MACaptionAppearanceBehavior behavior; + RetainPtr color = adoptCF(MACaptionAppearanceCopyWindowColor(kMACaptionAppearanceDomainUser, &behavior)); + + Color windowColor(color.get()); + if (!windowColor.isValid()) + windowColor = Color::transparent; + + bool important = behavior == kMACaptionAppearanceBehaviorUseValue; + CGFloat opacity = MACaptionAppearanceGetWindowOpacity(kMACaptionAppearanceDomainUser, &behavior); + if (!important) + important = behavior == kMACaptionAppearanceBehaviorUseValue; + String windowStyle = colorPropertyCSS(CSSPropertyBackgroundColor, Color(windowColor.red(), windowColor.green(), windowColor.blue(), static_cast(opacity * 255)), important); + + if (!opacity) + return windowStyle; + + return makeString(windowStyle, getPropertyNameString(CSSPropertyPadding), ": .4em !important;"); +} + +String CaptionUserPreferencesMediaAF::captionsBackgroundCSS() const +{ + // This default value must be the same as the one specified in mediaControls.css for -webkit-media-text-track-past-nodes + // and webkit-media-text-track-future-nodes. + static NeverDestroyed defaultBackgroundColor(0, 0, 0, 0.8 * 255); + + MACaptionAppearanceBehavior behavior; + + RetainPtr color = adoptCF(MACaptionAppearanceCopyBackgroundColor(kMACaptionAppearanceDomainUser, &behavior)); + Color backgroundColor(color.get()); + if (!backgroundColor.isValid()) + backgroundColor = defaultBackgroundColor; + + bool important = behavior == kMACaptionAppearanceBehaviorUseValue; + CGFloat opacity = MACaptionAppearanceGetBackgroundOpacity(kMACaptionAppearanceDomainUser, &behavior); + if (!important) + important = behavior == kMACaptionAppearanceBehaviorUseValue; + return colorPropertyCSS(CSSPropertyBackgroundColor, Color(backgroundColor.red(), backgroundColor.green(), backgroundColor.blue(), static_cast(opacity * 255)), important); +} + +Color CaptionUserPreferencesMediaAF::captionsTextColor(bool& important) const +{ + MACaptionAppearanceBehavior behavior; + RetainPtr color = adoptCF(MACaptionAppearanceCopyForegroundColor(kMACaptionAppearanceDomainUser, &behavior)); + Color textColor(color.get()); + if (!textColor.isValid()) + // This default value must be the same as the one specified in mediaControls.css for -webkit-media-text-track-container. + textColor = Color::white; + + important = behavior == kMACaptionAppearanceBehaviorUseValue; + CGFloat opacity = MACaptionAppearanceGetForegroundOpacity(kMACaptionAppearanceDomainUser, &behavior); + if (!important) + important = behavior == kMACaptionAppearanceBehaviorUseValue; + return Color(textColor.red(), textColor.green(), textColor.blue(), static_cast(opacity * 255)); +} + +String CaptionUserPreferencesMediaAF::captionsTextColorCSS() const +{ + bool important; + Color textColor = captionsTextColor(important); + + if (!textColor.isValid()) + return emptyString(); + + return colorPropertyCSS(CSSPropertyColor, textColor, important); +} + +String CaptionUserPreferencesMediaAF::windowRoundedCornerRadiusCSS() const +{ + MACaptionAppearanceBehavior behavior; + CGFloat radius = MACaptionAppearanceGetWindowRoundedCornerRadius(kMACaptionAppearanceDomainUser, &behavior); + if (!radius) + return emptyString(); + + StringBuilder builder; + builder.append(getPropertyNameString(CSSPropertyBorderRadius)); + builder.append(String::format(":%.02fpx", radius)); + if (behavior == kMACaptionAppearanceBehaviorUseValue) + builder.appendLiteral(" !important"); + builder.append(';'); + + return builder.toString(); +} + +Color CaptionUserPreferencesMediaAF::captionsEdgeColorForTextColor(const Color& textColor) const +{ + int distanceFromWhite = differenceSquared(textColor, Color::white); + int distanceFromBlack = differenceSquared(textColor, Color::black); + + if (distanceFromWhite < distanceFromBlack) + return textColor.dark(); + + return textColor.light(); +} + +String CaptionUserPreferencesMediaAF::cssPropertyWithTextEdgeColor(CSSPropertyID id, const String& value, const Color& textColor, bool important) const +{ + StringBuilder builder; + + builder.append(getPropertyNameString(id)); + builder.append(':'); + builder.append(value); + builder.append(' '); + builder.append(captionsEdgeColorForTextColor(textColor).serialized()); + if (important) + builder.appendLiteral(" !important"); + builder.append(';'); + if (id == CSSPropertyWebkitTextStroke) { + builder.append(" paint-order: stroke;"); + builder.append(" stroke-linejoin: round;"); + builder.append(" stroke-linecap: round;"); + } + + return builder.toString(); +} + +String CaptionUserPreferencesMediaAF::colorPropertyCSS(CSSPropertyID id, const Color& color, bool important) const +{ + StringBuilder builder; + + builder.append(getPropertyNameString(id)); + builder.append(':'); + builder.append(color.serialized()); + if (important) + builder.appendLiteral(" !important"); + builder.append(';'); + + return builder.toString(); +} + +String CaptionUserPreferencesMediaAF::strokeWidth() const +{ + static NeverDestroyed strokeWidthDefault(ASCIILiteral(" .03em ")); + + if (!MACaptionFontAttributeStrokeWidth && !canLoad_MediaAccessibility_MACaptionFontAttributeStrokeWidth()) + return strokeWidthDefault; + + MACaptionAppearanceBehavior behavior; + + auto font = adoptCF(MACaptionAppearanceCopyFontDescriptorForStyle(kMACaptionAppearanceDomainUser, &behavior, kMACaptionAppearanceFontStyleDefault)); + if (!font) + return strokeWidthDefault; + + auto strokeWidthAttribute = adoptCF(CTFontDescriptorCopyAttribute(font.get(), MACaptionFontAttributeStrokeWidth)); + if (!strokeWidthAttribute) + return strokeWidthDefault; + + int strokeWidth = 0; + if (!CFNumberGetValue(static_cast(strokeWidthAttribute.get()), kCFNumberIntType, &strokeWidth)) + return strokeWidthDefault; + + return String::format(" %dpx ", strokeWidth); +} + +String CaptionUserPreferencesMediaAF::captionsTextEdgeCSS() const +{ + static NeverDestroyed edgeStyleRaised(ASCIILiteral(" -.05em -.05em 0 ")); + static NeverDestroyed edgeStyleDepressed(ASCIILiteral(" .05em .05em 0 ")); + static NeverDestroyed edgeStyleDropShadow(ASCIILiteral(" .075em .075em 0 ")); + + bool unused; + Color color = captionsTextColor(unused); + if (!color.isValid()) + color = Color { Color::black }; + color = captionsEdgeColorForTextColor(color); + + MACaptionAppearanceBehavior behavior; + MACaptionAppearanceTextEdgeStyle textEdgeStyle = MACaptionAppearanceGetTextEdgeStyle(kMACaptionAppearanceDomainUser, &behavior); + switch (textEdgeStyle) { + case kMACaptionAppearanceTextEdgeStyleUndefined: + case kMACaptionAppearanceTextEdgeStyleNone: + return emptyString(); + + case kMACaptionAppearanceTextEdgeStyleRaised: + return cssPropertyWithTextEdgeColor(CSSPropertyTextShadow, edgeStyleRaised, color, behavior == kMACaptionAppearanceBehaviorUseValue); + case kMACaptionAppearanceTextEdgeStyleDepressed: + return cssPropertyWithTextEdgeColor(CSSPropertyTextShadow, edgeStyleDepressed, color, behavior == kMACaptionAppearanceBehaviorUseValue); + case kMACaptionAppearanceTextEdgeStyleDropShadow: + return cssPropertyWithTextEdgeColor(CSSPropertyTextShadow, edgeStyleDropShadow, color, behavior == kMACaptionAppearanceBehaviorUseValue); + case kMACaptionAppearanceTextEdgeStyleUniform: + return cssPropertyWithTextEdgeColor(CSSPropertyWebkitTextStroke, strokeWidth(), color, behavior == kMACaptionAppearanceBehaviorUseValue); + + default: + ASSERT_NOT_REACHED(); + break; + } + + return emptyString(); +} + +String CaptionUserPreferencesMediaAF::captionsDefaultFontCSS() const +{ + MACaptionAppearanceBehavior behavior; + + RetainPtr font = adoptCF(MACaptionAppearanceCopyFontDescriptorForStyle(kMACaptionAppearanceDomainUser, &behavior, kMACaptionAppearanceFontStyleDefault)); + if (!font) + return emptyString(); + + RetainPtr name = adoptCF(CTFontDescriptorCopyAttribute(font.get(), kCTFontNameAttribute)); + if (!name) + return emptyString(); + + StringBuilder builder; + + builder.append(getPropertyNameString(CSSPropertyFontFamily)); + builder.appendLiteral(": \""); + builder.append(static_cast(name.get())); + builder.append('"'); + if (behavior == kMACaptionAppearanceBehaviorUseValue) + builder.appendLiteral(" !important"); + builder.append(';'); + + return builder.toString(); +} + +float CaptionUserPreferencesMediaAF::captionFontSizeScaleAndImportance(bool& important) const +{ + if (testingMode() || !MediaAccessibilityLibrary()) + return CaptionUserPreferences::captionFontSizeScaleAndImportance(important); + + MACaptionAppearanceBehavior behavior; + CGFloat characterScale = CaptionUserPreferences::captionFontSizeScaleAndImportance(important); + CGFloat scaleAdjustment = MACaptionAppearanceGetRelativeCharacterSize(kMACaptionAppearanceDomainUser, &behavior); + + if (!scaleAdjustment) + return characterScale; + + important = behavior == kMACaptionAppearanceBehaviorUseValue; +#if defined(__LP64__) && __LP64__ + return narrowPrecisionToFloat(scaleAdjustment * characterScale); +#else + return scaleAdjustment * characterScale; +#endif +} + +void CaptionUserPreferencesMediaAF::setPreferredLanguage(const String& language) +{ + if (CaptionUserPreferences::captionDisplayMode() == Manual) + return; + + if (testingMode() || !MediaAccessibilityLibrary()) { + CaptionUserPreferences::setPreferredLanguage(language); + return; + } + + MACaptionAppearanceAddSelectedLanguage(kMACaptionAppearanceDomainUser, language.createCFString().get()); +} + +Vector CaptionUserPreferencesMediaAF::preferredLanguages() const +{ + if (testingMode() || !MediaAccessibilityLibrary()) + return CaptionUserPreferences::preferredLanguages(); + + Vector platformLanguages = platformUserPreferredLanguages(); + Vector override = userPreferredLanguagesOverride(); + if (!override.isEmpty()) { + if (platformLanguages.size() != override.size()) + return override; + for (size_t i = 0; i < override.size(); i++) { + if (override[i] != platformLanguages[i]) + return override; + } + } + + CFIndex languageCount = 0; + RetainPtr languages = adoptCF(MACaptionAppearanceCopySelectedLanguages(kMACaptionAppearanceDomainUser)); + if (languages) + languageCount = CFArrayGetCount(languages.get()); + + if (!languageCount) + return CaptionUserPreferences::preferredLanguages(); + + Vector userPreferredLanguages; + userPreferredLanguages.reserveCapacity(languageCount + platformLanguages.size()); + for (CFIndex i = 0; i < languageCount; i++) + userPreferredLanguages.append(static_cast(CFArrayGetValueAtIndex(languages.get(), i))); + + userPreferredLanguages.appendVector(platformLanguages); + + return userPreferredLanguages; +} + +void CaptionUserPreferencesMediaAF::setPreferredAudioCharacteristic(const String& characteristic) +{ + if (testingMode() || !MediaAccessibilityLibrary()) + CaptionUserPreferences::setPreferredAudioCharacteristic(characteristic); +} + +Vector CaptionUserPreferencesMediaAF::preferredAudioCharacteristics() const +{ + if (testingMode() || !MediaAccessibilityLibrary() || !canLoad_MediaAccessibility_MAAudibleMediaCopyPreferredCharacteristics()) + return CaptionUserPreferences::preferredAudioCharacteristics(); + + CFIndex characteristicCount = 0; + RetainPtr characteristics = adoptCF(MAAudibleMediaCopyPreferredCharacteristics()); + if (characteristics) + characteristicCount = CFArrayGetCount(characteristics.get()); + + if (!characteristicCount) + return CaptionUserPreferences::preferredAudioCharacteristics(); + + Vector userPreferredAudioCharacteristics; + userPreferredAudioCharacteristics.reserveCapacity(characteristicCount); + for (CFIndex i = 0; i < characteristicCount; i++) + userPreferredAudioCharacteristics.append(static_cast(CFArrayGetValueAtIndex(characteristics.get(), i))); + + return userPreferredAudioCharacteristics; +} +#endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + +String CaptionUserPreferencesMediaAF::captionsStyleSheetOverride() const +{ + if (testingMode()) + return CaptionUserPreferences::captionsStyleSheetOverride(); + + StringBuilder captionsOverrideStyleSheet; + +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + if (!MediaAccessibilityLibrary()) + return CaptionUserPreferences::captionsStyleSheetOverride(); + + String captionsColor = captionsTextColorCSS(); + String edgeStyle = captionsTextEdgeCSS(); + String fontName = captionsDefaultFontCSS(); + String background = captionsBackgroundCSS(); + if (!background.isEmpty() || !captionsColor.isEmpty() || !edgeStyle.isEmpty() || !fontName.isEmpty()) { + captionsOverrideStyleSheet.appendLiteral(" video::"); + captionsOverrideStyleSheet.append(TextTrackCue::cueShadowPseudoId()); + captionsOverrideStyleSheet.append('{'); + + if (!background.isEmpty()) + captionsOverrideStyleSheet.append(background); + if (!captionsColor.isEmpty()) + captionsOverrideStyleSheet.append(captionsColor); + if (!edgeStyle.isEmpty()) + captionsOverrideStyleSheet.append(edgeStyle); + if (!fontName.isEmpty()) + captionsOverrideStyleSheet.append(fontName); + + captionsOverrideStyleSheet.append('}'); + } + + String windowColor = captionsWindowCSS(); + String windowCornerRadius = windowRoundedCornerRadiusCSS(); + if (!windowColor.isEmpty() || !windowCornerRadius.isEmpty()) { + captionsOverrideStyleSheet.appendLiteral(" video::"); + captionsOverrideStyleSheet.append(VTTCue::cueBackdropShadowPseudoId()); + captionsOverrideStyleSheet.append('{'); + + if (!windowColor.isEmpty()) + captionsOverrideStyleSheet.append(windowColor); + if (!windowCornerRadius.isEmpty()) { + captionsOverrideStyleSheet.append(windowCornerRadius); + } + + captionsOverrideStyleSheet.append('}'); + } +#endif // HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + + LOG(Media, "CaptionUserPreferencesMediaAF::captionsStyleSheetOverrideSetting sytle to:\n%s", captionsOverrideStyleSheet.toString().utf8().data()); + + return captionsOverrideStyleSheet.toString(); +} + +static String languageIdentifier(const String& languageCode) +{ + if (languageCode.isEmpty()) + return languageCode; + + String lowercaseLanguageCode = languageCode.convertToASCIILowercase(); + + // Need 2U here to disambiguate String::operator[] from operator(NSString*, int)[] in a production build. + if (lowercaseLanguageCode.length() >= 3 && (lowercaseLanguageCode[2U] == '_' || lowercaseLanguageCode[2U] == '-')) + lowercaseLanguageCode.truncate(2); + + return lowercaseLanguageCode; +} + +static void buildDisplayStringForTrackBase(StringBuilder& displayName, const TrackBase& track) +{ + String label = track.label(); + String trackLanguageIdentifier = track.validBCP47Language(); + + RetainPtr currentLocale = adoptCF(CFLocaleCreate(kCFAllocatorDefault, defaultLanguage().createCFString().get())); + RetainPtr localeIdentifier = adoptCF(CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, trackLanguageIdentifier.createCFString().get())); + RetainPtr languageCF = adoptCF(CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleLanguageCode, localeIdentifier.get())); + String language = languageCF.get(); + + if (!label.isEmpty()) { + if (language.isEmpty() || label.contains(language)) + displayName.append(label); + else { + RetainPtr localeDict = adoptCF(CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorDefault, localeIdentifier.get())); + if (localeDict) { + CFStringRef countryCode = 0; + String countryName; + + CFDictionaryGetValueIfPresent(localeDict.get(), kCFLocaleCountryCode, (const void **)&countryCode); + if (countryCode) { + RetainPtr countryNameCF = adoptCF(CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleCountryCode, countryCode)); + countryName = countryNameCF.get(); + } + + if (!countryName.isEmpty()) + displayName.append(textTrackCountryAndLanguageMenuItemText(label, countryName, language)); + else + displayName.append(textTrackLanguageMenuItemText(label, language)); + } + } + } else { + String languageAndLocale = adoptCF(CFLocaleCopyDisplayNameForPropertyValue(currentLocale.get(), kCFLocaleIdentifier, trackLanguageIdentifier.createCFString().get())).get(); + if (!languageAndLocale.isEmpty()) + displayName.append(languageAndLocale); + else if (!language.isEmpty()) + displayName.append(language); + else + displayName.append(localeIdentifier.get()); + } +} + +static String trackDisplayName(AudioTrack* track) +{ + StringBuilder displayName; + buildDisplayStringForTrackBase(displayName, *track); + + if (displayName.isEmpty()) + displayName.append(audioTrackNoLabelText()); + + if (track->kind() != AudioTrack::descriptionKeyword()) + return displayName.toString(); + + return audioDescriptionTrackSuffixText(displayName.toString()); +} + +String CaptionUserPreferencesMediaAF::displayNameForTrack(AudioTrack* track) const +{ + return trackDisplayName(track); +} + +static String trackDisplayName(TextTrack* track) +{ + if (track == TextTrack::captionMenuOffItem()) + return textTrackOffMenuItemText(); + if (track == TextTrack::captionMenuAutomaticItem()) + return textTrackAutomaticMenuItemText(); + + StringBuilder displayNameBuilder; + buildDisplayStringForTrackBase(displayNameBuilder, *track); + + if (displayNameBuilder.isEmpty()) + displayNameBuilder.append(textTrackNoLabelText()); + + String displayName = displayNameBuilder.toString(); + + if (track->isClosedCaptions()) { + displayName = closedCaptionTrackMenuItemText(displayName); + if (track->isEasyToRead()) + displayName = easyReaderTrackMenuItemText(displayName); + + return displayName; + } + + if (track->isSDH()) + displayName = sdhTrackMenuItemText(displayName); + + if (track->containsOnlyForcedSubtitles()) + displayName = forcedTrackMenuItemText(displayName); + + if (track->isEasyToRead()) + displayName = easyReaderTrackMenuItemText(displayName); + + return displayName; +} + +String CaptionUserPreferencesMediaAF::displayNameForTrack(TextTrack* track) const +{ + return trackDisplayName(track); +} + +int CaptionUserPreferencesMediaAF::textTrackSelectionScore(TextTrack* track, HTMLMediaElement* mediaElement) const +{ + CaptionDisplayMode displayMode = captionDisplayMode(); + if (displayMode == Manual) + return 0; + + bool legacyOverride = mediaElement->webkitClosedCaptionsVisible(); + if (displayMode == AlwaysOn && (!userPrefersSubtitles() && !userPrefersCaptions() && !legacyOverride)) + return 0; + if (track->kind() != TextTrack::Kind::Captions && track->kind() != TextTrack::Kind::Subtitles && track->kind() != TextTrack::Kind::Forced) + return 0; + if (!track->isMainProgramContent()) + return 0; + + bool trackHasOnlyForcedSubtitles = track->containsOnlyForcedSubtitles(); + if (!legacyOverride && ((trackHasOnlyForcedSubtitles && displayMode != ForcedOnly) || (!trackHasOnlyForcedSubtitles && displayMode == ForcedOnly))) + return 0; + + Vector userPreferredCaptionLanguages = preferredLanguages(); + + if ((displayMode == Automatic && !legacyOverride) || trackHasOnlyForcedSubtitles) { + + if (!mediaElement || !mediaElement->player()) + return 0; + + String textTrackLanguage = track->validBCP47Language(); + if (textTrackLanguage.isEmpty()) + return 0; + + Vector languageList; + languageList.reserveCapacity(1); + + String audioTrackLanguage; + if (testingMode()) + audioTrackLanguage = primaryAudioTrackLanguageOverride(); + else + audioTrackLanguage = mediaElement->player()->languageOfPrimaryAudioTrack(); + + if (audioTrackLanguage.isEmpty()) + return 0; + + bool exactMatch; + if (trackHasOnlyForcedSubtitles) { + languageList.append(audioTrackLanguage); + size_t offset = indexOfBestMatchingLanguageInList(textTrackLanguage, languageList, exactMatch); + + // Only consider a forced-only track if it IS in the same language as the primary audio track. + if (offset) + return 0; + } else { + languageList.append(defaultLanguage()); + + // Only enable a text track if the current audio track is NOT in the user's preferred language ... + size_t offset = indexOfBestMatchingLanguageInList(audioTrackLanguage, languageList, exactMatch); + if (!offset) + return 0; + + // and the text track matches the user's preferred language. + offset = indexOfBestMatchingLanguageInList(textTrackLanguage, languageList, exactMatch); + if (offset) + return 0; + } + + userPreferredCaptionLanguages = languageList; + } + + int trackScore = 0; + + if (userPrefersCaptions()) { + // When the user prefers accessibility tracks, rank is SDH, then CC, then subtitles. + if (track->kind() == TextTrack::Kind::Subtitles) + trackScore = 1; + else if (track->isClosedCaptions()) + trackScore = 2; + else + trackScore = 3; + } else { + // When the user prefers translation tracks, rank is subtitles, then SDH, then CC tracks. + if (track->kind() == TextTrack::Kind::Subtitles) + trackScore = 3; + else if (!track->isClosedCaptions()) + trackScore = 2; + else + trackScore = 1; + } + + return trackScore + textTrackLanguageSelectionScore(track, userPreferredCaptionLanguages); +} + +static bool textTrackCompare(const RefPtr& a, const RefPtr& b) +{ + String preferredLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(defaultLanguage())); + String aLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(a->validBCP47Language())); + String bLanguageDisplayName = displayNameForLanguageLocale(languageIdentifier(b->language())); + + // Tracks in the user's preferred language are always at the top of the menu. + bool aIsPreferredLanguage = !codePointCompare(aLanguageDisplayName, preferredLanguageDisplayName); + bool bIsPreferredLanguage = !codePointCompare(bLanguageDisplayName, preferredLanguageDisplayName); + if ((aIsPreferredLanguage || bIsPreferredLanguage) && (aIsPreferredLanguage != bIsPreferredLanguage)) + return aIsPreferredLanguage; + + // Tracks not in the user's preferred language sort first by language ... + if (codePointCompare(aLanguageDisplayName, bLanguageDisplayName)) + return codePointCompare(aLanguageDisplayName, bLanguageDisplayName) < 0; + + // ... but when tracks have the same language, main program content sorts next highest ... + bool aIsMainContent = a->isMainProgramContent(); + bool bIsMainContent = b->isMainProgramContent(); + if ((aIsMainContent || bIsMainContent) && (aIsMainContent != bIsMainContent)) + return aIsMainContent; + + // ... and main program trakcs sort higher than CC tracks ... + bool aIsCC = a->isClosedCaptions(); + bool bIsCC = b->isClosedCaptions(); + if ((aIsCC || bIsCC) && (aIsCC != bIsCC)) { + if (aIsCC) + return aIsMainContent; + return bIsMainContent; + } + + // ... and tracks of the same type and language sort by the menu item text. + return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0; +} + +Vector> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(AudioTrackList* trackList) +{ + ASSERT(trackList); + + Vector> tracksForMenu; + + for (unsigned i = 0, length = trackList->length(); i < length; ++i) { + AudioTrack* track = trackList->item(i); + String language = displayNameForLanguageLocale(track->validBCP47Language()); + tracksForMenu.append(track); + } + + std::sort(tracksForMenu.begin(), tracksForMenu.end(), [](auto& a, auto& b) { + return codePointCompare(trackDisplayName(a.get()), trackDisplayName(b.get())) < 0; + }); + + return tracksForMenu; +} + +Vector> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(TextTrackList* trackList) +{ + ASSERT(trackList); + + Vector> tracksForMenu; + HashSet languagesIncluded; + CaptionDisplayMode displayMode = captionDisplayMode(); + bool prefersAccessibilityTracks = userPrefersCaptions(); + bool filterTrackList = shouldFilterTrackMenu(); + + for (unsigned i = 0, length = trackList->length(); i < length; ++i) { + TextTrack* track = trackList->item(i); + String language = displayNameForLanguageLocale(track->validBCP47Language()); + + if (displayMode == Manual) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because selection mode is 'manual'", track->kindKeyword().string().utf8().data(), language.utf8().data()); + tracksForMenu.append(track); + continue; + } + + auto kind = track->kind(); + if (kind != TextTrack::Kind::Captions && kind != TextTrack::Kind::Descriptions && kind != TextTrack::Kind::Subtitles) + continue; + + if (track->containsOnlyForcedSubtitles()) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - skipping '%s' track with language '%s' because it contains only forced subtitles", track->kindKeyword().string().utf8().data(), language.utf8().data()); + continue; + } + + if (track->isEasyToRead()) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because it is 'easy to read'", track->kindKeyword().string().utf8().data(), language.utf8().data()); + if (!language.isEmpty()) + languagesIncluded.add(language); + tracksForMenu.append(track); + continue; + } + + if (track->mode() == TextTrack::Mode::Showing) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because it is already visible", track->kindKeyword().string().utf8().data(), language.utf8().data()); + if (!language.isEmpty()) + languagesIncluded.add(language); + tracksForMenu.append(track); + continue; + } + + if (!language.isEmpty() && track->isMainProgramContent()) { + bool isAccessibilityTrack = track->kind() == TextTrack::Kind::Captions; + if (prefersAccessibilityTracks) { + // In the first pass, include only caption tracks if the user prefers accessibility tracks. + if (!isAccessibilityTrack && filterTrackList) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - skipping '%s' track with language '%s' because it is NOT an accessibility track", track->kindKeyword().string().utf8().data(), language.utf8().data()); + continue; + } + } else { + // In the first pass, only include the first non-CC or SDH track with each language if the user prefers translation tracks. + if (isAccessibilityTrack && filterTrackList) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - skipping '%s' track with language '%s' because it is an accessibility track", track->kindKeyword().string().utf8().data(), language.utf8().data()); + continue; + } + if (languagesIncluded.contains(language) && filterTrackList) { + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - skipping '%s' track with language '%s' because it is not the first with this language", track->kindKeyword().string().utf8().data(), language.utf8().data()); + continue; + } + } + } + + if (!language.isEmpty()) + languagesIncluded.add(language); + tracksForMenu.append(track); + + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s', is%s main program content", track->kindKeyword().string().utf8().data(), language.utf8().data(), track->isMainProgramContent() ? "" : " NOT"); + } + + // Now that we have filtered for the user's accessibility/translation preference, add all tracks with a unique language without regard to track type. + for (unsigned i = 0, length = trackList->length(); i < length; ++i) { + TextTrack* track = trackList->item(i); + String language = displayNameForLanguageLocale(track->language()); + + if (tracksForMenu.contains(track)) + continue; + + auto kind = track->kind(); + if (kind != TextTrack::Kind::Captions && kind != TextTrack::Kind::Descriptions && kind != TextTrack::Kind::Subtitles) + continue; + + // All candidates with no languge were added the first time through. + if (language.isEmpty()) + continue; + + if (track->containsOnlyForcedSubtitles()) + continue; + + if (!languagesIncluded.contains(language) && track->isMainProgramContent()) { + languagesIncluded.add(language); + tracksForMenu.append(track); + LOG(Media, "CaptionUserPreferencesMediaAF::sortedTrackListForMenu - adding '%s' track with language '%s' because it is the only track with this language", track->kindKeyword().string().utf8().data(), language.utf8().data()); + } + } + + if (tracksForMenu.isEmpty()) + return tracksForMenu; + + std::sort(tracksForMenu.begin(), tracksForMenu.end(), textTrackCompare); + + tracksForMenu.insert(0, TextTrack::captionMenuOffItem()); + tracksForMenu.insert(1, TextTrack::captionMenuAutomaticItem()); + + return tracksForMenu; +} + +} + +#endif + +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/page/CaptionUserPreferencesMediaAF.h b/Source/WebCore/page/CaptionUserPreferencesMediaAF.h new file mode 100644 index 000000000..7616f5197 --- /dev/null +++ b/Source/WebCore/page/CaptionUserPreferencesMediaAF.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012-2015 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#pragma once + +#if ENABLE(VIDEO_TRACK) + +#include "CSSPropertyNames.h" +#include "CaptionUserPreferences.h" +#include "Color.h" + +namespace WebCore { + +class CaptionUserPreferencesMediaAF : public CaptionUserPreferences { + WTF_MAKE_FAST_ALLOCATED; +public: + CaptionUserPreferencesMediaAF(PageGroup&); + virtual ~CaptionUserPreferencesMediaAF(); + +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + CaptionDisplayMode captionDisplayMode() const override; + void setCaptionDisplayMode(CaptionDisplayMode) override; + + bool userPrefersCaptions() const override; + bool userPrefersSubtitles() const override; + + float captionFontSizeScaleAndImportance(bool&) const override; + + void setInterestedInCaptionPreferenceChanges() override; + + void setPreferredLanguage(const String&) override; + Vector preferredLanguages() const override; + + void setPreferredAudioCharacteristic(const String&) override; + Vector preferredAudioCharacteristics() const override; + + void captionPreferencesChanged() override; + + bool shouldFilterTrackMenu() const { return true; } +#else + bool shouldFilterTrackMenu() const { return false; } +#endif + + String captionsStyleSheetOverride() const override; + int textTrackSelectionScore(TextTrack*, HTMLMediaElement*) const override; + Vector> sortedTrackListForMenu(AudioTrackList*) override; + Vector> sortedTrackListForMenu(TextTrackList*) override; + String displayNameForTrack(AudioTrack*) const override; + String displayNameForTrack(TextTrack*) const override; + +private: +#if HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) + void updateTimerFired(); + + String captionsWindowCSS() const; + String captionsBackgroundCSS() const; + String captionsTextColorCSS() const; + Color captionsTextColor(bool&) const; + String captionsDefaultFontCSS() const; + Color captionsEdgeColorForTextColor(const Color&) const; + String windowRoundedCornerRadiusCSS() const; + String strokeWidth() const; + String captionsTextEdgeCSS() const; + String cssPropertyWithTextEdgeColor(CSSPropertyID, const String&, const Color&, bool) const; + String colorPropertyCSS(CSSPropertyID, const Color&, bool) const; + Timer m_updateStyleSheetTimer; + + bool m_listeningForPreferenceChanges; + bool m_registeringForNotification { false }; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(VIDEO_TRACK) diff --git a/Source/WebCore/page/Chrome.cpp b/Source/WebCore/page/Chrome.cpp index 85cfd4d65..a5b00ae0b 100644 --- a/Source/WebCore/page/Chrome.cpp +++ b/Source/WebCore/page/Chrome.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2009, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2012, Samsung Electronics. All rights reserved. * @@ -23,14 +23,13 @@ #include "Chrome.h" #include "ChromeClient.h" -#include "DNS.h" -#include "DateTimeChooser.h" #include "Document.h" #include "DocumentType.h" #include "FileIconLoader.h" #include "FileChooser.h" #include "FileList.h" #include "FloatRect.h" +#include "FrameLoaderClient.h" #include "FrameTree.h" #include "Geolocation.h" #include "HTMLFormElement.h" @@ -45,14 +44,12 @@ #include "PopupOpeningObserver.h" #include "RenderObject.h" #include "ResourceHandle.h" -#include "SecurityOrigin.h" #include "Settings.h" #include "StorageNamespace.h" #include "WindowFeatures.h" -#include -#include +#include +#include #include -#include #if ENABLE(INPUT_TYPE_COLOR) #include "ColorChooser.h" @@ -65,10 +62,6 @@ using namespace HTMLNames; Chrome::Chrome(Page& page, ChromeClient& client) : m_page(page) , m_client(client) - , m_displayID(0) -#if PLATFORM(IOS) - , m_isDispatchViewportDataDidChangeSuppressed(false) -#endif { } @@ -77,28 +70,28 @@ Chrome::~Chrome() m_client.chromeDestroyed(); } -void Chrome::invalidateRootView(const IntRect& updateRect, bool immediate) +void Chrome::invalidateRootView(const IntRect& updateRect) { - m_client.invalidateRootView(updateRect, immediate); + m_client.invalidateRootView(updateRect); } -void Chrome::invalidateContentsAndRootView(const IntRect& updateRect, bool immediate) +void Chrome::invalidateContentsAndRootView(const IntRect& updateRect) { - m_client.invalidateContentsAndRootView(updateRect, immediate); + m_client.invalidateContentsAndRootView(updateRect); } -void Chrome::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate) +void Chrome::invalidateContentsForSlowScroll(const IntRect& updateRect) { - m_client.invalidateContentsForSlowScroll(updateRect, immediate); + m_client.invalidateContentsForSlowScroll(updateRect); } void Chrome::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) { m_client.scroll(scrollDelta, rectToScroll, clipRect); - InspectorInstrumentation::didScroll(&m_page); + InspectorInstrumentation::didScroll(m_page); } -#if USE(TILED_BACKING_STORE) +#if USE(COORDINATED_GRAPHICS) void Chrome::delegatedScrollRequested(const IntPoint& scrollPoint) { m_client.delegatedScrollRequested(scrollPoint); @@ -114,20 +107,29 @@ IntRect Chrome::rootViewToScreen(const IntRect& rect) const { return m_client.rootViewToScreen(rect); } + +#if PLATFORM(IOS) -PlatformPageClient Chrome::platformPageClient() const +IntPoint Chrome::accessibilityScreenToRootView(const IntPoint& point) const { - return m_client.platformPageClient(); + return m_client.accessibilityScreenToRootView(point); } -void Chrome::contentsSizeChanged(Frame* frame, const IntSize& size) const +IntRect Chrome::rootViewToAccessibilityScreen(const IntRect& rect) const { - m_client.contentsSizeChanged(frame, size); + return m_client.rootViewToAccessibilityScreen(rect); +} + +#endif + +PlatformPageClient Chrome::platformPageClient() const +{ + return m_client.platformPageClient(); } -void Chrome::layoutUpdated(Frame* frame) const +void Chrome::contentsSizeChanged(Frame& frame, const IntSize& size) const { - m_client.layoutUpdated(frame); + m_client.contentsSizeChanged(frame, size); } void Chrome::scrollRectIntoView(const IntRect& rect) const @@ -185,14 +187,16 @@ void Chrome::focusedFrameChanged(Frame* frame) const m_client.focusedFrameChanged(frame); } -Page* Chrome::createWindow(Frame* frame, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction& action) const +Page* Chrome::createWindow(Frame& frame, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction& action) const { Page* newPage = m_client.createWindow(frame, request, features, action); if (!newPage) - return 0; + return nullptr; - if (StorageNamespace* oldSessionStorage = m_page.sessionStorage(false)) + if (auto* oldSessionStorage = m_page.sessionStorage(false)) newPage->setSessionStorage(oldSessionStorage->copy(newPage)); + if (auto* oldEphemeralLocalStorage = m_page.ephemeralLocalStorage(false)) + newPage->setEphemeralLocalStorage(oldEphemeralLocalStorage->copy(newPage)); return newPage; } @@ -207,30 +211,16 @@ bool Chrome::canRunModal() const return m_client.canRunModal(); } -static bool canRunModalIfDuringPageDismissal(Page& page, ChromeClient::DialogType dialog, const String& message) -{ - for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { - FrameLoader::PageDismissalType dismissal = frame->loader().pageDismissalEventBeingDispatched(); - if (dismissal != FrameLoader::NoDismissal) - return page.chrome().client().shouldRunModalDialogDuringPageDismissal(dialog, message, dismissal); - } - return true; -} - -bool Chrome::canRunModalNow() const -{ - // If loads are blocked, we can't run modal because the contents - // of the modal dialog will never show up! - return canRunModal() && !ResourceHandle::loadsBlocked() - && canRunModalIfDuringPageDismissal(m_page, ChromeClient::HTMLDialog, String()); -} - void Chrome::runModal() const { // Defer callbacks in all the other pages in this group, so we don't try to run JavaScript // in a way that could interact with this view. PageGroupLoadDeferrer deferrer(m_page, false); + // JavaScript that runs within the nested event loop must not be run in the context of the + // script that called showModalDialog. Null out entryScope to break the connection. + SetForScope entryScopeNullifier { m_page.mainFrame().document()->vm().entryScope, nullptr }; + TimerBase::fireTimersInNestedEventLoop(); m_client.runModal(); } @@ -285,16 +275,13 @@ bool Chrome::canRunBeforeUnloadConfirmPanel() return m_client.canRunBeforeUnloadConfirmPanel(); } -bool Chrome::runBeforeUnloadConfirmPanel(const String& message, Frame* frame) +bool Chrome::runBeforeUnloadConfirmPanel(const String& message, Frame& frame) { // Defer loads in case the client method runs a new event loop that would // otherwise cause the load to continue while we're in the middle of executing JavaScript. PageGroupLoadDeferrer deferrer(m_page, true); - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRunJavaScriptDialog(&m_page, message); - bool ok = m_client.runBeforeUnloadConfirmPanel(message, frame); - InspectorInstrumentation::didRunJavaScriptDialog(cookie); - return ok; + return m_client.runBeforeUnloadConfirmPanel(message, frame); } void Chrome::closeWindowSoon() @@ -302,93 +289,56 @@ void Chrome::closeWindowSoon() m_client.closeWindowSoon(); } -void Chrome::runJavaScriptAlert(Frame* frame, const String& message) +void Chrome::runJavaScriptAlert(Frame& frame, const String& message) { - if (!canRunModalIfDuringPageDismissal(m_page, ChromeClient::AlertDialog, message)) - return; - // Defer loads in case the client method runs a new event loop that would // otherwise cause the load to continue while we're in the middle of executing JavaScript. PageGroupLoadDeferrer deferrer(m_page, true); - ASSERT(frame); notifyPopupOpeningObservers(); - String displayMessage = frame->displayStringModifiedByEncoding(message); + String displayMessage = frame.displayStringModifiedByEncoding(message); - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRunJavaScriptDialog(&m_page, displayMessage); m_client.runJavaScriptAlert(frame, displayMessage); - InspectorInstrumentation::didRunJavaScriptDialog(cookie); } -bool Chrome::runJavaScriptConfirm(Frame* frame, const String& message) +bool Chrome::runJavaScriptConfirm(Frame& frame, const String& message) { - if (!canRunModalIfDuringPageDismissal(m_page, ChromeClient::ConfirmDialog, message)) - return false; - // Defer loads in case the client method runs a new event loop that would // otherwise cause the load to continue while we're in the middle of executing JavaScript. PageGroupLoadDeferrer deferrer(m_page, true); - ASSERT(frame); notifyPopupOpeningObservers(); - String displayMessage = frame->displayStringModifiedByEncoding(message); - - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRunJavaScriptDialog(&m_page, displayMessage); - bool ok = m_client.runJavaScriptConfirm(frame, displayMessage); - InspectorInstrumentation::didRunJavaScriptDialog(cookie); - return ok; + return m_client.runJavaScriptConfirm(frame, frame.displayStringModifiedByEncoding(message)); } -bool Chrome::runJavaScriptPrompt(Frame* frame, const String& prompt, const String& defaultValue, String& result) +bool Chrome::runJavaScriptPrompt(Frame& frame, const String& prompt, const String& defaultValue, String& result) { - if (!canRunModalIfDuringPageDismissal(m_page, ChromeClient::PromptDialog, prompt)) - return false; - // Defer loads in case the client method runs a new event loop that would // otherwise cause the load to continue while we're in the middle of executing JavaScript. PageGroupLoadDeferrer deferrer(m_page, true); - ASSERT(frame); notifyPopupOpeningObservers(); - String displayPrompt = frame->displayStringModifiedByEncoding(prompt); - - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRunJavaScriptDialog(&m_page, displayPrompt); - bool ok = m_client.runJavaScriptPrompt(frame, displayPrompt, frame->displayStringModifiedByEncoding(defaultValue), result); - InspectorInstrumentation::didRunJavaScriptDialog(cookie); + String displayPrompt = frame.displayStringModifiedByEncoding(prompt); + bool ok = m_client.runJavaScriptPrompt(frame, displayPrompt, frame.displayStringModifiedByEncoding(defaultValue), result); if (ok) - result = frame->displayStringModifiedByEncoding(result); + result = frame.displayStringModifiedByEncoding(result); return ok; } -void Chrome::setStatusbarText(Frame* frame, const String& status) -{ - ASSERT(frame); - m_client.setStatusbarText(frame->displayStringModifiedByEncoding(status)); -} - -bool Chrome::shouldInterruptJavaScript() -{ - // Defer loads in case the client method runs a new event loop that would - // otherwise cause the load to continue while we're in the middle of executing JavaScript. - PageGroupLoadDeferrer deferrer(m_page, true); - - return m_client.shouldInterruptJavaScript(); -} - -IntRect Chrome::windowResizerRect() const +void Chrome::setStatusbarText(Frame& frame, const String& status) { - return m_client.windowResizerRect(); + m_client.setStatusbarText(frame.displayStringModifiedByEncoding(status)); } void Chrome::mouseDidMoveOverElement(const HitTestResult& result, unsigned modifierFlags) { if (result.innerNode() && result.innerNode()->document().isDNSPrefetchEnabled()) - prefetchDNS(result.absoluteLinkURL().host()); + m_page.mainFrame().loader().client().prefetchDNS(result.absoluteLinkURL().host()); m_client.mouseDidMoveOverElement(result, modifierFlags); - InspectorInstrumentation::mouseDidMoveOverElement(&m_page, result, modifierFlags); + InspectorInstrumentation::mouseDidMoveOverElement(m_page, result, modifierFlags); } void Chrome::setToolTip(const HitTestResult& result) @@ -401,10 +351,10 @@ void Chrome::setToolTip(const HitTestResult& result) if (toolTip.isEmpty() && m_page.settings().showsURLsInToolTips()) { if (Element* element = result.innerNonSharedElement()) { // Get tooltip representing form action, if relevant - if (isHTMLInputElement(element)) { - HTMLInputElement* input = toHTMLInputElement(element); - if (input->isSubmitButton()) { - if (HTMLFormElement* form = input->form()) { + if (is(*element)) { + HTMLInputElement& input = downcast(*element); + if (input.isSubmitButton()) { + if (HTMLFormElement* form = input.form()) { toolTip = form->action(); if (form->renderer()) toolTipDirection = form->renderer()->style().direction(); @@ -434,8 +384,8 @@ void Chrome::setToolTip(const HitTestResult& result) // Lastly, for that allow multiple files, we'll consider a tooltip for the selected filenames if (toolTip.isEmpty()) { if (Element* element = result.innerNonSharedElement()) { - if (isHTMLInputElement(element)) { - toolTip = toHTMLInputElement(element)->defaultToolTip(); + if (is(*element)) { + toolTip = downcast(*element).defaultToolTip(); // FIXME: We should obtain text direction of tooltip from // ChromeClient or platform. As of October 2011, all client @@ -450,9 +400,9 @@ void Chrome::setToolTip(const HitTestResult& result) m_client.setToolTip(toolTip, toolTipDirection); } -void Chrome::print(Frame* frame) +void Chrome::print(Frame& frame) { - // FIXME: This should have PageGroupLoadDeferrer, like runModal() or runJavaScriptAlert(), becasue it's no different from those. + // FIXME: This should have PageGroupLoadDeferrer, like runModal() or runJavaScriptAlert(), because it's no different from those. m_client.print(frame); } @@ -466,40 +416,37 @@ void Chrome::disableSuddenTermination() m_client.disableSuddenTermination(); } -#if ENABLE(DIRECTORY_UPLOAD) -void Chrome::enumerateChosenDirectory(FileChooser* fileChooser) -{ - m_client.enumerateChosenDirectory(fileChooser); -} -#endif - #if ENABLE(INPUT_TYPE_COLOR) -PassOwnPtr Chrome::createColorChooser(ColorChooserClient* client, const Color& initialColor) + +std::unique_ptr Chrome::createColorChooser(ColorChooserClient& client, const Color& initialColor) { notifyPopupOpeningObservers(); return m_client.createColorChooser(client, initialColor); } -#endif -#if ENABLE(DATE_AND_TIME_INPUT_TYPES) && !PLATFORM(IOS) -PassRefPtr Chrome::openDateTimeChooser(DateTimeChooserClient* client, const DateTimeChooserParameters& parameters) -{ - notifyPopupOpeningObservers(); - return m_client.openDateTimeChooser(client, parameters); -} #endif -void Chrome::runOpenPanel(Frame* frame, PassRefPtr fileChooser) +void Chrome::runOpenPanel(Frame& frame, FileChooser& fileChooser) { notifyPopupOpeningObservers(); m_client.runOpenPanel(frame, fileChooser); } -void Chrome::loadIconForFiles(const Vector& filenames, FileIconLoader* loader) +void Chrome::loadIconForFiles(const Vector& filenames, FileIconLoader& loader) { m_client.loadIconForFiles(filenames, loader); } +FloatSize Chrome::screenSize() const +{ + return m_client.screenSize(); +} + +FloatSize Chrome::availableScreenSize() const +{ + return m_client.availableScreenSize(); +} + void Chrome::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) const { #if PLATFORM(IOS) @@ -527,14 +474,12 @@ void Chrome::setCursorHiddenUntilMouseMoves(bool hiddenUntilMouseMoves) #endif } -#if ENABLE(REQUEST_ANIMATION_FRAME) void Chrome::scheduleAnimation() { #if !USE(REQUEST_ANIMATION_FRAME_TIMER) m_client.scheduleAnimation(); #endif } -#endif PlatformDisplayID Chrome::displayID() const { @@ -562,19 +507,6 @@ void ChromeClient::annotatedRegionsChanged() } #endif -void ChromeClient::populateVisitedLinks() -{ -} - -FloatRect ChromeClient::customHighlightRect(Node*, const AtomicString&, const FloatRect&) -{ - return FloatRect(); -} - -void ChromeClient::paintCustomHighlight(Node*, const AtomicString&, const FloatRect&, const FloatRect&, bool, bool) -{ -} - bool ChromeClient::shouldReplaceWithGeneratedFileForUpload(const String&, String&) { return false; @@ -601,13 +533,13 @@ bool Chrome::hasOpenedPopup() const return m_client.hasOpenedPopup(); } -PassRefPtr Chrome::createPopupMenu(PopupMenuClient* client) const +RefPtr Chrome::createPopupMenu(PopupMenuClient& client) const { notifyPopupOpeningObservers(); return m_client.createPopupMenu(client); } -PassRefPtr Chrome::createSearchPopupMenu(PopupMenuClient* client) const +RefPtr Chrome::createSearchPopupMenu(PopupMenuClient& client) const { notifyPopupOpeningObservers(); return m_client.createSearchPopupMenu(client); @@ -618,44 +550,35 @@ bool Chrome::requiresFullscreenForVideoPlayback() return m_client.requiresFullscreenForVideoPlayback(); } -#if PLATFORM(IOS) -// FIXME: Make argument, frame, a reference. -void Chrome::didReceiveDocType(Frame* frame) +void Chrome::didReceiveDocType(Frame& frame) { - ASSERT(frame); - if (!frame->isMainFrame()) - return; - - DocumentType* documentType = frame->document()->doctype(); - if (!documentType) { - // FIXME: We should notify the client when is removed so that - // it can adjust the viewport accordingly. See . +#if !PLATFORM(IOS) + UNUSED_PARAM(frame); +#else + if (!frame.isMainFrame()) return; - } - if (documentType->publicId().contains("xhtml mobile", false)) - m_client.didReceiveMobileDocType(); -} + auto* doctype = frame.document()->doctype(); + m_client.didReceiveMobileDocType(doctype && doctype->publicId().containsIgnoringASCIICase("xhtml mobile")); #endif +} -void Chrome::registerPopupOpeningObserver(PopupOpeningObserver* observer) +void Chrome::registerPopupOpeningObserver(PopupOpeningObserver& observer) { - ASSERT(observer); - m_popupOpeningObservers.append(observer); + m_popupOpeningObservers.append(&observer); } -void Chrome::unregisterPopupOpeningObserver(PopupOpeningObserver* observer) +void Chrome::unregisterPopupOpeningObserver(PopupOpeningObserver& observer) { - size_t index = m_popupOpeningObservers.find(observer); - ASSERT(index != notFound); - m_popupOpeningObservers.remove(index); + bool removed = m_popupOpeningObservers.removeFirst(&observer); + ASSERT_UNUSED(removed, removed); } void Chrome::notifyPopupOpeningObservers() const { const Vector observers(m_popupOpeningObservers); - for (size_t i = 0; i < observers.size(); ++i) - observers[i]->willOpenPopup(); + for (auto& observer : observers) + observer->willOpenPopup(); } } // namespace WebCore diff --git a/Source/WebCore/page/Chrome.h b/Source/WebCore/page/Chrome.h index 71852cdda..b3c4647c1 100644 --- a/Source/WebCore/page/Chrome.h +++ b/Source/WebCore/page/Chrome.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2012, Samsung Electronics. All rights reserved. * @@ -19,8 +19,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef Chrome_h -#define Chrome_h +#pragma once #include "Cursor.h" #include "FocusDirection.h" @@ -28,10 +27,8 @@ #include #include -#if PLATFORM(MAC) -#ifndef __OBJC__ -class NSView; -#endif +#if PLATFORM(COCOA) +OBJC_CLASS NSView; #endif namespace WebCore { @@ -48,6 +45,7 @@ class Element; class Frame; class Geolocation; class HitTestResult; +class IntPoint; class IntRect; class NavigationAction; class Page; @@ -69,34 +67,38 @@ public: ChromeClient& client() { return m_client; } // HostWindow methods. - virtual void invalidateRootView(const IntRect&, bool) override; - virtual void invalidateContentsAndRootView(const IntRect&, bool) override; - virtual void invalidateContentsForSlowScroll(const IntRect&, bool) override; - virtual void scroll(const IntSize&, const IntRect&, const IntRect&) override; -#if USE(TILED_BACKING_STORE) - virtual void delegatedScrollRequested(const IntPoint& scrollPoint) override; + void invalidateRootView(const IntRect&) override; + void invalidateContentsAndRootView(const IntRect&) override; + void invalidateContentsForSlowScroll(const IntRect&) override; + void scroll(const IntSize&, const IntRect&, const IntRect&) override; +#if USE(COORDINATED_GRAPHICS) + void delegatedScrollRequested(const IntPoint& scrollPoint) override; #endif - virtual IntPoint screenToRootView(const IntPoint&) const override; - virtual IntRect rootViewToScreen(const IntRect&) const override; - virtual PlatformPageClient platformPageClient() const override; - virtual void scrollbarsModeDidChange() const override; - virtual void setCursor(const Cursor&) override; - virtual void setCursorHiddenUntilMouseMoves(bool) override; - -#if ENABLE(REQUEST_ANIMATION_FRAME) - virtual void scheduleAnimation() override; + IntPoint screenToRootView(const IntPoint&) const override; + IntRect rootViewToScreen(const IntRect&) const override; +#if PLATFORM(IOS) + IntPoint accessibilityScreenToRootView(const IntPoint&) const override; + IntRect rootViewToAccessibilityScreen(const IntRect&) const override; #endif + PlatformPageClient platformPageClient() const override; + void scrollbarsModeDidChange() const override; + void setCursor(const Cursor&) override; + void setCursorHiddenUntilMouseMoves(bool) override; + + void scheduleAnimation() override; - virtual PlatformDisplayID displayID() const override; - virtual void windowScreenDidChange(PlatformDisplayID) override; + PlatformDisplayID displayID() const override; + void windowScreenDidChange(PlatformDisplayID) override; + + FloatSize screenSize() const override; + FloatSize availableScreenSize() const override; void scrollRectIntoView(const IntRect&) const; - void contentsSizeChanged(Frame*, const IntSize&) const; - void layoutUpdated(Frame*) const; + void contentsSizeChanged(Frame&, const IntSize&) const; - void setWindowRect(const FloatRect&) const; - FloatRect windowRect() const; + WEBCORE_EXPORT void setWindowRect(const FloatRect&) const; + WEBCORE_EXPORT FloatRect windowRect() const; FloatRect pageRect() const; @@ -109,11 +111,10 @@ public: void focusedElementChanged(Element*) const; void focusedFrameChanged(Frame*) const; - Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) const; - void show() const; + WEBCORE_EXPORT Page* createWindow(Frame&, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) const; + WEBCORE_EXPORT void show() const; bool canRunModal() const; - bool canRunModalNow() const; void runModal() const; void setToolbarsVisible(bool) const; @@ -131,77 +132,65 @@ public: void setResizable(bool) const; bool canRunBeforeUnloadConfirmPanel(); - bool runBeforeUnloadConfirmPanel(const String& message, Frame*); + bool runBeforeUnloadConfirmPanel(const String& message, Frame&); void closeWindowSoon(); - void runJavaScriptAlert(Frame*, const String&); - bool runJavaScriptConfirm(Frame*, const String&); - bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result); - void setStatusbarText(Frame*, const String&); - bool shouldInterruptJavaScript(); - - IntRect windowResizerRect() const; + void runJavaScriptAlert(Frame&, const String&); + bool runJavaScriptConfirm(Frame&, const String&); + bool runJavaScriptPrompt(Frame&, const String& message, const String& defaultValue, String& result); + WEBCORE_EXPORT void setStatusbarText(Frame&, const String&); void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags); void setToolTip(const HitTestResult&); - void print(Frame*); + WEBCORE_EXPORT void print(Frame&); - void enableSuddenTermination(); - void disableSuddenTermination(); + WEBCORE_EXPORT void enableSuddenTermination(); + WEBCORE_EXPORT void disableSuddenTermination(); #if ENABLE(INPUT_TYPE_COLOR) - PassOwnPtr createColorChooser(ColorChooserClient*, const Color& initialColor); + std::unique_ptr createColorChooser(ColorChooserClient&, const Color& initialColor); #endif -#if ENABLE(DATE_AND_TIME_INPUT_TYPES) && !PLATFORM(IOS) - PassRefPtr openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) -#endif - - void runOpenPanel(Frame*, PassRefPtr); - void loadIconForFiles(const Vector&, FileIconLoader*); -#if ENABLE(DIRECTORY_UPLOAD) - void enumerateChosenDirectory(FileChooser*); -#endif + void runOpenPanel(Frame&, FileChooser&); + void loadIconForFiles(const Vector&, FileIconLoader&); void dispatchViewportPropertiesDidChange(const ViewportArguments&) const; bool requiresFullscreenForVideoPlayback(); -#if PLATFORM(MAC) - void focusNSView(NSView*); +#if PLATFORM(COCOA) + WEBCORE_EXPORT void focusNSView(NSView*); #endif bool selectItemWritingDirectionIsNatural(); bool selectItemAlignmentFollowsMenuWritingDirection(); bool hasOpenedPopup() const; - PassRefPtr createPopupMenu(PopupMenuClient*) const; - PassRefPtr createSearchPopupMenu(PopupMenuClient*) const; + RefPtr createPopupMenu(PopupMenuClient&) const; + RefPtr createSearchPopupMenu(PopupMenuClient&) const; #if PLATFORM(IOS) // FIXME: Can we come up with a better name for this setter? void setDispatchViewportDataDidChangeSuppressed(bool dispatchViewportDataDidChangeSuppressed) { m_isDispatchViewportDataDidChangeSuppressed = dispatchViewportDataDidChangeSuppressed; } - - void didReceiveDocType(Frame*); #endif - void registerPopupOpeningObserver(PopupOpeningObserver*); - void unregisterPopupOpeningObserver(PopupOpeningObserver*); + void didReceiveDocType(Frame&); + + void registerPopupOpeningObserver(PopupOpeningObserver&); + void unregisterPopupOpeningObserver(PopupOpeningObserver&); private: void notifyPopupOpeningObservers() const; Page& m_page; ChromeClient& m_client; - PlatformDisplayID m_displayID; + PlatformDisplayID m_displayID { 0 }; Vector m_popupOpeningObservers; #if PLATFORM(IOS) - bool m_isDispatchViewportDataDidChangeSuppressed; + bool m_isDispatchViewportDataDidChangeSuppressed { false }; #endif }; -} - -#endif // Chrome_h +} // namespace WebCore diff --git a/Source/WebCore/page/ChromeClient.h b/Source/WebCore/page/ChromeClient.h index d7fe0f88b..1a4298c50 100644 --- a/Source/WebCore/page/ChromeClient.h +++ b/Source/WebCore/page/ChromeClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple, Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple, Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * Copyright (C) 2012 Samsung Electronics. All rights reserved. * @@ -19,17 +19,19 @@ * Boston, MA 02110-1301, USA. */ -#ifndef ChromeClient_h -#define ChromeClient_h +#pragma once #include "AXObjectCache.h" -#include "ConsoleAPITypes.h" -#include "ConsoleTypes.h" #include "Cursor.h" +#include "DatabaseDetails.h" +#include "DisplayRefreshMonitor.h" #include "FocusDirection.h" #include "FrameLoader.h" #include "GraphicsContext.h" +#include "HTMLMediaElementEnums.h" #include "HostWindow.h" +#include "LayerFlushThrottleState.h" +#include "MediaProducer.h" #include "PopupMenu.h" #include "PopupMenuClient.h" #include "RenderEmbeddedObject.h" @@ -37,10 +39,15 @@ #include "ScrollingCoordinator.h" #include "SearchPopupMenu.h" #include "WebCoreKeyboardUIMode.h" +#include #include -#include +#include #include +#if ENABLE(WIRELESS_PLAYBACK_TARGET) +#include "MediaPlaybackTargetContext.h" +#endif + #if PLATFORM(IOS) #include "PlatformLayer.h" #define NSResponder WAKResponder @@ -51,10 +58,6 @@ class WAKResponder; #endif #endif -#if ENABLE(SQL_DATABASE) -#include "DatabaseDetails.h" -#endif - OBJC_CLASS NSResponder; namespace WebCore { @@ -70,12 +73,14 @@ class FileIconLoader; class FloatRect; class Frame; class Geolocation; -class GraphicsContext3D; class GraphicsLayer; class GraphicsLayerFactory; class HTMLInputElement; +class HTMLMediaElement; +class HTMLVideoElement; class HitTestResult; class IntRect; +class MediaSessionMetadata; class NavigationAction; class Node; class Page; @@ -84,13 +89,17 @@ class SecurityOrigin; class ViewportConstraints; class Widget; +#if ENABLE(VIDEO) && USE(GSTREAMER) +class MediaPlayerRequestInstallMissingPluginsCallback; +#endif + struct DateTimeChooserParameters; struct FrameLoadRequest; struct GraphicsDeviceAdapter; struct ViewportArguments; struct WindowFeatures; -class ChromeClient { +class WEBCORE_EXPORT ChromeClient { public: virtual void chromeDestroyed() = 0; @@ -114,7 +123,7 @@ public: // created Page has its show method called. // The FrameLoadRequest parameter is only for ChromeClient to check if the // request could be fulfilled. The ChromeClient should not load the request. - virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) = 0; + virtual Page* createWindow(Frame&, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) = 0; virtual void show() = 0; virtual bool canRunModal() = 0; @@ -135,67 +144,69 @@ public: virtual void setResizable(bool) = 0; virtual void addMessageToConsole(MessageSource, MessageLevel, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceID) = 0; - // FIXME: Remove this MessageType variant once all the clients are updated. - virtual void addMessageToConsole(MessageSource source, MessageType, MessageLevel level, const String& message, unsigned lineNumber, unsigned columnNumber, const String& sourceID) - { - addMessageToConsole(source, level, message, lineNumber, columnNumber, sourceID); - } virtual bool canRunBeforeUnloadConfirmPanel() = 0; - virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame*) = 0; + virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame&) = 0; virtual void closeWindowSoon() = 0; - virtual void runJavaScriptAlert(Frame*, const String&) = 0; - virtual bool runJavaScriptConfirm(Frame*, const String&) = 0; - virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result) = 0; + virtual void runJavaScriptAlert(Frame&, const String&) = 0; + virtual bool runJavaScriptConfirm(Frame&, const String&) = 0; + virtual bool runJavaScriptPrompt(Frame&, const String& message, const String& defaultValue, String& result) = 0; virtual void setStatusbarText(const String&) = 0; - virtual bool shouldInterruptJavaScript() = 0; virtual KeyboardUIMode keyboardUIMode() = 0; - virtual IntRect windowResizerRect() const = 0; - - // Methods used by HostWindow. virtual bool supportsImmediateInvalidation() { return false; } - virtual void invalidateRootView(const IntRect&, bool immediate) = 0; - virtual void invalidateContentsAndRootView(const IntRect&, bool immediate) = 0; - virtual void invalidateContentsForSlowScroll(const IntRect&, bool immediate) = 0; + virtual void invalidateRootView(const IntRect&) = 0; + virtual void invalidateContentsAndRootView(const IntRect&) = 0; + virtual void invalidateContentsForSlowScroll(const IntRect&) = 0; virtual void scroll(const IntSize&, const IntRect&, const IntRect&) = 0; -#if USE(TILED_BACKING_STORE) + +#if USE(COORDINATED_GRAPHICS) virtual void delegatedScrollRequested(const IntPoint&) = 0; #endif + virtual IntPoint screenToRootView(const IntPoint&) const = 0; virtual IntRect rootViewToScreen(const IntRect&) const = 0; + +#if PLATFORM(IOS) + virtual IntPoint accessibilityScreenToRootView(const IntPoint&) const = 0; + virtual IntRect rootViewToAccessibilityScreen(const IntRect&) const = 0; +#endif + virtual PlatformPageClient platformPageClient() const = 0; virtual void scrollbarsModeDidChange() const = 0; + #if ENABLE(CURSOR_SUPPORT) virtual void setCursor(const Cursor&) = 0; virtual void setCursorHiddenUntilMouseMoves(bool) = 0; #endif -#if ENABLE(REQUEST_ANIMATION_FRAME) && !USE(REQUEST_ANIMATION_FRAME_TIMER) + +#if !USE(REQUEST_ANIMATION_FRAME_TIMER) virtual void scheduleAnimation() = 0; #endif - // End methods used by HostWindow. + + virtual FloatSize screenSize() const { return const_cast(*this).windowRect().size(); } + virtual FloatSize availableScreenSize() const { return const_cast(*this).windowRect().size(); } virtual void dispatchViewportPropertiesDidChange(const ViewportArguments&) const { } - virtual void contentsSizeChanged(Frame*, const IntSize&) const = 0; - virtual void layoutUpdated(Frame*) const { } + virtual void contentsSizeChanged(Frame&, const IntSize&) const = 0; virtual void scrollRectIntoView(const IntRect&) const { }; // Currently only Mac has a non empty implementation. virtual bool shouldUnavailablePluginMessageBeButton(RenderEmbeddedObject::PluginUnavailabilityReason) const { return false; } - virtual void unavailablePluginButtonClicked(Element*, RenderEmbeddedObject::PluginUnavailabilityReason) const { } + virtual void unavailablePluginButtonClicked(Element&, RenderEmbeddedObject::PluginUnavailabilityReason) const { } virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) = 0; virtual void setToolTip(const String&, TextDirection) = 0; - virtual void print(Frame*) = 0; + virtual void print(Frame&) = 0; virtual Color underlayColor() const { return Color(); } -#if ENABLE(SQL_DATABASE) - virtual void exceededDatabaseQuota(Frame*, const String& databaseName, DatabaseDetails) = 0; -#endif + virtual void pageExtendedBackgroundColorDidChange(Color) const { } + + virtual void exceededDatabaseQuota(Frame&, const String& databaseName, DatabaseDetails) = 0; // Callback invoked when the application cache fails to save a cache object // because storing it would grow the database file past its defined maximum @@ -212,17 +223,12 @@ public: // storage, in bytes, needed to store the new cache along with all of the // other existing caches for the origin that would not be replaced by // the new cache. - virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t totalSpaceNeeded) = 0; + virtual void reachedApplicationCacheOriginQuota(SecurityOrigin&, int64_t totalSpaceNeeded) = 0; #if ENABLE(DASHBOARD_SUPPORT) virtual void annotatedRegionsChanged(); #endif - virtual void populateVisitedLinks(); - - virtual FloatRect customHighlightRect(Node*, const AtomicString& type, const FloatRect& lineRect); - virtual void paintCustomHighlight(Node*, const AtomicString& type, const FloatRect& boxRect, const FloatRect& lineRect, bool behindText, bool entireLine); - virtual bool shouldReplaceWithGeneratedFileForUpload(const String& path, String& generatedFilename); virtual String generateReplacementFile(const String& path); @@ -230,12 +236,14 @@ public: virtual void didPreventDefaultForEvent() = 0; #endif + virtual Seconds eventThrottlingDelay() { return 0_s; }; + #if PLATFORM(IOS) - virtual void didReceiveMobileDocType() = 0; - virtual void setNeedsScrollNotifications(Frame*, bool) = 0; - virtual void observedContentChange(Frame*) = 0; - virtual void clearContentChangeObservers(Frame*) = 0; - virtual void notifyRevealedSelectionByScrollingFrame(Frame*) = 0; + virtual void didReceiveMobileDocType(bool) = 0; + virtual void setNeedsScrollNotifications(Frame&, bool) = 0; + virtual void observedContentChange(Frame&) = 0; + virtual void clearContentChangeObservers(Frame&) = 0; + virtual void notifyRevealedSelectionByScrollingFrame(Frame&) = 0; enum LayoutType { NormalLayout, Scroll }; virtual void didLayout(LayoutType = NormalLayout) = 0; @@ -252,47 +260,44 @@ public: virtual bool fetchCustomFixedPositionLayoutRect(IntRect&) { return false; } - // FIXME: Use std::unique_ptr instead of OwnPtr. - virtual void updateViewportConstrainedLayers(HashMap>&, HashMap&) { } + virtual void updateViewportConstrainedLayers(HashMap>&, HashMap&) { } virtual void addOrUpdateScrollingLayer(Node*, PlatformLayer* scrollingLayer, PlatformLayer* contentsLayer, const IntSize& scrollSize, bool allowHorizontalScrollbar, bool allowVerticalScrollbar) = 0; virtual void removeScrollingLayer(Node*, PlatformLayer* scrollingLayer, PlatformLayer* contentsLayer) = 0; virtual void webAppOrientationsUpdated() = 0; + virtual void showPlaybackTargetPicker(bool hasVideo) = 0; #endif -#if ENABLE(INPUT_TYPE_COLOR) - virtual PassOwnPtr createColorChooser(ColorChooserClient*, const Color&) = 0; +#if ENABLE(ORIENTATION_EVENTS) + virtual int deviceOrientation() const = 0; #endif -#if ENABLE(DATE_AND_TIME_INPUT_TYPES) && !PLATFORM(IOS) - virtual PassRefPtr openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) = 0; +#if ENABLE(INPUT_TYPE_COLOR) + virtual std::unique_ptr createColorChooser(ColorChooserClient&, const Color&) = 0; #endif - virtual void runOpenPanel(Frame*, PassRefPtr) = 0; + virtual void runOpenPanel(Frame&, FileChooser&) = 0; // Asynchronous request to load an icon for specified filenames. - virtual void loadIconForFiles(const Vector&, FileIconLoader*) = 0; - -#if ENABLE(DIRECTORY_UPLOAD) - // Asychronous request to enumerate all files in a directory chosen by the user. - virtual void enumerateChosenDirectory(FileChooser*) = 0; -#endif - - // Notification that the given form element has changed. This function - // will be called frequently, so handling should be very fast. - virtual void formStateDidChange(const Node*) = 0; + virtual void loadIconForFiles(const Vector&, FileIconLoader&) = 0; - virtual void elementDidFocus(const Node*) { }; - virtual void elementDidBlur(const Node*) { }; + virtual void elementDidFocus(Element&) { } + virtual void elementDidBlur(Element&) { } + virtual void elementDidRefocus(Element&) { } virtual bool shouldPaintEntireContents() const { return false; } + virtual bool hasStablePageScaleFactor() const { return true; } -#if USE(ACCELERATED_COMPOSITING) // Allows ports to customize the type of graphics layers created by this page. - virtual GraphicsLayerFactory* graphicsLayerFactory() const { return 0; } + virtual GraphicsLayerFactory* graphicsLayerFactory() const { return nullptr; } + +#if USE(REQUEST_ANIMATION_FRAME_DISPLAY_MONITOR) + virtual RefPtr createDisplayRefreshMonitor(PlatformDisplayID) const { return nullptr; } +#endif - // Pass 0 as the GraphicsLayer to detatch the root layer. - virtual void attachRootGraphicsLayer(Frame*, GraphicsLayer*) = 0; + // Pass nullptr as the GraphicsLayer to detatch the root layer. + virtual void attachRootGraphicsLayer(Frame&, GraphicsLayer*) = 0; + virtual void attachViewOverlayGraphicsLayer(Frame&, GraphicsLayer*) = 0; // Sets a flag to specify that the next time content is drawn to the window, // the changes appear on the screen in synchrony with updates to GraphicsLayers. virtual void setNeedsOneShotDrawingSynchronization() = 0; @@ -321,32 +326,40 @@ public: // Returns true if layer tree updates are disabled. virtual bool layerTreeStateIsFrozen() const { return false; } -#endif - virtual PassRefPtr createScrollingCoordinator(Page*) const { return nullptr; } + virtual bool adjustLayerFlushThrottling(LayerFlushThrottleState::Flags) { return false; } + + virtual RefPtr createScrollingCoordinator(Page&) const { return nullptr; } #if PLATFORM(WIN) && USE(AVFOUNDATION) - virtual GraphicsDeviceAdapter* graphicsDeviceAdapter() const { return 0; } + virtual GraphicsDeviceAdapter* graphicsDeviceAdapter() const { return nullptr; } #endif - virtual bool supportsFullscreenForNode(const Node*) { return false; } - virtual void enterFullscreenForNode(Node*) { } - virtual void exitFullscreenForNode(Node*) { } + virtual bool supportsVideoFullscreen(HTMLMediaElementEnums::VideoFullscreenMode) { return false; } + +#if ENABLE(VIDEO) + virtual void enterVideoFullscreenForVideoElement(HTMLVideoElement&, HTMLMediaElementEnums::VideoFullscreenMode) { } + virtual void setUpPlaybackControlsManager(HTMLMediaElement&) { } + virtual void clearPlaybackControlsManager() { } +#endif + + virtual void exitVideoFullscreenForVideoElement(HTMLVideoElement&) { } + virtual void exitVideoFullscreenToModeWithoutAnimation(HTMLVideoElement&, HTMLMediaElementEnums::VideoFullscreenMode) { } virtual bool requiresFullscreenForVideoPlayback() { return false; } #if ENABLE(FULLSCREEN_API) - virtual bool supportsFullScreenForElement(const Element*, bool) { return false; } - virtual void enterFullScreenForElement(Element*) { } + virtual bool supportsFullScreenForElement(const Element&, bool) { return false; } + virtual void enterFullScreenForElement(Element&) { } virtual void exitFullScreenForElement(Element*) { } virtual void setRootFullScreenLayer(GraphicsLayer*) { } #endif -#if USE(TILED_BACKING_STORE) +#if USE(COORDINATED_GRAPHICS) virtual IntRect visibleRectForTiledBackingStore() const { return IntRect(); } #endif -#if PLATFORM(MAC) - virtual NSResponder *firstResponder() { return 0; } +#if PLATFORM(COCOA) + virtual NSResponder *firstResponder() { return nullptr; } virtual void makeFirstResponder(NSResponder *) { } // Focuses on the containing view associated with this page. virtual void makeFirstResponder() { } @@ -366,42 +379,29 @@ public: virtual void AXFinishFrameLoad() = 0; #endif -#if ENABLE(TOUCH_EVENTS) - virtual void needTouchEvents(bool) = 0; -#endif - virtual bool selectItemWritingDirectionIsNatural() = 0; virtual bool selectItemAlignmentFollowsMenuWritingDirection() = 0; // Checks if there is an opened popup, called by RenderMenuList::showPopup(). virtual bool hasOpenedPopup() const = 0; - virtual PassRefPtr createPopupMenu(PopupMenuClient*) const = 0; - virtual PassRefPtr createSearchPopupMenu(PopupMenuClient*) const = 0; + virtual RefPtr createPopupMenu(PopupMenuClient&) const = 0; + virtual RefPtr createSearchPopupMenu(PopupMenuClient&) const = 0; - virtual void postAccessibilityNotification(AccessibilityObject*, AXObjectCache::AXNotification) { } + virtual void postAccessibilityNotification(AccessibilityObject&, AXObjectCache::AXNotification) { } virtual void notifyScrollerThumbIsVisibleInRect(const IntRect&) { } - virtual void recommendedScrollbarStyleDidChange(int /*newStyle*/) { } + virtual void recommendedScrollbarStyleDidChange(ScrollbarStyle) { } - enum DialogType { - AlertDialog = 0, - ConfirmDialog = 1, - PromptDialog = 2, - HTMLDialog = 3 - }; - virtual bool shouldRunModalDialogDuringPageDismissal(const DialogType&, const String& dialogMessage, FrameLoader::PageDismissalType) const { UNUSED_PARAM(dialogMessage); return true; } + virtual std::optional preferredScrollbarOverlayStyle() { return ScrollbarOverlayStyleDefault; } - virtual void numWheelEventHandlersChanged(unsigned) = 0; + virtual void wheelEventHandlersChanged(bool hasHandlers) = 0; virtual bool isSVGImageChromeClient() const { return false; } #if ENABLE(POINTER_LOCK) virtual bool requestPointerLock() { return false; } virtual void requestPointerUnlock() { } - virtual bool isPointerLocked() { return false; } #endif - virtual void logDiagnosticMessage(const String& message, const String& description, const String& status) { UNUSED_PARAM(message); UNUSED_PARAM(description); UNUSED_PARAM(status); } - virtual FloatSize minimumWindowSize() const { return FloatSize(100, 100); }; virtual bool isEmptyChromeClient() const { return false; } @@ -414,20 +414,59 @@ public: virtual void didAssociateFormControls(const Vector>&) { }; virtual bool shouldNotifyOnFormChanges() { return false; }; - virtual void didAddHeaderLayer(GraphicsLayer*) { } - virtual void didAddFooterLayer(GraphicsLayer*) { } + virtual void didAddHeaderLayer(GraphicsLayer&) { } + virtual void didAddFooterLayer(GraphicsLayer&) { } + + virtual bool shouldUseTiledBackingForFrameView(const FrameView&) const { return false; } + + virtual void isPlayingMediaDidChange(MediaProducer::MediaStateFlags, uint64_t) { } + virtual void didPlayMediaPreventedFromPlayingWithoutUserGesture() { } + +#if ENABLE(MEDIA_SESSION) + virtual void hasMediaSessionWithActiveMediaElementsDidChange(bool) { } + virtual void mediaSessionMetadataDidChange(const MediaSessionMetadata&) { } + virtual void focusedContentMediaElementDidChange(uint64_t) { } +#endif + +#if ENABLE(SUBTLE_CRYPTO) + virtual bool wrapCryptoKey(const Vector&, Vector&) const { return false; } + virtual bool unwrapCryptoKey(const Vector&, Vector&) const { return false; } +#endif + +#if ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(MAC) + virtual void handleTelephoneNumberClick(const String&, const IntPoint&) { } +#endif + +#if ENABLE(SERVICE_CONTROLS) + virtual void handleSelectionServiceClick(FrameSelection&, const Vector&, const IntPoint&) { } + virtual bool hasRelevantSelectionServices(bool /*isTextOnly*/) const { return false; } +#endif + + virtual bool shouldDispatchFakeMouseMoveEvents() const { return true; } + + virtual void handleAutoFillButtonClick(HTMLInputElement&) { } + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + virtual void addPlaybackTargetPickerClient(uint64_t /*contextId*/) { } + virtual void removePlaybackTargetPickerClient(uint64_t /*contextId*/) { } + virtual void showPlaybackTargetPicker(uint64_t /*contextId*/, const IntPoint&, bool /*isVideo*/) { } + virtual void playbackTargetPickerClientStateDidChange(uint64_t /*contextId*/, MediaProducer::MediaStateFlags) { } + virtual void setMockMediaPlaybackTargetPickerEnabled(bool) { } + virtual void setMockMediaPlaybackTargetPickerState(const String&, MediaPlaybackTargetContext::State) { } +#endif + + virtual void imageOrMediaDocumentSizeChanged(const IntSize&) { } + +#if ENABLE(VIDEO) && USE(GSTREAMER) + virtual void requestInstallMissingMediaPlugins(const String& /*details*/, const String& /*description*/, MediaPlayerRequestInstallMissingPluginsCallback&) { } +#endif - virtual bool shouldUseTiledBackingForFrameView(const FrameView*) const { return false; } + virtual void didInvalidateDocumentMarkerRects() { } - // These methods are used to report pages that are performing - // some task that we consider to be "active", and so the user - // would likely want the page to remain running uninterrupted. - virtual void incrementActivePageCount() { } - virtual void decrementActivePageCount() { } + virtual void reportProcessCPUTime(int64_t, ActivityStateForCPUSampling) { } protected: virtual ~ChromeClient() { } }; -} -#endif // ChromeClient_h +} // namespace WebCore diff --git a/Source/WebCore/page/Console.cpp b/Source/WebCore/page/Console.cpp deleted file mode 100644 index e21b862e9..000000000 --- a/Source/WebCore/page/Console.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" -#include "Console.h" - -#include "Chrome.h" -#include "ChromeClient.h" -#include "ConsoleAPITypes.h" -#include "ConsoleTypes.h" -#include "Document.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameTree.h" -#include "InspectorConsoleInstrumentation.h" -#include "InspectorController.h" -#include "Page.h" -#include "PageConsole.h" -#include "PageGroup.h" -#include "ScriptArguments.h" -#include "ScriptCallStack.h" -#include "ScriptCallStackFactory.h" -#include "ScriptProfile.h" -#include "ScriptProfiler.h" -#include "ScriptableDocumentParser.h" -#include "Settings.h" -#include -#include -#include -#include - -namespace WebCore { - -Console::Console(Frame* frame) - : DOMWindowProperty(frame) -{ -} - -Console::~Console() -{ -} - -static void internalAddMessage(Page* page, MessageType type, MessageLevel level, JSC::ExecState* state, PassRefPtr prpArguments, bool acceptNoArguments = false, bool printTrace = false) -{ - RefPtr arguments = prpArguments; - - if (!page) - return; - - if (!acceptNoArguments && !arguments->argumentCount()) - return; - - size_t stackSize = printTrace ? ScriptCallStack::maxCallStackSizeToCapture : 1; - RefPtr callStack(createScriptCallStack(state, stackSize)); - const ScriptCallFrame& lastCaller = callStack->at(0); - - String message; - bool gotMessage = arguments->getFirstArgumentAsString(message); - InspectorInstrumentation::addMessageToConsole(page, ConsoleAPIMessageSource, type, level, message, state, arguments); - - if (page->settings().privateBrowsingEnabled()) - return; - - if (gotMessage) - page->chrome().client().addMessageToConsole(ConsoleAPIMessageSource, type, level, message, lastCaller.lineNumber(), lastCaller.columnNumber(), lastCaller.sourceURL()); - - if (!page->settings().logsPageMessagesToSystemConsoleEnabled() && !PageConsole::shouldPrintExceptions()) - return; - - PageConsole::printSourceURLAndPosition(lastCaller.sourceURL(), lastCaller.lineNumber()); - - printf(": "); - - PageConsole::printMessageSourceAndLevelPrefix(ConsoleAPIMessageSource, level, printTrace); - - for (size_t i = 0; i < arguments->argumentCount(); ++i) { - String argAsString = arguments->argumentAt(i).toString(arguments->globalState()); - printf(" %s", argAsString.utf8().data()); - } - - printf("\n"); - - if (!printTrace) - return; - - for (size_t i = 0; i < callStack->size(); ++i) { - const ScriptCallFrame& callFrame = callStack->at(i); - - String functionName = String(callFrame.functionName()); - if (functionName.isEmpty()) - functionName = ASCIILiteral("(unknown)"); - - printf("%lu: %s (", static_cast(i), functionName.utf8().data()); - - PageConsole::printSourceURLAndPosition(callFrame.sourceURL(), callFrame.lineNumber()); - - printf(")\n"); - } -} - -void Console::debug(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), LogMessageType, DebugMessageLevel, state, arguments); -} - -void Console::error(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), LogMessageType, ErrorMessageLevel, state, arguments); -} - -void Console::info(JSC::ExecState* state, PassRefPtr arguments) -{ - log(state, arguments); -} - -void Console::log(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), LogMessageType, LogMessageLevel, state, arguments); -} - -void Console::warn(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), LogMessageType, WarningMessageLevel, state, arguments); -} - -void Console::dir(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), DirMessageType, LogMessageLevel, state, arguments); -} - -void Console::dirxml(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), DirXMLMessageType, LogMessageLevel, state, arguments); -} - -void Console::table(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), TableMessageType, LogMessageLevel, state, arguments); -} - -void Console::clear(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), ClearMessageType, LogMessageLevel, state, arguments, true); -} - -void Console::trace(JSC::ExecState* state, PassRefPtr arguments) -{ - internalAddMessage(page(), TraceMessageType, LogMessageLevel, state, arguments, true, true); -} - -void Console::assertCondition(JSC::ExecState* state, PassRefPtr arguments, bool condition) -{ - if (condition) - return; - - internalAddMessage(page(), AssertMessageType, ErrorMessageLevel, state, arguments, true); -} - -void Console::count(JSC::ExecState* state, PassRefPtr arguments) -{ - InspectorInstrumentation::consoleCount(page(), state, arguments); -} - -void Console::profile(JSC::ExecState* state, const String& title) -{ - Page* page = this->page(); - if (!page) - return; - - // FIXME: log a console message when profiling is disabled. - if (!InspectorInstrumentation::profilerEnabled(page)) - return; - - String resolvedTitle = title; - if (title.isNull()) // no title so give it the next user initiated profile title. - resolvedTitle = InspectorInstrumentation::getCurrentUserInitiatedProfileName(page, true); - - ScriptProfiler::start(state, resolvedTitle); - - RefPtr callStack(createScriptCallStack(state, 1)); - const ScriptCallFrame& lastCaller = callStack->at(0); - InspectorInstrumentation::addStartProfilingMessageToConsole(page, resolvedTitle, lastCaller.lineNumber(), lastCaller.columnNumber(), lastCaller.sourceURL()); -} - -void Console::profileEnd(JSC::ExecState* state, const String& title) -{ - Page* page = this->page(); - if (!page) - return; - - if (!InspectorInstrumentation::profilerEnabled(page)) - return; - - RefPtr profile = ScriptProfiler::stop(state, title); - if (!profile) - return; - - m_profiles.append(profile); - RefPtr callStack(createScriptCallStack(state, 1)); - InspectorInstrumentation::addProfile(page, profile, callStack); -} - -void Console::time(const String& title) -{ - InspectorInstrumentation::startConsoleTiming(m_frame, title); -} - -void Console::timeEnd(JSC::ExecState* state, const String& title) -{ - RefPtr callStack(createScriptCallStackForConsole(state)); - InspectorInstrumentation::stopConsoleTiming(m_frame, title, callStack.release()); -} - -void Console::timeStamp(PassRefPtr arguments) -{ - InspectorInstrumentation::consoleTimeStamp(m_frame, arguments); -} - -void Console::group(JSC::ExecState* state, PassRefPtr arguments) -{ - InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, StartGroupMessageType, LogMessageLevel, String(), state, arguments); -} - -void Console::groupCollapsed(JSC::ExecState* state, PassRefPtr arguments) -{ - InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, StartGroupCollapsedMessageType, LogMessageLevel, String(), state, arguments); -} - -void Console::groupEnd() -{ - InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, EndGroupMessageType, LogMessageLevel, String(), String(), 0, 0); -} - -Page* Console::page() const -{ - if (!m_frame) - return 0; - return m_frame->page(); -} - -} // namespace WebCore diff --git a/Source/WebCore/page/Console.h b/Source/WebCore/page/Console.h deleted file mode 100644 index 64eac1ac4..000000000 --- a/Source/WebCore/page/Console.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2007 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -#ifndef Console_h -#define Console_h - -#include "DOMWindowProperty.h" -#include "ScriptProfile.h" -#include "ScriptState.h" -#include "ScriptWrappable.h" -#include -#include -#include - -namespace WebCore { - -class Frame; -class Page; -class ScriptArguments; - -typedef Vector> ProfilesArray; - -class Console : public ScriptWrappable, public RefCounted, public DOMWindowProperty { -public: - static PassRefPtr create(Frame* frame) { return adoptRef(new Console(frame)); } - virtual ~Console(); - - void debug(JSC::ExecState*, PassRefPtr); - void error(JSC::ExecState*, PassRefPtr); - void info(JSC::ExecState*, PassRefPtr); - void log(JSC::ExecState*, PassRefPtr); - void clear(JSC::ExecState*, PassRefPtr); - void warn(JSC::ExecState*, PassRefPtr); - void dir(JSC::ExecState*, PassRefPtr); - void dirxml(JSC::ExecState*, PassRefPtr); - void table(JSC::ExecState*, PassRefPtr); - void trace(JSC::ExecState*, PassRefPtr); - void assertCondition(JSC::ExecState*, PassRefPtr, bool condition); - void count(JSC::ExecState*, PassRefPtr); - const ProfilesArray& profiles() const { return m_profiles; } - void profile(JSC::ExecState*, const String& = String()); - void profileEnd(JSC::ExecState*, const String& = String()); - void time(const String&); - void timeEnd(JSC::ExecState*, const String&); - void timeStamp(PassRefPtr); - void group(JSC::ExecState*, PassRefPtr); - void groupCollapsed(JSC::ExecState*, PassRefPtr); - void groupEnd(); - -private: - inline Page* page() const; - - explicit Console(Frame*); - - ProfilesArray m_profiles; -}; - -} // namespace WebCore - -#endif // Console_h diff --git a/Source/WebCore/page/Console.idl b/Source/WebCore/page/Console.idl deleted file mode 100644 index 90ad10a50..000000000 --- a/Source/WebCore/page/Console.idl +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple 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: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. - */ - -[ - NoInterfaceObject, - GenerateIsReachable=ImplFrame, -] interface Console { - - [CallWith=ScriptArguments&ScriptState] void debug(); - [CallWith=ScriptArguments&ScriptState] void error(); - [CallWith=ScriptArguments&ScriptState] void info(); - [CallWith=ScriptArguments&ScriptState] void log(); - [CallWith=ScriptArguments&ScriptState] void warn(); - [CallWith=ScriptArguments&ScriptState] void dir(); - [CallWith=ScriptArguments&ScriptState] void dirxml(); - [CallWith=ScriptArguments&ScriptState] void table(); - [CallWith=ScriptArguments&ScriptState] void trace(); - [CallWith=ScriptArguments&ScriptState, ImplementedAs=assertCondition] void assert(boolean condition); - [CallWith=ScriptArguments&ScriptState] void count(); - - // As per spec: http://www.w3.org/TR/WebIDL/#idl-sequence - // "Sequences must not be used as the type of an attribute, constant or exception field." - // FIXME: this will lead to BUG console.profiles !== console.profiles as profile will always returns new array. - readonly attribute ScriptProfile[] profiles; - [CallWith=ScriptState] void profile([TreatNullAs=NullString, TreatUndefinedAs=NullString] optional DOMString title); - [CallWith=ScriptState] void profileEnd([TreatNullAs=NullString, TreatUndefinedAs=NullString] optional DOMString title); - - void time([TreatNullAs=NullString, TreatUndefinedAs=NullString] DOMString title); - [CallWith=ScriptState] void timeEnd([TreatNullAs=NullString, TreatUndefinedAs=NullString] DOMString title); - [CallWith=ScriptArguments] void timeStamp(); - [CallWith=ScriptArguments&ScriptState] void group(); - [CallWith=ScriptArguments&ScriptState] void groupCollapsed(); - void groupEnd(); - [CallWith=ScriptArguments&ScriptState] void clear(); -}; - diff --git a/Source/WebCore/page/ConsoleTypes.h b/Source/WebCore/page/ConsoleTypes.h deleted file mode 100644 index 804a167cc..000000000 --- a/Source/WebCore/page/ConsoleTypes.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2011 Apple 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. - */ - -#ifndef ConsoleTypes_h -#define ConsoleTypes_h - -namespace WebCore { - -enum MessageSource { - XMLMessageSource, - JSMessageSource, - NetworkMessageSource, - ConsoleAPIMessageSource, - StorageMessageSource, - AppCacheMessageSource, - RenderingMessageSource, - CSSMessageSource, - SecurityMessageSource, - OtherMessageSource, -}; - -enum MessageLevel { - DebugMessageLevel = 4, - LogMessageLevel = 1, - WarningMessageLevel = 2, - ErrorMessageLevel = 3 -}; - -} // namespace WebCore - -#endif // ConsoleTypes_h diff --git a/Source/WebCore/page/ContentSecurityPolicy.cpp b/Source/WebCore/page/ContentSecurityPolicy.cpp deleted file mode 100644 index 0aea9aad2..000000000 --- a/Source/WebCore/page/ContentSecurityPolicy.cpp +++ /dev/null @@ -1,1919 +0,0 @@ -/* - * Copyright (C) 2011 Google, Inc. All rights reserved. - * Copyright (C) 2013 Apple 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``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 APPLE COMPUTER, INC. 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 "config.h" -#include "ContentSecurityPolicy.h" - -#include "Console.h" -#include "DOMStringList.h" -#include "Document.h" -#include "FeatureObserver.h" -#include "FormData.h" -#include "FormDataList.h" -#include "Frame.h" -#include "InspectorInstrumentation.h" -#include "URL.h" -#include "PingLoader.h" -#include "RuntimeEnabledFeatures.h" -#include "SchemeRegistry.h" -#include "ScriptCallStack.h" -#include "ScriptCallStackFactory.h" -#include "ScriptState.h" -#include "SecurityOrigin.h" -#include "SecurityPolicyViolationEvent.h" -#include "TextEncoding.h" -#include -#include -#include -#include - -using namespace Inspector; - -namespace WebCore { - -// Normally WebKit uses "static" for internal linkage, but using "static" for -// these functions causes a compile error because these functions are used as -// template parameters. -namespace { - -bool isDirectiveNameCharacter(UChar c) -{ - return isASCIIAlphanumeric(c) || c == '-'; -} - -bool isDirectiveValueCharacter(UChar c) -{ - return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR -} - -bool isNonceCharacter(UChar c) -{ - return (c >= 0x21 && c <= 0x7e) && c != ',' && c != ';'; // VCHAR - ',' - ';' -} - -bool isSourceCharacter(UChar c) -{ - return !isASCIISpace(c); -} - -bool isPathComponentCharacter(UChar c) -{ - return c != '?' && c != '#'; -} - -bool isHostCharacter(UChar c) -{ - return isASCIIAlphanumeric(c) || c == '-'; -} - -bool isSchemeContinuationCharacter(UChar c) -{ - return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.'; -} - -bool isNotASCIISpace(UChar c) -{ - return !isASCIISpace(c); -} - -bool isNotColonOrSlash(UChar c) -{ - return c != ':' && c != '/'; -} - -bool isMediaTypeCharacter(UChar c) -{ - return !isASCIISpace(c) && c != '/'; -} - -// CSP 1.0 Directives -static const char connectSrc[] = "connect-src"; -static const char defaultSrc[] = "default-src"; -static const char fontSrc[] = "font-src"; -static const char frameSrc[] = "frame-src"; -static const char imgSrc[] = "img-src"; -static const char mediaSrc[] = "media-src"; -static const char objectSrc[] = "object-src"; -static const char reportURI[] = "report-uri"; -static const char sandbox[] = "sandbox"; -static const char scriptSrc[] = "script-src"; -static const char styleSrc[] = "style-src"; - -// CSP 1.1 Directives -static const char baseURI[] = "base-uri"; -static const char formAction[] = "form-action"; -static const char pluginTypes[] = "plugin-types"; -static const char scriptNonce[] = "script-nonce"; -#if ENABLE(CSP_NEXT) -static const char reflectedXSS[] = "reflected-xss"; -#endif - -bool isDirectiveName(const String& name) -{ - return (equalIgnoringCase(name, connectSrc) - || equalIgnoringCase(name, defaultSrc) - || equalIgnoringCase(name, fontSrc) - || equalIgnoringCase(name, frameSrc) - || equalIgnoringCase(name, imgSrc) - || equalIgnoringCase(name, mediaSrc) - || equalIgnoringCase(name, objectSrc) - || equalIgnoringCase(name, reportURI) - || equalIgnoringCase(name, sandbox) - || equalIgnoringCase(name, scriptSrc) - || equalIgnoringCase(name, styleSrc) -#if ENABLE(CSP_NEXT) - || equalIgnoringCase(name, baseURI) - || equalIgnoringCase(name, formAction) - || equalIgnoringCase(name, pluginTypes) - || equalIgnoringCase(name, scriptNonce) - || equalIgnoringCase(name, reflectedXSS) -#endif - ); -} - -FeatureObserver::Feature getFeatureObserverType(ContentSecurityPolicy::HeaderType type) -{ - switch (type) { - case ContentSecurityPolicy::PrefixedEnforce: - return FeatureObserver::PrefixedContentSecurityPolicy; - case ContentSecurityPolicy::Enforce: - return FeatureObserver::ContentSecurityPolicy; - case ContentSecurityPolicy::PrefixedReport: - return FeatureObserver::PrefixedContentSecurityPolicyReportOnly; - case ContentSecurityPolicy::Report: - return FeatureObserver::ContentSecurityPolicyReportOnly; - } - ASSERT_NOT_REACHED(); - return FeatureObserver::NumberOfFeatures; -} - -const ScriptCallFrame& getFirstNonNativeFrame(PassRefPtr stack) -{ - int frameNumber = 0; - if (!stack->at(0).lineNumber() && stack->size() > 1 && stack->at(1).lineNumber()) - frameNumber = 1; - - return stack->at(frameNumber); -} - -} // namespace - -static bool skipExactly(const UChar*& position, const UChar* end, UChar delimiter) -{ - if (position < end && *position == delimiter) { - ++position; - return true; - } - return false; -} - -template -static bool skipExactly(const UChar*& position, const UChar* end) -{ - if (position < end && characterPredicate(*position)) { - ++position; - return true; - } - return false; -} - -static void skipUntil(const UChar*& position, const UChar* end, UChar delimiter) -{ - while (position < end && *position != delimiter) - ++position; -} - -template -static void skipWhile(const UChar*& position, const UChar* end) -{ - while (position < end && characterPredicate(*position)) - ++position; -} - -static bool isSourceListNone(const String& value) -{ - const UChar* begin = value.deprecatedCharacters(); - const UChar* end = value.deprecatedCharacters() + value.length(); - skipWhile(begin, end); - - const UChar* position = begin; - skipWhile(position, end); - if (!equalIgnoringCase("'none'", begin, position - begin)) - return false; - - skipWhile(position, end); - if (position != end) - return false; - - return true; -} - -class CSPSource { -public: - CSPSource(ContentSecurityPolicy* policy, const String& scheme, const String& host, int port, const String& path, bool hostHasWildcard, bool portHasWildcard) - : m_policy(policy) - , m_scheme(scheme) - , m_host(host) - , m_port(port) - , m_path(path) - , m_hostHasWildcard(hostHasWildcard) - , m_portHasWildcard(portHasWildcard) - { - } - - bool matches(const URL& url) const - { - if (!schemeMatches(url)) - return false; - if (isSchemeOnly()) - return true; - return hostMatches(url) && portMatches(url) && pathMatches(url); - } - -private: - bool schemeMatches(const URL& url) const - { - if (m_scheme.isEmpty()) { - String protectedResourceScheme(m_policy->securityOrigin()->protocol()); -#if ENABLE(CSP_NEXT) - if (equalIgnoringCase("http", protectedResourceScheme)) - return url.protocolIsInHTTPFamily(); -#endif - return equalIgnoringCase(url.protocol(), protectedResourceScheme); - } - return equalIgnoringCase(url.protocol(), m_scheme); - } - - bool hostMatches(const URL& url) const - { - const String& host = url.host(); - if (equalIgnoringCase(host, m_host)) - return true; - return m_hostHasWildcard && host.endsWith("." + m_host, false); - - } - - bool pathMatches(const URL& url) const - { - if (m_path.isEmpty()) - return true; - - String path = decodeURLEscapeSequences(url.path()); - - if (m_path.endsWith("/")) - return path.startsWith(m_path, false); - - return path == m_path; - } - - bool portMatches(const URL& url) const - { - if (m_portHasWildcard) - return true; - - int port = url.port(); - - if (port == m_port) - return true; - - if (!port) - return isDefaultPortForProtocol(m_port, url.protocol()); - - if (!m_port) - return isDefaultPortForProtocol(port, url.protocol()); - - return false; - } - - bool isSchemeOnly() const { return m_host.isEmpty(); } - - ContentSecurityPolicy* m_policy; - String m_scheme; - String m_host; - int m_port; - String m_path; - - bool m_hostHasWildcard; - bool m_portHasWildcard; -}; - -class CSPSourceList { -public: - CSPSourceList(ContentSecurityPolicy*, const String& directiveName); - - void parse(const String&); - bool matches(const URL&); - bool allowInline() const { return m_allowInline; } - bool allowEval() const { return m_allowEval; } - -private: - void parse(const UChar* begin, const UChar* end); - - bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, int& port, String& path, bool& hostHasWildcard, bool& portHasWildcard); - bool parseScheme(const UChar* begin, const UChar* end, String& scheme); - bool parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard); - bool parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard); - bool parsePath(const UChar* begin, const UChar* end, String& path); - - void addSourceSelf(); - void addSourceStar(); - void addSourceUnsafeInline(); - void addSourceUnsafeEval(); - - ContentSecurityPolicy* m_policy; - Vector m_list; - String m_directiveName; - bool m_allowStar; - bool m_allowInline; - bool m_allowEval; -}; - -CSPSourceList::CSPSourceList(ContentSecurityPolicy* policy, const String& directiveName) - : m_policy(policy) - , m_directiveName(directiveName) - , m_allowStar(false) - , m_allowInline(false) - , m_allowEval(false) -{ -} - -void CSPSourceList::parse(const String& value) -{ - // We represent 'none' as an empty m_list. - if (isSourceListNone(value)) - return; - parse(value.deprecatedCharacters(), value.deprecatedCharacters() + value.length()); -} - -bool CSPSourceList::matches(const URL& url) -{ - if (m_allowStar) - return true; - - URL effectiveURL = SecurityOrigin::shouldUseInnerURL(url) ? SecurityOrigin::extractInnerURL(url) : url; - - for (size_t i = 0; i < m_list.size(); ++i) { - if (m_list[i].matches(effectiveURL)) - return true; - } - - return false; -} - -// source-list = *WSP [ source *( 1*WSP source ) *WSP ] -// / *WSP "'none'" *WSP -// -void CSPSourceList::parse(const UChar* begin, const UChar* end) -{ - const UChar* position = begin; - - while (position < end) { - skipWhile(position, end); - if (position == end) - return; - - const UChar* beginSource = position; - skipWhile(position, end); - - String scheme, host, path; - int port = 0; - bool hostHasWildcard = false; - bool portHasWildcard = false; - - if (parseSource(beginSource, position, scheme, host, port, path, hostHasWildcard, portHasWildcard)) { - // Wildcard hosts and keyword sources ('self', 'unsafe-inline', - // etc.) aren't stored in m_list, but as attributes on the source - // list itself. - if (scheme.isEmpty() && host.isEmpty()) - continue; - if (isDirectiveName(host)) - m_policy->reportDirectiveAsSourceExpression(m_directiveName, host); - m_list.append(CSPSource(m_policy, scheme, host, port, path, hostHasWildcard, portHasWildcard)); - } else - m_policy->reportInvalidSourceExpression(m_directiveName, String(beginSource, position - beginSource)); - - ASSERT(position == end || isASCIISpace(*position)); - } -} - -// source = scheme ":" -// / ( [ scheme "://" ] host [ port ] [ path ] ) -// / "'self'" -// -bool CSPSourceList::parseSource(const UChar* begin, const UChar* end, - String& scheme, String& host, int& port, String& path, - bool& hostHasWildcard, bool& portHasWildcard) -{ - if (begin == end) - return false; - - if (equalIgnoringCase("'none'", begin, end - begin)) - return false; - - if (end - begin == 1 && *begin == '*') { - addSourceStar(); - return true; - } - - if (equalIgnoringCase("'self'", begin, end - begin)) { - addSourceSelf(); - return true; - } - - if (equalIgnoringCase("'unsafe-inline'", begin, end - begin)) { - addSourceUnsafeInline(); - return true; - } - - if (equalIgnoringCase("'unsafe-eval'", begin, end - begin)) { - addSourceUnsafeEval(); - return true; - } - - const UChar* position = begin; - const UChar* beginHost = begin; - const UChar* beginPath = end; - const UChar* beginPort = 0; - - skipWhile(position, end); - - if (position == end) { - // host - // ^ - return parseHost(beginHost, position, host, hostHasWildcard); - } - - if (position < end && *position == '/') { - // host/path || host/ || / - // ^ ^ ^ - if (!parseHost(beginHost, position, host, hostHasWildcard) - || !parsePath(position, end, path) - || position != end) - return false; - return true; - } - - if (position < end && *position == ':') { - if (end - position == 1) { - // scheme: - // ^ - return parseScheme(begin, position, scheme); - } - - if (position[1] == '/') { - // scheme://host || scheme:// - // ^ ^ - if (!parseScheme(begin, position, scheme) - || !skipExactly(position, end, ':') - || !skipExactly(position, end, '/') - || !skipExactly(position, end, '/')) - return false; - if (position == end) - return true; - beginHost = position; - skipWhile(position, end); - } - - if (position < end && *position == ':') { - // host:port || scheme://host:port - // ^ ^ - beginPort = position; - skipUntil(position, end, '/'); - } - } - - if (position < end && *position == '/') { - // scheme://host/path || scheme://host:port/path - // ^ ^ - if (position == beginHost) - return false; - - beginPath = position; - } - - if (!parseHost(beginHost, beginPort ? beginPort : beginPath, host, hostHasWildcard)) - return false; - - if (beginPort) { - if (!parsePort(beginPort, beginPath, port, portHasWildcard)) - return false; - } else { - port = 0; - } - - if (beginPath != end) { - if (!parsePath(beginPath, end, path)) - return false; - } - - return true; -} - -// ; production from RFC 3986 -// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) -// -bool CSPSourceList::parseScheme(const UChar* begin, const UChar* end, String& scheme) -{ - ASSERT(begin <= end); - ASSERT(scheme.isEmpty()); - - if (begin == end) - return false; - - const UChar* position = begin; - - if (!skipExactly(position, end)) - return false; - - skipWhile(position, end); - - if (position != end) - return false; - - scheme = String(begin, end - begin); - return true; -} - -// host = [ "*." ] 1*host-char *( "." 1*host-char ) -// / "*" -// host-char = ALPHA / DIGIT / "-" -// -bool CSPSourceList::parseHost(const UChar* begin, const UChar* end, String& host, bool& hostHasWildcard) -{ - ASSERT(begin <= end); - ASSERT(host.isEmpty()); - ASSERT(!hostHasWildcard); - - if (begin == end) - return false; - - const UChar* position = begin; - - if (skipExactly(position, end, '*')) { - hostHasWildcard = true; - - if (position == end) - return true; - - if (!skipExactly(position, end, '.')) - return false; - } - - const UChar* hostBegin = position; - - while (position < end) { - if (!skipExactly(position, end)) - return false; - - skipWhile(position, end); - - if (position < end && !skipExactly(position, end, '.')) - return false; - } - - ASSERT(position == end); - host = String(hostBegin, end - hostBegin); - return true; -} - -bool CSPSourceList::parsePath(const UChar* begin, const UChar* end, String& path) -{ - ASSERT(begin <= end); - ASSERT(path.isEmpty()); - - const UChar* position = begin; - skipWhile(position, end); - // path/to/file.js?query=string || path/to/file.js#anchor - // ^ ^ - if (position < end) - m_policy->reportInvalidPathCharacter(m_directiveName, String(begin, end - begin), *position); - - path = decodeURLEscapeSequences(String(begin, position - begin)); - - ASSERT(position <= end); - ASSERT(position == end || (*position == '#' || *position == '?')); - return true; -} - -// port = ":" ( 1*DIGIT / "*" ) -// -bool CSPSourceList::parsePort(const UChar* begin, const UChar* end, int& port, bool& portHasWildcard) -{ - ASSERT(begin <= end); - ASSERT(!port); - ASSERT(!portHasWildcard); - - if (!skipExactly(begin, end, ':')) - ASSERT_NOT_REACHED(); - - if (begin == end) - return false; - - if (end - begin == 1 && *begin == '*') { - port = 0; - portHasWildcard = true; - return true; - } - - const UChar* position = begin; - skipWhile(position, end); - - if (position != end) - return false; - - bool ok; - port = charactersToIntStrict(begin, end - begin, &ok); - return ok; -} - -void CSPSourceList::addSourceSelf() -{ - m_list.append(CSPSource(m_policy, m_policy->securityOrigin()->protocol(), m_policy->securityOrigin()->host(), m_policy->securityOrigin()->port(), String(), false, false)); -} - -void CSPSourceList::addSourceStar() -{ - m_allowStar = true; -} - -void CSPSourceList::addSourceUnsafeInline() -{ - m_allowInline = true; -} - -void CSPSourceList::addSourceUnsafeEval() -{ - m_allowEval = true; -} - -class CSPDirective { -public: - CSPDirective(const String& name, const String& value, ContentSecurityPolicy* policy) - : m_name(name) - , m_text(name + ' ' + value) - , m_policy(policy) - { - } - - const String& text() const { return m_text; } - -protected: - const ContentSecurityPolicy* policy() const { return m_policy; } - -private: - String m_name; - String m_text; - ContentSecurityPolicy* m_policy; -}; - -class NonceDirective : public CSPDirective { -public: - NonceDirective(const String& name, const String& value, ContentSecurityPolicy* policy) - : CSPDirective(name, value, policy) - { - parse(value); - } - - bool allows(const String& nonce) const - { - return (!m_scriptNonce.isEmpty() && nonce.stripWhiteSpace() == m_scriptNonce); - } - -private: - void parse(const String& value) - { - String nonce; - const UChar* position = value.deprecatedCharacters(); - const UChar* end = position + value.length(); - - skipWhile(position, end); - const UChar* nonceBegin = position; - if (position == end) { - policy()->reportInvalidNonce(String()); - m_scriptNonce = ""; - return; - } - skipWhile(position, end); - if (nonceBegin < position) - nonce = String(nonceBegin, position - nonceBegin); - - // Trim off trailing whitespace: If we're not at the end of the string, log - // an error. - skipWhile(position, end); - if (position < end) { - policy()->reportInvalidNonce(value); - m_scriptNonce = ""; - } else - m_scriptNonce = nonce; - } - - String m_scriptNonce; -}; - -class MediaListDirective : public CSPDirective { -public: - MediaListDirective(const String& name, const String& value, ContentSecurityPolicy* policy) - : CSPDirective(name, value, policy) - { - parse(value); - } - - bool allows(const String& type) - { - return m_pluginTypes.contains(type); - } - -private: - void parse(const String& value) - { - const UChar* begin = value.deprecatedCharacters(); - const UChar* position = begin; - const UChar* end = begin + value.length(); - - // 'plugin-types ____;' OR 'plugin-types;' - if (value.isEmpty()) { - policy()->reportInvalidPluginTypes(value); - return; - } - - while (position < end) { - // _____ OR _____mime1/mime1 - // ^ ^ - skipWhile(position, end); - if (position == end) - return; - - // mime1/mime1 mime2/mime2 - // ^ - begin = position; - if (!skipExactly(position, end)) { - skipWhile(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - skipWhile(position, end); - - // mime1/mime1 mime2/mime2 - // ^ - if (!skipExactly(position, end, '/')) { - skipWhile(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - - // mime1/mime1 mime2/mime2 - // ^ - if (!skipExactly(position, end)) { - skipWhile(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - skipWhile(position, end); - - // mime1/mime1 mime2/mime2 OR mime1/mime1 OR mime1/mime1/error - // ^ ^ ^ - if (position < end && isNotASCIISpace(*position)) { - skipWhile(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - m_pluginTypes.add(String(begin, position - begin)); - - ASSERT(position == end || isASCIISpace(*position)); - } - } - - HashSet m_pluginTypes; -}; - -class SourceListDirective : public CSPDirective { -public: - SourceListDirective(const String& name, const String& value, ContentSecurityPolicy* policy) - : CSPDirective(name, value, policy) - , m_sourceList(policy, name) - { - m_sourceList.parse(value); - } - - bool allows(const URL& url) - { - return m_sourceList.matches(url.isEmpty() ? policy()->url() : url); - } - - bool allowInline() const { return m_sourceList.allowInline(); } - bool allowEval() const { return m_sourceList.allowEval(); } - -private: - CSPSourceList m_sourceList; -}; - -class CSPDirectiveList { - WTF_MAKE_FAST_ALLOCATED; -public: - static PassOwnPtr create(ContentSecurityPolicy*, const String&, ContentSecurityPolicy::HeaderType); - - const String& header() const { return m_header; } - ContentSecurityPolicy::HeaderType headerType() const { return m_headerType; } - - bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; - bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; - bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; - bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus) const; - bool allowEval(JSC::ExecState*, ContentSecurityPolicy::ReportingStatus) const; - bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL&) const; - bool allowPluginType(const String& type, const String& typeAttribute, const URL&, ContentSecurityPolicy::ReportingStatus) const; - - bool allowScriptFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowObjectFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowChildFrameFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowImageFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowStyleFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowFontFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowMediaFromSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowConnectToSource(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowFormAction(const URL&, ContentSecurityPolicy::ReportingStatus) const; - bool allowBaseURI(const URL&, ContentSecurityPolicy::ReportingStatus) const; - - void gatherReportURIs(DOMStringList&) const; - const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; } - ContentSecurityPolicy::ReflectedXSSDisposition reflectedXSSDisposition() const { return m_reflectedXSSDisposition; } - bool isReportOnly() const { return m_reportOnly; } - const Vector& reportURIs() const { return m_reportURIs; } - -private: - CSPDirectiveList(ContentSecurityPolicy*, ContentSecurityPolicy::HeaderType); - - void parse(const String&); - - bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value); - void parseReportURI(const String& name, const String& value); - void parseScriptNonce(const String& name, const String& value); - void parsePluginTypes(const String& name, const String& value); - void parseReflectedXSS(const String& name, const String& value); - void addDirective(const String& name, const String& value); - void applySandboxPolicy(const String& name, const String& sandboxPolicy); - - template - void setCSPDirective(const String& name, const String& value, OwnPtr&); - - SourceListDirective* operativeDirective(SourceListDirective*) const; - void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL = URL(), const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = 0) const; - - bool checkEval(SourceListDirective*) const; - bool checkInline(SourceListDirective*) const; - bool checkNonce(NonceDirective*, const String&) const; - bool checkSource(SourceListDirective*, const URL&) const; - bool checkMediaType(MediaListDirective*, const String& type, const String& typeAttribute) const; - - void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; } - - bool checkEvalAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = 0) const; - bool checkInlineAndReportViolation(SourceListDirective*, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const; - bool checkNonceAndReportViolation(NonceDirective*, const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const; - - bool checkSourceAndReportViolation(SourceListDirective*, const URL&, const String& effectiveDirective) const; - bool checkMediaTypeAndReportViolation(MediaListDirective*, const String& type, const String& typeAttribute, const String& consoleMessage) const; - - bool denyIfEnforcingPolicy() const { return m_reportOnly; } - - ContentSecurityPolicy* m_policy; - - String m_header; - ContentSecurityPolicy::HeaderType m_headerType; - - bool m_reportOnly; - bool m_haveSandboxPolicy; - ContentSecurityPolicy::ReflectedXSSDisposition m_reflectedXSSDisposition; - - OwnPtr m_pluginTypes; - OwnPtr m_scriptNonce; - OwnPtr m_baseURI; - OwnPtr m_connectSrc; - OwnPtr m_defaultSrc; - OwnPtr m_fontSrc; - OwnPtr m_formAction; - OwnPtr m_frameSrc; - OwnPtr m_imgSrc; - OwnPtr m_mediaSrc; - OwnPtr m_objectSrc; - OwnPtr m_scriptSrc; - OwnPtr m_styleSrc; - - Vector m_reportURIs; - - String m_evalDisabledErrorMessage; -}; - -CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy, ContentSecurityPolicy::HeaderType type) - : m_policy(policy) - , m_headerType(type) - , m_reportOnly(false) - , m_haveSandboxPolicy(false) - , m_reflectedXSSDisposition(ContentSecurityPolicy::ReflectedXSSUnset) -{ - m_reportOnly = (type == ContentSecurityPolicy::Report || type == ContentSecurityPolicy::PrefixedReport); -} - -PassOwnPtr CSPDirectiveList::create(ContentSecurityPolicy* policy, const String& header, ContentSecurityPolicy::HeaderType type) -{ - OwnPtr directives = adoptPtr(new CSPDirectiveList(policy, type)); - directives->parse(header); - - if (!directives->checkEval(directives->operativeDirective(directives->m_scriptSrc.get()))) { - String message = makeString("Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: \"", directives->operativeDirective(directives->m_scriptSrc.get())->text(), "\".\n"); - directives->setEvalDisabledErrorMessage(message); - } - - if (directives->isReportOnly() && directives->reportURIs().isEmpty()) - policy->reportMissingReportURI(header); - - return directives.release(); -} - -void CSPDirectiveList::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const -{ - String message = m_reportOnly ? "[Report Only] " + consoleMessage : consoleMessage; - m_policy->reportViolation(directiveText, effectiveDirective, message, blockedURL, m_reportURIs, m_header, contextURL, contextLine, state); -} - -bool CSPDirectiveList::checkEval(SourceListDirective* directive) const -{ - return !directive || directive->allowEval(); -} - -bool CSPDirectiveList::checkInline(SourceListDirective* directive) const -{ - return !directive || directive->allowInline(); -} - -bool CSPDirectiveList::checkNonce(NonceDirective* directive, const String& nonce) const -{ - return !directive || directive->allows(nonce); -} - -bool CSPDirectiveList::checkSource(SourceListDirective* directive, const URL& url) const -{ - return !directive || directive->allows(url); -} - -bool CSPDirectiveList::checkMediaType(MediaListDirective* directive, const String& type, const String& typeAttribute) const -{ - if (!directive) - return true; - if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type) - return false; - return directive->allows(type); -} - -SourceListDirective* CSPDirectiveList::operativeDirective(SourceListDirective* directive) const -{ - return directive ? directive : m_defaultSrc.get(); -} - -bool CSPDirectiveList::checkEvalAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const -{ - if (checkEval(directive)) - return true; - - String suffix = String(); - if (directive == m_defaultSrc) - suffix = " Note that 'script-src' was not explicitly set, so 'default-src' is used as a fallback."; - - reportViolation(directive->text(), scriptSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine, state); - if (!m_reportOnly) { - m_policy->reportBlockedScriptExecutionToInspector(directive->text()); - return false; - } - return true; -} - -bool CSPDirectiveList::checkNonceAndReportViolation(NonceDirective* directive, const String& nonce, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine) const -{ - if (checkNonce(directive, nonce)) - return true; - reportViolation(directive->text(), scriptNonce, consoleMessage + "\"" + directive->text() + "\".\n", URL(), contextURL, contextLine); - return denyIfEnforcingPolicy(); -} - -bool CSPDirectiveList::checkMediaTypeAndReportViolation(MediaListDirective* directive, const String& type, const String& typeAttribute, const String& consoleMessage) const -{ - if (checkMediaType(directive, type, typeAttribute)) - return true; - - String message = makeString(consoleMessage, "\'", directive->text(), "\'."); - if (typeAttribute.isEmpty()) - message = message + " When enforcing the 'plugin-types' directive, the plugin's media type must be explicitly declared with a 'type' attribute on the containing element (e.g. '')."; - - reportViolation(directive->text(), pluginTypes, message + "\n", URL()); - return denyIfEnforcingPolicy(); -} - -bool CSPDirectiveList::checkInlineAndReportViolation(SourceListDirective* directive, const String& consoleMessage, const String& contextURL, const WTF::OrdinalNumber& contextLine, bool isScript) const -{ - if (checkInline(directive)) - return true; - - String suffix = String(); - if (directive == m_defaultSrc) - suffix = makeString(" Note that '", (isScript ? "script" : "style"), "-src' was not explicitly set, so 'default-src' is used as a fallback."); - - reportViolation(directive->text(), isScript ? scriptSrc : styleSrc, consoleMessage + "\"" + directive->text() + "\"." + suffix + "\n", URL(), contextURL, contextLine); - - if (!m_reportOnly) { - if (isScript) - m_policy->reportBlockedScriptExecutionToInspector(directive->text()); - return false; - } - return true; -} - -bool CSPDirectiveList::checkSourceAndReportViolation(SourceListDirective* directive, const URL& url, const String& effectiveDirective) const -{ - if (checkSource(directive, url)) - return true; - - String prefix; - if (baseURI == effectiveDirective) - prefix = "Refused to set the document's base URI to '"; - else if (connectSrc == effectiveDirective) - prefix = "Refused to connect to '"; - else if (fontSrc == effectiveDirective) - prefix = "Refused to load the font '"; - else if (formAction == effectiveDirective) - prefix = "Refused to send form data to '"; - else if (frameSrc == effectiveDirective) - prefix = "Refused to frame '"; - else if (imgSrc == effectiveDirective) - prefix = "Refused to load the image '"; - else if (mediaSrc == effectiveDirective) - prefix = "Refused to load media from '"; - else if (objectSrc == effectiveDirective) - prefix = "Refused to load plugin data from '"; - else if (scriptSrc == effectiveDirective) - prefix = "Refused to load the script '"; - else if (styleSrc == effectiveDirective) - prefix = "Refused to load the stylesheet '"; - - String suffix = String(); - if (directive == m_defaultSrc) - suffix = " Note that '" + effectiveDirective + "' was not explicitly set, so 'default-src' is used as a fallback."; - - reportViolation(directive->text(), effectiveDirective, prefix + url.stringCenterEllipsizedToLength() + "' because it violates the following Content Security Policy directive: \"" + directive->text() + "\"." + suffix + "\n", url); - return denyIfEnforcingPolicy(); -} - -bool CSPDirectiveList::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "))); - if (reportingStatus == ContentSecurityPolicy::SendReport) { - return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) - && checkNonceAndReportViolation(m_scriptNonce.get(), String(), consoleMessage, contextURL, contextLine)); - } else { - return (checkInline(operativeDirective(m_scriptSrc.get())) - && checkNonce(m_scriptNonce.get(), String())); - } -} - -bool CSPDirectiveList::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute inline event handler because it violates the following Content Security Policy directive: "))); - if (reportingStatus == ContentSecurityPolicy::SendReport) { - return (checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) - && checkNonceAndReportViolation(m_scriptNonce.get(), String(), consoleMessage, contextURL, contextLine)); - } else { - return (checkInline(operativeDirective(m_scriptSrc.get())) - && checkNonce(m_scriptNonce.get(), String())); - } -} - -bool CSPDirectiveList::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute inline script because it violates the following Content Security Policy directive: "))); - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkInlineAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, contextURL, contextLine, true) : - checkInline(operativeDirective(m_scriptSrc.get())); -} - -bool CSPDirectiveList::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to apply inline style because it violates the following Content Security Policy directive: "))); - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkInlineAndReportViolation(operativeDirective(m_styleSrc.get()), consoleMessage, contextURL, contextLine, false) : - checkInline(operativeDirective(m_styleSrc.get())); -} - -bool CSPDirectiveList::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to evaluate script because it violates the following Content Security Policy directive: "))); - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkEvalAndReportViolation(operativeDirective(m_scriptSrc.get()), consoleMessage, String(), WTF::OrdinalNumber::beforeFirst(), state) : - checkEval(operativeDirective(m_scriptSrc.get())); -} - -bool CSPDirectiveList::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& url) const -{ - DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Refused to execute script because it violates the following Content Security Policy directive: "))); - if (url.isEmpty()) - return checkNonceAndReportViolation(m_scriptNonce.get(), nonce, consoleMessage, contextURL, contextLine); - return checkNonceAndReportViolation(m_scriptNonce.get(), nonce, "Refused to load '" + url.stringCenterEllipsizedToLength() + "' because it violates the following Content Security Policy directive: ", contextURL, contextLine); -} - -bool CSPDirectiveList::allowPluginType(const String& type, const String& typeAttribute, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkMediaTypeAndReportViolation(m_pluginTypes.get(), type, typeAttribute, "Refused to load '" + url.stringCenterEllipsizedToLength() + "' (MIME type '" + typeAttribute + "') because it violates the following Content Security Policy Directive: ") : - checkMediaType(m_pluginTypes.get(), type, typeAttribute); -} - -bool CSPDirectiveList::allowScriptFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_scriptSrc.get()), url, scriptSrc) : - checkSource(operativeDirective(m_scriptSrc.get()), url); -} - -bool CSPDirectiveList::allowObjectFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - if (url.isBlankURL()) - return true; - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_objectSrc.get()), url, objectSrc) : - checkSource(operativeDirective(m_objectSrc.get()), url); -} - -bool CSPDirectiveList::allowChildFrameFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - if (url.isBlankURL()) - return true; - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_frameSrc.get()), url, frameSrc) : - checkSource(operativeDirective(m_frameSrc.get()), url); -} - -bool CSPDirectiveList::allowImageFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_imgSrc.get()), url, imgSrc) : - checkSource(operativeDirective(m_imgSrc.get()), url); -} - -bool CSPDirectiveList::allowStyleFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_styleSrc.get()), url, styleSrc) : - checkSource(operativeDirective(m_styleSrc.get()), url); -} - -bool CSPDirectiveList::allowFontFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_fontSrc.get()), url, fontSrc) : - checkSource(operativeDirective(m_fontSrc.get()), url); -} - -bool CSPDirectiveList::allowMediaFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_mediaSrc.get()), url, mediaSrc) : - checkSource(operativeDirective(m_mediaSrc.get()), url); -} - -bool CSPDirectiveList::allowConnectToSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(operativeDirective(m_connectSrc.get()), url, connectSrc) : - checkSource(operativeDirective(m_connectSrc.get()), url); -} - -void CSPDirectiveList::gatherReportURIs(DOMStringList& list) const -{ - for (size_t i = 0; i < m_reportURIs.size(); ++i) - list.append(m_reportURIs[i].string()); -} - -bool CSPDirectiveList::allowFormAction(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(m_formAction.get(), url, formAction) : - checkSource(m_formAction.get(), url); -} - -bool CSPDirectiveList::allowBaseURI(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return reportingStatus == ContentSecurityPolicy::SendReport ? - checkSourceAndReportViolation(m_baseURI.get(), url, baseURI) : - checkSource(m_baseURI.get(), url); -} - -// policy = directive-list -// directive-list = [ directive *( ";" [ directive ] ) ] -// -void CSPDirectiveList::parse(const String& policy) -{ - m_header = policy; - if (policy.isEmpty()) - return; - - const UChar* position = policy.deprecatedCharacters(); - const UChar* end = position + policy.length(); - - while (position < end) { - const UChar* directiveBegin = position; - skipUntil(position, end, ';'); - - String name, value; - if (parseDirective(directiveBegin, position, name, value)) { - ASSERT(!name.isEmpty()); - addDirective(name, value); - } - - ASSERT(position == end || *position == ';'); - skipExactly(position, end, ';'); - } -} - -// directive = *WSP [ directive-name [ WSP directive-value ] ] -// directive-name = 1*( ALPHA / DIGIT / "-" ) -// directive-value = *( WSP / ) -// -bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) -{ - ASSERT(name.isEmpty()); - ASSERT(value.isEmpty()); - - const UChar* position = begin; - skipWhile(position, end); - - // Empty directive (e.g. ";;;"). Exit early. - if (position == end) - return false; - - const UChar* nameBegin = position; - skipWhile(position, end); - - // The directive-name must be non-empty. - if (nameBegin == position) { - skipWhile(position, end); - m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); - return false; - } - - name = String(nameBegin, position - nameBegin); - - if (position == end) - return true; - - if (!skipExactly(position, end)) { - skipWhile(position, end); - m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); - return false; - } - - skipWhile(position, end); - - const UChar* valueBegin = position; - skipWhile(position, end); - - if (position != end) { - m_policy->reportInvalidDirectiveValueCharacter(name, String(valueBegin, end - valueBegin)); - return false; - } - - // The directive-value may be empty. - if (valueBegin == position) - return true; - - value = String(valueBegin, position - valueBegin); - return true; -} - -void CSPDirectiveList::parseReportURI(const String& name, const String& value) -{ - if (!m_reportURIs.isEmpty()) { - m_policy->reportDuplicateDirective(name); - return; - } - const UChar* position = value.deprecatedCharacters(); - const UChar* end = position + value.length(); - - while (position < end) { - skipWhile(position, end); - - const UChar* urlBegin = position; - skipWhile(position, end); - - if (urlBegin < position) { - String url = String(urlBegin, position - urlBegin); - m_reportURIs.append(m_policy->completeURL(url)); - } - } -} - - -template -void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr& directive) -{ - if (directive) { - m_policy->reportDuplicateDirective(name); - return; - } - directive = adoptPtr(new CSPDirectiveType(name, value, m_policy)); -} - -void CSPDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy) -{ - if (m_haveSandboxPolicy) { - m_policy->reportDuplicateDirective(name); - return; - } - m_haveSandboxPolicy = true; - String invalidTokens; - m_policy->enforceSandboxFlags(SecurityContext::parseSandboxPolicy(sandboxPolicy, invalidTokens)); - if (!invalidTokens.isNull()) - m_policy->reportInvalidSandboxFlags(invalidTokens); -} - -void CSPDirectiveList::parseReflectedXSS(const String& name, const String& value) -{ - if (m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset) { - m_policy->reportDuplicateDirective(name); - m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid; - return; - } - - if (value.isEmpty()) { - m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid; - m_policy->reportInvalidReflectedXSS(value); - return; - } - - const UChar* position = value.deprecatedCharacters(); - const UChar* end = position + value.length(); - - skipWhile(position, end); - const UChar* begin = position; - skipWhile(position, end); - - // value1 - // ^ - if (equalIgnoringCase("allow", begin, position - begin)) - m_reflectedXSSDisposition = ContentSecurityPolicy::AllowReflectedXSS; - else if (equalIgnoringCase("filter", begin, position - begin)) - m_reflectedXSSDisposition = ContentSecurityPolicy::FilterReflectedXSS; - else if (equalIgnoringCase("block", begin, position - begin)) - m_reflectedXSSDisposition = ContentSecurityPolicy::BlockReflectedXSS; - else { - m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid; - m_policy->reportInvalidReflectedXSS(value); - return; - } - - skipWhile(position, end); - if (position == end && m_reflectedXSSDisposition != ContentSecurityPolicy::ReflectedXSSUnset) - return; - - // value1 value2 - // ^ - m_reflectedXSSDisposition = ContentSecurityPolicy::ReflectedXSSInvalid; - m_policy->reportInvalidReflectedXSS(value); -} - -void CSPDirectiveList::addDirective(const String& name, const String& value) -{ - ASSERT(!name.isEmpty()); - - if (equalIgnoringCase(name, defaultSrc)) - setCSPDirective(name, value, m_defaultSrc); - else if (equalIgnoringCase(name, scriptSrc)) - setCSPDirective(name, value, m_scriptSrc); - else if (equalIgnoringCase(name, objectSrc)) - setCSPDirective(name, value, m_objectSrc); - else if (equalIgnoringCase(name, frameSrc)) - setCSPDirective(name, value, m_frameSrc); - else if (equalIgnoringCase(name, imgSrc)) - setCSPDirective(name, value, m_imgSrc); - else if (equalIgnoringCase(name, styleSrc)) - setCSPDirective(name, value, m_styleSrc); - else if (equalIgnoringCase(name, fontSrc)) - setCSPDirective(name, value, m_fontSrc); - else if (equalIgnoringCase(name, mediaSrc)) - setCSPDirective(name, value, m_mediaSrc); - else if (equalIgnoringCase(name, connectSrc)) - setCSPDirective(name, value, m_connectSrc); - else if (equalIgnoringCase(name, sandbox)) - applySandboxPolicy(name, value); - else if (equalIgnoringCase(name, reportURI)) - parseReportURI(name, value); -#if ENABLE(CSP_NEXT) - else if (m_policy->experimentalFeaturesEnabled()) { - if (equalIgnoringCase(name, baseURI)) - setCSPDirective(name, value, m_baseURI); - else if (equalIgnoringCase(name, formAction)) - setCSPDirective(name, value, m_formAction); - else if (equalIgnoringCase(name, pluginTypes)) - setCSPDirective(name, value, m_pluginTypes); - else if (equalIgnoringCase(name, scriptNonce)) - setCSPDirective(name, value, m_scriptNonce); - else if (equalIgnoringCase(name, reflectedXSS)) - parseReflectedXSS(name, value); - else - m_policy->reportUnsupportedDirective(name); - } -#endif - else - m_policy->reportUnsupportedDirective(name); -} - -ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext* scriptExecutionContext) - : m_scriptExecutionContext(scriptExecutionContext) - , m_overrideInlineStyleAllowed(false) -{ -} - -ContentSecurityPolicy::~ContentSecurityPolicy() -{ -} - -void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) -{ - ASSERT(m_policies.isEmpty()); - for (CSPDirectiveListVector::const_iterator iter = other->m_policies.begin(); iter != other->m_policies.end(); ++iter) - didReceiveHeader((*iter)->header(), (*iter)->headerType()); -} - -void ContentSecurityPolicy::didReceiveHeader(const String& header, HeaderType type) -{ - if (m_scriptExecutionContext->isDocument()) { - Document* document = toDocument(m_scriptExecutionContext); - if (document->domWindow()) - FeatureObserver::observe(document->domWindow(), getFeatureObserverType(type)); - } - - // RFC2616, section 4.2 specifies that headers appearing multiple times can - // be combined with a comma. Walk the header string, and parse each comma - // separated chunk as a separate header. - const UChar* begin = header.deprecatedCharacters(); - const UChar* position = begin; - const UChar* end = begin + header.length(); - while (position < end) { - skipUntil(position, end, ','); - - // header1,header2 OR header1 - // ^ ^ - OwnPtr policy = CSPDirectiveList::create(this, String(begin, position - begin), type); - if (!policy->isReportOnly() && !policy->allowEval(0, SuppressReport)) - m_scriptExecutionContext->disableEval(policy->evalDisabledErrorMessage()); - - m_policies.append(policy.release()); - - // Skip the comma, and begin the next header from the current position. - ASSERT(position == end || *position == ','); - skipExactly(position, end, ','); - begin = position; - } -} - -void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) -{ - m_overrideInlineStyleAllowed = value; -} - -const String& ContentSecurityPolicy::deprecatedHeader() const -{ - return m_policies.isEmpty() ? emptyString() : m_policies[0]->header(); -} - -ContentSecurityPolicy::HeaderType ContentSecurityPolicy::deprecatedHeaderType() const -{ - return m_policies.isEmpty() ? Enforce : m_policies[0]->headerType(); -} - -template -bool isAllowedByAll(const CSPDirectiveListVector& policies, ContentSecurityPolicy::ReportingStatus reportingStatus) -{ - for (size_t i = 0; i < policies.size(); ++i) { - if (!(policies[i].get()->*allowed)(reportingStatus)) - return false; - } - return true; -} - -template -bool isAllowedByAllWithState(const CSPDirectiveListVector& policies, JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) -{ - for (size_t i = 0; i < policies.size(); ++i) { - if (!(policies[i].get()->*allowed)(state, reportingStatus)) - return false; - } - return true; -} - -template -bool isAllowedByAllWithContext(const CSPDirectiveListVector& policies, const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) -{ - for (size_t i = 0; i < policies.size(); ++i) { - if (!(policies[i].get()->*allowed)(contextURL, contextLine, reportingStatus)) - return false; - } - return true; -} - -template -bool isAllowedByAllWithNonce(const CSPDirectiveListVector& policies, const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& url) -{ - for (size_t i = 0; i < policies.size(); ++i) { - if (!(policies[i].get()->*allowed)(nonce, contextURL, contextLine, url)) - return false; - } - return true; -} - -template -bool isAllowedByAllWithURL(const CSPDirectiveListVector& policies, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) -{ - if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol())) - return true; - - for (size_t i = 0; i < policies.size(); ++i) { - if (!(policies[i].get()->*allowFromURL)(url, reportingStatus)) - return false; - } - return true; -} - -bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithContext<&CSPDirectiveList::allowJavaScriptURLs>(m_policies, contextURL, contextLine, reportingStatus); -} - -bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineEventHandlers>(m_policies, contextURL, contextLine, reportingStatus); -} - -bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineScript>(m_policies, contextURL, contextLine, reportingStatus); -} - -bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - if (m_overrideInlineStyleAllowed) - return true; - return isAllowedByAllWithContext<&CSPDirectiveList::allowInlineStyle>(m_policies, contextURL, contextLine, reportingStatus); -} - -bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithState<&CSPDirectiveList::allowEval>(m_policies, state, reportingStatus); -} - -String ContentSecurityPolicy::evalDisabledErrorMessage() const -{ - for (size_t i = 0; i < m_policies.size(); ++i) { - if (!m_policies[i]->allowEval(0, SuppressReport)) - return m_policies[i]->evalDisabledErrorMessage(); - } - return String(); -} - -bool ContentSecurityPolicy::allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& url) const -{ - return isAllowedByAllWithNonce<&CSPDirectiveList::allowScriptNonce>(m_policies, nonce, contextURL, contextLine, url); -} - -bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - for (size_t i = 0; i < m_policies.size(); ++i) { - if (!m_policies[i]->allowPluginType(type, typeAttribute, url, reportingStatus)) - return false; - } - return true; -} - -bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowScriptFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowObjectFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowChildFrameFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowImageFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowImageFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowStyleFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowFontFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowFontFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowMediaFromSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowConnectToSource(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowConnectToSource>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowFormAction(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowFormAction>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::allowBaseURI(const URL& url, ContentSecurityPolicy::ReportingStatus reportingStatus) const -{ - return isAllowedByAllWithURL<&CSPDirectiveList::allowBaseURI>(m_policies, url, reportingStatus); -} - -bool ContentSecurityPolicy::isActive() const -{ - return !m_policies.isEmpty(); -} - -ContentSecurityPolicy::ReflectedXSSDisposition ContentSecurityPolicy::reflectedXSSDisposition() const -{ - ReflectedXSSDisposition disposition = ReflectedXSSUnset; - for (size_t i = 0; i < m_policies.size(); ++i) { - if (m_policies[i]->reflectedXSSDisposition() > disposition) - disposition = std::max(disposition, m_policies[i]->reflectedXSSDisposition()); - } - return disposition; -} - -void ContentSecurityPolicy::gatherReportURIs(DOMStringList& list) const -{ - for (size_t i = 0; i < m_policies.size(); ++i) - m_policies[i]->gatherReportURIs(list); -} - -SecurityOrigin* ContentSecurityPolicy::securityOrigin() const -{ - return m_scriptExecutionContext->securityOrigin(); -} - -const URL& ContentSecurityPolicy::url() const -{ - return m_scriptExecutionContext->url(); -} - -URL ContentSecurityPolicy::completeURL(const String& url) const -{ - return m_scriptExecutionContext->completeURL(url); -} - -void ContentSecurityPolicy::enforceSandboxFlags(SandboxFlags mask) const -{ - m_scriptExecutionContext->enforceSandboxFlags(mask); -} - -static String stripURLForUseInReport(Document* document, const URL& url) -{ - if (!url.isValid()) - return String(); - if (!url.isHierarchical() || url.protocolIs("file")) - return url.protocol(); - return document->securityOrigin()->canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url)->toString(); -} - -#if ENABLE(CSP_NEXT) -static void gatherSecurityPolicyViolationEventData(SecurityPolicyViolationEventInit& init, Document* document, const String& directiveText, const String& effectiveDirective, const URL& blockedURL, const String& header) -{ - init.documentURI = document->url().string(); - init.referrer = document->referrer(); - init.blockedURI = stripURLForUseInReport(document, blockedURL); - init.violatedDirective = directiveText; - init.effectiveDirective = effectiveDirective; - init.originalPolicy = header; - init.sourceFile = String(); - init.lineNumber = 0; - - RefPtr stack = createScriptCallStack(2, false); - if (!stack) - return; - - const ScriptCallFrame& callFrame = getFirstNonNativeFrame(stack); - - if (callFrame.lineNumber()) { - URL source = URL(ParsedURLString, callFrame.sourceURL()); - init.sourceFile = stripURLForUseInReport(document, source); - init.lineNumber = callFrame.lineNumber(); - } -} -#endif - -void ContentSecurityPolicy::reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const Vector& reportURIs, const String& header, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const -{ - logToConsole(consoleMessage, contextURL, contextLine, state); - - // FIXME: Support sending reports from worker. - if (!m_scriptExecutionContext->isDocument()) - return; - - Document* document = toDocument(m_scriptExecutionContext); - Frame* frame = document->frame(); - if (!frame) - return; - -#if ENABLE(CSP_NEXT) - if (experimentalFeaturesEnabled()) { - // FIXME: This code means that we're gathering information like line numbers twice. Once we can bring this out from behind the flag, we should reuse the data gathered here when generating the JSON report below. - SecurityPolicyViolationEventInit init; - gatherSecurityPolicyViolationEventData(init, document, directiveText, effectiveDirective, blockedURL, header); - document->enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, init)); - } -#endif - - if (reportURIs.isEmpty()) - return; - - // We need to be careful here when deciding what information to send to the - // report-uri. Currently, we send only the current document's URL and the - // directive that was violated. The document's URL is safe to send because - // it's the document itself that's requesting that it be sent. You could - // make an argument that we shouldn't send HTTPS document URLs to HTTP - // report-uris (for the same reasons that we supress the Referer in that - // case), but the Referer is sent implicitly whereas this request is only - // sent explicitly. As for which directive was violated, that's pretty - // harmless information. - - RefPtr cspReport = InspectorObject::create(); - cspReport->setString("document-uri", document->url().strippedForUseAsReferrer()); - cspReport->setString("referrer", document->referrer()); - cspReport->setString("violated-directive", directiveText); -#if ENABLE(CSP_NEXT) - if (experimentalFeaturesEnabled()) - cspReport->setString("effective-directive", effectiveDirective); -#else - UNUSED_PARAM(effectiveDirective); -#endif - cspReport->setString("original-policy", header); - cspReport->setString("blocked-uri", stripURLForUseInReport(document, blockedURL)); - - RefPtr stack = createScriptCallStack(2, false); - if (stack) { - const ScriptCallFrame& callFrame = getFirstNonNativeFrame(stack); - - if (callFrame.lineNumber()) { - URL source = URL(ParsedURLString, callFrame.sourceURL()); - cspReport->setString("source-file", stripURLForUseInReport(document, source)); - cspReport->setNumber("line-number", callFrame.lineNumber()); - } - } - - RefPtr reportObject = InspectorObject::create(); - reportObject->setObject("csp-report", cspReport.release()); - - RefPtr report = FormData::create(reportObject->toJSONString().utf8()); - - for (size_t i = 0; i < reportURIs.size(); ++i) - PingLoader::sendViolationReport(frame, reportURIs[i], report); -} - -void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const -{ - DEFINE_STATIC_LOCAL(String, allow, (ASCIILiteral("allow"))); - DEFINE_STATIC_LOCAL(String, options, (ASCIILiteral("options"))); - DEFINE_STATIC_LOCAL(String, policyURI, (ASCIILiteral("policy-uri"))); - DEFINE_STATIC_LOCAL(String, allowMessage, (ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."))); - DEFINE_STATIC_LOCAL(String, optionsMessage, (ASCIILiteral("The 'options' directive has been replaced with 'unsafe-inline' and 'unsafe-eval' source expressions for the 'script-src' and 'style-src' directives. Please use those directives instead, as 'options' has no effect."))); - DEFINE_STATIC_LOCAL(String, policyURIMessage, (ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header."))); - - String message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); - if (equalIgnoringCase(name, allow)) - message = allowMessage; - else if (equalIgnoringCase(name, options)) - message = optionsMessage; - else if (equalIgnoringCase(name, policyURI)) - message = policyURIMessage; - - logToConsole(message); -} - -void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const -{ - String message = "The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?"; - logToConsole(message); -} - -void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const -{ - String message = makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n"); - logToConsole(message); -} - -void ContentSecurityPolicy::reportInvalidPluginTypes(const String& pluginType) const -{ - String message; - if (pluginType.isNull()) - message = "'plugin-types' Content Security Policy directive is empty; all plugins will be blocked.\n"; - else - message = makeString("Invalid plugin type in 'plugin-types' Content Security Policy directive: '", pluginType, "'.\n"); - logToConsole(message); -} - -void ContentSecurityPolicy::reportInvalidSandboxFlags(const String& invalidFlags) const -{ - logToConsole("Error while parsing the 'sandbox' Content Security Policy directive: " + invalidFlags); -} - -void ContentSecurityPolicy::reportInvalidReflectedXSS(const String& invalidValue) const -{ - logToConsole("The 'reflected-xss' Content Security Policy directive has the invalid value \"" + invalidValue + "\". Value values are \"allow\", \"filter\", and \"block\"."); -} - -void ContentSecurityPolicy::reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const -{ - String message = makeString("The value for Content Security Policy directive '", directiveName, "' contains an invalid character: '", value, "'. Non-whitespace characters outside ASCII 0x21-0x7E must be percent-encoded, as described in RFC 3986, section 2.1: http://tools.ietf.org/html/rfc3986#section-2.1."); - logToConsole(message); -} - -void ContentSecurityPolicy::reportInvalidPathCharacter(const String& directiveName, const String& value, const char invalidChar) const -{ - ASSERT(invalidChar == '#' || invalidChar == '?'); - - String ignoring = "The fragment identifier, including the '#', will be ignored."; - if (invalidChar == '?') - ignoring = "The query component, including the '?', will be ignored."; - String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains a source with an invalid path: '", value, "'. ", ignoring); - logToConsole(message); -} - -void ContentSecurityPolicy::reportInvalidNonce(const String& nonce) const -{ - String message = makeString("Ignoring invalid Content Security Policy script nonce: '", nonce, "'.\n"); - logToConsole(message); -} - -void ContentSecurityPolicy::reportInvalidSourceExpression(const String& directiveName, const String& source) const -{ - String message = makeString("The source list for Content Security Policy directive '", directiveName, "' contains an invalid source: '", source, "'. It will be ignored."); - if (equalIgnoringCase(source, "'none'")) - message = makeString(message, " Note that 'none' has no effect unless it is the only expression in the source list."); - logToConsole(message); -} - -void ContentSecurityPolicy::reportMissingReportURI(const String& policy) const -{ - logToConsole("The Content Security Policy '" + policy + "' was delivered in report-only mode, but does not specify a 'report-uri'; the policy will have no effect. Please either add a 'report-uri' directive, or deliver the policy via the 'Content-Security-Policy' header."); -} - -void ContentSecurityPolicy::logToConsole(const String& message, const String& contextURL, const WTF::OrdinalNumber& contextLine, JSC::ExecState* state) const -{ - // FIXME: ContentSecurityPolicy::logToConsole should include a column number - m_scriptExecutionContext->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, contextURL, contextLine.oneBasedInt(), 0, state); -} - -void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const -{ - InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText); -} - -bool ContentSecurityPolicy::experimentalFeaturesEnabled() const -{ -#if ENABLE(CSP_NEXT) - return RuntimeEnabledFeatures::sharedFeatures().experimentalContentSecurityPolicyFeaturesEnabled(); -#else - return false; -#endif -} - -} diff --git a/Source/WebCore/page/ContentSecurityPolicy.h b/Source/WebCore/page/ContentSecurityPolicy.h deleted file mode 100644 index a834bbbf6..000000000 --- a/Source/WebCore/page/ContentSecurityPolicy.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2011 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``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 APPLE COMPUTER, INC. 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. - */ - -#ifndef ContentSecurityPolicy_h -#define ContentSecurityPolicy_h - -#include "URL.h" -#include "ScriptState.h" -#include -#include -#include -#include -#include - -namespace WTF { -class OrdinalNumber; -} - -namespace WebCore { - -class CSPDirectiveList; -class DOMStringList; -class ScriptExecutionContext; -class SecurityOrigin; - -typedef int SandboxFlags; -typedef Vector> CSPDirectiveListVector; - -class ContentSecurityPolicy { - WTF_MAKE_FAST_ALLOCATED; -public: - static PassOwnPtr create(ScriptExecutionContext* scriptExecutionContext) - { - return adoptPtr(new ContentSecurityPolicy(scriptExecutionContext)); - } - ~ContentSecurityPolicy(); - - void copyStateFrom(const ContentSecurityPolicy*); - - enum HeaderType { - Report, - Enforce, - PrefixedReport, - PrefixedEnforce - }; - - enum ReportingStatus { - SendReport, - SuppressReport - }; - - // Be sure to update the behavior of XSSAuditor::combineXSSProtectionHeaderAndCSP whenever you change this enum's content or ordering. - enum ReflectedXSSDisposition { - ReflectedXSSUnset = 0, - AllowReflectedXSS, - ReflectedXSSInvalid, - FilterReflectedXSS, - BlockReflectedXSS - }; - - void didReceiveHeader(const String&, HeaderType); - - // These functions are wrong because they assume that there is only one header. - // FIXME: Replace them with functions that return vectors. - const String& deprecatedHeader() const; - HeaderType deprecatedHeaderType() const; - - bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, ReportingStatus = SendReport) const; - bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, ReportingStatus = SendReport) const; - bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, ReportingStatus = SendReport) const; - bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, ReportingStatus = SendReport) const; - bool allowEval(JSC::ExecState* = 0, ReportingStatus = SendReport) const; - bool allowScriptNonce(const String& nonce, const String& contextURL, const WTF::OrdinalNumber& contextLine, const URL& = URL()) const; - bool allowPluginType(const String& type, const String& typeAttribute, const URL&, ReportingStatus = SendReport) const; - - bool allowScriptFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowObjectFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowChildFrameFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowImageFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowStyleFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowFontFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowMediaFromSource(const URL&, ReportingStatus = SendReport) const; - bool allowConnectToSource(const URL&, ReportingStatus = SendReport) const; - bool allowFormAction(const URL&, ReportingStatus = SendReport) const; - bool allowBaseURI(const URL&, ReportingStatus = SendReport) const; - - ReflectedXSSDisposition reflectedXSSDisposition() const; - - void setOverrideAllowInlineStyle(bool); - - bool isActive() const; - void gatherReportURIs(DOMStringList&) const; - - void reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const; - void reportDuplicateDirective(const String&) const; - void reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const; - void reportInvalidPathCharacter(const String& directiveName, const String& value, const char) const; - void reportInvalidNonce(const String&) const; - void reportInvalidPluginTypes(const String&) const; - void reportInvalidSandboxFlags(const String&) const; - void reportInvalidSourceExpression(const String& directiveName, const String& source) const; - void reportInvalidReflectedXSS(const String&) const; - void reportMissingReportURI(const String&) const; - void reportUnsupportedDirective(const String&) const; - void reportViolation(const String& directiveText, const String& effectiveDirective, const String& consoleMessage, const URL& blockedURL, const Vector& reportURIs, const String& header, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = 0) const; - - void reportBlockedScriptExecutionToInspector(const String& directiveText) const; - - const URL& url() const; - URL completeURL(const String&) const; - SecurityOrigin* securityOrigin() const; - void enforceSandboxFlags(SandboxFlags) const; - String evalDisabledErrorMessage() const; - - bool experimentalFeaturesEnabled() const; - -private: - explicit ContentSecurityPolicy(ScriptExecutionContext*); - - void logToConsole(const String& message, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = 0) const; - - ScriptExecutionContext* m_scriptExecutionContext; - bool m_overrideInlineStyleAllowed; - CSPDirectiveListVector m_policies; -}; - -} - -#endif diff --git a/Source/WebCore/page/ContextMenuClient.h b/Source/WebCore/page/ContextMenuClient.h index 23d3a5aa6..990c1e0b1 100644 --- a/Source/WebCore/page/ContextMenuClient.h +++ b/Source/WebCore/page/ContextMenuClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,20 +23,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ContextMenuClient_h -#define ContextMenuClient_h +#pragma once #if ENABLE(CONTEXT_MENUS) #include "ContextMenu.h" -#include "PlatformMenuDescription.h" #include -#include namespace WebCore { - class ContextMenuItem; + class Frame; - class HitTestResult; class URL; class ContextMenuClient { @@ -44,14 +40,6 @@ namespace WebCore { virtual ~ContextMenuClient() { } virtual void contextMenuDestroyed() = 0; -#if USE(CROSS_PLATFORM_CONTEXT_MENUS) - virtual PassOwnPtr customizeMenu(PassOwnPtr) = 0; -#else - virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) = 0; -#endif - - virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) = 0; - virtual void downloadURL(const URL& url) = 0; virtual void searchWithGoogle(const Frame*) = 0; virtual void lookUpInDictionary(Frame*) = 0; @@ -59,7 +47,7 @@ namespace WebCore { virtual void speak(const String&) = 0; virtual void stopSpeaking() = 0; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) virtual void searchWithSpotlight() = 0; #endif @@ -67,7 +55,7 @@ namespace WebCore { virtual void showContextMenu() = 0; #endif }; -} + +} // namespace WebCore #endif // ENABLE(CONTEXT_MENUS) -#endif diff --git a/Source/WebCore/page/ContextMenuContext.cpp b/Source/WebCore/page/ContextMenuContext.cpp new file mode 100644 index 000000000..bc81dc0fb --- /dev/null +++ b/Source/WebCore/page/ContextMenuContext.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``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 APPLE INC. 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 "config.h" +#include "ContextMenuContext.h" + +#if ENABLE(CONTEXT_MENUS) + +namespace WebCore { + +ContextMenuContext::ContextMenuContext() +{ +} + +ContextMenuContext::ContextMenuContext(const HitTestResult& hitTestResult) + : m_hitTestResult(hitTestResult) +#if ENABLE(SERVICE_CONTROLS) + , m_controlledImage(nullptr) +#endif +{ +} + +} // namespace WebCore + +#endif // ENABLE(CONTEXT_MENUS) diff --git a/Source/WebCore/page/ContextMenuContext.h b/Source/WebCore/page/ContextMenuContext.h new file mode 100644 index 000000000..c22b1a21d --- /dev/null +++ b/Source/WebCore/page/ContextMenuContext.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE, INC. ``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 APPLE INC. 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. + * + */ + +#pragma once + +#if ENABLE(CONTEXT_MENUS) + +#include "HitTestResult.h" +#include "Image.h" + +namespace WebCore { + +class ContextMenuContext { +public: + ContextMenuContext(); + ContextMenuContext(const HitTestResult&); + + const HitTestResult& hitTestResult() const { return m_hitTestResult; } + + void setSelectedText(const String& selectedText) { m_selectedText = selectedText; } + const String& selectedText() const { return m_selectedText; } + +#if ENABLE(SERVICE_CONTROLS) + void setControlledImage(Image* controlledImage) { m_controlledImage = controlledImage; } + Image* controlledImage() const { return m_controlledImage.get(); } +#endif + +private: + HitTestResult m_hitTestResult; + String m_selectedText; + +#if ENABLE(SERVICE_CONTROLS) + RefPtr m_controlledImage; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(CONTEXT_MENUS) diff --git a/Source/WebCore/page/ContextMenuController.cpp b/Source/WebCore/page/ContextMenuController.cpp index 04a06d11d..cb078cd98 100644 --- a/Source/WebCore/page/ContextMenuController.cpp +++ b/Source/WebCore/page/ContextMenuController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2010 Igalia S.L * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -42,13 +42,12 @@ #include "EditorClient.h" #include "Event.h" #include "EventHandler.h" -#include "EventNames.h" -#include "ExceptionCodePlaceholder.h" #include "FormState.h" #include "FrameLoadRequest.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "FrameSelection.h" +#include "HTMLFormControlElement.h" #include "HTMLFormElement.h" #include "HitTestRequest.h" #include "HitTestResult.h" @@ -60,6 +59,7 @@ #include "Node.h" #include "Page.h" #include "PlatformEvent.h" +#include "RenderImage.h" #include "ReplaceSelectionCommand.h" #include "ResourceRequest.h" #include "Settings.h" @@ -69,7 +69,6 @@ #include "WindowFeatures.h" #include "markup.h" #include -#include using namespace WTF; using namespace Unicode; @@ -89,15 +88,15 @@ ContextMenuController::~ContextMenuController() void ContextMenuController::clearContextMenu() { - m_contextMenu.clear(); + m_contextMenu = nullptr; if (m_menuProvider) m_menuProvider->contextMenuCleared(); - m_menuProvider = 0; + m_menuProvider = nullptr; } -void ContextMenuController::handleContextMenuEvent(Event* event) +void ContextMenuController::handleContextMenuEvent(Event& event) { - m_contextMenu = createContextMenu(event); + m_contextMenu = maybeCreateContextMenu(event); if (!m_contextMenu) return; @@ -106,175 +105,192 @@ void ContextMenuController::handleContextMenuEvent(Event* event) showContextMenu(event); } -static PassOwnPtr separatorItem() +static std::unique_ptr separatorItem() { - return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String())); + return std::unique_ptr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String())); } -void ContextMenuController::showContextMenu(Event* event, PassRefPtr menuProvider) +void ContextMenuController::showContextMenu(Event& event, ContextMenuProvider& provider) { - m_menuProvider = menuProvider; + m_menuProvider = &provider; - m_contextMenu = createContextMenu(event); + m_contextMenu = maybeCreateContextMenu(event); if (!m_contextMenu) { clearContextMenu(); return; } - m_menuProvider->populateContextMenu(m_contextMenu.get()); - if (m_hitTestResult.isSelected()) { + provider.populateContextMenu(m_contextMenu.get()); + if (m_context.hitTestResult().isSelected()) { appendItem(*separatorItem(), m_contextMenu.get()); populate(); } showContextMenu(event); } -PassOwnPtr ContextMenuController::createContextMenu(Event* event) +#if ENABLE(SERVICE_CONTROLS) + +static Image* imageFromImageElementNode(Node& node) { - ASSERT(event); - - if (!event->isMouseEvent()) + auto* renderer = node.renderer(); + if (!is(renderer)) return nullptr; + auto* image = downcast(*renderer).cachedImage(); + if (!image || image->errorOccurred()) + return nullptr; + return image->imageForRenderer(renderer); +} - MouseEvent* mouseEvent = static_cast(event); - HitTestResult result(mouseEvent->absoluteLocation()); +#endif + +std::unique_ptr ContextMenuController::maybeCreateContextMenu(Event& event) +{ + if (!is(event)) + return nullptr; - if (Frame* frame = event->target()->toNode()->document().frame()) - result = frame->eventHandler().hitTestResultAtPoint(mouseEvent->absoluteLocation()); + auto& mouseEvent = downcast(event); + auto* node = mouseEvent.target()->toNode(); + if (!node) + return nullptr; + auto* frame = node->document().frame(); + if (!frame) + return nullptr; + auto result = frame->eventHandler().hitTestResultAtPoint(mouseEvent.absoluteLocation()); if (!result.innerNonSharedNode()) return nullptr; - m_hitTestResult = result; + m_context = ContextMenuContext(result); + +#if ENABLE(SERVICE_CONTROLS) + if (node->isImageControlsButtonElement()) { + if (auto* image = imageFromImageElementNode(*result.innerNonSharedNode())) + m_context.setControlledImage(image); - return adoptPtr(new ContextMenu); + // FIXME: If we couldn't get the image then we shouldn't try to show the image controls menu for it. + return nullptr; + } +#endif + + return std::make_unique(); } -void ContextMenuController::showContextMenu(Event* event) +void ContextMenuController::showContextMenu(Event& event) { -#if ENABLE(INSPECTOR) if (m_page.inspectorController().enabled()) addInspectElementItem(); -#endif -#if USE(CROSS_PLATFORM_CONTEXT_MENUS) - m_contextMenu = m_client.customizeMenu(m_contextMenu.release()); -#else - PlatformMenuDescription customMenu = m_client.getCustomMenuFromDefaultItems(m_contextMenu.get()); - m_contextMenu->setPlatformDescription(customMenu); -#endif - event->setDefaultHandled(); + event.setDefaultHandled(); } -static void openNewWindow(const URL& urlToLoad, Frame* frame) +static void openNewWindow(const URL& urlToLoad, Frame& frame, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy) { - Page* oldPage = frame->page(); + Page* oldPage = frame.page(); if (!oldPage) return; - - FrameLoadRequest request(frame->document()->securityOrigin(), ResourceRequest(urlToLoad, frame->loader().outgoingReferrer())); - Page* newPage = oldPage; - newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest())); + FrameLoadRequest request(frame.document()->securityOrigin(), ResourceRequest(urlToLoad, frame.loader().outgoingReferrer()), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, shouldOpenExternalURLsPolicy); + + Page* newPage = oldPage->chrome().createWindow(frame, request, WindowFeatures(), NavigationAction(request.resourceRequest())); if (!newPage) return; newPage->chrome().show(); - newPage->mainFrame().loader().loadFrameRequest(request, false, false, 0, 0, MaybeSendReferrer); + newPage->mainFrame().loader().loadFrameRequest(request, nullptr, nullptr); } #if PLATFORM(GTK) -static void insertUnicodeCharacter(UChar character, Frame* frame) + +static void insertUnicodeCharacter(UChar character, Frame& frame) { String text(&character, 1); - if (!frame->editor().shouldInsertText(text, frame->selection().toNormalizedRange().get(), EditorInsertActionTyped)) + if (!frame.editor().shouldInsertText(text, frame.selection().toNormalizedRange().get(), EditorInsertAction::Typed)) return; - ASSERT(frame->document()); - TypingCommand::insertText(*frame->document(), text, 0, TypingCommand::TextCompositionNone); + ASSERT(frame.document()); + TypingCommand::insertText(*frame.document(), text, 0, TypingCommand::TextCompositionNone); } + #endif -void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) +void ContextMenuController::contextMenuItemSelected(ContextMenuAction action, const String& title) { - ASSERT(item->type() == ActionType || item->type() == CheckableActionType); - - if (item->action() >= ContextMenuItemBaseApplicationTag) { - m_client.contextMenuItemSelected(item, m_contextMenu.get()); - return; - } - - if (item->action() >= ContextMenuItemBaseCustomTag) { + if (action >= ContextMenuItemBaseCustomTag) { ASSERT(m_menuProvider); - m_menuProvider->contextMenuItemSelected(item); + m_menuProvider->contextMenuItemSelected(action, title); return; } - Frame* frame = m_hitTestResult.innerNonSharedNode()->document().frame(); + Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame(); if (!frame) return; - switch (item->action()) { + Ref protector(*frame); + + switch (action) { case ContextMenuItemTagOpenLinkInNewWindow: - openNewWindow(m_hitTestResult.absoluteLinkURL(), frame); + openNewWindow(m_context.hitTestResult().absoluteLinkURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes); break; case ContextMenuItemTagDownloadLinkToDisk: // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709) - m_client.downloadURL(m_hitTestResult.absoluteLinkURL()); + m_client.downloadURL(m_context.hitTestResult().absoluteLinkURL()); break; case ContextMenuItemTagCopyLinkToClipboard: - frame->editor().copyURL(m_hitTestResult.absoluteLinkURL(), m_hitTestResult.textContent()); + frame->editor().copyURL(m_context.hitTestResult().absoluteLinkURL(), m_context.hitTestResult().textContent()); break; case ContextMenuItemTagOpenImageInNewWindow: - openNewWindow(m_hitTestResult.absoluteImageURL(), frame); + openNewWindow(m_context.hitTestResult().absoluteImageURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldNotAllow); break; case ContextMenuItemTagDownloadImageToDisk: // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709) - m_client.downloadURL(m_hitTestResult.absoluteImageURL()); + m_client.downloadURL(m_context.hitTestResult().absoluteImageURL()); break; case ContextMenuItemTagCopyImageToClipboard: // FIXME: The Pasteboard class is not written yet // For now, call into the client. This is temporary! - frame->editor().copyImage(m_hitTestResult); + frame->editor().copyImage(m_context.hitTestResult()); break; -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) case ContextMenuItemTagCopyImageUrlToClipboard: - frame->editor().copyURL(m_hitTestResult.absoluteImageURL(), m_hitTestResult.textContent()); + frame->editor().copyURL(m_context.hitTestResult().absoluteImageURL(), m_context.hitTestResult().textContent()); break; #endif case ContextMenuItemTagOpenMediaInNewWindow: - openNewWindow(m_hitTestResult.absoluteMediaURL(), frame); + openNewWindow(m_context.hitTestResult().absoluteMediaURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldNotAllow); break; case ContextMenuItemTagDownloadMediaToDisk: // FIXME: Some day we should be able to do this from within WebCore. (Bug 117709) - m_client.downloadURL(m_hitTestResult.absoluteMediaURL()); + m_client.downloadURL(m_context.hitTestResult().absoluteMediaURL()); break; case ContextMenuItemTagCopyMediaLinkToClipboard: - frame->editor().copyURL(m_hitTestResult.absoluteMediaURL(), m_hitTestResult.textContent()); + frame->editor().copyURL(m_context.hitTestResult().absoluteMediaURL(), m_context.hitTestResult().textContent()); break; case ContextMenuItemTagToggleMediaControls: - m_hitTestResult.toggleMediaControlsDisplay(); + m_context.hitTestResult().toggleMediaControlsDisplay(); break; case ContextMenuItemTagToggleMediaLoop: - m_hitTestResult.toggleMediaLoopPlayback(); + m_context.hitTestResult().toggleMediaLoopPlayback(); break; case ContextMenuItemTagToggleVideoFullscreen: - m_hitTestResult.toggleMediaFullscreenState(); + m_context.hitTestResult().toggleMediaFullscreenState(); break; case ContextMenuItemTagEnterVideoFullscreen: - m_hitTestResult.enterFullscreenForVideo(); + m_context.hitTestResult().enterFullscreenForVideo(); break; case ContextMenuItemTagMediaPlayPause: - m_hitTestResult.toggleMediaPlayState(); + m_context.hitTestResult().toggleMediaPlayState(); break; case ContextMenuItemTagMediaMute: - m_hitTestResult.toggleMediaMuteState(); + m_context.hitTestResult().toggleMediaMuteState(); + break; + case ContextMenuItemTagToggleVideoEnhancedFullscreen: + m_context.hitTestResult().toggleEnhancedFullscreenForVideo(); break; case ContextMenuItemTagOpenFrameInNewWindow: { DocumentLoader* loader = frame->loader().documentLoader(); if (!loader->unreachableURL().isEmpty()) - openNewWindow(loader->unreachableURL(), frame); + openNewWindow(loader->unreachableURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldNotAllow); else - openNewWindow(loader->url(), frame); + openNewWindow(loader->url(), *frame, ShouldOpenExternalURLsPolicy::ShouldNotAllow); break; } case ContextMenuItemTagCopy: @@ -305,51 +321,51 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) frame->editor().performDelete(); break; case ContextMenuItemTagUnicodeInsertLRMMark: - insertUnicodeCharacter(leftToRightMark, frame); + insertUnicodeCharacter(leftToRightMark, *frame); break; case ContextMenuItemTagUnicodeInsertRLMMark: - insertUnicodeCharacter(rightToLeftMark, frame); + insertUnicodeCharacter(rightToLeftMark, *frame); break; case ContextMenuItemTagUnicodeInsertLREMark: - insertUnicodeCharacter(leftToRightEmbed, frame); + insertUnicodeCharacter(leftToRightEmbed, *frame); break; case ContextMenuItemTagUnicodeInsertRLEMark: - insertUnicodeCharacter(rightToLeftEmbed, frame); + insertUnicodeCharacter(rightToLeftEmbed, *frame); break; case ContextMenuItemTagUnicodeInsertLROMark: - insertUnicodeCharacter(leftToRightOverride, frame); + insertUnicodeCharacter(leftToRightOverride, *frame); break; case ContextMenuItemTagUnicodeInsertRLOMark: - insertUnicodeCharacter(rightToLeftOverride, frame); + insertUnicodeCharacter(rightToLeftOverride, *frame); break; case ContextMenuItemTagUnicodeInsertPDFMark: - insertUnicodeCharacter(popDirectionalFormatting, frame); + insertUnicodeCharacter(popDirectionalFormatting, *frame); break; case ContextMenuItemTagUnicodeInsertZWSMark: - insertUnicodeCharacter(zeroWidthSpace, frame); + insertUnicodeCharacter(zeroWidthSpace, *frame); break; case ContextMenuItemTagUnicodeInsertZWJMark: - insertUnicodeCharacter(zeroWidthJoiner, frame); + insertUnicodeCharacter(zeroWidthJoiner, *frame); break; case ContextMenuItemTagUnicodeInsertZWNJMark: - insertUnicodeCharacter(zeroWidthNonJoiner, frame); + insertUnicodeCharacter(zeroWidthNonJoiner, *frame); break; #endif -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) case ContextMenuItemTagSelectAll: frame->editor().command("SelectAll").execute(); break; #endif case ContextMenuItemTagSpellingGuess: { - FrameSelection& frameSelection = frame->selection(); - if (frame->editor().shouldInsertText(item->title(), frameSelection.toNormalizedRange().get(), EditorInsertActionPasted)) { + VisibleSelection selection = frame->selection().selection(); + if (frame->editor().shouldInsertText(title, selection.toNormalizedRange().get(), EditorInsertAction::Pasted)) { ReplaceSelectionCommand::CommandOptions replaceOptions = ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting; if (frame->editor().behavior().shouldAllowSpellingSuggestionsWithoutSelection()) { - ASSERT(frameSelection.isCaretOrRange()); - VisibleSelection wordSelection(frameSelection.base()); + ASSERT(selection.isCaretOrRange()); + VisibleSelection wordSelection(selection.base()); wordSelection.expandUsingGranularity(WordGranularity); - frameSelection.setSelection(wordSelection); + frame->selection().setSelection(wordSelection); } else { ASSERT(frame->editor().selectedText().length()); replaceOptions |= ReplaceSelectionCommand::SelectReplacement; @@ -357,9 +373,9 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) Document* document = frame->document(); ASSERT(document); - RefPtr command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, item->title(), ""), replaceOptions); + RefPtr command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, title, emptyString()), replaceOptions); applyCommand(command); - frameSelection.revealSelection(ScrollAlignment::alignToEdgeIfNeeded); + frame->selection().revealSelection(SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded); } break; } @@ -377,13 +393,10 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) m_client.lookUpInDictionary(frame); break; case ContextMenuItemTagOpenLink: - if (Frame* targetFrame = m_hitTestResult.targetFrame()) - targetFrame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_hitTestResult.absoluteLinkURL(), frame->loader().outgoingReferrer())), false, false, 0, 0, MaybeSendReferrer); + if (Frame* targetFrame = m_context.hitTestResult().targetFrame()) + targetFrame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_context.hitTestResult().absoluteLinkURL(), frame->loader().outgoingReferrer()), LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, targetFrame->isMainFrame() ? ShouldOpenExternalURLsPolicy::ShouldAllow : ShouldOpenExternalURLsPolicy::ShouldNotAllow), nullptr, nullptr); else - openNewWindow(m_hitTestResult.absoluteLinkURL(), frame); - break; - case ContextMenuItemTagOpenLinkInThisWindow: - frame->loader().loadFrameRequest(FrameLoadRequest(frame->document()->securityOrigin(), ResourceRequest(m_hitTestResult.absoluteLinkURL(), frame->loader().outgoingReferrer())), false, false, 0, 0, MaybeSendReferrer); + openNewWindow(m_context.hitTestResult().absoluteLinkURL(), *frame, ShouldOpenExternalURLsPolicy::ShouldAllow); break; case ContextMenuItemTagBold: frame->editor().command("ToggleBold").execute(); @@ -400,10 +413,11 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) break; case ContextMenuItemTagStartSpeaking: { RefPtr selectedRange = frame->selection().toNormalizedRange(); - if (!selectedRange || selectedRange->collapsed(IGNORE_EXCEPTION)) { - Document& document = m_hitTestResult.innerNonSharedNode()->document(); + if (!selectedRange || selectedRange->collapsed()) { + auto& document = m_context.hitTestResult().innerNonSharedNode()->document(); selectedRange = document.createRange(); - selectedRange->selectNode(document.documentElement(), IGNORE_EXCEPTION); + if (auto* element = document.documentElement()) + selectedRange->selectNode(*element); } m_client.speak(plainText(selectedRange.get())); break; @@ -429,7 +443,7 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) case ContextMenuItemTagTextDirectionRightToLeft: frame->editor().command("MakeTextWritingDirectionRightToLeft").execute(); break; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) case ContextMenuItemTagSearchInSpotlight: m_client.searchWithSpotlight(); break; @@ -446,7 +460,7 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) case ContextMenuItemTagCheckGrammarWithSpelling: frame->editor().toggleGrammarChecking(); break; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) case ContextMenuItemTagShowFonts: frame->editor().showFontPanel(); break; @@ -468,9 +482,9 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) frame->editor().capitalizeWord(); break; #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) case ContextMenuItemTagChangeBack: - frame->editor().changeBackToReplacedString(m_hitTestResult.replacedString()); + frame->editor().changeBackToReplacedString(m_context.hitTestResult().replacedString()); break; #endif #if USE(AUTOMATIC_TEXT_REPLACEMENT) @@ -496,14 +510,12 @@ void ContextMenuController::contextMenuItemSelected(ContextMenuItem* item) frame->editor().toggleAutomaticSpellingCorrection(); break; #endif -#if ENABLE(INSPECTOR) case ContextMenuItemTagInspectElement: if (Page* page = frame->page()) - page->inspectorController().inspect(m_hitTestResult.innerNonSharedNode()); + page->inspectorController().inspect(m_context.hitTestResult().innerNonSharedNode()); break; -#endif case ContextMenuItemTagDictationAlternative: - frame->editor().applyDictationAlternativelternative(item->title()); + frame->editor().applyDictationAlternativelternative(title); break; default: break; @@ -521,26 +533,26 @@ void ContextMenuController::createAndAppendFontSubMenu(ContextMenuItem& fontMenu { ContextMenu fontMenu; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) ContextMenuItem showFonts(ActionType, ContextMenuItemTagShowFonts, contextMenuItemTagShowFonts()); #endif ContextMenuItem bold(CheckableActionType, ContextMenuItemTagBold, contextMenuItemTagBold()); ContextMenuItem italic(CheckableActionType, ContextMenuItemTagItalic, contextMenuItemTagItalic()); ContextMenuItem underline(CheckableActionType, ContextMenuItemTagUnderline, contextMenuItemTagUnderline()); ContextMenuItem outline(ActionType, ContextMenuItemTagOutline, contextMenuItemTagOutline()); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) ContextMenuItem styles(ActionType, ContextMenuItemTagStyles, contextMenuItemTagStyles()); ContextMenuItem showColors(ActionType, ContextMenuItemTagShowColors, contextMenuItemTagShowColors()); #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) appendItem(showFonts, &fontMenu); #endif appendItem(bold, &fontMenu); appendItem(italic, &fontMenu); appendItem(underline, &fontMenu); appendItem(outline, &fontMenu); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) appendItem(styles, &fontMenu); appendItem(*separatorItem(), &fontMenu); appendItem(showColors, &fontMenu); @@ -564,19 +576,19 @@ void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenu contextMenuItemTagCheckSpellingWhileTyping()); ContextMenuItem grammarWithSpelling(CheckableActionType, ContextMenuItemTagCheckGrammarWithSpelling, contextMenuItemTagCheckGrammarWithSpelling()); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) ContextMenuItem correctSpelling(CheckableActionType, ContextMenuItemTagCorrectSpellingAutomatically, contextMenuItemTagCorrectSpellingAutomatically()); #endif appendItem(showSpellingPanel, &spellingAndGrammarMenu); appendItem(checkSpelling, &spellingAndGrammarMenu); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) appendItem(*separatorItem(), &spellingAndGrammarMenu); #endif appendItem(checkAsYouType, &spellingAndGrammarMenu); appendItem(grammarWithSpelling, &spellingAndGrammarMenu); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) appendItem(correctSpelling, &spellingAndGrammarMenu); #endif @@ -586,7 +598,7 @@ void ContextMenuController::createAndAppendSpellingAndGrammarSubMenu(ContextMenu #endif // !PLATFORM(GTK) -#if PLATFORM(MAC) +#if PLATFORM(COCOA) void ContextMenuController::createAndAppendSpeechSubMenu(ContextMenuItem& speechMenuItem) { @@ -669,7 +681,7 @@ void ContextMenuController::createAndAppendTextDirectionSubMenu(ContextMenuItem& #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) void ContextMenuController::createAndAppendSubstitutionsSubMenu(ContextMenuItem& substitutionsMenuItem) { @@ -710,26 +722,13 @@ void ContextMenuController::createAndAppendTransformationsSubMenu(ContextMenuIte #endif -static bool selectionContainsPossibleWord(Frame* frame) -{ - // Current algorithm: look for a character that's not just a separator. - for (TextIterator it(frame->selection().toNormalizedRange().get()); !it.atEnd(); it.advance()) { - int length = it.length(); - for (int i = 0; i < length; ++i) { - if (!(U_GET_GC_MASK(it.characterAt(i)) & U_GC_Z_MASK)) - return true; - } - } - return false; -} - -#if PLATFORM(MAC) +#if PLATFORM(COCOA) #define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 1 #else #define SUPPORTS_TOGGLE_VIDEO_FULLSCREEN 0 #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) #define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 1 #else #define SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS 0 @@ -750,7 +749,7 @@ void ContextMenuController::populate() contextMenuItemTagDownloadImageToDisk()); ContextMenuItem CopyImageItem(ActionType, ContextMenuItemTagCopyImageToClipboard, contextMenuItemTagCopyImageToClipboard()); -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) ContextMenuItem CopyImageUrlItem(ActionType, ContextMenuItemTagCopyImageUrlToClipboard, contextMenuItemTagCopyImageUrlToClipboard()); #endif @@ -774,7 +773,10 @@ void ContextMenuController::populate() contextMenuItemTagEnterVideoFullscreen()); ContextMenuItem ToggleVideoFullscreen(ActionType, ContextMenuItemTagToggleVideoFullscreen, contextMenuItemTagEnterVideoFullscreen()); -#if PLATFORM(MAC) +#if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE) + ContextMenuItem ToggleVideoEnhancedFullscreen(ActionType, ContextMenuItemTagToggleVideoEnhancedFullscreen, contextMenuItemTagEnterVideoEnhancedFullscreen()); +#endif +#if PLATFORM(COCOA) ContextMenuItem SearchSpotlightItem(ActionType, ContextMenuItemTagSearchInSpotlight, contextMenuItemTagSearchInSpotlight()); #endif @@ -801,24 +803,39 @@ void ContextMenuController::populate() #if PLATFORM(GTK) ContextMenuItem DeleteItem(ActionType, ContextMenuItemTagDelete, contextMenuItemTagDelete()); #endif -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) ContextMenuItem SelectAllItem(ActionType, ContextMenuItemTagSelectAll, contextMenuItemTagSelectAll()); #endif - Node* node = m_hitTestResult.innerNonSharedNode(); +#if PLATFORM(GTK) || PLATFORM(WIN) + ContextMenuItem ShareMenuItem; +#else + ContextMenuItem ShareMenuItem(SubmenuType, ContextMenuItemTagShareMenu, emptyString()); +#endif + + Node* node = m_context.hitTestResult().innerNonSharedNode(); if (!node) return; #if PLATFORM(GTK) - if (!m_hitTestResult.isContentEditable() && (node->isElementNode() && toElement(node)->isFormControlElement())) + if (!m_context.hitTestResult().isContentEditable() && is(*node)) return; #endif Frame* frame = node->document().frame(); if (!frame) return; - if (!m_hitTestResult.isContentEditable()) { +#if ENABLE(SERVICE_CONTROLS) + // The default image control menu gets populated solely by the platform. + if (m_context.controlledImage()) + return; +#endif + + if (!m_context.hitTestResult().isContentEditable()) { + String selectedString = m_context.hitTestResult().selectedText(); + m_context.setSelectedText(selectedString); + FrameLoader& loader = frame->loader(); - URL linkURL = m_hitTestResult.absoluteLinkURL(); + URL linkURL = m_context.hitTestResult().absoluteLinkURL(); if (!linkURL.isEmpty()) { if (loader.client().canHandleRequest(ResourceRequest(linkURL))) { appendItem(OpenLinkItem, m_contextMenu.get()); @@ -828,21 +845,21 @@ void ContextMenuController::populate() appendItem(CopyLinkItem, m_contextMenu.get()); } - URL imageURL = m_hitTestResult.absoluteImageURL(); + URL imageURL = m_context.hitTestResult().absoluteImageURL(); if (!imageURL.isEmpty()) { if (!linkURL.isEmpty()) appendItem(*separatorItem(), m_contextMenu.get()); appendItem(OpenImageInNewWindowItem, m_contextMenu.get()); appendItem(DownloadImageItem, m_contextMenu.get()); - if (imageURL.isLocalFile() || m_hitTestResult.image()) + if (imageURL.isLocalFile() || m_context.hitTestResult().image()) appendItem(CopyImageItem, m_contextMenu.get()); -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) appendItem(CopyImageUrlItem, m_contextMenu.get()); #endif } - URL mediaURL = m_hitTestResult.absoluteMediaURL(); + URL mediaURL = m_context.hitTestResult().absoluteMediaURL(); if (!mediaURL.isEmpty()) { if (!linkURL.isEmpty() || !imageURL.isEmpty()) appendItem(*separatorItem(), m_contextMenu.get()); @@ -855,19 +872,21 @@ void ContextMenuController::populate() appendItem(ToggleVideoFullscreen, m_contextMenu.get()); #else appendItem(EnterVideoFullscreen, m_contextMenu.get()); +#endif +#if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE) + appendItem(ToggleVideoEnhancedFullscreen, m_contextMenu.get()); #endif appendItem(*separatorItem(), m_contextMenu.get()); appendItem(CopyMediaLinkItem, m_contextMenu.get()); appendItem(OpenMediaInNewWindowItem, m_contextMenu.get()); - if (loader.client().canHandleRequest(ResourceRequest(mediaURL))) + if (m_context.hitTestResult().isDownloadableMedia() && loader.client().canHandleRequest(ResourceRequest(mediaURL))) appendItem(DownloadMediaItem, m_contextMenu.get()); } if (imageURL.isEmpty() && linkURL.isEmpty() && mediaURL.isEmpty()) { - if (m_hitTestResult.isSelected()) { - if (selectionContainsPossibleWord(frame)) { -#if PLATFORM(MAC) - String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText()); + if (m_context.hitTestResult().isSelected()) { + if (!selectedString.isEmpty()) { +#if PLATFORM(COCOA) ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString)); appendItem(LookUpInDictionaryItem, m_contextMenu.get()); @@ -880,7 +899,10 @@ void ContextMenuController::populate() } appendItem(CopyItem, m_contextMenu.get()); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) + appendItem(*separatorItem(), m_contextMenu.get()); + + appendItem(ShareMenuItem, m_contextMenu.get()); appendItem(*separatorItem(), m_contextMenu.get()); ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu()); @@ -888,9 +910,7 @@ void ContextMenuController::populate() appendItem(SpeechMenuItem, m_contextMenu.get()); #endif } else { -#if ENABLE(INSPECTOR) - if (!(frame->page() && (frame->page()->inspectorController().hasInspectorFrontendClient() || frame->page()->inspectorController().hasRemoteFrontend()))) { -#endif + if (!(frame->page() && (frame->page()->inspectorController().inspectionLevel() > 0 || frame->page()->inspectorController().hasRemoteFrontend()))) { // In GTK+ unavailable items are not hidden but insensitive. #if PLATFORM(GTK) @@ -912,16 +932,22 @@ void ContextMenuController::populate() else appendItem(ReloadItem, m_contextMenu.get()); #endif -#if ENABLE(INSPECTOR) } -#endif if (frame->page() && !frame->isMainFrame()) appendItem(OpenFrameItem, m_contextMenu.get()); + + if (!ShareMenuItem.isNull()) { + appendItem(*separatorItem(), m_contextMenu.get()); + appendItem(ShareMenuItem, m_contextMenu.get()); + } } + } else if (!ShareMenuItem.isNull()) { + appendItem(*separatorItem(), m_contextMenu.get()); + appendItem(ShareMenuItem, m_contextMenu.get()); } } else { // Make an editing context menu - bool inPasswordField = frame->selection().isInPasswordField(); + bool inPasswordField = frame->selection().selection().isInPasswordField(); if (!inPasswordField) { bool haveContextMenuItemsForMisspellingOrGrammer = false; bool spellCheckingEnabled = frame->editor().isSpellCheckingEnabledFor(node); @@ -932,8 +958,7 @@ void ContextMenuController::populate() bool badGrammar; Vector guesses = frame->editor().guessesForMisspelledOrUngrammatical(misspelling, badGrammar); if (misspelling || badGrammar) { - size_t size = guesses.size(); - if (!size) { + if (guesses.isEmpty()) { // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions // list and trailing separator rather than adding a "No Guesses Found" item (matches AppKit) if (misspelling) { @@ -941,8 +966,7 @@ void ContextMenuController::populate() appendItem(*separatorItem(), m_contextMenu.get()); } } else { - for (unsigned i = 0; i < size; i++) { - const String &guess = guesses[i]; + for (const auto& guess : guesses) { if (!guess.isEmpty()) { ContextMenuItem item(ActionType, ContextMenuItemTagSpellingGuess, guess); appendItem(item, m_contextMenu.get()); @@ -957,10 +981,10 @@ void ContextMenuController::populate() appendItem(IgnoreGrammarItem, m_contextMenu.get()); appendItem(*separatorItem(), m_contextMenu.get()); haveContextMenuItemsForMisspellingOrGrammer = true; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) } else { // If the string was autocorrected, generate a contextual menu item allowing it to be changed back. - String replacedString = m_hitTestResult.replacedString(); + String replacedString = m_context.hitTestResult().replacedString(); if (!replacedString.isEmpty()) { ContextMenuItem item(ActionType, ContextMenuItemTagChangeBack, contextMenuItemTagChangeBack(replacedString)); appendItem(item, m_contextMenu.get()); @@ -973,10 +997,10 @@ void ContextMenuController::populate() if (!haveContextMenuItemsForMisspellingOrGrammer) { // Spelling and grammar checking is mutually exclusive with dictation alternatives. - Vector dictationAlternatives = m_hitTestResult.dictationAlternatives(); + Vector dictationAlternatives = m_context.hitTestResult().dictationAlternatives(); if (!dictationAlternatives.isEmpty()) { - for (size_t i = 0; i < dictationAlternatives.size(); ++i) { - ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, dictationAlternatives[i]); + for (auto& alternative : dictationAlternatives) { + ContextMenuItem item(ActionType, ContextMenuItemTagDictationAlternative, alternative); appendItem(item, m_contextMenu.get()); } appendItem(*separatorItem(), m_contextMenu.get()); @@ -985,7 +1009,7 @@ void ContextMenuController::populate() } FrameLoader& loader = frame->loader(); - URL linkURL = m_hitTestResult.absoluteLinkURL(); + URL linkURL = m_context.hitTestResult().absoluteLinkURL(); if (!linkURL.isEmpty()) { if (loader.client().canHandleRequest(ResourceRequest(linkURL))) { appendItem(OpenLinkItem, m_contextMenu.get()); @@ -996,10 +1020,10 @@ void ContextMenuController::populate() appendItem(*separatorItem(), m_contextMenu.get()); } - if (m_hitTestResult.isSelected() && !inPasswordField && selectionContainsPossibleWord(frame)) { -#if PLATFORM(MAC) - String selectedString = frame->displayStringModifiedByEncoding(frame->editor().selectedText()); - ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedString)); + String selectedText = m_context.hitTestResult().selectedText(); + if (m_context.hitTestResult().isSelected() && !inPasswordField && !selectedText.isEmpty()) { +#if PLATFORM(COCOA) + ContextMenuItem LookUpInDictionaryItem(ActionType, ContextMenuItemTagLookUpInDictionary, contextMenuItemTagLookUpInDictionary(selectedText)); appendItem(LookUpInDictionaryItem, m_contextMenu.get()); #endif @@ -1017,7 +1041,7 @@ void ContextMenuController::populate() appendItem(DeleteItem, m_contextMenu.get()); appendItem(*separatorItem(), m_contextMenu.get()); #endif -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) appendItem(SelectAllItem, m_contextMenu.get()); #endif @@ -1029,7 +1053,7 @@ void ContextMenuController::populate() createAndAppendSpellingAndGrammarSubMenu(SpellingAndGrammarMenuItem); appendItem(SpellingAndGrammarMenuItem, m_contextMenu.get()); #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) ContextMenuItem substitutionsMenuItem(SubmenuType, ContextMenuItemTagSubstitutionsMenu, contextMenuItemTagSubstitutionsMenu()); createAndAppendSubstitutionsSubMenu(substitutionsMenuItem); @@ -1050,7 +1074,7 @@ void ContextMenuController::populate() createAndAppendFontSubMenu(FontMenuItem); appendItem(FontMenuItem, m_contextMenu.get()); } -#if PLATFORM(MAC) +#if PLATFORM(COCOA) ContextMenuItem SpeechMenuItem(SubmenuType, ContextMenuItemTagSpeechMenu, contextMenuItemTagSpeechMenu()); createAndAppendSpeechSubMenu(SpeechMenuItem); appendItem(SpeechMenuItem, m_contextMenu.get()); @@ -1079,13 +1103,17 @@ void ContextMenuController::populate() } #endif } + + if (!ShareMenuItem.isNull()) { + appendItem(*separatorItem(), m_contextMenu.get()); + appendItem(ShareMenuItem, m_contextMenu.get()); + } } } -#if ENABLE(INSPECTOR) void ContextMenuController::addInspectElementItem() { - Node* node = m_hitTestResult.innerNonSharedNode(); + Node* node = m_context.hitTestResult().innerNonSharedNode(); if (!node) return; @@ -1098,22 +1126,17 @@ void ContextMenuController::addInspectElementItem() return; ContextMenuItem InspectElementItem(ActionType, ContextMenuItemTagInspectElement, contextMenuItemTagInspectElement()); -#if USE(CROSS_PLATFORM_CONTEXT_MENUS) if (m_contextMenu && !m_contextMenu->items().isEmpty()) -#else - if (m_contextMenu && m_contextMenu->itemCount()) -#endif appendItem(*separatorItem(), m_contextMenu.get()); appendItem(InspectElementItem, m_contextMenu.get()); } -#endif // ENABLE(INSPECTOR) void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const { if (item.type() == SeparatorType) return; - Frame* frame = m_hitTestResult.innerNonSharedNode()->document().frame(); + Frame* frame = m_context.hitTestResult().innerNonSharedNode()->document().frame(); if (!frame) return; @@ -1189,7 +1212,7 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const shouldEnable = true; break; #endif -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) case ContextMenuItemTagSelectAll: shouldEnable = true; break; @@ -1233,7 +1256,7 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const case ContextMenuItemTagCheckSpellingWhileTyping: shouldCheck = frame->editor().isContinuousSpellCheckingEnabled(); break; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) case ContextMenuItemTagSubstitutionsMenu: case ContextMenuItemTagTransformationsMenu: break; @@ -1271,7 +1294,7 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const case ContextMenuItemTagStopSpeaking: shouldEnable = m_client.isSpeaking(); break; -#else // PLATFORM(MAC) ends here +#else // PLATFORM(COCOA) ends here case ContextMenuItemTagStopSpeaking: break; #endif @@ -1300,51 +1323,63 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const #endif case ContextMenuItemTagNoAction: case ContextMenuItemTagOpenLinkInNewWindow: - case ContextMenuItemTagOpenLinkInThisWindow: case ContextMenuItemTagDownloadLinkToDisk: case ContextMenuItemTagCopyLinkToClipboard: case ContextMenuItemTagOpenImageInNewWindow: - case ContextMenuItemTagDownloadImageToDisk: case ContextMenuItemTagCopyImageToClipboard: -#if PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(GTK) case ContextMenuItemTagCopyImageUrlToClipboard: +#endif + break; + case ContextMenuItemTagDownloadImageToDisk: +#if PLATFORM(MAC) + if (WebCore::protocolIs(m_context.hitTestResult().absoluteImageURL(), "file")) + shouldEnable = false; #endif break; case ContextMenuItemTagOpenMediaInNewWindow: - if (m_hitTestResult.mediaIsVideo()) + if (m_context.hitTestResult().mediaIsVideo()) item.setTitle(contextMenuItemTagOpenVideoInNewWindow()); else item.setTitle(contextMenuItemTagOpenAudioInNewWindow()); break; case ContextMenuItemTagDownloadMediaToDisk: - if (m_hitTestResult.mediaIsVideo()) + if (m_context.hitTestResult().mediaIsVideo()) item.setTitle(contextMenuItemTagDownloadVideoToDisk()); else item.setTitle(contextMenuItemTagDownloadAudioToDisk()); + if (WebCore::protocolIs(m_context.hitTestResult().absoluteImageURL(), "file")) + shouldEnable = false; break; case ContextMenuItemTagCopyMediaLinkToClipboard: - if (m_hitTestResult.mediaIsVideo()) + if (m_context.hitTestResult().mediaIsVideo()) item.setTitle(contextMenuItemTagCopyVideoLinkToClipboard()); else item.setTitle(contextMenuItemTagCopyAudioLinkToClipboard()); break; case ContextMenuItemTagToggleMediaControls: #if SUPPORTS_TOGGLE_SHOW_HIDE_MEDIA_CONTROLS - item.setTitle(m_hitTestResult.mediaControlsEnabled() ? contextMenuItemTagHideMediaControls() : contextMenuItemTagShowMediaControls()); + item.setTitle(m_context.hitTestResult().mediaControlsEnabled() ? contextMenuItemTagHideMediaControls() : contextMenuItemTagShowMediaControls()); #else - shouldCheck = m_hitTestResult.mediaControlsEnabled(); + shouldCheck = m_context.hitTestResult().mediaControlsEnabled(); #endif break; case ContextMenuItemTagToggleMediaLoop: - shouldCheck = m_hitTestResult.mediaLoopEnabled(); + shouldCheck = m_context.hitTestResult().mediaLoopEnabled(); break; case ContextMenuItemTagToggleVideoFullscreen: #if SUPPORTS_TOGGLE_VIDEO_FULLSCREEN - item.setTitle(m_hitTestResult.mediaIsInFullscreen() ? contextMenuItemTagExitVideoFullscreen() : contextMenuItemTagEnterVideoFullscreen()); + item.setTitle(m_context.hitTestResult().mediaIsInFullscreen() ? contextMenuItemTagExitVideoFullscreen() : contextMenuItemTagEnterVideoFullscreen()); break; #endif case ContextMenuItemTagEnterVideoFullscreen: - shouldEnable = m_hitTestResult.mediaSupportsFullscreen(); + shouldEnable = m_context.hitTestResult().mediaSupportsFullscreen(); + break; + case ContextMenuItemTagToggleVideoEnhancedFullscreen: +#if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE) + item.setTitle(m_context.hitTestResult().mediaIsInEnhancedFullscreen() ? contextMenuItemTagExitVideoEnhancedFullscreen() : contextMenuItemTagEnterVideoEnhancedFullscreen()); +#endif + shouldEnable = m_context.hitTestResult().mediaSupportsEnhancedFullscreen(); break; case ContextMenuItemTagOpenFrameInNewWindow: case ContextMenuItemTagSpellingGuess: @@ -1373,24 +1408,22 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const case ContextMenuItemTagTextDirectionMenu: case ContextMenuItemTagPDFSinglePageScrolling: case ContextMenuItemTagPDFFacingPagesScrolling: -#if ENABLE(INSPECTOR) case ContextMenuItemTagInspectElement: -#endif case ContextMenuItemBaseCustomTag: - case ContextMenuItemCustomTagNoAction: case ContextMenuItemLastCustomTag: case ContextMenuItemBaseApplicationTag: case ContextMenuItemTagDictationAlternative: + case ContextMenuItemTagShareMenu: break; case ContextMenuItemTagMediaPlayPause: - if (m_hitTestResult.mediaPlaying()) + if (m_context.hitTestResult().mediaPlaying()) item.setTitle(contextMenuItemTagMediaPause()); else item.setTitle(contextMenuItemTagMediaPlay()); break; case ContextMenuItemTagMediaMute: - shouldEnable = m_hitTestResult.mediaHasAudio(); - shouldCheck = shouldEnable && m_hitTestResult.mediaMuted(); + shouldEnable = m_context.hitTestResult().mediaHasAudio(); + shouldCheck = shouldEnable && m_context.hitTestResult().mediaMuted(); break; } @@ -1399,17 +1432,30 @@ void ContextMenuController::checkOrEnableIfNeeded(ContextMenuItem& item) const } #if USE(ACCESSIBILITY_CONTEXT_MENUS) -void ContextMenuController::showContextMenuAt(Frame* frame, const IntPoint& clickPoint) + +void ContextMenuController::showContextMenuAt(Frame& frame, const IntPoint& clickPoint) { clearContextMenu(); // Simulate a click in the middle of the accessibility object. - PlatformMouseEvent mouseEvent(clickPoint, clickPoint, RightButton, PlatformEvent::MousePressed, 1, false, false, false, false, currentTime()); - frame->eventHandler().handleMousePressEvent(mouseEvent); - bool handled = frame->eventHandler().sendContextMenuEvent(mouseEvent); + PlatformMouseEvent mouseEvent(clickPoint, clickPoint, RightButton, PlatformEvent::MousePressed, 1, false, false, false, false, currentTime(), ForceAtClick, NoTap); + frame.eventHandler().handleMousePressEvent(mouseEvent); + bool handled = frame.eventHandler().sendContextMenuEvent(mouseEvent); if (handled) m_client.showContextMenu(); } + +#endif + +#if ENABLE(SERVICE_CONTROLS) + +void ContextMenuController::showImageControlsMenu(Event& event) +{ + clearContextMenu(); + handleContextMenuEvent(event); + m_client.showContextMenu(); +} + #endif } // namespace WebCore diff --git a/Source/WebCore/page/ContextMenuController.h b/Source/WebCore/page/ContextMenuController.h index fef13537d..f639648b1 100644 --- a/Source/WebCore/page/ContextMenuController.h +++ b/Source/WebCore/page/ContextMenuController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,77 +23,78 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ContextMenuController_h -#define ContextMenuController_h +#pragma once #if ENABLE(CONTEXT_MENUS) -#include "HitTestResult.h" -#include -#include -#include -#include +#include "ContextMenuContext.h" +#include "ContextMenuItem.h" namespace WebCore { - class ContextMenu; - class ContextMenuClient; - class ContextMenuItem; - class ContextMenuProvider; - class Event; - class Page; +class ContextMenuClient; +class ContextMenuProvider; +class Event; +class Page; - class ContextMenuController { - WTF_MAKE_NONCOPYABLE(ContextMenuController); WTF_MAKE_FAST_ALLOCATED; - public: - ContextMenuController(Page&, ContextMenuClient&); - ~ContextMenuController(); +class ContextMenuController { + WTF_MAKE_FAST_ALLOCATED; +public: + ContextMenuController(Page&, ContextMenuClient&); + ~ContextMenuController(); - ContextMenu* contextMenu() const { return m_contextMenu.get(); } - void clearContextMenu(); + Page& page() { return m_page; } + ContextMenuClient& client() { return m_client; } - void handleContextMenuEvent(Event*); - void showContextMenu(Event*, PassRefPtr); + ContextMenu* contextMenu() const { return m_contextMenu.get(); } + WEBCORE_EXPORT void clearContextMenu(); - void populate(); - void contextMenuItemSelected(ContextMenuItem*); - void addInspectElementItem(); + void handleContextMenuEvent(Event&); + void showContextMenu(Event&, ContextMenuProvider&); - void checkOrEnableIfNeeded(ContextMenuItem&) const; + void populate(); + WEBCORE_EXPORT void contextMenuItemSelected(ContextMenuAction, const String& title); + void addInspectElementItem(); - void setHitTestResult(const HitTestResult& result) { m_hitTestResult = result; } - const HitTestResult& hitTestResult() { return m_hitTestResult; } + WEBCORE_EXPORT void checkOrEnableIfNeeded(ContextMenuItem&) const; + + void setContextMenuContext(const ContextMenuContext& context) { m_context = context; } + const ContextMenuContext& context() const { return m_context; } + const HitTestResult& hitTestResult() const { return m_context.hitTestResult(); } #if USE(ACCESSIBILITY_CONTEXT_MENUS) - void showContextMenuAt(Frame*, const IntPoint& clickPoint); + void showContextMenuAt(Frame&, const IntPoint& clickPoint); +#endif + +#if ENABLE(SERVICE_CONTROLS) + void showImageControlsMenu(Event&); #endif - private: - PassOwnPtr createContextMenu(Event*); - void showContextMenu(Event*); - - void appendItem(ContextMenuItem&, ContextMenu* parentMenu); - - void createAndAppendFontSubMenu(ContextMenuItem&); - void createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem&); - void createAndAppendSpellingSubMenu(ContextMenuItem&); - void createAndAppendSpeechSubMenu(ContextMenuItem& ); - void createAndAppendWritingDirectionSubMenu(ContextMenuItem&); - void createAndAppendTextDirectionSubMenu(ContextMenuItem&); - void createAndAppendSubstitutionsSubMenu(ContextMenuItem&); - void createAndAppendTransformationsSubMenu(ContextMenuItem&); +private: + std::unique_ptr maybeCreateContextMenu(Event&); + void showContextMenu(Event&); + + void appendItem(ContextMenuItem&, ContextMenu* parentMenu); + + void createAndAppendFontSubMenu(ContextMenuItem&); + void createAndAppendSpellingAndGrammarSubMenu(ContextMenuItem&); + void createAndAppendSpellingSubMenu(ContextMenuItem&); + void createAndAppendSpeechSubMenu(ContextMenuItem&); + void createAndAppendWritingDirectionSubMenu(ContextMenuItem&); + void createAndAppendTextDirectionSubMenu(ContextMenuItem&); + void createAndAppendSubstitutionsSubMenu(ContextMenuItem&); + void createAndAppendTransformationsSubMenu(ContextMenuItem&); #if PLATFORM(GTK) - void createAndAppendUnicodeSubMenu(ContextMenuItem&); + void createAndAppendUnicodeSubMenu(ContextMenuItem&); #endif - Page& m_page; - ContextMenuClient& m_client; - OwnPtr m_contextMenu; - RefPtr m_menuProvider; - HitTestResult m_hitTestResult; - }; + Page& m_page; + ContextMenuClient& m_client; + std::unique_ptr m_contextMenu; + RefPtr m_menuProvider; + ContextMenuContext m_context; +}; -} +} // namespace WebCore #endif // ENABLE(CONTEXT_MENUS) -#endif diff --git a/Source/WebCore/page/ContextMenuProvider.h b/Source/WebCore/page/ContextMenuProvider.h index 57598d1bc..efdc4746d 100644 --- a/Source/WebCore/page/ContextMenuProvider.h +++ b/Source/WebCore/page/ContextMenuProvider.h @@ -28,25 +28,22 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ContextMenuProvider_h -#define ContextMenuProvider_h +#pragma once +#include "ContextMenuItem.h" #include namespace WebCore { class ContextMenu; -class ContextMenuItem; class ContextMenuProvider : public RefCounted { public: virtual ~ContextMenuProvider() { }; virtual void populateContextMenu(ContextMenu*) = 0; - virtual void contextMenuItemSelected(ContextMenuItem*) = 0; + virtual void contextMenuItemSelected(ContextMenuAction, const String& title) = 0; virtual void contextMenuCleared() = 0; }; -} - -#endif // ContextMenuProvider_h +} // namespace WebCore diff --git a/Source/WebCore/page/Crypto.cpp b/Source/WebCore/page/Crypto.cpp index 335873051..5bb80925f 100644 --- a/Source/WebCore/page/Crypto.cpp +++ b/Source/WebCore/page/Crypto.cpp @@ -34,22 +34,17 @@ #include "Document.h" #include "ExceptionCode.h" #include "SubtleCrypto.h" +#include "WebKitSubtleCrypto.h" #include #include namespace WebCore { -namespace { - -bool isIntegerArray(ArrayBufferView* array) -{ - return JSC::isInt(array->getType()); -} - -} - -Crypto::Crypto(Document& document) - : ContextDestructionObserver(&document) +Crypto::Crypto(ScriptExecutionContext& context) + : ContextDestructionObserver(&context) +#if ENABLE(SUBTLE_CRYPTO) + , m_subtle(SubtleCrypto::create(context)) +#endif { } @@ -57,33 +52,36 @@ Crypto::~Crypto() { } -Document* Crypto::document() const +ExceptionOr Crypto::getRandomValues(ArrayBufferView& array) { - return toDocument(scriptExecutionContext()); + if (!isInt(array.getType())) + return Exception { TYPE_MISMATCH_ERR }; + if (array.byteLength() > 65536) + return Exception { QUOTA_EXCEEDED_ERR }; + cryptographicallyRandomValues(array.baseAddress(), array.byteLength()); + return { }; } -void Crypto::getRandomValues(ArrayBufferView* array, ExceptionCode& ec) +#if ENABLE(SUBTLE_CRYPTO) + +SubtleCrypto& Crypto::subtle() { - if (!array || !isIntegerArray(array)) { - ec = TYPE_MISMATCH_ERR; - return; - } - if (array->byteLength() > 65536) { - ec = QUOTA_EXCEEDED_ERR; - return; - } - cryptographicallyRandomValues(array->baseAddress(), array->byteLength()); + return m_subtle; } -#if ENABLE(SUBTLE_CRYPTO) -SubtleCrypto* Crypto::subtle() +ExceptionOr Crypto::webkitSubtle() { - ASSERT(isMainThread()); - if (!m_subtle) - m_subtle = SubtleCrypto::create(*document()); + if (!isMainThread()) + return Exception { NOT_SUPPORTED_ERR }; + + if (!m_webkitSubtle) { + m_webkitSubtle = WebKitSubtleCrypto::create(*downcast(scriptExecutionContext())); + scriptExecutionContext()->addConsoleMessage(MessageSource::Other, MessageLevel::Warning, ASCIILiteral("WebKitSubtleCrypto is deprecated. Please use SubtleCrypto instead.")); + } - return m_subtle.get(); + return *m_webkitSubtle; } + #endif } diff --git a/Source/WebCore/page/Crypto.h b/Source/WebCore/page/Crypto.h index 94eab5678..9b480e054 100644 --- a/Source/WebCore/page/Crypto.h +++ b/Source/WebCore/page/Crypto.h @@ -27,14 +27,10 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Crypto_h -#define Crypto_h +#pragma once #include "ContextDestructionObserver.h" -#include -#include -#include -#include +#include "ExceptionOr.h" namespace JSC { class ArrayBufferView; @@ -42,32 +38,32 @@ class ArrayBufferView; namespace WebCore { -typedef int ExceptionCode; - -class Document; class SubtleCrypto; +class WebKitSubtleCrypto; class Crypto : public ContextDestructionObserver, public RefCounted { public: - static PassRefPtr create(Document& document) { return adoptRef(new Crypto(document)); } + static Ref create(ScriptExecutionContext& context) { return adoptRef(*new Crypto(context)); } virtual ~Crypto(); - Document* document() const; - - void getRandomValues(JSC::ArrayBufferView*, ExceptionCode&); + ExceptionOr getRandomValues(JSC::ArrayBufferView&); #if ENABLE(SUBTLE_CRYPTO) - SubtleCrypto* subtle(); + SubtleCrypto& subtle(); + + // Will be deprecated. + ExceptionOr webkitSubtle(); #endif private: - Crypto(Document&); + Crypto(ScriptExecutionContext&); #if ENABLE(SUBTLE_CRYPTO) - RefPtr m_subtle; + Ref m_subtle; + + // Will be deprecated. + RefPtr m_webkitSubtle; #endif }; } - -#endif diff --git a/Source/WebCore/page/Crypto.idl b/Source/WebCore/page/Crypto.idl index 4e63ecf09..05dbee1f4 100644 --- a/Source/WebCore/page/Crypto.idl +++ b/Source/WebCore/page/Crypto.idl @@ -28,10 +28,12 @@ */ [ - NoInterfaceObject, - GenerateIsReachable=ImplDocument, + Exposed=(Window,Worker), + GenerateIsReachable=ImplScriptExecutionContext, ] interface Crypto { - [Custom, RaisesException] ArrayBufferView getRandomValues(ArrayBufferView array); + [Conditional=SUBTLE_CRYPTO, EnabledAtRuntime=SubtleCrypto] readonly attribute SubtleCrypto subtle; + [Custom, MayThrowException] ArrayBufferView getRandomValues(ArrayBufferView array); - [Conditional=SUBTLE_CRYPTO, ImplementedAs=subtle] readonly attribute SubtleCrypto webkitSubtle; + // Will be deprecated. + [Conditional=SUBTLE_CRYPTO, GetterMayThrowException] readonly attribute WebKitSubtleCrypto webkitSubtle; }; diff --git a/Source/WebCore/page/DOMSecurityPolicy.cpp b/Source/WebCore/page/DOMSecurityPolicy.cpp deleted file mode 100644 index 7a264ee4a..000000000 --- a/Source/WebCore/page/DOMSecurityPolicy.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``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 APPLE COMPUTER, INC. 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 "config.h" -#include "DOMSecurityPolicy.h" - -#include "ContentSecurityPolicy.h" -#include "ContextDestructionObserver.h" -#include "DOMStringList.h" -#include "Frame.h" -#include "ScriptCallStack.h" -#include "ScriptExecutionContext.h" -#include -#include - -namespace WebCore { - -namespace { - -bool isPolicyActiveInContext(ScriptExecutionContext* context) -{ - // If the ScriptExecutionContext has been destroyed, there's no active policy. - if (!context) - return false; - - return context->contentSecurityPolicy()->isActive(); -} - -template -bool isAllowedWithType(ScriptExecutionContext* context, const String& type) -{ - if (!isPolicyActiveInContext(context)) - return true; - - return (context->contentSecurityPolicy()->*allowWithType)(type, type, URL(), ContentSecurityPolicy::SuppressReport); -} - -template -bool isAllowedWithURL(ScriptExecutionContext* context, const String& url) -{ - if (!isPolicyActiveInContext(context)) - return true; - - URL parsedURL = context->completeURL(url); - if (!parsedURL.isValid()) - return false; // FIXME: Figure out how to throw a JavaScript error. - - return (context->contentSecurityPolicy()->*allowWithURL)(parsedURL, ContentSecurityPolicy::SuppressReport); -} - -template -bool isAllowed(ScriptExecutionContext* context) -{ - if (!isPolicyActiveInContext(context)) - return true; - - return (context->contentSecurityPolicy()->*allowWithContext)(String(), WTF::OrdinalNumber::beforeFirst(), ContentSecurityPolicy::SuppressReport); -} - -} // namespace - -DOMSecurityPolicy::DOMSecurityPolicy(ScriptExecutionContext* context) - : ContextDestructionObserver(context) -{ -} - -DOMSecurityPolicy::~DOMSecurityPolicy() -{ -} - -bool DOMSecurityPolicy::isActive() const -{ - return isPolicyActiveInContext(scriptExecutionContext()); -} - -PassRefPtr DOMSecurityPolicy::reportURIs() const -{ - RefPtr result = DOMStringList::create(); - - if (isActive()) - scriptExecutionContext()->contentSecurityPolicy()->gatherReportURIs(*result.get()); - - return result.release(); -} - -bool DOMSecurityPolicy::allowsInlineScript() const -{ - return isAllowed<&ContentSecurityPolicy::allowInlineScript>(scriptExecutionContext()); -} - -bool DOMSecurityPolicy::allowsInlineStyle() const -{ - return isAllowed<&ContentSecurityPolicy::allowInlineStyle>(scriptExecutionContext()); -} - -bool DOMSecurityPolicy::allowsEval() const -{ - if (!isActive()) - return true; - - return scriptExecutionContext()->contentSecurityPolicy()->allowEval(0, ContentSecurityPolicy::SuppressReport); -} - - -bool DOMSecurityPolicy::allowsConnectionTo(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowConnectToSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsFontFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowFontFromSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsFormAction(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowFormAction>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsFrameFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowChildFrameFromSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsImageFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowImageFromSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsMediaFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowMediaFromSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsObjectFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowObjectFromSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsPluginType(const String& type) const -{ - return isAllowedWithType<&ContentSecurityPolicy::allowPluginType>(scriptExecutionContext(), type); -} - -bool DOMSecurityPolicy::allowsScriptFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowScriptFromSource>(scriptExecutionContext(), url); -} - -bool DOMSecurityPolicy::allowsStyleFrom(const String& url) const -{ - return isAllowedWithURL<&ContentSecurityPolicy::allowStyleFromSource>(scriptExecutionContext(), url); -} - -} // namespace WebCore diff --git a/Source/WebCore/page/DOMSecurityPolicy.h b/Source/WebCore/page/DOMSecurityPolicy.h deleted file mode 100644 index 97dbae11a..000000000 --- a/Source/WebCore/page/DOMSecurityPolicy.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2011 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``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 APPLE COMPUTER, INC. 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. - */ - -#ifndef DOMSecurityPolicy_h -#define DOMSecurityPolicy_h - -#include "ContextDestructionObserver.h" -#include -#include -#include -#include - -namespace WebCore { - -class ContentSecurityPolicy; -class DOMStringList; -class Frame; - -class DOMSecurityPolicy : public RefCounted, public ContextDestructionObserver { -public: - static PassRefPtr create(ScriptExecutionContext* context) - { - return adoptRef(new DOMSecurityPolicy(context)); - } - ~DOMSecurityPolicy(); - - bool isActive() const; - PassRefPtr reportURIs() const; - - bool allowsInlineScript() const; - bool allowsInlineStyle() const; - bool allowsEval() const; - - bool allowsConnectionTo(const String& url) const; - bool allowsFontFrom(const String& url) const; - bool allowsFormAction(const String& url) const; - bool allowsFrameFrom(const String& url) const; - bool allowsImageFrom(const String& url) const; - bool allowsMediaFrom(const String& url) const; - bool allowsObjectFrom(const String& url) const; - bool allowsPluginType(const String& type) const; - bool allowsScriptFrom(const String& url) const; - bool allowsStyleFrom(const String& url) const; - -private: - explicit DOMSecurityPolicy(ScriptExecutionContext*); -}; - -} - -#endif diff --git a/Source/WebCore/page/DOMSecurityPolicy.idl b/Source/WebCore/page/DOMSecurityPolicy.idl deleted file mode 100644 index fe473b35d..000000000 --- a/Source/WebCore/page/DOMSecurityPolicy.idl +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. - */ - -[ - Conditional=CSP_NEXT, - InterfaceName=SecurityPolicy, -] interface DOMSecurityPolicy { - readonly attribute boolean allowsEval; - readonly attribute boolean allowsInlineScript; - readonly attribute boolean allowsInlineStyle; - readonly attribute boolean isActive; - - readonly attribute DOMStringList reportURIs; - - boolean allowsConnectionTo(DOMString url); - boolean allowsFontFrom(DOMString url); - boolean allowsFormAction(DOMString url); - boolean allowsFrameFrom(DOMString url); - boolean allowsImageFrom(DOMString url); - boolean allowsMediaFrom(DOMString url); - boolean allowsObjectFrom(DOMString url); - boolean allowsPluginType(DOMString type); - boolean allowsScriptFrom(DOMString url); - boolean allowsStyleFrom(DOMString url); -}; diff --git a/Source/WebCore/page/DOMSelection.cpp b/Source/WebCore/page/DOMSelection.cpp index b0875d97c..4a1bd66b2 100644 --- a/Source/WebCore/page/DOMSelection.cpp +++ b/Source/WebCore/page/DOMSelection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009, 2016 Apple Inc. All rights reserved. * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -35,38 +35,28 @@ #include "ExceptionCode.h" #include "Frame.h" #include "FrameSelection.h" -#include "Node.h" #include "Range.h" #include "TextIterator.h" -#include "TreeScope.h" #include "htmlediting.h" -#include namespace WebCore { -static Node* selectionShadowAncestor(Frame* frame) +static Node* selectionShadowAncestor(Frame& frame) { - Node* node = frame->selection().selection().base().anchorNode(); + auto* node = frame.selection().selection().base().anchorNode(); if (!node) - return 0; - + return nullptr; if (!node->isInShadowTree()) - return 0; - - return frame->document()->ancestorInThisScope(node); + return nullptr; + // FIXME: Unclear on why this needs to be the possibly null frame.document() instead of the never null node->document(). + return frame.document()->ancestorNodeInThisScope(node); } -DOMSelection::DOMSelection(const TreeScope* treeScope) - : DOMWindowProperty(treeScope->rootNode()->document().frame()) - , m_treeScope(treeScope) +DOMSelection::DOMSelection(Frame& frame) + : DOMWindowProperty(&frame) { } -void DOMSelection::clearTreeScope() -{ - m_treeScope = 0; -} - const VisibleSelection& DOMSelection::visibleSelection() const { ASSERT(m_frame); @@ -75,13 +65,13 @@ const VisibleSelection& DOMSelection::visibleSelection() const static Position anchorPosition(const VisibleSelection& selection) { - Position anchor = selection.isBaseFirst() ? selection.start() : selection.end(); + auto anchor = selection.isBaseFirst() ? selection.start() : selection.end(); return anchor.parentAnchoredEquivalent(); } static Position focusPosition(const VisibleSelection& selection) { - Position focus = selection.isBaseFirst() ? selection.end() : selection.start(); + auto focus = selection.isBaseFirst() ? selection.end() : selection.start(); return focus.parentAnchoredEquivalent(); } @@ -99,31 +89,27 @@ Node* DOMSelection::anchorNode() const { if (!m_frame) return 0; - return shadowAdjustedNode(anchorPosition(visibleSelection())); } -int DOMSelection::anchorOffset() const +unsigned DOMSelection::anchorOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(anchorPosition(visibleSelection())); } Node* DOMSelection::focusNode() const { if (!m_frame) - return 0; - + return nullptr; return shadowAdjustedNode(focusPosition(visibleSelection())); } -int DOMSelection::focusOffset() const +unsigned DOMSelection::focusOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(focusPosition(visibleSelection())); } @@ -131,15 +117,13 @@ Node* DOMSelection::baseNode() const { if (!m_frame) return 0; - return shadowAdjustedNode(basePosition(visibleSelection())); } -int DOMSelection::baseOffset() const +unsigned DOMSelection::baseOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(basePosition(visibleSelection())); } @@ -147,21 +131,19 @@ Node* DOMSelection::extentNode() const { if (!m_frame) return 0; - return shadowAdjustedNode(extentPosition(visibleSelection())); } -int DOMSelection::extentOffset() const +unsigned DOMSelection::extentOffset() const { if (!m_frame) return 0; - return shadowAdjustedOffset(extentPosition(visibleSelection())); } bool DOMSelection::isCollapsed() const { - if (!m_frame || selectionShadowAncestor(m_frame)) + if (!m_frame || selectionShadowAncestor(*m_frame)) return true; return !m_frame->selection().isRange(); } @@ -169,72 +151,53 @@ bool DOMSelection::isCollapsed() const String DOMSelection::type() const { if (!m_frame) - return String(); - - FrameSelection& selection = m_frame->selection(); - - // This is a WebKit DOM extension, incompatible with an IE extension - // IE has this same attribute, but returns "none", "text" and "control" - // http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx + return ASCIILiteral("None"); + auto& selection = m_frame->selection(); if (selection.isNone()) - return "None"; + return ASCIILiteral("None"); if (selection.isCaret()) - return "Caret"; - return "Range"; + return ASCIILiteral("Caret"); + return ASCIILiteral("Range"); } -int DOMSelection::rangeCount() const +unsigned DOMSelection::rangeCount() const { - if (!m_frame) - return 0; - return m_frame->selection().isNone() ? 0 : 1; + return !m_frame || m_frame->selection().isNone() ? 0 : 1; } -void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec) +void DOMSelection::collapse(Node* node, unsigned offset) { - if (!m_frame) - return; - - if (offset < 0) { - ec = INDEX_SIZE_ERR; - return; - } - if (!isValidForPosition(node)) return; - // FIXME: Eliminate legacy editing positions - m_frame->selection().moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); + Ref protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM); } -void DOMSelection::collapseToEnd(ExceptionCode& ec) +ExceptionOr DOMSelection::collapseToEnd() { if (!m_frame) - return; - - const VisibleSelection& selection = m_frame->selection().selection(); - - if (selection.isNone()) { - ec = INVALID_STATE_ERR; - return; - } + return { }; + auto& selection = m_frame->selection(); + if (selection.isNone()) + return Exception { INVALID_STATE_ERR }; - m_frame->selection().moveTo(VisiblePosition(selection.end(), DOWNSTREAM)); + Ref protector(*m_frame); + selection.moveTo(selection.selection().end(), DOWNSTREAM); + return { }; } -void DOMSelection::collapseToStart(ExceptionCode& ec) +ExceptionOr DOMSelection::collapseToStart() { if (!m_frame) - return; - - const VisibleSelection& selection = m_frame->selection().selection(); - - if (selection.isNone()) { - ec = INVALID_STATE_ERR; - return; - } + return { }; + auto& selection = m_frame->selection(); + if (selection.isNone()) + return Exception { INVALID_STATE_ERR }; - m_frame->selection().moveTo(VisiblePosition(selection.start(), DOWNSTREAM)); + Ref protector(*m_frame); + selection.moveTo(selection.selection().start(), DOWNSTREAM); + return { }; } void DOMSelection::empty() @@ -244,40 +207,22 @@ void DOMSelection::empty() m_frame->selection().clear(); } -void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec) +void DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset) { - if (!m_frame) - return; - - if (baseOffset < 0 || extentOffset < 0) { - ec = INDEX_SIZE_ERR; - return; - } - if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode)) return; - // FIXME: Eliminate legacy editing positions - VisiblePosition visibleBase = VisiblePosition(createLegacyEditingPosition(baseNode, baseOffset), DOWNSTREAM); - VisiblePosition visibleExtent = VisiblePosition(createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM); - - m_frame->selection().moveTo(visibleBase, visibleExtent); + Ref protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(baseNode, baseOffset), createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM); } -void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec) +void DOMSelection::setPosition(Node* node, unsigned offset) { - if (!m_frame) - return; - if (offset < 0) { - ec = INDEX_SIZE_ERR; - return; - } - if (!isValidForPosition(node)) return; - // FIXME: Eliminate legacy editing positions - m_frame->selection().moveTo(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); + Ref protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM); } void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString) @@ -286,93 +231,84 @@ void DOMSelection::modify(const String& alterString, const String& directionStri return; FrameSelection::EAlteration alter; - if (equalIgnoringCase(alterString, "extend")) + if (equalLettersIgnoringASCIICase(alterString, "extend")) alter = FrameSelection::AlterationExtend; - else if (equalIgnoringCase(alterString, "move")) + else if (equalLettersIgnoringASCIICase(alterString, "move")) alter = FrameSelection::AlterationMove; else return; SelectionDirection direction; - if (equalIgnoringCase(directionString, "forward")) + if (equalLettersIgnoringASCIICase(directionString, "forward")) direction = DirectionForward; - else if (equalIgnoringCase(directionString, "backward")) + else if (equalLettersIgnoringASCIICase(directionString, "backward")) direction = DirectionBackward; - else if (equalIgnoringCase(directionString, "left")) + else if (equalLettersIgnoringASCIICase(directionString, "left")) direction = DirectionLeft; - else if (equalIgnoringCase(directionString, "right")) + else if (equalLettersIgnoringASCIICase(directionString, "right")) direction = DirectionRight; else return; TextGranularity granularity; - if (equalIgnoringCase(granularityString, "character")) + if (equalLettersIgnoringASCIICase(granularityString, "character")) granularity = CharacterGranularity; - else if (equalIgnoringCase(granularityString, "word")) + else if (equalLettersIgnoringASCIICase(granularityString, "word")) granularity = WordGranularity; - else if (equalIgnoringCase(granularityString, "sentence")) + else if (equalLettersIgnoringASCIICase(granularityString, "sentence")) granularity = SentenceGranularity; - else if (equalIgnoringCase(granularityString, "line")) + else if (equalLettersIgnoringASCIICase(granularityString, "line")) granularity = LineGranularity; - else if (equalIgnoringCase(granularityString, "paragraph")) + else if (equalLettersIgnoringASCIICase(granularityString, "paragraph")) granularity = ParagraphGranularity; - else if (equalIgnoringCase(granularityString, "lineboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "lineboundary")) granularity = LineBoundary; - else if (equalIgnoringCase(granularityString, "sentenceboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "sentenceboundary")) granularity = SentenceBoundary; - else if (equalIgnoringCase(granularityString, "paragraphboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "paragraphboundary")) granularity = ParagraphBoundary; - else if (equalIgnoringCase(granularityString, "documentboundary")) + else if (equalLettersIgnoringASCIICase(granularityString, "documentboundary")) granularity = DocumentBoundary; else return; + Ref protector(*m_frame); m_frame->selection().modify(alter, direction, granularity); } -void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) +ExceptionOr DOMSelection::extend(Node& node, unsigned offset) { if (!m_frame) - return; + return { }; + if (offset > (node.offsetInCharacters() ? caretMaxOffset(node) : node.countChildNodes())) + return Exception { INDEX_SIZE_ERR }; + if (!isValidForPosition(&node)) + return { }; - if (!node) { - ec = TYPE_MISMATCH_ERR; - return; - } - - if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) { - ec = INDEX_SIZE_ERR; - return; - } - - if (!isValidForPosition(node)) - return; - - // FIXME: Eliminate legacy editing positions - m_frame->selection().setExtent(VisiblePosition(createLegacyEditingPosition(node, offset), DOWNSTREAM)); + Ref protector(*m_frame); + m_frame->selection().setExtent(createLegacyEditingPosition(&node, offset), DOWNSTREAM); + return { }; } -PassRefPtr DOMSelection::getRangeAt(int index, ExceptionCode& ec) +ExceptionOr> DOMSelection::getRangeAt(unsigned index) { - if (!m_frame) - return 0; + if (index >= rangeCount()) + return Exception { INDEX_SIZE_ERR }; - if (index < 0 || index >= rangeCount()) { - ec = INDEX_SIZE_ERR; - return 0; - } - - // If you're hitting this, you've added broken multi-range selection support + // If you're hitting this, you've added broken multi-range selection support. ASSERT(rangeCount() == 1); - if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) { - ContainerNode* container = shadowAncestor->parentNodeGuaranteedHostFree(); - int offset = shadowAncestor->nodeIndex(); + if (auto* shadowAncestor = selectionShadowAncestor(*m_frame)) { + auto* container = shadowAncestor->parentNodeGuaranteedHostFree(); + unsigned offset = shadowAncestor->computeNodeIndex(); return Range::create(shadowAncestor->document(), container, offset, container, offset); } - const VisibleSelection& selection = m_frame->selection().selection(); - return selection.firstRange(); + auto firstRange = m_frame->selection().selection().firstRange(); + ASSERT(firstRange); + if (!firstRange) + return Exception { INDEX_SIZE_ERR }; + return firstRange.releaseNonNull(); } void DOMSelection::removeAllRanges() @@ -382,41 +318,49 @@ void DOMSelection::removeAllRanges() m_frame->selection().clear(); } -void DOMSelection::addRange(Range* r) +void DOMSelection::addRange(Range& range) { if (!m_frame) return; - if (!r) - return; - FrameSelection& selection = m_frame->selection(); + Ref protector(*m_frame); + auto& selection = m_frame->selection(); if (selection.isNone()) { - selection.setSelection(VisibleSelection(r)); + selection.moveTo(&range); return; } - RefPtr range = selection.selection().toNormalizedRange(); - if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), IGNORE_EXCEPTION) == -1) { - // We don't support discontiguous selection. We don't do anything if r and range don't intersect. - if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), IGNORE_EXCEPTION) > -1) { - if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), IGNORE_EXCEPTION) == -1) - // The original range and r intersect. - selection.setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM)); - else - // r contains the original range. - selection.setSelection(VisibleSelection(r)); + auto normalizedRange = selection.selection().toNormalizedRange(); + if (!normalizedRange) + return; + + auto result = range.compareBoundaryPoints(Range::START_TO_START, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() == -1) { + // We don't support discontiguous selection. We don't do anything if the two ranges don't intersect. + result = range.compareBoundaryPoints(Range::START_TO_END, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() > -1) { + result = range.compareBoundaryPoints(Range::END_TO_END, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() == -1) { + // The ranges intersect. + selection.moveTo(range.startPosition(), normalizedRange->endPosition(), DOWNSTREAM); + } else { + // The new range contains the original range. + selection.moveTo(&range); + } } } else { - // We don't support discontiguous selection. We don't do anything if r and range don't intersect. - ExceptionCode ec = 0; - if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1 && !ec) { - if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), IGNORE_EXCEPTION) == -1) - // The original range contains r. - selection.setSelection(VisibleSelection(range.get())); - else - // The original range and r intersect. - selection.setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM)); + // We don't support discontiguous selection. We don't do anything if the two ranges don't intersect. + result = range.compareBoundaryPoints(Range::END_TO_START, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() < 1) { + result = range.compareBoundaryPoints(Range::END_TO_END, *normalizedRange); + if (!result.hasException() && result.releaseReturnValue() == -1) { + // The original range contains the new range. + selection.moveTo(normalizedRange.get()); + } else { + // The ranges intersect. + selection.moveTo(normalizedRange->startPosition(), range.endPosition(), DOWNSTREAM); + } } } } @@ -426,84 +370,79 @@ void DOMSelection::deleteFromDocument() if (!m_frame) return; - FrameSelection& selection = m_frame->selection(); - + auto& selection = m_frame->selection(); if (selection.isNone()) return; - if (isCollapsed()) - selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity); - - RefPtr selectedRange = selection.selection().toNormalizedRange(); - if (!selectedRange) + auto selectedRange = selection.selection().toNormalizedRange(); + if (!selectedRange || selectedRange->shadowRoot()) return; - selectedRange->deleteContents(ASSERT_NO_EXCEPTION); - - setBaseAndExtent(selectedRange->startContainer(ASSERT_NO_EXCEPTION), selectedRange->startOffset(), selectedRange->startContainer(), selectedRange->startOffset(), ASSERT_NO_EXCEPTION); + Ref protector(*m_frame); + selectedRange->deleteContents(); + setBaseAndExtent(&selectedRange->startContainer(), selectedRange->startOffset(), &selectedRange->startContainer(), selectedRange->startOffset()); } -bool DOMSelection::containsNode(Node* n, bool allowPartial) const +bool DOMSelection::containsNode(Node& node, bool allowPartial) const { if (!m_frame) return false; - FrameSelection& selection = m_frame->selection(); - - if (!n || m_frame->document() != &n->document() || selection.isNone()) + auto& selection = m_frame->selection(); + if (m_frame->document() != &node.document() || selection.isNone()) return false; - RefPtr node = n; - RefPtr selectedRange = selection.selection().toNormalizedRange(); + Ref protectedNode(node); + auto selectedRange = selection.selection().toNormalizedRange(); - ContainerNode* parentNode = node->parentNode(); - if (!parentNode || !parentNode->inDocument()) + ContainerNode* parentNode = node.parentNode(); + if (!parentNode || !parentNode->isConnected()) return false; - unsigned nodeIndex = node->nodeIndex(); - - ExceptionCode ec = 0; - bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(), selectedRange->startOffset(), ec) >= 0 && !ec - && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(), selectedRange->endOffset(), ec) <= 0 && !ec; - ASSERT(!ec); - if (nodeFullySelected) + unsigned nodeIndex = node.computeNodeIndex(); + + auto startsResult = Range::compareBoundaryPoints(parentNode, nodeIndex, &selectedRange->startContainer(), selectedRange->startOffset()); + ASSERT(!startsResult.hasException()); + auto endsResult = Range::compareBoundaryPoints(parentNode, nodeIndex + 1, &selectedRange->endContainer(), selectedRange->endOffset()); + ASSERT(!endsResult.hasException()); + bool isNodeFullySelected = !startsResult.hasException() && startsResult.releaseReturnValue() >= 0 + && !endsResult.hasException() && endsResult.releaseReturnValue() <= 0; + if (isNodeFullySelected) return true; - bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(), selectedRange->endOffset(), ec) > 0 && !ec) - || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(), selectedRange->startOffset(), ec) < 0 && !ec); - ASSERT(!ec); - if (nodeFullyUnselected) + auto startEndResult = Range::compareBoundaryPoints(parentNode, nodeIndex, &selectedRange->endContainer(), selectedRange->endOffset()); + ASSERT(!startEndResult.hasException()); + auto endStartResult = Range::compareBoundaryPoints(parentNode, nodeIndex + 1, &selectedRange->startContainer(), selectedRange->startOffset()); + ASSERT(!endStartResult.hasException()); + bool isNodeFullyUnselected = (!startEndResult.hasException() && startEndResult.releaseReturnValue() > 0) + || (!endStartResult.hasException() && endStartResult.releaseReturnValue() < 0); + if (isNodeFullyUnselected) return false; - return allowPartial || node->isTextNode(); + return allowPartial || node.isTextNode(); } -void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec) +void DOMSelection::selectAllChildren(Node& node) { - if (!n) - return; - // This doesn't (and shouldn't) select text node characters. - setBaseAndExtent(n, 0, n, n->childNodeCount(), ec); + setBaseAndExtent(&node, 0, &node, node.countChildNodes()); } String DOMSelection::toString() { if (!m_frame) return String(); - return plainText(m_frame->selection().selection().toNormalizedRange().get()); } Node* DOMSelection::shadowAdjustedNode(const Position& position) const { if (position.isNull()) - return 0; - - Node* containerNode = position.containerNode(); - Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode); + return nullptr; + auto* containerNode = position.containerNode(); + auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode); if (!adjustedNode) - return 0; + return nullptr; if (containerNode == adjustedNode) return containerNode; @@ -511,26 +450,26 @@ Node* DOMSelection::shadowAdjustedNode(const Position& position) const return adjustedNode->parentNodeGuaranteedHostFree(); } -int DOMSelection::shadowAdjustedOffset(const Position& position) const +unsigned DOMSelection::shadowAdjustedOffset(const Position& position) const { if (position.isNull()) return 0; - Node* containerNode = position.containerNode(); - Node* adjustedNode = m_treeScope->ancestorInThisScope(containerNode); - + auto* containerNode = position.containerNode(); + auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode); if (!adjustedNode) return 0; if (containerNode == adjustedNode) return position.computeOffsetInContainerNode(); - return adjustedNode->nodeIndex(); + return adjustedNode->computeNodeIndex(); } bool DOMSelection::isValidForPosition(Node* node) const { - ASSERT(m_frame); + if (!m_frame) + return false; if (!node) return true; return &node->document() == m_frame->document(); diff --git a/Source/WebCore/page/DOMSelection.h b/Source/WebCore/page/DOMSelection.h index 1dda6abea..65836a9c8 100644 --- a/Source/WebCore/page/DOMSelection.h +++ b/Source/WebCore/page/DOMSelection.h @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2012 Google Inc. All rights reserved. + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 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 @@ -11,7 +11,7 @@ * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,85 +27,68 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#ifndef DOMSelection_h -#define DOMSelection_h +#pragma once #include "DOMWindowProperty.h" +#include "ExceptionOr.h" #include -#include #include +#include namespace WebCore { - class Frame; - class Node; - class Position; - class Range; - class TreeScope; - class VisibleSelection; - - typedef int ExceptionCode; - - class DOMSelection : public RefCounted, public DOMWindowProperty { - public: - static PassRefPtr create(const TreeScope* treeScope) { return adoptRef(new DOMSelection(treeScope)); } - - void clearTreeScope(); - - // Safari Selection Object API - // These methods return the valid equivalents of internal editing positions. - Node* baseNode() const; - Node* extentNode() const; - int baseOffset() const; - int extentOffset() const; - String type() const; - void setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode&); - void setPosition(Node*, int offset, ExceptionCode&); - void modify(const String& alter, const String& direction, const String& granularity); - - // Mozilla Selection Object API - // In Firefox, anchor/focus are the equal to the start/end of the selection, - // but reflect the direction in which the selection was made by the user. That does - // not mean that they are base/extent, since the base/extent don't reflect - // expansion. - // These methods return the valid equivalents of internal editing positions. - Node* anchorNode() const; - int anchorOffset() const; - Node* focusNode() const; - int focusOffset() const; - bool isCollapsed() const; - int rangeCount() const; - void collapse(Node*, int offset, ExceptionCode&); - void collapseToEnd(ExceptionCode&); - void collapseToStart(ExceptionCode&); - void extend(Node*, int offset, ExceptionCode&); - PassRefPtr getRangeAt(int, ExceptionCode&); - void removeAllRanges(); - void addRange(Range*); - void deleteFromDocument(); - bool containsNode(Node*, bool partlyContained) const; - void selectAllChildren(Node*, ExceptionCode&); - - String toString(); - - // Microsoft Selection Object API - void empty(); - - private: - const TreeScope* m_treeScope; - - explicit DOMSelection(const TreeScope*); - - // Convenience method for accessors, does not NULL check m_frame. - const VisibleSelection& visibleSelection() const; - - Node* shadowAdjustedNode(const Position&) const; - int shadowAdjustedOffset(const Position&) const; - - bool isValidForPosition(Node*) const; - }; +class Node; +class Position; +class Range; +class VisibleSelection; + +class DOMSelection : public RefCounted, public DOMWindowProperty { +public: + static Ref create(Frame& frame) { return adoptRef(*new DOMSelection(frame)); } + + Node* baseNode() const; + Node* extentNode() const; + unsigned baseOffset() const; + unsigned extentOffset() const; + String type() const; + void setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset); + void setPosition(Node*, unsigned offset); + void modify(const String& alter, const String& direction, const String& granularity); + + // The anchor and focus are the start and end of the selection, and + // reflect the direction in which the selection was made by the user. + // The base and extent are different, because they don't reflect expansion. + Node* anchorNode() const; + unsigned anchorOffset() const; + Node* focusNode() const; + unsigned focusOffset() const; + bool isCollapsed() const; + unsigned rangeCount() const; + void collapse(Node*, unsigned offset); + ExceptionOr collapseToEnd(); + ExceptionOr collapseToStart(); + ExceptionOr extend(Node&, unsigned offset); + ExceptionOr> getRangeAt(unsigned); + void removeAllRanges(); + void addRange(Range&); + void deleteFromDocument(); + bool containsNode(Node&, bool partlyContained) const; + void selectAllChildren(Node&); + + String toString(); + + void empty(); + +private: + explicit DOMSelection(Frame&); + + // Convenience method for accessors, caller must null-check m_frame. + const VisibleSelection& visibleSelection() const; + + Node* shadowAdjustedNode(const Position&) const; + unsigned shadowAdjustedOffset(const Position&) const; + + bool isValidForPosition(Node*) const; +}; } // namespace WebCore - -#endif // DOMSelection_h diff --git a/Source/WebCore/page/DOMSelection.idl b/Source/WebCore/page/DOMSelection.idl index c1f763f9c..92eb2bf70 100644 --- a/Source/WebCore/page/DOMSelection.idl +++ b/Source/WebCore/page/DOMSelection.idl @@ -1,6 +1,6 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,7 +11,7 @@ * 2. 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -27,63 +27,50 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// This is based off of Mozilla's Selection interface -// https://developer.mozilla.org/En/DOM/Selection +// https://www.w3.org/TR/selection-api/#idl-def-Selection [ GenerateIsReachable=ImplFrame, InterfaceName=Selection, ] interface DOMSelection { - readonly attribute Node anchorNode; - readonly attribute long anchorOffset; - readonly attribute Node focusNode; - readonly attribute long focusOffset; + readonly attribute Node? anchorNode; + readonly attribute unsigned long anchorOffset; + readonly attribute Node? focusNode; + readonly attribute unsigned long focusOffset; readonly attribute boolean isCollapsed; - readonly attribute long rangeCount; + readonly attribute unsigned long rangeCount; - [RaisesException] void collapse([Default=Undefined] optional Node node, - [Default=Undefined] optional long index); - [RaisesException] void collapseToEnd(); - [RaisesException] void collapseToStart(); + void collapse(Node? node, optional unsigned long offset = 0); + [MayThrowException] void collapseToEnd(); + [MayThrowException] void collapseToStart(); - void deleteFromDocument(); - boolean containsNode([Default=Undefined] optional Node node, - [Default=Undefined] optional boolean allowPartial); - [RaisesException] void selectAllChildren([Default=Undefined] optional Node node); + [CEReactions] void deleteFromDocument(); + boolean containsNode(Node node, optional boolean allowPartial = false); + void selectAllChildren(Node node); - [RaisesException] void extend([Default=Undefined] optional Node node, - [Default=Undefined] optional long offset); + [MayThrowException] void extend(Node node, optional unsigned long offset = 0); - [RaisesException] Range getRangeAt([Default=Undefined] optional long index); + [MayThrowException] Range getRangeAt(unsigned long index); void removeAllRanges(); - void addRange([Default=Undefined] optional Range range); + void addRange(Range range); -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT [NotEnumerable] DOMString toString(); -#endif - // WebKit extensions - readonly attribute Node baseNode; - readonly attribute long baseOffset; - readonly attribute Node extentNode; - readonly attribute long extentOffset; - - // WebKit's "type" accessor returns "None", "Range" and "Caret" - // IE's type accessor returns "none", "text" and "control" readonly attribute DOMString type; - void modify([Default=Undefined] optional DOMString alter, - [Default=Undefined] optional DOMString direction, - [Default=Undefined] optional DOMString granularity); - [RaisesException] void setBaseAndExtent([Default=Undefined] optional Node baseNode, - [Default=Undefined] optional long baseOffset, - [Default=Undefined] optional Node extentNode, - [Default=Undefined] optional long extentOffset); - [RaisesException] void setPosition([Default=Undefined] optional Node node, - [Default=Undefined] optional long offset); + void setBaseAndExtent(Node? baseNode, unsigned long baseOffset, Node? extentNode, unsigned long extentOffset); + void setPosition(Node? node, optional unsigned long offset = 0); - // IE extentions - // http://msdn.microsoft.com/en-us/library/ms535869(VS.85).aspx void empty(); -}; + // FIXME: The following operation should be implemented. + // void removeRange(Range range); + + // FIXME: Using "undefined" as default parameter value is wrong. + void modify(optional DOMString alter = "undefined", optional DOMString direction = "undefined", optional DOMString granularity = "undefined"); + + readonly attribute Node? baseNode; + readonly attribute unsigned long baseOffset; + readonly attribute Node? extentNode; + readonly attribute unsigned long extentOffset; +}; diff --git a/Source/WebCore/page/DOMTimer.cpp b/Source/WebCore/page/DOMTimer.cpp index 0c99e0d39..80e1b8d68 100644 --- a/Source/WebCore/page/DOMTimer.cpp +++ b/Source/WebCore/page/DOMTimer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -27,73 +27,191 @@ #include "config.h" #include "DOMTimer.h" +#include "HTMLPlugInElement.h" #include "InspectorInstrumentation.h" +#include "Logging.h" +#include "Page.h" +#include "PluginViewBase.h" #include "ScheduledAction.h" #include "ScriptExecutionContext.h" -#include "UserGestureIndicator.h" +#include "Settings.h" #include -#include +#include +#include +#include +#include #include #if PLATFORM(IOS) #include "Chrome.h" #include "ChromeClient.h" #include "Frame.h" -#include "Page.h" #include "WKContentObservation.h" +#include "WKContentObservationInternal.h" #endif namespace WebCore { -static const int maxIntervalForUserGestureForwarding = 1000; // One second matches Gecko. +static const auto maxIntervalForUserGestureForwarding = 1000ms; // One second matches Gecko. +static const auto minIntervalForNonUserObservableChangeTimers = 1000ms; // Empirically determined to maximize battery life. static const int maxTimerNestingLevel = 5; -static const double oneMillisecond = 0.001; -static int timerNestingLevel = 0; - -static inline bool shouldForwardUserGesture(int interval, int nestingLevel) +class DOMTimerFireState { +public: + explicit DOMTimerFireState(ScriptExecutionContext& context) + : m_context(context) + , m_contextIsDocument(is(m_context)) + { + // For worker threads, don't update the current DOMTimerFireState. + // Setting this from workers would not be thread-safe, and its not relevant to current uses. + if (m_contextIsDocument) { + m_initialDOMTreeVersion = downcast(context).domTreeVersion(); + m_previous = current; + current = this; + } + } + + ~DOMTimerFireState() + { + if (m_contextIsDocument) + current = m_previous; + } + + Document* contextDocument() const { return m_contextIsDocument ? &downcast(m_context) : nullptr; } + + void setScriptMadeUserObservableChanges() { m_scriptMadeUserObservableChanges = true; } + void setScriptMadeNonUserObservableChanges() { m_scriptMadeNonUserObservableChanges = true; } + + bool scriptMadeNonUserObservableChanges() const { return m_scriptMadeNonUserObservableChanges; } + bool scriptMadeUserObservableChanges() const + { + if (m_scriptMadeUserObservableChanges) + return true; + + Document* document = contextDocument(); + // To be conservative, we also consider any DOM Tree change to be user observable. + return document && document->domTreeVersion() != m_initialDOMTreeVersion; + } + + static DOMTimerFireState* current; + +private: + ScriptExecutionContext& m_context; + uint64_t m_initialDOMTreeVersion; + DOMTimerFireState* m_previous; + bool m_contextIsDocument; + bool m_scriptMadeNonUserObservableChanges { false }; + bool m_scriptMadeUserObservableChanges { false }; +}; + +DOMTimerFireState* DOMTimerFireState::current = nullptr; + +struct NestedTimersMap { + typedef HashMap::const_iterator const_iterator; + + static NestedTimersMap* instanceForContext(ScriptExecutionContext& context) + { + // For worker threads, we don't use NestedTimersMap as doing so would not + // be thread safe. + if (is(context)) + return &instance(); + return nullptr; + } + + void startTracking() + { + // Make sure we start with an empty HashMap. In theory, it is possible the HashMap is not + // empty if a timer fires during the execution of another timer (may happen with the + // in-process Web Inspector). + nestedTimers.clear(); + isTrackingNestedTimers = true; + } + + void stopTracking() + { + isTrackingNestedTimers = false; + nestedTimers.clear(); + } + + void add(int timeoutId, DOMTimer* timer) + { + if (isTrackingNestedTimers) + nestedTimers.add(timeoutId, timer); + } + + void remove(int timeoutId) + { + if (isTrackingNestedTimers) + nestedTimers.remove(timeoutId); + } + + const_iterator begin() const { return nestedTimers.begin(); } + const_iterator end() const { return nestedTimers.end(); } + +private: + static NestedTimersMap& instance() + { + static NeverDestroyed map; + return map; + } + + static bool isTrackingNestedTimers; + HashMap nestedTimers; +}; + +bool NestedTimersMap::isTrackingNestedTimers = false; + +static inline bool shouldForwardUserGesture(std::chrono::milliseconds interval, int nestingLevel) { return UserGestureIndicator::processingUserGesture() && interval <= maxIntervalForUserGestureForwarding - && nestingLevel == 1; // Gestures should not be forwarded to nested timers. + && !nestingLevel; // Gestures should not be forwarded to nested timers. +} + +static inline RefPtr userGestureTokenToForward(std::chrono::milliseconds interval, int nestingLevel) +{ + if (!shouldForwardUserGesture(interval, nestingLevel)) + return nullptr; + + return UserGestureIndicator::currentUserGesture(); } -DOMTimer::DOMTimer(ScriptExecutionContext* context, PassOwnPtr action, int interval, bool singleShot) +DOMTimer::DOMTimer(ScriptExecutionContext& context, std::unique_ptr action, std::chrono::milliseconds interval, bool singleShot) : SuspendableTimer(context) - , m_nestingLevel(timerNestingLevel + 1) - , m_action(action) + , m_nestingLevel(context.timerNestingLevel()) + , m_action(WTFMove(action)) , m_originalInterval(interval) - , m_shouldForwardUserGesture(shouldForwardUserGesture(interval, m_nestingLevel)) + , m_throttleState(Undetermined) + , m_currentTimerInterval(intervalClampedToMinimum()) + , m_userGestureTokenToForward(userGestureTokenToForward(interval, m_nestingLevel)) { + RefPtr reference = adoptRef(this); + // Keep asking for the next id until we're given one that we don't already have. do { - m_timeoutId = context->circularSequentialID(); - } while (!context->addTimeout(m_timeoutId, this)); + m_timeoutId = context.circularSequentialID(); + } while (!context.addTimeout(m_timeoutId, *this)); - double intervalMilliseconds = intervalClampedToMinimum(interval, context->minimumTimerInterval()); if (singleShot) - startOneShot(intervalMilliseconds); + startOneShot(m_currentTimerInterval); else - startRepeating(intervalMilliseconds); + startRepeating(m_currentTimerInterval); } DOMTimer::~DOMTimer() { - if (scriptExecutionContext()) - scriptExecutionContext()->removeTimeout(m_timeoutId); } -int DOMTimer::install(ScriptExecutionContext* context, PassOwnPtr action, int timeout, bool singleShot) +int DOMTimer::install(ScriptExecutionContext& context, std::unique_ptr action, std::chrono::milliseconds timeout, bool singleShot) { - // DOMTimer constructor links the new timer into a list of ActiveDOMObjects held by the 'context'. - // The timer is deleted when context is deleted (DOMTimer::contextDestroyed) or explicitly via DOMTimer::removeById(), - // or if it is a one-time timer and it has fired (DOMTimer::fired). - DOMTimer* timer = new DOMTimer(context, action, timeout, singleShot); + // DOMTimer constructor passes ownership of the initial ref on the object to the constructor. + // This reference will be released automatically when a one-shot timer fires, when the context + // is destroyed, or if explicitly cancelled by removeById. + DOMTimer* timer = new DOMTimer(context, WTFMove(action), timeout, singleShot); #if PLATFORM(IOS) - if (context->isDocument()) { - Document& document = toDocument(*context); - bool didDeferTimeout = document.frame() && document.frame()->timersPaused(); - if (!didDeferTimeout && timeout <= 100 && singleShot) { + if (is(context)) { + bool didDeferTimeout = context.activeDOMObjectsAreSuspended(); + if (!didDeferTimeout && timeout.count() <= 100 && singleShot) { WKSetObservedContentChange(WKContentIndeterminateChange); WebThreadAddObservedContentModifier(timer); // Will only take affect if not already visibility change. } @@ -103,10 +221,14 @@ int DOMTimer::install(ScriptExecutionContext* context, PassOwnPtrsuspendIfNeeded(); InspectorInstrumentation::didInstallTimer(context, timer->m_timeoutId, timeout, singleShot); + // Keep track of nested timer installs. + if (NestedTimersMap* nestedTimers = NestedTimersMap::instanceForContext(context)) + nestedTimers->add(timer->m_timeoutId, timer); + return timer->m_timeoutId; } -void DOMTimer::removeById(ScriptExecutionContext* context, int timeoutId) +void DOMTimer::removeById(ScriptExecutionContext& context, int timeoutId) { // timeout IDs have to be positive, and 0 and -1 are unsafe to // even look up since they are the empty and deleted value @@ -114,58 +236,105 @@ void DOMTimer::removeById(ScriptExecutionContext* context, int timeoutId) if (timeoutId <= 0) return; + if (NestedTimersMap* nestedTimers = NestedTimersMap::instanceForContext(context)) + nestedTimers->remove(timeoutId); + InspectorInstrumentation::didRemoveTimer(context, timeoutId); + context.removeTimeout(timeoutId); +} - delete context->findTimeout(timeoutId); +inline bool DOMTimer::isDOMTimersThrottlingEnabled(Document& document) const +{ + auto* page = document.page(); + if (!page) + return true; + return page->settings().domTimersThrottlingEnabled(); } -void DOMTimer::fired() +void DOMTimer::updateThrottlingStateIfNecessary(const DOMTimerFireState& fireState) { - ScriptExecutionContext* context = scriptExecutionContext(); - ASSERT(context); -#if PLATFORM(IOS) - Document* document = nullptr; - if (!context->isDocument()) { - document = toDocument(context); - ASSERT(!document->frame()->timersPaused()); + Document* contextDocument = fireState.contextDocument(); + // We don't throttle timers in worker threads. + if (!contextDocument) + return; + + if (UNLIKELY(!isDOMTimersThrottlingEnabled(*contextDocument))) { + if (m_throttleState == ShouldThrottle) { + // Unthrottle the timer in case it was throttled before the setting was updated. + LOG(DOMTimers, "%p - Unthrottling DOM timer because throttling was disabled via settings.", this); + m_throttleState = ShouldNotThrottle; + updateTimerIntervalIfNecessary(); + } + return; } -#endif - timerNestingLevel = m_nestingLevel; + + if (fireState.scriptMadeUserObservableChanges()) { + if (m_throttleState != ShouldNotThrottle) { + m_throttleState = ShouldNotThrottle; + updateTimerIntervalIfNecessary(); + } + } else if (fireState.scriptMadeNonUserObservableChanges()) { + if (m_throttleState != ShouldThrottle) { + m_throttleState = ShouldThrottle; + updateTimerIntervalIfNecessary(); + } + } +} + +void DOMTimer::scriptDidInteractWithPlugin(HTMLPlugInElement& pluginElement) +{ + if (!DOMTimerFireState::current) + return; + + if (pluginElement.isUserObservable()) + DOMTimerFireState::current->setScriptMadeUserObservableChanges(); + else + DOMTimerFireState::current->setScriptMadeNonUserObservableChanges(); +} + +void DOMTimer::fired() +{ + // Retain this - if the timer is cancelled while this function is on the stack (implicitly and always + // for one-shot timers, or if removeById is called on itself from within an interval timer fire) then + // wait unit the end of this function to delete DOMTimer. + RefPtr reference = this; + + ASSERT(scriptExecutionContext()); + ScriptExecutionContext& context = *scriptExecutionContext(); + + DOMTimerFireState fireState(context); + + context.setTimerNestingLevel(std::min(m_nestingLevel + 1, maxTimerNestingLevel)); + ASSERT(!isSuspended()); - ASSERT(!context->activeDOMObjectsAreSuspended()); - UserGestureIndicator gestureIndicator(m_shouldForwardUserGesture ? DefinitelyProcessingUserGesture : PossiblyProcessingUserGesture); + ASSERT(!context.activeDOMObjectsAreSuspended()); + UserGestureIndicator gestureIndicator(m_userGestureTokenToForward); // Only the first execution of a multi-shot timer should get an affirmative user gesture indicator. - m_shouldForwardUserGesture = false; + m_userGestureTokenToForward = nullptr; InspectorInstrumentationCookie cookie = InspectorInstrumentation::willFireTimer(context, m_timeoutId); // Simple case for non-one-shot timers. if (isActive()) { - double minimumInterval = context->minimumTimerInterval(); - if (repeatInterval() && repeatInterval() < minimumInterval) { + if (m_nestingLevel < maxTimerNestingLevel) { m_nestingLevel++; - if (m_nestingLevel >= maxTimerNestingLevel) - augmentRepeatInterval(minimumInterval - repeatInterval()); + updateTimerIntervalIfNecessary(); } - // No access to member variables after this point, it can delete the timer. m_action->execute(context); InspectorInstrumentation::didFireTimer(cookie); + updateThrottlingStateIfNecessary(fireState); return; } - // Delete timer before executing the action for one-shot timers. - OwnPtr action = m_action.release(); - - // No access to member variables after this point. - delete this; + context.removeTimeout(m_timeoutId); #if PLATFORM(IOS) bool shouldReportLackOfChanges; bool shouldBeginObservingChanges; - if (document) { + if (is(context)) { shouldReportLackOfChanges = WebThreadCountOfObservedContentModifiers() == 1; shouldBeginObservingChanges = WebThreadContainsObservedContentModifier(this); } else { @@ -179,27 +348,38 @@ void DOMTimer::fired() } #endif - action->execute(context); + // Keep track nested timer installs. + NestedTimersMap* nestedTimers = NestedTimersMap::instanceForContext(context); + if (nestedTimers) + nestedTimers->startTracking(); + + m_action->execute(context); #if PLATFORM(IOS) if (shouldBeginObservingChanges) { WKStopObservingContentChanges(); - if (WKObservedContentChange() == WKContentVisibilityChange || shouldReportLackOfChanges) - if (document && document->page()) - document->page()->chrome().client().observedContentChange(document->frame()); + if (WKObservedContentChange() == WKContentVisibilityChange || shouldReportLackOfChanges) { + Document& document = downcast(context); + if (Page* page = document.page()) + page->chrome().client().observedContentChange(*document.frame()); + } } #endif InspectorInstrumentation::didFireTimer(cookie); - timerNestingLevel = 0; -} + // Check if we should throttle nested single-shot timers. + if (nestedTimers) { + for (auto& keyValue : *nestedTimers) { + auto* timer = keyValue.value; + if (timer->isActive() && !timer->repeatInterval()) + timer->updateThrottlingStateIfNecessary(fireState); + } + nestedTimers->stopTracking(); + } -void DOMTimer::contextDestroyed() -{ - SuspendableTimer::contextDestroyed(); - delete this; + context.setTimerNestingLevel(0); } void DOMTimer::didStop() @@ -207,48 +387,64 @@ void DOMTimer::didStop() // Need to release JS objects potentially protected by ScheduledAction // because they can form circular references back to the ScriptExecutionContext // which will cause a memory leak. - m_action.clear(); + m_action = nullptr; } -void DOMTimer::adjustMinimumTimerInterval(double oldMinimumTimerInterval) +void DOMTimer::updateTimerIntervalIfNecessary() { - if (m_nestingLevel < maxTimerNestingLevel) - return; + ASSERT(m_nestingLevel <= maxTimerNestingLevel); - double newMinimumInterval = scriptExecutionContext()->minimumTimerInterval(); - double newClampedInterval = intervalClampedToMinimum(m_originalInterval, newMinimumInterval); + auto previousInterval = m_currentTimerInterval; + m_currentTimerInterval = intervalClampedToMinimum(); + if (previousInterval == m_currentTimerInterval) + return; if (repeatInterval()) { - augmentRepeatInterval(newClampedInterval - repeatInterval()); - return; + ASSERT(repeatIntervalMS() == previousInterval); + LOG(DOMTimers, "%p - Updating DOMTimer's repeat interval from %" PRId64 " ms to %" PRId64 " ms due to throttling.", this, previousInterval.count(), m_currentTimerInterval.count()); + augmentRepeatInterval(m_currentTimerInterval - previousInterval); + } else { + LOG(DOMTimers, "%p - Updating DOMTimer's fire interval from %" PRId64 " ms to %" PRId64 " ms due to throttling.", this, previousInterval.count(), m_currentTimerInterval.count()); + augmentFireInterval(m_currentTimerInterval - previousInterval); } - - double previousClampedInterval = intervalClampedToMinimum(m_originalInterval, oldMinimumTimerInterval); - augmentFireInterval(newClampedInterval - previousClampedInterval); } -double DOMTimer::intervalClampedToMinimum(int timeout, double minimumTimerInterval) const +std::chrono::milliseconds DOMTimer::intervalClampedToMinimum() const { - double intervalMilliseconds = std::max(oneMillisecond, timeout * oneMillisecond); + ASSERT(scriptExecutionContext()); + ASSERT(m_nestingLevel <= maxTimerNestingLevel); + + auto interval = std::max(1ms, m_originalInterval); + + // Only apply throttling to repeating timers. + if (m_nestingLevel < maxTimerNestingLevel) + return interval; - if (intervalMilliseconds < minimumTimerInterval && m_nestingLevel >= maxTimerNestingLevel) - intervalMilliseconds = minimumTimerInterval; - return intervalMilliseconds; + // Apply two throttles - the global (per Page) minimum, and also a per-timer throttle. + interval = std::max(interval, scriptExecutionContext()->minimumTimerInterval()); + if (m_throttleState == ShouldThrottle) + interval = std::max(interval, minIntervalForNonUserObservableChangeTimers); + return interval; } -double DOMTimer::alignedFireTime(double fireTime) const +std::optional DOMTimer::alignedFireTime(std::chrono::milliseconds fireTime) const { - double alignmentInterval = scriptExecutionContext()->timerAlignmentInterval(); - if (alignmentInterval) { - double currentTime = monotonicallyIncreasingTime(); - if (fireTime <= currentTime) - return fireTime; + auto alignmentInterval = scriptExecutionContext()->timerAlignmentInterval(m_nestingLevel >= maxTimerNestingLevel); + if (alignmentInterval == 0ms) + return std::nullopt; + + static const double randomizedProportion = randomNumber(); - double alignedTime = ceil(fireTime / alignmentInterval) * alignmentInterval; - return alignedTime; - } + // Force alignment to randomizedAlignment fraction of the way between alignemntIntervals, e.g. + // if alignmentInterval is 10 and randomizedAlignment is 0.3 this will align to 3, 13, 23, ... + auto randomizedOffset = std::chrono::duration_cast(alignmentInterval * randomizedProportion); + auto adjustedFireTime = fireTime - randomizedOffset; + return adjustedFireTime - (adjustedFireTime % alignmentInterval) + alignmentInterval + randomizedOffset; +} - return fireTime; +const char* DOMTimer::activeDOMObjectName() const +{ + return "DOMTimer"; } } // namespace WebCore diff --git a/Source/WebCore/page/DOMTimer.h b/Source/WebCore/page/DOMTimer.h index a47dd5f7c..39a0461da 100644 --- a/Source/WebCore/page/DOMTimer.h +++ b/Source/WebCore/page/DOMTimer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,53 +24,71 @@ * */ -#ifndef DOMTimer_h -#define DOMTimer_h +#pragma once #include "SuspendableTimer.h" -#include -#include +#include "UserGestureIndicator.h" +#include +#include namespace WebCore { - class ScheduledAction; +class DOMTimerFireState; +class Document; +class HTMLPlugInElement; +class ScheduledAction; - class DOMTimer final : public SuspendableTimer { - public: - virtual ~DOMTimer(); - // Creates a new timer owned by specified ScriptExecutionContext, starts it - // and returns its Id. - static int install(ScriptExecutionContext*, PassOwnPtr, int timeout, bool singleShot); - static void removeById(ScriptExecutionContext*, int timeoutId); +class DOMTimer final : public RefCounted, public SuspendableTimer { + WTF_MAKE_NONCOPYABLE(DOMTimer); + WTF_MAKE_FAST_ALLOCATED; +public: + virtual ~DOMTimer(); - // Adjust to a change in the ScriptExecutionContext's minimum timer interval. - // This allows the minimum allowable interval time to be changed in response - // to events like moving a tab to the background. - void adjustMinimumTimerInterval(double oldMinimumTimerInterval); + static std::chrono::milliseconds defaultMinimumInterval() { return 4ms; } + static std::chrono::milliseconds defaultAlignmentInterval() { return 0ms; } + static std::chrono::milliseconds hiddenPageAlignmentInterval() { return 1000ms; } - private: - DOMTimer(ScriptExecutionContext*, PassOwnPtr, int interval, bool singleShot); - virtual void fired() override; + // Creates a new timer owned by specified ScriptExecutionContext, starts it + // and returns its Id. + static int install(ScriptExecutionContext&, std::unique_ptr, std::chrono::milliseconds timeout, bool singleShot); + static void removeById(ScriptExecutionContext&, int timeoutId); - // ActiveDOMObject - virtual void contextDestroyed() override; + // Notify that the interval may need updating (e.g. because the minimum interval + // setting for the context has changed). + void updateTimerIntervalIfNecessary(); - // SuspendableTimer - virtual void didStop() override; + static void scriptDidInteractWithPlugin(HTMLPlugInElement&); - double intervalClampedToMinimum(int timeout, double minimumTimerInterval) const; +private: + DOMTimer(ScriptExecutionContext&, std::unique_ptr, std::chrono::milliseconds interval, bool singleShot); + friend class Internals; - // Retuns timer fire time rounded to the next multiple of timer alignment interval. - virtual double alignedFireTime(double) const override; + std::chrono::milliseconds intervalClampedToMinimum() const; - int m_timeoutId; - int m_nestingLevel; - OwnPtr m_action; - int m_originalInterval; - bool m_shouldForwardUserGesture; - }; + bool isDOMTimersThrottlingEnabled(Document&) const; + void updateThrottlingStateIfNecessary(const DOMTimerFireState&); -} // namespace WebCore + // SuspendableTimer + void fired() override; + void didStop() override; + std::optional alignedFireTime(std::chrono::milliseconds) const override; + + // ActiveDOMObject API. + const char* activeDOMObjectName() const override; -#endif // DOMTimer_h + enum TimerThrottleState { + Undetermined, + ShouldThrottle, + ShouldNotThrottle + }; + + int m_timeoutId; + int m_nestingLevel; + std::unique_ptr m_action; + std::chrono::milliseconds m_originalInterval; + TimerThrottleState m_throttleState; + std::chrono::milliseconds m_currentTimerInterval; + RefPtr m_userGestureTokenToForward; +}; +} // namespace WebCore diff --git a/Source/WebCore/page/DOMWindow.cpp b/Source/WebCore/page/DOMWindow.cpp index 7533b009a..6aedb09c8 100644 --- a/Source/WebCore/page/DOMWindow.cpp +++ b/Source/WebCore/page/DOMWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple 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 @@ -11,17 +11,17 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -29,22 +29,22 @@ #include "BackForwardController.h" #include "BarProp.h" -#include "BeforeUnloadEvent.h" #include "CSSComputedStyleDeclaration.h" #include "CSSRule.h" #include "CSSRuleList.h" #include "Chrome.h" #include "ChromeClient.h" -#include "Console.h" +#include "ComposedTreeIterator.h" +#include "ContentExtensionActions.h" +#include "ContentExtensionRule.h" #include "Crypto.h" +#include "CustomElementRegistry.h" #include "DOMApplicationCache.h" #include "DOMSelection.h" -#include "DOMSettableTokenList.h" #include "DOMStringList.h" #include "DOMTimer.h" #include "DOMTokenList.h" #include "DOMURL.h" -#include "DOMWindowCSS.h" #include "DOMWindowExtension.h" #include "DOMWindowNotifications.h" #include "DeviceMotionController.h" @@ -53,12 +53,10 @@ #include "DocumentLoader.h" #include "Editor.h" #include "Element.h" -#include "EventException.h" #include "EventHandler.h" #include "EventListener.h" #include "EventNames.h" #include "ExceptionCode.h" -#include "ExceptionCodePlaceholder.h" #include "FloatRect.h" #include "FocusController.h" #include "FrameLoadRequest.h" @@ -69,7 +67,8 @@ #include "HTMLFrameOwnerElement.h" #include "History.h" #include "InspectorInstrumentation.h" -#include "URL.h" +#include "JSMainThreadExecState.h" +#include "Language.h" #include "Location.h" #include "MainFrame.h" #include "MediaQueryList.h" @@ -77,103 +76,132 @@ #include "MessageEvent.h" #include "Navigator.h" #include "Page.h" -#include "PageConsole.h" -#include "PageGroup.h" +#include "PageConsoleClient.h" #include "PageTransitionEvent.h" #include "Performance.h" -#include "PlatformScreen.h" +#include "RequestAnimationFrameCallback.h" +#include "ResourceLoadInfo.h" +#include "RuntimeApplicationChecks.h" #include "RuntimeEnabledFeatures.h" #include "ScheduledAction.h" #include "Screen.h" -#include "ScriptCallStack.h" -#include "ScriptCallStackFactory.h" #include "ScriptController.h" #include "SecurityOrigin.h" +#include "SecurityOriginData.h" #include "SecurityPolicy.h" +#include "SelectorQuery.h" #include "SerializedScriptValue.h" #include "Settings.h" +#include "StaticNodeList.h" #include "Storage.h" #include "StorageArea.h" #include "StorageNamespace.h" +#include "StorageNamespaceProvider.h" #include "StyleMedia.h" #include "StyleResolver.h" +#include "StyleScope.h" #include "SuddenTermination.h" +#include "URL.h" +#include "UserGestureIndicator.h" #include "WebKitPoint.h" #include "WindowFeatures.h" #include "WindowFocusAllowedIndicator.h" #include +#include +#include +#include #include #include #include +#include #include -#include +#include #include -#if ENABLE(PROXIMITY_EVENTS) -#include "DeviceProximityController.h" +#if ENABLE(USER_MESSAGE_HANDLERS) +#include "UserContentController.h" +#include "UserMessageHandlerDescriptor.h" +#include "WebKitNamespace.h" #endif -#if ENABLE(REQUEST_ANIMATION_FRAME) -#include "RequestAnimationFrameCallback.h" +#if ENABLE(GAMEPAD) +#include "GamepadManager.h" #endif -#if PLATFORM(IOS) #if ENABLE(GEOLOCATION) #include "NavigatorGeolocation.h" #endif + +#if ENABLE(POINTER_LOCK) +#include "PointerLockController.h" +#endif + +#if ENABLE(PROXIMITY_EVENTS) +#include "DeviceProximityController.h" +#endif + +#if PLATFORM(IOS) #include "WKContentObservation.h" +#include "WKContentObservationInternal.h" #endif +using namespace Inspector; + namespace WebCore { class PostMessageTimer : public TimerBase { public: - PostMessageTimer(DOMWindow* window, PassRefPtr message, const String& sourceOrigin, PassRefPtr source, PassOwnPtr channels, SecurityOrigin* targetOrigin, PassRefPtr stackTrace) + PostMessageTimer(DOMWindow& window, Ref&& message, const String& sourceOrigin, DOMWindow& source, std::unique_ptr channels, RefPtr&& targetOrigin, RefPtr&& stackTrace) : m_window(window) - , m_message(message) + , m_message(WTFMove(message)) , m_origin(sourceOrigin) , m_source(source) - , m_channels(channels) - , m_targetOrigin(targetOrigin) + , m_channels(WTFMove(channels)) + , m_targetOrigin(WTFMove(targetOrigin)) , m_stackTrace(stackTrace) + , m_userGestureToForward(UserGestureIndicator::currentUserGesture()) { } - PassRefPtr event(ScriptExecutionContext* context) + Ref event(ScriptExecutionContext& context) { - OwnPtr messagePorts = MessagePort::entanglePorts(*context, m_channels.release()); - return MessageEvent::create(messagePorts.release(), m_message, m_origin, String(), m_source); + return MessageEvent::create(MessagePort::entanglePorts(context, WTFMove(m_channels)), WTFMove(m_message), m_origin, { }, MessageEventSource(RefPtr(WTFMove(m_source)))); } + SecurityOrigin* targetOrigin() const { return m_targetOrigin.get(); } ScriptCallStack* stackTrace() const { return m_stackTrace.get(); } private: - virtual void fired() + void fired() override { - m_window->postMessageTimerFired(adoptPtr(this)); - // This object is deleted now. + // This object gets deleted when std::unique_ptr falls out of scope.. + std::unique_ptr timer(this); + + UserGestureIndicator userGestureIndicator(m_userGestureToForward); + m_window->postMessageTimerFired(*timer); } - RefPtr m_window; - RefPtr m_message; + Ref m_window; + Ref m_message; String m_origin; - RefPtr m_source; - OwnPtr m_channels; + Ref m_source; + std::unique_ptr m_channels; RefPtr m_targetOrigin; RefPtr m_stackTrace; + RefPtr m_userGestureToForward; }; typedef HashCountedSet DOMWindowSet; static DOMWindowSet& windowsWithUnloadEventListeners() { - DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithUnloadEventListeners, ()); + static NeverDestroyed windowsWithUnloadEventListeners; return windowsWithUnloadEventListeners; } static DOMWindowSet& windowsWithBeforeUnloadEventListeners() { - DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithBeforeUnloadEventListeners, ()); + static NeverDestroyed windowsWithBeforeUnloadEventListeners; return windowsWithBeforeUnloadEventListeners; } @@ -237,22 +265,21 @@ bool DOMWindow::dispatchAllPendingBeforeUnloadEvents() Vector> windows; windows.reserveInitialCapacity(set.size()); - for (auto it = set.begin(), end = set.end(); it != end; ++it) - windows.uncheckedAppend(*it->key); + for (auto& window : set) + windows.uncheckedAppend(*window.key); - for (unsigned i = 0; i < windows.size(); ++i) { - DOMWindow& window = windows[i].get(); - if (!set.contains(&window)) + for (auto& window : windows) { + if (!set.contains(window.ptr())) continue; - Frame* frame = window.frame(); + Frame* frame = window->frame(); if (!frame) continue; if (!frame->loader().shouldClose()) return false; - window.enableSuddenTermination(); + window->enableSuddenTermination(); } alreadyDispatched = true; @@ -277,18 +304,17 @@ void DOMWindow::dispatchAllPendingUnloadEvents() Vector> windows; windows.reserveInitialCapacity(set.size()); - for (auto it = set.begin(), end = set.end(); it != end; ++it) - windows.uncheckedAppend(*it->key); + for (auto& keyValue : set) + windows.uncheckedAppend(*keyValue.key); - for (unsigned i = 0; i < windows.size(); ++i) { - DOMWindow& window = windows[i].get(); - if (!set.contains(&window)) + for (auto& window : windows) { + if (!set.contains(window.ptr())) continue; - window.dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, false), window.document()); - window.dispatchEvent(Event::create(eventNames().unloadEvent, false, false), window.document()); + window->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, false), window->document()); + window->dispatchEvent(Event::create(eventNames().unloadEvent, false, false), window->document()); - window.enableSuddenTermination(); + window->enableSuddenTermination(); } alreadyDispatched = true; @@ -300,12 +326,10 @@ void DOMWindow::dispatchAllPendingUnloadEvents() // 3) Constrains the window rect to within the top and left boundaries of the available screen rect. // 4) Constrains the window rect to within the bottom and right boundaries of the available screen rect. // 5) Translate the window rect coordinates to be within the coordinate space of the screen. -FloatRect DOMWindow::adjustWindowRect(Page* page, const FloatRect& pendingChanges) +FloatRect DOMWindow::adjustWindowRect(Page& page, const FloatRect& pendingChanges) { - ASSERT(page); - - FloatRect screen = screenAvailableRect(page->mainFrame().view()); - FloatRect window = page->chrome().windowRect(); + FloatRect screen = screenAvailableRect(page.mainFrame().view()); + FloatRect window = page.chrome().windowRect(); // Make sure we're in a valid state before adjusting dimensions. ASSERT(std::isfinite(screen.x())); @@ -327,7 +351,7 @@ FloatRect DOMWindow::adjustWindowRect(Page* page, const FloatRect& pendingChange if (!std::isnan(pendingChanges.height())) window.setHeight(pendingChanges.height()); - FloatSize minimumSize = page->chrome().client().minimumWindowSize(); + FloatSize minimumSize = page.chrome().client().minimumWindowSize(); window.setWidth(std::min(std::max(minimumSize.width(), window.width()), screen.width())); window.setHeight(std::min(std::max(minimumSize.height(), window.height()), screen.height())); @@ -338,67 +362,59 @@ FloatRect DOMWindow::adjustWindowRect(Page* page, const FloatRect& pendingChange return window; } -bool DOMWindow::allowPopUp(Frame* firstFrame) +bool DOMWindow::allowPopUp(Frame& firstFrame) { - ASSERT(firstFrame); - - if (ScriptController::processingUserGesture()) - return true; - - return firstFrame->settings().javaScriptCanOpenWindowsAutomatically(); + return ScriptController::processingUserGesture() + || firstFrame.settings().javaScriptCanOpenWindowsAutomatically(); } bool DOMWindow::allowPopUp() { - return m_frame && allowPopUp(m_frame); + return m_frame && allowPopUp(*m_frame); } -bool DOMWindow::canShowModalDialog(const Frame* frame) +bool DOMWindow::canShowModalDialog(const Frame& frame) { - if (!frame) - return false; - Page* page = frame->page(); - if (!page) - return false; - return page->chrome().canRunModal(); + // Override support for layout testing purposes. + if (auto* document = frame.document()) { + if (auto* window = document->domWindow()) { + if (window->m_canShowModalDialogOverride) + return window->m_canShowModalDialogOverride.value(); + } + } + + auto* page = frame.page(); + return page && page->chrome().canRunModal(); } -bool DOMWindow::canShowModalDialogNow(const Frame* frame) +static void languagesChangedCallback(void* context) { - if (!frame) - return false; - Page* page = frame->page(); - if (!page) - return false; - return page->chrome().canRunModalNow(); + static_cast(context)->languagesChanged(); } -DOMWindow::DOMWindow(Document* document) - : ContextDestructionObserver(document) - , FrameDestructionObserver(document->frame()) - , m_shouldPrintWhenFinishedLoading(false) - , m_suspendedForPageCache(false) - , m_lastPageStatus(PageStatusNone) -#if PLATFORM(IOS) - , m_scrollEventListenerCount(0) -#endif -#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) - , m_touchEventListenerCount(0) -#endif +void DOMWindow::setCanShowModalDialogOverride(bool allow) +{ + m_canShowModalDialogOverride = allow; +} + +DOMWindow::DOMWindow(Document& document) + : ContextDestructionObserver(&document) + , FrameDestructionObserver(document.frame()) + , m_weakPtrFactory(this) { ASSERT(frame()); - ASSERT(DOMWindow::document()); + addLanguageChangeObserver(this, &languagesChangedCallback); } -void DOMWindow::didSecureTransitionTo(Document* document) +void DOMWindow::didSecureTransitionTo(Document& document) { - observeContext(document); + observeContext(&document); } DOMWindow::~DOMWindow() { #ifndef NDEBUG - if (!m_suspendedForPageCache) { + if (!m_suspendedForDocumentSuspension) { ASSERT(!m_screen); ASSERT(!m_history); ASSERT(!m_crypto); @@ -408,7 +424,6 @@ DOMWindow::~DOMWindow() ASSERT(!m_scrollbars); ASSERT(!m_statusbar); ASSERT(!m_toolbar); - ASSERT(!m_console); ASSERT(!m_navigator); #if ENABLE(WEB_TIMING) ASSERT(!m_performance); @@ -421,7 +436,7 @@ DOMWindow::~DOMWindow() } #endif - if (m_suspendedForPageCache) + if (m_suspendedForDocumentSuspension) willDestroyCachedFrame(); else willDestroyDocumentInFrame(); @@ -432,6 +447,13 @@ DOMWindow::~DOMWindow() removeAllUnloadEventListeners(this); removeAllBeforeUnloadEventListeners(this); + +#if ENABLE(GAMEPAD) + if (m_gamepadEventListenerCount) + GamepadManager::singleton().unregisterDOMWindow(this); +#endif + + removeLanguageChangeObserver(this); } DOMWindow* DOMWindow::toDOMWindow() @@ -439,26 +461,30 @@ DOMWindow* DOMWindow::toDOMWindow() return this; } -PassRefPtr DOMWindow::matchMedia(const String& media) +RefPtr DOMWindow::matchMedia(const String& media) { - return document() ? document()->mediaQueryMatcher().matchMedia(media) : 0; + return document() ? document()->mediaQueryMatcher().matchMedia(media) : nullptr; } Page* DOMWindow::page() { - return frame() ? frame()->page() : 0; + return frame() ? frame()->page() : nullptr; } void DOMWindow::frameDestroyed() { + Ref protectedThis(*this); + willDestroyDocumentInFrame(); FrameDestructionObserver::frameDestroyed(); resetDOMWindowProperties(); + JSDOMWindowBase::fireFrameClearedWatchpointsForWindow(this); } void DOMWindow::willDetachPage() { - InspectorInstrumentation::frameWindowDiscarded(m_frame, this); + if (m_frame) + InspectorInstrumentation::frameWindowDiscarded(*m_frame, this); } void DOMWindow::willDestroyCachedFrame() @@ -467,8 +493,8 @@ void DOMWindow::willDestroyCachedFrame() // unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInCachedFrame. Vector properties; copyToVector(m_properties, properties); - for (size_t i = 0; i < properties.size(); ++i) - properties[i]->willDestroyGlobalObjectInCachedFrame(); + for (auto& property : properties) + property->willDestroyGlobalObjectInCachedFrame(); } void DOMWindow::willDestroyDocumentInFrame() @@ -477,8 +503,8 @@ void DOMWindow::willDestroyDocumentInFrame() // unregister themselves from the DOMWindow as a result of the call to willDestroyGlobalObjectInFrame. Vector properties; copyToVector(m_properties, properties); - for (size_t i = 0; i < properties.size(); ++i) - properties[i]->willDestroyGlobalObjectInFrame(); + for (auto& property : properties) + property->willDestroyGlobalObjectInFrame(); } void DOMWindow::willDetachDocumentFromFrame() @@ -487,84 +513,103 @@ void DOMWindow::willDetachDocumentFromFrame() // unregister themselves from the DOMWindow as a result of the call to willDetachGlobalObjectFromFrame. Vector properties; copyToVector(m_properties, properties); - for (size_t i = 0; i < properties.size(); ++i) - properties[i]->willDetachGlobalObjectFromFrame(); + for (auto& property : properties) + property->willDetachGlobalObjectFromFrame(); } -void DOMWindow::registerProperty(DOMWindowProperty* property) +#if ENABLE(GAMEPAD) + +void DOMWindow::incrementGamepadEventListenerCount() { - m_properties.add(property); + if (++m_gamepadEventListenerCount == 1) + GamepadManager::singleton().registerDOMWindow(this); } -void DOMWindow::unregisterProperty(DOMWindowProperty* property) +void DOMWindow::decrementGamepadEventListenerCount() { - m_properties.remove(property); + ASSERT(m_gamepadEventListenerCount); + + if (!--m_gamepadEventListenerCount) + GamepadManager::singleton().unregisterDOMWindow(this); } -void DOMWindow::resetUnlessSuspendedForPageCache() +#endif + +void DOMWindow::registerProperty(DOMWindowProperty& property) +{ + m_properties.add(&property); +} + +void DOMWindow::unregisterProperty(DOMWindowProperty& property) { - if (m_suspendedForPageCache) + m_properties.remove(&property); +} + +void DOMWindow::resetUnlessSuspendedForDocumentSuspension() +{ + if (m_suspendedForDocumentSuspension) return; willDestroyDocumentInFrame(); resetDOMWindowProperties(); } -void DOMWindow::suspendForPageCache() +void DOMWindow::suspendForDocumentSuspension() { disconnectDOMWindowProperties(); - m_suspendedForPageCache = true; + m_suspendedForDocumentSuspension = true; } -void DOMWindow::resumeFromPageCache() +void DOMWindow::resumeFromDocumentSuspension() { reconnectDOMWindowProperties(); - m_suspendedForPageCache = false; + m_suspendedForDocumentSuspension = false; } void DOMWindow::disconnectDOMWindowProperties() { // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may - // unregister themselves from the DOMWindow as a result of the call to disconnectFrameForPageCache. + // unregister themselves from the DOMWindow as a result of the call to disconnectFrameForDocumentSuspension. Vector properties; copyToVector(m_properties, properties); - for (size_t i = 0; i < properties.size(); ++i) - properties[i]->disconnectFrameForPageCache(); + for (auto& property : properties) + property->disconnectFrameForDocumentSuspension(); } void DOMWindow::reconnectDOMWindowProperties() { - ASSERT(m_suspendedForPageCache); + ASSERT(m_suspendedForDocumentSuspension); // It is necessary to copy m_properties to a separate vector because the DOMWindowProperties may // unregister themselves from the DOMWindow as a result of the call to reconnectFromPageCache. Vector properties; copyToVector(m_properties, properties); - for (size_t i = 0; i < properties.size(); ++i) - properties[i]->reconnectFrameFromPageCache(m_frame); + for (auto& property : properties) + property->reconnectFrameFromDocumentSuspension(m_frame); } void DOMWindow::resetDOMWindowProperties() { m_properties.clear(); - m_screen = 0; - m_history = 0; - m_crypto = 0; - m_locationbar = 0; - m_menubar = 0; - m_personalbar = 0; - m_scrollbars = 0; - m_statusbar = 0; - m_toolbar = 0; - m_console = 0; - m_navigator = 0; + m_applicationCache = nullptr; + m_crypto = nullptr; + m_history = nullptr; + m_localStorage = nullptr; + m_location = nullptr; + m_locationbar = nullptr; + m_media = nullptr; + m_menubar = nullptr; + m_navigator = nullptr; + m_personalbar = nullptr; + m_screen = nullptr; + m_scrollbars = nullptr; + m_selection = nullptr; + m_sessionStorage = nullptr; + m_statusbar = nullptr; + m_toolbar = nullptr; + #if ENABLE(WEB_TIMING) - m_performance = 0; + m_performance = nullptr; #endif - m_location = 0; - m_media = 0; - m_sessionStorage = 0; - m_localStorage = 0; - m_applicationCache = 0; } bool DOMWindow::isCurrentlyDisplayedInFrame() const @@ -572,7 +617,66 @@ bool DOMWindow::isCurrentlyDisplayedInFrame() const return m_frame && m_frame->document()->domWindow() == this; } +CustomElementRegistry& DOMWindow::ensureCustomElementRegistry() +{ + if (!m_customElementRegistry) + m_customElementRegistry = CustomElementRegistry::create(*this); + return *m_customElementRegistry; +} + +static ExceptionOr selectorQueryInFrame(Frame* frame, const String& selectors) +{ + if (!frame) + return Exception { NOT_SUPPORTED_ERR }; + + Document* document = frame->document(); + if (!document) + return Exception { NOT_SUPPORTED_ERR }; + + return document->selectorQueryForString(selectors); +} + +ExceptionOr> DOMWindow::collectMatchingElementsInFlatTree(Node& scope, const String& selectors) +{ + auto queryOrException = selectorQueryInFrame(m_frame, selectors); + if (queryOrException.hasException()) + return queryOrException.releaseException(); + + if (!is(scope)) + return Ref { StaticElementList::create() }; + + SelectorQuery& query = queryOrException.releaseReturnValue(); + + Vector> result; + for (auto& node : composedTreeDescendants(downcast(scope))) { + if (is(node) && query.matches(downcast(node)) && !node.isInUserAgentShadowTree()) + result.append(downcast(node)); + } + + return Ref { StaticElementList::create(WTFMove(result)) }; +} + +ExceptionOr> DOMWindow::matchingElementInFlatTree(Node& scope, const String& selectors) +{ + auto queryOrException = selectorQueryInFrame(m_frame, selectors); + if (queryOrException.hasException()) + return queryOrException.releaseException(); + + if (!is(scope)) + return RefPtr { nullptr }; + + SelectorQuery& query = queryOrException.releaseReturnValue(); + + for (auto& node : composedTreeDescendants(downcast(scope))) { + if (is(node) && query.matches(downcast(node)) && !node.isInUserAgentShadowTree()) + return &downcast(node); + } + + return RefPtr { nullptr }; +} + #if ENABLE(ORIENTATION_EVENTS) + int DOMWindow::orientation() const { if (!m_frame) @@ -580,12 +684,13 @@ int DOMWindow::orientation() const return m_frame->orientation(); } + #endif Screen* DOMWindow::screen() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_screen) m_screen = Screen::create(m_frame); return m_screen.get(); @@ -594,9 +699,9 @@ Screen* DOMWindow::screen() const History* DOMWindow::history() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_history) - m_history = History::create(m_frame); + m_history = History::create(*m_frame); return m_history.get(); } @@ -604,7 +709,7 @@ Crypto* DOMWindow::crypto() const { // FIXME: Why is crypto not available when the window is not currently displayed in a frame? if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_crypto) m_crypto = Crypto::create(*document()); return m_crypto.get(); @@ -613,7 +718,7 @@ Crypto* DOMWindow::crypto() const BarProp* DOMWindow::locationbar() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_locationbar) m_locationbar = BarProp::create(m_frame, BarProp::Locationbar); return m_locationbar.get(); @@ -622,7 +727,7 @@ BarProp* DOMWindow::locationbar() const BarProp* DOMWindow::menubar() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_menubar) m_menubar = BarProp::create(m_frame, BarProp::Menubar); return m_menubar.get(); @@ -631,7 +736,7 @@ BarProp* DOMWindow::menubar() const BarProp* DOMWindow::personalbar() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_personalbar) m_personalbar = BarProp::create(m_frame, BarProp::Personalbar); return m_personalbar.get(); @@ -640,7 +745,7 @@ BarProp* DOMWindow::personalbar() const BarProp* DOMWindow::scrollbars() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_scrollbars) m_scrollbars = BarProp::create(m_frame, BarProp::Scrollbars); return m_scrollbars.get(); @@ -649,7 +754,7 @@ BarProp* DOMWindow::scrollbars() const BarProp* DOMWindow::statusbar() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_statusbar) m_statusbar = BarProp::create(m_frame, BarProp::Statusbar); return m_statusbar.get(); @@ -658,240 +763,271 @@ BarProp* DOMWindow::statusbar() const BarProp* DOMWindow::toolbar() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_toolbar) m_toolbar = BarProp::create(m_frame, BarProp::Toolbar); return m_toolbar.get(); } -Console* DOMWindow::console() const -{ - if (!isCurrentlyDisplayedInFrame()) - return 0; - if (!m_console) - m_console = Console::create(m_frame); - return m_console.get(); -} - -PageConsole* DOMWindow::pageConsole() const +PageConsoleClient* DOMWindow::console() const { if (!isCurrentlyDisplayedInFrame()) - return 0; - return m_frame->page() ? &m_frame->page()->console() : 0; + return nullptr; + return m_frame->page() ? &m_frame->page()->console() : nullptr; } DOMApplicationCache* DOMWindow::applicationCache() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_applicationCache) - m_applicationCache = DOMApplicationCache::create(m_frame); + m_applicationCache = DOMApplicationCache::create(*m_frame); return m_applicationCache.get(); } Navigator* DOMWindow::navigator() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_navigator) - m_navigator = Navigator::create(m_frame); + m_navigator = Navigator::create(*m_frame); return m_navigator.get(); } #if ENABLE(WEB_TIMING) + Performance* DOMWindow::performance() const { if (!isCurrentlyDisplayedInFrame()) - return 0; - if (!m_performance) - m_performance = Performance::create(m_frame); + return nullptr; + if (!m_performance) { + MonotonicTime timeOrigin = document()->loader() ? document()->loader()->timing().referenceMonotonicTime() : MonotonicTime::now(); + m_performance = Performance::create(*document(), timeOrigin); + } return m_performance.get(); } + #endif +double DOMWindow::nowTimestamp() const +{ +#if ENABLE(WEB_TIMING) + return performance() ? performance()->now() / 1000 : 0; +#else + return document() ? document()->monotonicTimestamp() : 0; +#endif +} + Location* DOMWindow::location() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_location) m_location = Location::create(m_frame); return m_location.get(); } -Storage* DOMWindow::sessionStorage(ExceptionCode& ec) const +#if ENABLE(USER_MESSAGE_HANDLERS) + +bool DOMWindow::shouldHaveWebKitNamespaceForWorld(DOMWrapperWorld& world) +{ + if (!m_frame) + return false; + + auto* page = m_frame->page(); + if (!page) + return false; + + bool hasUserMessageHandler = false; + page->userContentProvider().forEachUserMessageHandler([&](const UserMessageHandlerDescriptor& descriptor) { + if (&descriptor.world() == &world) { + hasUserMessageHandler = true; + return; + } + }); + + return hasUserMessageHandler; +} + +WebKitNamespace* DOMWindow::webkitNamespace() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; + auto* page = m_frame->page(); + if (!page) + return nullptr; + if (!m_webkitNamespace) + m_webkitNamespace = WebKitNamespace::create(*m_frame, page->userContentProvider()); + return m_webkitNamespace.get(); +} - Document* document = this->document(); +#endif + +ExceptionOr DOMWindow::sessionStorage() const +{ + if (!isCurrentlyDisplayedInFrame()) + return nullptr; + + auto* document = this->document(); if (!document) - return 0; + return nullptr; - if (!document->securityOrigin()->canAccessSessionStorage(document->topOrigin())) { - ec = SECURITY_ERR; - return 0; - } + if (!document->securityOrigin().canAccessSessionStorage(document->topOrigin())) + return Exception { SECURITY_ERR }; if (m_sessionStorage) { - if (!m_sessionStorage->area().canAccessStorage(m_frame)) { - ec = SECURITY_ERR; - return 0; - } + if (!m_sessionStorage->area().canAccessStorage(m_frame)) + return Exception { SECURITY_ERR }; return m_sessionStorage.get(); } - Page* page = document->page(); + auto* page = document->page(); if (!page) - return 0; + return nullptr; - RefPtr storageArea = page->sessionStorage()->storageArea(document->securityOrigin()); - if (!storageArea->canAccessStorage(m_frame)) { - ec = SECURITY_ERR; - return 0; - } + auto storageArea = page->sessionStorage()->storageArea(SecurityOriginData::fromSecurityOrigin(document->securityOrigin())); + if (!storageArea->canAccessStorage(m_frame)) + return Exception { SECURITY_ERR }; - m_sessionStorage = Storage::create(m_frame, storageArea.release()); + m_sessionStorage = Storage::create(m_frame, WTFMove(storageArea)); return m_sessionStorage.get(); } -Storage* DOMWindow::localStorage(ExceptionCode& ec) const +ExceptionOr DOMWindow::localStorage() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; - Document* document = this->document(); + auto* document = this->document(); if (!document) - return 0; - - if (!document->securityOrigin()->canAccessLocalStorage(0)) { - ec = SECURITY_ERR; - return 0; - } - - if (m_localStorage) { - if (!m_localStorage->area().canAccessStorage(m_frame)) { - ec = SECURITY_ERR; - return 0; + return nullptr; + + if (!document->securityOrigin().canAccessLocalStorage(nullptr)) + return Exception { SECURITY_ERR }; + + auto* page = document->page(); + // FIXME: We should consider supporting access/modification to local storage + // after calling window.close(). See . + if (!page || !page->isClosing()) { + if (m_localStorage) { + if (!m_localStorage->area().canAccessStorage(m_frame)) + return Exception { SECURITY_ERR }; + return m_localStorage.get(); } - return m_localStorage.get(); } - Page* page = document->page(); if (!page) - return 0; + return nullptr; + + if (page->isClosing()) + return nullptr; if (!page->settings().localStorageEnabled()) - return 0; + return nullptr; - RefPtr storageArea; - if (!document->securityOrigin()->canAccessLocalStorage(document->topOrigin())) - storageArea = page->group().transientLocalStorage(document->topOrigin())->storageArea(document->securityOrigin()); - else - storageArea = page->group().localStorage()->storageArea(document->securityOrigin()); + auto storageArea = page->storageNamespaceProvider().localStorageArea(*document); - if (!storageArea->canAccessStorage(m_frame)) { - ec = SECURITY_ERR; - return 0; - } + if (!storageArea->canAccessStorage(m_frame)) + return Exception { SECURITY_ERR }; - m_localStorage = Storage::create(m_frame, storageArea.release()); + m_localStorage = Storage::create(m_frame, WTFMove(storageArea)); return m_localStorage.get(); } -void DOMWindow::postMessage(PassRefPtr message, MessagePort* port, const String& targetOrigin, DOMWindow& source, ExceptionCode& ec) -{ - MessagePortArray ports; - if (port) - ports.append(port); - postMessage(message, &ports, targetOrigin, source, ec); -} - -void DOMWindow::postMessage(PassRefPtr message, const MessagePortArray* ports, const String& targetOrigin, DOMWindow& source, ExceptionCode& ec) +ExceptionOr DOMWindow::postMessage(JSC::ExecState& state, DOMWindow& callerWindow, JSC::JSValue messageValue, const String& targetOrigin, Vector>&& transfer) { if (!isCurrentlyDisplayedInFrame()) - return; + return { }; - Document* sourceDocument = source.document(); + Document* sourceDocument = callerWindow.document(); // Compute the target origin. We need to do this synchronously in order // to generate the SYNTAX_ERR exception correctly. RefPtr target; if (targetOrigin == "/") { if (!sourceDocument) - return; - target = sourceDocument->securityOrigin(); + return { }; + target = &sourceDocument->securityOrigin(); } else if (targetOrigin != "*") { target = SecurityOrigin::createFromString(targetOrigin); // It doesn't make sense target a postMessage at a unique origin // because there's no way to represent a unique origin in a string. - if (target->isUnique()) { - ec = SYNTAX_ERR; - return; - } + if (target->isUnique()) + return Exception { SYNTAX_ERR }; } - OwnPtr channels = MessagePort::disentanglePorts(ports, ec); - if (ec) - return; + Vector> ports; + auto message = SerializedScriptValue::create(state, messageValue, WTFMove(transfer), ports); + if (message.hasException()) + return message.releaseException(); + + auto channels = MessagePort::disentanglePorts(WTFMove(ports)); + if (channels.hasException()) + return channels.releaseException(); // Capture the source of the message. We need to do this synchronously // in order to capture the source of the message correctly. if (!sourceDocument) - return; - String sourceOrigin = sourceDocument->securityOrigin()->toString(); + return { }; + auto sourceOrigin = sourceDocument->securityOrigin().toString(); // Capture stack trace only when inspector front-end is loaded as it may be time consuming. RefPtr stackTrace; if (InspectorInstrumentation::consoleAgentEnabled(sourceDocument)) - stackTrace = createScriptCallStack(ScriptCallStack::maxCallStackSizeToCapture, true); + stackTrace = createScriptCallStack(JSMainThreadExecState::currentState(), ScriptCallStack::maxCallStackSizeToCapture); // Schedule the message. - PostMessageTimer* timer = new PostMessageTimer(this, message, sourceOrigin, &source, channels.release(), target.get(), stackTrace.release()); + auto* timer = new PostMessageTimer(*this, message.releaseReturnValue(), sourceOrigin, callerWindow, channels.releaseReturnValue(), WTFMove(target), WTFMove(stackTrace)); timer->startOneShot(0); + + return { }; } -void DOMWindow::postMessageTimerFired(PassOwnPtr t) +void DOMWindow::postMessageTimerFired(PostMessageTimer& timer) { - OwnPtr timer(t); - if (!document() || !isCurrentlyDisplayedInFrame()) return; - dispatchMessageEventWithOriginCheck(timer->targetOrigin(), timer->event(document()), timer->stackTrace()); -} - -void DOMWindow::dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtr event, PassRefPtr stackTrace) -{ - if (intendedTargetOrigin) { + if (auto* intendedTargetOrigin = timer.targetOrigin()) { // Check target origin now since the target document may have changed since the timer was scheduled. if (!intendedTargetOrigin->isSameSchemeHostPort(document()->securityOrigin())) { - String message = "Unable to post message to " + intendedTargetOrigin->toString() + - ". Recipient has origin " + document()->securityOrigin()->toString() + ".\n"; - pageConsole()->addMessage(SecurityMessageSource, ErrorMessageLevel, message, stackTrace); + if (auto* pageConsole = console()) { + String message = makeString("Unable to post message to ", intendedTargetOrigin->toString(), ". Recipient has origin ", document()->securityOrigin().toString(), ".\n"); + if (timer.stackTrace()) + pageConsole->addMessage(MessageSource::Security, MessageLevel::Error, message, *timer.stackTrace()); + else + pageConsole->addMessage(MessageSource::Security, MessageLevel::Error, message); + } return; } } - dispatchEvent(event); + dispatchEvent(timer.event(*document())); } DOMSelection* DOMWindow::getSelection() { - if (!isCurrentlyDisplayedInFrame() || !m_frame) - return 0; - - return m_frame->document()->getSelection(); + if (!isCurrentlyDisplayedInFrame()) + return nullptr; + if (!m_selection) + m_selection = DOMSelection::create(*m_frame); + return m_selection.get(); } Element* DOMWindow::frameElement() const { if (!m_frame) - return 0; + return nullptr; return m_frame->ownerElement(); } -void DOMWindow::focus(ScriptExecutionContext* context) +void DOMWindow::focus(DOMWindow& callerWindow) +{ + focus(opener() && opener() != this && &callerWindow == opener()); +} + +void DOMWindow::focus(bool allowFocus) { if (!m_frame) return; @@ -900,13 +1036,7 @@ void DOMWindow::focus(ScriptExecutionContext* context) if (!page) return; - bool allowFocus = WindowFocusAllowedIndicator::windowFocusAllowed() || !m_frame->settings().windowFocusRestricted(); - if (context) { - ASSERT(isMainThread()); - Document* activeDocument = toDocument(context); - if (opener() && opener() != this && activeDocument->domWindow() == opener()) - allowFocus = true; - } + allowFocus = allowFocus || WindowFocusAllowedIndicator::windowFocusAllowed() || !m_frame->settings().windowFocusRestricted(); // If we're a top level window, bring the window to the front. if (m_frame->isMainFrame() && allowFocus) @@ -918,9 +1048,11 @@ void DOMWindow::focus(ScriptExecutionContext* context) // Clear the current frame's focused node if a new frame is about to be focused. Frame* focusedFrame = page->focusController().focusedFrame(); if (focusedFrame && focusedFrame != m_frame) - focusedFrame->document()->setFocusedElement(0); + focusedFrame->document()->setFocusedElement(nullptr); - m_frame->eventHandler().focusDocumentView(); + // setFocusedElement may clear m_frame, so recheck before using it. + if (m_frame) + m_frame->eventHandler().focusDocumentView(); } void DOMWindow::blur() @@ -941,7 +1073,14 @@ void DOMWindow::blur() page->chrome().unfocus(); } -void DOMWindow::close(ScriptExecutionContext* context) +void DOMWindow::close(Document& document) +{ + if (!document.canNavigate(m_frame)) + return; + close(); +} + +void DOMWindow::close() { if (!m_frame) return; @@ -953,26 +1092,17 @@ void DOMWindow::close(ScriptExecutionContext* context) if (!m_frame->isMainFrame()) return; - if (context) { - ASSERT(isMainThread()); - Document* activeDocument = toDocument(context); - if (!activeDocument) - return; - - if (!activeDocument->canNavigate(m_frame)) - return; - } - bool allowScriptsToCloseWindows = m_frame->settings().allowScriptsToCloseWindows(); if (!(page->openedByDOM() || page->backForward().count() <= 1 || allowScriptsToCloseWindows)) { - pageConsole()->addMessage(JSMessageSource, WarningMessageLevel, ASCIILiteral("Can't close the window since it was not opened by JavaScript")); + console()->addMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("Can't close the window since it was not opened by JavaScript")); return; } if (!m_frame->loader().shouldClose()) return; + page->setIsClosing(); page->chrome().closeWindowSoon(); } @@ -981,13 +1111,12 @@ void DOMWindow::print() if (!m_frame) return; - Page* page = m_frame->page(); + auto* page = m_frame->page(); if (!page) return; - // Pages are not allowed to bring up a modal print dialog during BeforeUnload dispatch. - if (page->isAnyFrameHandlingBeforeUnloadEvent()) { - printErrorMessage("Use of window.print is not allowed during beforeunload event dispatch."); + if (!page->arePromptsAllowed()) { + printErrorMessage("Use of window.print is not allowed while unloading a page."); return; } @@ -996,7 +1125,7 @@ void DOMWindow::print() return; } m_shouldPrintWhenFinishedLoading = false; - page->chrome().print(m_frame); + page->chrome().print(*m_frame); } void DOMWindow::stop() @@ -1014,19 +1143,21 @@ void DOMWindow::alert(const String& message) if (!m_frame) return; - // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. - if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { - printErrorMessage("Use of window.alert is not allowed during beforeunload event dispatch."); + auto* page = m_frame->page(); + if (!page) + return; + + if (!page->arePromptsAllowed()) { + printErrorMessage("Use of window.alert is not allowed while unloading a page."); return; } m_frame->document()->updateStyleIfNeeded(); +#if ENABLE(POINTER_LOCK) + page->pointerLockController().requestPointerUnlock(); +#endif - Page* page = m_frame->page(); - if (!page) - return; - - page->chrome().runJavaScriptAlert(m_frame, message); + page->chrome().runJavaScriptAlert(*m_frame, message); } bool DOMWindow::confirm(const String& message) @@ -1034,19 +1165,21 @@ bool DOMWindow::confirm(const String& message) if (!m_frame) return false; - // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. - if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { - printErrorMessage("Use of window.confirm is not allowed during beforeunload event dispatch."); + auto* page = m_frame->page(); + if (!page) + return false; + + if (!page->arePromptsAllowed()) { + printErrorMessage("Use of window.confirm is not allowed while unloading a page."); return false; } m_frame->document()->updateStyleIfNeeded(); +#if ENABLE(POINTER_LOCK) + page->pointerLockController().requestPointerUnlock(); +#endif - Page* page = m_frame->page(); - if (!page) - return false; - - return page->chrome().runJavaScriptConfirm(m_frame, message); + return page->chrome().runJavaScriptConfirm(*m_frame, message); } String DOMWindow::prompt(const String& message, const String& defaultValue) @@ -1054,57 +1187,27 @@ String DOMWindow::prompt(const String& message, const String& defaultValue) if (!m_frame) return String(); - // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. - if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { - printErrorMessage("Use of window.prompt is not allowed during beforeunload event dispatch."); + auto* page = m_frame->page(); + if (!page) + return String(); + + if (!page->arePromptsAllowed()) { + printErrorMessage("Use of window.prompt is not allowed while unloading a page."); return String(); } m_frame->document()->updateStyleIfNeeded(); - - Page* page = m_frame->page(); - if (!page) - return String(); +#if ENABLE(POINTER_LOCK) + page->pointerLockController().requestPointerUnlock(); +#endif String returnValue; - if (page->chrome().runJavaScriptPrompt(m_frame, message, defaultValue, returnValue)) + if (page->chrome().runJavaScriptPrompt(*m_frame, message, defaultValue, returnValue)) return returnValue; return String(); } -String DOMWindow::btoa(const String& stringToEncode, ExceptionCode& ec) -{ - if (stringToEncode.isNull()) - return String(); - - if (!stringToEncode.containsOnlyLatin1()) { - ec = INVALID_CHARACTER_ERR; - return String(); - } - - return base64Encode(stringToEncode.latin1()); -} - -String DOMWindow::atob(const String& encodedString, ExceptionCode& ec) -{ - if (encodedString.isNull()) - return String(); - - if (!encodedString.containsOnlyLatin1()) { - ec = INVALID_CHARACTER_ERR; - return String(); - } - - Vector out; - if (!base64Decode(encodedString, out, Base64FailOnInvalidCharacterOrExcessPadding)) { - ec = INVALID_CHARACTER_ERR; - return String(); - } - - return String(out.data(), out.size()); -} - bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, bool wrap, bool /*wholeWord*/, bool /*searchInFrames*/, bool /*showDialog*/) const { if (!isCurrentlyDisplayedInFrame()) @@ -1112,7 +1215,7 @@ bool DOMWindow::find(const String& string, bool caseSensitive, bool backwards, b // FIXME (13016): Support wholeWord, searchInFrames and showDialog. FindOptions options = (backwards ? Backwards : 0) | (caseSensitive ? 0 : CaseInsensitive) | (wrap ? WrapAround : 0); - return m_frame->editor().findString(string, options); + return m_frame->editor().findString(string, options | DoNotTraverseFlatTree); } bool DOMWindow::offscreenBuffering() const @@ -1122,6 +1225,9 @@ bool DOMWindow::offscreenBuffering() const int DOMWindow::outerHeight() const { +#if PLATFORM(IOS) + return 0; +#else if (!m_frame) return 0; @@ -1130,10 +1236,14 @@ int DOMWindow::outerHeight() const return 0; return static_cast(page->chrome().windowRect().height()); +#endif } int DOMWindow::outerWidth() const { +#if PLATFORM(IOS) + return 0; +#else if (!m_frame) return 0; @@ -1142,6 +1252,7 @@ int DOMWindow::outerWidth() const return 0; return static_cast(page->chrome().windowRect().width()); +#endif } int DOMWindow::innerHeight() const @@ -1153,7 +1264,7 @@ int DOMWindow::innerHeight() const if (!view) return 0; - return view->mapFromLayoutToCSSUnits(static_cast(view->visibleContentRectIncludingScrollbars().height())); + return view->mapFromLayoutToCSSUnits(static_cast(view->unobscuredContentRectIncludingScrollbars().height())); } int DOMWindow::innerWidth() const @@ -1165,7 +1276,7 @@ int DOMWindow::innerWidth() const if (!view) return 0; - return view->mapFromLayoutToCSSUnits(static_cast(view->visibleContentRectIncludingScrollbars().width())); + return view->mapFromLayoutToCSSUnits(static_cast(view->unobscuredContentRectIncludingScrollbars().width())); } int DOMWindow::screenX() const @@ -1201,16 +1312,13 @@ int DOMWindow::scrollX() const if (!view) return 0; - if (!view->scrollX()) + int scrollX = view->contentsScrollPosition().x(); + if (!scrollX) return 0; m_frame->document()->updateLayoutIgnorePendingStylesheets(); -#if PLATFORM(IOS) - return static_cast(view->actualScrollX() / (m_frame->pageZoomFactor() * m_frame->frameScaleFactor())); -#else - return view->mapFromLayoutToCSSUnits(view->scrollX()); -#endif + return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().x()); } int DOMWindow::scrollY() const @@ -1222,16 +1330,13 @@ int DOMWindow::scrollY() const if (!view) return 0; - if (!view->scrollY()) + int scrollY = view->contentsScrollPosition().y(); + if (!scrollY) return 0; m_frame->document()->updateLayoutIgnorePendingStylesheets(); -#if PLATFORM(IOS) - return static_cast(view->actualScrollY() / (m_frame->pageZoomFactor() * m_frame->frameScaleFactor())); -#else - return view->mapFromLayoutToCSSUnits(view->scrollY()); -#endif + return view->mapFromLayoutToCSSUnits(view->contentsScrollPosition().y()); } bool DOMWindow::closed() const @@ -1275,7 +1380,7 @@ void DOMWindow::setStatus(const String& string) return; ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state. - page->chrome().setStatusbarText(m_frame, m_status); + page->chrome().setStatusbarText(*m_frame, m_status); } void DOMWindow::setDefaultStatus(const String& string) @@ -1290,13 +1395,13 @@ void DOMWindow::setDefaultStatus(const String& string) return; ASSERT(m_frame->document()); // Client calls shouldn't be made when the frame is in inconsistent state. - page->chrome().setStatusbarText(m_frame, m_defaultStatus); + page->chrome().setStatusbarText(*m_frame, m_defaultStatus); } DOMWindow* DOMWindow::self() const { if (!m_frame) - return 0; + return nullptr; return m_frame->document()->domWindow(); } @@ -1304,11 +1409,11 @@ DOMWindow* DOMWindow::self() const DOMWindow* DOMWindow::opener() const { if (!m_frame) - return 0; + return nullptr; Frame* opener = m_frame->loader().opener(); if (!opener) - return 0; + return nullptr; return opener->document()->domWindow(); } @@ -1316,7 +1421,7 @@ DOMWindow* DOMWindow::opener() const DOMWindow* DOMWindow::parent() const { if (!m_frame) - return 0; + return nullptr; Frame* parent = m_frame->tree().parent(); if (parent) @@ -1328,47 +1433,45 @@ DOMWindow* DOMWindow::parent() const DOMWindow* DOMWindow::top() const { if (!m_frame) - return 0; + return nullptr; Page* page = m_frame->page(); if (!page) - return 0; + return nullptr; return m_frame->tree().top().document()->domWindow(); } Document* DOMWindow::document() const { - ScriptExecutionContext* context = ContextDestructionObserver::scriptExecutionContext(); - return toDocument(context); + return downcast(ContextDestructionObserver::scriptExecutionContext()); } -PassRefPtr DOMWindow::styleMedia() const +RefPtr DOMWindow::styleMedia() const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; if (!m_media) m_media = StyleMedia::create(m_frame); - return m_media.get(); + return m_media; } -PassRefPtr DOMWindow::getComputedStyle(Element* elt, const String& pseudoElt) const +Ref DOMWindow::getComputedStyle(Element& element, const String& pseudoElt) const { - if (!elt) - return 0; - - return CSSComputedStyleDeclaration::create(elt, false, pseudoElt); + return CSSComputedStyleDeclaration::create(element, false, pseudoElt); } -PassRefPtr DOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement, bool authorOnly) const +RefPtr DOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement, bool authorOnly) const { if (!isCurrentlyDisplayedInFrame()) - return 0; + return nullptr; unsigned colonStart = pseudoElement[0] == ':' ? (pseudoElement[1] == ':' ? 2 : 1) : 0; - CSSSelector::PseudoType pseudoType = CSSSelector::parsePseudoType(AtomicString(pseudoElement.substring(colonStart))); - if (pseudoType == CSSSelector::PseudoUnknown && !pseudoElement.isEmpty()) - return 0; + CSSSelector::PseudoElementType pseudoType = CSSSelector::parsePseudoElementType(pseudoElement.substringSharingImpl(colonStart)); + if (pseudoType == CSSSelector::PseudoElementUnknown && !pseudoElement.isEmpty()) + return nullptr; + + m_frame->document()->styleScope().flushPendingUpdate(); unsigned rulesToInclude = StyleResolver::AuthorCSSRules; if (!authorOnly) @@ -1378,24 +1481,24 @@ PassRefPtr DOMWindow::getMatchedCSSRules(Element* element, const St PseudoId pseudoId = CSSSelector::pseudoId(pseudoType); - Vector> matchedRules = m_frame->document()->ensureStyleResolver().pseudoStyleRulesForElement(element, pseudoId, rulesToInclude); + auto matchedRules = m_frame->document()->styleScope().resolver().pseudoStyleRulesForElement(element, pseudoId, rulesToInclude); if (matchedRules.isEmpty()) - return 0; + return nullptr; RefPtr ruleList = StaticCSSRuleList::create(); - for (unsigned i = 0; i < matchedRules.size(); ++i) - ruleList->rules().append(matchedRules[i]->createCSSOMWrapper()); + for (auto& rule : matchedRules) + ruleList->rules().append(rule->createCSSOMWrapper()); - return ruleList.release(); + return ruleList; } -PassRefPtr DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const +RefPtr DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const { if (!node || !p) - return 0; + return nullptr; if (!document()) - return 0; + return nullptr; document()->updateLayoutIgnorePendingStylesheets(); @@ -1404,13 +1507,13 @@ PassRefPtr DOMWindow::webkitConvertPointFromNodeToPage(Node* node, return WebKitPoint::create(pagePoint.x(), pagePoint.y()); } -PassRefPtr DOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const +RefPtr DOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const { if (!node || !p) - return 0; + return nullptr; if (!document()) - return 0; + return nullptr; document()->updateLayoutIgnorePendingStylesheets(); @@ -1431,7 +1534,12 @@ double DOMWindow::devicePixelRatio() const return page->deviceScaleFactor(); } -void DOMWindow::scrollBy(int x, int y) const +void DOMWindow::scrollBy(const ScrollToOptions& options) const +{ + return scrollBy(options.left.value_or(0), options.top.value_or(0)); +} + +void DOMWindow::scrollBy(double x, double y) const { if (!isCurrentlyDisplayedInFrame()) return; @@ -1442,34 +1550,48 @@ void DOMWindow::scrollBy(int x, int y) const if (!view) return; + // Normalize non-finite values (https://drafts.csswg.org/cssom-view/#normalize-non-finite-values). + x = std::isfinite(x) ? x : 0; + y = std::isfinite(y) ? y : 0; + IntSize scaledOffset(view->mapFromCSSToLayoutUnits(x), view->mapFromCSSToLayoutUnits(y)); -#if PLATFORM(IOS) - view->setActualScrollPosition(view->actualScrollPosition() + scaledOffset); -#else - view->scrollBy(scaledOffset); -#endif + view->setContentsScrollPosition(view->contentsScrollPosition() + scaledOffset); } -void DOMWindow::scrollTo(int x, int y) const +void DOMWindow::scrollTo(const ScrollToOptions& options) const { if (!isCurrentlyDisplayedInFrame()) return; - document()->updateLayoutIgnorePendingStylesheets(); + RefPtr view = m_frame->view(); + if (!view) + return; + + double x = options.left ? options.left.value() : view->contentsScrollPosition().x(); + double y = options.top ? options.top.value() : view->contentsScrollPosition().y(); + return scrollTo(x, y); +} + +void DOMWindow::scrollTo(double x, double y) const +{ + if (!isCurrentlyDisplayedInFrame()) + return; RefPtr view = m_frame->view(); if (!view) return; + // Normalize non-finite values (https://drafts.csswg.org/cssom-view/#normalize-non-finite-values). + x = std::isfinite(x) ? x : 0; + y = std::isfinite(y) ? y : 0; + + if (!x && !y && view->contentsScrollPosition() == IntPoint(0, 0)) + return; + + document()->updateLayoutIgnorePendingStylesheets(); -#if PLATFORM(IOS) - int zoomedX = static_cast(x * m_frame->pageZoomFactor() * m_frame->frameScaleFactor()); - int zoomedY = static_cast(y * m_frame->pageZoomFactor() * m_frame->frameScaleFactor()); - view->setActualScrollPosition(IntPoint(zoomedX, zoomedY)); -#else IntPoint layoutPos(view->mapFromCSSToLayoutUnits(x), view->mapFromCSSToLayoutUnits(y)); - view->setScrollPosition(layoutPos); -#endif + view->setContentsScrollPosition(layoutPos); } bool DOMWindow::allowedToChangeWindowGeometry() const @@ -1495,8 +1617,7 @@ void DOMWindow::moveBy(float x, float y) const FloatRect fr = page->chrome().windowRect(); FloatRect update = fr; update.move(x, y); - // Security check (the spec talks about UniversalBrowserWrite to disable this check...) - page->chrome().setWindowRect(adjustWindowRect(page, update)); + page->chrome().setWindowRect(adjustWindowRect(*page, update)); } void DOMWindow::moveTo(float x, float y) const @@ -1510,8 +1631,7 @@ void DOMWindow::moveTo(float x, float y) const fr.setLocation(sr.location()); FloatRect update = fr; update.move(x, y); - // Security check (the spec talks about UniversalBrowserWrite to disable this check...) - page->chrome().setWindowRect(adjustWindowRect(page, update)); + page->chrome().setWindowRect(adjustWindowRect(*page, update)); } void DOMWindow::resizeBy(float x, float y) const @@ -1523,7 +1643,7 @@ void DOMWindow::resizeBy(float x, float y) const FloatRect fr = page->chrome().windowRect(); FloatSize dest = fr.size() + FloatSize(x, y); FloatRect update(fr.location(), dest); - page->chrome().setWindowRect(adjustWindowRect(page, update)); + page->chrome().setWindowRect(adjustWindowRect(*page, update)); } void DOMWindow::resizeTo(float width, float height) const @@ -1535,17 +1655,15 @@ void DOMWindow::resizeTo(float width, float height) const FloatRect fr = page->chrome().windowRect(); FloatSize dest = FloatSize(width, height); FloatRect update(fr.location(), dest); - page->chrome().setWindowRect(adjustWindowRect(page, update)); + page->chrome().setWindowRect(adjustWindowRect(*page, update)); } -int DOMWindow::setTimeout(PassOwnPtr action, int timeout, ExceptionCode& ec) +ExceptionOr DOMWindow::setTimeout(std::unique_ptr action, int timeout) { - ScriptExecutionContext* context = scriptExecutionContext(); - if (!context) { - ec = INVALID_ACCESS_ERR; - return -1; - } - return DOMTimer::install(context, action, timeout, true); + auto* context = scriptExecutionContext(); + if (!context) + return Exception { INVALID_ACCESS_ERR }; + return DOMTimer::install(*context, WTFMove(action), std::chrono::milliseconds(timeout), true); } void DOMWindow::clearTimeout(int timeoutId) @@ -1560,7 +1678,7 @@ void DOMWindow::clearTimeout(int timeoutId) if (!WebThreadCountOfObservedContentModifiers()) { if (Page* page = m_frame->page()) - page->chrome().client().observedContentChange(m_frame); + page->chrome().client().observedContentChange(*m_frame); } } } @@ -1569,17 +1687,15 @@ void DOMWindow::clearTimeout(int timeoutId) ScriptExecutionContext* context = scriptExecutionContext(); if (!context) return; - DOMTimer::removeById(context, timeoutId); + DOMTimer::removeById(*context, timeoutId); } -int DOMWindow::setInterval(PassOwnPtr action, int timeout, ExceptionCode& ec) +ExceptionOr DOMWindow::setInterval(std::unique_ptr action, int timeout) { - ScriptExecutionContext* context = scriptExecutionContext(); - if (!context) { - ec = INVALID_ACCESS_ERR; - return -1; - } - return DOMTimer::install(context, action, timeout, false); + auto* context = scriptExecutionContext(); + if (!context) + return Exception { INVALID_ACCESS_ERR }; + return DOMTimer::install(*context, WTFMove(action), std::chrono::milliseconds(timeout), false); } void DOMWindow::clearInterval(int timeoutId) @@ -1587,65 +1703,74 @@ void DOMWindow::clearInterval(int timeoutId) ScriptExecutionContext* context = scriptExecutionContext(); if (!context) return; - DOMTimer::removeById(context, timeoutId); + DOMTimer::removeById(*context, timeoutId); } -#if ENABLE(REQUEST_ANIMATION_FRAME) -int DOMWindow::requestAnimationFrame(PassRefPtr callback) +int DOMWindow::requestAnimationFrame(Ref&& callback) { callback->m_useLegacyTimeBase = false; - if (Document* d = document()) - return d->requestAnimationFrame(callback); - return 0; + auto* document = this->document(); + if (!document) + return 0; + return document->requestAnimationFrame(WTFMove(callback)); } -int DOMWindow::webkitRequestAnimationFrame(PassRefPtr callback) +int DOMWindow::webkitRequestAnimationFrame(Ref&& callback) { callback->m_useLegacyTimeBase = true; - if (Document* d = document()) - return d->requestAnimationFrame(callback); - return 0; + auto* document = this->document(); + if (!document) + return 0; + return document->requestAnimationFrame(WTFMove(callback)); } void DOMWindow::cancelAnimationFrame(int id) { - if (Document* d = document()) - d->cancelAnimationFrame(id); -} -#endif - -#if ENABLE(CSS3_CONDITIONAL_RULES) -DOMWindowCSS* DOMWindow::css() -{ - if (!m_css) - m_css = DOMWindowCSS::create(); - return m_css.get(); + auto* document = this->document(); + if (!document) + return; + document->cancelAnimationFrame(id); } -#endif -static void didAddStorageEventListener(DOMWindow* window) +static void didAddStorageEventListener(DOMWindow& window) { // Creating these WebCore::Storage objects informs the system that we'd like to receive // notifications about storage events that might be triggered in other processes. Rather // than subscribe to these notifications explicitly, we subscribe to them implicitly to // simplify the work done by the system. - window->localStorage(IGNORE_EXCEPTION); - window->sessionStorage(IGNORE_EXCEPTION); + window.localStorage(); + window.sessionStorage(); } -bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtr listener, bool useCapture) +bool DOMWindow::isSameSecurityOriginAsMainFrame() const { - if (!EventTarget::addEventListener(eventType, listener, useCapture)) + if (!m_frame || !m_frame->page() || !document()) + return false; + + if (m_frame->isMainFrame()) + return true; + + Document* mainFrameDocument = m_frame->mainFrame().document(); + + if (mainFrameDocument && document()->securityOrigin().canAccess(mainFrameDocument->securityOrigin())) + return true; + + return false; +} + +bool DOMWindow::addEventListener(const AtomicString& eventType, Ref&& listener, const AddEventListenerOptions& options) +{ + if (!EventTarget::addEventListener(eventType, WTFMove(listener), options)) return false; if (Document* document = this->document()) { document->addListenerTypeIfNeeded(eventType); - if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) - document->didAddWheelEventHandler(); + if (eventNames().isWheelEventType(eventType)) + document->didAddWheelEventHandler(*document); else if (eventNames().isTouchEventType(eventType)) - document->didAddTouchEventHandler(document); + document->didAddTouchEventHandler(*document); else if (eventType == eventNames().storageEvent) - didAddStorageEventListener(this); + didAddStorageEventListener(*this); } if (eventType == eventNames().unloadEvent) @@ -1654,17 +1779,28 @@ bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtrdeviceMotionController()->addDeviceEventListener(this); - else if (eventType == eventNames().deviceorientationEvent && document()) - document()->deviceOrientationController()->addDeviceEventListener(this); + else if ((eventType == eventNames().devicemotionEvent || eventType == eventNames().deviceorientationEvent) && document()) { + if (isSameSecurityOriginAsMainFrame()) { + if (eventType == eventNames().deviceorientationEvent) + document()->deviceOrientationController()->addDeviceEventListener(this); + else + document()->deviceMotionController()->addDeviceEventListener(this); + } else if (document()) + document()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("Blocked attempt add device motion or orientation listener from child frame that wasn't the same security origin as the main page.")); + } #else - else if (eventType == eventNames().devicemotionEvent && RuntimeEnabledFeatures::sharedFeatures().deviceMotionEnabled()) { - if (DeviceMotionController* controller = DeviceMotionController::from(page())) - controller->addDeviceEventListener(this); - } else if (eventType == eventNames().deviceorientationEvent && RuntimeEnabledFeatures::sharedFeatures().deviceOrientationEnabled()) { - if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) - controller->addDeviceEventListener(this); + else if (eventType == eventNames().devicemotionEvent) { + if (isSameSecurityOriginAsMainFrame()) { + if (DeviceMotionController* controller = DeviceMotionController::from(page())) + controller->addDeviceEventListener(this); + } else if (document()) + document()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("Blocked attempt add device motion listener from child frame that wasn't the same security origin as the main page.")); + } else if (eventType == eventNames().deviceorientationEvent) { + if (isSameSecurityOriginAsMainFrame()) { + if (DeviceOrientationController* controller = DeviceOrientationController::from(page())) + controller->addDeviceEventListener(this); + } else if (document()) + document()->addConsoleMessage(MessageSource::JS, MessageLevel::Warning, ASCIILiteral("Blocked attempt add device orientation listener from child frame that wasn't the same security origin as the main page.")); } #endif // PLATFORM(IOS) #endif // ENABLE(DEVICE_ORIENTATION) @@ -1680,7 +1816,10 @@ bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtrdocument(); if (++m_scrollEventListenerCount == 1 && document == &document->topDocument()) { Frame* frame = this->frame(); if (frame && frame->page()) - frame->page()->chrome().client().setNeedsScrollNotifications(frame, true); + frame->page()->chrome().client().setNeedsScrollNotifications(*frame, true); } } @@ -1707,31 +1847,32 @@ void DOMWindow::decrementScrollEventListenersCount() Document* document = this->document(); if (!--m_scrollEventListenerCount && document == &document->topDocument()) { Frame* frame = this->frame(); - if (frame && frame->page() && !document->inPageCache()) - frame->page()->chrome().client().setNeedsScrollNotifications(frame, false); + if (frame && frame->page() && document->pageCacheState() == Document::NotInPageCache) + frame->page()->chrome().client().setNeedsScrollNotifications(*frame, false); } } + #endif void DOMWindow::resetAllGeolocationPermission() { - // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to Geolocation.cpp. + // FIXME: Can we remove the PLATFORM(IOS)-guard? #if ENABLE(GEOLOCATION) && PLATFORM(IOS) if (m_navigator) NavigatorGeolocation::from(m_navigator.get())->resetAllGeolocationPermission(); #endif } -bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener* listener, bool useCapture) +bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options) { - if (!EventTarget::removeEventListener(eventType, listener, useCapture)) + if (!EventTarget::removeEventListener(eventType, listener, options.capture)) return false; if (Document* document = this->document()) { - if (eventType == eventNames().wheelEvent || eventType == eventNames().mousewheelEvent) - document->didRemoveWheelEventHandler(); + if (eventNames().isWheelEventType(eventType)) + document->didRemoveWheelEventHandler(*document); else if (eventNames().isTouchEventType(eventType)) - document->didRemoveTouchEventHandler(document); + document->didRemoveTouchEventHandler(*document); } if (eventType == eventNames().unloadEvent) @@ -1770,7 +1911,10 @@ bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener --m_touchEventListenerCount; } #endif - +#if ENABLE(GAMEPAD) + else if (eventNames().isGamepadEventType(eventType)) + decrementGamepadEventListenerCount(); +#endif #if ENABLE(PROXIMITY_EVENTS) else if (eventType == eventNames().webkitdeviceproximityEvent) { if (DeviceProximityController* controller = DeviceProximityController::from(page())) @@ -1781,58 +1925,63 @@ bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener return true; } +void DOMWindow::languagesChanged() +{ + if (auto* document = this->document()) + document->enqueueWindowEvent(Event::create(eventNames().languagechangeEvent, false, false)); +} + void DOMWindow::dispatchLoadEvent() { - RefPtr loadEvent(Event::create(eventNames().loadEvent, false, false)); - if (m_frame && m_frame->loader().documentLoader() && !m_frame->loader().documentLoader()->timing()->loadEventStart()) { - // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed while dispatching + Ref loadEvent = Event::create(eventNames().loadEvent, false, false); + if (m_frame && m_frame->loader().documentLoader() && !m_frame->loader().documentLoader()->timing().loadEventStart()) { + // The DocumentLoader (and thus its LoadTiming) might get destroyed while dispatching // the event, so protect it to prevent writing the end time into freed memory. RefPtr documentLoader = m_frame->loader().documentLoader(); - DocumentLoadTiming* timing = documentLoader->timing(); - timing->markLoadEventStart(); + LoadTiming& timing = documentLoader->timing(); + timing.markLoadEventStart(); dispatchEvent(loadEvent, document()); - timing->markLoadEventEnd(); + timing.markLoadEventEnd(); } else dispatchEvent(loadEvent, document()); // For load events, send a separate load event to the enclosing frame only. // This is a DOM extension and is independent of bubbling/capturing rules of // the DOM. - Element* ownerElement = m_frame ? m_frame->ownerElement() : 0; + Element* ownerElement = m_frame ? m_frame->ownerElement() : nullptr; if (ownerElement) ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); InspectorInstrumentation::loadEventFired(frame()); } -bool DOMWindow::dispatchEvent(PassRefPtr prpEvent, PassRefPtr prpTarget) +bool DOMWindow::dispatchEvent(Event& event, EventTarget* target) { - Ref protect(*this); - RefPtr event = prpEvent; + Ref protectedThis(*this); // Pausing a page may trigger pagehide and pageshow events. WebCore also implicitly fires these // events when closing a WebView. Here we keep track of the state of the page to prevent duplicate, // unbalanced events per the definition of the pageshow event: // . - if (event->eventInterface() == PageTransitionEventInterfaceType) { - if (event->type() == eventNames().pageshowEvent) { - if (m_lastPageStatus == PageStatusShown) + if (event.eventInterface() == PageTransitionEventInterfaceType) { + if (event.type() == eventNames().pageshowEvent) { + if (m_lastPageStatus == PageStatus::Shown) return true; // Event was previously dispatched; do not fire a duplicate event. - m_lastPageStatus = PageStatusShown; - } else if (event->type() == eventNames().pagehideEvent) { - if (m_lastPageStatus == PageStatusHidden) + m_lastPageStatus = PageStatus::Shown; + } else if (event.type() == eventNames().pagehideEvent) { + if (m_lastPageStatus == PageStatus::Hidden) return true; // Event was previously dispatched; do not fire a duplicate event. - m_lastPageStatus = PageStatusHidden; + m_lastPageStatus = PageStatus::Hidden; } } - event->setTarget(prpTarget ? prpTarget : this); - event->setCurrentTarget(this); - event->setEventPhase(Event::AT_TARGET); + event.setTarget(target ? target : this); + event.setCurrentTarget(this); + event.setEventPhase(Event::AT_TARGET); - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), *event, this); + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEventOnWindow(frame(), event, *this); - bool result = fireEventListeners(event.get()); + bool result = fireEventListeners(event); InspectorInstrumentation::didDispatchEventOnWindow(cookie); @@ -1870,7 +2019,7 @@ void DOMWindow::removeAllEventListeners() #if ENABLE(TOUCH_EVENTS) if (Document* document = this->document()) - document->didRemoveEventTargetNode(document); + document->didRemoveEventTargetNode(*document); #endif #if ENABLE(PROXIMITY_EVENTS) @@ -1878,6 +2027,11 @@ void DOMWindow::removeAllEventListeners() controller->removeAllDeviceEventListeners(this); #endif +#if ENABLE(WEB_TIMING) + if (m_performance) + m_performance->removeAllEventListeners(); +#endif + removeAllUnloadEventListeners(this); removeAllBeforeUnloadEventListeners(this); } @@ -1896,11 +2050,12 @@ void DOMWindow::finishedLoading() { if (m_shouldPrintWhenFinishedLoading) { m_shouldPrintWhenFinishedLoading = false; - print(); + if (m_frame->loader().activeDocumentLoader()->mainDocumentError().isNull()) + print(); } } -void DOMWindow::setLocation(const String& urlString, DOMWindow& activeWindow, DOMWindow& firstWindow, SetLocationLocking locking) +void DOMWindow::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& urlString, SetLocationLocking locking) { if (!isCurrentlyDisplayedInFrame()) return; @@ -1924,11 +2079,12 @@ void DOMWindow::setLocation(const String& urlString, DOMWindow& activeWindow, DO return; // We want a new history item if we are processing a user gesture. - m_frame->navigationScheduler().scheduleLocationChange(activeDocument->securityOrigin(), + LockHistory lockHistory = (locking != LockHistoryBasedOnGestureState || !ScriptController::processingUserGesture()) ? LockHistory::Yes : LockHistory::No; + LockBackForwardList lockBackForwardList = (locking != LockHistoryBasedOnGestureState) ? LockBackForwardList::Yes : LockBackForwardList::No; + m_frame->navigationScheduler().scheduleLocationChange(*activeDocument, activeDocument->securityOrigin(), // FIXME: What if activeDocument()->frame() is 0? completedURL, activeDocument->frame()->loader().outgoingReferrer(), - locking != LockHistoryBasedOnGestureState || !ScriptController::processingUserGesture(), - locking != LockHistoryBasedOnGestureState); + lockHistory, lockBackForwardList); } void DOMWindow::printErrorMessage(const String& message) @@ -1936,7 +2092,8 @@ void DOMWindow::printErrorMessage(const String& message) if (message.isEmpty()) return; - pageConsole()->addMessage(JSMessageSource, ErrorMessageLevel, message); + if (PageConsoleClient* pageConsole = console()) + pageConsole->addMessage(MessageSource::JS, MessageLevel::Error, message); } String DOMWindow::crossDomainAccessErrorMessage(const DOMWindow& activeWindow) @@ -1945,18 +2102,18 @@ String DOMWindow::crossDomainAccessErrorMessage(const DOMWindow& activeWindow) if (activeWindowURL.isNull()) return String(); - ASSERT(!activeWindow.document()->securityOrigin()->canAccess(document()->securityOrigin())); + ASSERT(!activeWindow.document()->securityOrigin().canAccess(document()->securityOrigin())); // FIXME: This message, and other console messages, have extra newlines. Should remove them. - SecurityOrigin* activeOrigin = activeWindow.document()->securityOrigin(); - SecurityOrigin* targetOrigin = document()->securityOrigin(); - String message = "Blocked a frame with origin \"" + activeOrigin->toString() + "\" from accessing a frame with origin \"" + targetOrigin->toString() + "\". "; + SecurityOrigin& activeOrigin = activeWindow.document()->securityOrigin(); + SecurityOrigin& targetOrigin = document()->securityOrigin(); + String message = "Blocked a frame with origin \"" + activeOrigin.toString() + "\" from accessing a frame with origin \"" + targetOrigin.toString() + "\". "; // Sandbox errors: Use the origin of the frames' location, rather than their actual origin (since we know that at least one will be "null"). URL activeURL = activeWindow.document()->url(); URL targetURL = document()->url(); if (document()->isSandboxed(SandboxOrigin) || activeWindow.document()->isSandboxed(SandboxOrigin)) { - message = "Blocked a frame at \"" + SecurityOrigin::create(activeURL)->toString() + "\" from accessing a frame at \"" + SecurityOrigin::create(targetURL)->toString() + "\". "; + message = "Blocked a frame at \"" + SecurityOrigin::create(activeURL).get().toString() + "\" from accessing a frame at \"" + SecurityOrigin::create(targetURL).get().toString() + "\". "; if (document()->isSandboxed(SandboxOrigin) && activeWindow.document()->isSandboxed(SandboxOrigin)) return "Sandbox access violation: " + message + " Both frames are sandboxed and lack the \"allow-same-origin\" flag."; if (document()->isSandboxed(SandboxOrigin)) @@ -1965,16 +2122,16 @@ String DOMWindow::crossDomainAccessErrorMessage(const DOMWindow& activeWindow) } // Protocol errors: Use the URL's protocol rather than the origin's protocol so that we get a useful message for non-heirarchal URLs like 'data:'. - if (targetOrigin->protocol() != activeOrigin->protocol()) + if (targetOrigin.protocol() != activeOrigin.protocol()) return message + " The frame requesting access has a protocol of \"" + activeURL.protocol() + "\", the frame being accessed has a protocol of \"" + targetURL.protocol() + "\". Protocols must match.\n"; // 'document.domain' errors. - if (targetOrigin->domainWasSetInDOM() && activeOrigin->domainWasSetInDOM()) - return message + "The frame requesting access set \"document.domain\" to \"" + activeOrigin->domain() + "\", the frame being accessed set it to \"" + targetOrigin->domain() + "\". Both must set \"document.domain\" to the same value to allow access."; - if (activeOrigin->domainWasSetInDOM()) - return message + "The frame requesting access set \"document.domain\" to \"" + activeOrigin->domain() + "\", but the frame being accessed did not. Both must set \"document.domain\" to the same value to allow access."; - if (targetOrigin->domainWasSetInDOM()) - return message + "The frame being accessed set \"document.domain\" to \"" + targetOrigin->domain() + "\", but the frame requesting access did not. Both must set \"document.domain\" to the same value to allow access."; + if (targetOrigin.domainWasSetInDOM() && activeOrigin.domainWasSetInDOM()) + return message + "The frame requesting access set \"document.domain\" to \"" + activeOrigin.domain() + "\", the frame being accessed set it to \"" + targetOrigin.domain() + "\". Both must set \"document.domain\" to the same value to allow access."; + if (activeOrigin.domainWasSetInDOM()) + return message + "The frame requesting access set \"document.domain\" to \"" + activeOrigin.domain() + "\", but the frame being accessed did not. Both must set \"document.domain\" to the same value to allow access."; + if (targetOrigin.domainWasSetInDOM()) + return message + "The frame being accessed set \"document.domain\" to \"" + targetOrigin.domain() + "\", but the frame requesting access did not. Both must set \"document.domain\" to the same value to allow access."; // Default. return message + "Protocols, domains, and ports must match."; @@ -1996,7 +2153,7 @@ bool DOMWindow::isInsecureScriptAccess(DOMWindow& activeWindow, const String& ur // FIXME: The name canAccess seems to be a roundabout way to ask "can execute script". // Can we name the SecurityOrigin function better to make this more clear? - if (activeWindow.document()->securityOrigin()->canAccess(document()->securityOrigin())) + if (activeWindow.document()->securityOrigin().canAccess(document()->securityOrigin())) return false; } @@ -2004,76 +2161,98 @@ bool DOMWindow::isInsecureScriptAccess(DOMWindow& activeWindow, const String& ur return true; } -PassRefPtr DOMWindow::createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures& windowFeatures, DOMWindow& activeWindow, Frame* firstFrame, Frame* openerFrame, std::function prepareDialogFunction) +RefPtr DOMWindow::createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures& windowFeatures, DOMWindow& activeWindow, Frame& firstFrame, Frame& openerFrame, std::function prepareDialogFunction) { Frame* activeFrame = activeWindow.frame(); + if (!activeFrame) + return nullptr; + + Document* activeDocument = activeWindow.document(); + if (!activeDocument) + return nullptr; - URL completedURL = urlString.isEmpty() ? URL(ParsedURLString, emptyString()) : firstFrame->document()->completeURL(urlString); + URL completedURL = urlString.isEmpty() ? URL(ParsedURLString, emptyString()) : firstFrame.document()->completeURL(urlString); if (!completedURL.isEmpty() && !completedURL.isValid()) { // Don't expose client code to invalid URLs. activeWindow.printErrorMessage("Unable to open a window with invalid URL '" + completedURL.string() + "'.\n"); - return 0; + return nullptr; } // For whatever reason, Firefox uses the first frame to determine the outgoingReferrer. We replicate that behavior here. - String referrer = SecurityPolicy::generateReferrerHeader(firstFrame->document()->referrerPolicy(), completedURL, firstFrame->loader().outgoingReferrer()); + String referrer = SecurityPolicy::generateReferrerHeader(firstFrame.document()->referrerPolicy(), completedURL, firstFrame.loader().outgoingReferrer()); ResourceRequest request(completedURL, referrer); - FrameLoader::addHTTPOriginIfNeeded(request, firstFrame->loader().outgoingOrigin()); - FrameLoadRequest frameRequest(activeWindow.document()->securityOrigin(), request, frameName); + FrameLoader::addHTTPOriginIfNeeded(request, firstFrame.loader().outgoingOrigin()); + FrameLoadRequest frameRequest(activeDocument->securityOrigin(), request, frameName, LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, ReplaceDocumentIfJavaScriptURL, activeDocument->shouldOpenExternalURLsPolicyToPropagate()); // We pass the opener frame for the lookupFrame in case the active frame is different from // the opener frame, and the name references a frame relative to the opener frame. bool created; - RefPtr newFrame = WebCore::createWindow(activeFrame, openerFrame, frameRequest, windowFeatures, created); + RefPtr newFrame = WebCore::createWindow(*activeFrame, openerFrame, frameRequest, windowFeatures, created); if (!newFrame) - return 0; + return nullptr; - newFrame->loader().setOpener(openerFrame); + newFrame->loader().setOpener(&openerFrame); newFrame->page()->setOpenedByDOM(); if (newFrame->document()->domWindow()->isInsecureScriptAccess(activeWindow, completedURL)) - return newFrame.release(); + return newFrame; if (prepareDialogFunction) prepareDialogFunction(*newFrame->document()->domWindow()); - if (created) - newFrame->loader().changeLocation(activeWindow.document()->securityOrigin(), completedURL, referrer, false, false); - else if (!urlString.isEmpty()) { - bool lockHistory = !ScriptController::processingUserGesture(); - newFrame->navigationScheduler().scheduleLocationChange(activeWindow.document()->securityOrigin(), completedURL.string(), referrer, lockHistory, false); + if (created) { + ResourceRequest resourceRequest(completedURL, referrer, UseProtocolCachePolicy); + FrameLoadRequest frameRequest(activeWindow.document()->securityOrigin(), resourceRequest, "_self", LockHistory::No, LockBackForwardList::No, MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Allow, activeDocument->shouldOpenExternalURLsPolicyToPropagate()); + newFrame->loader().changeLocation(frameRequest); + } else if (!urlString.isEmpty()) { + LockHistory lockHistory = ScriptController::processingUserGesture() ? LockHistory::No : LockHistory::Yes; + newFrame->navigationScheduler().scheduleLocationChange(*activeWindow.document(), activeWindow.document()->securityOrigin(), completedURL, referrer, lockHistory, LockBackForwardList::No); } // Navigating the new frame could result in it being detached from its page by a navigation policy delegate. if (!newFrame->page()) - return 0; + return nullptr; - return newFrame.release(); + return newFrame; } -PassRefPtr DOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, - DOMWindow& activeWindow, DOMWindow& firstWindow) +RefPtr DOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow) { if (!isCurrentlyDisplayedInFrame()) - return 0; - Document* activeDocument = activeWindow.document(); + return nullptr; + + auto* activeDocument = activeWindow.document(); if (!activeDocument) - return 0; - Frame* firstFrame = firstWindow.frame(); + return nullptr; + + auto* firstFrame = firstWindow.frame(); if (!firstFrame) - return 0; + return nullptr; + +#if ENABLE(CONTENT_EXTENSIONS) + if (firstFrame->document() + && firstFrame->page() + && firstFrame->mainFrame().document() + && firstFrame->mainFrame().document()->loader()) { + ResourceLoadInfo resourceLoadInfo { firstFrame->document()->completeURL(urlString), firstFrame->mainFrame().document()->url(), ResourceType::Popup }; + for (auto& action : firstFrame->page()->userContentProvider().actionsForResourceLoad(resourceLoadInfo, *firstFrame->mainFrame().document()->loader())) { + if (action.type() == ContentExtensions::ActionType::BlockLoad) + return nullptr; + } + } +#endif if (!firstWindow.allowPopUp()) { - // Because FrameTree::find() returns true for empty strings, we must check for empty frame names. + // Because FrameTree::findFrameForNavigation() returns true for empty strings, we must check for empty frame names. // Otherwise, illegitimate window.open() calls with no name will pass right through the popup blocker. - if (frameName.isEmpty() || !m_frame->tree().find(frameName)) - return 0; + if (frameName.isEmpty() || !m_frame->loader().findFrameForNavigation(frameName, activeDocument)) + return nullptr; } // Get the target frame for the special cases of _top and _parent. // In those cases, we schedule a location change right now and return early. - Frame* targetFrame = 0; + Frame* targetFrame = nullptr; if (frameName == "_top") targetFrame = &m_frame->tree().top(); else if (frameName == "_parent") { @@ -2084,7 +2263,7 @@ PassRefPtr DOMWindow::open(const String& urlString, const AtomicStrin } if (targetFrame) { if (!activeDocument->canNavigate(targetFrame)) - return 0; + return nullptr; URL completedURL = firstFrame->document()->completeURL(urlString); @@ -2096,43 +2275,39 @@ PassRefPtr DOMWindow::open(const String& urlString, const AtomicStrin // For whatever reason, Firefox uses the first window rather than the active window to // determine the outgoing referrer. We replicate that behavior here. - bool lockHistory = !ScriptController::processingUserGesture(); - targetFrame->navigationScheduler().scheduleLocationChange( - activeDocument->securityOrigin(), - completedURL, - firstFrame->loader().outgoingReferrer(), - lockHistory, - false); + LockHistory lockHistory = ScriptController::processingUserGesture() ? LockHistory::No : LockHistory::Yes; + targetFrame->navigationScheduler().scheduleLocationChange(*activeDocument, activeDocument->securityOrigin(), completedURL, firstFrame->loader().outgoingReferrer(), + lockHistory, LockBackForwardList::No); return targetFrame->document()->domWindow(); } - WindowFeatures windowFeatures(windowFeaturesString); - RefPtr result = createWindow(urlString, frameName, windowFeatures, activeWindow, firstFrame, m_frame); - return result ? result->document()->domWindow() : 0; + RefPtr result = createWindow(urlString, frameName, parseWindowFeatures(windowFeaturesString), activeWindow, *firstFrame, *m_frame); + return result ? result->document()->domWindow() : nullptr; } void DOMWindow::showModalDialog(const String& urlString, const String& dialogFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow, std::function prepareDialogFunction) { if (!isCurrentlyDisplayedInFrame()) return; - Frame* activeFrame = activeWindow.frame(); - if (!activeFrame) + if (!activeWindow.frame()) return; Frame* firstFrame = firstWindow.frame(); if (!firstFrame) return; - // Pages are not allowed to cause modal alerts during BeforeUnload dispatch. - if (page() && page()->isAnyFrameHandlingBeforeUnloadEvent()) { - printErrorMessage("Use of window.showModalDialog is not allowed during beforeunload event dispatch."); + auto* page = m_frame->page(); + if (!page) + return; + + if (!page->arePromptsAllowed()) { + printErrorMessage("Use of window.showModalDialog is not allowed while unloading a page."); return; } - if (!canShowModalDialogNow(m_frame) || !firstWindow.allowPopUp()) + if (!canShowModalDialog(*m_frame) || !firstWindow.allowPopUp()) return; - WindowFeatures windowFeatures(dialogFeaturesString, screenAvailableRect(m_frame->view())); - RefPtr dialogFrame = createWindow(urlString, emptyAtom, windowFeatures, activeWindow, firstFrame, m_frame, std::move(prepareDialogFunction)); + RefPtr dialogFrame = createWindow(urlString, emptyAtom, parseDialogFeatures(dialogFeaturesString, screenAvailableRect(m_frame->view())), activeWindow, *firstFrame, *m_frame, WTFMove(prepareDialogFunction)); if (!dialogFrame) return; dialogFrame->page()->chrome().runModal(); diff --git a/Source/WebCore/page/DOMWindow.h b/Source/WebCore/page/DOMWindow.h index b681f77c3..584ba5afc 100644 --- a/Source/WebCore/page/DOMWindow.h +++ b/Source/WebCore/page/DOMWindow.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple 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 @@ -11,501 +11,413 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMWindow_h -#define DOMWindow_h +#pragma once +#include "Base64Utilities.h" #include "ContextDestructionObserver.h" #include "EventTarget.h" +#include "ExceptionOr.h" #include "FrameDestructionObserver.h" -#include "URL.h" +#include "ScrollToOptions.h" #include "Supplementable.h" -#include +#include +#include -namespace WebCore { - - class BarProp; - class CSSRuleList; - class CSSStyleDeclaration; - class Console; - class Crypto; - class DOMApplicationCache; - class DOMSelection; - class DOMURL; - class DOMWindowProperty; - class Database; - class DatabaseCallback; - class Document; - class Element; - class EventListener; - class FloatRect; - class Frame; - class History; - class IDBFactory; - class Location; - class MediaQueryList; - class MessageEvent; - class Navigator; - class Node; - class Page; - class PageConsole; - class Performance; - class PostMessageTimer; - class ScheduledAction; - class Screen; - class ScriptCallStack; - class SecurityOrigin; - class SerializedScriptValue; - class Storage; - class StyleMedia; - class WebKitPoint; - class DOMWindowCSS; - -#if ENABLE(REQUEST_ANIMATION_FRAME) - class RequestAnimationFrameCallback; -#endif - - struct WindowFeatures; - - typedef Vector, 1> MessagePortArray; - - typedef int ExceptionCode; - - enum SetLocationLocking { LockHistoryBasedOnGestureState, LockHistoryAndBackForwardList }; - - // FIXME: DOMWindow shouldn't subclass FrameDestructionObserver and instead should get to Frame via its Document. - class DOMWindow final - : public RefCounted - , public EventTargetWithInlineData - , public ContextDestructionObserver - , public FrameDestructionObserver - , public Supplementable { - public: - static PassRefPtr create(Document* document) { return adoptRef(new DOMWindow(document)); } - virtual ~DOMWindow(); - - // In some rare cases, we'll re-used a DOMWindow for a new Document. For example, - // when a script calls window.open("..."), the browser gives JavaScript a window - // synchronously but kicks off the load in the window asynchronously. Web sites - // expect that modifications that they make to the window object synchronously - // won't be blown away when the network load commits. To make that happen, we - // "securely transition" the existing DOMWindow to the Document that results from - // the network load. See also SecurityContext::isSecureTransitionTo. - void didSecureTransitionTo(Document*); +namespace JSC { +class ExecState; +class JSObject; +class JSValue; +template class Strong; +} - virtual EventTargetInterface eventTargetInterface() const override { return DOMWindowEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override { return ContextDestructionObserver::scriptExecutionContext(); } - - virtual DOMWindow* toDOMWindow() override; - - void registerProperty(DOMWindowProperty*); - void unregisterProperty(DOMWindowProperty*); - - void resetUnlessSuspendedForPageCache(); - void suspendForPageCache(); - void resumeFromPageCache(); - - PassRefPtr matchMedia(const String&); +namespace WebCore { - unsigned pendingUnloadEventListeners() const; +class BarProp; +class CSSRuleList; +class CSSStyleDeclaration; +class Crypto; +class CustomElementRegistry; +class DOMApplicationCache; +class DOMSelection; +class DOMWindowProperty; +class DOMWrapperWorld; +class Document; +class Element; +class EventListener; +class FloatRect; +class History; +class Location; +class MediaQueryList; +class Navigator; +class Node; +class NodeList; +class Page; +class PageConsoleClient; +class Performance; +class PostMessageTimer; +class RequestAnimationFrameCallback; +class ScheduledAction; +class Screen; +class Storage; +class StyleMedia; +class WebKitNamespace; +class WebKitPoint; + +struct WindowFeatures; + +enum SetLocationLocking { LockHistoryBasedOnGestureState, LockHistoryAndBackForwardList }; + +// FIXME: DOMWindow shouldn't subclass FrameDestructionObserver and instead should get to Frame via its Document. +class DOMWindow final + : public RefCounted + , public EventTargetWithInlineData + , public ContextDestructionObserver + , public FrameDestructionObserver + , public Base64Utilities + , public Supplementable { +public: + static Ref create(Document& document) { return adoptRef(*new DOMWindow(document)); } + WEBCORE_EXPORT virtual ~DOMWindow(); + + // In some rare cases, we'll reuse a DOMWindow for a new Document. For example, + // when a script calls window.open("..."), the browser gives JavaScript a window + // synchronously but kicks off the load in the window asynchronously. Web sites + // expect that modifications that they make to the window object synchronously + // won't be blown away when the network load commits. To make that happen, we + // "securely transition" the existing DOMWindow to the Document that results from + // the network load. See also SecurityContext::isSecureTransitionTo. + void didSecureTransitionTo(Document&); + + void registerProperty(DOMWindowProperty&); + void unregisterProperty(DOMWindowProperty&); + + void resetUnlessSuspendedForDocumentSuspension(); + void suspendForDocumentSuspension(); + void resumeFromDocumentSuspension(); + + RefPtr matchMedia(const String&); + + WEBCORE_EXPORT unsigned pendingUnloadEventListeners() const; + + WEBCORE_EXPORT static bool dispatchAllPendingBeforeUnloadEvents(); + WEBCORE_EXPORT static void dispatchAllPendingUnloadEvents(); + + static FloatRect adjustWindowRect(Page&, const FloatRect& pendingChanges); + + bool allowPopUp(); // Call on first window, not target window. + static bool allowPopUp(Frame& firstFrame); + static bool canShowModalDialog(const Frame&); + WEBCORE_EXPORT void setCanShowModalDialogOverride(bool); + + Screen* screen() const; + History* history() const; + Crypto* crypto() const; + BarProp* locationbar() const; + BarProp* menubar() const; + BarProp* personalbar() const; + BarProp* scrollbars() const; + BarProp* statusbar() const; + BarProp* toolbar() const; + Navigator* navigator() const; + Navigator* clientInformation() const { return navigator(); } + + Location* location() const; + void setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& location, SetLocationLocking = LockHistoryBasedOnGestureState); + + DOMSelection* getSelection(); + + Element* frameElement() const; + + WEBCORE_EXPORT void focus(bool allowFocus = false); + void focus(DOMWindow& callerWindow); + void blur(); + WEBCORE_EXPORT void close(); + void close(Document&); + void print(); + void stop(); + + WEBCORE_EXPORT RefPtr open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow); + + void showModalDialog(const String& urlString, const String& dialogFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow, std::function prepareDialogFunction); + + void alert(const String& message = emptyString()); + bool confirm(const String& message); + String prompt(const String& message, const String& defaultValue); - static bool dispatchAllPendingBeforeUnloadEvents(); - static void dispatchAllPendingUnloadEvents(); + bool find(const String&, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool searchInFrames, bool showDialog) const; - static FloatRect adjustWindowRect(Page*, const FloatRect& pendingChanges); + bool offscreenBuffering() const; - bool allowPopUp(); // Call on first window, not target window. - static bool allowPopUp(Frame* firstFrame); - static bool canShowModalDialog(const Frame*); - static bool canShowModalDialogNow(const Frame*); + int outerHeight() const; + int outerWidth() const; + int innerHeight() const; + int innerWidth() const; + int screenX() const; + int screenY() const; + int screenLeft() const { return screenX(); } + int screenTop() const { return screenY(); } + int scrollX() const; + int scrollY() const; - // DOM Level 0 + bool closed() const; - Screen* screen() const; - History* history() const; - Crypto* crypto() const; - BarProp* locationbar() const; - BarProp* menubar() const; - BarProp* personalbar() const; - BarProp* scrollbars() const; - BarProp* statusbar() const; - BarProp* toolbar() const; - Navigator* navigator() const; - Navigator* clientInformation() const { return navigator(); } + unsigned length() const; - Location* location() const; - void setLocation(const String& location, DOMWindow& activeWindow, DOMWindow& firstWindow, - SetLocationLocking = LockHistoryBasedOnGestureState); + String name() const; + void setName(const String&); - DOMSelection* getSelection(); + String status() const; + void setStatus(const String&); + String defaultStatus() const; + void setDefaultStatus(const String&); - Element* frameElement() const; + // Self-referential attributes - void focus(ScriptExecutionContext* = 0); - void blur(); - void close(ScriptExecutionContext* = 0); - void print(); - void stop(); + DOMWindow* self() const; + DOMWindow* window() const { return self(); } + DOMWindow* frames() const { return self(); } - PassRefPtr open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, - DOMWindow& activeWindow, DOMWindow& firstWindow); + DOMWindow* opener() const; + DOMWindow* parent() const; + DOMWindow* top() const; - void showModalDialog(const String& urlString, const String& dialogFeaturesString, DOMWindow& activeWindow, DOMWindow& firstWindow, std::function prepareDialogFunction); + // DOM Level 2 AbstractView Interface - void alert(const String& message); - bool confirm(const String& message); - String prompt(const String& message, const String& defaultValue); - String btoa(const String& stringToEncode, ExceptionCode&); - String atob(const String& encodedString, ExceptionCode&); + WEBCORE_EXPORT Document* document() const; - bool find(const String&, bool caseSensitive, bool backwards, bool wrap, bool wholeWord, bool searchInFrames, bool showDialog) const; + // CSSOM View Module - bool offscreenBuffering() const; + RefPtr styleMedia() const; - int outerHeight() const; - int outerWidth() const; - int innerHeight() const; - int innerWidth() const; - int screenX() const; - int screenY() const; - int screenLeft() const { return screenX(); } - int screenTop() const { return screenY(); } - int scrollX() const; - int scrollY() const; - int pageXOffset() const { return scrollX(); } - int pageYOffset() const { return scrollY(); } + // DOM Level 2 Style Interface - bool closed() const; + WEBCORE_EXPORT Ref getComputedStyle(Element&, const String& pseudoElt) const; - unsigned length() const; + // WebKit extensions - String name() const; - void setName(const String&); + WEBCORE_EXPORT RefPtr getMatchedCSSRules(Element*, const String& pseudoElt, bool authorOnly = true) const; + double devicePixelRatio() const; - String status() const; - void setStatus(const String&); - String defaultStatus() const; - void setDefaultStatus(const String&); + RefPtr webkitConvertPointFromPageToNode(Node*, const WebKitPoint*) const; + RefPtr webkitConvertPointFromNodeToPage(Node*, const WebKitPoint*) const; - // Self-referential attributes + PageConsoleClient* console() const; - DOMWindow* self() const; - DOMWindow* window() const { return self(); } - DOMWindow* frames() const { return self(); } + void printErrorMessage(const String&); + String crossDomainAccessErrorMessage(const DOMWindow& activeWindow); - DOMWindow* opener() const; - DOMWindow* parent() const; - DOMWindow* top() const; + ExceptionOr postMessage(JSC::ExecState&, DOMWindow& callerWindow, JSC::JSValue message, const String& targetOrigin, Vector>&&); + void postMessageTimerFired(PostMessageTimer&); - // DOM Level 2 AbstractView Interface + void languagesChanged(); - Document* document() const; + void scrollBy(const ScrollToOptions&) const; + void scrollBy(double x, double y) const; + void scrollTo(const ScrollToOptions&) const; + void scrollTo(double x, double y) const; - // CSSOM View Module + void moveBy(float x, float y) const; + void moveTo(float x, float y) const; - PassRefPtr styleMedia() const; + void resizeBy(float x, float y) const; + void resizeTo(float width, float height) const; - // DOM Level 2 Style Interface + // Timers + ExceptionOr setTimeout(std::unique_ptr, int timeout); + void clearTimeout(int timeoutId); + ExceptionOr setInterval(std::unique_ptr, int timeout); + void clearInterval(int timeoutId); - PassRefPtr getComputedStyle(Element*, const String& pseudoElt) const; + int requestAnimationFrame(Ref&&); + int webkitRequestAnimationFrame(Ref&&); + void cancelAnimationFrame(int id); - // WebKit extensions + // Events + // EventTarget API + bool addEventListener(const AtomicString& eventType, Ref&&, const AddEventListenerOptions&) final; + bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) final; + void removeAllEventListeners() final; - PassRefPtr getMatchedCSSRules(Element*, const String& pseudoElt, bool authorOnly = true) const; - double devicePixelRatio() const; + using EventTarget::dispatchEvent; + bool dispatchEvent(Event&, EventTarget*); - PassRefPtr webkitConvertPointFromPageToNode(Node*, const WebKitPoint*) const; - PassRefPtr webkitConvertPointFromNodeToPage(Node*, const WebKitPoint*) const; + void dispatchLoadEvent(); - Console* console() const; - PageConsole* pageConsole() const; + void captureEvents(); + void releaseEvents(); - void printErrorMessage(const String&); - String crossDomainAccessErrorMessage(const DOMWindow& activeWindow); + void finishedLoading(); - void postMessage(PassRefPtr message, const MessagePortArray*, const String& targetOrigin, DOMWindow& source, ExceptionCode&); - // Needed for Objective-C bindings (see bug 28774). - void postMessage(PassRefPtr message, MessagePort*, const String& targetOrigin, DOMWindow& source, ExceptionCode&); - void postMessageTimerFired(PassOwnPtr); - void dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtr, PassRefPtr); + using RefCounted::ref; + using RefCounted::deref; - void scrollBy(int x, int y) const; - void scrollTo(int x, int y) const; - void scroll(int x, int y) const { scrollTo(x, y); } + // HTML 5 key/value storage + ExceptionOr sessionStorage() const; + ExceptionOr localStorage() const; + Storage* optionalSessionStorage() const { return m_sessionStorage.get(); } + Storage* optionalLocalStorage() const { return m_localStorage.get(); } - void moveBy(float x, float y) const; - void moveTo(float x, float y) const; + DOMApplicationCache* applicationCache() const; + DOMApplicationCache* optionalApplicationCache() const { return m_applicationCache.get(); } - void resizeBy(float x, float y) const; - void resizeTo(float width, float height) const; + CustomElementRegistry* customElementRegistry() { return m_customElementRegistry.get(); } + CustomElementRegistry& ensureCustomElementRegistry(); - // Timers - int setTimeout(PassOwnPtr, int timeout, ExceptionCode&); - void clearTimeout(int timeoutId); - int setInterval(PassOwnPtr, int timeout, ExceptionCode&); - void clearInterval(int timeoutId); + ExceptionOr> collectMatchingElementsInFlatTree(Node&, const String& selectors); + ExceptionOr> matchingElementInFlatTree(Node&, const String& selectors); - // WebKit animation extensions -#if ENABLE(REQUEST_ANIMATION_FRAME) - int requestAnimationFrame(PassRefPtr); - int webkitRequestAnimationFrame(PassRefPtr); - void cancelAnimationFrame(int id); +#if ENABLE(ORIENTATION_EVENTS) + // This is the interface orientation in degrees. Some examples are: + // 0 is straight up; -90 is when the device is rotated 90 clockwise; + // 90 is when rotated counter clockwise. + int orientation() const; #endif -#if ENABLE(CSS3_CONDITIONAL_RULES) - DOMWindowCSS* css(); +#if ENABLE(WEB_TIMING) + Performance* performance() const; #endif - // Events - // EventTarget API - virtual bool addEventListener(const AtomicString& eventType, PassRefPtr, bool useCapture) override; - virtual bool removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture) override; - virtual void removeAllEventListeners() override; - - using EventTarget::dispatchEvent; - bool dispatchEvent(PassRefPtr prpEvent, PassRefPtr prpTarget); - - void dispatchLoadEvent(); - - DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); - DEFINE_ATTRIBUTE_EVENT_LISTENER(beforeunload); - DEFINE_ATTRIBUTE_EVENT_LISTENER(blur); - DEFINE_ATTRIBUTE_EVENT_LISTENER(canplay); - DEFINE_ATTRIBUTE_EVENT_LISTENER(canplaythrough); - DEFINE_ATTRIBUTE_EVENT_LISTENER(change); - DEFINE_ATTRIBUTE_EVENT_LISTENER(click); - DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dblclick); - DEFINE_ATTRIBUTE_EVENT_LISTENER(drag); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragenter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragleave); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragover); - DEFINE_ATTRIBUTE_EVENT_LISTENER(dragstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(drop); - DEFINE_ATTRIBUTE_EVENT_LISTENER(durationchange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(emptied); - DEFINE_ATTRIBUTE_EVENT_LISTENER(ended); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - DEFINE_ATTRIBUTE_EVENT_LISTENER(focus); - DEFINE_ATTRIBUTE_EVENT_LISTENER(hashchange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(input); - DEFINE_ATTRIBUTE_EVENT_LISTENER(invalid); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keydown); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keypress); - DEFINE_ATTRIBUTE_EVENT_LISTENER(keyup); - DEFINE_ATTRIBUTE_EVENT_LISTENER(load); - DEFINE_ATTRIBUTE_EVENT_LISTENER(loadeddata); - DEFINE_ATTRIBUTE_EVENT_LISTENER(loadedmetadata); - DEFINE_ATTRIBUTE_EVENT_LISTENER(loadstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(message); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousedown); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseenter); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseleave); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousemove); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseout); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseover); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mouseup); - DEFINE_ATTRIBUTE_EVENT_LISTENER(mousewheel); - DEFINE_ATTRIBUTE_EVENT_LISTENER(offline); - DEFINE_ATTRIBUTE_EVENT_LISTENER(online); - DEFINE_ATTRIBUTE_EVENT_LISTENER(pagehide); - DEFINE_ATTRIBUTE_EVENT_LISTENER(pageshow); - DEFINE_ATTRIBUTE_EVENT_LISTENER(pause); - DEFINE_ATTRIBUTE_EVENT_LISTENER(play); - DEFINE_ATTRIBUTE_EVENT_LISTENER(playing); - DEFINE_ATTRIBUTE_EVENT_LISTENER(popstate); - DEFINE_ATTRIBUTE_EVENT_LISTENER(progress); - DEFINE_ATTRIBUTE_EVENT_LISTENER(ratechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(reset); - DEFINE_ATTRIBUTE_EVENT_LISTENER(resize); - DEFINE_ATTRIBUTE_EVENT_LISTENER(scroll); - DEFINE_ATTRIBUTE_EVENT_LISTENER(search); - DEFINE_ATTRIBUTE_EVENT_LISTENER(seeked); - DEFINE_ATTRIBUTE_EVENT_LISTENER(seeking); - DEFINE_ATTRIBUTE_EVENT_LISTENER(select); - DEFINE_ATTRIBUTE_EVENT_LISTENER(stalled); - DEFINE_ATTRIBUTE_EVENT_LISTENER(storage); - DEFINE_ATTRIBUTE_EVENT_LISTENER(submit); - DEFINE_ATTRIBUTE_EVENT_LISTENER(suspend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(timeupdate); - DEFINE_ATTRIBUTE_EVENT_LISTENER(unload); - DEFINE_ATTRIBUTE_EVENT_LISTENER(volumechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(waiting); - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitbeginfullscreen); - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitendfullscreen); - DEFINE_ATTRIBUTE_EVENT_LISTENER(wheel); - - DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(webkitanimationstart, webkitAnimationStart); - DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(webkitanimationiteration, webkitAnimationIteration); - DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(webkitanimationend, webkitAnimationEnd); - DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(webkittransitionend, webkitTransitionEnd); - DEFINE_MAPPED_ATTRIBUTE_EVENT_LISTENER(transitionend, transitionend); - - void captureEvents(); - void releaseEvents(); - - void finishedLoading(); - - using RefCounted::ref; - using RefCounted::deref; - -#if ENABLE(DEVICE_ORIENTATION) - DEFINE_ATTRIBUTE_EVENT_LISTENER(devicemotion); - DEFINE_ATTRIBUTE_EVENT_LISTENER(deviceorientation); -#endif + double nowTimestamp() const; -#if ENABLE(PROXIMITY_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitdeviceproximity); +#if PLATFORM(IOS) + void incrementScrollEventListenersCount(); + void decrementScrollEventListenersCount(); + unsigned scrollEventListenerCount() const { return m_scrollEventListenerCount; } #endif - // HTML 5 key/value storage - Storage* sessionStorage(ExceptionCode&) const; - Storage* localStorage(ExceptionCode&) const; - Storage* optionalSessionStorage() const { return m_sessionStorage.get(); } - Storage* optionalLocalStorage() const { return m_localStorage.get(); } + void resetAllGeolocationPermission(); - DOMApplicationCache* applicationCache() const; - DOMApplicationCache* optionalApplicationCache() const { return m_applicationCache.get(); } - -#if ENABLE(ORIENTATION_EVENTS) - // This is the interface orientation in degrees. Some examples are: - // 0 is straight up; -90 is when the device is rotated 90 clockwise; - // 90 is when rotated counter clockwise. - int orientation() const; - - DEFINE_ATTRIBUTE_EVENT_LISTENER(orientationchange); +#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) + bool hasTouchEventListeners() const { return m_touchEventListenerCount > 0; } #endif -#if ENABLE(TOUCH_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchstart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchmove); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchend); - DEFINE_ATTRIBUTE_EVENT_LISTENER(touchcancel); +#if ENABLE(USER_MESSAGE_HANDLERS) + bool shouldHaveWebKitNamespaceForWorld(DOMWrapperWorld&); + WebKitNamespace* webkitNamespace() const; #endif -#if ENABLE(IOS_GESTURE_EVENTS) - DEFINE_ATTRIBUTE_EVENT_LISTENER(gesturestart); - DEFINE_ATTRIBUTE_EVENT_LISTENER(gesturechange); - DEFINE_ATTRIBUTE_EVENT_LISTENER(gestureend); -#endif + // FIXME: When this DOMWindow is no longer the active DOMWindow (i.e., + // when its document is no longer the document that is displayed in its + // frame), we would like to zero out m_frame to avoid being confused + // by the document that is currently active in m_frame. + bool isCurrentlyDisplayedInFrame() const; -#if ENABLE(WEB_TIMING) - Performance* performance() const; -#endif + void willDetachDocumentFromFrame(); + void willDestroyCachedFrame(); -#if PLATFORM(IOS) - void incrementScrollEventListenersCount(); - void decrementScrollEventListenersCount(); - unsigned scrollEventListenerCount() const { return m_scrollEventListenerCount; } -#endif + void enableSuddenTermination(); + void disableSuddenTermination(); - void resetAllGeolocationPermission(); + WeakPtr createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); } -#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) - bool hasTouchEventListeners() const { return m_touchEventListenerCount > 0; } -#endif +private: + explicit DOMWindow(Document&); - // FIXME: When this DOMWindow is no longer the active DOMWindow (i.e., - // when its document is no longer the document that is displayed in its - // frame), we would like to zero out m_frame to avoid being confused - // by the document that is currently active in m_frame. - bool isCurrentlyDisplayedInFrame() const; + EventTargetInterface eventTargetInterface() const final { return DOMWindowEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); } - void willDetachDocumentFromFrame(); - void willDestroyCachedFrame(); + DOMWindow* toDOMWindow() final; - void enableSuddenTermination(); - void disableSuddenTermination(); + Page* page(); + bool allowedToChangeWindowGeometry() const; - private: - explicit DOMWindow(Document*); + void frameDestroyed() final; + void willDetachPage() final; - Page* page(); - bool allowedToChangeWindowGeometry() const; + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } - virtual void frameDestroyed() override; - virtual void willDetachPage() override; + static RefPtr createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures&, DOMWindow& activeWindow, Frame& firstFrame, Frame& openerFrame, std::function prepareDialogFunction = nullptr); + bool isInsecureScriptAccess(DOMWindow& activeWindow, const String& urlString); - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } + void resetDOMWindowProperties(); + void disconnectDOMWindowProperties(); + void reconnectDOMWindowProperties(); + void willDestroyDocumentInFrame(); - static PassRefPtr createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures&, DOMWindow& activeWindow, Frame* firstFrame, Frame* openerFrame, std::function prepareDialogFunction = nullptr); - bool isInsecureScriptAccess(DOMWindow& activeWindow, const String& urlString); + bool isSameSecurityOriginAsMainFrame() const; + +#if ENABLE(GAMEPAD) + void incrementGamepadEventListenerCount(); + void decrementGamepadEventListenerCount(); +#endif - void resetDOMWindowProperties(); - void disconnectDOMWindowProperties(); - void reconnectDOMWindowProperties(); - void willDestroyDocumentInFrame(); + bool m_shouldPrintWhenFinishedLoading { false }; + bool m_suspendedForDocumentSuspension { false }; + std::optional m_canShowModalDialogOverride; - bool m_shouldPrintWhenFinishedLoading; - bool m_suspendedForPageCache; + HashSet m_properties; - HashSet m_properties; + mutable RefPtr m_crypto; + mutable RefPtr m_history; + mutable RefPtr m_locationbar; + mutable RefPtr m_media; + mutable RefPtr m_menubar; + mutable RefPtr m_navigator; + mutable RefPtr m_personalbar; + mutable RefPtr m_screen; + mutable RefPtr m_scrollbars; + mutable RefPtr m_selection; + mutable RefPtr m_statusbar; + mutable RefPtr m_toolbar; + mutable RefPtr m_location; - mutable RefPtr m_screen; - mutable RefPtr m_history; - mutable RefPtr m_crypto; - mutable RefPtr m_locationbar; - mutable RefPtr m_menubar; - mutable RefPtr m_personalbar; - mutable RefPtr m_scrollbars; - mutable RefPtr m_statusbar; - mutable RefPtr m_toolbar; - mutable RefPtr m_console; - mutable RefPtr m_navigator; - mutable RefPtr m_location; - mutable RefPtr m_media; + String m_status; + String m_defaultStatus; - String m_status; - String m_defaultStatus; + enum class PageStatus { None, Shown, Hidden }; + PageStatus m_lastPageStatus { PageStatus::None }; - enum PageStatus { PageStatusNone, PageStatusShown, PageStatusHidden }; - PageStatus m_lastPageStatus; + WeakPtrFactory m_weakPtrFactory; #if PLATFORM(IOS) - unsigned m_scrollEventListenerCount; + unsigned m_scrollEventListenerCount { 0 }; #endif #if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) - unsigned m_touchEventListenerCount; + unsigned m_touchEventListenerCount { 0 }; #endif - mutable RefPtr m_sessionStorage; - mutable RefPtr m_localStorage; - mutable RefPtr m_applicationCache; +#if ENABLE(GAMEPAD) + unsigned m_gamepadEventListenerCount { 0 }; +#endif + + mutable RefPtr m_sessionStorage; + mutable RefPtr m_localStorage; + mutable RefPtr m_applicationCache; + + RefPtr m_customElementRegistry; #if ENABLE(WEB_TIMING) - mutable RefPtr m_performance; + mutable RefPtr m_performance; #endif -#if ENABLE(CSS3_CONDITIONAL_RULES) - mutable RefPtr m_css; +#if ENABLE(USER_MESSAGE_HANDLERS) + mutable RefPtr m_webkitNamespace; #endif - }; +}; - inline String DOMWindow::status() const - { - return m_status; - } +inline String DOMWindow::status() const +{ + return m_status; +} - inline String DOMWindow::defaultStatus() const - { - return m_defaultStatus; - } +inline String DOMWindow::defaultStatus() const +{ + return m_defaultStatus; +} } // namespace WebCore - -#endif // DOMWindow_h diff --git a/Source/WebCore/page/DOMWindow.idl b/Source/WebCore/page/DOMWindow.idl index a067f5431..b62e455f8 100644 --- a/Source/WebCore/page/DOMWindow.idl +++ b/Source/WebCore/page/DOMWindow.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -26,67 +26,61 @@ [ CheckSecurity, - JSCustomDefineOwnProperty, CustomDeleteProperty, - CustomGetOwnPropertySlot, CustomEnumerateProperty, + CustomGetOwnPropertySlot, + CustomGetPrototype, + CustomPreventExtensions, + CustomProxyToJSObject, + CustomPutFunction, + CustomToStringName, + ExportMacro=WEBCORE_EXPORT, + ImplicitThis, + InterfaceName=Window, + IsImmutablePrototypeExoticObject, + IsImmutablePrototypeExoticObjectOnPrototype, + JSCustomDefineOwnProperty, JSCustomMarkFunction, JSCustomToNativeObject, - CustomPutFunction, - EventTarget, - JSGenerateToNativeObject, - ReplaceableConstructor, JSLegacyParent=JSDOMWindowBase, - InterfaceName=Window, -] interface DOMWindow { - // DOM Level 0 + LegacyUnenumerableNamedProperties, + PrimaryGlobal, +] interface DOMWindow : EventTarget { [Replaceable] readonly attribute Screen screen; - [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute History history; + readonly attribute History history; [Replaceable] readonly attribute BarProp locationbar; [Replaceable] readonly attribute BarProp menubar; [Replaceable] readonly attribute BarProp personalbar; [Replaceable] readonly attribute BarProp scrollbars; [Replaceable] readonly attribute BarProp statusbar; [Replaceable] readonly attribute BarProp toolbar; - [Replaceable] readonly attribute Navigator navigator; + readonly attribute Navigator navigator; [Replaceable] readonly attribute Navigator clientInformation; - readonly attribute Crypto crypto; -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP - [DoNotCheckSecurity, CustomSetter] attribute Location location; -#endif + [DoNotCheckSecurity, CustomSetter, Unforgeable] attribute Location location; [Replaceable, CustomGetter] readonly attribute Event event; DOMSelection getSelection(); [CheckSecurityForNode] readonly attribute Element frameElement; - [DoNotCheckSecurity, CallWith=ScriptExecutionContext] void focus(); - [DoNotCheckSecurity] void blur(); - [DoNotCheckSecurity, CallWith=ScriptExecutionContext] void close(); + [DoNotCheckSecurity, CallWith=CallerWindow, ForwardDeclareInHeader] void focus(); + [DoNotCheckSecurity, ForwardDeclareInHeader] void blur(); + [DoNotCheckSecurity, CallWith=CallerDocument, ForwardDeclareInHeader] void close(); void print(); void stop(); - [Custom] DOMWindow open(DOMString url, - DOMString name, - optional DOMString options); + [Custom] DOMWindow open(optional USVString url = "about:blank", optional DOMString target = "_blank", [TreatNullAs=EmptyString] optional DOMString features = ""); - [Custom] any showModalDialog(DOMString url, - optional any dialogArgs, - optional DOMString featureArgs); + [Custom] any showModalDialog(DOMString url, optional any dialogArgs, optional DOMString featureArgs); - void alert([Default=Undefined] optional DOMString message); - boolean confirm([Default=Undefined] optional DOMString message); - [TreatReturnedNullStringAs=Null] DOMString prompt([Default=Undefined] optional DOMString message, - [TreatNullAs=NullString, TreatUndefinedAs=NullString,Default=Undefined] optional DOMString defaultValue); + void alert(); + void alert(DOMString message); + boolean confirm(optional DOMString message = ""); + DOMString? prompt(optional DOMString message = "", optional DOMString defaultValue = ""); - boolean find([Default=Undefined] optional DOMString string, - [Default=Undefined] optional boolean caseSensitive, - [Default=Undefined] optional boolean backwards, - [Default=Undefined] optional boolean wrap, - [Default=Undefined] optional boolean wholeWord, - [Default=Undefined] optional boolean searchInFrames, - [Default=Undefined] optional boolean showDialog); + // FIXME: Using "undefined" as default parameter value is wrong. + boolean find(optional DOMString string = "undefined", optional boolean caseSensitive = false, optional boolean backwards = false, optional boolean wrap = false, optional boolean wholeWord = false, optional boolean searchInFrames = false, optional boolean showDialog = false); [Replaceable] readonly attribute boolean offscreenBuffering; @@ -98,20 +92,26 @@ [Replaceable] readonly attribute long screenY; [Replaceable] readonly attribute long screenLeft; [Replaceable] readonly attribute long screenTop; - [Replaceable] readonly attribute long scrollX; - [Replaceable] readonly attribute long scrollY; - readonly attribute long pageXOffset; - readonly attribute long pageYOffset; - void scrollBy([Default=Undefined] optional long x, [Default=Undefined] optional long y); - void scrollTo([Default=Undefined] optional long x, [Default=Undefined] optional long y); - void scroll([Default=Undefined] optional long x, [Default=Undefined] optional long y); - void moveBy([Default=Undefined] optional float x, [Default=Undefined] optional float y); // FIXME: this should take longs not floats. - void moveTo([Default=Undefined] optional float x, [Default=Undefined] optional float y); // FIXME: this should take longs not floats. - void resizeBy([Default=Undefined] optional float x, [Default=Undefined] optional float y); // FIXME: this should take longs not floats. - void resizeTo([Default=Undefined] optional float width, [Default=Undefined] optional float height); // FIXME: this should take longs not floats. + [Replaceable] readonly attribute double scrollX; + [Replaceable] readonly attribute double scrollY; + [Replaceable, ImplementedAs=scrollX] readonly attribute double pageXOffset; + [Replaceable, ImplementedAs=scrollY] readonly attribute double pageYOffset; + + void scrollBy(unrestricted double x, unrestricted double y); + void scrollTo(unrestricted double x, unrestricted double y); + [ImplementedAs=scrollTo] void scroll(unrestricted double x, unrestricted double y); + + void scrollBy(optional ScrollToOptions option); + void scrollTo(optional ScrollToOptions options); + [ImplementedAs=scrollTo] void scroll(optional ScrollToOptions options); + + void moveBy(optional unrestricted float x = NaN, optional unrestricted float y = NaN); // FIXME: This should take longs, not floats. + void moveTo(optional unrestricted float x = NaN, optional unrestricted float y = NaN); // FIXME: This should take longs, not floats. + void resizeBy(optional unrestricted float x = NaN, optional unrestricted float y = NaN); // FIXME: This should take longs, not floats. + void resizeTo(optional unrestricted float width = NaN, optional unrestricted float height = NaN); // FIXME: This should take longs, not floats. - [DoNotCheckSecurity] readonly attribute boolean closed; + [DoNotCheckSecurity, ForwardDeclareInHeader] readonly attribute boolean closed; [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute unsigned long length; @@ -119,215 +119,85 @@ attribute DOMString status; attribute DOMString defaultStatus; -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - // This attribute is an alias of defaultStatus and is necessary for legacy uses. - [ImplementedAs=defaultStatus] attribute DOMString defaultstatus; -#endif + [ImplementedAs=defaultStatus] attribute DOMString defaultstatus; // For compatibility with legacy content. - // Self referential attributes [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute DOMWindow self; - [DoNotCheckSecurity] readonly attribute DOMWindow window; + [DoNotCheckSecurity, Unforgeable] readonly attribute DOMWindow window; [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute DOMWindow frames; [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute DOMWindow opener; [Replaceable, DoNotCheckSecurityOnGetter] readonly attribute DOMWindow parent; - [DoNotCheckSecurityOnGetter] readonly attribute DOMWindow top; + [DoNotCheckSecurityOnGetter, Unforgeable] readonly attribute DOMWindow top; - // DOM Level 2 AbstractView Interface - readonly attribute Document document; + [Unforgeable] readonly attribute Document document; - // CSSOM View Module MediaQueryList matchMedia(DOMString query); - // styleMedia has been removed from the CSSOM View specification. - readonly attribute StyleMedia styleMedia; + readonly attribute StyleMedia styleMedia; // Keeping for now, but styleMedia has been removed from the CSSOM View specification. - // DOM Level 2 Style Interface - CSSStyleDeclaration getComputedStyle([Default=Undefined] optional Element element, - [TreatNullAs=NullString, TreatUndefinedAs=NullString,Default=Undefined] optional DOMString pseudoElement); + [NewObject] CSSStyleDeclaration getComputedStyle(Element element, optional DOMString? pseudoElement = null); - // WebKit extensions -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - CSSRuleList getMatchedCSSRules([Default=Undefined] optional Element element, - [TreatNullAs=NullString, TreatUndefinedAs=NullString,Default=Undefined] optional DOMString pseudoElement); -#endif + CSSRuleList getMatchedCSSRules(optional Element? element = null, optional DOMString? pseudoElement = null); - [Replaceable] readonly attribute double devicePixelRatio; + [Replaceable] readonly attribute unrestricted double devicePixelRatio; - WebKitPoint webkitConvertPointFromPageToNode([Default=Undefined] optional Node node, - [Default=Undefined] optional WebKitPoint p); - WebKitPoint webkitConvertPointFromNodeToPage([Default=Undefined] optional Node node, - [Default=Undefined] optional WebKitPoint p); + WebKitPoint webkitConvertPointFromPageToNode(optional Node? node = null, optional WebKitPoint? p = null); + WebKitPoint webkitConvertPointFromNodeToPage(optional Node? node = null, optional WebKitPoint? p = null); readonly attribute DOMApplicationCache applicationCache; - [GetterRaisesException] readonly attribute Storage sessionStorage; - [GetterRaisesException] readonly attribute Storage localStorage; + [GetterMayThrowException] readonly attribute Storage sessionStorage; + [GetterMayThrowException] readonly attribute Storage localStorage; -#if defined(ENABLE_ORIENTATION_EVENTS) && ENABLE_ORIENTATION_EVENTS - // This is the interface orientation in degrees. Some examples are: - // 0 is straight up; -90 is when the device is rotated 90 clockwise; - // 90 is when rotated counter clockwise. - readonly attribute long orientation; -#endif + // This is the interface orientation in degrees. Some examples: + // 0 when straight up; -90 when rotated 90 degrees clockwise; 90 counter clockwise. + [Conditional=ORIENTATION_EVENTS] readonly attribute long orientation; - [Replaceable] readonly attribute Console console; + [CallWith=ScriptState&CallerWindow, DoNotCheckSecurity, ForwardDeclareInHeader, MayThrowException] void postMessage(any message, USVString targetOrigin, optional sequence transfer = []); - // cross-document messaging -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [DoNotCheckSecurity, Custom, RaisesException] void postMessage(SerializedScriptValue message, DOMString targetOrigin, optional Array messagePorts); -#else - // There's no good way to expose an array via the ObjC bindings, so for now just allow passing in a single port. - [DoNotCheckSecurity, Custom, RaisesException] void postMessage(SerializedScriptValue message, optional MessagePort messagePort, DOMString targetOrigin); -#endif - -#if defined(ENABLE_WEB_TIMING) && ENABLE_WEB_TIMING - [Replaceable] readonly attribute Performance performance; -#endif - -#if defined(ENABLE_REQUEST_ANIMATION_FRAME) && ENABLE_REQUEST_ANIMATION_FRAME long requestAnimationFrame(RequestAnimationFrameCallback callback); void cancelAnimationFrame(long id); long webkitRequestAnimationFrame(RequestAnimationFrameCallback callback); [ImplementedAs=cancelAnimationFrame] void webkitCancelAnimationFrame(long id); [ImplementedAs=cancelAnimationFrame] void webkitCancelRequestAnimationFrame(long id); // This is a deprecated alias for webkitCancelAnimationFrame(). Remove this when removing vendor prefix. -#endif - - [Replaceable,Conditional=CSS3_CONDITIONAL_RULES] readonly attribute DOMWindowCSS CSS; - - // Events - attribute EventListener onabort; - attribute EventListener onbeforeunload; - attribute EventListener onblur; - attribute EventListener oncanplay; - attribute EventListener oncanplaythrough; - attribute EventListener onchange; - attribute EventListener onclick; - attribute EventListener oncontextmenu; - attribute EventListener ondblclick; - attribute EventListener ondrag; - attribute EventListener ondragend; - attribute EventListener ondragenter; - attribute EventListener ondragleave; - attribute EventListener ondragover; - attribute EventListener ondragstart; - attribute EventListener ondrop; - attribute EventListener ondurationchange; - attribute EventListener onemptied; - attribute EventListener onended; - attribute EventListener onerror; - attribute EventListener onfocus; - attribute EventListener onhashchange; - attribute EventListener oninput; - attribute EventListener oninvalid; - attribute EventListener onkeydown; - attribute EventListener onkeypress; - attribute EventListener onkeyup; - attribute EventListener onload; - attribute EventListener onloadeddata; - attribute EventListener onloadedmetadata; - attribute EventListener onloadstart; - attribute EventListener onmessage; - attribute EventListener onmousedown; - attribute EventListener onmouseenter; - attribute EventListener onmouseleave; - attribute EventListener onmousemove; - attribute EventListener onmouseout; - attribute EventListener onmouseover; - attribute EventListener onmouseup; - attribute EventListener onmousewheel; - attribute EventListener onoffline; - attribute EventListener ononline; - attribute EventListener onpagehide; - attribute EventListener onpageshow; - attribute EventListener onpause; - attribute EventListener onplay; - attribute EventListener onplaying; - attribute EventListener onpopstate; - attribute EventListener onprogress; - attribute EventListener onratechange; - attribute EventListener onresize; - attribute EventListener onscroll; - attribute EventListener onseeked; - attribute EventListener onseeking; - attribute EventListener onselect; - attribute EventListener onstalled; - attribute EventListener onstorage; - attribute EventListener onsubmit; - attribute EventListener onsuspend; - attribute EventListener ontimeupdate; - attribute EventListener onunload; - attribute EventListener onvolumechange; - attribute EventListener onwaiting; - attribute EventListener onwheel; - - // Not implemented yet. - // attribute EventListener onafterprint; - // attribute EventListener onbeforeprint; - // attribute EventListener onreadystatechange; - // attribute EventListener onredo; - // attribute EventListener onshow; - // attribute EventListener onundo; - - // Webkit extensions - attribute EventListener onreset; - attribute EventListener onsearch; - attribute EventListener onwebkitanimationend; - attribute EventListener onwebkitanimationiteration; - attribute EventListener onwebkitanimationstart; - attribute EventListener onwebkittransitionend; - attribute EventListener ontransitionend; -#if defined(ENABLE_ORIENTATION_EVENTS) && ENABLE_ORIENTATION_EVENTS - attribute EventListener onorientationchange; -#endif - [Conditional=TOUCH_EVENTS] attribute EventListener ontouchstart; - [Conditional=TOUCH_EVENTS] attribute EventListener ontouchmove; - [Conditional=TOUCH_EVENTS] attribute EventListener ontouchend; - [Conditional=TOUCH_EVENTS] attribute EventListener ontouchcancel; - - [Conditional=IOS_GESTURE_EVENTS] attribute EventListener ongesturestart; - [Conditional=IOS_GESTURE_EVENTS] attribute EventListener ongesturechange; - [Conditional=IOS_GESTURE_EVENTS] attribute EventListener ongestureend; - - [Conditional=DEVICE_ORIENTATION] attribute EventListener ondevicemotion; - [Conditional=DEVICE_ORIENTATION] attribute EventListener ondeviceorientation; - - [Conditional=PROXIMITY_EVENTS] attribute EventListener onwebkitdeviceproximity; - - // EventTarget interface - [Custom] void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [Custom] void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); - - void captureEvents(/*in long eventFlags*/); - void releaseEvents(/*in long eventFlags*/); - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - // Additional constructors. - [CustomGetter, CustomConstructor] attribute HTMLImageElementNamedConstructor Image; // Usable with new operator - // Mozilla has a separate XMLDocument object for XML documents. - // We just use Document for this. - attribute DocumentConstructor XMLDocument; - - [Conditional=IOS_TOUCH_EVENTS, CustomGetter] attribute TouchConstructor Touch; // Usable with the new operator - [Conditional=IOS_TOUCH_EVENTS, CustomGetter] attribute TouchListConstructor TouchList; // Usable with the new operator - - [Conditional=BLOB] attribute DOMURLConstructor webkitURL; // FIXME: deprecate this. - attribute MutationObserverConstructor WebKitMutationObserver; // FIXME: Add metrics to determine when we can remove this. - [Conditional=INDEXED_DATABASE] attribute IDBCursorConstructor webkitIDBCursor; - [Conditional=INDEXED_DATABASE] attribute IDBDatabaseConstructor webkitIDBDatabase; - [Conditional=INDEXED_DATABASE] attribute IDBFactoryConstructor webkitIDBFactory; - [Conditional=INDEXED_DATABASE] attribute IDBIndexConstructor webkitIDBIndex; - [Conditional=INDEXED_DATABASE] attribute IDBKeyRangeConstructor webkitIDBKeyRange; - [Conditional=INDEXED_DATABASE] attribute IDBObjectStoreConstructor webkitIDBObjectStore; - [Conditional=INDEXED_DATABASE] attribute IDBRequestConstructor webkitIDBRequest; - [Conditional=INDEXED_DATABASE] attribute IDBTransactionConstructor webkitIDBTransaction; -#endif // defined(LANGUAGE_JAVASCRIPT) + + void captureEvents(); // Not implemented. Also not in modern standards. Empty function may help compatibility with legacy content. + void releaseEvents(); // Not implemented. Also not in modern standards. Empty function may help compatibility with legacy content. + + attribute XMLDocumentConstructor SVGDocument; + + attribute DOMURLConstructor webkitURL; // FIXME: Deprecate this. + attribute MutationObserverConstructor WebKitMutationObserver; // FIXME: Remove once we prove it is not needed for compatibility with legacy content. + + [EnabledAtRuntime=CustomElements, ImplementedAs=ensureCustomElementRegistry] readonly attribute CustomElementRegistry customElements; + + [MayThrowException, EnabledForWorld=shadowRootIsAlwaysOpen] + NodeList collectMatchingElementsInFlatTree(Node scope, DOMString selectors); + [MayThrowException, EnabledForWorld=shadowRootIsAlwaysOpen] + Element? matchingElementInFlatTree(Node scope, DOMString selectors); + + // Event handlers unique to Element and DOMWindow. + // FIXME: Should these be exposed on Document as well (and therefore moved to GlobalEventHandlers.idl)? + [NotEnumerable] attribute EventHandler onanimationend; + [NotEnumerable] attribute EventHandler onanimationiteration; + [NotEnumerable] attribute EventHandler onanimationstart; + [NotEnumerable] attribute EventHandler ontransitionend; + [NotEnumerable, ImplementedAs=onwebkitAnimationEnd] attribute EventHandler onwebkitanimationend; + [NotEnumerable, ImplementedAs=onwebkitAnimationIteration] attribute EventHandler onwebkitanimationiteration; + [NotEnumerable, ImplementedAs=onwebkitAnimationStart] attribute EventHandler onwebkitanimationstart; + [NotEnumerable, ImplementedAs=onwebkitTransitionEnd] attribute EventHandler onwebkittransitionend; + [NotEnumerable, Conditional=IOS_GESTURE_EVENTS] attribute EventHandler ongesturechange; + [NotEnumerable, Conditional=IOS_GESTURE_EVENTS] attribute EventHandler ongestureend; + [NotEnumerable, Conditional=IOS_GESTURE_EVENTS] attribute EventHandler ongesturestart; + + // Event handlers unique to DOMWindow. + [NotEnumerable, Conditional=DEVICE_ORIENTATION] attribute EventHandler ondevicemotion; + [NotEnumerable, Conditional=DEVICE_ORIENTATION] attribute EventHandler ondeviceorientation; + [NotEnumerable, Conditional=PROXIMITY_EVENTS] attribute EventHandler onwebkitdeviceproximity; }; -DOMWindow implements WindowTimers; -DOMWindow implements WindowBase64; +DOMWindow implements GlobalCrypto; +DOMWindow implements GlobalEventHandlers; +DOMWindow implements GlobalPerformance; +DOMWindow implements WindowEventHandlers; +DOMWindow implements WindowOrWorkerGlobalScope; diff --git a/Source/WebCore/page/DOMWindowExtension.cpp b/Source/WebCore/page/DOMWindowExtension.cpp index cdfe9aa32..9c25e5d11 100644 --- a/Source/WebCore/page/DOMWindowExtension.cpp +++ b/Source/WebCore/page/DOMWindowExtension.cpp @@ -43,26 +43,26 @@ DOMWindowExtension::DOMWindowExtension(Frame* frame, DOMWrapperWorld& world) ASSERT(this->frame()); } -void DOMWindowExtension::disconnectFrameForPageCache() +void DOMWindowExtension::disconnectFrameForDocumentSuspension() { // Calling out to the client might result in this DOMWindowExtension being destroyed // while there is still work to do. - Ref protect(*this); + Ref protectedThis(*this); Frame* frame = this->frame(); frame->loader().client().dispatchWillDisconnectDOMWindowExtensionFromGlobalObject(this); m_disconnectedFrame = frame; - DOMWindowProperty::disconnectFrameForPageCache(); + DOMWindowProperty::disconnectFrameForDocumentSuspension(); } -void DOMWindowExtension::reconnectFrameFromPageCache(Frame* frame) +void DOMWindowExtension::reconnectFrameFromDocumentSuspension(Frame* frame) { ASSERT(m_disconnectedFrame == frame); - DOMWindowProperty::reconnectFrameFromPageCache(frame); - m_disconnectedFrame = 0; + DOMWindowProperty::reconnectFrameFromDocumentSuspension(frame); + m_disconnectedFrame = nullptr; this->frame()->loader().client().dispatchDidReconnectDOMWindowExtensionToGlobalObject(this); } @@ -73,10 +73,10 @@ void DOMWindowExtension::willDestroyGlobalObjectInCachedFrame() // Calling out to the client might result in this DOMWindowExtension being destroyed // while there is still work to do. - Ref protect(*this); + Ref protectedThis(*this); m_disconnectedFrame->loader().client().dispatchWillDestroyGlobalObjectForDOMWindowExtension(this); - m_disconnectedFrame = 0; + m_disconnectedFrame = nullptr; DOMWindowProperty::willDestroyGlobalObjectInCachedFrame(); } @@ -87,7 +87,7 @@ void DOMWindowExtension::willDestroyGlobalObjectInFrame() // Calling out to the client might result in this DOMWindowExtension being destroyed // while there is still work to do. - Ref protect(*this); + Ref protectedThis(*this); if (!m_wasDetached) { Frame* frame = this->frame(); @@ -105,7 +105,7 @@ void DOMWindowExtension::willDetachGlobalObjectFromFrame() // Calling out to the client might result in this DOMWindowExtension being destroyed // while there is still work to do. - Ref protect(*this); + Ref protectedThis(*this); Frame* frame = this->frame(); ASSERT(frame); diff --git a/Source/WebCore/page/DOMWindowExtension.h b/Source/WebCore/page/DOMWindowExtension.h index c184a0e6a..842661f14 100644 --- a/Source/WebCore/page/DOMWindowExtension.h +++ b/Source/WebCore/page/DOMWindowExtension.h @@ -23,11 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMWindowExtension_h -#define DOMWindowExtension_h +#pragma once #include "DOMWindowProperty.h" -#include #include #include @@ -39,27 +37,25 @@ class Frame; class DOMWindowExtension : public RefCounted, public DOMWindowProperty { public: - static PassRefPtr create(Frame* frame, DOMWrapperWorld& world) + static Ref create(Frame* frame, DOMWrapperWorld& world) { - return adoptRef(new DOMWindowExtension(frame, world)); + return adoptRef(*new DOMWindowExtension(frame, world)); } - virtual void disconnectFrameForPageCache() override; - virtual void reconnectFrameFromPageCache(Frame*) override; - virtual void willDestroyGlobalObjectInCachedFrame() override; - virtual void willDestroyGlobalObjectInFrame() override; - virtual void willDetachGlobalObjectFromFrame() override; + void disconnectFrameForDocumentSuspension() override; + void reconnectFrameFromDocumentSuspension(Frame*) override; + void willDestroyGlobalObjectInCachedFrame() override; + void willDestroyGlobalObjectInFrame() override; + void willDetachGlobalObjectFromFrame() override; DOMWrapperWorld& world() const { return *m_world; } private: - DOMWindowExtension(Frame*, DOMWrapperWorld&); + WEBCORE_EXPORT DOMWindowExtension(Frame*, DOMWrapperWorld&); RefPtr m_world; RefPtr m_disconnectedFrame; bool m_wasDetached; }; -} - -#endif // DOMWindowExtension_h +} // namespace WebCore diff --git a/Source/WebCore/page/DOMWindowProperty.cpp b/Source/WebCore/page/DOMWindowProperty.cpp index 6f109f1ed..55096b64a 100644 --- a/Source/WebCore/page/DOMWindowProperty.cpp +++ b/Source/WebCore/page/DOMWindowProperty.cpp @@ -35,37 +35,37 @@ namespace WebCore { DOMWindowProperty::DOMWindowProperty(Frame* frame) : m_frame(frame) - , m_associatedDOMWindow(0) + , m_associatedDOMWindow(nullptr) { // FIXME: For now it *is* acceptable for a DOMWindowProperty to be created with a null frame. // See fast/dom/navigator-detached-no-crash.html for the recipe. // We should fix that. if (m_frame) { m_associatedDOMWindow = m_frame->document()->domWindow(); - m_associatedDOMWindow->registerProperty(this); + m_associatedDOMWindow->registerProperty(*this); } } DOMWindowProperty::~DOMWindowProperty() { if (m_associatedDOMWindow) - m_associatedDOMWindow->unregisterProperty(this); + m_associatedDOMWindow->unregisterProperty(*this); - m_associatedDOMWindow = 0; - m_frame = 0; + m_associatedDOMWindow = nullptr; + m_frame = nullptr; } -void DOMWindowProperty::disconnectFrameForPageCache() +void DOMWindowProperty::disconnectFrameForDocumentSuspension() { // If this property is being disconnected from its Frame to enter the PageCache, it must have // been created with a Frame in the first place. ASSERT(m_frame); ASSERT(m_associatedDOMWindow); - m_frame = 0; + m_frame = nullptr; } -void DOMWindowProperty::reconnectFrameFromPageCache(Frame* frame) +void DOMWindowProperty::reconnectFrameFromDocumentSuspension(Frame* frame) { // If this property is being reconnected to its Frame to enter the PageCache, it must have // been disconnected from its Frame in the first place and it should still have an associated DOMWindow. @@ -86,9 +86,9 @@ void DOMWindowProperty::willDestroyGlobalObjectInCachedFrame() // DOMWindowProperty lifetime isn't tied directly to the DOMWindow itself so it is important that it unregister // itself from any DOMWindow it is associated with if that DOMWindow is going away. if (m_associatedDOMWindow) - m_associatedDOMWindow->unregisterProperty(this); - m_associatedDOMWindow = 0; - m_frame = 0; + m_associatedDOMWindow->unregisterProperty(*this); + m_associatedDOMWindow = nullptr; + m_frame = nullptr; } void DOMWindowProperty::willDestroyGlobalObjectInFrame() @@ -100,9 +100,9 @@ void DOMWindowProperty::willDestroyGlobalObjectInFrame() // DOMWindowProperty lifetime isn't tied directly to the DOMWindow itself so it is important that it unregister // itself from any DOMWindow it is associated with if that DOMWindow is going away. if (m_associatedDOMWindow) - m_associatedDOMWindow->unregisterProperty(this); - m_associatedDOMWindow = 0; - m_frame = 0; + m_associatedDOMWindow->unregisterProperty(*this); + m_associatedDOMWindow = nullptr; + m_frame = nullptr; } void DOMWindowProperty::willDetachGlobalObjectFromFrame() diff --git a/Source/WebCore/page/DOMWindowProperty.h b/Source/WebCore/page/DOMWindowProperty.h index e2dcc4437..c4bdd80d6 100644 --- a/Source/WebCore/page/DOMWindowProperty.h +++ b/Source/WebCore/page/DOMWindowProperty.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMWindowProperty_h -#define DOMWindowProperty_h +#pragma once namespace WebCore { @@ -35,8 +34,8 @@ class DOMWindowProperty { public: explicit DOMWindowProperty(Frame*); - virtual void disconnectFrameForPageCache(); - virtual void reconnectFrameFromPageCache(Frame*); + virtual void disconnectFrameForDocumentSuspension(); + virtual void reconnectFrameFromDocumentSuspension(Frame*); virtual void willDestroyGlobalObjectInCachedFrame(); virtual void willDestroyGlobalObjectInFrame(); virtual void willDetachGlobalObjectFromFrame(); @@ -50,6 +49,4 @@ protected: DOMWindow* m_associatedDOMWindow; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/DatabaseProvider.cpp b/Source/WebCore/page/DatabaseProvider.cpp new file mode 100644 index 000000000..e37a8a872 --- /dev/null +++ b/Source/WebCore/page/DatabaseProvider.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "DatabaseProvider.h" + +namespace WebCore { + +DatabaseProvider::~DatabaseProvider() +{ +} + +} diff --git a/Source/WebCore/page/DatabaseProvider.h b/Source/WebCore/page/DatabaseProvider.h new file mode 100644 index 000000000..a76c791c8 --- /dev/null +++ b/Source/WebCore/page/DatabaseProvider.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +#include + +namespace WebCore { + +class SessionID; + +namespace IDBClient { +class IDBConnectionToServer; +} + +class WEBCORE_EXPORT DatabaseProvider : public RefCounted { +public: + virtual ~DatabaseProvider(); + +#if ENABLE(INDEXED_DATABASE) + virtual IDBClient::IDBConnectionToServer& idbConnectionToServerForSession(const SessionID&) = 0; +#endif +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/DebugPageOverlays.cpp b/Source/WebCore/page/DebugPageOverlays.cpp new file mode 100644 index 000000000..e8e75593a --- /dev/null +++ b/Source/WebCore/page/DebugPageOverlays.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2014-2017 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h" +#include "DebugPageOverlays.h" + +#include "ElementIterator.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "MainFrame.h" +#include "Page.h" +#include "PageOverlay.h" +#include "PageOverlayController.h" +#include "Region.h" +#include "ScrollingCoordinator.h" +#include "Settings.h" + +namespace WebCore { + +DebugPageOverlays* DebugPageOverlays::sharedDebugOverlays; + +class RegionOverlay : public RefCounted, public PageOverlay::Client { +public: + static Ref create(MainFrame&, DebugPageOverlays::RegionType); + virtual ~RegionOverlay(); + + void recomputeRegion(); + PageOverlay& overlay() { return *m_overlay; } + +protected: + RegionOverlay(MainFrame&, Color); + +private: + void willMoveToPage(PageOverlay&, Page*) final; + void didMoveToPage(PageOverlay&, Page*) final; + void drawRect(PageOverlay&, GraphicsContext&, const IntRect& dirtyRect) final; + bool mouseEvent(PageOverlay&, const PlatformMouseEvent&) final; + void didScrollFrame(PageOverlay&, Frame&) final; + +protected: + // Returns true if the region changed. + virtual bool updateRegion() = 0; + + MainFrame& m_frame; + RefPtr m_overlay; + std::unique_ptr m_region; + Color m_color; +}; + +class MouseWheelRegionOverlay final : public RegionOverlay { +public: + static Ref create(MainFrame& frame) + { + return adoptRef(*new MouseWheelRegionOverlay(frame)); + } + +private: + explicit MouseWheelRegionOverlay(MainFrame& frame) + : RegionOverlay(frame, Color(0.5f, 0.0f, 0.0f, 0.4f)) + { + } + + bool updateRegion() override; +}; + +bool MouseWheelRegionOverlay::updateRegion() +{ + auto region = std::make_unique(); + + for (const Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext()) { + if (!frame->view() || !frame->document()) + continue; + + auto frameRegion = frame->document()->absoluteRegionForEventTargets(frame->document()->wheelEventTargets()); + frameRegion.first.translate(toIntSize(frame->view()->contentsToRootView(IntPoint()))); + region->unite(frameRegion.first); + } + + region->translate(m_overlay->viewToOverlayOffset()); + + bool regionChanged = !m_region || !(*m_region == *region); + m_region = WTFMove(region); + return regionChanged; +} + +class NonFastScrollableRegionOverlay final : public RegionOverlay { +public: + static Ref create(MainFrame& frame) + { + return adoptRef(*new NonFastScrollableRegionOverlay(frame)); + } + +private: + explicit NonFastScrollableRegionOverlay(MainFrame& frame) + : RegionOverlay(frame, Color(1.0f, 0.5f, 0.0f, 0.4f)) + { + } + + bool updateRegion() override; +}; + +bool NonFastScrollableRegionOverlay::updateRegion() +{ + auto region = std::make_unique(); + + if (Page* page = m_frame.page()) { + if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) { + EventTrackingRegions eventTrackingRegions = scrollingCoordinator->absoluteEventTrackingRegions(); + for (const auto& synchronousEventRegion : eventTrackingRegions.eventSpecificSynchronousDispatchRegions) + region->unite(synchronousEventRegion.value); + } + } + + bool regionChanged = !m_region || !(*m_region == *region); + m_region = WTFMove(region); + return regionChanged; +} + +Ref RegionOverlay::create(MainFrame& frame, DebugPageOverlays::RegionType regionType) +{ + switch (regionType) { + case DebugPageOverlays::RegionType::WheelEventHandlers: + return MouseWheelRegionOverlay::create(frame); + case DebugPageOverlays::RegionType::NonFastScrollableRegion: + return NonFastScrollableRegionOverlay::create(frame); + } + ASSERT_NOT_REACHED(); + return MouseWheelRegionOverlay::create(frame); +} + +RegionOverlay::RegionOverlay(MainFrame& frame, Color regionColor) + : m_frame(frame) + , m_overlay(PageOverlay::create(*this, PageOverlay::OverlayType::Document)) + , m_color(regionColor) +{ +} + +RegionOverlay::~RegionOverlay() +{ + if (m_overlay) + m_frame.pageOverlayController().uninstallPageOverlay(*m_overlay, PageOverlay::FadeMode::DoNotFade); +} + +void RegionOverlay::willMoveToPage(PageOverlay&, Page* page) +{ + if (!page) + m_overlay = nullptr; +} + +void RegionOverlay::didMoveToPage(PageOverlay&, Page* page) +{ + if (page) + recomputeRegion(); +} + +void RegionOverlay::drawRect(PageOverlay&, GraphicsContext& context, const IntRect& dirtyRect) +{ + context.clearRect(dirtyRect); + + if (!m_region) + return; + + GraphicsContextStateSaver saver(context); + context.setFillColor(m_color); + for (auto rect : m_region->rects()) { + if (rect.intersects(dirtyRect)) + context.fillRect(rect); + } +} + +bool RegionOverlay::mouseEvent(PageOverlay&, const PlatformMouseEvent&) +{ + return false; +} + +void RegionOverlay::didScrollFrame(PageOverlay&, Frame&) +{ +} + +void RegionOverlay::recomputeRegion() +{ + if (updateRegion()) + m_overlay->setNeedsDisplay(); +} + +DebugPageOverlays& DebugPageOverlays::singleton() +{ + if (!sharedDebugOverlays) + sharedDebugOverlays = new DebugPageOverlays; + + return *sharedDebugOverlays; +} + +static inline size_t indexOf(DebugPageOverlays::RegionType regionType) +{ + return static_cast(regionType); +} + +RegionOverlay& DebugPageOverlays::ensureRegionOverlayForFrame(MainFrame& frame, RegionType regionType) +{ + auto it = m_frameRegionOverlays.find(&frame); + if (it != m_frameRegionOverlays.end()) { + auto& visualizer = it->value[indexOf(regionType)]; + if (!visualizer) + visualizer = RegionOverlay::create(frame, regionType); + return *visualizer; + } + + Vector> visualizers(NumberOfRegionTypes); + auto visualizer = RegionOverlay::create(frame, regionType); + visualizers[indexOf(regionType)] = visualizer.copyRef(); + m_frameRegionOverlays.add(&frame, WTFMove(visualizers)); + return visualizer; +} + +void DebugPageOverlays::showRegionOverlay(MainFrame& frame, RegionType regionType) +{ + auto& visualizer = ensureRegionOverlayForFrame(frame, regionType); + frame.pageOverlayController().installPageOverlay(visualizer.overlay(), PageOverlay::FadeMode::DoNotFade); +} + +void DebugPageOverlays::hideRegionOverlay(MainFrame& frame, RegionType regionType) +{ + auto it = m_frameRegionOverlays.find(&frame); + if (it == m_frameRegionOverlays.end()) + return; + auto& visualizer = it->value[indexOf(regionType)]; + if (!visualizer) + return; + frame.pageOverlayController().uninstallPageOverlay(visualizer->overlay(), PageOverlay::FadeMode::DoNotFade); + visualizer = nullptr; +} + +void DebugPageOverlays::regionChanged(Frame& frame, RegionType regionType) +{ + if (auto* visualizer = regionOverlayForFrame(frame.mainFrame(), regionType)) + visualizer->recomputeRegion(); +} + +RegionOverlay* DebugPageOverlays::regionOverlayForFrame(MainFrame& frame, RegionType regionType) const +{ + auto it = m_frameRegionOverlays.find(&frame); + if (it == m_frameRegionOverlays.end()) + return nullptr; + return it->value.at(indexOf(regionType)).get(); +} + +void DebugPageOverlays::updateOverlayRegionVisibility(MainFrame& frame, DebugOverlayRegions visibleRegions) +{ + if (visibleRegions & NonFastScrollableRegion) + showRegionOverlay(frame, RegionType::NonFastScrollableRegion); + else + hideRegionOverlay(frame, RegionType::NonFastScrollableRegion); + + if (visibleRegions & WheelEventHandlerRegion) + showRegionOverlay(frame, RegionType::WheelEventHandlers); + else + hideRegionOverlay(frame, RegionType::WheelEventHandlers); +} + +void DebugPageOverlays::settingsChanged(MainFrame& frame) +{ + DebugOverlayRegions activeOverlayRegions = frame.settings().visibleDebugOverlayRegions(); + if (!activeOverlayRegions && !hasOverlays(frame)) + return; + + DebugPageOverlays::singleton().updateOverlayRegionVisibility(frame, activeOverlayRegions); +} + +} diff --git a/Source/WebCore/page/DebugPageOverlays.h b/Source/WebCore/page/DebugPageOverlays.h new file mode 100644 index 000000000..e1744c0e6 --- /dev/null +++ b/Source/WebCore/page/DebugPageOverlays.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +#include "Frame.h" +#include "Settings.h" +#include +#include + +namespace WebCore { + +class MainFrame; +class RegionOverlay; + +class DebugPageOverlays { +public: + static DebugPageOverlays& singleton(); + + enum class RegionType { + WheelEventHandlers, + NonFastScrollableRegion, + }; + static const unsigned NumberOfRegionTypes = NonFastScrollableRegion + 1; + + static void didLayout(Frame&); + static void didChangeEventHandlers(Frame&); + + WEBCORE_EXPORT static void settingsChanged(MainFrame&); + +private: + static bool hasOverlays(MainFrame&); + + void showRegionOverlay(MainFrame&, RegionType); + void hideRegionOverlay(MainFrame&, RegionType); + + void regionChanged(Frame&, RegionType); + + bool hasOverlaysForFrame(MainFrame& frame) const + { + return m_frameRegionOverlays.contains(&frame); + } + + void updateOverlayRegionVisibility(MainFrame&, DebugOverlayRegions); + + RegionOverlay* regionOverlayForFrame(MainFrame&, RegionType) const; + RegionOverlay& ensureRegionOverlayForFrame(MainFrame&, RegionType); + + HashMap>> m_frameRegionOverlays; + + static DebugPageOverlays* sharedDebugOverlays; +}; + +#define FAST_RETURN_IF_NO_OVERLAYS(frame) if (LIKELY(!hasOverlays(frame))) return; + +inline bool DebugPageOverlays::hasOverlays(MainFrame& frame) +{ + if (!sharedDebugOverlays) + return false; + + return sharedDebugOverlays->hasOverlaysForFrame(frame); +} + +inline void DebugPageOverlays::didLayout(Frame& frame) +{ + FAST_RETURN_IF_NO_OVERLAYS(frame.mainFrame()); + + sharedDebugOverlays->regionChanged(frame, RegionType::WheelEventHandlers); + sharedDebugOverlays->regionChanged(frame, RegionType::NonFastScrollableRegion); +} + +inline void DebugPageOverlays::didChangeEventHandlers(Frame& frame) +{ + FAST_RETURN_IF_NO_OVERLAYS(frame.mainFrame()); + + sharedDebugOverlays->regionChanged(frame, RegionType::WheelEventHandlers); + sharedDebugOverlays->regionChanged(frame, RegionType::NonFastScrollableRegion); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/DeviceClient.h b/Source/WebCore/page/DeviceClient.h index d5160b717..63bf0ece5 100644 --- a/Source/WebCore/page/DeviceClient.h +++ b/Source/WebCore/page/DeviceClient.h @@ -24,12 +24,12 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceClient_h -#define DeviceClient_h +#pragma once namespace WebCore { class DeviceClient { + WTF_MAKE_FAST_ALLOCATED; public: virtual ~DeviceClient() { } @@ -38,5 +38,3 @@ public: }; } // namespace WebCore - -#endif // DeviceClient_h diff --git a/Source/WebCore/page/DeviceController.cpp b/Source/WebCore/page/DeviceController.cpp index a2baa23c0..bd5494aa3 100644 --- a/Source/WebCore/page/DeviceController.cpp +++ b/Source/WebCore/page/DeviceController.cpp @@ -29,13 +29,12 @@ #include "DeviceClient.h" #include "Document.h" -#include "Page.h" namespace WebCore { DeviceController::DeviceController(DeviceClient* client) : m_client(client) - , m_timer(this, &DeviceController::fireDeviceEvent) + , m_timer(*this, &DeviceController::fireDeviceEvent) { ASSERT(m_client); } @@ -71,33 +70,31 @@ void DeviceController::removeAllDeviceEventListeners(DOMWindow* window) m_client->stopUpdating(); } -void DeviceController::dispatchDeviceEvent(PassRefPtr prpEvent) +void DeviceController::dispatchDeviceEvent(Event& event) { - RefPtr event = prpEvent; Vector> listenerVector; copyToVector(m_listeners, listenerVector); - for (size_t i = 0; i < listenerVector.size(); ++i) { - if (listenerVector[i]->document() - && !listenerVector[i]->document()->activeDOMObjectsAreSuspended() - && !listenerVector[i]->document()->activeDOMObjectsAreStopped()) - listenerVector[i]->dispatchEvent(event); + for (auto& listener : listenerVector) { + auto document = listener->document(); + if (document && !document->activeDOMObjectsAreSuspended() && !document->activeDOMObjectsAreStopped()) + listener->dispatchEvent(event); } } -void DeviceController::fireDeviceEvent(Timer& timer) +void DeviceController::fireDeviceEvent() { - ASSERT_UNUSED(timer, &timer == &m_timer); ASSERT(hasLastData()); m_timer.stop(); Vector> listenerVector; copyToVector(m_lastEventListeners, listenerVector); m_lastEventListeners.clear(); - for (size_t i = 0; i < listenerVector.size(); ++i) { - if (listenerVector[i]->document() - && !listenerVector[i]->document()->activeDOMObjectsAreSuspended() - && !listenerVector[i]->document()->activeDOMObjectsAreStopped()) - listenerVector[i]->dispatchEvent(getLastEvent()); + for (auto& listener : listenerVector) { + auto document = listener->document(); + if (document && !document->activeDOMObjectsAreSuspended() && !document->activeDOMObjectsAreStopped()) { + if (auto lastEvent = getLastEvent()) + listener->dispatchEvent(*lastEvent); + } } } diff --git a/Source/WebCore/page/DeviceController.h b/Source/WebCore/page/DeviceController.h index 6a5c242de..c10db344c 100644 --- a/Source/WebCore/page/DeviceController.h +++ b/Source/WebCore/page/DeviceController.h @@ -24,8 +24,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DeviceController_h -#define DeviceController_h +#pragma once #include "DOMWindow.h" #include "Event.h" @@ -39,30 +38,29 @@ class DeviceClient; class Page; class DeviceController : public Supplement { + WTF_MAKE_FAST_ALLOCATED; public: explicit DeviceController(DeviceClient*); - ~DeviceController() { } + virtual ~DeviceController() { } void addDeviceEventListener(DOMWindow*); void removeDeviceEventListener(DOMWindow*); void removeAllDeviceEventListeners(DOMWindow*); - void dispatchDeviceEvent(PassRefPtr); + void dispatchDeviceEvent(Event&); bool isActive() { return !m_listeners.isEmpty(); } DeviceClient* client() { return m_client; } virtual bool hasLastData() { return false; } - virtual PassRefPtr getLastEvent() { return 0; } + virtual RefPtr getLastEvent() { return nullptr; } protected: - void fireDeviceEvent(Timer&); + void fireDeviceEvent(); HashCountedSet> m_listeners; HashCountedSet> m_lastEventListeners; DeviceClient* m_client; - Timer m_timer; + Timer m_timer; }; } // namespace WebCore - -#endif // DeviceController_h diff --git a/Source/WebCore/page/DiagnosticLoggingClient.h b/Source/WebCore/page/DiagnosticLoggingClient.h new file mode 100644 index 000000000..00596c78b --- /dev/null +++ b/Source/WebCore/page/DiagnosticLoggingClient.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +#include "DiagnosticLoggingResultType.h" +#include +#include +#include + +namespace WebCore { + +enum class ShouldSample { No, Yes }; + +class DiagnosticLoggingClient { + WTF_MAKE_FAST_ALLOCATED; +public: + virtual void logDiagnosticMessage(const String& message, const String& description, ShouldSample) = 0; + virtual void logDiagnosticMessageWithResult(const String& message, const String& description, DiagnosticLoggingResultType, ShouldSample) = 0; + virtual void logDiagnosticMessageWithValue(const String& message, const String& description, double value, unsigned significantFigures, ShouldSample) = 0; + virtual void logDiagnosticMessageWithEnhancedPrivacy(const String& message, const String& description, ShouldSample) = 0; + + static bool shouldLogAfterSampling(ShouldSample); + + virtual ~DiagnosticLoggingClient() { } +}; + +inline bool DiagnosticLoggingClient::shouldLogAfterSampling(ShouldSample shouldSample) +{ + if (shouldSample == ShouldSample::No) + return true; + + static const double selectionProbability = 0.05; + return randomNumber() <= selectionProbability; +} + +} // namespace WebCore diff --git a/Source/WebCore/page/DiagnosticLoggingKeys.cpp b/Source/WebCore/page/DiagnosticLoggingKeys.cpp index a4354a647..bd555f32a 100644 --- a/Source/WebCore/page/DiagnosticLoggingKeys.cpp +++ b/Source/WebCore/page/DiagnosticLoggingKeys.cpp @@ -38,6 +38,21 @@ String DiagnosticLoggingKeys::mediaLoadingFailedKey() return ASCIILiteral("mediaFailedLoading"); } +String DiagnosticLoggingKeys::memoryCacheEntryDecisionKey() +{ + return ASCIILiteral("memoryCacheEntryDecision"); +} + +String DiagnosticLoggingKeys::memoryCacheUsageKey() +{ + return ASCIILiteral("memoryCacheUsage"); +} + +String DiagnosticLoggingKeys::missingValidatorFieldsKey() +{ + return ASCIILiteral("missingValidatorFields"); +} + String DiagnosticLoggingKeys::pluginLoadedKey() { return ASCIILiteral("pluginLoaded"); @@ -48,6 +63,36 @@ String DiagnosticLoggingKeys::pluginLoadingFailedKey() return ASCIILiteral("pluginFailedLoading"); } +String DiagnosticLoggingKeys::postPageBackgroundingCPUUsageKey() +{ + return ASCIILiteral("postPageBackgroundingCPUUsage"); +} + +String DiagnosticLoggingKeys::postPageBackgroundingMemoryUsageKey() +{ + return ASCIILiteral("postPageBackgroundingMemoryUsage"); +} + +String DiagnosticLoggingKeys::pageHandlesWebGLContextLossKey() +{ + return ASCIILiteral("pageHandlesWebGLContextLoss"); +} + +String DiagnosticLoggingKeys::postPageLoadCPUUsageKey() +{ + return ASCIILiteral("postPageLoadCPUUsage"); +} + +String DiagnosticLoggingKeys::postPageLoadMemoryUsageKey() +{ + return ASCIILiteral("postPageLoadMemoryUsage"); +} + +String DiagnosticLoggingKeys::provisionalLoadKey() +{ + return ASCIILiteral("provisionalLoad"); +} + String DiagnosticLoggingKeys::pageContainsPluginKey() { return ASCIILiteral("pageContainsPlugin"); @@ -68,19 +113,624 @@ String DiagnosticLoggingKeys::pageContainsAtLeastOneMediaEngineKey() return ASCIILiteral("pageContainsAtLeastOneMediaEngine"); } -String DiagnosticLoggingKeys::passKey() +String DiagnosticLoggingKeys::pageLoadedKey() +{ + return ASCIILiteral("pageLoaded"); +} + +String DiagnosticLoggingKeys::playedKey() +{ + return ASCIILiteral("played"); +} + +String DiagnosticLoggingKeys::engineFailedToLoadKey() +{ + return ASCIILiteral("engineFailedToLoad"); +} + +String DiagnosticLoggingKeys::entryRightlyNotWarmedUpKey() +{ + return ASCIILiteral("entryRightlyNotWarmedUp"); +} + +String DiagnosticLoggingKeys::entryWronglyNotWarmedUpKey() +{ + return ASCIILiteral("entryWronglyNotWarmedUp"); +} + +String DiagnosticLoggingKeys::navigationKey() +{ + return ASCIILiteral("navigation"); +} + +String DiagnosticLoggingKeys::needsRevalidationKey() +{ + return ASCIILiteral("needsRevalidation"); +} + +String DiagnosticLoggingKeys::networkCacheKey() +{ + return ASCIILiteral("networkCache"); +} + +String DiagnosticLoggingKeys::networkCacheFailureReasonKey() +{ + return ASCIILiteral("networkCacheFailureReason"); +} + +String DiagnosticLoggingKeys::networkCacheUnusedReasonKey() +{ + return ASCIILiteral("networkCacheUnusedReason"); +} + +String DiagnosticLoggingKeys::networkCacheReuseFailureKey() +{ + return ASCIILiteral("networkCacheReuseFailure"); +} + +String DiagnosticLoggingKeys::networkKey() +{ + return ASCIILiteral("network"); +} + +String DiagnosticLoggingKeys::networkProcessCrashedKey() +{ + return ASCIILiteral("networkProcessCrashed"); +} + +String DiagnosticLoggingKeys::neverSeenBeforeKey() +{ + return ASCIILiteral("neverSeenBefore"); +} + +String DiagnosticLoggingKeys::noKey() +{ + return ASCIILiteral("no"); +} + +String DiagnosticLoggingKeys::noCacheKey() +{ + return ASCIILiteral("noCache"); +} + +String DiagnosticLoggingKeys::noStoreKey() +{ + return ASCIILiteral("noStore"); +} + +String DiagnosticLoggingKeys::nonVisibleStateKey() +{ + return ASCIILiteral("nonVisibleState"); +} + +String DiagnosticLoggingKeys::notInMemoryCacheKey() +{ + return ASCIILiteral("notInMemoryCache"); +} + +String DiagnosticLoggingKeys::pageCacheKey() +{ + return ASCIILiteral("pageCache"); +} + +String DiagnosticLoggingKeys::pageCacheFailureKey() +{ + return ASCIILiteral("pageCacheFailure"); +} + +String DiagnosticLoggingKeys::noDocumentLoaderKey() +{ + return ASCIILiteral("noDocumentLoader"); +} + +String DiagnosticLoggingKeys::noLongerInCacheKey() +{ + return ASCIILiteral("noLongerInCache"); +} + +String DiagnosticLoggingKeys::otherKey() +{ + return ASCIILiteral("other"); +} + +String DiagnosticLoggingKeys::mainDocumentErrorKey() +{ + return ASCIILiteral("mainDocumentError"); +} + +String DiagnosticLoggingKeys::mainResourceKey() +{ + return ASCIILiteral("mainResource"); +} + +String DiagnosticLoggingKeys::isErrorPageKey() +{ + return ASCIILiteral("isErrorPage"); +} + +String DiagnosticLoggingKeys::isExpiredKey() +{ + return ASCIILiteral("isExpired"); +} + +String DiagnosticLoggingKeys::isReloadIgnoringCacheDataKey() +{ + return ASCIILiteral("isReloadIgnoringCacheData"); +} + +String DiagnosticLoggingKeys::loadingKey() { - return ASCIILiteral("pass"); + return ASCIILiteral("loading"); } -String DiagnosticLoggingKeys::failKey() +String DiagnosticLoggingKeys::hasPluginsKey() { - return ASCIILiteral("fail"); + return ASCIILiteral("hasPlugins"); } -String DiagnosticLoggingKeys::noopKey() +String DiagnosticLoggingKeys::httpsNoStoreKey() { - return ASCIILiteral("noop"); + return ASCIILiteral("httpsNoStore"); } +String DiagnosticLoggingKeys::imageKey() +{ + return ASCIILiteral("image"); +} + +String DiagnosticLoggingKeys::inMemoryCacheKey() +{ + return ASCIILiteral("inMemoryCache"); +} + +String DiagnosticLoggingKeys::inactiveKey() +{ + return ASCIILiteral("inactive"); +} + +String DiagnosticLoggingKeys::internalErrorKey() +{ + return ASCIILiteral("internalError"); } + +String DiagnosticLoggingKeys::invalidSessionIDKey() +{ + return ASCIILiteral("invalidSessionID"); +} + +String DiagnosticLoggingKeys::isAttachmentKey() +{ + return ASCIILiteral("isAttachment"); +} + +String DiagnosticLoggingKeys::isConditionalRequestKey() +{ + return ASCIILiteral("isConditionalRequest"); +} + +String DiagnosticLoggingKeys::isDisabledKey() +{ + return ASCIILiteral("isDisabled"); +} + +String DiagnosticLoggingKeys::noCurrentHistoryItemKey() +{ + return ASCIILiteral("noCurrentHistoryItem"); +} + +String DiagnosticLoggingKeys::quirkRedirectComingKey() +{ + return ASCIILiteral("quirkRedirectComing"); +} + +String DiagnosticLoggingKeys::rawKey() +{ + return ASCIILiteral("raw"); +} + +String DiagnosticLoggingKeys::redirectKey() +{ + return ASCIILiteral("redirect"); +} + +String DiagnosticLoggingKeys::isLoadingKey() +{ + return ASCIILiteral("isLoading"); +} + +String DiagnosticLoggingKeys::documentLoaderStoppingKey() +{ + return ASCIILiteral("documentLoaderStopping"); +} + +String DiagnosticLoggingKeys::domainVisitedKey() +{ + return ASCIILiteral("DomainVisited"); +} + +String DiagnosticLoggingKeys::cannotSuspendActiveDOMObjectsKey() +{ + return ASCIILiteral("cannotSuspendActiveDOMObjects"); +} + +String DiagnosticLoggingKeys::cpuUsageKey() +{ + return ASCIILiteral("cpuUsage"); +} + +String DiagnosticLoggingKeys::createSharedBufferFailedKey() +{ + return ASCIILiteral("createSharedBufferFailed"); +} + +String DiagnosticLoggingKeys::activeInForegroundTabKey() +{ + return ASCIILiteral("activeInForegroundTab"); +} + +String DiagnosticLoggingKeys::activeInBackgroundTabOnlyKey() +{ + return ASCIILiteral("activeInBackgroundTabOnly"); +} + +String DiagnosticLoggingKeys::applicationCacheKey() +{ + return ASCIILiteral("applicationCache"); +} + +String DiagnosticLoggingKeys::audioKey() +{ + return ASCIILiteral("audio"); +} + +String DiagnosticLoggingKeys::backNavigationDeltaKey() +{ + return ASCIILiteral("backNavigationDelta"); +} + +String DiagnosticLoggingKeys::canCacheKey() +{ + return ASCIILiteral("canCache"); +} + +String DiagnosticLoggingKeys::cacheControlNoStoreKey() +{ + return ASCIILiteral("cacheControlNoStore"); +} + +String DiagnosticLoggingKeys::cachedResourceRevalidationKey() +{ + return ASCIILiteral("cachedResourceRevalidation"); +} + +String DiagnosticLoggingKeys::cachedResourceRevalidationReasonKey() +{ + return ASCIILiteral("cachedResourceRevalidationReason"); +} + +String DiagnosticLoggingKeys::deniedByClientKey() +{ + return ASCIILiteral("deniedByClient"); +} + +String DiagnosticLoggingKeys::deviceMotionKey() +{ + return ASCIILiteral("deviceMotion"); +} + +String DiagnosticLoggingKeys::deviceOrientationKey() +{ + return ASCIILiteral("deviceOrientation"); +} + +String DiagnosticLoggingKeys::deviceProximityKey() +{ + return ASCIILiteral("deviceProximity"); +} + +String DiagnosticLoggingKeys::diskCacheKey() +{ + return ASCIILiteral("diskCache"); +} + +String DiagnosticLoggingKeys::diskCacheAfterValidationKey() +{ + return ASCIILiteral("diskCacheAfterValidation"); +} + +String DiagnosticLoggingKeys::reloadKey() +{ + return ASCIILiteral("reload"); +} + +String DiagnosticLoggingKeys::replaceKey() +{ + return ASCIILiteral("replace"); +} + +String DiagnosticLoggingKeys::retrievalRequestKey() +{ + return ASCIILiteral("retrievalRequest"); +} + +String DiagnosticLoggingKeys::resourceLoadedKey() +{ + return ASCIILiteral("resourceLoaded"); +} + +String DiagnosticLoggingKeys::resourceResponseSourceKey() +{ + return ASCIILiteral("resourceResponseSource"); +} + +String DiagnosticLoggingKeys::retrievalKey() +{ + return ASCIILiteral("retrieval"); +} + +String DiagnosticLoggingKeys::revalidatingKey() +{ + return ASCIILiteral("revalidating"); +} + +String DiagnosticLoggingKeys::reloadFromOriginKey() +{ + return ASCIILiteral("reloadFromOrigin"); +} + +String DiagnosticLoggingKeys::sameLoadKey() +{ + return ASCIILiteral("sameLoad"); +} + +String DiagnosticLoggingKeys::scriptKey() +{ + return ASCIILiteral("script"); +} + +String DiagnosticLoggingKeys::streamingMedia() +{ + return ASCIILiteral("streamingMedia"); +} + +String DiagnosticLoggingKeys::styleSheetKey() +{ + return ASCIILiteral("styleSheet"); +} + +String DiagnosticLoggingKeys::successfulSpeculativeWarmupWithRevalidationKey() +{ + return ASCIILiteral("successfulSpeculativeWarmupWithRevalidation"); +} + +String DiagnosticLoggingKeys::successfulSpeculativeWarmupWithoutRevalidationKey() +{ + return ASCIILiteral("successfulSpeculativeWarmupWithoutRevalidation"); +} + +String DiagnosticLoggingKeys::svgDocumentKey() +{ + return ASCIILiteral("svgDocument"); +} + +String DiagnosticLoggingKeys::synchronousMessageFailedKey() +{ + return ASCIILiteral("synchronousMessageFailed"); +} + +String DiagnosticLoggingKeys::uncacheableStatusCodeKey() +{ + return ASCIILiteral("uncacheableStatusCode"); +} + +String DiagnosticLoggingKeys::underMemoryPressureKey() +{ + return ASCIILiteral("underMemoryPressure"); +} + +String DiagnosticLoggingKeys::unknownEntryRequestKey() +{ + return ASCIILiteral("unknownEntryRequest"); +} + +String DiagnosticLoggingKeys::unlikelyToReuseKey() +{ + return ASCIILiteral("unlikelyToReuse"); +} + +String DiagnosticLoggingKeys::unsupportedHTTPMethodKey() +{ + return ASCIILiteral("unsupportedHTTPMethod"); +} + +String DiagnosticLoggingKeys::unsuspendableDOMObjectKey() +{ + return ASCIILiteral("unsuspendableDOMObject"); +} + +String DiagnosticLoggingKeys::unusedKey() +{ + return ASCIILiteral("unused"); +} + +String DiagnosticLoggingKeys::unusedReasonCredentialSettingsKey() +{ + return ASCIILiteral("unused.reason.credentialSettings"); +} + +String DiagnosticLoggingKeys::unusedReasonErrorKey() +{ + return ASCIILiteral("unused.reason.error"); +} + +String DiagnosticLoggingKeys::unusedReasonMustRevalidateNoValidatorKey() +{ + return ASCIILiteral("unused.reason.mustRevalidateNoValidator"); +} + +String DiagnosticLoggingKeys::unusedReasonNoStoreKey() +{ + return ASCIILiteral("unused.reason.noStore"); +} + +String DiagnosticLoggingKeys::unusedReasonRedirectChainKey() +{ + return ASCIILiteral("unused.reason.redirectChain"); +} + +String DiagnosticLoggingKeys::unusedReasonReloadKey() +{ + return ASCIILiteral("unused.reason.reload"); +} + +String DiagnosticLoggingKeys::unusedReasonTypeMismatchKey() +{ + return ASCIILiteral("unused.reason.typeMismatch"); +} + +String DiagnosticLoggingKeys::usedKey() +{ + return ASCIILiteral("used"); +} + +String DiagnosticLoggingKeys::userZoomActionKey() +{ + return ASCIILiteral("userZoomAction"); +} + +String DiagnosticLoggingKeys::varyingHeaderMismatchKey() +{ + return ASCIILiteral("varyingHeaderMismatch"); +} + +String DiagnosticLoggingKeys::videoKey() +{ + return ASCIILiteral("video"); +} + +String DiagnosticLoggingKeys::visibleNonActiveStateKey() +{ + return ASCIILiteral("visibleNonActiveState"); +} + +String DiagnosticLoggingKeys::visibleAndActiveStateKey() +{ + return ASCIILiteral("visibleAndActiveState"); +} + +String DiagnosticLoggingKeys::wastedSpeculativeWarmupWithRevalidationKey() +{ + return ASCIILiteral("wastedSpeculativeWarmupWithRevalidation"); +} + +String DiagnosticLoggingKeys::wastedSpeculativeWarmupWithoutRevalidationKey() +{ + return ASCIILiteral("wastedSpeculativeWarmupWithoutRevalidation"); +} + +String DiagnosticLoggingKeys::webViewKey() +{ + return ASCIILiteral("webView"); +} + +String DiagnosticLoggingKeys::yesKey() +{ + return ASCIILiteral("yes"); +} + +String DiagnosticLoggingKeys::expiredKey() +{ + return ASCIILiteral("expired"); +} + +String DiagnosticLoggingKeys::fontKey() +{ + return ASCIILiteral("font"); +} + +String DiagnosticLoggingKeys::prunedDueToMemoryPressureKey() +{ + return ASCIILiteral("pruned.memoryPressure"); +} + +String DiagnosticLoggingKeys::prunedDueToMaxSizeReached() +{ + return ASCIILiteral("pruned.capacityReached"); +} + +String DiagnosticLoggingKeys::prunedDueToProcessSuspended() +{ + return ASCIILiteral("pruned.processSuspended"); +} + +String WebCore::DiagnosticLoggingKeys::notHTTPFamilyKey() +{ + return ASCIILiteral("notHTTPFamily"); +} + +String WebCore::DiagnosticLoggingKeys::webGLStateKey() +{ + return ASCIILiteral("webGLState"); +} + +String DiagnosticLoggingKeys::memoryUsageToDiagnosticLoggingKey(uint64_t memoryUsage) +{ + if (memoryUsage < 32 * MB) + return ASCIILiteral("below32"); + if (memoryUsage < 64 * MB) + return ASCIILiteral("32to64"); + if (memoryUsage < 128 * MB) + return ASCIILiteral("64to128"); + if (memoryUsage < 256 * MB) + return ASCIILiteral("128to256"); + if (memoryUsage < 512 * MB) + return ASCIILiteral("256to512"); + if (memoryUsage < 1024 * MB) + return ASCIILiteral("512to1024"); + if (memoryUsage < 2048 * MB) + return ASCIILiteral("1024to2048"); + if (memoryUsage < 4096llu * MB) + return ASCIILiteral("2048to4096"); + if (memoryUsage < 8192llu * MB) + return ASCIILiteral("4096to8192"); + if (memoryUsage < 16384llu * MB) + return ASCIILiteral("8192to16384"); + if (memoryUsage < 32768llu * MB) + return ASCIILiteral("16384to32768"); + return ASCIILiteral("over32768"); +} + +String DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLoggingKey(double cpuUsage) +{ + if (cpuUsage < 10) + return ASCIILiteral("below10"); + if (cpuUsage < 20) + return ASCIILiteral("10to20"); + if (cpuUsage < 40) + return ASCIILiteral("20to40"); + if (cpuUsage < 60) + return ASCIILiteral("40to60"); + if (cpuUsage < 80) + return ASCIILiteral("60to80"); + return ASCIILiteral("over80"); +} + +String DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLoggingKey(double cpuUsage) +{ + if (cpuUsage < 1) + return ASCIILiteral("below1"); + if (cpuUsage < 5) + return ASCIILiteral("1to5"); + if (cpuUsage < 10) + return ASCIILiteral("5to10"); + if (cpuUsage < 30) + return ASCIILiteral("10to30"); + if (cpuUsage < 50) + return ASCIILiteral("30to50"); + if (cpuUsage < 70) + return ASCIILiteral("50to70"); + return ASCIILiteral("over70"); +} + +} // namespace WebCore + diff --git a/Source/WebCore/page/DiagnosticLoggingKeys.h b/Source/WebCore/page/DiagnosticLoggingKeys.h index baa51563e..39a77c67b 100644 --- a/Source/WebCore/page/DiagnosticLoggingKeys.h +++ b/Source/WebCore/page/DiagnosticLoggingKeys.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DiagnosticLoggingKeys_h -#define DiagnosticLoggingKeys_h +#pragma once #include @@ -32,22 +31,139 @@ namespace WebCore { class DiagnosticLoggingKeys { public: - // Message keys. + WEBCORE_EXPORT static String activeInForegroundTabKey(); + WEBCORE_EXPORT static String activeInBackgroundTabOnlyKey(); + static String applicationCacheKey(); + static String audioKey(); + WEBCORE_EXPORT static String backNavigationDeltaKey(); + WEBCORE_EXPORT static String cacheControlNoStoreKey(); + static String cachedResourceRevalidationKey(); + static String cachedResourceRevalidationReasonKey(); + static String canCacheKey(); + static String cannotSuspendActiveDOMObjectsKey(); + WEBCORE_EXPORT static String cpuUsageKey(); + WEBCORE_EXPORT static String createSharedBufferFailedKey(); + static String deniedByClientKey(); + static String deviceMotionKey(); + static String deviceOrientationKey(); + static String deviceProximityKey(); + static String diskCacheKey(); + static String diskCacheAfterValidationKey(); + static String documentLoaderStoppingKey(); + static String domainVisitedKey(); + static String engineFailedToLoadKey(); + WEBCORE_EXPORT static String entryRightlyNotWarmedUpKey(); + WEBCORE_EXPORT static String entryWronglyNotWarmedUpKey(); + static String expiredKey(); + static String fontKey(); + static String hasPluginsKey(); + static String httpsNoStoreKey(); + static String imageKey(); + static String inMemoryCacheKey(); + WEBCORE_EXPORT static String inactiveKey(); + WEBCORE_EXPORT static String internalErrorKey(); + WEBCORE_EXPORT static String invalidSessionIDKey(); + WEBCORE_EXPORT static String isAttachmentKey(); + WEBCORE_EXPORT static String isConditionalRequestKey(); + static String isDisabledKey(); + static String isErrorPageKey(); + static String isExpiredKey(); + WEBCORE_EXPORT static String isReloadIgnoringCacheDataKey(); + static String loadingKey(); + static String isLoadingKey(); + static String mainDocumentErrorKey(); + static String mainResourceKey(); static String mediaLoadedKey(); static String mediaLoadingFailedKey(); - static String pluginLoadedKey(); - static String pluginLoadingFailedKey(); - static String pageContainsPluginKey(); + static String memoryCacheEntryDecisionKey(); + static String memoryCacheUsageKey(); + WEBCORE_EXPORT static String missingValidatorFieldsKey(); + static String navigationKey(); + WEBCORE_EXPORT static String needsRevalidationKey(); + WEBCORE_EXPORT static String networkCacheKey(); + WEBCORE_EXPORT static String networkCacheFailureReasonKey(); + WEBCORE_EXPORT static String networkCacheUnusedReasonKey(); + WEBCORE_EXPORT static String networkCacheReuseFailureKey(); + static String networkKey(); + WEBCORE_EXPORT static String networkProcessCrashedKey(); + WEBCORE_EXPORT static String neverSeenBeforeKey(); + static String noKey(); + static String noCacheKey(); + static String noCurrentHistoryItemKey(); + static String noDocumentLoaderKey(); + WEBCORE_EXPORT static String noLongerInCacheKey(); + static String noStoreKey(); + WEBCORE_EXPORT static String nonVisibleStateKey(); + WEBCORE_EXPORT static String notHTTPFamilyKey(); + static String notInMemoryCacheKey(); + WEBCORE_EXPORT static String otherKey(); + static String pageCacheKey(); + static String pageCacheFailureKey(); + static String pageContainsAtLeastOneMediaEngineKey(); static String pageContainsAtLeastOnePluginKey(); static String pageContainsMediaEngineKey(); - static String pageContainsAtLeastOneMediaEngineKey(); + static String pageContainsPluginKey(); + static String pageHandlesWebGLContextLossKey(); + static String pageLoadedKey(); + static String playedKey(); + static String pluginLoadedKey(); + static String pluginLoadingFailedKey(); + static String postPageBackgroundingCPUUsageKey(); + static String postPageBackgroundingMemoryUsageKey(); + static String postPageLoadCPUUsageKey(); + static String postPageLoadMemoryUsageKey(); + static String provisionalLoadKey(); + static String prunedDueToMaxSizeReached(); + static String prunedDueToMemoryPressureKey(); + static String prunedDueToProcessSuspended(); + static String quirkRedirectComingKey(); + static String rawKey(); + static String redirectKey(); + static String reloadFromOriginKey(); + static String reloadKey(); + static String replaceKey(); + static String resourceLoadedKey(); + static String resourceResponseSourceKey(); + WEBCORE_EXPORT static String retrievalKey(); + WEBCORE_EXPORT static String retrievalRequestKey(); + WEBCORE_EXPORT static String revalidatingKey(); + static String sameLoadKey(); + static String scriptKey(); + WEBCORE_EXPORT static String streamingMedia(); + static String styleSheetKey(); + WEBCORE_EXPORT static String successfulSpeculativeWarmupWithRevalidationKey(); + WEBCORE_EXPORT static String successfulSpeculativeWarmupWithoutRevalidationKey(); + static String svgDocumentKey(); + WEBCORE_EXPORT static String synchronousMessageFailedKey(); + WEBCORE_EXPORT static String uncacheableStatusCodeKey(); + static String underMemoryPressureKey(); + WEBCORE_EXPORT static String unknownEntryRequestKey(); + WEBCORE_EXPORT static String unlikelyToReuseKey(); + WEBCORE_EXPORT static String unsupportedHTTPMethodKey(); + static String unsuspendableDOMObjectKey(); + WEBCORE_EXPORT static String unusedKey(); + static String unusedReasonCredentialSettingsKey(); + static String unusedReasonErrorKey(); + static String unusedReasonMustRevalidateNoValidatorKey(); + static String unusedReasonNoStoreKey(); + static String unusedReasonRedirectChainKey(); + static String unusedReasonReloadKey(); + static String unusedReasonTypeMismatchKey(); + static String usedKey(); + WEBCORE_EXPORT static String userZoomActionKey(); + WEBCORE_EXPORT static String varyingHeaderMismatchKey(); + static String videoKey(); + WEBCORE_EXPORT static String visibleNonActiveStateKey(); + WEBCORE_EXPORT static String visibleAndActiveStateKey(); + WEBCORE_EXPORT static String wastedSpeculativeWarmupWithRevalidationKey(); + WEBCORE_EXPORT static String wastedSpeculativeWarmupWithoutRevalidationKey(); + WEBCORE_EXPORT static String webGLStateKey(); + WEBCORE_EXPORT static String webViewKey(); + static String yesKey(); - // Success keys. - static String passKey(); - static String failKey(); - static String noopKey(); + WEBCORE_EXPORT static String memoryUsageToDiagnosticLoggingKey(uint64_t memoryUsage); + WEBCORE_EXPORT static String foregroundCPUUsageToDiagnosticLoggingKey(double cpuUsage); + WEBCORE_EXPORT static String backgroundCPUUsageToDiagnosticLoggingKey(double cpuUsage); }; -} - -#endif // DiagnosticLoggingKeys_h +} // namespace WebCore diff --git a/Source/WebCore/page/DiagnosticLoggingResultType.h b/Source/WebCore/page/DiagnosticLoggingResultType.h new file mode 100644 index 000000000..0fd0cea39 --- /dev/null +++ b/Source/WebCore/page/DiagnosticLoggingResultType.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2015 Apple 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +#pragma once + +namespace WebCore { + +enum DiagnosticLoggingResultType { + DiagnosticLoggingResultPass, + DiagnosticLoggingResultFail, + DiagnosticLoggingResultNoop, +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/DragActions.h b/Source/WebCore/page/DragActions.h index de8114723..ad2863472 100644 --- a/Source/WebCore/page/DragActions.h +++ b/Source/WebCore/page/DragActions.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DragActions_h -#define DragActions_h +#pragma once #include @@ -46,6 +45,9 @@ namespace WebCore { DragSourceActionImage = 2, DragSourceActionLink = 4, DragSourceActionSelection = 8, +#if ENABLE(ATTACHMENT_ELEMENT) + DragSourceActionAttachment = 16, +#endif DragSourceActionAny = UINT_MAX } DragSourceAction; @@ -61,6 +63,4 @@ namespace WebCore { DragOperationEvery = UINT_MAX } DragOperation; -} - -#endif // !DragActions_h +} // namespace WebCore diff --git a/Source/WebCore/page/DragClient.h b/Source/WebCore/page/DragClient.h index 343490c90..7c6a08891 100644 --- a/Source/WebCore/page/DragClient.h +++ b/Source/WebCore/page/DragClient.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,17 +23,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#ifndef DragClient_h -#define DragClient_h +#pragma once #include "DragActions.h" #include "DragImage.h" +#include "FloatPoint.h" #include "IntPoint.h" namespace WebCore { -class Clipboard; +class DataTransfer; class DragData; class Element; class Frame; @@ -43,24 +42,24 @@ class DragClient { public: virtual void dragControllerDestroyed() = 0; - virtual void willPerformDragDestinationAction(DragDestinationAction, DragData&) = 0; - virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard&) = 0; - virtual DragDestinationAction actionMaskForDrag(DragData&) = 0; + virtual void willPerformDragDestinationAction(DragDestinationAction, const DragData&) = 0; + virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, DataTransfer&) = 0; + virtual DragDestinationAction actionMaskForDrag(const DragData&) = 0; virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint& rootViewPoint) = 0; - virtual void startDrag(DragImageRef, const IntPoint& dragImageOrigin, const IntPoint& eventPos, Clipboard&, Frame&, bool linkDrag = false) = 0; + virtual void startDrag(DragImage, const IntPoint& dragImageOrigin, const IntPoint& eventPos, const FloatPoint& dragImageAnchor, DataTransfer&, Frame&, DragSourceAction) = 0; virtual void dragEnded() { } -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Mac-specific helper function to allow access to web archives and NSPasteboard extras in WebKit. - // This is not abstract as that would require another #if PLATFORM(MAC) for the SVGImage client empty implentation. + // This is not abstract as that would require another #if PLATFORM(COCOA) for the SVGImage client empty implentation. virtual void declareAndWriteDragImage(const String&, Element&, const URL&, const String&, Frame*) { } +#if ENABLE(ATTACHMENT_ELEMENT) + virtual void declareAndWriteAttachment(const String&, Element&, const URL&, const String&, Frame*) { } +#endif #endif virtual ~DragClient() { } }; } // namespace WebCore - -#endif // DragClient_h - diff --git a/Source/WebCore/page/DragController.cpp b/Source/WebCore/page/DragController.cpp index 3d9f4a6aa..095061adb 100644 --- a/Source/WebCore/page/DragController.cpp +++ b/Source/WebCore/page/DragController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -26,34 +26,32 @@ #include "config.h" #include "DragController.h" -#if ENABLE(DRAG_SUPPORT) +#include "HTMLAnchorElement.h" +#include "SVGAElement.h" +#if ENABLE(DRAG_SUPPORT) #include "CachedImage.h" -#include "Clipboard.h" -#include "ClipboardAccessPolicy.h" #include "CachedResourceLoader.h" +#include "ClientRect.h" +#include "DataTransfer.h" #include "Document.h" #include "DocumentFragment.h" #include "DragActions.h" #include "DragClient.h" #include "DragData.h" #include "DragImage.h" -#include "DragSession.h" #include "DragState.h" #include "Editor.h" #include "EditorClient.h" -#include "Element.h" #include "EventHandler.h" -#include "ExceptionCodePlaceholder.h" #include "FloatRect.h" #include "FrameLoadRequest.h" #include "FrameLoader.h" #include "FrameSelection.h" #include "FrameView.h" -#include "HTMLAnchorElement.h" +#include "HTMLAttachmentElement.h" #include "HTMLImageElement.h" #include "HTMLInputElement.h" -#include "HTMLNames.h" #include "HTMLPlugInElement.h" #include "HitTestRequest.h" #include "HitTestResult.h" @@ -79,32 +77,46 @@ #include "TextEvent.h" #include "htmlediting.h" #include "markup.h" + +#if ENABLE(DATA_INTERACTION) +#include "SelectionRect.h" +#endif + #include #include +#endif namespace WebCore { -static PlatformMouseEvent createMouseEvent(DragData& dragData) +bool isDraggableLink(const Element& element) { - bool shiftKey, ctrlKey, altKey, metaKey; - shiftKey = ctrlKey = altKey = metaKey = false; - int keyState = dragData.modifierKeyState(); - shiftKey = static_cast(keyState & PlatformEvent::ShiftKey); - ctrlKey = static_cast(keyState & PlatformEvent::CtrlKey); - altKey = static_cast(keyState & PlatformEvent::AltKey); - metaKey = static_cast(keyState & PlatformEvent::MetaKey); + if (is(element)) + return downcast(element).isLiveLink(); + if (is(element)) + return element.isLink(); + return false; +} + +#if ENABLE(DRAG_SUPPORT) + +static PlatformMouseEvent createMouseEvent(const DragData& dragData) +{ + bool shiftKey = false; + bool ctrlKey = false; + bool altKey = false; + bool metaKey = false; + + PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); return PlatformMouseEvent(dragData.clientPosition(), dragData.globalPosition(), LeftButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, - metaKey, currentTime()); + metaKey, currentTime(), ForceAtClick, NoTap); } DragController::DragController(Page& page, DragClient& client) : m_page(page) , m_client(client) - , m_documentUnderMouse(0) - , m_dragInitiator(0) - , m_fileInputElementUnderMouse(0) + , m_numberOfItemsToBeAccepted(0) , m_documentIsHandlingDrag(false) , m_dragDestinationAction(DragDestinationActionNone) , m_dragSourceAction(DragSourceActionNone) @@ -118,121 +130,131 @@ DragController::~DragController() m_client.dragControllerDestroyed(); } -static PassRefPtr documentFragmentFromDragData(DragData& dragData, Frame* frame, Range& context, bool allowPlainText, bool& chosePlainText) +static RefPtr documentFragmentFromDragData(const DragData& dragData, Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText) { chosePlainText = false; Document& document = context.ownerDocument(); if (dragData.containsCompatibleContent()) { - if (PassRefPtr fragment = dragData.asFragment(frame, context, allowPlainText, chosePlainText)) + if (auto fragment = frame.editor().webContentFromPasteboard(*Pasteboard::createForDragAndDrop(dragData), context, allowPlainText, chosePlainText)) return fragment; - if (dragData.containsURL(frame, DragData::DoNotConvertFilenames)) { + if (dragData.containsURL(DragData::DoNotConvertFilenames)) { String title; - String url = dragData.asURL(frame, DragData::DoNotConvertFilenames, &title); + String url = dragData.asURL(DragData::DoNotConvertFilenames, &title); if (!url.isEmpty()) { - RefPtr anchor = HTMLAnchorElement::create(document); + auto anchor = HTMLAnchorElement::create(document); anchor->setHref(url); if (title.isEmpty()) { // Try the plain text first because the url might be normalized or escaped. if (dragData.containsPlainText()) - title = dragData.asPlainText(frame); + title = dragData.asPlainText(); if (title.isEmpty()) title = url; } - RefPtr anchorText = document.createTextNode(title); - anchor->appendChild(anchorText, IGNORE_EXCEPTION); - RefPtr fragment = document.createDocumentFragment(); - fragment->appendChild(anchor, IGNORE_EXCEPTION); - return fragment.get(); + anchor->appendChild(document.createTextNode(title)); + auto fragment = document.createDocumentFragment(); + fragment->appendChild(anchor); + return WTFMove(fragment); } } } if (allowPlainText && dragData.containsPlainText()) { chosePlainText = true; - return createFragmentFromText(context, dragData.asPlainText(frame)).get(); + return createFragmentFromText(context, dragData.asPlainText()).ptr(); } - return 0; + return nullptr; } -bool DragController::dragIsMove(FrameSelection& selection, DragData& dragData) +bool DragController::dragIsMove(FrameSelection& selection, const DragData& dragData) { - return m_documentUnderMouse == m_dragInitiator && selection.isContentEditable() && selection.isRange() && !isCopyKeyDown(dragData); + const VisibleSelection& visibleSelection = selection.selection(); + return m_documentUnderMouse == m_dragInitiator && visibleSelection.isContentEditable() && visibleSelection.isRange() && !isCopyKeyDown(dragData); } -// FIXME: This method is poorly named. We're just clearing the selection from the document this drag is exiting. -void DragController::cancelDrag() +void DragController::clearDragCaret() { m_page.dragCaretController().clear(); } void DragController::dragEnded() { - m_dragInitiator = 0; + m_dragInitiator = nullptr; m_didInitiateDrag = false; - m_page.dragCaretController().clear(); + clearDragCaret(); m_client.dragEnded(); } -DragSession DragController::dragEntered(DragData& dragData) +DragOperation DragController::dragEntered(const DragData& dragData) { return dragEnteredOrUpdated(dragData); } -void DragController::dragExited(DragData& dragData) +void DragController::dragExited(const DragData& dragData) { if (RefPtr v = m_page.mainFrame().view()) { - ClipboardAccessPolicy policy = (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin()->isLocal()) ? ClipboardReadable : ClipboardTypesReadable; - RefPtr clipboard = Clipboard::createForDragAndDrop(policy, dragData); - clipboard->setSourceOperation(dragData.draggingSourceOperationMask()); - m_page.mainFrame().eventHandler().cancelDragAndDrop(createMouseEvent(dragData), clipboard.get()); - clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security +#if ENABLE(DASHBOARD_SUPPORT) + DataTransferAccessPolicy policy = (m_page.mainFrame().settings().usesDashboardBackwardCompatibilityMode() && (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin().isLocal())) + ? DataTransferAccessPolicy::Readable : DataTransferAccessPolicy::TypesReadable; +#else + DataTransferAccessPolicy policy = DataTransferAccessPolicy::TypesReadable; +#endif + auto dataTransfer = DataTransfer::createForDrop(policy, dragData); + dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask()); + m_page.mainFrame().eventHandler().cancelDragAndDrop(createMouseEvent(dragData), dataTransfer); + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. } - mouseMovedIntoDocument(0); + mouseMovedIntoDocument(nullptr); if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); - m_fileInputElementUnderMouse = 0; + m_fileInputElementUnderMouse = nullptr; } -DragSession DragController::dragUpdated(DragData& dragData) +DragOperation DragController::dragUpdated(const DragData& dragData) { return dragEnteredOrUpdated(dragData); } -bool DragController::performDrag(DragData& dragData) +bool DragController::performDragOperation(const DragData& dragData) { m_documentUnderMouse = m_page.mainFrame().documentAtPoint(dragData.clientPosition()); + + ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = ShouldOpenExternalURLsPolicy::ShouldNotAllow; + if (m_documentUnderMouse) + shouldOpenExternalURLsPolicy = m_documentUnderMouse->shouldOpenExternalURLsPolicyToPropagate(); + if ((m_dragDestinationAction & DragDestinationActionDHTML) && m_documentIsHandlingDrag) { m_client.willPerformDragDestinationAction(DragDestinationActionDHTML, dragData); Ref mainFrame(m_page.mainFrame()); bool preventedDefault = false; if (mainFrame->view()) { // Sending an event can result in the destruction of the view and part. - RefPtr clipboard = Clipboard::createForDragAndDrop(ClipboardReadable, dragData); - clipboard->setSourceOperation(dragData.draggingSourceOperationMask()); - preventedDefault = mainFrame->eventHandler().performDragAndDrop(createMouseEvent(dragData), clipboard.get()); - clipboard->setAccessPolicy(ClipboardNumb); // Invalidate clipboard here for security + auto dataTransfer = DataTransfer::createForDrop(DataTransferAccessPolicy::Readable, dragData); + dataTransfer->setSourceOperation(dragData.draggingSourceOperationMask()); + preventedDefault = mainFrame->eventHandler().performDragAndDrop(createMouseEvent(dragData), dataTransfer); + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. } if (preventedDefault) { - m_documentUnderMouse = 0; + clearDragCaret(); + m_documentUnderMouse = nullptr; return true; } } if ((m_dragDestinationAction & DragDestinationActionEdit) && concludeEditDrag(dragData)) { - m_documentUnderMouse = 0; + m_documentUnderMouse = nullptr; return true; } - m_documentUnderMouse = 0; + m_documentUnderMouse = nullptr; if (operationForLoad(dragData) == DragOperationNone) return false; m_client.willPerformDragDestinationAction(DragDestinationActionLoad, dragData); - m_page.mainFrame().loader().load(FrameLoadRequest(&m_page.mainFrame(), ResourceRequest(dragData.asURL(&m_page.mainFrame())))); + m_page.mainFrame().loader().load(FrameLoadRequest(&m_page.mainFrame(), ResourceRequest(dragData.asURL()), shouldOpenExternalURLsPolicy)); return true; } @@ -243,38 +265,41 @@ void DragController::mouseMovedIntoDocument(Document* newDocument) // If we were over another document clear the selection if (m_documentUnderMouse) - cancelDrag(); + clearDragCaret(); m_documentUnderMouse = newDocument; } -DragSession DragController::dragEnteredOrUpdated(DragData& dragData) +DragOperation DragController::dragEnteredOrUpdated(const DragData& dragData) { mouseMovedIntoDocument(m_page.mainFrame().documentAtPoint(dragData.clientPosition())); m_dragDestinationAction = m_client.actionMaskForDrag(dragData); if (m_dragDestinationAction == DragDestinationActionNone) { - cancelDrag(); // FIXME: Why not call mouseMovedIntoDocument(0)? - return DragSession(); + clearDragCaret(); // FIXME: Why not call mouseMovedIntoDocument(nullptr)? + return DragOperationNone; } - DragSession dragSession; - m_documentIsHandlingDrag = tryDocumentDrag(dragData, m_dragDestinationAction, dragSession); + DragOperation dragOperation = DragOperationNone; + m_documentIsHandlingDrag = tryDocumentDrag(dragData, m_dragDestinationAction, dragOperation); if (!m_documentIsHandlingDrag && (m_dragDestinationAction & DragDestinationActionLoad)) - dragSession.operation = operationForLoad(dragData); - return dragSession; + dragOperation = operationForLoad(dragData); + return dragOperation; } -static HTMLInputElement* asFileInput(Node* node) +static HTMLInputElement* asFileInput(Node& node) { - ASSERT(node); + if (!is(node)) + return nullptr; - HTMLInputElement* inputElement = node->toInputElement(); + auto* inputElement = &downcast(node); // If this is a button inside of the a file input, move up to the file input. - if (inputElement && inputElement->isTextButton() && inputElement->treeScope().rootNode()->isShadowRoot()) - inputElement = toShadowRoot(inputElement->treeScope().rootNode())->hostElement()->toInputElement(); + if (inputElement->isTextButton() && is(inputElement->treeScope().rootNode())) { + auto& host = *downcast(inputElement->treeScope().rootNode()).host(); + inputElement = is(host) ? &downcast(host) : nullptr; + } - return inputElement && inputElement->isFileUpload() ? inputElement : 0; + return inputElement && inputElement->isFileUpload() ? inputElement : nullptr; } // This can return null if an empty document is loaded. @@ -282,32 +307,31 @@ static Element* elementUnderMouse(Document* documentUnderMouse, const IntPoint& { Frame* frame = documentUnderMouse->frame(); float zoomFactor = frame ? frame->pageZoomFactor() : 1; - LayoutPoint point = roundedLayoutPoint(FloatPoint(p.x() * zoomFactor, p.y() * zoomFactor)); + LayoutPoint point(p.x() * zoomFactor, p.y() * zoomFactor); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); HitTestResult result(point); - documentUnderMouse->renderView()->hitTest(request, result); + documentUnderMouse->renderView()->hitTest(HitTestRequest(), result); - Node* n = result.innerNode(); - while (n && !n->isElementNode()) - n = n->parentNode(); - if (n) - n = n->deprecatedShadowAncestorNode(); + Node* node = result.innerNode(); + while (node && !is(*node)) + node = node->parentNode(); + if (node) + node = node->deprecatedShadowAncestorNode(); - return toElement(n); + return downcast(node); } -bool DragController::tryDocumentDrag(DragData& dragData, DragDestinationAction actionMask, DragSession& dragSession) +bool DragController::tryDocumentDrag(const DragData& dragData, DragDestinationAction actionMask, DragOperation& dragOperation) { if (!m_documentUnderMouse) return false; - if (m_dragInitiator && !m_documentUnderMouse->securityOrigin()->canReceiveDragData(m_dragInitiator->securityOrigin())) + if (m_dragInitiator && !m_documentUnderMouse->securityOrigin().canReceiveDragData(m_dragInitiator->securityOrigin())) return false; bool isHandlingDrag = false; if (actionMask & DragDestinationActionDHTML) { - isHandlingDrag = tryDHTMLDrag(dragData, dragSession.operation); + isHandlingDrag = tryDHTMLDrag(dragData, dragOperation); // Do not continue if m_documentUnderMouse has been reset by tryDHTMLDrag. // tryDHTMLDrag fires dragenter event. The event listener that listens // to this event may create a nested message loop (open a modal dialog), @@ -324,13 +348,13 @@ bool DragController::tryDocumentDrag(DragData& dragData, DragDestinationAction a return false; if (isHandlingDrag) { - m_page.dragCaretController().clear(); + clearDragCaret(); return true; } if ((actionMask & DragDestinationActionEdit) && canProcessDrag(dragData)) { if (dragData.containsColor()) { - dragSession.operation = DragOperationGeneric; + dragOperation = DragOperationGeneric; return true; } @@ -339,7 +363,7 @@ bool DragController::tryDocumentDrag(DragData& dragData, DragDestinationAction a if (!element) return false; - HTMLInputElement* elementAsFileInput = asFileInput(element); + HTMLInputElement* elementAsFileInput = asFileInput(*element); if (m_fileInputElementUnderMouse != elementAsFileInput) { if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); @@ -348,40 +372,41 @@ bool DragController::tryDocumentDrag(DragData& dragData, DragDestinationAction a if (!m_fileInputElementUnderMouse) m_page.dragCaretController().setCaretPosition(m_documentUnderMouse->frame()->visiblePositionForPoint(point)); + else + clearDragCaret(); Frame* innerFrame = element->document().frame(); - dragSession.operation = dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy; - dragSession.mouseIsOverFileInput = m_fileInputElementUnderMouse; - dragSession.numberOfItemsToBeAccepted = 0; + dragOperation = dragIsMove(innerFrame->selection(), dragData) ? DragOperationMove : DragOperationCopy; + m_numberOfItemsToBeAccepted = 0; unsigned numberOfFiles = dragData.numberOfFiles(); if (m_fileInputElementUnderMouse) { if (m_fileInputElementUnderMouse->isDisabledFormControl()) - dragSession.numberOfItemsToBeAccepted = 0; + m_numberOfItemsToBeAccepted = 0; else if (m_fileInputElementUnderMouse->multiple()) - dragSession.numberOfItemsToBeAccepted = numberOfFiles; + m_numberOfItemsToBeAccepted = numberOfFiles; else if (numberOfFiles > 1) - dragSession.numberOfItemsToBeAccepted = 0; + m_numberOfItemsToBeAccepted = 0; else - dragSession.numberOfItemsToBeAccepted = 1; + m_numberOfItemsToBeAccepted = 1; - if (!dragSession.numberOfItemsToBeAccepted) - dragSession.operation = DragOperationNone; - m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(dragSession.numberOfItemsToBeAccepted); + if (!m_numberOfItemsToBeAccepted) + dragOperation = DragOperationNone; + m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(m_numberOfItemsToBeAccepted); } else { // We are not over a file input element. The dragged item(s) will only // be loaded into the view the number of dragged items is 1. - dragSession.numberOfItemsToBeAccepted = numberOfFiles != 1 ? 0 : 1; + m_numberOfItemsToBeAccepted = numberOfFiles != 1 ? 0 : 1; } return true; } // We are not over an editable region. Make sure we're clearing any prior drag cursor. - m_page.dragCaretController().clear(); + clearDragCaret(); if (m_fileInputElementUnderMouse) m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); - m_fileInputElementUnderMouse = 0; + m_fileInputElementUnderMouse = nullptr; return false; } @@ -391,50 +416,51 @@ DragSourceAction DragController::delegateDragSourceAction(const IntPoint& rootVi return m_dragSourceAction; } -DragOperation DragController::operationForLoad(DragData& dragData) +DragOperation DragController::operationForLoad(const DragData& dragData) { - Document* doc = m_page.mainFrame().documentAtPoint(dragData.clientPosition()); + Document* document = m_page.mainFrame().documentAtPoint(dragData.clientPosition()); bool pluginDocumentAcceptsDrags = false; - if (doc && doc->isPluginDocument()) { - const Widget* widget = toPluginDocument(doc)->pluginWidget(); - const PluginViewBase* pluginView = (widget && widget->isPluginViewBase()) ? toPluginViewBase(widget) : nullptr; + if (is(document)) { + const Widget* widget = downcast(*document).pluginWidget(); + const PluginViewBase* pluginView = is(widget) ? downcast(widget) : nullptr; if (pluginView) pluginDocumentAcceptsDrags = pluginView->shouldAllowNavigationFromDrags(); } - if (doc && (m_didInitiateDrag || (doc->isPluginDocument() && !pluginDocumentAcceptsDrags) || doc->hasEditableStyle())) + if (document && (m_didInitiateDrag || (is(*document) && !pluginDocumentAcceptsDrags) || document->hasEditableStyle())) return DragOperationNone; return dragOperation(dragData); } static bool setSelectionToDragCaret(Frame* frame, VisibleSelection& dragCaret, RefPtr& range, const IntPoint& point) { + Ref protector(*frame); frame->selection().setSelection(dragCaret); - if (frame->selection().isNone()) { + if (frame->selection().selection().isNone()) { dragCaret = frame->visiblePositionForPoint(point); frame->selection().setSelection(dragCaret); range = dragCaret.toNormalizedRange(); } - return !frame->selection().isNone() && frame->selection().isContentEditable(); + return !frame->selection().isNone() && frame->selection().selection().isContentEditable(); } -bool DragController::dispatchTextInputEventFor(Frame* innerFrame, DragData& dragData) +bool DragController::dispatchTextInputEventFor(Frame* innerFrame, const DragData& dragData) { ASSERT(m_page.dragCaretController().hasCaret()); - String text = m_page.dragCaretController().isContentRichlyEditable() ? emptyString() : dragData.asPlainText(innerFrame); + String text = m_page.dragCaretController().isContentRichlyEditable() ? emptyString() : dragData.asPlainText(); Node* target = innerFrame->editor().findEventTargetFrom(m_page.dragCaretController().caretPosition()); - return target->dispatchEvent(TextEvent::createForDrop(innerFrame->document()->domWindow(), text), IGNORE_EXCEPTION); + return target->dispatchEvent(TextEvent::createForDrop(innerFrame->document()->domWindow(), text)); } -bool DragController::concludeEditDrag(DragData& dragData) +bool DragController::concludeEditDrag(const DragData& dragData) { RefPtr fileInput = m_fileInputElementUnderMouse; if (m_fileInputElementUnderMouse) { m_fileInputElementUnderMouse->setCanReceiveDroppedFiles(false); - m_fileInputElementUnderMouse = 0; + m_fileInputElementUnderMouse = nullptr; } if (!m_documentUnderMouse) @@ -475,26 +501,25 @@ bool DragController::concludeEditDrag(DragData& dragData) } if (!m_page.dragController().canProcessDrag(dragData)) { - m_page.dragCaretController().clear(); + clearDragCaret(); return false; } VisibleSelection dragCaret = m_page.dragCaretController().caretPosition(); - m_page.dragCaretController().clear(); + clearDragCaret(); RefPtr range = dragCaret.toNormalizedRange(); - RefPtr rootEditableElement = innerFrame->selection().rootEditableElement(); + RefPtr rootEditableElement = innerFrame->selection().selection().rootEditableElement(); // For range to be null a WebKit client must have done something bad while // manually controlling drag behaviour if (!range) return false; - CachedResourceLoader* cachedResourceLoader = range->ownerDocument().cachedResourceLoader(); - ResourceCacheValidationSuppressor validationSuppressor(cachedResourceLoader); + ResourceCacheValidationSuppressor validationSuppressor(range->ownerDocument().cachedResourceLoader()); if (dragIsMove(innerFrame->selection(), dragData) || dragCaret.isContentRichlyEditable()) { bool chosePlainText = false; - RefPtr fragment = documentFragmentFromDragData(dragData, innerFrame.get(), *range, true, chosePlainText); - if (!fragment || !innerFrame->editor().shouldInsertFragment(fragment, range, EditorInsertActionDropped)) { + RefPtr fragment = documentFragmentFromDragData(dragData, *innerFrame, *range, true, chosePlainText); + if (!fragment || !innerFrame->editor().shouldInsertFragment(fragment, range, EditorInsertAction::Dropped)) { return false; } @@ -512,29 +537,29 @@ bool DragController::concludeEditDrag(DragData& dragData) options |= ReplaceSelectionCommand::SmartReplace; if (chosePlainText) options |= ReplaceSelectionCommand::MatchStyle; - applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, fragment, options)); + applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, WTFMove(fragment), options, EditActionInsertFromDrop)); } } } else { - String text = dragData.asPlainText(innerFrame.get()); - if (text.isEmpty() || !innerFrame->editor().shouldInsertText(text, range.get(), EditorInsertActionDropped)) { + String text = dragData.asPlainText(); + if (text.isEmpty() || !innerFrame->editor().shouldInsertText(text, range.get(), EditorInsertAction::Dropped)) { return false; } m_client.willPerformDragDestinationAction(DragDestinationActionEdit, dragData); if (setSelectionToDragCaret(innerFrame.get(), dragCaret, range, point)) - applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, createFragmentFromText(*range, text), ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting)); + applyCommand(ReplaceSelectionCommand::create(*m_documentUnderMouse, createFragmentFromText(*range, text), ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MatchStyle | ReplaceSelectionCommand::PreventNesting, EditActionInsertFromDrop)); } if (rootEditableElement) { if (Frame* frame = rootEditableElement->document().frame()) - frame->eventHandler().updateDragStateAfterEditDragIfNeeded(rootEditableElement.get()); + frame->eventHandler().updateDragStateAfterEditDragIfNeeded(*rootEditableElement); } return true; } -bool DragController::canProcessDrag(DragData& dragData) +bool DragController::canProcessDrag(const DragData& dragData) { if (!dragData.containsCompatibleContent()) return false; @@ -549,11 +574,11 @@ bool DragController::canProcessDrag(DragData& dragData) if (!result.innerNonSharedNode()) return false; - if (dragData.containsFiles() && asFileInput(result.innerNonSharedNode())) + if (dragData.containsFiles() && asFileInput(*result.innerNonSharedNode())) return true; - if (result.innerNonSharedNode()->isPluginElement()) { - if (!toHTMLPlugInElement(result.innerNonSharedNode())->canProcessDrag() && !result.innerNonSharedNode()->hasEditableStyle()) + if (is(*result.innerNonSharedNode())) { + if (!downcast(result.innerNonSharedNode())->canProcessDrag() && !result.innerNonSharedNode()->hasEditableStyle()) return false; } else if (!result.innerNonSharedNode()->hasEditableStyle()) return false; @@ -583,7 +608,7 @@ static DragOperation defaultOperationForDrag(DragOperation srcOpMask) return DragOperationGeneric; } -bool DragController::tryDHTMLDrag(DragData& dragData, DragOperation& operation) +bool DragController::tryDHTMLDrag(const DragData& dragData, DragOperation& operation) { ASSERT(m_documentUnderMouse); Ref mainFrame(m_page.mainFrame()); @@ -591,26 +616,31 @@ bool DragController::tryDHTMLDrag(DragData& dragData, DragOperation& operation) if (!viewProtector) return false; - ClipboardAccessPolicy policy = m_documentUnderMouse->securityOrigin()->isLocal() ? ClipboardReadable : ClipboardTypesReadable; - RefPtr clipboard = Clipboard::createForDragAndDrop(policy, dragData); +#if ENABLE(DASHBOARD_SUPPORT) + DataTransferAccessPolicy policy = (mainFrame->settings().usesDashboardBackwardCompatibilityMode() && m_documentUnderMouse->securityOrigin().isLocal()) ? + DataTransferAccessPolicy::Readable : DataTransferAccessPolicy::TypesReadable; +#else + DataTransferAccessPolicy policy = DataTransferAccessPolicy::TypesReadable; +#endif + auto dataTransfer = DataTransfer::createForDrop(policy, dragData); DragOperation srcOpMask = dragData.draggingSourceOperationMask(); - clipboard->setSourceOperation(srcOpMask); + dataTransfer->setSourceOperation(srcOpMask); PlatformMouseEvent event = createMouseEvent(dragData); - if (!mainFrame->eventHandler().updateDragAndDrop(event, clipboard.get())) { - clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security + if (!mainFrame->eventHandler().updateDragAndDrop(event, dataTransfer)) { + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. return false; } - operation = clipboard->destinationOperation(); - if (clipboard->dropEffectIsUninitialized()) + operation = dataTransfer->destinationOperation(); + if (dataTransfer->dropEffectIsUninitialized()) operation = defaultOperationForDrag(srcOpMask); else if (!(srcOpMask & operation)) { // The element picked an operation which is not supported by the source operation = DragOperationNone; } - clipboard->setAccessPolicy(ClipboardNumb); // invalidate clipboard here for security + dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); // Invalidate dataTransfer here for security. return true; } @@ -618,15 +648,21 @@ Element* DragController::draggableElement(const Frame* sourceFrame, Element* sta { state.type = (sourceFrame->selection().contains(dragOrigin)) ? DragSourceActionSelection : DragSourceActionNone; if (!startElement) - return 0; + return nullptr; +#if ENABLE(ATTACHMENT_ELEMENT) + // Unlike image elements, attachment elements are immediately selected upon mouse down, + // but for those elements we still want to use the single element drag behavior as long as + // the element is the only content of the selection. + const VisibleSelection& selection = sourceFrame->selection().selection(); + if (selection.isRange() && is(selection.start().anchorNode()) && selection.start().anchorNode() == selection.end().anchorNode()) + state.type = DragSourceActionNone; +#endif - for (auto renderer = startElement->renderer(); renderer; renderer = renderer->parent()) { - Element* element = renderer->nonPseudoElement(); - if (!element) { - // Anonymous render blocks don't correspond to actual DOM elements, so we skip over them - // for the purposes of finding a draggable element. + for (auto* element = startElement; element; element = element->parentOrShadowHostElement()) { + auto* renderer = element->renderer(); + if (!renderer) continue; - } + EUserDrag dragMode = renderer->style().userDrag(); if ((m_dragSourceAction & DragSourceActionDHTML) && dragMode == DRAG_ELEMENT) { state.type = static_cast(state.type | DragSourceActionDHTML); @@ -634,31 +670,37 @@ Element* DragController::draggableElement(const Frame* sourceFrame, Element* sta } if (dragMode == DRAG_AUTO) { if ((m_dragSourceAction & DragSourceActionImage) - && isHTMLImageElement(element) + && is(*element) && sourceFrame->settings().loadsImagesAutomatically()) { state.type = static_cast(state.type | DragSourceActionImage); return element; } - if ((m_dragSourceAction & DragSourceActionLink) - && isHTMLAnchorElement(element) - && toHTMLAnchorElement(element)->isLiveLink()) { + if ((m_dragSourceAction & DragSourceActionLink) && isDraggableLink(*element)) { state.type = static_cast(state.type | DragSourceActionLink); return element; } +#if ENABLE(ATTACHMENT_ELEMENT) + if ((m_dragSourceAction & DragSourceActionAttachment) + && is(*element) + && downcast(*element).file()) { + state.type = static_cast(state.type | DragSourceActionAttachment); + return element; + } +#endif } } // We either have nothing to drag or we have a selection and we're not over a draggable element. - return (state.type & DragSourceActionSelection) ? startElement : 0; + return (state.type & DragSourceActionSelection) ? startElement : nullptr; } static CachedImage* getCachedImage(Element& element) { RenderObject* renderer = element.renderer(); - if (!renderer || !renderer->isRenderImage()) - return 0; - RenderImage* image = toRenderImage(renderer); - return image->cachedImage(); + if (!is(renderer)) + return nullptr; + auto& image = downcast(*renderer); + return image.cachedImage(); } static Image* getImage(Element& element) @@ -668,20 +710,20 @@ static Image* getImage(Element& element) // Users of getImage() want access to the SVGImage, in order to figure out the filename extensions, // which would be empty when asking the cached BitmapImages. return (cachedImage && !cachedImage->errorOccurred()) ? - cachedImage->image() : 0; + cachedImage->image() : nullptr; } static void selectElement(Element& element) { RefPtr range = element.document().createRange(); - range->selectNode(&element); - element.document().frame()->selection().setSelection(VisibleSelection(range.get(), DOWNSTREAM)); + range->selectNode(element); + element.document().frame()->selection().setSelection(VisibleSelection(*range, DOWNSTREAM)); } static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const IntPoint& dragOrigin, const IntPoint& dragImageOffset, bool isLinkImage) { // dragImageOffset is the cursor position relative to the lower-left corner of the image. -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // We add in the Y dimension because we are a flipped view, so adding moves the image down. const int yOffset = dragImageOffset.y(); #else @@ -696,11 +738,11 @@ static IntPoint dragLocForDHTMLDrag(const IntPoint& mouseDraggedPoint, const Int static IntPoint dragLocForSelectionDrag(Frame& src) { - IntRect draggingRect = enclosingIntRect(src.selection().bounds()); + IntRect draggingRect = enclosingIntRect(src.selection().selectionBounds()); int xpos = draggingRect.maxX(); xpos = draggingRect.x() < xpos ? draggingRect.x() : xpos; int ypos = draggingRect.maxY(); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Deal with flipped coordinates on Mac ypos = draggingRect.y() > ypos ? draggingRect.y() : ypos; #else @@ -711,174 +753,238 @@ static IntPoint dragLocForSelectionDrag(Frame& src) bool DragController::startDrag(Frame& src, const DragState& state, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin) { - if (!src.view() || !src.contentRenderer()) + if (!src.view() || !src.contentRenderer() || !state.source) return false; + Ref protector(src); HitTestResult hitTestResult = src.eventHandler().hitTestResultAtPoint(dragOrigin, HitTestRequest::ReadOnly | HitTestRequest::Active); - if (!state.source->contains(hitTestResult.innerNode())) + + // FIXME(136836): Investigate whether all elements should use the containsIncludingShadowDOM() path here. + bool includeShadowDOM = false; +#if ENABLE(VIDEO) + includeShadowDOM = state.source->isMediaElement(); +#endif + bool sourceContainsHitNode; + if (!includeShadowDOM) + sourceContainsHitNode = state.source->contains(hitTestResult.innerNode()); + else + sourceContainsHitNode = state.source->containsIncludingShadowDOM(hitTestResult.innerNode()); + + if (!sourceContainsHitNode) { // The original node being dragged isn't under the drag origin anymore... maybe it was // hidden or moved out from under the cursor. Regardless, we don't want to start a drag on // something that's not actually under the drag origin. return false; + } + URL linkURL = hitTestResult.absoluteLinkURL(); URL imageURL = hitTestResult.absoluteImageURL(); +#if ENABLE(ATTACHMENT_ELEMENT) + URL attachmentURL = hitTestResult.absoluteAttachmentURL(); + m_draggingAttachmentURL = URL(); +#endif IntPoint mouseDraggedPoint = src.view()->windowToContents(dragEvent.position()); m_draggingImageURL = URL(); m_sourceDragOperation = srcOp; - DragImageRef dragImage = 0; + DragImage dragImage; IntPoint dragLoc(0, 0); IntPoint dragImageOffset(0, 0); - ASSERT(state.clipboard); + ASSERT(state.dataTransfer); - Clipboard& clipboard = *state.clipboard; - if (state.type == DragSourceActionDHTML) - dragImage = clipboard.createDragImage(dragImageOffset); - if (state.type == DragSourceActionSelection || !imageURL.isEmpty() || !linkURL.isEmpty()) + DataTransfer& dataTransfer = *state.dataTransfer; + if (state.type == DragSourceActionDHTML) { + dragImage = DragImage { dataTransfer.createDragImage(dragImageOffset) }; + // We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging. + // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp. + if (dragImage) { + dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty()); + m_dragOffset = dragImageOffset; + } + } + + if (state.type == DragSourceActionSelection || !imageURL.isEmpty() || !linkURL.isEmpty()) { // Selection, image, and link drags receive a default set of allowed drag operations that // follows from: // http://trac.webkit.org/browser/trunk/WebKit/mac/WebView/WebHTMLView.mm?rev=48526#L3430 m_sourceDragOperation = static_cast(m_sourceDragOperation | DragOperationGeneric | DragOperationCopy); - - // We allow DHTML/JS to set the drag image, even if its a link, image or text we're dragging. - // This is in the spirit of the IE API, which allows overriding of pasteboard data and DragOp. - if (dragImage) { - dragLoc = dragLocForDHTMLDrag(mouseDraggedPoint, dragOrigin, dragImageOffset, !linkURL.isEmpty()); - m_dragOffset = dragImageOffset; } - bool startedDrag = true; // optimism - we almost always manage to start the drag - ASSERT(state.source); Element& element = *state.source; + IntRect dragImageBounds; Image* image = getImage(element); if (state.type == DragSourceActionSelection) { - if (!clipboard.pasteboard().hasData()) { + if (!dataTransfer.pasteboard().hasData()) { // FIXME: This entire block is almost identical to the code in Editor::copy, and the code should be shared. - RefPtr selectionRange = src.selection().toNormalizedRange(); ASSERT(selectionRange); +#if ENABLE(DATA_INTERACTION) + Vector selectionRects; + selectionRange->collectSelectionRects(selectionRects); + for (auto selectionRect : selectionRects) + dragImageBounds.unite(selectionRect.rect()); + dragImageBounds.inflate(SelectionDragImagePadding); +#endif + src.editor().willWriteSelectionToPasteboard(selectionRange.get()); - if (enclosingTextFormControl(src.selection().start())) - clipboard.pasteboard().writePlainText(src.editor().selectedTextForClipboard(), Pasteboard::CannotSmartReplace); + if (enclosingTextFormControl(src.selection().selection().start())) + dataTransfer.pasteboard().writePlainText(src.editor().selectedTextForDataTransfer(), Pasteboard::CannotSmartReplace); else { -#if PLATFORM(MAC) || PLATFORM(EFL) - src.editor().writeSelectionToPasteboard(clipboard.pasteboard()); +#if PLATFORM(COCOA) || PLATFORM(GTK) + src.editor().writeSelectionToPasteboard(dataTransfer.pasteboard()); #else // FIXME: Convert all other platforms to match Mac and delete this. - clipboard.pasteboard().writeSelection(*selectionRange, src.editor().canSmartCopyOrDelete(), src, IncludeImageAltTextForClipboard); + dataTransfer.pasteboard().writeSelection(*selectionRange, src.editor().canSmartCopyOrDelete(), src, IncludeImageAltTextForDataTransfer); #endif } src.editor().didWriteSelectionToPasteboard(); } - m_client.willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, clipboard); + m_client.willPerformDragSourceAction(DragSourceActionSelection, dragOrigin, dataTransfer); if (!dragImage) { - dragImage = dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha); + dragImage = DragImage { dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha) }; dragLoc = dragLocForSelectionDrag(src); m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y()); } - doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); - } else if (!imageURL.isEmpty() && image && !image->isNull() && (m_dragSourceAction & DragSourceActionImage)) { + + if (!dragImage) + return false; + + doSystemDrag(WTFMove(dragImage), dragLoc, dragOrigin, dragImageBounds, dataTransfer, src, DragSourceActionSelection); + return true; + } + + if (!src.document()->securityOrigin().canDisplay(linkURL)) { + src.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to drag local resource: " + linkURL.stringCenterEllipsizedToLength()); + return false; + } + + if (!imageURL.isEmpty() && image && !image->isNull() && (m_dragSourceAction & DragSourceActionImage)) { // We shouldn't be starting a drag for an image that can't provide an extension. // This is an early detection for problems encountered later upon drop. ASSERT(!image->filenameExtension().isEmpty()); - if (!clipboard.pasteboard().hasData()) { + if (!dataTransfer.pasteboard().hasData()) { m_draggingImageURL = imageURL; if (element.isContentRichlyEditable()) selectElement(element); - declareAndWriteDragImage(clipboard, element, !linkURL.isEmpty() ? linkURL : imageURL, hitTestResult.altDisplayString()); + declareAndWriteDragImage(dataTransfer, element, !linkURL.isEmpty() ? linkURL : imageURL, hitTestResult.altDisplayString()); } - m_client.willPerformDragSourceAction(DragSourceActionImage, dragOrigin, clipboard); + m_client.willPerformDragSourceAction(DragSourceActionImage, dragOrigin, dataTransfer); if (!dragImage) { IntRect imageRect = hitTestResult.imageRect(); imageRect.setLocation(m_page.mainFrame().view()->rootViewToContents(src.view()->contentsToRootView(imageRect.location()))); - doImageDrag(element, dragOrigin, hitTestResult.imageRect(), clipboard, src, m_dragOffset); + doImageDrag(element, dragOrigin, hitTestResult.imageRect(), dataTransfer, src, m_dragOffset); } else { // DHTML defined drag image - doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); + doSystemDrag(WTFMove(dragImage), dragLoc, dragOrigin, { }, dataTransfer, src, DragSourceActionImage); } - } else if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) { - if (!clipboard.pasteboard().hasData()) - // Simplify whitespace so the title put on the clipboard resembles what the user sees + + return true; + } + + if (!linkURL.isEmpty() && (m_dragSourceAction & DragSourceActionLink)) { + if (!dataTransfer.pasteboard().hasData()) { + // Simplify whitespace so the title put on the dataTransfer resembles what the user sees // on the web page. This includes replacing newlines with spaces. - src.editor().copyURL(linkURL, hitTestResult.textContent().simplifyWhiteSpace(), clipboard.pasteboard()); + src.editor().copyURL(linkURL, hitTestResult.textContent().simplifyWhiteSpace(), dataTransfer.pasteboard()); + } else { + // Make sure the pasteboard also contains trustworthy link data + // but don't overwrite more general pasteboard types. + PasteboardURL pasteboardURL; + pasteboardURL.url = linkURL; + pasteboardURL.title = hitTestResult.textContent(); + dataTransfer.pasteboard().writeTrustworthyWebURLsPboardType(pasteboardURL); + } - if (src.selection().isCaret() && src.selection().isContentEditable()) { + const VisibleSelection& sourceSelection = src.selection().selection(); + if (sourceSelection.isCaret() && sourceSelection.isContentEditable()) { // a user can initiate a drag on a link without having any text // selected. In this case, we should expand the selection to // the enclosing anchor element - Position pos = src.selection().base(); + Position pos = sourceSelection.base(); Node* node = enclosingAnchorElement(pos); if (node) src.selection().setSelection(VisibleSelection::selectionFromContentsOfNode(node)); } - m_client.willPerformDragSourceAction(DragSourceActionLink, dragOrigin, clipboard); + m_client.willPerformDragSourceAction(DragSourceActionLink, dragOrigin, dataTransfer); if (!dragImage) { - dragImage = createDragImageForLink(linkURL, hitTestResult.textContent(), src.settings().fontRenderingMode()); - IntSize size = dragImageSize(dragImage); + dragImage = DragImage { createDragImageForLink(linkURL, hitTestResult.textContent(), src.settings().fontRenderingMode()) }; + IntSize size = dragImageSize(dragImage.get()); m_dragOffset = IntPoint(-size.width() / 2, -LinkDragBorderInset); dragLoc = IntPoint(mouseDraggedPoint.x() + m_dragOffset.x(), mouseDraggedPoint.y() + m_dragOffset.y()); - // Later code expects the drag image to be scaled by device's scale factor. - dragImage = scaleDragImage(dragImage, FloatSize(m_page.deviceScaleFactor(), m_page.deviceScaleFactor())); + dragImage = DragImage { platformAdjustDragImageForDeviceScaleFactor(dragImage.get(), m_page.deviceScaleFactor()) }; } - doSystemDrag(dragImage, dragLoc, mouseDraggedPoint, clipboard, src, true); - } else if (state.type == DragSourceActionDHTML) { - if (dragImage) { - ASSERT(m_dragSourceAction & DragSourceActionDHTML); - m_client.willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, clipboard); - doSystemDrag(dragImage, dragLoc, dragOrigin, clipboard, src, false); - } else - startedDrag = false; - } else { - // draggableElement() determined an image or link node was draggable, but it turns out the - // image or link had no URL, so there is nothing to drag. - startedDrag = false; + doSystemDrag(WTFMove(dragImage), dragLoc, mouseDraggedPoint, { }, dataTransfer, src, DragSourceActionLink); + + return true; + } + +#if ENABLE(ATTACHMENT_ELEMENT) + if (!attachmentURL.isEmpty() && (m_dragSourceAction & DragSourceActionAttachment)) { + if (!dataTransfer.pasteboard().hasData()) { + m_draggingAttachmentURL = attachmentURL; + selectElement(element); + declareAndWriteAttachment(dataTransfer, element, attachmentURL); + } + + m_client.willPerformDragSourceAction(DragSourceActionAttachment, dragOrigin, dataTransfer); + + if (!dragImage) { + dragImage = DragImage { dissolveDragImageToFraction(createDragImageForSelection(src), DragImageAlpha) }; + dragLoc = dragLocForSelectionDrag(src); + m_dragOffset = IntPoint(dragOrigin.x() - dragLoc.x(), dragOrigin.y() - dragLoc.y()); + } + doSystemDrag(WTFMove(dragImage), dragLoc, dragOrigin, { }, dataTransfer, src, DragSourceActionAttachment); + return true; + } +#endif + + if (state.type == DragSourceActionDHTML && dragImage) { + ASSERT(m_dragSourceAction & DragSourceActionDHTML); + m_client.willPerformDragSourceAction(DragSourceActionDHTML, dragOrigin, dataTransfer); + doSystemDrag(WTFMove(dragImage), dragLoc, dragOrigin, { }, dataTransfer, src, DragSourceActionDHTML); + return true; } - if (dragImage) - deleteDragImage(dragImage); - return startedDrag; + return false; } -void DragController::doImageDrag(Element& element, const IntPoint& dragOrigin, const IntRect& layoutRect, Clipboard& clipboard, Frame& frame, IntPoint& dragImageOffset) +void DragController::doImageDrag(Element& element, const IntPoint& dragOrigin, const IntRect& layoutRect, DataTransfer& dataTransfer, Frame& frame, IntPoint& dragImageOffset) { IntPoint mouseDownPoint = dragOrigin; - DragImageRef dragImage = nullptr; + DragImage dragImage; IntPoint scaledOrigin; if (!element.renderer()) return; - ImageOrientationDescription orientationDescription(element.renderer()->shouldRespectImageOrientation()); -#if ENABLE(CSS_IMAGE_ORIENTATION) - orientationDescription.setImageOrientationEnum(element.renderer()->style().imageOrientation()); -#endif + ImageOrientationDescription orientationDescription(element.renderer()->shouldRespectImageOrientation(), element.renderer()->style().imageOrientation()); Image* image = getImage(element); if (image && image->size().height() * image->size().width() <= MaxOriginalImageArea - && (dragImage = createDragImageFromImage(image, element.renderer() ? orientationDescription : ImageOrientationDescription()))) { + && (dragImage = DragImage { createDragImageFromImage(image, element.renderer() ? orientationDescription : ImageOrientationDescription()) })) { - dragImage = fitDragImageToMaxSize(dragImage, layoutRect.size(), maxDragImageSize()); - IntSize fittedSize = dragImageSize(dragImage); + dragImage = DragImage { fitDragImageToMaxSize(dragImage.get(), layoutRect.size(), maxDragImageSize()) }; + IntSize fittedSize = dragImageSize(dragImage.get()); - dragImage = scaleDragImage(dragImage, FloatSize(m_page.deviceScaleFactor(), m_page.deviceScaleFactor())); - dragImage = dissolveDragImageToFraction(dragImage, DragImageAlpha); + dragImage = DragImage { platformAdjustDragImageForDeviceScaleFactor(dragImage.get(), m_page.deviceScaleFactor()) }; + dragImage = DragImage { dissolveDragImageToFraction(dragImage.get(), DragImageAlpha) }; // Properly orient the drag image and orient it differently if it's smaller than the original. float scale = fittedSize.width() / (float)layoutRect.width(); float dx = scale * (layoutRect.x() - mouseDownPoint.x()); float originY = layoutRect.y(); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Compensate for accursed flipped coordinates in Cocoa. originY += layoutRect.height(); #endif @@ -886,27 +992,35 @@ void DragController::doImageDrag(Element& element, const IntPoint& dragOrigin, c scaledOrigin = IntPoint((int)(dx + 0.5), (int)(dy + 0.5)); } else { if (CachedImage* cachedImage = getCachedImage(element)) { - dragImage = createDragImageIconForCachedImageFilename(cachedImage->response().suggestedFilename()); + dragImage = DragImage { createDragImageIconForCachedImageFilename(cachedImage->response().suggestedFilename()) }; if (dragImage) - scaledOrigin = IntPoint(DragIconRightInset - dragImageSize(dragImage).width(), DragIconBottomInset); + scaledOrigin = IntPoint(DragIconRightInset - dragImageSize(dragImage.get()).width(), DragIconBottomInset); } } - dragImageOffset = mouseDownPoint + scaledOrigin; - doSystemDrag(dragImage, dragImageOffset, dragOrigin, clipboard, frame, false); + if (!dragImage) + return; - deleteDragImage(dragImage); + dragImageOffset = mouseDownPoint + scaledOrigin; + doSystemDrag(WTFMove(dragImage), dragImageOffset, dragOrigin, element.boundsInRootViewSpace(), dataTransfer, frame, DragSourceActionImage); } -void DragController::doSystemDrag(DragImageRef image, const IntPoint& dragLoc, const IntPoint& eventPos, Clipboard& clipboard, Frame& frame, bool forLink) +void DragController::doSystemDrag(DragImage image, const IntPoint& dragLoc, const IntPoint& eventPos, const IntRect& dragImageBounds, DataTransfer& dataTransfer, Frame& frame, DragSourceAction dragSourceAction) { + FloatPoint dragImageAnchor = { 0.5, 0.5 }; + if (dragSourceAction == DragSourceActionLink) + dragImageAnchor.setY(1); + else if (!dragImageBounds.isEmpty()) { + dragImageAnchor.setX((eventPos.x() - dragImageBounds.x()) / (float)dragImageBounds.width()); + dragImageAnchor.setY((eventPos.y() - dragImageBounds.y()) / (float)dragImageBounds.height()); + } + m_didInitiateDrag = true; m_dragInitiator = frame.document(); // Protect this frame and view, as a load may occur mid drag and attempt to unload this frame Ref frameProtector(m_page.mainFrame()); RefPtr viewProtector = frameProtector->view(); - m_client.startDrag(image, viewProtector->rootViewToContents(frame.view()->contentsToRootView(dragLoc)), - viewProtector->rootViewToContents(frame.view()->contentsToRootView(eventPos)), clipboard, frameProtector.get(), forLink); + m_client.startDrag(WTFMove(image), viewProtector->rootViewToContents(frame.view()->contentsToRootView(dragLoc)), viewProtector->rootViewToContents(frame.view()->contentsToRootView(eventPos)), dragImageAnchor, dataTransfer, frameProtector.get(), dragSourceAction); // DragClient::startDrag can cause our Page to dispear, deallocating |this|. if (!frameProtector->page()) return; @@ -929,6 +1043,6 @@ void DragController::placeDragCaret(const IntPoint& windowPoint) m_page.dragCaretController().setCaretPosition(frame->visiblePositionForPoint(framePoint)); } -} // namespace WebCore - #endif // ENABLE(DRAG_SUPPORT) + +} // namespace WebCore diff --git a/Source/WebCore/page/DragController.h b/Source/WebCore/page/DragController.h index 050899035..d3c209149 100644 --- a/Source/WebCore/page/DragController.h +++ b/Source/WebCore/page/DragController.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,17 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DragController_h -#define DragController_h +#pragma once #include "DragActions.h" #include "DragImage.h" #include "IntPoint.h" +#include "IntRect.h" #include "URL.h" namespace WebCore { - class Clipboard; + class DataTransfer; class Document; class DragClient; class DragData; @@ -45,7 +45,6 @@ namespace WebCore { class Page; class PlatformMouseEvent; - struct DragSession; struct DragState; class DragController { @@ -54,21 +53,27 @@ namespace WebCore { DragController(Page&, DragClient&); ~DragController(); - static PassOwnPtr create(Page*, DragClient*); + static std::unique_ptr create(Page&, DragClient&); DragClient& client() const { return m_client; } - DragSession dragEntered(DragData&); - void dragExited(DragData&); - DragSession dragUpdated(DragData&); - bool performDrag(DragData&); - + WEBCORE_EXPORT DragOperation dragEntered(const DragData&); + WEBCORE_EXPORT void dragExited(const DragData&); + WEBCORE_EXPORT DragOperation dragUpdated(const DragData&); + WEBCORE_EXPORT bool performDragOperation(const DragData&); + + bool mouseIsOverFileInput() const { return m_fileInputElementUnderMouse; } + unsigned numberOfItemsToBeAccepted() const { return m_numberOfItemsToBeAccepted; } + // FIXME: It should be possible to remove a number of these accessors once all // drag logic is in WebCore. void setDidInitiateDrag(bool initiated) { m_didInitiateDrag = initiated; } bool didInitiateDrag() const { return m_didInitiateDrag; } DragOperation sourceDragOperation() const { return m_sourceDragOperation; } const URL& draggingImageURL() const { return m_draggingImageURL; } +#if ENABLE(ATTACHMENT_ELEMENT) + const URL& draggingAttachmentURL() const { return m_draggingAttachmentURL; } +#endif void setDragOffset(const IntPoint& offset) { m_dragOffset = offset; } const IntPoint& dragOffset() const { return m_dragOffset; } DragSourceAction dragSourceAction() const { return m_dragSourceAction; } @@ -78,9 +83,9 @@ namespace WebCore { DragSourceAction delegateDragSourceAction(const IntPoint& rootViewPoint); Element* draggableElement(const Frame*, Element* start, const IntPoint&, DragState&) const; - void dragEnded(); + WEBCORE_EXPORT void dragEnded(); - void placeDragCaret(const IntPoint&); + WEBCORE_EXPORT void placeDragCaret(const IntPoint&); bool startDrag(Frame& src, const DragState&, DragOperation srcOp, const PlatformMouseEvent& dragEvent, const IntPoint& dragOrigin); static const IntSize& maxDragImageSize(); @@ -92,31 +97,34 @@ namespace WebCore { static const float DragImageAlpha; private: - bool dispatchTextInputEventFor(Frame*, DragData&); - bool canProcessDrag(DragData&); - bool concludeEditDrag(DragData&); - DragSession dragEnteredOrUpdated(DragData&); - DragOperation operationForLoad(DragData&); - bool tryDocumentDrag(DragData&, DragDestinationAction, DragSession&); - bool tryDHTMLDrag(DragData&, DragOperation&); - DragOperation dragOperation(DragData&); - void cancelDrag(); - bool dragIsMove(FrameSelection&, DragData&); - bool isCopyKeyDown(DragData&); + bool dispatchTextInputEventFor(Frame*, const DragData&); + bool canProcessDrag(const DragData&); + bool concludeEditDrag(const DragData&); + DragOperation dragEnteredOrUpdated(const DragData&); + DragOperation operationForLoad(const DragData&); + bool tryDocumentDrag(const DragData&, DragDestinationAction, DragOperation&); + bool tryDHTMLDrag(const DragData&, DragOperation&); + DragOperation dragOperation(const DragData&); + void clearDragCaret(); + bool dragIsMove(FrameSelection&, const DragData&); + bool isCopyKeyDown(const DragData&); void mouseMovedIntoDocument(Document*); - void doImageDrag(Element&, const IntPoint&, const IntRect&, Clipboard&, Frame&, IntPoint&); - void doSystemDrag(DragImageRef, const IntPoint&, const IntPoint&, Clipboard&, Frame&, bool forLink); + void doImageDrag(Element&, const IntPoint&, const IntRect&, DataTransfer&, Frame&, IntPoint&); + void doSystemDrag(DragImage, const IntPoint&, const IntPoint&, const IntRect& dragImageBounds, DataTransfer&, Frame&, DragSourceAction); void cleanupAfterSystemDrag(); - void declareAndWriteDragImage(Clipboard&, Element&, const URL&, const String& label); - + void declareAndWriteDragImage(DataTransfer&, Element&, const URL&, const String& label); +#if ENABLE(ATTACHMENT_ELEMENT) + void declareAndWriteAttachment(DataTransfer&, Element&, const URL&); +#endif Page& m_page; DragClient& m_client; RefPtr m_documentUnderMouse; // The document the mouse was last dragged over. RefPtr m_dragInitiator; // The Document (if any) that initiated the drag. RefPtr m_fileInputElementUnderMouse; + unsigned m_numberOfItemsToBeAccepted; bool m_documentIsHandlingDrag; DragDestinationAction m_dragDestinationAction; @@ -125,8 +133,11 @@ namespace WebCore { DragOperation m_sourceDragOperation; // Set in startDrag when a drag starts from a mouse down within WebKit IntPoint m_dragOffset; URL m_draggingImageURL; +#if ENABLE(ATTACHMENT_ELEMENT) + URL m_draggingAttachmentURL; +#endif }; -} + WEBCORE_EXPORT bool isDraggableLink(const Element&); -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/DragSession.h b/Source/WebCore/page/DragSession.h deleted file mode 100644 index 0e9375196..000000000 --- a/Source/WebCore/page/DragSession.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2011 Apple 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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. - */ - -#ifndef DragSession_h -#define DragSession_h - -#include "DragActions.h" - -namespace WebCore { - -struct DragSession { - DragOperation operation; - bool mouseIsOverFileInput; - unsigned numberOfItemsToBeAccepted; - - DragSession() - : operation(DragOperationNone) - , mouseIsOverFileInput(false) - , numberOfItemsToBeAccepted(0) - { - } -}; - -} - -#endif diff --git a/Source/WebCore/page/DragState.h b/Source/WebCore/page/DragState.h index 70849fdc5..6d1625900 100644 --- a/Source/WebCore/page/DragState.h +++ b/Source/WebCore/page/DragState.h @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,10 +24,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DragState_h -#define DragState_h +#pragma once -#include "Clipboard.h" +#include "DataTransfer.h" #include "DragActions.h" #include "Element.h" @@ -37,9 +36,7 @@ struct DragState { RefPtr source; // Element that may be a drag source, for the current mouse gesture. bool shouldDispatchEvents; DragSourceAction type; - RefPtr clipboard; // Used on only the source side of dragging. + RefPtr dataTransfer; // Used on only the source side of dragging. }; } // namespace WebCore - -#endif // DragState_h diff --git a/Source/WebCore/page/EditorClient.h b/Source/WebCore/page/EditorClient.h index 8623994e4..f4132109f 100644 --- a/Source/WebCore/page/EditorClient.h +++ b/Source/WebCore/page/EditorClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -24,19 +24,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EditorClient_h -#define EditorClient_h +#pragma once #include "EditorInsertAction.h" -#include "FloatRect.h" #include "TextAffinity.h" #include "TextChecking.h" #include "UndoStep.h" #include #include -#if PLATFORM(MAC) -OBJC_CLASS NSAttributedString; +#if PLATFORM(COCOA) OBJC_CLASS NSString; OBJC_CLASS NSURL; #endif @@ -55,6 +52,7 @@ class Element; class Frame; class HTMLElement; class KeyboardEvent; +class LayoutRect; class Node; class Range; class SharedBuffer; @@ -63,12 +61,12 @@ class TextCheckerClient; class VisibleSelection; class VisiblePosition; +struct GapRects; struct GrammarDetail; class EditorClient { public: virtual ~EditorClient() { } - virtual void pageDestroyed() = 0; virtual bool shouldDeleteRange(Range*) = 0; virtual bool smartInsertDeleteEnabled() = 0; @@ -86,18 +84,28 @@ public: virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting) = 0; virtual bool shouldApplyStyle(StyleProperties*, Range*) = 0; + virtual void didApplyStyle() = 0; virtual bool shouldMoveRangeAfterDelete(Range*, Range*) = 0; virtual void didBeginEditing() = 0; virtual void respondToChangedContents() = 0; virtual void respondToChangedSelection(Frame*) = 0; + virtual void didChangeSelectionAndUpdateLayout() = 0; + virtual void updateEditorStateAfterLayoutIfEditabilityChanged() = 0; virtual void didEndEditing() = 0; virtual void willWriteSelectionToPasteboard(Range*) = 0; virtual void didWriteSelectionToPasteboard() = 0; virtual void getClientPasteboardDataForRange(Range*, Vector& pasteboardTypes, Vector>& pasteboardData) = 0; - - virtual void registerUndoStep(PassRefPtr) = 0; - virtual void registerRedoStep(PassRefPtr) = 0; + virtual void requestCandidatesForSelection(const VisibleSelection&) { } + virtual void handleAcceptedCandidateWithSoftSpaces(TextCheckingResult) { } + + // Notify an input method that a composition was voluntarily discarded by WebCore, so that it could clean up too. + // This function is not called when a composition is closed per a request from an input method. + virtual void discardedComposition(Frame*) = 0; + virtual void canceledComposition() = 0; + + virtual void registerUndoStep(UndoStep&) = 0; + virtual void registerRedoStep(UndoStep&) = 0; virtual void clearUndoRedoOperations() = 0; virtual bool canCopyCut(Frame*, bool defaultValue) const = 0; @@ -117,10 +125,9 @@ public: virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) = 0; virtual void textWillBeDeletedInTextField(Element*) = 0; virtual void textDidChangeInTextArea(Element*) = 0; + virtual void overflowScrollPositionChanged() = 0; #if PLATFORM(IOS) - virtual void suppressSelectionNotifications() = 0; - virtual void restoreSelectionNotifications() = 0; virtual void startDelayingAndCoalescingContentChangeNotifications() = 0; virtual void stopDelayingAndCoalescingContentChangeNotifications() = 0; virtual void writeDataToPasteboard(NSDictionary*) = 0; @@ -128,17 +135,16 @@ public: virtual NSArray* readDataFromPasteboard(NSString* type, int index) = 0; virtual bool hasRichlyEditableSelection() = 0; virtual int getPasteboardItemsCount() = 0; - virtual DocumentFragment* documentFragmentFromDelegate(int index) = 0; + virtual RefPtr documentFragmentFromDelegate(int index) = 0; virtual bool performsTwoStepPaste(DocumentFragment*) = 0; virtual int pasteboardChangeCount() = 0; #endif -#if PLATFORM(MAC) - virtual NSString* userVisibleString(NSURL*) = 0; - virtual DocumentFragment* documentFragmentFromAttributedString(NSAttributedString*, Vector< RefPtr>&) = 0; +#if PLATFORM(COCOA) + virtual NSString *userVisibleString(NSURL *) = 0; virtual void setInsertionPasteboard(const String& pasteboardName) = 0; - virtual NSURL* canonicalizeURL(NSURL*) = 0; - virtual NSURL* canonicalizeURLString(NSString*) = 0; + virtual NSURL *canonicalizeURL(NSURL *) = 0; + virtual NSURL *canonicalizeURLString(NSString *) = 0; #endif #if USE(APPKIT) @@ -163,10 +169,6 @@ public: virtual void toggleAutomaticSpellingCorrection() = 0; #endif -#if ENABLE(DELETION_UI) - virtual bool shouldShowDeleteInterface(HTMLElement*) = 0; -#endif - #if PLATFORM(GTK) virtual bool shouldShowUnicodeMenu() = 0; #endif @@ -186,5 +188,3 @@ public: }; } - -#endif // EditorClient_h diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp index bf0f7631c..9d48975cb 100644 --- a/Source/WebCore/page/EventHandler.cpp +++ b/Source/WebCore/page/EventHandler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2015 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) * @@ -12,10 +12,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -28,22 +28,17 @@ #include "config.h" #include "EventHandler.h" -#include "AXObjectCache.h" #include "AutoscrollController.h" #include "BackForwardController.h" #include "CachedImage.h" #include "Chrome.h" #include "ChromeClient.h" -#include "Cursor.h" #include "CursorList.h" -#include "Document.h" -#include "DocumentEventQueue.h" #include "DragController.h" #include "DragState.h" #include "Editor.h" #include "EditorClient.h" #include "EventNames.h" -#include "ExceptionCodePlaceholder.h" #include "FileList.h" #include "FloatPoint.h" #include "FloatRect.h" @@ -52,9 +47,10 @@ #include "FrameSelection.h" #include "FrameTree.h" #include "FrameView.h" -#include "htmlediting.h" -#include "HTMLFrameElementBase.h" +#include "HTMLFrameElement.h" #include "HTMLFrameSetElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLIFrameElement.h" #include "HTMLInputElement.h" #include "HTMLNames.h" #include "HitTestRequest.h" @@ -62,21 +58,27 @@ #include "Image.h" #include "InspectorInstrumentation.h" #include "KeyboardEvent.h" +#include "Logging.h" #include "MainFrame.h" #include "MouseEvent.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" +#include "PageOverlayController.h" #include "PlatformEvent.h" #include "PlatformKeyboardEvent.h" #include "PlatformWheelEvent.h" #include "PluginDocument.h" #include "RenderFrameSet.h" #include "RenderLayer.h" +#include "RenderListBox.h" +#include "RenderNamedFlowThread.h" #include "RenderTextControlSingleLine.h" #include "RenderView.h" #include "RenderWidget.h" #include "RuntimeApplicationChecks.h" -#include "ScrollAnimator.h" +#include "SVGDocument.h" +#include "SVGNames.h" +#include "ScrollLatchingState.h" #include "Scrollbar.h" #include "Settings.h" #include "ShadowRoot.h" @@ -86,33 +88,36 @@ #include "TextIterator.h" #include "UserGestureIndicator.h" #include "UserTypingGestureIndicator.h" +#include "ValidationMessageClient.h" +#include "VisibleUnits.h" #include "WheelEvent.h" +#include "WheelEventDeltaFilter.h" #include "WindowsKeyboardCodes.h" +#include "htmlediting.h" #include #include +#include #include -#include -#include - -#if ENABLE(SVG) -#include "SVGDocument.h" -#include "SVGElementInstance.h" -#include "SVGNames.h" -#include "SVGUseElement.h" -#endif -#if ENABLE(TOUCH_EVENTS) #if ENABLE(IOS_TOUCH_EVENTS) #include "PlatformTouchEventIOS.h" -#else -#include "PlatformTouchEvent.h" #endif + +#if ENABLE(TOUCH_EVENTS) #include "TouchEvent.h" #include "TouchList.h" #endif -#if ENABLE(CSS_IMAGE_SET) -#include "StyleCachedImageSet.h" +#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) +#include "PlatformTouchEvent.h" +#endif + +#if ENABLE(MAC_GESTURE_EVENTS) +#include "PlatformGestureEventMac.h" +#endif + +#if ENABLE(POINTER_LOCK) +#include "PointerLockController.h" #endif namespace WebCore { @@ -127,9 +132,12 @@ const int LinkDragHysteresis = 40; const int ImageDragHysteresis = 5; const int TextDragHysteresis = 3; const int GeneralDragHysteresis = 3; +#if PLATFORM(COCOA) +const double EventHandler::TextDragDelay = 0.15; +#endif #endif // ENABLE(DRAG_SUPPORT) -#if ENABLE(IOS_GESTURE_EVENTS) +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) const float GestureUnknown = 0; #endif @@ -142,16 +150,16 @@ const unsigned InvalidTouchIdentifier = 0; // IE sends VK_PROCESSKEY which has value 229; const int CompositionEventKeyCode = 229; -#if ENABLE(SVG) using namespace SVGNames; -#endif +#if !ENABLE(IOS_TOUCH_EVENTS) // The amount of time to wait before sending a fake mouse event, triggered // during a scroll. The short interval is used if the content responds to the mouse events // in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used. const double fakeMouseMoveDurationThreshold = 0.01; const double fakeMouseMoveShortInterval = 0.1; const double fakeMouseMoveLongInterval = 0.25; +#endif #if ENABLE(CURSOR_SUPPORT) // The amount of time to wait for a cursor update on style and layout changes @@ -168,21 +176,6 @@ const int maximumCursorSize = 128; const double minimumCursorScale = 0.001; #endif -enum NoCursorChangeType { NoCursorChange }; - -class OptionalCursor { -public: - OptionalCursor(NoCursorChangeType) : m_isCursorChange(false) { } - OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(cursor) { } - - bool isCursorChange() const { return m_isCursorChange; } - const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; } - -private: - bool m_isCursorChange; - Cursor m_cursor; -}; - class MaximumDurationTracker { public: explicit MaximumDurationTracker(double *maxDuration) @@ -282,16 +275,63 @@ static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned del } } -static inline bool scrollNode(float delta, ScrollGranularity granularity, ScrollDirection positiveDirection, ScrollDirection negativeDirection, Node* node, Element** stopElement, const IntPoint& wheelEventAbsolutePoint) +static inline bool didScrollInScrollableArea(ScrollableArea* scrollableArea, WheelEvent& wheelEvent) { - if (!delta) - return false; - if (!node->renderer()) + ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode()); + bool didHandleWheelEvent = false; + if (float absoluteDelta = std::abs(wheelEvent.deltaX())) + didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta); + + if (float absoluteDelta = std::abs(wheelEvent.deltaY())) + didHandleWheelEvent |= scrollableArea->scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta); + + return didHandleWheelEvent; +} + +static inline bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, WheelEvent& wheelEvent, Element** stopElement, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity) +{ + bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY(); +#if PLATFORM(MAC) + shouldHandleEvent |= wheelEvent.phase() == PlatformWheelEventPhaseEnded; +#if ENABLE(CSS_SCROLL_SNAP) + shouldHandleEvent |= wheelEvent.momentumPhase() == PlatformWheelEventPhaseEnded; +#endif +#endif + if (!startNode->renderer() || !shouldHandleEvent) return false; - RenderBox* enclosingBox = node->renderer()->enclosingBox(); - float absDelta = delta > 0 ? delta : -delta; - return enclosingBox->scroll(delta < 0 ? negativeDirection : positiveDirection, granularity, absDelta, stopElement, enclosingBox, wheelEventAbsolutePoint); + RenderBox& initialEnclosingBox = startNode->renderer()->enclosingBox(); + if (initialEnclosingBox.isListBox()) + return didScrollInScrollableArea(static_cast(&initialEnclosingBox), wheelEvent); + + RenderBox* currentEnclosingBox = &initialEnclosingBox; + while (currentEnclosingBox) { + if (RenderLayer* boxLayer = currentEnclosingBox->layer()) { + const PlatformWheelEvent* platformEvent = wheelEvent.wheelEvent(); + bool scrollingWasHandled; + if (platformEvent != nullptr) { + auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity); + scrollingWasHandled = boxLayer->handleWheelEvent(copiedEvent); + } else + scrollingWasHandled = didScrollInScrollableArea(boxLayer, wheelEvent); + + if (scrollingWasHandled) { + if (stopElement) + *stopElement = currentEnclosingBox->element(); + return true; + } + } + + if (stopElement && *stopElement && *stopElement == currentEnclosingBox->element()) + return true; + + currentEnclosingBox = currentEnclosingBox->containingBlock(); + if (currentEnclosingBox && currentEnclosingBox->isRenderNamedFlowThread()) + currentEnclosingBox = RenderNamedFlowThread::fragmentFromRenderBoxAsRenderBlock(currentEnclosingBox, roundedIntPoint(wheelEvent.absoluteLocation()), initialEnclosingBox); + if (!currentEnclosingBox || currentEnclosingBox->isRenderView()) + return false; + } + return false; } #if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)) @@ -304,7 +344,7 @@ static inline bool shouldGesturesTriggerActive() } #endif -#if !PLATFORM(MAC) +#if !PLATFORM(COCOA) inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&) { @@ -322,79 +362,42 @@ inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTes EventHandler::EventHandler(Frame& frame) : m_frame(frame) - , m_mousePressed(false) - , m_capturesDragging(false) - , m_mouseDownMayStartSelect(false) -#if ENABLE(DRAG_SUPPORT) - , m_mouseDownMayStartDrag(false) - , m_dragMayStartSelectionInstead(false) -#endif - , m_mouseDownWasSingleClickInSelection(false) - , m_selectionInitiationState(HaveNotStartedSelection) - , m_hoverTimer(this, &EventHandler::hoverTimerFired) + , m_hoverTimer(*this, &EventHandler::hoverTimerFired) #if ENABLE(CURSOR_SUPPORT) - , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired) -#endif - , m_autoscrollController(adoptPtr(new AutoscrollController)) - , m_mouseDownMayStartAutoscroll(false) - , m_mouseDownWasInSubframe(false) - , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired) -#if ENABLE(SVG) - , m_svgPan(false) -#endif - , m_resizeLayer(0) - , m_eventHandlerWillResetCapturingMouseEventsElement(nullptr) - , m_clickCount(0) -#if ENABLE(IOS_GESTURE_EVENTS) - , m_gestureInitialDiameter(GestureUnknown) - , m_gestureLastDiameter(GestureUnknown) - , m_gestureInitialRotation(GestureUnknown) - , m_gestureLastRotation(GestureUnknown) -#endif -#if ENABLE(IOS_TOUCH_EVENTS) - , m_firstTouchID(InvalidTouchIdentifier) + , m_cursorUpdateTimer(*this, &EventHandler::cursorUpdateTimerFired) #endif - , m_mousePositionIsUnknown(true) - , m_mouseDownTimestamp(0) - , m_inTrackingScrollGesturePhase(false) - , m_widgetIsLatched(false) #if PLATFORM(MAC) - , m_mouseDownView(nil) - , m_sendingEventToSubview(false) -#if !PLATFORM(IOS) - , m_activationEventNumber(-1) -#endif // !PLATFORM(IOS) + , m_pendingMomentumWheelEventsTimer(*this, &EventHandler::clearLatchedState) +#endif + , m_autoscrollController(std::make_unique()) +#if !ENABLE(IOS_TOUCH_EVENTS) + , m_fakeMouseMoveEventTimer(*this, &EventHandler::fakeMouseMoveEventTimerFired) #endif -#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) - , m_originatingTouchPointTargetKey(0) - , m_touchPressed(false) -#endif - , m_maxMouseMovedDuration(0) - , m_baseEventType(PlatformEvent::NoType) - , m_didStartDrag(false) - , m_didLongPressInvokeContextMenu(false) - , m_isHandlingWheelEvent(false) #if ENABLE(CURSOR_VISIBILITY) - , m_autoHideCursorTimer(this, &EventHandler::autoHideCursorTimerFired) + , m_autoHideCursorTimer(*this, &EventHandler::autoHideCursorTimerFired) #endif { } EventHandler::~EventHandler() { +#if !ENABLE(IOS_TOUCH_EVENTS) ASSERT(!m_fakeMouseMoveEventTimer.isActive()); +#endif #if ENABLE(CURSOR_VISIBILITY) ASSERT(!m_autoHideCursorTimer.isActive()); #endif } #if ENABLE(DRAG_SUPPORT) + DragState& EventHandler::dragState() { - DEFINE_STATIC_LOCAL(DragState, state, ()); + static NeverDestroyed state; return state; } -#endif // ENABLE(DRAG_SUPPORT) + +#endif void EventHandler::clear() { @@ -402,62 +405,59 @@ void EventHandler::clear() #if ENABLE(CURSOR_SUPPORT) m_cursorUpdateTimer.stop(); #endif +#if !ENABLE(IOS_TOUCH_EVENTS) m_fakeMouseMoveEventTimer.stop(); +#endif #if ENABLE(CURSOR_VISIBILITY) cancelAutoHideCursorTimer(); #endif - m_resizeLayer = 0; + m_resizeLayer = nullptr; m_elementUnderMouse = nullptr; m_lastElementUnderMouse = nullptr; -#if ENABLE(SVG) - m_instanceUnderMouse = 0; - m_lastInstanceUnderMouse = 0; -#endif - m_lastMouseMoveEventSubframe = 0; + m_lastMouseMoveEventSubframe = nullptr; m_lastScrollbarUnderMouse = nullptr; m_clickCount = 0; - m_clickNode = 0; + m_clickNode = nullptr; #if ENABLE(IOS_GESTURE_EVENTS) m_gestureInitialDiameter = GestureUnknown; - m_gestureLastDiameter = GestureUnknown; m_gestureInitialRotation = GestureUnknown; +#endif +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) + m_gestureLastDiameter = GestureUnknown; m_gestureLastRotation = GestureUnknown; m_gestureTargets.clear(); #endif #if ENABLE(IOS_TOUCH_EVENTS) m_touches.clear(); m_firstTouchID = InvalidTouchIdentifier; - m_touchEventTargetSubframe = 0; + m_touchEventTargetSubframe = nullptr; #endif - m_frameSetBeingResized = 0; + m_frameSetBeingResized = nullptr; #if ENABLE(DRAG_SUPPORT) - m_dragTarget = 0; + m_dragTarget = nullptr; m_shouldOnlyFireDragOverEvent = false; #endif m_mousePositionIsUnknown = true; m_lastKnownMousePosition = IntPoint(); m_lastKnownMouseGlobalPosition = IntPoint(); - m_mousePressNode = 0; + m_mousePressNode = nullptr; m_mousePressed = false; m_capturesDragging = false; m_capturingMouseEventsElement = nullptr; - m_latchedWheelEventElement = nullptr; - m_previousWheelScrolledElement = nullptr; + clearLatchedState(); #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) m_originatingTouchPointTargets.clear(); - m_originatingTouchPointDocument.clear(); + m_originatingTouchPointDocument = nullptr; m_originatingTouchPointTargetKey = 0; #endif m_maxMouseMovedDuration = 0; - m_baseEventType = PlatformEvent::NoType; m_didStartDrag = false; - m_didLongPressInvokeContextMenu = false; } -void EventHandler::nodeWillBeRemoved(Node* nodeToBeRemoved) +void EventHandler::nodeWillBeRemoved(Node& nodeToBeRemoved) { - if (nodeToBeRemoved->contains(m_clickNode.get())) - m_clickNode = 0; + if (nodeToBeRemoved.contains(m_clickNode.get())) + m_clickNode = nullptr; } static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection) @@ -474,22 +474,30 @@ static inline bool dispatchSelectStart(Node* node) return node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)); } -static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection) +static Node* nodeToSelectOnMouseDownForNode(Node& targetNode) { #if ENABLE(USERSELECT_ALL) - Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode); - if (!rootUserSelectAll) + if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(&targetNode)) + return rootUserSelectAll; +#endif + + if (targetNode.shouldSelectOnMouseDown()) + return &targetNode; + + return nullptr; +} + +static VisibleSelection expandSelectionToRespectSelectOnMouseDown(Node& targetNode, const VisibleSelection& selection) +{ + Node* nodeToSelect = nodeToSelectOnMouseDownForNode(targetNode); + if (!nodeToSelect) return selection; VisibleSelection newSelection(selection); - newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary)); - newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary)); + newSelection.setBase(positionBeforeNode(nodeToSelect).upstream(CanCrossEditingBoundary)); + newSelection.setExtent(positionAfterNode(nodeToSelect).downstream(CanCrossEditingBoundary)); return newSelection; -#else - UNUSED_PARAM(targetNode); - return selection; -#endif } bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity) @@ -507,7 +515,7 @@ bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targe m_selectionInitiationState = PlacedCaret; } - m_frame.selection().setNonDirectionalSelectionIfNeeded(selection, granularity); + m_frame.selection().setSelectionByMouseIfDifferent(selection, granularity); return true; } @@ -518,7 +526,7 @@ void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul VisibleSelection newSelection; if (targetNode && targetNode->renderer()) { - VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint())); + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); if (pos.isNotNull()) { newSelection = VisibleSelection(pos); newSelection.expandUsingGranularity(WordGranularity); @@ -527,33 +535,73 @@ void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& resul if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) newSelection.appendTrailingWhitespace(); - updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); } } +static AppendTrailingWhitespace shouldAppendTrailingWhitespace(const MouseEventWithHitTestResults& result, const Frame& frame) +{ + return (result.event().clickCount() == 2 && frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace; +} + void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result) { - if (m_mouseDownMayStartSelect) { - selectClosestWordFromHitTestResult(result.hitTestResult(), - (result.event().clickCount() == 2 && m_frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace); - } + if (m_mouseDownMayStartSelect) + selectClosestWordFromHitTestResult(result.hitTestResult(), shouldAppendTrailingWhitespace(result, m_frame)); } -void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result) +#if !PLATFORM(MAC) +VisibleSelection EventHandler::selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&) +{ + return VisibleSelection(); +} +#endif + +void EventHandler::selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults& mouseEvent) +{ + Node* targetNode = mouseEvent.targetNode(); + const HitTestResult& result = mouseEvent.hitTestResult(); + VisibleSelection newSelection; + bool appendTrailingWhitespace = shouldAppendTrailingWhitespace(mouseEvent, m_frame); + + if (targetNode && targetNode->renderer()) { + newSelection = selectClosestWordFromHitTestResultBasedOnLookup(result); + if (newSelection.isNone()) { + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); + if (pos.isNotNull()) { + newSelection = VisibleSelection(pos); + newSelection.expandUsingGranularity(WordGranularity); + } + } + + if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange()) + newSelection.appendTrailingWhitespace(); + + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); + } +} + +void EventHandler::selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result) { - if (!result.hitTestResult().isLiveLink()) - return selectClosestWordFromMouseEvent(result); + Element* urlElement = result.hitTestResult().URLElement(); + if (!urlElement || !isDraggableLink(*urlElement)) { + if (Node* targetNode = result.targetNode()) { + if (isEditableNode(*targetNode)) + return selectClosestWordFromMouseEvent(result); + } + + return selectClosestContextualWordFromMouseEvent(result); + } Node* targetNode = result.targetNode(); if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) { VisibleSelection newSelection; - Element* URLElement = result.hitTestResult().URLElement(); - VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint())); - if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(URLElement)) - newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement); + VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint(), nullptr)); + if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(*urlElement)) + newSelection = VisibleSelection::selectionFromContentsOfNode(urlElement); - updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity); + updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), WordGranularity); } } @@ -585,13 +633,13 @@ bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR return false; VisibleSelection newSelection; - VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint())); + VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); if (pos.isNotNull()) { newSelection = VisibleSelection(pos); newSelection.expandUsingGranularity(ParagraphGranularity); } - return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), ParagraphGranularity); + return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectSelectOnMouseDown(*targetNode, newSelection), ParagraphGranularity); } static int textDistance(const Position& start, const Position& end) @@ -602,6 +650,8 @@ static int textDistance(const Position& start, const Position& end) bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event) { + Ref protectedFrame(m_frame); + m_frame.document()->updateLayoutIgnorePendingStylesheets(); Node* targetNode = event.targetNode(); if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect)) @@ -620,7 +670,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR } } - VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint())); + VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint(), nullptr)); if (visiblePos.isNull()) visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM); Position pos = visiblePos.deepEquivalent(); @@ -629,7 +679,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR TextGranularity granularity = CharacterGranularity; if (extendSelection && newSelection.isCaretOrRange()) { - VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(targetNode, VisibleSelection(pos)); + VisibleSelection selectionInUserSelectAll = expandSelectionToRespectSelectOnMouseDown(*targetNode, VisibleSelection(pos)); if (selectionInUserSelectAll.isRange()) { if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0) pos = selectionInUserSelectAll.start(); @@ -656,7 +706,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR newSelection.expandUsingGranularity(m_frame.selection().granularity()); } } else - newSelection = expandSelectionToRespectUserSelectAll(targetNode, visiblePos); + newSelection = expandSelectionToRespectSelectOnMouseDown(*targetNode, visiblePos); bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity); @@ -677,12 +727,16 @@ static inline bool canMouseDownStartSelect(Node* node) bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event) { + Ref protectedFrame(m_frame); + #if ENABLE(DRAG_SUPPORT) // Reset drag state. - dragState().source = 0; + dragState().source = nullptr; #endif +#if !ENABLE(IOS_TOUCH_EVENTS) cancelFakeMouseMoveEvent(); +#endif m_frame.document()->updateLayoutIgnorePendingStylesheets(); @@ -699,26 +753,31 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve #if ENABLE(DRAG_SUPPORT) // Careful that the drag starting logic stays in sync with eventMayStartDrag() - m_mouseDownMayStartDrag = singleClick; + // FIXME: eventMayStartDrag() does not check for shift key press, link or image event targets. + // Bug: https://bugs.webkit.org/show_bug.cgi?id=155390 + + // Single mouse down on links or images can always trigger drag-n-drop. + bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image(); + m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage); #endif m_mouseDownWasSingleClickInSelection = false; m_mouseDown = event.event(); + if (m_immediateActionStage != ImmediateActionStage::PerformedHitTest) + m_immediateActionStage = ImmediateActionStage::None; + if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event)) return true; -#if ENABLE(SVG) - if (m_frame.document()->isSVGDocument() - && toSVGDocument(m_frame.document())->zoomAndPanEnabled()) { + if (is(*m_frame.document()) && downcast(*m_frame.document()).zoomAndPanEnabled()) { if (event.event().shiftKey() && singleClick) { m_svgPan = true; - toSVGDocument(m_frame.document())->startPan(m_frame.view()->windowToContents(event.event().position())); + downcast(*m_frame.document()).startPan(m_frame.view()->windowToContents(event.event().position())); return true; } } -#endif // We don't do this at the start of mouse down handling, // because we don't want to do it until we know we didn't hit a widget. @@ -726,14 +785,16 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve focusDocumentView(); m_mousePressNode = event.targetNode(); + m_frame.document()->setFocusNavigationStartingNode(event.targetNode()); + #if ENABLE(DRAG_SUPPORT) - m_dragStartPos = event.event().position(); + m_dragStartPosition = event.event().position(); #endif - bool swallowEvent = false; m_mousePressed = true; m_selectionInitiationState = HaveNotStartedSelection; + bool swallowEvent = false; if (event.event().clickCount() == 2) swallowEvent = handleMousePressEventDoubleClick(event); else if (event.event().clickCount() >= 3) @@ -748,12 +809,14 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve } #if ENABLE(DRAG_SUPPORT) -bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event) +bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis) { if (!m_mousePressed) return false; - if (handleDrag(event, ShouldCheckDragHysteresis)) + Ref protectedFrame(m_frame); + + if (handleDrag(event, checkDragHysteresis)) return true; Node* targetNode = event.targetNode(); @@ -771,7 +834,7 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e return false; } -#if PLATFORM(MAC) // FIXME: Why does this assertion fire on other platforms? +#if PLATFORM(COCOA) // FIXME: Why does this assertion fire on other platforms? ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll); #endif @@ -783,9 +846,8 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e } if (m_selectionInitiationState != ExtendedSelection) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); HitTestResult result(m_mouseDownPos); - m_frame.document()->renderView()->hitTest(request, result); + m_frame.document()->renderView()->hitTest(HitTestRequest(), result); updateSelectionForMouseDrag(result); } @@ -798,13 +860,13 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const // This is a pre-flight check of whether the event might lead to a drag being started. Be careful // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag // in handleMousePressEvent - - if (!m_frame.contentRenderer() || !m_frame.contentRenderer()->hasLayer()) + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return false; if (event.button() != LeftButton || event.clickCount() != 1) return false; - + FrameView* view = m_frame.view(); if (!view) return false; @@ -813,12 +875,15 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const if (!page) return false; + Ref protectedFrame(m_frame); + updateDragSourceActionsAllowed(); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(view->windowToContents(event.position())); - m_frame.contentRenderer()->hitTest(request, result); + renderView->hitTest(request, result); DragState state; - return result.innerElement() && page->dragController().draggableElement(&m_frame, result.innerElement(), result.roundedPointInInnerNodeFrame(), state); + Element* targetElement = result.targetElement(); + return targetElement && page->dragController().draggableElement(&m_frame, targetElement, result.roundedPointInInnerNodeFrame(), state); } void EventHandler::updateSelectionForMouseDrag() @@ -826,19 +891,19 @@ void EventHandler::updateSelectionForMouseDrag() FrameView* view = m_frame.view(); if (!view) return; - RenderView* renderer = m_frame.contentRenderer(); - if (!renderer) + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return; - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); - renderer->hitTest(request, result); + renderView->hitTest(request, result); updateSelectionForMouseDrag(result); } static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode) { - LayoutPoint selectionEndPoint = localPoint; + FloatPoint selectionEndPoint = localPoint; Element* editableElement = selection.rootEditableElement(); if (!targetNode->renderer()) @@ -849,11 +914,11 @@ static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSel return VisiblePosition(); FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint)); - selectionEndPoint = roundedLayoutPoint(editableElement->renderer()->absoluteToLocal(absolutePoint)); + selectionEndPoint = editableElement->renderer()->absoluteToLocal(absolutePoint); targetNode = editableElement; } - return targetNode->renderer()->positionForPoint(selectionEndPoint); + return targetNode->renderer()->positionForPoint(LayoutPoint(selectionEndPoint), nullptr); } void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult) @@ -875,7 +940,6 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul // done in handleMousePressEvent, but not if the mouse press was on an existing selection. VisibleSelection newSelection = m_frame.selection().selection(); -#if ENABLE(SVG) // Special case to limit selection to the containing block for SVG text. // FIXME: Isn't there a better non-SVG-specific way to do this? if (Node* selectionBaseNode = newSelection.base().deprecatedNode()) @@ -883,7 +947,6 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul if (selectionBaseRenderer->isSVGText()) if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock()) return; -#endif if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target)) return; @@ -901,11 +964,11 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary)); } else { // Reset base for user select all when base is inside user-select-all area and extent < base. - if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0) + if (rootUserSelectAllForMousePressNode && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0) newSelection.setBase(positionAfterNode(rootUserSelectAllForMousePressNode).downstream(CanCrossEditingBoundary)); Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNode(target); - if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint()), m_mousePressNode->renderer()->positionForPoint(m_dragStartPos)) < 0) + if (rootUserSelectAllForTarget && m_mousePressNode->renderer() && comparePositions(target->renderer()->positionForPoint(hitTestResult.localPoint(), nullptr), m_mousePressNode->renderer()->positionForPoint(m_dragStartPosition, nullptr)) < 0) newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTarget).upstream(CanCrossEditingBoundary)); else if (rootUserSelectAllForTarget && m_mousePressNode->renderer()) newSelection.setExtent(positionAfterNode(rootUserSelectAllForTarget).downstream(CanCrossEditingBoundary)); @@ -919,7 +982,7 @@ void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResul if (m_frame.selection().granularity() != CharacterGranularity) newSelection.expandUsingGranularity(m_frame.selection().granularity()); - m_frame.selection().setNonDirectionalSelectionIfNeeded(newSelection, m_frame.selection().granularity(), + m_frame.selection().setSelectionByMouseIfDifferent(newSelection, m_frame.selection().granularity(), FrameSelection::AdjustEndpointsAtBidiBoundary); } #endif // ENABLE(DRAG_SUPPORT) @@ -947,6 +1010,8 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e if (autoscrollInProgress()) stopAutoscrollTimer(); + Ref protectedFrame(m_frame); + if (handleMouseUp(event)) return true; @@ -969,7 +1034,7 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e // editing, place the caret. if (m_mouseDownWasSingleClickInSelection && m_selectionInitiationState != ExtendedSelection #if ENABLE(DRAG_SUPPORT) - && m_dragStartPos == event.event().position() + && m_dragStartPosition == event.event().position() #endif && m_frame.selection().isRange() && event.event().button() != RightButton) { @@ -977,7 +1042,7 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e Node* node = event.targetNode(); bool caretBrowsing = m_frame.settings().caretBrowsingEnabled(); if (node && node->renderer() && (caretBrowsing || node->hasEditableStyle())) { - VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint()); + VisiblePosition pos = node->renderer()->positionForPoint(event.localPoint(), nullptr); newSelection = VisibleSelection(pos); } @@ -986,10 +1051,6 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e handled = true; } - m_frame.selection().updateSelectionCachesIfSelectionIsInsideTextFormControl(UserTriggered); - - m_frame.selection().selectFrameElementInParentIfFullySelected(); - if (event.event().button() == MiddleButton) { // Ignore handled, since we want to paste to where the caret was placed anyway. handled = handlePasteGlobalSelection(event.event()) || handled; @@ -1010,12 +1071,12 @@ void EventHandler::didPanScrollStop() m_autoscrollController->didPanScrollStop(); } -void EventHandler::startPanScrolling(RenderElement* renderer) +void EventHandler::startPanScrolling(RenderElement& renderer) { #if !PLATFORM(IOS) - if (!renderer->isBox()) + if (!is(renderer)) return; - m_autoscrollController->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition()); + m_autoscrollController->startPanScrolling(&downcast(renderer), lastKnownMousePosition()); invalidateClick(); #endif } @@ -1059,6 +1120,8 @@ DragSourceAction EventHandler::updateDragSourceActionsAllowed() const HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTestRequest::HitTestRequestType hitType, const LayoutSize& padding) { + Ref protectedFrame(m_frame); + // We always send hitTestResultAtPoint to the main frame if we have one, // otherwise we might hit areas that are obscured by higher frames. if (!m_frame.isMainFrame()) { @@ -1071,19 +1134,26 @@ HitTestResult EventHandler::hitTestResultAtPoint(const LayoutPoint& point, HitTe } } - HitTestResult result(point, padding.height(), padding.width(), padding.height(), padding.width()); + unsigned nonNegativePaddingWidth = std::max(0, padding.width()).toUnsigned(); + unsigned nonNegativePaddingHeight = std::max(0, padding.height()).toUnsigned(); + + // We should always start hit testing a clean tree. + if (auto* frameView = m_frame.view()) + frameView->updateLayoutAndStyleIfNeededRecursive(); - if (!m_frame.contentRenderer()) + HitTestResult result(point, nonNegativePaddingHeight, nonNegativePaddingWidth, nonNegativePaddingHeight, nonNegativePaddingWidth); + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) return result; // hitTestResultAtPoint is specifically used to hitTest into all frames, thus it always allows child frame content. HitTestRequest request(hitType | HitTestRequest::AllowChildFrameContent); - m_frame.contentRenderer()->hitTest(request, result); + renderView->hitTest(request, result); if (!request.readOnly()) - m_frame.document()->updateHoverActiveState(request, result.innerElement()); + m_frame.document()->updateHoverActiveState(request, result.targetElement()); - if (request.disallowsShadowContent()) - result.setToNonShadowAncestor(); + if (request.disallowsUserAgentShadowContent()) + result.setToNonUserAgentShadowAncestor(); return result; } @@ -1093,16 +1163,6 @@ void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed) m_autoscrollController->stopAutoscrollTimer(rendererIsBeingDestroyed); } -Node* EventHandler::mousePressNode() const -{ - return m_mousePressNode.get(); -} - -void EventHandler::setMousePressNode(PassRefPtr node) -{ - m_mousePressNode = node; -} - bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) { Node* node = startingNode; @@ -1115,7 +1175,7 @@ bool EventHandler::scrollOverflow(ScrollDirection direction, ScrollGranularity g if (node) { auto r = node->renderer(); - if (r && !r->isListBox() && r->enclosingBox()->scroll(direction, granularity)) { + if (r && !r->isListBox() && r->enclosingBox().scroll(direction, granularity)) { setFrameWasScrolledByUser(); return true; } @@ -1136,7 +1196,7 @@ bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, Scrol if (node) { auto r = node->renderer(); - if (r && !r->isListBox() && r->enclosingBox()->logicalScroll(direction, granularity)) { + if (r && !r->isListBox() && r->enclosingBox().logicalScroll(direction, granularity)) { setFrameWasScrolledByUser(); return true; } @@ -1147,6 +1207,8 @@ bool EventHandler::logicalScrollOverflow(ScrollLogicalDirection direction, Scrol bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularity granularity, Node* startingNode) { + Ref protectedFrame(m_frame); + // The layout needs to be up to date to determine if we can scroll. We may be // here because of an onLoad event, in which case the final layout hasn't been performed yet. m_frame.document()->updateLayoutIgnorePendingStylesheets(); @@ -1164,6 +1226,8 @@ bool EventHandler::scrollRecursively(ScrollDirection direction, ScrollGranularit bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, ScrollGranularity granularity, Node* startingNode) { + Ref protectedFrame(m_frame); + // The layout needs to be up to date to determine if we can scroll. We may be // here because of an onLoad event, in which case the final layout hasn't been performed yet. m_frame.document()->updateLayoutIgnorePendingStylesheets(); @@ -1173,7 +1237,7 @@ bool EventHandler::logicalScrollRecursively(ScrollLogicalDirection direction, Sc FrameView* view = frame->view(); bool scrolled = false; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Mac also resets the scroll position in the inline direction. if (granularity == ScrollByDocument && view && view->logicalScroll(ScrollInlineDirectionBackward, ScrollByDocument)) scrolled = true; @@ -1206,29 +1270,29 @@ Frame* EventHandler::subframeForHitTestResult(const MouseEventWithHitTestResults Frame* EventHandler::subframeForTargetNode(Node* node) { if (!node) - return 0; + return nullptr; auto renderer = node->renderer(); - if (!renderer || !renderer->isWidget()) - return 0; + if (!is(renderer)) + return nullptr; - Widget* widget = toRenderWidget(renderer)->widget(); - if (!widget || !widget->isFrameView()) - return 0; + Widget* widget = downcast(*renderer).widget(); + if (!is(widget)) + return nullptr; - return &toFrameView(widget)->frame(); + return &downcast(*widget).frame(); } #if ENABLE(CURSOR_SUPPORT) static bool isSubmitImage(Node* node) { - return node && isHTMLInputElement(node) && toHTMLInputElement(node)->isImageButton(); + return is(node) && downcast(*node).isImageButton(); } // Returns true if the node's editable block is not current focused for editing static bool nodeIsNotBeingEdited(const Node& node, const Frame& frame) { - return frame.selection().rootEditableElement() != node.rootEditableElement(); + return frame.selection().selection().rootEditableElement() != node.rootEditableElement(); } bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) @@ -1265,7 +1329,7 @@ bool EventHandler::useHandCursor(Node* node, bool isOverLink, bool shiftKey) return ((isOverLink || isSubmitImage(node)) && (!editable || editableLinkEnabled)); } -void EventHandler::cursorUpdateTimerFired(Timer&) +void EventHandler::cursorUpdateTimerFired() { ASSERT(m_frame.document()); updateCursor(); @@ -1293,46 +1357,57 @@ void EventHandler::updateCursor() bool metaKey; PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); - m_frame.document()->updateLayout(); - - HitTestRequest request(HitTestRequest::ReadOnly); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::AllowFrameScrollbars); HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); renderView->hitTest(request, result); - OptionalCursor optionalCursor = selectCursor(result, shiftKey); - if (optionalCursor.isCursorChange()) { - m_currentMouseCursor = optionalCursor.cursor(); - view->setCursor(m_currentMouseCursor); + updateCursor(*view, result, shiftKey); +} + +void EventHandler::updateCursor(FrameView& view, const HitTestResult& result, bool shiftKey) +{ + if (auto optionalCursor = selectCursor(result, shiftKey)) { + m_currentMouseCursor = WTFMove(optionalCursor.value()); + view.setCursor(m_currentMouseCursor); } } -OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shiftKey) +std::optional EventHandler::selectCursor(const HitTestResult& result, bool shiftKey) { if (m_resizeLayer && m_resizeLayer->inResizeMode()) - return NoCursorChange; + return std::nullopt; if (!m_frame.page()) - return NoCursorChange; + return std::nullopt; #if ENABLE(PAN_SCROLLING) if (m_frame.mainFrame().eventHandler().panScrollInProgress()) - return NoCursorChange; + return std::nullopt; +#endif + + Ref protectedFrame(m_frame); + + // Use always pointer cursor for scrollbars. + if (result.scrollbar()) { +#if ENABLE(CURSOR_VISIBILITY) + cancelAutoHideCursorTimer(); #endif + return pointerCursor(); + } Node* node = result.targetNode(); if (!node) - return NoCursorChange; + return std::nullopt; auto renderer = node->renderer(); - RenderStyle* style = renderer ? &renderer->style() : nullptr; + auto* style = renderer ? &renderer->style() : nullptr; bool horizontalText = !style || style->isHorizontalWritingMode(); const Cursor& iBeam = horizontalText ? iBeamCursor() : verticalTextCursor(); #if ENABLE(CURSOR_VISIBILITY) - if (style && style->cursorVisibility() == CursorVisibilityAutoHide) { - FeatureObserver::observe(m_frame.document(), FeatureObserver::CursorVisibility); + if (style && style->cursorVisibility() == CursorVisibilityAutoHide) startAutoHideCursorTimer(); - } else + else cancelAutoHideCursorTimer(); #endif @@ -1344,7 +1419,7 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif case SetCursor: return overrideCursor; case DoNotSetCursor: - return NoCursorChange; + return std::nullopt; } } @@ -1360,8 +1435,7 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif float scale = styleImage->imageScaleFactor(); // Get hotspot and convert from logical pixels to physical pixels. IntPoint hotSpot = (*cursors)[i].hotSpot(); - hotSpot.scale(scale, scale); - IntSize size = cachedImage->imageForRenderer(renderer)->size(); + FloatSize size = cachedImage->imageForRenderer(renderer)->size(); if (cachedImage->errorOccurred()) continue; // Limit the size of cursors (in UI pixels) so that they cannot be @@ -1383,8 +1457,19 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif } } - switch (style ? style->cursor() : CURSOR_AUTO) { - case CURSOR_AUTO: { + // During selection, use an I-beam regardless of the content beneath the cursor. + // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection. + if (m_mousePressed + && m_mouseDownMayStartSelect +#if ENABLE(DRAG_SUPPORT) + && !m_mouseDownMayStartDrag +#endif + && m_frame.selection().isCaretOrRange() + && !m_capturingMouseEventsElement) + return iBeam; + + switch (style ? style->cursor() : CursorAuto) { + case CursorAuto: { bool editable = node->hasEditableStyle(); if (useHandCursor(node, result.isOverLink(), shiftKey)) @@ -1398,90 +1483,79 @@ OptionalCursor EventHandler::selectCursor(const HitTestResult& result, bool shif } } - // During selection, use an I-beam regardless of the content beneath the cursor when cursor style is not explicitly specified. - // If a drag may be starting or we're capturing mouse events for a particular node, don't treat this as a selection. - if (m_mousePressed && m_mouseDownMayStartSelect -#if ENABLE(DRAG_SUPPORT) - && !m_mouseDownMayStartDrag -#endif - && m_frame.selection().isCaretOrRange() - && !m_capturingMouseEventsElement) { - return iBeam; - } - if ((editable || (renderer && renderer->isText() && node->canStartSelection())) && !inResizer && !result.scrollbar()) return iBeam; return pointerCursor(); } - case CURSOR_CROSS: + case CursorCross: return crossCursor(); - case CURSOR_POINTER: + case CursorPointer: return handCursor(); - case CURSOR_MOVE: + case CursorMove: return moveCursor(); - case CURSOR_ALL_SCROLL: + case CursorAllScroll: return moveCursor(); - case CURSOR_E_RESIZE: + case CursorEResize: return eastResizeCursor(); - case CURSOR_W_RESIZE: + case CursorWResize: return westResizeCursor(); - case CURSOR_N_RESIZE: + case CursorNResize: return northResizeCursor(); - case CURSOR_S_RESIZE: + case CursorSResize: return southResizeCursor(); - case CURSOR_NE_RESIZE: + case CursorNeResize: return northEastResizeCursor(); - case CURSOR_SW_RESIZE: + case CursorSwResize: return southWestResizeCursor(); - case CURSOR_NW_RESIZE: + case CursorNwResize: return northWestResizeCursor(); - case CURSOR_SE_RESIZE: + case CursorSeResize: return southEastResizeCursor(); - case CURSOR_NS_RESIZE: + case CursorNsResize: return northSouthResizeCursor(); - case CURSOR_EW_RESIZE: + case CursorEwResize: return eastWestResizeCursor(); - case CURSOR_NESW_RESIZE: + case CursorNeswResize: return northEastSouthWestResizeCursor(); - case CURSOR_NWSE_RESIZE: + case CursorNwseResize: return northWestSouthEastResizeCursor(); - case CURSOR_COL_RESIZE: + case CursorColResize: return columnResizeCursor(); - case CURSOR_ROW_RESIZE: + case CursorRowResize: return rowResizeCursor(); - case CURSOR_TEXT: + case CursorText: return iBeamCursor(); - case CURSOR_WAIT: + case CursorWait: return waitCursor(); - case CURSOR_HELP: + case CursorHelp: return helpCursor(); - case CURSOR_VERTICAL_TEXT: + case CursorVerticalText: return verticalTextCursor(); - case CURSOR_CELL: + case CursorCell: return cellCursor(); - case CURSOR_CONTEXT_MENU: + case CursorContextMenu: return contextMenuCursor(); - case CURSOR_PROGRESS: + case CursorProgress: return progressCursor(); - case CURSOR_NO_DROP: + case CursorNoDrop: return noDropCursor(); - case CURSOR_ALIAS: + case CursorAlias: return aliasCursor(); - case CURSOR_COPY: + case CursorCopy: return copyCursor(); - case CURSOR_NONE: + case CursorNone: return noneCursor(); - case CURSOR_NOT_ALLOWED: + case CursorNotAllowed: return notAllowedCursor(); - case CURSOR_DEFAULT: + case CursorDefault: return pointerCursor(); - case CURSOR_WEBKIT_ZOOM_IN: + case CursorZoomIn: return zoomInCursor(); - case CURSOR_WEBKIT_ZOOM_OUT: + case CursorZoomOut: return zoomOutCursor(); - case CURSOR_WEBKIT_GRAB: + case CursorWebkitGrab: return grabCursor(); - case CURSOR_WEBKIT_GRABBING: + case CursorWebkitGrabbing: return grabbingCursor(); } return pointerCursor(); @@ -1497,10 +1571,12 @@ void EventHandler::startAutoHideCursorTimer() m_autoHideCursorTimer.startOneShot(page->settings().timeWithoutMouseMovementBeforeHidingControls()); +#if !ENABLE(IOS_TOUCH_EVENTS) // The fake mouse move event screws up the auto-hide feature (by resetting the auto-hide timer) // so cancel any pending fake mouse moves. if (m_fakeMouseMoveEventTimer.isActive()) m_fakeMouseMoveEventTimer.stop(); +#endif } void EventHandler::cancelAutoHideCursorTimer() @@ -1509,9 +1585,8 @@ void EventHandler::cancelAutoHideCursorTimer() m_autoHideCursorTimer.stop(); } -void EventHandler::autoHideCursorTimerFired(Timer& timer) +void EventHandler::autoHideCursorTimerFired() { - ASSERT_UNUSED(timer, &timer == &m_autoHideCursorTimer); m_currentMouseCursor = noneCursor(); FrameView* view = m_frame.view(); if (view && view->isActive()) @@ -1527,68 +1602,98 @@ static LayoutPoint documentPointForWindowPoint(Frame& frame, const IntPoint& win return view ? view->windowToContents(windowPoint) : windowPoint; } -bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) +static Scrollbar* scrollbarForMouseEvent(const MouseEventWithHitTestResults& mouseEvent, FrameView* view) +{ + if (view) { + if (auto* scrollbar = view->scrollbarAtPoint(mouseEvent.event().position())) + return scrollbar; + } + return mouseEvent.scrollbar(); + +} + +bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& platformMouseEvent) { + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); - if (InspectorInstrumentation::handleMousePress(m_frame.page())) { + if (InspectorInstrumentation::handleMousePress(m_frame)) { invalidateClick(); return true; } +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousedownEvent); + return true; + } +#endif + + if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(platformMouseEvent)) + return true; + #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); // FIXME (bug 68185): this call should be made at another abstraction layer m_frame.loader().resetMultipleFormSubmissionProtection(); - + +#if !ENABLE(IOS_TOUCH_EVENTS) cancelFakeMouseMoveEvent(); +#endif m_mousePressed = true; m_capturesDragging = true; - setLastKnownMousePosition(mouseEvent); - m_mouseDownTimestamp = mouseEvent.timestamp(); + setLastKnownMousePosition(platformMouseEvent); + m_mouseDownTimestamp = platformMouseEvent.timestamp(); #if ENABLE(DRAG_SUPPORT) m_mouseDownMayStartDrag = false; #endif m_mouseDownMayStartSelect = false; m_mouseDownMayStartAutoscroll = false; if (FrameView* view = m_frame.view()) - m_mouseDownPos = view->windowToContents(mouseEvent.position()); + m_mouseDownPos = view->windowToContents(platformMouseEvent.position()); else { invalidateClick(); return false; } m_mouseDownWasInSubframe = false; - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); // Save the document point we generate in case the window coordinate is invalidated by what happens // when we dispatch the event. - LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, mouseEvent.position()); - MouseEventWithHitTestResults mev = m_frame.document()->prepareMouseEvent(request, documentPoint, mouseEvent); + LayoutPoint documentPoint = documentPointForWindowPoint(m_frame, platformMouseEvent.position()); + MouseEventWithHitTestResults mouseEvent = m_frame.document()->prepareMouseEvent(request, documentPoint, platformMouseEvent); - if (!mev.targetNode()) { + if (!mouseEvent.targetNode()) { invalidateClick(); return false; } - m_mousePressNode = mev.targetNode(); - - RefPtr subframe = subframeForHitTestResult(mev); - if (subframe && passMousePressEventToSubframe(mev, subframe.get())) { - // Start capturing future events for this frame. We only do this if we didn't clear - // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. - m_capturesDragging = subframe->eventHandler().capturesDragging(); - if (m_mousePressed && m_capturesDragging) { - m_capturingMouseEventsElement = subframe->ownerElement(); - m_eventHandlerWillResetCapturingMouseEventsElement = true; + m_mousePressNode = mouseEvent.targetNode(); + m_frame.document()->setFocusNavigationStartingNode(mouseEvent.targetNode()); + + Scrollbar* scrollbar = scrollbarForMouseEvent(mouseEvent, m_frame.view()); + updateLastScrollbarUnderMouse(scrollbar, SetOrClearLastScrollbar::Set); + bool passedToScrollbar = scrollbar && passMousePressEventToScrollbar(mouseEvent, scrollbar); + + if (!passedToScrollbar) { + RefPtr subframe = subframeForHitTestResult(mouseEvent); + if (subframe && passMousePressEventToSubframe(mouseEvent, subframe.get())) { + // Start capturing future events for this frame. We only do this if we didn't clear + // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. + m_capturesDragging = subframe->eventHandler().capturesDragging(); + if (m_mousePressed && m_capturesDragging) { + m_capturingMouseEventsElement = subframe->ownerElement(); + m_eventHandlerWillResetCapturingMouseEventsElement = true; + } + invalidateClick(); + return true; } - invalidateClick(); - return true; } #if ENABLE(PAN_SCROLLING) @@ -1604,8 +1709,8 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) } #endif - m_clickCount = mouseEvent.clickCount(); - m_clickNode = mev.targetNode(); + m_clickCount = platformMouseEvent.clickCount(); + m_clickNode = mouseEvent.targetNode(); if (!m_clickNode) { invalidateClick(); @@ -1614,7 +1719,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) if (FrameView* view = m_frame.view()) { RenderLayer* layer = m_clickNode->renderer() ? m_clickNode->renderer()->enclosingLayer() : 0; - IntPoint p = view->windowToContents(mouseEvent.position()); + IntPoint p = view->windowToContents(platformMouseEvent.position()); if (layer && layer->isPointInResizeControl(p)) { layer->setInResizeMode(true); m_resizeLayer = layer; @@ -1626,110 +1731,108 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) m_frame.selection().setCaretBlinkingSuspended(true); - bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); - m_capturesDragging = !swallowEvent || mev.scrollbar(); + bool swallowEvent = !dispatchMouseEvent(eventNames().mousedownEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true); + m_capturesDragging = !swallowEvent || mouseEvent.scrollbar(); // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults // in case the scrollbar widget was destroyed when the mouse event was handled. - if (mev.scrollbar()) { - const bool wasLastScrollBar = mev.scrollbar() == m_lastScrollbarUnderMouse.get(); - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - mev = m_frame.document()->prepareMouseEvent(request, documentPoint, mouseEvent); - if (wasLastScrollBar && mev.scrollbar() != m_lastScrollbarUnderMouse.get()) + if (mouseEvent.scrollbar()) { + const bool wasLastScrollBar = mouseEvent.scrollbar() == m_lastScrollbarUnderMouse; + mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); + if (wasLastScrollBar && mouseEvent.scrollbar() != m_lastScrollbarUnderMouse) m_lastScrollbarUnderMouse = nullptr; } - if (swallowEvent) { - // scrollbars should get events anyway, even disabled controls might be scrollable - Scrollbar* scrollbar = mev.scrollbar(); - - updateLastScrollbarUnderMouse(scrollbar, true); - - if (scrollbar) - passMousePressEventToScrollbar(mev, scrollbar); - } else { + if (!swallowEvent) { // Refetch the event target node if it currently is the shadow node inside an element. // If a mouse event handler changes the input element type to one that has a widget associated, // we'd like to EventHandler::handleMousePressEvent to pass the event to the widget and thus the // event target node can't still be the shadow node. - if (mev.targetNode()->isShadowRoot() && isHTMLInputElement(toShadowRoot(mev.targetNode())->hostElement())) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - mev = m_frame.document()->prepareMouseEvent(request, documentPoint, mouseEvent); - } - - FrameView* view = m_frame.view(); - Scrollbar* scrollbar = view ? view->scrollbarAtPoint(mouseEvent.position()) : 0; - if (!scrollbar) - scrollbar = mev.scrollbar(); - - updateLastScrollbarUnderMouse(scrollbar, true); + if (is(*mouseEvent.targetNode()) && is(*downcast(*mouseEvent.targetNode()).host())) + mouseEvent = m_frame.document()->prepareMouseEvent(HitTestRequest(), documentPoint, platformMouseEvent); + } - if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar)) + if (!swallowEvent) { + if (passedToScrollbar) swallowEvent = true; else - swallowEvent = handleMousePressEvent(mev); + swallowEvent = handleMousePressEvent(mouseEvent); } - return swallowEvent; } // This method only exists for platforms that don't know how to deliver -bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& mouseEvent) +bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& platformMouseEvent) { + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); m_frame.selection().setCaretBlinkingSuspended(false); - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); + +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent); + return true; + } +#endif // We get this instead of a second mouse-up m_mousePressed = false; - setLastKnownMousePosition(mouseEvent); + setLastKnownMousePosition(platformMouseEvent); - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); - Frame* subframe = subframeForHitTestResult(mev); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + Frame* subframe = subframeForHitTestResult(mouseEvent); if (m_eventHandlerWillResetCapturingMouseEventsElement) m_capturingMouseEventsElement = nullptr; - if (subframe && passMousePressEventToSubframe(mev, subframe)) + if (subframe && passMousePressEventToSubframe(mouseEvent, subframe)) return true; - m_clickCount = mouseEvent.clickCount(); - bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + m_clickCount = platformMouseEvent.clickCount(); + bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false); - bool swallowClickEvent = mouseEvent.button() != RightButton && mev.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + bool swallowClickEvent = platformMouseEvent.button() != RightButton && mouseEvent.targetNode() == m_clickNode && !dispatchMouseEvent(eventNames().clickEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, true); if (m_lastScrollbarUnderMouse) - swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(mouseEvent); + swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent); - bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mev); + bool swallowMouseReleaseEvent = !swallowMouseUpEvent && handleMouseReleaseEvent(mouseEvent); invalidateClick(); return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent; } -static RenderLayer* layerForNode(Node* node) +static ScrollableArea* enclosingScrollableArea(Node* node) { - if (!node) - return 0; + for (auto ancestor = node; ancestor; ancestor = ancestor->parentOrShadowHostNode()) { + if (is(*ancestor) || is(*ancestor) || is(*ancestor)) + return nullptr; - auto renderer = node->renderer(); - if (!renderer) - return 0; + auto renderer = ancestor->renderer(); + if (!renderer) + continue; - RenderLayer* layer = renderer->enclosingLayer(); - if (!layer) - return 0; + if (is(*renderer)) + return downcast(renderer); + + return renderer->enclosingLayer(); + } - return layer; + return nullptr; } bool EventHandler::mouseMoved(const PlatformMouseEvent& event) { + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); MaximumDurationTracker maxDurationTracker(&m_maxMouseMovedDuration); + if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(event)) + return true; + HitTestResult hoveredNode = HitTestResult(LayoutPoint()); bool result = handleMouseMoveEvent(event, &hoveredNode); @@ -1737,17 +1840,17 @@ bool EventHandler::mouseMoved(const PlatformMouseEvent& event) if (!page) return result; - if (RenderLayer* layer = layerForNode(hoveredNode.innerNode())) { + if (auto scrolledArea = enclosingScrollableArea(hoveredNode.innerNode())) { if (FrameView* frameView = m_frame.view()) { - if (frameView->containsScrollableArea(layer)) - layer->mouseMovedInContentArea(); + if (frameView->containsScrollableArea(scrolledArea)) + scrolledArea->mouseMovedInContentArea(); } } if (FrameView* frameView = m_frame.view()) frameView->mouseMovedInContentArea(); - hoveredNode.setToNonShadowAncestor(); + hoveredNode.setToNonUserAgentShadowAncestor(); page->chrome().mouseDidMoveOverElement(hoveredNode, event.modifierFlags()); page->chrome().setToolTip(hoveredNode); return result; @@ -1759,17 +1862,25 @@ bool EventHandler::passMouseMovedEventToScrollbars(const PlatformMouseEvent& eve return handleMouseMoveEvent(event, &hoveredNode, true); } -bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars) +bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& platformMouseEvent, HitTestResult* hoveredNode, bool onlyUpdateScrollbars) { #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); - - setLastKnownMousePosition(mouseEvent); + +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mousemoveEvent); + return true; + } +#endif + + setLastKnownMousePosition(platformMouseEvent); if (m_hoverTimer.isActive()) m_hoverTimer.stop(); @@ -1778,26 +1889,26 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi m_cursorUpdateTimer.stop(); #endif +#if !ENABLE(IOS_TOUCH_EVENTS) cancelFakeMouseMoveEvent(); +#endif -#if ENABLE(SVG) if (m_svgPan) { - toSVGDocument(m_frame.document())->updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); + downcast(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); return true; } -#endif if (m_frameSetBeingResized) - return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, mouseEvent, false); + return !dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, platformMouseEvent, false); // On iOS, our scrollbars are managed by UIKit. #if !PLATFORM(IOS) // Send events right to a scrollbar if the mouse is pressed. if (m_lastScrollbarUnderMouse && m_mousePressed) - return m_lastScrollbarUnderMouse->mouseMoved(mouseEvent); + return m_lastScrollbarUnderMouse->mouseMoved(platformMouseEvent); #endif - HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowShadowContent | HitTestRequest::AllowFrameScrollbars; + HitTestRequest::HitTestRequestType hitType = HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::AllowFrameScrollbars; if (m_mousePressed) hitType |= HitTestRequest::Active; else if (onlyUpdateScrollbars) { @@ -1814,49 +1925,48 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi hitType |= HitTestRequest::Active | HitTestRequest::ReadOnly; #endif HitTestRequest request(hitType); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); if (hoveredNode) - *hoveredNode = mev.hitTestResult(); + *hoveredNode = mouseEvent.hitTestResult(); if (m_resizeLayer && m_resizeLayer->inResizeMode()) - m_resizeLayer->resize(mouseEvent, m_offsetFromResizeCorner); + m_resizeLayer->resize(platformMouseEvent, m_offsetFromResizeCorner); else { - Scrollbar* scrollbar = mev.scrollbar(); - updateLastScrollbarUnderMouse(scrollbar, !m_mousePressed); + Scrollbar* scrollbar = mouseEvent.scrollbar(); + updateLastScrollbarUnderMouse(scrollbar, m_mousePressed ? SetOrClearLastScrollbar::Clear : SetOrClearLastScrollbar::Set); // On iOS, our scrollbars are managed by UIKit. #if !PLATFORM(IOS) if (!m_mousePressed && scrollbar) - scrollbar->mouseMoved(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. + scrollbar->mouseMoved(platformMouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. #endif - if (onlyUpdateScrollbars) + if (onlyUpdateScrollbars) { + updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true); return true; + } } bool swallowEvent = false; - RefPtr newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mev); + RefPtr newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent); // We want mouseouts to happen first, from the inside out. First send a move event to the last subframe so that it will fire mouseouts. if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree().isDescendantOf(&m_frame) && m_lastMouseMoveEventSubframe != newSubframe) - passMouseMoveEventToSubframe(mev, m_lastMouseMoveEventSubframe.get()); + passMouseMoveEventToSubframe(mouseEvent, m_lastMouseMoveEventSubframe.get()); if (newSubframe) { // Update over/out state before passing the event to the subframe. - updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true); + updateMouseEventTargetNode(mouseEvent.targetNode(), platformMouseEvent, true); // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target // node to be detached from its FrameView, in which case the event should not be passed. if (newSubframe->view()) - swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); + swallowEvent |= passMouseMoveEventToSubframe(mouseEvent, newSubframe.get(), hoveredNode); + } + + if (!newSubframe || mouseEvent.scrollbar()) { #if ENABLE(CURSOR_SUPPORT) - } else { - if (FrameView* view = m_frame.view()) { - OptionalCursor optionalCursor = selectCursor(mev.hitTestResult(), mouseEvent.shiftKey()); - if (optionalCursor.isCursorChange()) { - m_currentMouseCursor = optionalCursor.cursor(); - view->setCursor(m_currentMouseCursor); - } - } + if (auto* view = m_frame.view()) + updateCursor(*view, mouseEvent.hitTestResult(), platformMouseEvent.shiftKey()); #endif } @@ -1865,11 +1975,11 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (swallowEvent) return true; - swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true); + swallowEvent = !dispatchMouseEvent(eventNames().mousemoveEvent, mouseEvent.targetNode(), false, 0, platformMouseEvent, true); #if ENABLE(DRAG_SUPPORT) if (!swallowEvent) - swallowEvent = handleMouseDraggedEvent(mev); -#endif // ENABLE(DRAG_SUPPORT) + swallowEvent = handleMouseDraggedEvent(mouseEvent); +#endif return swallowEvent; } @@ -1877,124 +1987,175 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi void EventHandler::invalidateClick() { m_clickCount = 0; - m_clickNode = 0; + m_clickNode = nullptr; } -inline static bool mouseIsReleasedOnPressedElement(Node* targetNode, Node* clickNode) +static Node* targetNodeForClickEvent(Node* mousePressNode, Node* mouseReleaseNode) { - if (targetNode == clickNode) - return true; - - if (!targetNode) - return false; - - ShadowRoot* containingShadowRoot = targetNode->containingShadowRoot(); - if (!containingShadowRoot) - return false; + if (!mousePressNode || !mouseReleaseNode) + return nullptr; - // FIXME: When an element in UA ShadowDOM (e.g. inner element in ) is clicked, - // we assume that the host element is clicked. This is necessary for implementing etc. - // However, we should not check ShadowRoot type basically. - // https://bugs.webkit.org/show_bug.cgi?id=108047 - if (containingShadowRoot->type() != ShadowRoot::UserAgentShadowRoot) - return false; + if (mousePressNode == mouseReleaseNode) + return mouseReleaseNode; - Node* adjustedTargetNode = targetNode->shadowHost(); - Node* adjustedClickNode = clickNode ? clickNode->shadowHost() : 0; - return adjustedTargetNode == adjustedClickNode; + Element* mouseReleaseShadowHost = mouseReleaseNode->shadowHost(); + if (mouseReleaseShadowHost && mouseReleaseShadowHost == mousePressNode->shadowHost()) { + // We want to dispatch the click to the shadow tree host element to give listeners the illusion that the + // shadom tree is a single element. For example, we want to give the illusion that + // is a single element even though it is a composition of multiple shadom tree elements. + return mouseReleaseShadowHost; + } + return nullptr; } -bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) +bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& platformMouseEvent) { + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); m_frame.selection().setCaretBlinkingSuspended(false); +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(platformMouseEvent, eventNames().mouseupEvent); + return true; + } +#endif + + if (m_frame.mainFrame().pageOverlayController().handleMouseEvent(platformMouseEvent)) + return true; + #if ENABLE(TOUCH_EVENTS) - bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(mouseEvent); + bool defaultPrevented = dispatchSyntheticTouchEventIfEnabled(platformMouseEvent); if (defaultPrevented) return true; #endif - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); #if ENABLE(PAN_SCROLLING) - m_autoscrollController->handleMouseReleaseEvent(mouseEvent); + m_autoscrollController->handleMouseReleaseEvent(platformMouseEvent); #endif m_mousePressed = false; - setLastKnownMousePosition(mouseEvent); + setLastKnownMousePosition(platformMouseEvent); -#if ENABLE(SVG) if (m_svgPan) { m_svgPan = false; - toSVGDocument(m_frame.document())->updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); + downcast(*m_frame.document()).updatePan(m_frame.view()->windowToContents(m_lastKnownMousePosition)); return true; } -#endif if (m_frameSetBeingResized) - return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false); + return !dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, platformMouseEvent, false); + + // If an immediate action began or was completed using this series of mouse events, then we should send mouseup to + // the DOM and return now so that we don't perform our own default behaviors. + if (m_immediateActionStage == ImmediateActionStage::ActionCompleted || m_immediateActionStage == ImmediateActionStage::ActionUpdated || m_immediateActionStage == ImmediateActionStage::ActionCancelledAfterUpdate) { + m_immediateActionStage = ImmediateActionStage::None; + return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), true, m_clickCount, platformMouseEvent, false); + } + m_immediateActionStage = ImmediateActionStage::None; if (m_lastScrollbarUnderMouse) { invalidateClick(); - m_lastScrollbarUnderMouse->mouseUp(mouseEvent); + m_lastScrollbarUnderMouse->mouseUp(platformMouseEvent); bool cancelable = true; bool setUnder = false; - return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, mouseEvent, setUnder); + return !dispatchMouseEvent(eventNames().mouseupEvent, m_lastElementUnderMouse.get(), cancelable, m_clickCount, platformMouseEvent, setUnder); } - HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, mouseEvent); - Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mev); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + Frame* subframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mouseEvent); if (m_eventHandlerWillResetCapturingMouseEventsElement) m_capturingMouseEventsElement = nullptr; - if (subframe && passMouseReleaseEventToSubframe(mev, subframe)) + if (subframe && passMouseReleaseEventToSubframe(mouseEvent, subframe)) return true; - bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + bool swallowMouseUpEvent = !dispatchMouseEvent(eventNames().mouseupEvent, mouseEvent.targetNode(), true, m_clickCount, platformMouseEvent, false); - bool contextMenuEvent = mouseEvent.button() == RightButton; + bool contextMenuEvent = platformMouseEvent.button() == RightButton; - bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && mouseIsReleasedOnPressedElement(mev.targetNode(), m_clickNode.get()) && !dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + Node* nodeToClick = targetNodeForClickEvent(m_clickNode.get(), mouseEvent.targetNode()); + bool swallowClickEvent = m_clickCount > 0 && !contextMenuEvent && nodeToClick && !dispatchMouseEvent(eventNames().clickEvent, nodeToClick, true, m_clickCount, platformMouseEvent, true); if (m_resizeLayer) { m_resizeLayer->setInResizeMode(false); - m_resizeLayer = 0; + m_resizeLayer = nullptr; } bool swallowMouseReleaseEvent = false; if (!swallowMouseUpEvent) - swallowMouseReleaseEvent = handleMouseReleaseEvent(mev); + swallowMouseReleaseEvent = handleMouseReleaseEvent(mouseEvent); invalidateClick(); return swallowMouseUpEvent || swallowClickEvent || swallowMouseReleaseEvent; } -bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEvent) +#if ENABLE(MOUSE_FORCE_EVENTS) +bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& event) { - // If the event was a middle click, attempt to copy global selection in after - // the newly set caret position. - // - // This code is called from either the mouse up or mouse down handling. There - // is some debate about when the global selection is pasted: - // xterm: pastes on up. - // GTK: pastes on down. - // Qt: pastes on up. - // Firefox: pastes on up. - // Chromium: pastes on up. - // + Ref protectedFrame(m_frame); + RefPtr protector(m_frame.view()); + +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcechangedEvent); + if (event.type() == PlatformEvent::MouseForceDown) + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforcedownEvent); + if (event.type() == PlatformEvent::MouseForceUp) + m_frame.page()->pointerLockController().dispatchLockedMouseEvent(event, eventNames().webkitmouseforceupEvent); + return true; + } +#endif + + setLastKnownMousePosition(event); + + HitTestRequest::HitTestRequestType hitType = HitTestRequest::DisallowUserAgentShadowContent | HitTestRequest::Active; + + HitTestRequest request(hitType); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); + + bool swallowedEvent = !dispatchMouseEvent(eventNames().webkitmouseforcechangedEvent, mouseEvent.targetNode(), false, 0, event, false); + if (event.type() == PlatformEvent::MouseForceDown) + swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforcedownEvent, mouseEvent.targetNode(), false, 0, event, false); + if (event.type() == PlatformEvent::MouseForceUp) + swallowedEvent |= !dispatchMouseEvent(eventNames().webkitmouseforceupEvent, mouseEvent.targetNode(), false, 0, event, false); + + return swallowedEvent; +} +#else +bool EventHandler::handleMouseForceEvent(const PlatformMouseEvent& ) +{ + return false; +} +#endif // #if ENABLE(MOUSE_FORCE_EVENTS) + +bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& platformMouseEvent) +{ + // If the event was a middle click, attempt to copy global selection in after + // the newly set caret position. + // + // This code is called from either the mouse up or mouse down handling. There + // is some debate about when the global selection is pasted: + // xterm: pastes on up. + // GTK: pastes on down. + // Qt: pastes on up. + // Firefox: pastes on up. + // Chromium: pastes on up. + // // There is something of a webcompat angle to this well, as highlighted by // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on // down then the text is pasted just before the onclick handler runs and // clears the text box. So it's important this happens after the event // handlers have been fired. #if PLATFORM(GTK) - if (mouseEvent.type() != PlatformEvent::MousePressed) + if (platformMouseEvent.type() != PlatformEvent::MousePressed) return false; #else - if (mouseEvent.type() != PlatformEvent::MouseReleased) + if (platformMouseEvent.type() != PlatformEvent::MouseReleased) return false; #endif @@ -2010,8 +2171,9 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve #if ENABLE(DRAG_SUPPORT) -bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, Clipboard* clipboard) +bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dragTarget, const PlatformMouseEvent& event, DataTransfer* dataTransfer) { + Ref protectedFrame(m_frame); FrameView* view = m_frame.view(); // FIXME: We might want to dispatch a dragleave even if the view is gone. @@ -2019,29 +2181,25 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Element& dra return false; view->disableLayerFlushThrottlingTemporarilyForInteraction(); - RefPtr me = MouseEvent::create(eventType, + Ref me = MouseEvent::create(eventType, true, true, event.timestamp(), m_frame.document()->defaultView(), 0, event.globalPosition().x(), event.globalPosition().y(), event.position().x(), event.position().y(), #if ENABLE(POINTER_LOCK) event.movementDelta().x(), event.movementDelta().y(), #endif event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), - 0, 0, clipboard); + 0, 0, event.force(), NoTap, dataTransfer); - dragTarget.dispatchEvent(me.get(), IGNORE_EXCEPTION); + dragTarget.dispatchEvent(me); return me->defaultPrevented(); } static bool targetIsFrame(Node* target, Frame*& frame) { - if (!target) + if (!is(target)) return false; - if (!target->hasTagName(frameTag) && !target->hasTagName(iframeTag)) - return false; - - frame = toHTMLFrameElementBase(target)->contentFrame(); - + frame = downcast(*target).contentFrame(); return true; } @@ -2070,85 +2228,62 @@ static String convertDragOperationToDropZoneOperation(DragOperation operation) } } -static inline bool hasFileOfType(Clipboard& clipboard, const String& type) -{ - RefPtr fileList = clipboard.files(); - for (unsigned i = 0; i < fileList->length(); i++) { - if (equalIgnoringCase(fileList->item(i)->type(), type)) - return true; - } - return false; -} - -static inline bool hasStringOfType(Clipboard& clipboard, const String& type) -{ - return !type.isNull() && clipboard.types().contains(type); -} - -static bool hasDropZoneType(Clipboard& clipboard, const String& keyword) +static bool hasDropZoneType(DataTransfer& dataTransfer, const String& keyword) { if (keyword.startsWith("file:")) - return hasFileOfType(clipboard, keyword.substring(5)); + return dataTransfer.hasFileOfType(keyword.substring(5)); if (keyword.startsWith("string:")) - return hasStringOfType(clipboard, keyword.substring(7)); + return dataTransfer.hasStringOfType(keyword.substring(7)); return false; } -static bool findDropZone(Node* target, Clipboard* clipboard) +static bool findDropZone(Node* target, DataTransfer* dataTransfer) { - Element* element = target->isElementNode() ? toElement(target) : target->parentElement(); + ASSERT(target); + Element* element = is(*target) ? downcast(target) : target->parentElement(); for (; element; element = element->parentElement()) { + SpaceSplitString keywords(element->attributeWithoutSynchronization(webkitdropzoneAttr), true); bool matched = false; - String dropZoneStr = element->fastGetAttribute(webkitdropzoneAttr); - - if (dropZoneStr.isEmpty()) - continue; - - dropZoneStr = dropZoneStr.lower(); - - SpaceSplitString keywords(dropZoneStr, false); - if (keywords.isEmpty()) - continue; - DragOperation dragOperation = DragOperationNone; - for (unsigned int i = 0; i < keywords.size(); i++) { + for (unsigned i = 0, size = keywords.size(); i < size; ++i) { DragOperation op = convertDropZoneOperationToDragOperation(keywords[i]); if (op != DragOperationNone) { if (dragOperation == DragOperationNone) dragOperation = op; } else - matched = matched || hasDropZoneType(*clipboard, keywords[i].string()); - + matched = matched || hasDropZoneType(*dataTransfer, keywords[i].string()); if (matched && dragOperation != DragOperationNone) break; } if (matched) { - clipboard->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation)); + dataTransfer->setDropEffect(convertDragOperationToDropZoneOperation(dragOperation)); return true; } } return false; } -bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer) { + Ref protectedFrame(m_frame); + bool accept = false; if (!m_frame.view()) return false; - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, event); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, event); RefPtr newTarget; - if (Node* targetNode = mev.targetNode()) { + if (Node* targetNode = mouseEvent.targetNode()) { // Drag events should never go to non-element nodes (following IE, and proper mouseover/out dispatch) - if (!targetNode->isElementNode()) + if (!is(*targetNode)) newTarget = targetNode->parentOrShadowHostElement(); else - newTarget = toElement(targetNode); + newTarget = downcast(targetNode); } m_autoscrollController->updateDragAndDrop(newTarget.get(), event.position(), event.timestamp()); @@ -2162,23 +2297,23 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* Frame* targetFrame; if (targetIsFrame(newTarget.get(), targetFrame)) { if (targetFrame) - accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard); + accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer); } else if (newTarget) { // As per section 7.9.4 of the HTML 5 spec., we must always fire a drag event before firing a dragenter, dragleave, or dragover event. if (dragState().source && dragState().shouldDispatchEvents) { // for now we don't care if event handler cancels default behavior, since there is none dispatchDragSrcEvent(eventNames().dragEvent, event); } - accept = dispatchDragEvent(eventNames().dragenterEvent, *newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragenterEvent, *newTarget, event, &dataTransfer); if (!accept) - accept = findDropZone(newTarget.get(), clipboard); + accept = findDropZone(newTarget.get(), &dataTransfer); } if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard); + accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer); } else if (m_dragTarget) - dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer); if (newTarget) { // We do not explicitly call dispatchDragEvent here because it could ultimately result in the appearance that @@ -2189,46 +2324,50 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* Frame* targetFrame; if (targetIsFrame(newTarget.get(), targetFrame)) { if (targetFrame) - accept = targetFrame->eventHandler().updateDragAndDrop(event, clipboard); + accept = targetFrame->eventHandler().updateDragAndDrop(event, dataTransfer); } else if (newTarget) { // Note, when dealing with sub-frames, we may need to fire only a dragover event as a drag event may have been fired earlier. if (!m_shouldOnlyFireDragOverEvent && dragState().source && dragState().shouldDispatchEvents) { // for now we don't care if event handler cancels default behavior, since there is none dispatchDragSrcEvent(eventNames().dragEvent, event); } - accept = dispatchDragEvent(eventNames().dragoverEvent, *newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragoverEvent, *newTarget, event, &dataTransfer); if (!accept) - accept = findDropZone(newTarget.get(), clipboard); + accept = findDropZone(newTarget.get(), &dataTransfer); m_shouldOnlyFireDragOverEvent = false; } } - m_dragTarget = newTarget.release(); + m_dragTarget = WTFMove(newTarget); return accept; } -void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer) { + Ref protectedFrame(m_frame); + Frame* targetFrame; if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - targetFrame->eventHandler().cancelDragAndDrop(event, clipboard); + targetFrame->eventHandler().cancelDragAndDrop(event, dataTransfer); } else if (m_dragTarget) { if (dragState().source && dragState().shouldDispatchEvents) dispatchDragSrcEvent(eventNames().dragEvent, event); - dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, *m_dragTarget, event, &dataTransfer); } clearDragState(); } -bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard* clipboard) +bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, DataTransfer& dataTransfer) { + Ref protectedFrame(m_frame); + Frame* targetFrame; bool preventedDefault = false; if (targetIsFrame(m_dragTarget.get(), targetFrame)) { if (targetFrame) - preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, clipboard); + preventedDefault = targetFrame->eventHandler().performDragAndDrop(event, dataTransfer); } else if (m_dragTarget) - preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, clipboard); + preventedDefault = dispatchDragEvent(eventNames().dropEvent, *m_dragTarget, event, &dataTransfer); clearDragState(); return preventedDefault; } @@ -2239,44 +2378,53 @@ void EventHandler::clearDragState() m_dragTarget = nullptr; m_capturingMouseEventsElement = nullptr; m_shouldOnlyFireDragOverEvent = false; -#if PLATFORM(MAC) +#if PLATFORM(COCOA) m_sendingEventToSubview = false; #endif } + #endif // ENABLE(DRAG_SUPPORT) -void EventHandler::setCapturingMouseEventsElement(PassRefPtr element) +void EventHandler::setCapturingMouseEventsElement(Element* element) { m_capturingMouseEventsElement = element; m_eventHandlerWillResetCapturingMouseEventsElement = false; } -MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mev) +MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestRequest& request, const PlatformMouseEvent& mouseEvent) { + Ref protectedFrame(m_frame); ASSERT(m_frame.document()); - return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mev.position()), mev); + return m_frame.document()->prepareMouseEvent(request, documentPointForWindowPoint(m_frame, mouseEvent.position()), mouseEvent); } -#if ENABLE(SVG) -static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* referenceNode) +static RenderElement* nearestCommonHoverAncestor(RenderElement* obj1, RenderElement* obj2) { - if (!referenceNode || !referenceNode->isSVGElement()) - return 0; + if (!obj1 || !obj2) + return nullptr; - ShadowRoot* shadowRoot = referenceNode->containingShadowRoot(); - if (!shadowRoot) - return 0; + for (RenderElement* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) { + for (RenderElement* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) { + if (currObj1 == currObj2) + return currObj1; + } + } - Element* shadowTreeParentElement = shadowRoot->hostElement(); - if (!shadowTreeParentElement || !shadowTreeParentElement->hasTagName(useTag)) - return 0; + return nullptr; +} - return toSVGUseElement(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode); +static bool hierarchyHasCapturingEventListeners(Element* element, const AtomicString& eventName) +{ + for (ContainerNode* curr = element; curr; curr = curr->parentOrShadowHostNode()) { + if (curr->hasCapturingEventListeners(eventName)) + return true; + } + return false; } -#endif -void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut) +void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& platformMouseEvent, bool fireMouseOverOut) { + Ref protectedFrame(m_frame); Element* targetElement = nullptr; // If we're capturing, we always go right to that element. @@ -2284,52 +2432,17 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo targetElement = m_capturingMouseEventsElement.get(); else if (targetNode) { // If the target node is a non-element, dispatch on the parent. - if (!targetNode->isElementNode()) - targetElement = targetNode->parentOrShadowHostElement(); - else - targetElement = toElement(targetNode); + while (targetNode && !is(*targetNode)) + targetNode = targetNode->parentInComposedTree(); + targetElement = downcast(targetNode); } m_elementUnderMouse = targetElement; -#if ENABLE(SVG) - m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(targetElement); - - // shadow tree elements may have been recloned, update node under mouse in any case - if (m_lastInstanceUnderMouse) { - SVGElement* lastCorrespondingElement = m_lastInstanceUnderMouse->correspondingElement(); - SVGElement* lastCorrespondingUseElement = m_lastInstanceUnderMouse->correspondingUseElement(); - - if (lastCorrespondingElement && lastCorrespondingUseElement) { - HashSet instances = lastCorrespondingElement->instancesForElement(); - - // Locate the recloned shadow tree element for our corresponding instance - HashSet::iterator end = instances.end(); - for (HashSet::iterator it = instances.begin(); it != end; ++it) { - SVGElementInstance* instance = (*it); - ASSERT(instance->correspondingElement() == lastCorrespondingElement); - - if (instance == m_lastInstanceUnderMouse) - continue; - - if (instance->correspondingUseElement() != lastCorrespondingUseElement) - continue; - - SVGElement* shadowTreeElement = instance->shadowTreeElement(); - if (!shadowTreeElement->inDocument() || m_lastElementUnderMouse == shadowTreeElement) - continue; - - m_lastElementUnderMouse = shadowTreeElement; - m_lastInstanceUnderMouse = instance; - break; - } - } - } -#endif // Fire mouseout/mouseover if the mouse has shifted to a different node. if (fireMouseOverOut) { - RenderLayer* layerForLastNode = layerForNode(m_lastElementUnderMouse.get()); - RenderLayer* layerForNodeUnderMouse = layerForNode(m_elementUnderMouse.get()); + auto scrollableAreaForLastNode = enclosingScrollableArea(m_lastElementUnderMouse.get()); + auto scrollableAreaForNodeUnderMouse = enclosingScrollableArea(m_elementUnderMouse.get()); Page* page = m_frame.page(); if (m_lastElementUnderMouse && (!m_elementUnderMouse || &m_elementUnderMouse->document() != m_frame.document())) { @@ -2338,12 +2451,12 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo if (FrameView* frameView = frame->view()) frameView->mouseExitedContentArea(); } - } else if (page && (layerForLastNode && (!layerForNodeUnderMouse || layerForNodeUnderMouse != layerForLastNode))) { + } else if (page && (scrollableAreaForLastNode && (!scrollableAreaForNodeUnderMouse || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) { // The mouse has moved between layers. if (Frame* frame = m_lastElementUnderMouse->document().frame()) { if (FrameView* frameView = frame->view()) { - if (frameView->containsScrollableArea(layerForLastNode)) - layerForLastNode->mouseExitedContentArea(); + if (frameView->containsScrollableArea(scrollableAreaForLastNode)) + scrollableAreaForLastNode->mouseExitedContentArea(); } } } @@ -2354,12 +2467,12 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo if (FrameView* frameView = frame->view()) frameView->mouseEnteredContentArea(); } - } else if (page && (layerForNodeUnderMouse && (!layerForLastNode || layerForNodeUnderMouse != layerForLastNode))) { + } else if (page && (scrollableAreaForNodeUnderMouse && (!scrollableAreaForLastNode || scrollableAreaForNodeUnderMouse != scrollableAreaForLastNode))) { // The mouse has moved between layers. if (Frame* frame = m_elementUnderMouse->document().frame()) { if (FrameView* frameView = frame->view()) { - if (frameView->containsScrollableArea(layerForNodeUnderMouse)) - layerForNodeUnderMouse->mouseEnteredContentArea(); + if (frameView->containsScrollableArea(scrollableAreaForNodeUnderMouse)) + scrollableAreaForNodeUnderMouse->mouseEnteredContentArea(); } } } @@ -2367,96 +2480,129 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo if (m_lastElementUnderMouse && &m_lastElementUnderMouse->document() != m_frame.document()) { m_lastElementUnderMouse = nullptr; m_lastScrollbarUnderMouse = nullptr; -#if ENABLE(SVG) - m_lastInstanceUnderMouse = 0; -#endif } if (m_lastElementUnderMouse != m_elementUnderMouse) { - // send mouseout event to the old node + // mouseenter and mouseleave events are only dispatched if there is a capturing eventhandler on an ancestor + // or a normal eventhandler on the element itself (they don't bubble). + // This optimization is necessary since these events can cause O(n^2) capturing event-handler checks. + bool hasCapturingMouseEnterListener = hierarchyHasCapturingEventListeners(m_elementUnderMouse.get(), eventNames().mouseenterEvent); + bool hasCapturingMouseLeaveListener = hierarchyHasCapturingEventListeners(m_lastElementUnderMouse.get(), eventNames().mouseleaveEvent); + + RenderElement* oldHoverRenderer = m_lastElementUnderMouse ? m_lastElementUnderMouse->renderer() : nullptr; + RenderElement* newHoverRenderer = m_elementUnderMouse ? m_elementUnderMouse->renderer() : nullptr; + RenderElement* ancestor = nearestCommonHoverAncestor(oldHoverRenderer, newHoverRenderer); + + Vector, 32> leftElementsChain; + if (oldHoverRenderer) { + for (RenderElement* curr = oldHoverRenderer; curr && curr != ancestor; curr = curr->hoverAncestor()) { + if (Element* element = curr->element()) + leftElementsChain.append(*element); + } + } else { + // If the old hovered element is not null but it's renderer is, it was probably detached. + // In this case, the old hovered element (and its ancestors) must be updated, to ensure it's normal style is re-applied. + for (Element* element = m_lastElementUnderMouse.get(); element; element = element->parentElement()) + leftElementsChain.append(*element); + } + + Vector, 32> enteredElementsChain; + const Element* ancestorElement = ancestor ? ancestor->element() : nullptr; + for (RenderElement* curr = newHoverRenderer; curr; curr = curr->hoverAncestor()) { + if (Element *element = curr->element()) { + if (element == ancestorElement) + break; + enteredElementsChain.append(*element); + } + } + + // Send mouseout event to the old node. if (m_lastElementUnderMouse) - m_lastElementUnderMouse->dispatchMouseEvent(mouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get()); - // send mouseover event to the new node + m_lastElementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoutEvent, 0, m_elementUnderMouse.get()); + + // Send mouseleave to the node hierarchy no longer under the mouse. + for (auto& chain : leftElementsChain) { + if (hasCapturingMouseLeaveListener || chain->hasEventListeners(eventNames().mouseleaveEvent)) + chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseleaveEvent, 0, m_elementUnderMouse.get()); + } + + // Send mouseover event to the new node. if (m_elementUnderMouse) - m_elementUnderMouse->dispatchMouseEvent(mouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get()); + m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventNames().mouseoverEvent, 0, m_lastElementUnderMouse.get()); + + // Send mouseleave event to the nodes hierarchy under the mouse. + for (auto& chain : enteredElementsChain) { + if (hasCapturingMouseEnterListener || chain->hasEventListeners(eventNames().mouseenterEvent)) + chain->dispatchMouseEvent(platformMouseEvent, eventNames().mouseenterEvent, 0, m_lastElementUnderMouse.get()); + } } m_lastElementUnderMouse = m_elementUnderMouse; -#if ENABLE(SVG) - m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_elementUnderMouse.get()); -#endif } } -bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& mouseEvent, bool setUnder) +bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targetNode, bool /*cancelable*/, int clickCount, const PlatformMouseEvent& platformMouseEvent, bool setUnder) { - if (FrameView* view = m_frame.view()) + Ref protectedFrame(m_frame); + + if (auto* view = m_frame.view()) view->disableLayerFlushThrottlingTemporarilyForInteraction(); - updateMouseEventTargetNode(targetNode, mouseEvent, setUnder); + updateMouseEventTargetNode(targetNode, platformMouseEvent, setUnder); - bool swallowEvent = false; + if (m_elementUnderMouse && !m_elementUnderMouse->dispatchMouseEvent(platformMouseEvent, eventType, clickCount)) + return false; - if (m_elementUnderMouse) - swallowEvent = !(m_elementUnderMouse->dispatchMouseEvent(mouseEvent, eventType, clickCount)); + if (eventType != eventNames().mousedownEvent) + return true; - if (!swallowEvent && eventType == eventNames().mousedownEvent) { + // If clicking on a frame scrollbar, do not make any change to which element is focused. + auto* view = m_frame.view(); + if (view && view->scrollbarAtPoint(platformMouseEvent.position())) + return true; - // If clicking on a frame scrollbar, do not mess up with content focus. - if (FrameView* view = m_frame.view()) { - if (view->scrollbarAtPoint(mouseEvent.position())) - return true; - } + // The layout needs to be up to date to determine if an element is focusable. + m_frame.document()->updateLayoutIgnorePendingStylesheets(); - // The layout needs to be up to date to determine if an element is focusable. - m_frame.document()->updateLayoutIgnorePendingStylesheets(); - - // Blur current focus node when a link/button is clicked; this - // is expected by some sites that rely on onChange handlers running - // from form fields before the button click is processed. - - Element* element = m_elementUnderMouse.get(); - - // Walk up the DOM tree to search for an element to focus. - while (element) { - if (element->isMouseFocusable()) { - // To fix Can't drag selected ToDo, we don't focus a - // node on mouse down if it's selected and inside a focused node. It will be - // focused if the user does a mouseup over it, however, because the mouseup - // will set a selection inside it, which will call setFocuseNodeIfNeeded. - if (m_frame.selection().isRange() - && m_frame.selection().toNormalizedRange()->compareNode(element, IGNORE_EXCEPTION) == Range::NODE_INSIDE - && element->isDescendantOf(m_frame.document()->focusedElement())) - return true; - - break; - } - element = element->parentOrShadowHostElement(); - } + // Remove focus from the currently focused element when a link or button is clicked. + // This is expected by some sites that rely on change event handlers running + // from form fields before the button click is processed, behavior that was inherited + // from the user interface of Windows, where pushing a button moves focus to the button. - // Only change the focus when clicking scrollbars if it can transfered to a mouse focusable node. - if ((!element || !element->isMouseFocusable()) && isInsideScrollbar(mouseEvent.position())) - return false; + // Walk up the DOM tree to search for an element to focus. + Element* element; + for (element = m_elementUnderMouse.get(); element; element = element->parentOrShadowHostElement()) { + if (element->isMouseFocusable()) + break; + } - // If focus shift is blocked, we eat the event. Note we should never clear swallowEvent - // if the page already set it (e.g., by canceling default behavior). - if (Page* page = m_frame.page()) { - if (element && element->isMouseFocusable()) { - if (!page->focusController().setFocusedElement(element, &m_frame)) - swallowEvent = true; - } else if (!element || !element->focused()) { - if (!page->focusController().setFocusedElement(0, &m_frame)) - swallowEvent = true; - } + // To fix Can't drag selected ToDo, we don't focus an + // element on mouse down if it's selected and inside a focused element. It will be + // focused if the user does a mouseup over it, however, because the mouseup + // will set a selection inside it, which will also set the focused element. + if (element && m_frame.selection().isRange()) { + if (auto range = m_frame.selection().toNormalizedRange()) { + auto result = range->compareNode(*element); + if (!result.hasException() && result.releaseReturnValue() == Range::NODE_INSIDE && element->isDescendantOf(m_frame.document()->focusedElement())) + return true; } } - return !swallowEvent; + // Only change the focus when clicking scrollbars if it can be transferred to a mouse focusable node. + if (!element && isInsideScrollbar(platformMouseEvent.position())) + return false; + + // If focus shift is blocked, we eat the event. + auto* page = m_frame.page(); + if (page && !page->focusController().setFocusedElement(element, m_frame)) + return false; + + return true; } bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const { if (RenderView* renderView = m_frame.contentRenderer()) { - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(windowPoint); renderView->hitTest(request, result); return result.scrollbar(); @@ -2466,197 +2612,257 @@ bool EventHandler::isInsideScrollbar(const IntPoint& windowPoint) const } #if !PLATFORM(GTK) + bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult&, const PlatformWheelEvent&) const { return false; } + #endif -void EventHandler::recordWheelEventDelta(const PlatformWheelEvent& event) +#if !PLATFORM(MAC) + +void EventHandler::platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr&, RefPtr&, WeakPtr&, bool&) { - const size_t recentEventCount = 3; - - m_recentWheelEventDeltas.append(FloatSize(event.deltaX(), event.deltaY())); - if (m_recentWheelEventDeltas.size() > recentEventCount) - m_recentWheelEventDeltas.removeFirst(); } -static bool deltaIsPredominantlyVertical(const FloatSize& delta) +void EventHandler::platformRecordWheelEvent(const PlatformWheelEvent& event) { - return fabs(delta.height()) > fabs(delta.width()); + m_frame.mainFrame().wheelEventDeltaFilter()->updateFromDelta(FloatSize(event.deltaX(), event.deltaY())); } -EventHandler::DominantScrollGestureDirection EventHandler::dominantScrollGestureDirection() const +bool EventHandler::platformCompleteWheelEvent(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr&) { - bool allVertical = m_recentWheelEventDeltas.size(); - bool allHorizontal = m_recentWheelEventDeltas.size(); + Ref protectedFrame(m_frame); - Deque::const_iterator end = m_recentWheelEventDeltas.end(); - for (Deque::const_iterator it = m_recentWheelEventDeltas.begin(); it != end; ++it) { - bool isVertical = deltaIsPredominantlyVertical(*it); - allVertical &= isVertical; - allHorizontal &= !isVertical; - } + // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed. + FrameView* view = m_frame.view(); - if (allVertical) - return DominantScrollDirectionVertical; + bool didHandleEvent = view ? view->wheelEvent(event) : false; + m_isHandlingWheelEvent = false; + return didHandleEvent; +} - if (allHorizontal) - return DominantScrollDirectionHorizontal; - - return DominantScrollDirectionNone; +bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*) +{ + return true; } -bool EventHandler::handleWheelEvent(const PlatformWheelEvent& e) +void EventHandler::platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr&) { - Document* document = m_frame.document(); +} + +IntPoint EventHandler::effectiveMousePositionForSelectionAutoscroll() const +{ + return m_lastKnownMousePosition; +} - if (!document->renderView()) +void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&) +{ + clearLatchedState(); +} +#endif + +Widget* EventHandler::widgetForEventTarget(Element* eventTarget) +{ + if (!eventTarget) + return nullptr; + + auto* target = eventTarget->renderer(); + if (!is(target)) + return nullptr; + + return downcast(*target).widget(); +} + +static WeakPtr widgetForElement(const Element& element) +{ + auto target = element.renderer(); + if (!is(target) || !downcast(*target).widget()) + return { }; + + return downcast(*target).widget()->createWeakPtr(); +} + +bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr& widget, const WeakPtr& scrollableArea, ContainerNode* scrollableContainer) +{ + m_isHandlingWheelEvent = false; + + // We do another check on the widget because the event handler can run JS which results in the frame getting destroyed. + if (!widget) return false; + if (scrollableArea) + scrollableArea->setScrolledProgrammatically(false); + + platformNotifyIfEndGesture(event, scrollableArea); + + if (!widget->platformWidget()) + return true; + + return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer); +} + +bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event) +{ + RenderView* renderView = m_frame.contentRenderer(); + if (!renderView) + return false; + + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); FrameView* view = m_frame.view(); if (!view) return false; +#if ENABLE(POINTER_LOCK) + if (m_frame.page()->pointerLockController().isLocked()) { + m_frame.page()->pointerLockController().dispatchLockedWheelEvent(event); + return true; + } +#endif + m_isHandlingWheelEvent = true; setFrameWasScrolledByUser(); - LayoutPoint vPoint = view->windowToContents(e.position()); - - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); - HitTestResult result(vPoint); - document->renderView()->hitTest(request, result); - - bool useLatchedWheelEventElement = e.useLatchedEventElement(); - - Element* element = result.innerElement(); - - bool isOverWidget; - if (useLatchedWheelEventElement) { - if (!m_latchedWheelEventElement) { - m_latchedWheelEventElement = element; - m_widgetIsLatched = result.isOverWidget(); - } else - element = m_latchedWheelEventElement.get(); - isOverWidget = m_widgetIsLatched; - } else { - if (m_latchedWheelEventElement) - m_latchedWheelEventElement = nullptr; - if (m_previousWheelScrolledElement) - m_previousWheelScrolledElement = nullptr; - - isOverWidget = result.isOverWidget(); - } + HitTestRequest request; + HitTestResult result(view->windowToContents(event.position())); + renderView->hitTest(request, result); - // FIXME: It should not be necessary to do this mutation here. - // Instead, the handlers should know convert vertical scrolls - // appropriately. - PlatformWheelEvent event = e; - if (m_baseEventType == PlatformEvent::NoType && shouldTurnVerticalTicksIntoHorizontal(result, e)) - event = event.copyTurningVerticalTicksIntoHorizontalTicks(); + RefPtr element = result.targetElement(); + RefPtr scrollableContainer; + WeakPtr scrollableArea; + bool isOverWidget = result.isOverWidget(); + platformPrepareForWheelEvents(event, result, element, scrollableContainer, scrollableArea, isOverWidget); #if PLATFORM(MAC) - switch (event.phase()) { - case PlatformWheelEventPhaseBegan: - m_recentWheelEventDeltas.clear(); - m_inTrackingScrollGesturePhase = true; - break; - case PlatformWheelEventPhaseEnded: - m_inTrackingScrollGesturePhase = false; - break; - default: - break; - } + if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone) + m_frame.mainFrame().resetLatchingState(); #endif - recordWheelEventDelta(event); + // FIXME: It should not be necessary to do this mutation here. + // Instead, the handlers should know convert vertical scrolls appropriately. + PlatformWheelEvent adjustedEvent = event; + if (shouldTurnVerticalTicksIntoHorizontal(result, event)) + adjustedEvent = event.copyTurningVerticalTicksIntoHorizontalTicks(); + + platformRecordWheelEvent(adjustedEvent); if (element) { - // Figure out which view to send the event to. - RenderElement* target = element->renderer(); - - if (isOverWidget && target && target->isWidget()) { - Widget* widget = toRenderWidget(target)->widget(); - if (widget && passWheelEventToWidget(e, widget)) { - m_isHandlingWheelEvent = false; - return true; + if (isOverWidget) { + if (WeakPtr widget = widgetForElement(*element)) { + if (widgetDidHandleWheelEvent(event, *widget.get())) + return completeWidgetWheelEvent(adjustedEvent, widget, scrollableArea, scrollableContainer.get()); } } - if (!element->dispatchWheelEvent(event)) { + if (!element->dispatchWheelEvent(adjustedEvent)) { m_isHandlingWheelEvent = false; + if (scrollableArea && scrollableArea->isScrolledProgrammatically()) { + // Web developer is controlling scrolling, so don't attempt to latch. + clearLatchedState(); + scrollableArea->setScrolledProgrammatically(false); + } + + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); return true; } } + if (scrollableArea) + scrollableArea->setScrolledProgrammatically(false); - // We do another check on the frame view because the event handler can run JS which results in the frame getting destroyed. - view = m_frame.view(); - bool didHandleEvent = view ? view->wheelEvent(event) : false; - m_isHandlingWheelEvent = false; - return didHandleEvent; + bool handledEvent = platformCompleteWheelEvent(adjustedEvent, scrollableContainer.get(), scrollableArea); + platformNotifyIfEndGesture(adjustedEvent, scrollableArea); + return handledEvent; } -void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent) +void EventHandler::clearLatchedState() { - if (!startNode || !wheelEvent) +#if PLATFORM(MAC) + m_frame.mainFrame().resetLatchingState(); +#endif + if (auto filter = m_frame.mainFrame().wheelEventDeltaFilter()) + filter->endFilteringDeltas(); +} + +void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent) +{ + if (!startNode) return; - Element* stopElement = m_previousWheelScrolledElement.get(); - ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->deltaMode()); + Ref protectedFrame(m_frame); + + FloatSize filteredPlatformDelta(wheelEvent.deltaX(), wheelEvent.deltaY()); + FloatSize filteredVelocity; + if (const PlatformWheelEvent* platformWheelEvent = wheelEvent.wheelEvent()) { + filteredPlatformDelta.setWidth(platformWheelEvent->deltaX()); + filteredPlatformDelta.setHeight(platformWheelEvent->deltaY()); + } - DominantScrollGestureDirection dominantDirection = DominantScrollDirectionNone; - // Workaround for scrolling issues in iTunes (). - if (m_inTrackingScrollGesturePhase && applicationIsITunes()) - dominantDirection = dominantScrollGestureDirection(); +#if PLATFORM(MAC) + ScrollLatchingState* latchedState = m_frame.mainFrame().latchingState(); + Element* stopElement = latchedState ? latchedState->previousWheelScrolledElement() : nullptr; + + if (m_frame.mainFrame().wheelEventDeltaFilter()->isFilteringDeltas()) { + filteredPlatformDelta = m_frame.mainFrame().wheelEventDeltaFilter()->filteredDelta(); + filteredVelocity = m_frame.mainFrame().wheelEventDeltaFilter()->filteredVelocity(); + } +#else + Element* stopElement = nullptr; +#endif - // Break up into two scrolls if we need to. Diagonal movement on - // a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set). - if (dominantDirection != DominantScrollDirectionVertical && scrollNode(wheelEvent->deltaX(), granularity, ScrollRight, ScrollLeft, startNode, &stopElement, roundedIntPoint(wheelEvent->absoluteLocation()))) - wheelEvent->setDefaultHandled(); - if (dominantDirection != DominantScrollDirectionHorizontal && scrollNode(wheelEvent->deltaY(), granularity, ScrollDown, ScrollUp, startNode, &stopElement, roundedIntPoint(wheelEvent->absoluteLocation()))) - wheelEvent->setDefaultHandled(); + if (handleWheelEventInAppropriateEnclosingBox(startNode, wheelEvent, &stopElement, filteredPlatformDelta, filteredVelocity)) + wheelEvent.setDefaultHandled(); - if (!m_latchedWheelEventElement) - m_previousWheelScrolledElement = stopElement; +#if PLATFORM(MAC) + if (latchedState && !latchedState->wheelEventElement()) + latchedState->setPreviousWheelScrolledElement(stopElement); +#endif } #if ENABLE(CONTEXT_MENUS) bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) { + Ref protectedFrame(m_frame); + Document* doc = m_frame.document(); - FrameView* v = m_frame.view(); - if (!v) + FrameView* view = m_frame.view(); + if (!view) return false; - + // Clear mouse press state to avoid initiating a drag while context menu is up. m_mousePressed = false; bool swallowEvent; - LayoutPoint viewportPos = v->windowToContents(event.position()); - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = doc->prepareMouseEvent(request, viewportPos, event); + LayoutPoint viewportPos = view->windowToContents(event.position()); + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = doc->prepareMouseEvent(request, viewportPos, event); + + // Do not show context menus when clicking on scrollbars. + if (mouseEvent.scrollbar() || view->scrollbarAtPoint(event.position())) + return false; if (m_frame.editor().behavior().shouldSelectOnContextualMenuClick() && !m_frame.selection().contains(viewportPos) - && !mev.scrollbar() // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse. // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items // available for text selections. But only if we're above text. - && (m_frame.selection().isContentEditable() || (mev.targetNode() && mev.targetNode()->isTextNode()))) { + && (m_frame.selection().selection().isContentEditable() || (mouseEvent.targetNode() && mouseEvent.targetNode()->isTextNode()))) { m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection - selectClosestWordOrLinkFromMouseEvent(mev); + selectClosestContextualWordOrLinkFromMouseEvent(mouseEvent); } - swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mev.targetNode(), true, 0, event, false); + swallowEvent = !dispatchMouseEvent(eventNames().contextmenuEvent, mouseEvent.targetNode(), true, 0, event, false); return swallowEvent; } bool EventHandler::sendContextMenuEventForKey() { + Ref protectedFrame(m_frame); + FrameView* view = m_frame.view(); if (!view) return false; @@ -2670,7 +2876,7 @@ bool EventHandler::sendContextMenuEventForKey() static const int kContextMenuMargin = 1; -#if OS(WINDOWS) && !OS(WINCE) +#if OS(WINDOWS) int rightAligned = ::GetSystemMetrics(SM_MENUDROPALIGNMENT); #else int rightAligned = 0; @@ -2678,8 +2884,8 @@ bool EventHandler::sendContextMenuEventForKey() IntPoint location; Element* focusedElement = doc->focusedElement(); - FrameSelection& selection = m_frame.selection(); - Position start = selection.selection().start(); + const VisibleSelection& selection = m_frame.selection().selection(); + Position start = selection.start(); if (start.deprecatedNode() && (selection.rootEditableElement() || selection.isRange())) { RefPtr selectionRange = selection.toNormalizedRange(); @@ -2693,8 +2899,9 @@ bool EventHandler::sendContextMenuEventForKey() RenderBoxModelObject* box = focusedElement->renderBoxModelObject(); if (!box) return false; - IntRect clippedRect = box->pixelSnappedAbsoluteClippedOverflowRect(); - location = IntPoint(clippedRect.x(), clippedRect.maxY() - 1); + + IntRect boundingBoxRect = box->absoluteBoundingBoxRect(true); + location = IntPoint(boundingBoxRect.x(), boundingBoxRect.maxY() - 1); } else { location = IntPoint( rightAligned ? view->contentsWidth() - kContextMenuMargin : kContextMenuMargin, @@ -2713,7 +2920,7 @@ bool EventHandler::sendContextMenuEventForKey() // Use the focused node as the target for hover and active. HitTestResult result(position); result.setInnerNode(targetNode); - doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowShadowContent, result.innerElement()); + doc->updateHoverActiveState(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, result.targetElement()); // The contextmenu event is a mouse event even when invoked using the keyboard. // This is required for web compatibility. @@ -2724,9 +2931,9 @@ bool EventHandler::sendContextMenuEventForKey() PlatformEvent::Type eventType = PlatformEvent::MousePressed; #endif - PlatformMouseEvent mouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime()); + PlatformMouseEvent platformMouseEvent(position, globalPosition, RightButton, eventType, 1, false, false, false, false, WTF::currentTime(), ForceAtClick, NoTap); - return !dispatchMouseEvent(eventNames().contextmenuEvent, targetNode, true, 0, mouseEvent, false); + return sendContextMenuEvent(platformMouseEvent); } #endif // ENABLE(CONTEXT_MENUS) @@ -2746,31 +2953,33 @@ void EventHandler::scheduleCursorUpdate() void EventHandler::dispatchFakeMouseMoveEventSoon() { +#if !ENABLE(IOS_TOUCH_EVENTS) if (m_mousePressed) return; if (m_mousePositionIsUnknown) return; - if (!m_frame.settings().deviceSupportsMouse()) - return; + if (Page* page = m_frame.page()) { + if (!page->chrome().client().shouldDispatchFakeMouseMoveEvents()) + return; + } // If the content has ever taken longer than fakeMouseMoveShortInterval we // reschedule the timer and use a longer time. This will cause the content // to receive these moves only after the user is done scrolling, reducing // pauses during the scroll. - if (m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold) { - if (m_fakeMouseMoveEventTimer.isActive()) - m_fakeMouseMoveEventTimer.stop(); - m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveLongInterval); - } else { - if (!m_fakeMouseMoveEventTimer.isActive()) - m_fakeMouseMoveEventTimer.startOneShot(fakeMouseMoveShortInterval); - } + if (m_fakeMouseMoveEventTimer.isActive()) + m_fakeMouseMoveEventTimer.stop(); + m_fakeMouseMoveEventTimer.startOneShot(m_maxMouseMovedDuration > fakeMouseMoveDurationThreshold ? fakeMouseMoveLongInterval : fakeMouseMoveShortInterval); +#endif } void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) { +#if ENABLE(IOS_TOUCH_EVENTS) + UNUSED_PARAM(quad); +#else FrameView* view = m_frame.view(); if (!view) return; @@ -2779,21 +2988,19 @@ void EventHandler::dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad& quad) return; dispatchFakeMouseMoveEventSoon(); +#endif } +#if !ENABLE(IOS_TOUCH_EVENTS) void EventHandler::cancelFakeMouseMoveEvent() { m_fakeMouseMoveEventTimer.stop(); } -void EventHandler::fakeMouseMoveEventTimerFired(Timer& timer) +void EventHandler::fakeMouseMoveEventTimerFired() { - ASSERT_UNUSED(timer, &timer == &m_fakeMouseMoveEventTimer); ASSERT(!m_mousePressed); - if (!m_frame.settings().deviceSupportsMouse()) - return; - FrameView* view = m_frame.view(); if (!view) return; @@ -2806,9 +3013,10 @@ void EventHandler::fakeMouseMoveEventTimerFired(Timer& timer) bool altKey; bool metaKey; PlatformKeyboardEvent::getCurrentModifierState(shiftKey, ctrlKey, altKey, metaKey); - PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime()); + PlatformMouseEvent fakeMouseMoveEvent(m_lastKnownMousePosition, m_lastKnownMouseGlobalPosition, NoButton, PlatformEvent::MouseMoved, 0, shiftKey, ctrlKey, altKey, metaKey, currentTime(), 0, NoTap); mouseMoved(fakeMouseMoveEvent); } +#endif // !ENABLE(IOS_TOUCH_EVENTS) void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet) { @@ -2818,43 +3026,45 @@ void EventHandler::setResizingFrameSet(HTMLFrameSetElement* frameSet) void EventHandler::resizeLayerDestroyed() { ASSERT(m_resizeLayer); - m_resizeLayer = 0; + m_resizeLayer = nullptr; } -void EventHandler::hoverTimerFired(Timer&) +void EventHandler::hoverTimerFired() { m_hoverTimer.stop(); ASSERT(m_frame.document()); - if (RenderView* renderer = m_frame.contentRenderer()) { + Ref protectedFrame(m_frame); + + if (RenderView* renderView = m_frame.contentRenderer()) { if (FrameView* view = m_frame.view()) { - HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::Move | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(view->windowToContents(m_lastKnownMousePosition)); - renderer->hitTest(request, result); - m_frame.document()->updateHoverActiveState(request, result.innerElement()); + renderView->hitTest(request, result); + m_frame.document()->updateHoverActiveState(request, result.targetElement()); } } } -bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt) +bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& event) { // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do. // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and // lower case variants are present in a document, the correct element is matched based on Shift key state. // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively. - ASSERT(!(accessKeyModifiers() & PlatformEvent::ShiftKey)); - if ((evt.modifiers() & ~PlatformEvent::ShiftKey) != accessKeyModifiers()) + ASSERT(!accessKeyModifiers().contains(PlatformEvent::Modifier::ShiftKey)); + + if ((event.modifiers() - PlatformEvent::Modifier::ShiftKey) != accessKeyModifiers()) return false; - String key = evt.unmodifiedText(); - Element* elem = m_frame.document()->getElementByAccessKey(key.lower()); - if (!elem) + Element* element = m_frame.document()->getElementByAccessKey(event.unmodifiedText()); + if (!element) return false; - elem->accessKeyAction(false); + element->accessKeyAction(false); return true; } -#if !PLATFORM(MAC) || PLATFORM(IOS) +#if !PLATFORM(MAC) bool EventHandler::needsKeyboardEventDisambiguationQuirks() const { return false; @@ -2883,17 +3093,57 @@ bool EventHandler::isKeyEventAllowedInFullScreen(const PlatformKeyboardEvent& ke } #endif -bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) +bool EventHandler::keyEvent(const PlatformKeyboardEvent& keyEvent) +{ + Document* topDocument = m_frame.document() ? &m_frame.document()->topDocument() : nullptr; + bool savedUserDidInteractWithPage = topDocument ? topDocument->userDidInteractWithPage() : false; + bool wasHandled = internalKeyEvent(keyEvent); + + // If the key event was not handled, do not treat it as user interaction with the page. + if (topDocument && !wasHandled) + topDocument->setUserDidInteractWithPage(savedUserDidInteractWithPage); + + return wasHandled; +} + +bool EventHandler::internalKeyEvent(const PlatformKeyboardEvent& initialKeyEvent) { + Ref protectedFrame(m_frame); RefPtr protector(m_frame.view()); + LOG(Editing, "EventHandler %p keyEvent (text %s keyIdentifier %s)", this, initialKeyEvent.text().utf8().data(), initialKeyEvent.keyIdentifier().utf8().data()); + +#if ENABLE(POINTER_LOCK) + if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE && m_frame.page()->pointerLockController().element()) { + m_frame.page()->pointerLockController().requestPointerUnlockAndForceCursorVisible(); + } +#endif + + if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) { + if (auto* page = m_frame.page()) { + if (auto* validationMessageClient = page->validationMessageClient()) + validationMessageClient->hideAnyValidationMessage(); + } + } + #if ENABLE(FULLSCREEN_API) - if (m_frame.document()->webkitIsFullScreen() && !isKeyEventAllowedInFullScreen(initialKeyEvent)) - return false; + if (m_frame.document()->webkitIsFullScreen()) { + if (initialKeyEvent.type() == PlatformEvent::KeyDown && initialKeyEvent.windowsVirtualKeyCode() == VK_ESCAPE) { + m_frame.document()->webkitCancelFullScreen(); + return true; + } + + if (!isKeyEventAllowedInFullScreen(initialKeyEvent)) + return false; + } #endif - if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) - capsLockStateMayHaveChanged(); + if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) { + if (auto* element = m_frame.document()->focusedElement()) { + if (is(*element)) + downcast(*element).capsLockStateMayHaveChanged(); + } + } #if ENABLE(PAN_SCROLLING) if (m_frame.mainFrame().eventHandler().panScrollInProgress()) { @@ -2912,7 +3162,7 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) if (!element) return false; - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); UserTypingGestureIndicator typingGestureIndicator(m_frame); if (FrameView* view = m_frame.view()) @@ -2939,13 +3189,13 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) PlatformKeyboardEvent keyDownEvent = initialKeyEvent; if (keyDownEvent.type() != PlatformEvent::RawKeyDown) keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown, backwardCompatibilityMode); - RefPtr keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView()); + Ref keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView()); if (matchedAnAccessKey) keydown->setDefaultPrevented(true); keydown->setTarget(element); if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) { - element->dispatchEvent(keydown, IGNORE_EXCEPTION); + element->dispatchEvent(keydown); // If frame changed as a result of keydown dispatch, then return true to avoid sending a subsequent keypress message to the new frame. bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); return keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; @@ -2966,8 +3216,11 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keydown->setTarget(element); keydown->setDefaultHandled(); } + + if (accessibilityPreventsEventPropogation(keydown)) + keydown->stopPropagation(); - element->dispatchEvent(keydown, IGNORE_EXCEPTION); + element->dispatchEvent(keydown); // If frame changed as a result of keydown dispatch, then return early to avoid sending a subsequent keypress message to the new frame. bool changedFocusedFrame = m_frame.page() && &m_frame != &m_frame.page()->focusController().focusedOrMainFrame(); bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented() || changedFocusedFrame; @@ -2986,24 +3239,24 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char, backwardCompatibilityMode); if (keyPressEvent.text().isEmpty()) return keydownResult; - RefPtr keypress = KeyboardEvent::create(keyPressEvent, m_frame.document()->defaultView()); + Ref keypress = KeyboardEvent::create(keyPressEvent, m_frame.document()->defaultView()); keypress->setTarget(element); if (keydownResult) keypress->setDefaultPrevented(true); -#if PLATFORM(MAC) +#if PLATFORM(COCOA) keypress->keypressCommands() = keydown->keypressCommands(); #endif - element->dispatchEvent(keypress, IGNORE_EXCEPTION); + element->dispatchEvent(keypress); return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled(); } static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier) { - DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left", AtomicString::ConstructFromLiteral)); - DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right", AtomicString::ConstructFromLiteral)); + static NeverDestroyed Down("Down", AtomicString::ConstructFromLiteral); + static NeverDestroyed Up("Up", AtomicString::ConstructFromLiteral); + static NeverDestroyed Left("Left", AtomicString::ConstructFromLiteral); + static NeverDestroyed Right("Right", AtomicString::ConstructFromLiteral); FocusDirection retVal = FocusDirectionNone; @@ -3019,18 +3272,54 @@ static FocusDirection focusDirectionForKey(const AtomicString& keyIdentifier) return retVal; } -static void handleKeyboardSelectionMovement(FrameSelection& selection, KeyboardEvent* event) +static void setInitialKeyboardSelection(Frame& frame, SelectionDirection direction) { - if (!event) + Document* document = frame.document(); + if (!document) + return; + + FrameSelection& selection = frame.selection(); + + if (!selection.isNone()) return; - bool isOptioned = event->getModifierState("Alt"); - bool isCommanded = event->getModifierState("Meta"); + Element* focusedElement = document->focusedElement(); + VisiblePosition visiblePosition; + + switch (direction) { + case DirectionBackward: + case DirectionLeft: + if (focusedElement) + visiblePosition = VisiblePosition(positionBeforeNode(focusedElement)); + else + visiblePosition = endOfDocument(document); + break; + case DirectionForward: + case DirectionRight: + if (focusedElement) + visiblePosition = VisiblePosition(positionAfterNode(focusedElement)); + else + visiblePosition = startOfDocument(document); + break; + } + + AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false }); + selection.setSelection(visiblePosition, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent); +} +static void handleKeyboardSelectionMovement(Frame& frame, KeyboardEvent& event) +{ + FrameSelection& selection = frame.selection(); + + bool isCommanded = event.getModifierState("Meta"); + bool isOptioned = event.getModifierState("Alt"); + bool isSelection = !selection.isNone(); + + FrameSelection::EAlteration alternation = event.getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove; SelectionDirection direction = DirectionForward; TextGranularity granularity = CharacterGranularity; - switch (focusDirectionForKey(event->keyIdentifier())) { + switch (focusDirectionForKey(event.keyIdentifier())) { case FocusDirectionNone: return; case FocusDirectionForward: @@ -3055,42 +3344,68 @@ static void handleKeyboardSelectionMovement(FrameSelection& selection, KeyboardE break; } - FrameSelection::EAlteration alternation = event->getModifierState("Shift") ? FrameSelection::AlterationExtend : FrameSelection::AlterationMove; - selection.modify(alternation, direction, granularity, UserTriggered); - event->setDefaultHandled(); + if (isSelection) + selection.modify(alternation, direction, granularity, UserTriggered); + else + setInitialKeyboardSelection(frame, direction); + + event.setDefaultHandled(); } -void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent* event) +void EventHandler::handleKeyboardSelectionMovementForAccessibility(KeyboardEvent& event) { - if (event->type() == eventNames().keydownEvent) { + if (event.type() == eventNames().keydownEvent) { if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) - handleKeyboardSelectionMovement(m_frame.selection(), event); + handleKeyboardSelectionMovement(m_frame, event); } } -void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event) +bool EventHandler::accessibilityPreventsEventPropogation(KeyboardEvent& event) { - if (event->type() == eventNames().keydownEvent) { +#if PLATFORM(COCOA) + if (!AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) + return false; + + if (!m_frame.settings().preventKeyboardDOMEventDispatch()) + return false; + + // Check for key events that are relevant to accessibility: tab and arrows keys that change focus + if (event.keyIdentifier() == "U+0009") + return true; + FocusDirection direction = focusDirectionForKey(event.keyIdentifier()); + if (direction != FocusDirectionNone) + return true; +#else + UNUSED_PARAM(event); +#endif + return false; +} + +void EventHandler::defaultKeyboardEventHandler(KeyboardEvent& event) +{ + Ref protectedFrame(m_frame); + + if (event.type() == eventNames().keydownEvent) { m_frame.editor().handleKeyboardEvent(event); - if (event->defaultHandled()) + if (event.defaultHandled()) return; - if (event->keyIdentifier() == "U+0009") + if (event.keyIdentifier() == "U+0009") defaultTabEventHandler(event); - else if (event->keyIdentifier() == "U+0008") + else if (event.keyIdentifier() == "U+0008") defaultBackspaceEventHandler(event); else { - FocusDirection direction = focusDirectionForKey(event->keyIdentifier()); + FocusDirection direction = focusDirectionForKey(event.keyIdentifier()); if (direction != FocusDirectionNone) defaultArrowEventHandler(direction, event); } handleKeyboardSelectionMovementForAccessibility(event); } - if (event->type() == eventNames().keypressEvent) { + if (event.type() == eventNames().keypressEvent) { m_frame.editor().handleKeyboardEvent(event); - if (event->defaultHandled()) + if (event.defaultHandled()) return; - if (event->charCode() == ' ') + if (event.charCode() == ' ') defaultSpaceEventHandler(event); } } @@ -3104,18 +3419,15 @@ bool EventHandler::dragHysteresisExceeded(const IntPoint& floatDragViewportLocat bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation) const { - FrameView* view = m_frame.view(); - if (!view) - return false; - IntPoint dragLocation = view->windowToContents(flooredIntPoint(dragViewportLocation)); - IntSize delta = dragLocation - m_mouseDownPos; - int threshold = GeneralDragHysteresis; switch (dragState().type) { case DragSourceActionSelection: threshold = TextDragHysteresis; break; case DragSourceActionImage: +#if ENABLE(ATTACHMENT_ELEMENT) + case DragSourceActionAttachment: +#endif threshold = ImageDragHysteresis; break; case DragSourceActionLink: @@ -3128,46 +3440,46 @@ bool EventHandler::dragHysteresisExceeded(const FloatPoint& dragViewportLocation ASSERT_NOT_REACHED(); } - return abs(delta.width()) >= threshold || abs(delta.height()) >= threshold; + return mouseMovementExceedsThreshold(dragViewportLocation, threshold); } -void EventHandler::freeClipboard() +void EventHandler::invalidateDataTransfer() { - if (!dragState().clipboard) + if (!dragState().dataTransfer) return; - dragState().clipboard->setAccessPolicy(ClipboardNumb); - dragState().clipboard = 0; + dragState().dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb); + dragState().dataTransfer = nullptr; } void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation) { // Send a hit test request so that RenderLayer gets a chance to update the :hover and :active pseudoclasses. - HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::Release | HitTestRequest::DisallowUserAgentShadowContent); prepareMouseEvent(request, event); if (dragState().source && dragState().shouldDispatchEvents) { - dragState().clipboard->setDestinationOperation(operation); + dragState().dataTransfer->setDestinationOperation(operation); // For now we don't care if event handler cancels default behavior, since there is no default behavior. dispatchDragSrcEvent(eventNames().dragendEvent, event); } - freeClipboard(); - dragState().source = 0; + invalidateDataTransfer(); + dragState().source = nullptr; // In case the drag was ended due to an escape key press we need to ensure // that consecutive mousemove events don't reinitiate the drag and drop. m_mouseDownMayStartDrag = false; } -void EventHandler::updateDragStateAfterEditDragIfNeeded(Element* rootEditableElement) +void EventHandler::updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement) { // If inserting the dragged contents removed the drag source, we still want to fire dragend at the root editable element. - if (dragState().source && !dragState().source->inDocument()) - dragState().source = rootEditableElement; + if (dragState().source && !dragState().source->isConnected()) + dragState().source = &rootEditableElement; } // Return value indicates if we should continue "default processing", i.e., whether event handler canceled. bool EventHandler::dispatchDragSrcEvent(const AtomicString& eventType, const PlatformMouseEvent& event) { - return !dispatchDragEvent(eventType, *dragState().source, event, dragState().clipboard.get()); + return !dispatchDragEvent(eventType, *dragState().source, event, dragState().dataTransfer.get()); } static bool ExactlyOneBitSet(DragSourceAction n) @@ -3186,6 +3498,8 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr return false; } + Ref protectedFrame(m_frame); + if (eventLoopHandleMouseDragged(event)) return true; @@ -3195,11 +3509,11 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr dragState().shouldDispatchEvents = (updateDragSourceActionsAllowed() & DragSourceActionDHTML); // try to find an element that wants to be dragged - HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent); + HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowUserAgentShadowContent); HitTestResult result(m_mouseDownPos); m_frame.contentRenderer()->hitTest(request, result); if (m_frame.page()) - dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.innerElement(), m_mouseDownPos, dragState()); + dragState().source = m_frame.page()->dragController().draggableElement(&m_frame, result.targetElement(), m_mouseDownPos, dragState()); if (!dragState().source) m_mouseDownMayStartDrag = false; // no element is draggable @@ -3217,7 +3531,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr } else if (!(dragState().type & (DragSourceActionDHTML | DragSourceActionLink))) { // ... but only bail if we're not over an unselectable element. m_mouseDownMayStartDrag = false; - dragState().source = 0; + dragState().source = nullptr; // ... but if this was the first click in the window, we don't even want to start selection if (eventActivatedView(event.event())) m_mouseDownMayStartSelect = false; @@ -3236,9 +3550,16 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr if (!ExactlyOneBitSet(dragState().type)) { ASSERT((dragState().type & DragSourceActionSelection)); +#if ENABLE(ATTACHMENT_ELEMENT) + ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionAttachment + || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink); +#else ASSERT((dragState().type & ~DragSourceActionSelection) == DragSourceActionDHTML || (dragState().type & ~DragSourceActionSelection) == DragSourceActionImage || (dragState().type & ~DragSourceActionSelection) == DragSourceActionLink); +#endif dragState().type = DragSourceActionSelection; } @@ -3256,10 +3577,10 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr DragOperation srcOp = DragOperationNone; - // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old clipboard gets numbed. - freeClipboard(); + // This does work only if we missed a dragEnd. Do it anyway, just to make sure the old dataTransfer gets numbed. + invalidateDataTransfer(); - dragState().clipboard = createDraggingClipboard(); + dragState().dataTransfer = createDraggingDataTransfer(); if (dragState().shouldDispatchEvents) { // Check to see if the is a DOM based drag. If it is, get the DOM specified drag image and offset. @@ -3268,30 +3589,33 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr // FIXME: This doesn't work correctly with transforms. FloatPoint absPos = renderer->localToAbsolute(); IntSize delta = m_mouseDownPos - roundedIntPoint(absPos); - dragState().clipboard->setDragImage(dragState().source.get(), delta.width(), delta.height()); + dragState().dataTransfer->setDragImage(dragState().source.get(), delta.width(), delta.height()); } else { // The renderer has disappeared, this can happen if the onStartDrag handler has hidden // the element in some way. In this case we just kill the drag. m_mouseDownMayStartDrag = false; - goto cleanupDrag; + invalidateDataTransfer(); + dragState().source = nullptr; + + return true; } } m_mouseDownMayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown) - && !m_frame.selection().isInPasswordField(); + && !m_frame.selection().selection().isInPasswordField(); - // Invalidate clipboard here against anymore pasteboard writing for security. The drag + // Invalidate dataTransfer here against anymore pasteboard writing for security. The drag // image can still be changed as we drag, but not the pasteboard data. - dragState().clipboard->setAccessPolicy(ClipboardImageWritable); + dragState().dataTransfer->setAccessPolicy(DataTransferAccessPolicy::ImageWritable); if (m_mouseDownMayStartDrag) { // Gather values from DHTML element, if it set any. - srcOp = dragState().clipboard->sourceOperation(); + srcOp = dragState().dataTransfer->sourceOperation(); // Yuck, a draggedImage:moveTo: message can be fired as a result of kicking off the // drag with dragImage! Because of that dumb reentrancy, we may think we've not // started the drag when that happens. So we have to assume it's started before we kick it off. - dragState().clipboard->setDragHasStarted(); + dragState().dataTransfer->setDragHasStarted(); } } @@ -3311,23 +3635,37 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event, CheckDr } } -cleanupDrag: if (!m_mouseDownMayStartDrag) { // Something failed to start the drag, clean up. - freeClipboard(); - dragState().source = 0; + invalidateDataTransfer(); + dragState().source = nullptr; } // No more default handling (like selection), whether we're past the hysteresis bounds or not return true; } #endif // ENABLE(DRAG_SUPPORT) - + +bool EventHandler::mouseMovementExceedsThreshold(const FloatPoint& viewportLocation, int pointsThreshold) const +{ + FrameView* view = m_frame.view(); + if (!view) + return false; + IntPoint location = view->windowToContents(flooredIntPoint(viewportLocation)); + IntSize delta = location - m_mouseDownPos; + + return abs(delta.width()) >= pointsThreshold || abs(delta.height()) >= pointsThreshold; +} + bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEvent, TextEventInputType inputType) { + LOG(Editing, "EventHandler %p handleTextInputEvent (text %s)", this, text.utf8().data()); + // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline), // and avoid dispatching text input events from keydown default handlers. - ASSERT(!underlyingEvent || !underlyingEvent->isKeyboardEvent() || static_cast(underlyingEvent)->type() == eventNames().keypressEvent); + ASSERT(!is(underlyingEvent) || downcast(*underlyingEvent).type() == eventNames().keypressEvent); + + Ref protectedFrame(m_frame); EventTarget* target; if (underlyingEvent) @@ -3340,32 +3678,31 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve if (FrameView* view = m_frame.view()) view->disableLayerFlushThrottlingTemporarilyForInteraction(); - RefPtr event = TextEvent::create(m_frame.document()->domWindow(), text, inputType); + Ref event = TextEvent::create(m_frame.document()->domWindow(), text, inputType); event->setUnderlyingEvent(underlyingEvent); - target->dispatchEvent(event, IGNORE_EXCEPTION); + target->dispatchEvent(event); return event->defaultHandled(); } -bool EventHandler::isKeyboardOptionTab(KeyboardEvent* event) +bool EventHandler::isKeyboardOptionTab(KeyboardEvent& event) { - return event - && (event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent) - && event->altKey() - && event->keyIdentifier() == "U+0009"; + return (event.type() == eventNames().keydownEvent || event.type() == eventNames().keypressEvent) + && event.altKey() + && event.keyIdentifier() == "U+0009"; } -bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent* event) +bool EventHandler::eventInvertsTabsToLinksClientCallResult(KeyboardEvent& event) { -#if PLATFORM(MAC) || PLATFORM(EFL) - return EventHandler::isKeyboardOptionTab(event); +#if PLATFORM(COCOA) + return isKeyboardOptionTab(event); #else UNUSED_PARAM(event); return false; #endif } -bool EventHandler::tabsToLinks(KeyboardEvent* event) const +bool EventHandler::tabsToLinks(KeyboardEvent& event) const { // FIXME: This function needs a better name. It can be called for keypresses other than Tab when spatial navigation is enabled. @@ -3377,23 +3714,25 @@ bool EventHandler::tabsToLinks(KeyboardEvent* event) const return eventInvertsTabsToLinksClientCallResult(event) ? !tabsToLinksClientCallResult : tabsToLinksClientCallResult; } -void EventHandler::defaultTextInputEventHandler(TextEvent* event) +void EventHandler::defaultTextInputEventHandler(TextEvent& event) { if (m_frame.editor().handleTextEvent(event)) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) +void EventHandler::defaultSpaceEventHandler(KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keypressEvent); + Ref protectedFrame(m_frame); + + ASSERT(event.type() == eventNames().keypressEvent); - if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey()) + if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey()) return; - ScrollLogicalDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; + ScrollLogicalDirection direction = event.shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; if (logicalScrollOverflow(direction, ScrollByPage)) { - event->setDefaultHandled(); + event.setDefaultHandled(); return; } @@ -3402,14 +3741,14 @@ void EventHandler::defaultSpaceEventHandler(KeyboardEvent* event) return; if (view->logicalScroll(direction, ScrollByPage)) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event) +void EventHandler::defaultBackspaceEventHandler(KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keydownEvent); + ASSERT(event.type() == eventNames().keydownEvent); - if (event->ctrlKey() || event->metaKey() || event->altKey() || event->altGraphKey()) + if (event.ctrlKey() || event.metaKey() || event.altKey() || event.altGraphKey()) return; if (!m_frame.editor().behavior().shouldNavigateBackOnBackspace()) @@ -3424,21 +3763,21 @@ void EventHandler::defaultBackspaceEventHandler(KeyboardEvent* event) bool handledEvent = false; - if (event->shiftKey()) + if (event.shiftKey()) handledEvent = page->backForward().goForward(); else handledEvent = page->backForward().goBack(); if (handledEvent) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent* event) +void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keydownEvent); + ASSERT(event.type() == eventNames().keydownEvent); - if (event->ctrlKey() || event->metaKey() || event->altGraphKey() || event->shiftKey()) + if (event.ctrlKey() || event.metaKey() || event.altGraphKey() || event.shiftKey()) return; Page* page = m_frame.page(); @@ -3454,15 +3793,17 @@ void EventHandler::defaultArrowEventHandler(FocusDirection focusDirection, Keybo return; if (page->focusController().advanceFocus(focusDirection, event)) - event->setDefaultHandled(); + event.setDefaultHandled(); } -void EventHandler::defaultTabEventHandler(KeyboardEvent* event) +void EventHandler::defaultTabEventHandler(KeyboardEvent& event) { - ASSERT(event->type() == eventNames().keydownEvent); + Ref protectedFrame(m_frame); + + ASSERT(event.type() == eventNames().keydownEvent); // We should only advance focus on tabs if no special modifier keys are held down. - if (event->ctrlKey() || event->metaKey() || event->altGraphKey()) + if (event.ctrlKey() || event.metaKey() || event.altGraphKey()) return; Page* page = m_frame.page(); @@ -3471,29 +3812,19 @@ void EventHandler::defaultTabEventHandler(KeyboardEvent* event) if (!page->tabKeyCyclesThroughElements()) return; - FocusDirection focusDirection = event->shiftKey() ? FocusDirectionBackward : FocusDirectionForward; + FocusDirection focusDirection = event.shiftKey() ? FocusDirectionBackward : FocusDirectionForward; // Tabs can be used in design mode editing. if (m_frame.document()->inDesignMode()) return; if (page->focusController().advanceFocus(focusDirection, event)) - event->setDefaultHandled(); -} - -void EventHandler::capsLockStateMayHaveChanged() -{ - Document* d = m_frame.document(); - if (Element* element = d->focusedElement()) { - if (RenderObject* r = element->renderer()) { - if (r->isTextField()) - toRenderTextControlSingleLine(r)->capsLockStateMayHaveChanged(); - } - } + event.setDefaultHandled(); } void EventHandler::sendScrollEvent() { + Ref protectedFrame(m_frame); setFrameWasScrolledByUser(); if (m_frame.view() && m_frame.document()) m_frame.document()->eventQueue().enqueueOrDispatchScrollEvent(*m_frame.document()); @@ -3506,30 +3837,27 @@ void EventHandler::setFrameWasScrolledByUser() v->setWasScrolledByUser(true); } -bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar) +bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mouseEvent, Scrollbar* scrollbar) { if (!scrollbar || !scrollbar->enabled()) return false; setFrameWasScrolledByUser(); - return scrollbar->mouseDown(mev.event()); + return scrollbar->mouseDown(mouseEvent.event()); } -// If scrollbar (under mouse) is different from last, send a mouse exited. Set -// last to scrollbar if setLast is true; else set last to nullptr. -void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setLast) +// If scrollbar (under mouse) is different from last, send a mouse exited. +void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, SetOrClearLastScrollbar setOrClear) { - if (m_lastScrollbarUnderMouse.get() != scrollbar) { + if (m_lastScrollbarUnderMouse != scrollbar) { // Send mouse exited to the old scrollbar. if (m_lastScrollbarUnderMouse) m_lastScrollbarUnderMouse->mouseExited(); // Send mouse entered if we're setting a new scrollbar. - if (scrollbar && setLast) + if (scrollbar && setOrClear == SetOrClearLastScrollbar::Set) { scrollbar->mouseEntered(); - - if (setLast && scrollbar) m_lastScrollbarUnderMouse = scrollbar->createWeakPtr(); - else + } else m_lastScrollbarUnderMouse = nullptr; } } @@ -3560,6 +3888,7 @@ static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point if (!frame || !frame->contentRenderer()) return result; + if (frame->view()) { IntRect rect = frame->view()->visibleContentRect(); if (!rect.contains(roundedIntPoint(point))) @@ -3571,6 +3900,8 @@ static HitTestResult hitTestResultInFrame(Frame* frame, const LayoutPoint& point bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) { + Ref protectedFrame(m_frame); + // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/ // for an overview of how these lists fit together. @@ -3594,21 +3925,18 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) const Vector& points = event.touchPoints(); - UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); + UserGestureIndicator gestureIndicator(ProcessingUserGesture, m_frame.document()); - unsigned i; bool freshTouchEvents = true; bool allTouchReleased = true; - for (i = 0; i < points.size(); ++i) { - const PlatformTouchPoint& point = points[i]; + for (auto& point : points) { if (point.state() != PlatformTouchPoint::TouchPressed) freshTouchEvents = false; if (point.state() != PlatformTouchPoint::TouchReleased && point.state() != PlatformTouchPoint::TouchCancelled) allTouchReleased = false; } - for (i = 0; i < points.size(); ++i) { - const PlatformTouchPoint& point = points[i]; + for (auto& point : points) { PlatformTouchPoint::State pointState = point.state(); LayoutPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos()); @@ -3656,14 +3984,13 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) } else continue; - // FIXME: This code should use Element* instead of Node*. - Node* node = result.innerElement(); - ASSERT(node); + Element* element = result.targetElement(); + ASSERT(element); - if (InspectorInstrumentation::handleTouchEvent(m_frame.page(), node)) + if (element && InspectorInstrumentation::handleTouchEvent(m_frame, *element)) return true; - Document& doc = node->document(); + Document& doc = element->document(); // Record the originating touch document even if it does not have a touch listener. if (freshTouchEvents) { m_originatingTouchPointDocument = &doc; @@ -3671,8 +3998,8 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) } if (!doc.hasTouchEventHandlers()) continue; - m_originatingTouchPointTargets.set(touchPointTargetKey, node); - touchTarget = node; + m_originatingTouchPointTargets.set(touchPointTargetKey, element); + touchTarget = element; } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) { // No need to perform a hit-test since we only need to unset :hover and :active states. if (!shouldGesturesTriggerActive() && allTouchReleased) @@ -3706,10 +4033,9 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) int adjustedPageX = lroundf(pagePoint.x() / scaleFactor); int adjustedPageY = lroundf(pagePoint.y() / scaleFactor); - RefPtr touch = Touch::create(targetFrame, touchTarget.get(), point.id(), - point.screenPos().x(), point.screenPos().y(), - adjustedPageX, adjustedPageY, - point.radiusX(), point.radiusY(), point.rotationAngle(), point.force()); + auto touch = Touch::create(targetFrame, touchTarget.get(), point.id(), + point.screenPos().x(), point.screenPos().y(), adjustedPageX, adjustedPageY, + point.radiusX(), point.radiusY(), point.rotationAngle(), point.force()); // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below. TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get()); @@ -3719,8 +4045,8 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) // touches and targetTouches should only contain information about touches still on the screen, so if this point is // released or cancelled it will only appear in the changedTouches list. if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) { - touches->append(touch); - targetTouchesIterator->value->append(touch); + touches->append(touch.copyRef()); + targetTouchesIterator->value->append(touch.copyRef()); } // Now build up the correct list for changedTouches. @@ -3733,13 +4059,13 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) ASSERT(pointState < PlatformTouchPoint::TouchStateEnd); if (!changedTouches[pointState].m_touches) changedTouches[pointState].m_touches = TouchList::create(); - changedTouches[pointState].m_touches->append(touch); + changedTouches[pointState].m_touches->append(WTFMove(touch)); changedTouches[pointState].m_targets.add(touchTarget); } } m_touchPressed = touches->length() > 0; if (allTouchReleased) - m_originatingTouchPointDocument.clear(); + m_originatingTouchPointDocument = nullptr; // Now iterate the changedTouches list and m_targets within it, sending events to the targets as required. bool swallowedEvent = false; @@ -3752,18 +4078,17 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) bool isTouchCancelEvent = (state == PlatformTouchPoint::TouchCancelled); RefPtr& effectiveTouches(isTouchCancelEvent ? emptyList : touches); const AtomicString& stateName(eventNameForTouchPointState(static_cast(state))); - const EventTargetSet& targetsForState = changedTouches[state].m_targets; - for (EventTargetSet::const_iterator it = targetsForState.begin(); it != targetsForState.end(); ++it) { - EventTarget* touchEventTarget = it->get(); + for (auto& taget : changedTouches[state].m_targets) { + EventTarget* touchEventTarget = taget.get(); RefPtr targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(touchEventTarget)); ASSERT(targetTouches); - RefPtr touchEvent = + Ref touchEvent = TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(), stateName, touchEventTarget->toNode()->document().defaultView(), 0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey()); - touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get()); + touchEventTarget->toNode()->dispatchTouchEvent(touchEvent); swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled(); } } @@ -3773,29 +4098,29 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event) #endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) #if ENABLE(TOUCH_EVENTS) -bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& event) +bool EventHandler::dispatchSyntheticTouchEventIfEnabled(const PlatformMouseEvent& platformMouseEvent) { #if ENABLE(IOS_TOUCH_EVENTS) - UNUSED_PARAM(event); + UNUSED_PARAM(platformMouseEvent); return false; #else if (!m_frame.settings().isTouchEventEmulationEnabled()) return false; - PlatformEvent::Type eventType = event.type(); + PlatformEvent::Type eventType = platformMouseEvent.type(); if (eventType != PlatformEvent::MouseMoved && eventType != PlatformEvent::MousePressed && eventType != PlatformEvent::MouseReleased) return false; - HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowShadowContent); - MouseEventWithHitTestResults mev = prepareMouseEvent(request, event); - if (mev.scrollbar() || subframeForHitTestResult(mev)) + HitTestRequest request(HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent); + MouseEventWithHitTestResults mouseEvent = prepareMouseEvent(request, platformMouseEvent); + if (mouseEvent.scrollbar() || subframeForHitTestResult(mouseEvent)) return false; // The order is important. This check should follow the subframe test: http://webkit.org/b/111292. if (eventType == PlatformEvent::MouseMoved && !m_touchPressed) return true; - SyntheticSingleTouchEvent touchEvent(event); + SyntheticSingleTouchEvent touchEvent(platformMouseEvent); return handleTouchEvent(touchEvent); #endif } @@ -3808,4 +4133,9 @@ void EventHandler::setLastKnownMousePosition(const PlatformMouseEvent& event) m_lastKnownMouseGlobalPosition = event.globalPosition(); } +void EventHandler::setImmediateActionStage(ImmediateActionStage stage) +{ + m_immediateActionStage = stage; +} + } // namespace WebCore diff --git a/Source/WebCore/page/EventHandler.h b/Source/WebCore/page/EventHandler.h index 387876f02..1ec5eed7a 100644 --- a/Source/WebCore/page/EventHandler.h +++ b/Source/WebCore/page/EventHandler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2015 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EventHandler_h -#define EventHandler_h +#pragma once #include "Cursor.h" #include "DragActions.h" @@ -32,58 +31,51 @@ #include "HitTestRequest.h" #include "LayoutPoint.h" #include "PlatformMouseEvent.h" -#include "PlatformWheelEvent.h" #include "ScrollTypes.h" #include "TextEventInputType.h" #include "TextGranularity.h" #include "Timer.h" -#include +#include #include -#include +#include +#include #include #include -#if PLATFORM(IOS) -#ifdef __OBJC__ -@class WebEvent; -@class WAKView; -#include "WAKAppKitStubs.h" -#else -class WebEvent; +#if PLATFORM(COCOA) +OBJC_CLASS NSView; #endif -#endif // PLATFORM(IOS) -#if PLATFORM(MAC) && !defined(__OBJC__) -class NSView; +#if PLATFORM(IOS) +OBJC_CLASS WebEvent; #endif -#if ENABLE(TOUCH_EVENTS) -#include +#if PLATFORM(MAC) +OBJC_CLASS NSEvent; #endif -#if ENABLE(IOS_TOUCH_EVENTS) -#include -#include +#if PLATFORM(IOS) && defined(__OBJC__) +#include "WAKAppKitStubs.h" #endif namespace WebCore { class AutoscrollController; -class Clipboard; +class ContainerNode; +class DataTransfer; class Document; class Element; class Event; class EventTarget; -class FloatPoint; class FloatQuad; class Frame; +class FrameView; class HTMLFrameSetElement; -class HitTestRequest; class HitTestResult; class KeyboardEvent; class MouseEventWithHitTestResults; class Node; -class OptionalCursor; +class PlatformGestureEvent; class PlatformKeyboardEvent; class PlatformTouchEvent; class PlatformWheelEvent; @@ -91,7 +83,7 @@ class RenderBox; class RenderElement; class RenderLayer; class RenderWidget; -class SVGElementInstance; +class ScrollableArea; class Scrollbar; class TextEvent; class Touch; @@ -107,35 +99,42 @@ extern const int LinkDragHysteresis; extern const int ImageDragHysteresis; extern const int TextDragHysteresis; extern const int GeneralDragHysteresis; -#endif // ENABLE(DRAG_SUPPORT) +#endif -#if ENABLE(IOS_GESTURE_EVENTS) +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) extern const float GestureUnknown; +extern const unsigned InvalidTouchIdentifier; #endif enum AppendTrailingWhitespace { ShouldAppendTrailingWhitespace, DontAppendTrailingWhitespace }; enum CheckDragHysteresis { ShouldCheckDragHysteresis, DontCheckDragHysteresis }; +enum class ImmediateActionStage { + None, + PerformedHitTest, + ActionUpdated, + ActionCancelledWithoutUpdate, + ActionCancelledAfterUpdate, + ActionCompleted +}; + class EventHandler { - WTF_MAKE_NONCOPYABLE(EventHandler); + WTF_MAKE_FAST_ALLOCATED; public: explicit EventHandler(Frame&); ~EventHandler(); void clear(); - void nodeWillBeRemoved(Node*); + void nodeWillBeRemoved(Node&); #if ENABLE(DRAG_SUPPORT) void updateSelectionForMouseDrag(); #endif - Node* mousePressNode() const; - void setMousePressNode(PassRefPtr); - #if ENABLE(PAN_SCROLLING) void didPanScrollStart(); void didPanScrollStop(); - void startPanScrolling(RenderElement*); + void startPanScrolling(RenderElement&); #endif void stopAutoscrollTimer(bool rendererIsBeingDestroyed = false); @@ -145,23 +144,23 @@ public: bool mouseDownWasInSubframe() const { return m_mouseDownWasInSubframe; } bool panScrollInProgress() const; - void dispatchFakeMouseMoveEventSoon(); + WEBCORE_EXPORT void dispatchFakeMouseMoveEventSoon(); void dispatchFakeMouseMoveEventSoonInQuad(const FloatQuad&); - HitTestResult hitTestResultAtPoint(const LayoutPoint&, - HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent, + WEBCORE_EXPORT HitTestResult hitTestResultAtPoint(const LayoutPoint&, + HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowUserAgentShadowContent, const LayoutSize& padding = LayoutSize()); bool mousePressed() const { return m_mousePressed; } - void setMousePressed(bool pressed) { m_mousePressed = pressed; } + Node* mousePressNode() const { return m_mousePressNode.get(); } - void setCapturingMouseEventsElement(PassRefPtr); // A caller is responsible for resetting capturing element to 0. + WEBCORE_EXPORT void setCapturingMouseEventsElement(Element*); #if ENABLE(DRAG_SUPPORT) - bool updateDragAndDrop(const PlatformMouseEvent&, Clipboard*); - void cancelDragAndDrop(const PlatformMouseEvent&, Clipboard*); - bool performDragAndDrop(const PlatformMouseEvent&, Clipboard*); - void updateDragStateAfterEditDragIfNeeded(Element* rootEditableElement); + bool updateDragAndDrop(const PlatformMouseEvent&, DataTransfer&); + void cancelDragAndDrop(const PlatformMouseEvent&, DataTransfer&); + bool performDragAndDrop(const PlatformMouseEvent&, DataTransfer&); + void updateDragStateAfterEditDragIfNeeded(Element& rootEditableElement); #endif void scheduleHoverStateUpdate(); @@ -174,120 +173,136 @@ public: void resizeLayerDestroyed(); IntPoint lastKnownMousePosition() const; + IntPoint lastKnownMouseGlobalPosition() const { return m_lastKnownMouseGlobalPosition; } Cursor currentMouseCursor() const { return m_currentMouseCursor; } + IntPoint effectiveMousePositionForSelectionAutoscroll() const; + static Frame* subframeForTargetNode(Node*); static Frame* subframeForHitTestResult(const MouseEventWithHitTestResults&); - bool scrollOverflow(ScrollDirection, ScrollGranularity, Node* startingNode = 0); - bool scrollRecursively(ScrollDirection, ScrollGranularity, Node* startingNode = 0); - bool logicalScrollRecursively(ScrollLogicalDirection, ScrollGranularity, Node* startingNode = 0); + WEBCORE_EXPORT bool scrollOverflow(ScrollDirection, ScrollGranularity, Node* startingNode = nullptr); + WEBCORE_EXPORT bool scrollRecursively(ScrollDirection, ScrollGranularity, Node* startingNode = nullptr); + WEBCORE_EXPORT bool logicalScrollRecursively(ScrollLogicalDirection, ScrollGranularity, Node* startingNode = nullptr); - bool tabsToLinks(KeyboardEvent*) const; - bool tabsToAllFormControls(KeyboardEvent*) const; + bool tabsToLinks(KeyboardEvent&) const; + bool tabsToAllFormControls(KeyboardEvent&) const; - bool mouseMoved(const PlatformMouseEvent&); - bool passMouseMovedEventToScrollbars(const PlatformMouseEvent&); + WEBCORE_EXPORT bool mouseMoved(const PlatformMouseEvent&); + WEBCORE_EXPORT bool passMouseMovedEventToScrollbars(const PlatformMouseEvent&); void lostMouseCapture(); - bool handleMousePressEvent(const PlatformMouseEvent&); - bool handleMouseMoveEvent(const PlatformMouseEvent&, HitTestResult* hoveredNode = 0, bool onlyUpdateScrollbars = false); - bool handleMouseReleaseEvent(const PlatformMouseEvent&); - bool handleWheelEvent(const PlatformWheelEvent&); - void defaultWheelEventHandler(Node*, WheelEvent*); + WEBCORE_EXPORT bool handleMousePressEvent(const PlatformMouseEvent&); + bool handleMouseMoveEvent(const PlatformMouseEvent&, HitTestResult* hoveredNode = nullptr, bool onlyUpdateScrollbars = false); + WEBCORE_EXPORT bool handleMouseReleaseEvent(const PlatformMouseEvent&); + bool handleMouseForceEvent(const PlatformMouseEvent&); + WEBCORE_EXPORT bool handleWheelEvent(const PlatformWheelEvent&); + void defaultWheelEventHandler(Node*, WheelEvent&); bool handlePasteGlobalSelection(const PlatformMouseEvent&); + void platformPrepareForWheelEvents(const PlatformWheelEvent&, const HitTestResult&, RefPtr& eventTarget, RefPtr& scrollableContainer, WeakPtr&, bool& isOverWidget); + void platformRecordWheelEvent(const PlatformWheelEvent&); + bool platformCompleteWheelEvent(const PlatformWheelEvent&, ContainerNode* scrollableContainer, const WeakPtr&); + bool platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode* scrollableContainer); + void platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr&); + #if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) - typedef Vector> TouchArray; - typedef HashMap EventTargetTouchMap; - typedef HashSet> EventTargetSet; + using TouchArray = Vector>; + using EventTargetTouchMap = HashMap; +#endif + +#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) + using EventTargetSet = HashSet>; #endif #if ENABLE(IOS_TOUCH_EVENTS) bool dispatchTouchEvent(const PlatformTouchEvent&, const AtomicString&, const EventTargetTouchMap&, float, float); + bool dispatchSimulatedTouchEvent(IntPoint location); #endif #if ENABLE(IOS_GESTURE_EVENTS) bool dispatchGestureEvent(const PlatformTouchEvent&, const AtomicString&, const EventTargetSet&, float, float); +#elif ENABLE(MAC_GESTURE_EVENTS) + bool dispatchGestureEvent(const PlatformGestureEvent&, const AtomicString&, const EventTargetSet&, float, float); + WEBCORE_EXPORT bool handleGestureEvent(const PlatformGestureEvent&); #endif #if PLATFORM(IOS) - void defaultTouchEventHandler(Node*, TouchEvent*); + void defaultTouchEventHandler(Node&, TouchEvent&); #endif #if ENABLE(CONTEXT_MENUS) - bool sendContextMenuEvent(const PlatformMouseEvent&); - bool sendContextMenuEventForKey(); + WEBCORE_EXPORT bool sendContextMenuEvent(const PlatformMouseEvent&); + WEBCORE_EXPORT bool sendContextMenuEventForKey(); #endif void setMouseDownMayStartAutoscroll() { m_mouseDownMayStartAutoscroll = true; } bool needsKeyboardEventDisambiguationQuirks() const; - static unsigned accessKeyModifiers(); - bool handleAccessKey(const PlatformKeyboardEvent&); - bool keyEvent(const PlatformKeyboardEvent&); - void defaultKeyboardEventHandler(KeyboardEvent*); + WEBCORE_EXPORT static OptionSet accessKeyModifiers(); + WEBCORE_EXPORT bool handleAccessKey(const PlatformKeyboardEvent&); + WEBCORE_EXPORT bool keyEvent(const PlatformKeyboardEvent&); + void defaultKeyboardEventHandler(KeyboardEvent&); - void handleKeyboardSelectionMovementForAccessibility(KeyboardEvent*); + bool accessibilityPreventsEventPropogation(KeyboardEvent&); + WEBCORE_EXPORT void handleKeyboardSelectionMovementForAccessibility(KeyboardEvent&); - bool handleTextInputEvent(const String& text, Event* underlyingEvent = 0, TextEventInputType = TextEventInputKeyboard); - void defaultTextInputEventHandler(TextEvent*); + bool handleTextInputEvent(const String& text, Event* underlyingEvent = nullptr, TextEventInputType = TextEventInputKeyboard); + void defaultTextInputEventHandler(TextEvent&); #if ENABLE(DRAG_SUPPORT) - bool eventMayStartDrag(const PlatformMouseEvent&) const; + WEBCORE_EXPORT bool eventMayStartDrag(const PlatformMouseEvent&) const; - void dragSourceEndedAt(const PlatformMouseEvent&, DragOperation); + WEBCORE_EXPORT void dragSourceEndedAt(const PlatformMouseEvent&, DragOperation); #endif void focusDocumentView(); - - void capsLockStateMayHaveChanged(); // Only called by FrameSelection - void sendScrollEvent(); // Ditto + WEBCORE_EXPORT void sendScrollEvent(); -#if PLATFORM(MAC) && defined(__OBJC__) -#if !PLATFORM(IOS) - void mouseDown(NSEvent *); - void mouseDragged(NSEvent *); - void mouseUp(NSEvent *); - void mouseMoved(NSEvent *); - bool keyEvent(NSEvent *); - bool wheelEvent(NSEvent *); -#else - void mouseDown(WebEvent *); - void mouseUp(WebEvent *); - void mouseMoved(WebEvent *); - bool keyEvent(WebEvent *); - bool wheelEvent(WebEvent *); +#if PLATFORM(MAC) + WEBCORE_EXPORT void mouseDown(NSEvent *, NSEvent *correspondingPressureEvent); + WEBCORE_EXPORT void mouseDragged(NSEvent *, NSEvent *correspondingPressureEvent); + WEBCORE_EXPORT void mouseUp(NSEvent *, NSEvent *correspondingPressureEvent); + WEBCORE_EXPORT void mouseMoved(NSEvent *, NSEvent *correspondingPressureEvent); + WEBCORE_EXPORT void pressureChange(NSEvent *, NSEvent* correspondingPressureEvent); + WEBCORE_EXPORT bool keyEvent(NSEvent *); + WEBCORE_EXPORT bool wheelEvent(NSEvent *); +#endif + +#if PLATFORM(IOS) + WEBCORE_EXPORT void mouseDown(WebEvent *); + WEBCORE_EXPORT void mouseUp(WebEvent *); + WEBCORE_EXPORT void mouseMoved(WebEvent *); + WEBCORE_EXPORT bool keyEvent(WebEvent *); + WEBCORE_EXPORT bool wheelEvent(WebEvent *); #endif #if ENABLE(IOS_TOUCH_EVENTS) - void touchEvent(WebEvent *); + WEBCORE_EXPORT void touchEvent(WebEvent *); #endif -#if !PLATFORM(IOS) - void passMouseMovedEventToScrollbars(NSEvent *); +#if PLATFORM(MAC) + WEBCORE_EXPORT void passMouseMovedEventToScrollbars(NSEvent *, NSEvent* correspondingPressureEvent); - void sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent); -#endif + WEBCORE_EXPORT void sendFakeEventsAfterWidgetTracking(NSEvent *initiatingEvent); -#if !PLATFORM(IOS) void setActivationEventNumber(int num) { m_activationEventNumber = num; } - static NSEvent *currentNSEvent(); -#else - static WebEvent *currentEvent(); -#endif // !PLATFORM(IOS) -#endif // PLATFORM(MAC) && defined(__OBJC__) + WEBCORE_EXPORT static NSEvent *currentNSEvent(); + static NSEvent *correspondingPressureEvent(); +#endif #if PLATFORM(IOS) + static WebEvent *currentEvent(); + void invalidateClick(); #endif #if ENABLE(TOUCH_EVENTS) - bool handleTouchEvent(const PlatformTouchEvent&); + WEBCORE_EXPORT bool handleTouchEvent(const PlatformTouchEvent&); #endif bool useHandCursor(Node*, bool isOverLink, bool shiftKey); @@ -295,56 +310,67 @@ public: bool isHandlingWheelEvent() const { return m_isHandlingWheelEvent; } + WEBCORE_EXPORT void setImmediateActionStage(ImmediateActionStage stage); + ImmediateActionStage immediateActionStage() const { return m_immediateActionStage; } + + static Widget* widgetForEventTarget(Element* eventTarget); + +#if ENABLE(DATA_INTERACTION) + WEBCORE_EXPORT bool tryToBeginDataInteractionAtPoint(const IntPoint& clientPosition, const IntPoint& globalPosition); +#endif + private: #if ENABLE(DRAG_SUPPORT) static DragState& dragState(); static const double TextDragDelay; - - PassRefPtr createDraggingClipboard() const; -#endif // ENABLE(DRAG_SUPPORT) + Ref createDraggingDataTransfer() const; +#endif bool eventActivatedView(const PlatformMouseEvent&) const; bool updateSelectionForMouseDownDispatchingSelectStart(Node*, const VisibleSelection&, TextGranularity); void selectClosestWordFromHitTestResult(const HitTestResult&, AppendTrailingWhitespace); + VisibleSelection selectClosestWordFromHitTestResultBasedOnLookup(const HitTestResult&); void selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults&); - void selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults&); + void selectClosestContextualWordFromMouseEvent(const MouseEventWithHitTestResults&); + void selectClosestContextualWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults&); bool handleMouseDoubleClickEvent(const PlatformMouseEvent&); - bool handleMousePressEvent(const MouseEventWithHitTestResults&); + WEBCORE_EXPORT bool handleMousePressEvent(const MouseEventWithHitTestResults&); bool handleMousePressEventSingleClick(const MouseEventWithHitTestResults&); bool handleMousePressEventDoubleClick(const MouseEventWithHitTestResults&); bool handleMousePressEventTripleClick(const MouseEventWithHitTestResults&); + #if ENABLE(DRAG_SUPPORT) - bool handleMouseDraggedEvent(const MouseEventWithHitTestResults&); + bool handleMouseDraggedEvent(const MouseEventWithHitTestResults&, CheckDragHysteresis = ShouldCheckDragHysteresis); #endif - bool handleMouseReleaseEvent(const MouseEventWithHitTestResults&); - OptionalCursor selectCursor(const HitTestResult&, bool shiftKey); + WEBCORE_EXPORT bool handleMouseReleaseEvent(const MouseEventWithHitTestResults&); + + bool internalKeyEvent(const PlatformKeyboardEvent&); + + std::optional selectCursor(const HitTestResult&, bool shiftKey); + void updateCursor(FrameView&, const HitTestResult&, bool shiftKey); + + void hoverTimerFired(); - void hoverTimerFired(Timer&); #if ENABLE(CURSOR_SUPPORT) - void cursorUpdateTimerFired(Timer&); + void cursorUpdateTimerFired(); #endif - bool logicalScrollOverflow(ScrollLogicalDirection, ScrollGranularity, Node* startingNode = 0); + bool logicalScrollOverflow(ScrollLogicalDirection, ScrollGranularity, Node* startingNode = nullptr); bool shouldTurnVerticalTicksIntoHorizontal(const HitTestResult&, const PlatformWheelEvent&) const; - void recordWheelEventDelta(const PlatformWheelEvent&); - enum DominantScrollGestureDirection { - DominantScrollDirectionNone, - DominantScrollDirectionVertical, - DominantScrollDirectionHorizontal - }; - DominantScrollGestureDirection dominantScrollGestureDirection() const; bool mouseDownMayStartSelect() const { return m_mouseDownMayStartSelect; } - static bool isKeyboardOptionTab(KeyboardEvent*); - static bool eventInvertsTabsToLinksClientCallResult(KeyboardEvent*); + static bool isKeyboardOptionTab(KeyboardEvent&); + static bool eventInvertsTabsToLinksClientCallResult(KeyboardEvent&); - void fakeMouseMoveEventTimerFired(Timer&); +#if !ENABLE(IOS_TOUCH_EVENTS) + void fakeMouseMoveEventTimerFired(); void cancelFakeMouseMoveEvent(); +#endif bool isInsideScrollbar(const IntPoint&) const; @@ -364,14 +390,16 @@ private: MouseEventWithHitTestResults prepareMouseEvent(const HitTestRequest&, const PlatformMouseEvent&); bool dispatchMouseEvent(const AtomicString& eventType, Node* target, bool cancelable, int clickCount, const PlatformMouseEvent&, bool setUnder); -#if ENABLE(DRAG_SUPPORT) - bool dispatchDragEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent&, Clipboard*); - void freeClipboard(); +#if ENABLE(DRAG_SUPPORT) + bool dispatchDragEvent(const AtomicString& eventType, Element& target, const PlatformMouseEvent&, DataTransfer*); + void invalidateDataTransfer(); bool handleDrag(const MouseEventWithHitTestResults&, CheckDragHysteresis); #endif + bool handleMouseUp(const MouseEventWithHitTestResults&); + #if ENABLE(DRAG_SUPPORT) void clearDragState(); @@ -379,13 +407,15 @@ private: bool dragHysteresisExceeded(const FloatPoint&) const; bool dragHysteresisExceeded(const IntPoint&) const; -#endif // ENABLE(DRAG_SUPPORT) +#endif + + bool mouseMovementExceedsThreshold(const FloatPoint&, int pointsThreshold) const; bool passMousePressEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe); - bool passMouseMoveEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = 0); + bool passMouseMoveEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = nullptr); bool passMouseReleaseEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe); - bool passSubframeEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = 0); + bool passSubframeEventToSubframe(MouseEventWithHitTestResults&, Frame* subframe, HitTestResult* hoveredNode = nullptr); bool passMousePressEventToScrollbar(MouseEventWithHitTestResults&, Scrollbar*); @@ -393,12 +423,13 @@ private: bool passWidgetMouseDownEventToWidget(RenderWidget*); bool passMouseDownEventToWidget(Widget*); - bool passWheelEventToWidget(const PlatformWheelEvent&, Widget*); + bool widgetDidHandleWheelEvent(const PlatformWheelEvent&, Widget&); + bool completeWidgetWheelEvent(const PlatformWheelEvent&, const WeakPtr&, const WeakPtr&, ContainerNode*); - void defaultSpaceEventHandler(KeyboardEvent*); - void defaultBackspaceEventHandler(KeyboardEvent*); - void defaultTabEventHandler(KeyboardEvent*); - void defaultArrowEventHandler(FocusDirection, KeyboardEvent*); + void defaultSpaceEventHandler(KeyboardEvent&); + void defaultBackspaceEventHandler(KeyboardEvent&); + void defaultTabEventHandler(KeyboardEvent&); + void defaultArrowEventHandler(FocusDirection, KeyboardEvent&); #if ENABLE(DRAG_SUPPORT) DragSourceAction updateDragSourceActionsAllowed() const; @@ -407,21 +438,20 @@ private: // The following are called at the beginning of handleMouseUp and handleDrag. // If they return true it indicates that they have consumed the event. bool eventLoopHandleMouseUp(const MouseEventWithHitTestResults&); -#if ENABLE(DRAG_SUPPORT) - bool eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&); -#endif #if ENABLE(DRAG_SUPPORT) + bool eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&); void updateSelectionForMouseDrag(const HitTestResult&); #endif - void updateLastScrollbarUnderMouse(Scrollbar*, bool); + enum class SetOrClearLastScrollbar { Clear, Set }; + void updateLastScrollbarUnderMouse(Scrollbar*, SetOrClearLastScrollbar); void setFrameWasScrolledByUser(); bool capturesDragging() const { return m_capturesDragging; } -#if PLATFORM(MAC) && defined(__OBJC__) +#if PLATFORM(COCOA) && defined(__OBJC__) NSView *mouseDownViewIfStillGood(); PlatformMouseEvent currentPlatformMouseEvent() const; @@ -436,51 +466,57 @@ private: #if ENABLE(CURSOR_VISIBILITY) void startAutoHideCursorTimer(); void cancelAutoHideCursorTimer(); - void autoHideCursorTimerFired(Timer&); + void autoHideCursorTimerFired(); #endif + void clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&); + void clearLatchedState(); + Frame& m_frame; - bool m_mousePressed; - bool m_capturesDragging; + bool m_mousePressed { false }; + bool m_capturesDragging { false }; RefPtr m_mousePressNode; - bool m_mouseDownMayStartSelect; + bool m_mouseDownMayStartSelect { false }; + #if ENABLE(DRAG_SUPPORT) - bool m_mouseDownMayStartDrag; - bool m_dragMayStartSelectionInstead; + bool m_mouseDownMayStartDrag { false }; + bool m_dragMayStartSelectionInstead { false }; #endif - bool m_mouseDownWasSingleClickInSelection; + + bool m_mouseDownWasSingleClickInSelection { false }; enum SelectionInitiationState { HaveNotStartedSelection, PlacedCaret, ExtendedSelection }; - SelectionInitiationState m_selectionInitiationState; + SelectionInitiationState m_selectionInitiationState { HaveNotStartedSelection }; #if ENABLE(DRAG_SUPPORT) - LayoutPoint m_dragStartPos; + LayoutPoint m_dragStartPosition; #endif - bool m_panScrollButtonPressed; + Timer m_hoverTimer; - Timer m_hoverTimer; #if ENABLE(CURSOR_SUPPORT) - Timer m_cursorUpdateTimer; + Timer m_cursorUpdateTimer; #endif - OwnPtr m_autoscrollController; - bool m_mouseDownMayStartAutoscroll; - bool m_mouseDownWasInSubframe; +#if PLATFORM(MAC) + Timer m_pendingMomentumWheelEventsTimer; +#endif - Timer m_fakeMouseMoveEventTimer; + std::unique_ptr m_autoscrollController; + bool m_mouseDownMayStartAutoscroll { false }; + bool m_mouseDownWasInSubframe { false }; -#if ENABLE(SVG) - bool m_svgPan; - RefPtr m_instanceUnderMouse; - RefPtr m_lastInstanceUnderMouse; +#if !ENABLE(IOS_TOUCH_EVENTS) + Timer m_fakeMouseMoveEventTimer; #endif - RenderLayer* m_resizeLayer; + bool m_svgPan { false }; + + RenderLayer* m_resizeLayer { nullptr }; RefPtr m_capturingMouseEventsElement; - bool m_eventHandlerWillResetCapturingMouseEventsElement; + bool m_eventHandlerWillResetCapturingMouseEventsElement { false }; RefPtr m_elementUnderMouse; RefPtr m_lastElementUnderMouse; @@ -488,73 +524,73 @@ private: WeakPtr m_lastScrollbarUnderMouse; Cursor m_currentMouseCursor; - int m_clickCount; + int m_clickCount { 0 }; RefPtr m_clickNode; #if ENABLE(IOS_GESTURE_EVENTS) - float m_gestureInitialDiameter; - float m_gestureLastDiameter; - float m_gestureInitialRotation; - float m_gestureLastRotation; + float m_gestureInitialDiameter { GestureUnknown }; + float m_gestureInitialRotation { GestureUnknown }; +#endif + +#if ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) + float m_gestureLastDiameter { GestureUnknown }; + float m_gestureLastRotation { GestureUnknown }; + EventTargetSet m_gestureTargets; +#endif + +#if ENABLE(MAC_GESTURE_EVENTS) + bool m_hasActiveGesture { false }; #endif #if ENABLE(IOS_TOUCH_EVENTS) - unsigned m_firstTouchID; + unsigned m_firstTouchID { InvalidTouchIdentifier }; TouchArray m_touches; - EventTargetSet m_gestureTargets; RefPtr m_touchEventTargetSubframe; #endif #if ENABLE(DRAG_SUPPORT) RefPtr m_dragTarget; - bool m_shouldOnlyFireDragOverEvent; + bool m_shouldOnlyFireDragOverEvent { false }; #endif RefPtr m_frameSetBeingResized; LayoutSize m_offsetFromResizeCorner; // In the coords of m_resizeLayer. - bool m_mousePositionIsUnknown; + bool m_mousePositionIsUnknown { true }; IntPoint m_lastKnownMousePosition; IntPoint m_lastKnownMouseGlobalPosition; IntPoint m_mouseDownPos; // In our view's coords. - double m_mouseDownTimestamp; + double m_mouseDownTimestamp { 0 }; PlatformMouseEvent m_mouseDown; - Deque m_recentWheelEventDeltas; - RefPtr m_latchedWheelEventElement; - bool m_inTrackingScrollGesturePhase; - bool m_widgetIsLatched; - - RefPtr m_previousWheelScrolledElement; - -#if PLATFORM(MAC) || PLATFORM(IOS) - NSView *m_mouseDownView; - bool m_sendingEventToSubview; -#if !PLATFORM(IOS) - int m_activationEventNumber; +#if PLATFORM(COCOA) + NSView *m_mouseDownView { nullptr }; + bool m_sendingEventToSubview { false }; #endif + +#if PLATFORM(MAC) + int m_activationEventNumber { -1 }; #endif + #if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS) - typedef HashMap> TouchTargetMap; + using TouchTargetMap = HashMap>; TouchTargetMap m_originatingTouchPointTargets; RefPtr m_originatingTouchPointDocument; - unsigned m_originatingTouchPointTargetKey; - bool m_touchPressed; + unsigned m_originatingTouchPointTargetKey { 0 }; + bool m_touchPressed { false }; #endif - double m_maxMouseMovedDuration; - PlatformEvent::Type m_baseEventType; - bool m_didStartDrag; - bool m_didLongPressInvokeContextMenu; - bool m_isHandlingWheelEvent; + double m_maxMouseMovedDuration { 0 }; + bool m_didStartDrag { false }; + bool m_isHandlingWheelEvent { false }; #if ENABLE(CURSOR_VISIBILITY) - Timer m_autoHideCursorTimer; + Timer m_autoHideCursorTimer; #endif + + ImmediateActionStage m_immediateActionStage { ImmediateActionStage::None }; }; } // namespace WebCore - -#endif // EventHandler_h diff --git a/Source/WebCore/page/EventSource.cpp b/Source/WebCore/page/EventSource.cpp index 4ccfbe380..d5555ed2c 100644 --- a/Source/WebCore/page/EventSource.cpp +++ b/Source/WebCore/page/EventSource.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved. - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2016 Apple Inc. All rights reserved. * Copyright (C) 2011, Code Aurora Forum. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,77 +34,50 @@ #include "EventSource.h" #include "ContentSecurityPolicy.h" -#include "DOMWindow.h" -#include "Dictionary.h" -#include "Document.h" -#include "Event.h" -#include "EventException.h" +#include "EventNames.h" #include "ExceptionCode.h" -#include "Frame.h" -#include "MemoryCache.h" #include "MessageEvent.h" #include "ResourceError.h" #include "ResourceRequest.h" #include "ResourceResponse.h" -#include "ScriptCallStack.h" -#include "ScriptController.h" #include "ScriptExecutionContext.h" #include "SecurityOrigin.h" -#include "SerializedScriptValue.h" #include "TextResourceDecoder.h" #include "ThreadableLoader.h" -#include namespace WebCore { -const unsigned long long EventSource::defaultReconnectDelay = 3000; +const uint64_t EventSource::defaultReconnectDelay = 3000; -inline EventSource::EventSource(ScriptExecutionContext& context, const URL& url, const Dictionary& eventSourceInit) +inline EventSource::EventSource(ScriptExecutionContext& context, const URL& url, const Init& eventSourceInit) : ActiveDOMObject(&context) , m_url(url) - , m_withCredentials(false) - , m_state(CONNECTING) - , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8")) - , m_connectTimer(this, &EventSource::connectTimerFired) - , m_discardTrailingNewline(false) - , m_requestInFlight(false) - , m_reconnectDelay(defaultReconnectDelay) + , m_withCredentials(eventSourceInit.withCredentials) + , m_decoder(TextResourceDecoder::create(ASCIILiteral("text/plain"), "UTF-8")) + , m_connectTimer(*this, &EventSource::connect) { - eventSourceInit.get("withCredentials", m_withCredentials); } -PassRefPtr EventSource::create(ScriptExecutionContext& context, const String& url, const Dictionary& eventSourceInit, ExceptionCode& ec) +ExceptionOr> EventSource::create(ScriptExecutionContext& context, const String& url, const Init& eventSourceInit) { - if (url.isEmpty()) { - ec = SYNTAX_ERR; - return 0; - } + if (url.isEmpty()) + return Exception { SYNTAX_ERR }; URL fullURL = context.completeURL(url); - if (!fullURL.isValid()) { - ec = SYNTAX_ERR; - return 0; - } + if (!fullURL.isValid()) + return Exception { SYNTAX_ERR }; - // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. - bool shouldBypassMainWorldContentSecurityPolicy = false; - if (context.isDocument()) { - Document& document = toDocument(context); - shouldBypassMainWorldContentSecurityPolicy = document.frame()->script().shouldBypassMainWorldContentSecurityPolicy(); - } - if (!shouldBypassMainWorldContentSecurityPolicy && !context.contentSecurityPolicy()->allowConnectToSource(fullURL)) { + // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is resolved. + if (!context.shouldBypassMainWorldContentSecurityPolicy() && !context.contentSecurityPolicy()->allowConnectToSource(fullURL)) { // FIXME: Should this be throwing an exception? - ec = SECURITY_ERR; - return 0; + return Exception { SECURITY_ERR }; } - RefPtr source = adoptRef(new EventSource(context, fullURL, eventSourceInit)); - - source->setPendingActivity(source.get()); + auto source = adoptRef(*new EventSource(context, fullURL, eventSourceInit)); + source->setPendingActivity(source.ptr()); source->scheduleInitialConnect(); source->suspendIfNeeded(); - - return source.release(); + return WTFMove(source); } EventSource::~EventSource() @@ -118,34 +91,33 @@ void EventSource::connect() ASSERT(m_state == CONNECTING); ASSERT(!m_requestInFlight); - ResourceRequest request(m_url); + ResourceRequest request { m_url }; request.setHTTPMethod("GET"); - request.setHTTPHeaderField("Accept", "text/event-stream"); - request.setHTTPHeaderField("Cache-Control", "no-cache"); + request.setHTTPHeaderField(HTTPHeaderName::Accept, "text/event-stream"); + request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache"); if (!m_lastEventId.isEmpty()) - request.setHTTPHeaderField("Last-Event-ID", m_lastEventId); - - SecurityOrigin* origin = scriptExecutionContext()->securityOrigin(); + request.setHTTPHeaderField(HTTPHeaderName::LastEventID, m_lastEventId); ThreadableLoaderOptions options; options.sendLoadCallbacks = SendCallbacks; - options.sniffContent = DoNotSniffContent; - options.allowCredentials = (origin->canRequest(m_url) || m_withCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; + options.credentials = m_withCredentials ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin; options.preflightPolicy = PreventPreflight; - options.crossOriginRequestPolicy = UseAccessControl; + options.mode = FetchOptions::Mode::Cors; + options.cache = FetchOptions::Cache::NoStore; options.dataBufferingPolicy = DoNotBufferData; - options.securityOrigin = origin; + options.contentSecurityPolicyEnforcement = scriptExecutionContext()->shouldBypassMainWorldContentSecurityPolicy() ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective; - m_loader = ThreadableLoader::create(scriptExecutionContext(), this, request, options); + ASSERT(scriptExecutionContext()); + m_loader = ThreadableLoader::create(*scriptExecutionContext(), *this, WTFMove(request), options); + // FIXME: Can we just use m_loader for this, null it out when it's no longer in flight, and eliminate the m_requestInFlight member? if (m_loader) m_requestInFlight = true; } void EventSource::networkRequestEnded() { - if (!m_requestInFlight) - return; + ASSERT(m_requestInFlight); m_requestInFlight = false; @@ -170,26 +142,6 @@ void EventSource::scheduleReconnect() dispatchEvent(Event::create(eventNames().errorEvent, false, false)); } -void EventSource::connectTimerFired(Timer&) -{ - connect(); -} - -String EventSource::url() const -{ - return m_url.string(); -} - -bool EventSource::withCredentials() const -{ - return m_withCredentials; -} - -EventSource::State EventSource::readyState() const -{ - return m_state; -} - void EventSource::close() { if (m_state == CLOSED) { @@ -209,46 +161,47 @@ void EventSource::close() } } +bool EventSource::responseIsValid(const ResourceResponse& response) const +{ + // Logs to the console as a side effect. + + // To keep the signal-to-noise ratio low, we don't log anything if the status code is not 200. + if (response.httpStatusCode() != 200) + return false; + + if (!equalLettersIgnoringASCIICase(response.mimeType(), "text/event-stream")) { + auto message = makeString("EventSource's response has a MIME type (\"", response.mimeType(), "\") that is not \"text/event-stream\". Aborting the connection."); + // FIXME: Console message would be better with a source code location; where would we get that? + scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, WTFMove(message)); + return false; + } + + // If we have a charset, the only allowed value is UTF-8 (case-insensitive). + auto& charset = response.textEncodingName(); + if (!charset.isEmpty() && !equalLettersIgnoringASCIICase(charset, "utf-8")) { + auto message = makeString("EventSource's response has a charset (\"", charset, "\") that is not UTF-8. Aborting the connection."); + // FIXME: Console message would be better with a source code location; where would we get that? + scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, WTFMove(message)); + return false; + } + + return true; +} + void EventSource::didReceiveResponse(unsigned long, const ResourceResponse& response) { ASSERT(m_state == CONNECTING); ASSERT(m_requestInFlight); - m_eventStreamOrigin = SecurityOrigin::create(response.url())->toString(); - int statusCode = response.httpStatusCode(); - bool mimeTypeIsValid = response.mimeType() == "text/event-stream"; - bool responseIsValid = statusCode == 200 && mimeTypeIsValid; - if (responseIsValid) { - const String& charset = response.textEncodingName(); - // If we have a charset, the only allowed value is UTF-8 (case-insensitive). - responseIsValid = charset.isEmpty() || equalIgnoringCase(charset, "UTF-8"); - if (!responseIsValid) { - StringBuilder message; - message.appendLiteral("EventSource's response has a charset (\""); - message.append(charset); - message.appendLiteral("\") that is not UTF-8. Aborting the connection."); - // FIXME: We are missing the source line. - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message.toString()); - } - } else { - // To keep the signal-to-noise ratio low, we only log 200-response with an invalid MIME type. - if (statusCode == 200 && !mimeTypeIsValid) { - StringBuilder message; - message.appendLiteral("EventSource's response has a MIME type (\""); - message.append(response.mimeType()); - message.appendLiteral("\") that is not \"text/event-stream\". Aborting the connection."); - // FIXME: We are missing the source line. - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message.toString()); - } - } - - if (responseIsValid) { - m_state = OPEN; - dispatchEvent(Event::create(eventNames().openEvent, false, false)); - } else { + if (!responseIsValid(response)) { m_loader->cancel(); dispatchEvent(Event::create(eventNames().errorEvent, false, false)); + return; } + + m_eventStreamOrigin = SecurityOrigin::create(response.url())->toString(); + m_state = OPEN; + dispatchEvent(Event::create(eventNames().openEvent, false, false)); } void EventSource::didReceiveData(const char* data, int length) @@ -256,7 +209,7 @@ void EventSource::didReceiveData(const char* data, int length) ASSERT(m_state == OPEN); ASSERT(m_requestInFlight); - append(m_receiveBuf, m_decoder->decode(data, length)); + append(m_receiveBuffer, m_decoder->decode(data, length)); parseEventStream(); } @@ -265,39 +218,40 @@ void EventSource::didFinishLoading(unsigned long, double) ASSERT(m_state == OPEN); ASSERT(m_requestInFlight); - if (m_receiveBuf.size() > 0 || m_data.size() > 0) { - parseEventStream(); + append(m_receiveBuffer, m_decoder->flush()); + parseEventStream(); + + // Discard everything that has not been dispatched by now. + // FIXME: Why does this need to be done? + // If this is important, why isn't it important to clear other data members: m_decoder, m_lastEventId, m_loader? + m_receiveBuffer.clear(); + m_data.clear(); + m_eventName = { }; + m_currentlyParsedEventId = { }; - // Discard everything that has not been dispatched by now. - m_receiveBuf.clear(); - m_data.clear(); - m_eventName = ""; - m_currentlyParsedEventId = String(); - } networkRequestEnded(); } void EventSource::didFail(const ResourceError& error) { ASSERT(m_state != CLOSED); + + if (error.isAccessControl()) { + String message = makeString("EventSource cannot load ", error.failingURL().string(), ". ", error.localizedDescription()); + scriptExecutionContext()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, message); + + abortConnectionAttempt(); + return; + } + ASSERT(m_requestInFlight); if (error.isCancellation()) m_state = CLOSED; - networkRequestEnded(); -} -void EventSource::didFailAccessControlCheck(const ResourceError& error) -{ - String message = makeString("EventSource cannot load ", error.failingURL(), ". ", error.localizedDescription()); - scriptExecutionContext()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message); - - abortConnectionAttempt(); -} + // FIXME: Why don't we need to clear data members here as in didFinishLoading? -void EventSource::didFailRedirectCheck() -{ - abortConnectionAttempt(); + networkRequestEnded(); } void EventSource::abortConnectionAttempt() @@ -317,37 +271,37 @@ void EventSource::abortConnectionAttempt() void EventSource::parseEventStream() { - unsigned int bufPos = 0; - unsigned int bufSize = m_receiveBuf.size(); - while (bufPos < bufSize) { + unsigned position = 0; + unsigned size = m_receiveBuffer.size(); + while (position < size) { if (m_discardTrailingNewline) { - if (m_receiveBuf[bufPos] == '\n') - bufPos++; + if (m_receiveBuffer[position] == '\n') + ++position; m_discardTrailingNewline = false; } - int lineLength = -1; - int fieldLength = -1; - for (unsigned int i = bufPos; lineLength < 0 && i < bufSize; i++) { - switch (m_receiveBuf[i]) { + std::optional lineLength; + std::optional fieldLength; + for (unsigned i = position; !lineLength && i < size; ++i) { + switch (m_receiveBuffer[i]) { case ':': - if (fieldLength < 0) - fieldLength = i - bufPos; + if (!fieldLength) + fieldLength = i - position; break; case '\r': m_discardTrailingNewline = true; FALLTHROUGH; case '\n': - lineLength = i - bufPos; + lineLength = i - position; break; } } - if (lineLength < 0) + if (!lineLength) break; - parseEventStreamLine(bufPos, fieldLength, lineLength); - bufPos += lineLength + 1; + parseEventStreamLine(position, fieldLength, lineLength.value()); + position += lineLength.value() + 1; // EventSource.close() might've been called by one of the message event handlers. // Per spec, no further messages should be fired after that. @@ -355,57 +309,55 @@ void EventSource::parseEventStream() break; } - if (bufPos == bufSize) - m_receiveBuf.clear(); - else if (bufPos) - m_receiveBuf.remove(0, bufPos); + // FIXME: The following operation makes it clear that m_receiveBuffer should be some other type, + // perhaps a Deque or a circular buffer of some sort. + if (position == size) + m_receiveBuffer.clear(); + else if (position) + m_receiveBuffer.remove(0, position); } -void EventSource::parseEventStreamLine(unsigned bufPos, int fieldLength, int lineLength) +void EventSource::parseEventStreamLine(unsigned position, std::optional fieldLength, unsigned lineLength) { if (!lineLength) { - if (!m_data.isEmpty()) { - m_data.removeLast(); - if (!m_currentlyParsedEventId.isNull()) { - m_lastEventId.swap(m_currentlyParsedEventId); - m_currentlyParsedEventId = String(); - } - dispatchEvent(createMessageEvent()); - } - if (!m_eventName.isEmpty()) - m_eventName = ""; - } else if (fieldLength) { - bool noValue = fieldLength < 0; - - String field(&m_receiveBuf[bufPos], noValue ? lineLength : fieldLength); - int step; - if (noValue) - step = lineLength; - else if (m_receiveBuf[bufPos + fieldLength + 1] != ' ') - step = fieldLength + 1; - else - step = fieldLength + 2; - bufPos += step; - int valueLength = lineLength - step; - - if (field == "data") { - if (valueLength) - m_data.append(&m_receiveBuf[bufPos], valueLength); - m_data.append('\n'); - } else if (field == "event") - m_eventName = valueLength ? String(&m_receiveBuf[bufPos], valueLength) : ""; - else if (field == "id") - m_currentlyParsedEventId = valueLength ? String(&m_receiveBuf[bufPos], valueLength) : ""; - else if (field == "retry") { - if (!valueLength) - m_reconnectDelay = defaultReconnectDelay; - else { - String value(&m_receiveBuf[bufPos], valueLength); - bool ok; - unsigned long long retry = value.toUInt64(&ok); - if (ok) - m_reconnectDelay = retry; - } + if (!m_data.isEmpty()) + dispatchMessageEvent(); + m_eventName = { }; + return; + } + + if (fieldLength && !fieldLength.value()) + return; + + StringView field { &m_receiveBuffer[position], fieldLength ? fieldLength.value() : lineLength }; + + unsigned step; + if (!fieldLength) + step = lineLength; + else if (m_receiveBuffer[position + fieldLength.value() + 1] != ' ') + step = fieldLength.value() + 1; + else + step = fieldLength.value() + 2; + position += step; + unsigned valueLength = lineLength - step; + + if (field == "data") { + m_data.append(&m_receiveBuffer[position], valueLength); + m_data.append('\n'); + } else if (field == "event") + m_eventName = { &m_receiveBuffer[position], valueLength }; + else if (field == "id") + m_currentlyParsedEventId = { &m_receiveBuffer[position], valueLength }; + else if (field == "retry") { + if (!valueLength) + m_reconnectDelay = defaultReconnectDelay; + else { + // FIXME: Do we really want to ignore trailing garbage here? Should we be using the strict version instead? + // FIXME: If we can't parse the value, should we leave m_reconnectDelay alone or set it to defaultReconnectDelay? + bool ok; + auto reconnectDelay = charactersToUInt64(&m_receiveBuffer[position], valueLength, &ok); + if (ok) + m_reconnectDelay = reconnectDelay; } } } @@ -415,11 +367,31 @@ void EventSource::stop() close(); } -PassRefPtr EventSource::createMessageEvent() +const char* EventSource::activeDOMObjectName() const { - RefPtr event = MessageEvent::create(); - event->initMessageEvent(m_eventName.isEmpty() ? eventNames().messageEvent : AtomicString(m_eventName), false, false, SerializedScriptValue::create(String::adopt(m_data)), m_eventStreamOrigin, m_lastEventId, 0, 0); - return event.release(); + return "EventSource"; +} + +bool EventSource::canSuspendForDocumentSuspension() const +{ + // FIXME: We should return true here when we can because this object is not actually currently active. + return false; +} + +void EventSource::dispatchMessageEvent() +{ + if (!m_currentlyParsedEventId.isNull()) + m_lastEventId = WTFMove(m_currentlyParsedEventId); + + auto& name = m_eventName.isEmpty() ? eventNames().messageEvent : m_eventName; + + // Omit the trailing "\n" character. + ASSERT(!m_data.isEmpty()); + unsigned size = m_data.size() - 1; + auto data = SerializedScriptValue::create({ m_data.data(), size }); + m_data = { }; + + dispatchEvent(MessageEvent::create(name, WTFMove(data), m_eventStreamOrigin, m_lastEventId)); } } // namespace WebCore diff --git a/Source/WebCore/page/EventSource.h b/Source/WebCore/page/EventSource.h index f2d1c1256..60501f3b2 100644 --- a/Source/WebCore/page/EventSource.h +++ b/Source/WebCore/page/EventSource.h @@ -29,99 +29,110 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef EventSource_h -#define EventSource_h +#pragma once #include "ActiveDOMObject.h" #include "EventTarget.h" +#include "ExceptionOr.h" #include "URL.h" #include "ThreadableLoaderClient.h" #include "Timer.h" -#include #include namespace WebCore { -class Dictionary; class MessageEvent; -class ResourceResponse; class TextResourceDecoder; class ThreadableLoader; class EventSource final : public RefCounted, public EventTargetWithInlineData, private ThreadableLoaderClient, public ActiveDOMObject { WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr create(ScriptExecutionContext&, const String& url, const Dictionary&, ExceptionCode&); + struct Init { + bool withCredentials; + }; + static ExceptionOr> create(ScriptExecutionContext&, const String& url, const Init&); virtual ~EventSource(); - static const unsigned long long defaultReconnectDelay; - - String url() const; + const String& url() const; bool withCredentials() const; - typedef short State; + using State = short; static const State CONNECTING = 0; static const State OPEN = 1; static const State CLOSED = 2; State readyState() const; - DEFINE_ATTRIBUTE_EVENT_LISTENER(open); - DEFINE_ATTRIBUTE_EVENT_LISTENER(message); - DEFINE_ATTRIBUTE_EVENT_LISTENER(error); - void close(); - using RefCounted::ref; - using RefCounted::deref; - - virtual EventTargetInterface eventTargetInterface() const override { return EventSourceEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override { return ActiveDOMObject::scriptExecutionContext(); } + using RefCounted::ref; + using RefCounted::deref; private: - EventSource(ScriptExecutionContext&, const URL&, const Dictionary&); + EventSource(ScriptExecutionContext&, const URL&, const Init&); - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } + EventTargetInterface eventTargetInterface() const final { return EventSourceEventTargetInterfaceType; } + ScriptExecutionContext* scriptExecutionContext() const final { return ActiveDOMObject::scriptExecutionContext(); } - virtual void didReceiveResponse(unsigned long, const ResourceResponse&) override; - virtual void didReceiveData(const char*, int) override; - virtual void didFinishLoading(unsigned long, double) override; - virtual void didFail(const ResourceError&) override; - virtual void didFailAccessControlCheck(const ResourceError&) override; - virtual void didFailRedirectCheck() override; + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } - virtual void stop() override; + // ThreadableLoaderClient + void didReceiveResponse(unsigned long, const ResourceResponse&) final; + void didReceiveData(const char*, int) final; + void didFinishLoading(unsigned long, double) final; + void didFail(const ResourceError&) final; + + void stop() final; + const char* activeDOMObjectName() const final; + bool canSuspendForDocumentSuspension() const final; void connect(); void networkRequestEnded(); void scheduleInitialConnect(); void scheduleReconnect(); - void connectTimerFired(Timer&); void abortConnectionAttempt(); void parseEventStream(); - void parseEventStreamLine(unsigned pos, int fieldLength, int lineLength); - PassRefPtr createMessageEvent(); + void parseEventStreamLine(unsigned position, std::optional fieldLength, unsigned lineLength); + void dispatchMessageEvent(); + + bool responseIsValid(const ResourceResponse&) const; + + static const uint64_t defaultReconnectDelay; URL m_url; bool m_withCredentials; - State m_state; + State m_state { CONNECTING }; - RefPtr m_decoder; + Ref m_decoder; RefPtr m_loader; - Timer m_connectTimer; - Vector m_receiveBuf; - bool m_discardTrailingNewline; - bool m_requestInFlight; + Timer m_connectTimer; + Vector m_receiveBuffer; + bool m_discardTrailingNewline { false }; + bool m_requestInFlight { false }; - String m_eventName; + AtomicString m_eventName; Vector m_data; String m_currentlyParsedEventId; String m_lastEventId; - unsigned long long m_reconnectDelay; + uint64_t m_reconnectDelay { defaultReconnectDelay }; String m_eventStreamOrigin; }; -} // namespace WebCore +inline const String& EventSource::url() const +{ + return m_url.string(); +} + +inline bool EventSource::withCredentials() const +{ + return m_withCredentials; +} -#endif // EventSource_h +inline EventSource::State EventSource::readyState() const +{ + return m_state; +} + +} // namespace WebCore diff --git a/Source/WebCore/page/EventSource.idl b/Source/WebCore/page/EventSource.idl index 1c8adc977..96243609a 100644 --- a/Source/WebCore/page/EventSource.idl +++ b/Source/WebCore/page/EventSource.idl @@ -30,17 +30,14 @@ */ [ - GlobalContext=DOMWindow&WorkerGlobalScope, + Exposed=(Window,Worker), ActiveDOMObject, - Constructor(DOMString url, optional Dictionary eventSourceInit), + Constructor(USVString url, optional EventSourceInit eventSourceInitDict), ConstructorCallWith=ScriptExecutionContext, - ConstructorRaisesException, - EventTarget, - JSNoStaticTables, -] interface EventSource { - - readonly attribute DOMString URL; // Lowercased .url is the one in the spec, but leaving .URL for compatibility reasons. - readonly attribute DOMString url; + ConstructorMayThrowException, +] interface EventSource : EventTarget { + readonly attribute USVString URL; // Lowercased .url is the one in the spec, but leaving .URL for compatibility reasons. + readonly attribute USVString url; readonly attribute boolean withCredentials; // ready state @@ -50,18 +47,12 @@ readonly attribute unsigned short readyState; // networking - attribute EventListener onopen; - attribute EventListener onmessage; - attribute EventListener onerror; + attribute EventHandler onopen; + attribute EventHandler onmessage; + attribute EventHandler onerror; void close(); +}; - // EventTarget interface - void addEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - void removeEventListener(DOMString type, - EventListener listener, - optional boolean useCapture); - [RaisesException] boolean dispatchEvent(Event evt); - +dictionary EventSourceInit { + boolean withCredentials = false; }; diff --git a/Source/WebCore/page/FeatureObserver.cpp b/Source/WebCore/page/FeatureObserver.cpp deleted file mode 100644 index f5397abee..000000000 --- a/Source/WebCore/page/FeatureObserver.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``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 APPLE COMPUTER, INC. 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 "config.h" -#include "FeatureObserver.h" - -#include "DOMWindow.h" -#include "Document.h" -#include "HistogramSupport.h" -#include "Page.h" - -namespace WebCore { - -FeatureObserver::FeatureObserver() -{ -} - -FeatureObserver::~FeatureObserver() -{ - updateMeasurements(); -} - -void FeatureObserver::updateMeasurements() -{ - if (!m_featureBits) - return; - - // Clearing feature bits is timing sensitive. Ports other than chromium do not use HistogramSupport, - // and pull the results on certain navigation events instead. - m_featureBits->clearAll(); -} - -void FeatureObserver::didCommitLoad() -{ - updateMeasurements(); -} - -void FeatureObserver::observe(Document* document, Feature feature) -{ - if (!document) - return; - - Page* page = document->page(); - if (!page) - return; - - page->featureObserver()->didObserve(feature); -} - -void FeatureObserver::observe(DOMWindow* domWindow, Feature feature) -{ - ASSERT(domWindow); - observe(domWindow->document(), feature); -} - -} // namespace WebCore diff --git a/Source/WebCore/page/FeatureObserver.h b/Source/WebCore/page/FeatureObserver.h deleted file mode 100644 index d7f4e42f9..000000000 --- a/Source/WebCore/page/FeatureObserver.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * - * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``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 APPLE COMPUTER, INC. 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. - */ - -#ifndef FeatureObserver_h -#define FeatureObserver_h - -#include -#include -#include -#include - -namespace WebCore { - -class DOMWindow; -class Document; - -class FeatureObserver { - WTF_MAKE_NONCOPYABLE(FeatureObserver); -public: - FeatureObserver(); - ~FeatureObserver(); - - enum Feature { - PageDestruction, - LegacyNotifications, - MultipartMainResource, - PrefixedIndexedDB, - WorkerStart, - SharedWorkerStart, - LegacyWebAudio, - WebAudioStart, - PrefixedContentSecurityPolicy, - UnprefixedIndexedDB, - OpenWebDatabase, - UnusedSlot01, // We used this slot for LegacyHTMLNotifications. - LegacyTextNotifications, - UnprefixedRequestAnimationFrame, - PrefixedRequestAnimationFrame, - ContentSecurityPolicy, - ContentSecurityPolicyReportOnly, - PrefixedContentSecurityPolicyReportOnly, - PrefixedTransitionEndEvent, - UnprefixedTransitionEndEvent, - PrefixedAndUnprefixedTransitionEndEvent, - AutoFocusAttribute, - AutoSaveAttribute, - DataListElement, - FormAttribute, - IncrementalAttribute, - InputTypeColor, - InputTypeDate, - InputTypeDateTime, - InputTypeDateTimeFallback, - InputTypeDateTimeLocal, - InputTypeEmail, - InputTypeMonth, - InputTypeNumber, - InputTypeRange, - InputTypeSearch, - InputTypeTel, - InputTypeTime, - InputTypeURL, - InputTypeWeek, - InputTypeWeekFallback, - ListAttribute, - MaxAttribute, - MinAttribute, - PatternAttribute, - PlaceholderAttribute, - PrecisionAttribute, - PrefixedDirectoryAttribute, - PrefixedSpeechAttribute, - RequiredAttribute, - ResultsAttribute, - StepAttribute, - PageVisits, - HTMLMarqueeElement, - CSSOverflowMarquee, - Reflection, - CursorVisibility, - StorageInfo, - XFrameOptions, - XFrameOptionsSameOrigin, - XFrameOptionsSameOriginWithBadAncestorChain, - DeprecatedFlexboxWebContent, - DeprecatedFlexboxChrome, - DeprecatedFlexboxChromeExtension, - // Add new features above this line. Don't change assigned numbers of each items. - NumberOfFeatures, // This enum value must be last. - }; - - static void observe(Document*, Feature); - static void observe(DOMWindow*, Feature); - void didCommitLoad(); - - const BitVector* accumulatedFeatureBits() const { return m_featureBits.get(); } - -private: - void didObserve(Feature feature) - { - ASSERT(feature != PageDestruction); // PageDestruction is reserved as a scaling factor. - ASSERT(feature < NumberOfFeatures); - if (!m_featureBits) { - m_featureBits = adoptPtr(new BitVector(NumberOfFeatures)); - m_featureBits->clearAll(); - } - m_featureBits->quickSet(feature); - } - - void updateMeasurements(); - - OwnPtr m_featureBits; -}; - -} // namespace WebCore - -#endif // FeatureObserver_h diff --git a/Source/WebCore/page/FocusController.cpp b/Source/WebCore/page/FocusController.cpp index a52b0d720..099cd02fd 100644 --- a/Source/WebCore/page/FocusController.cpp +++ b/Source/WebCore/page/FocusController.cpp @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -37,7 +37,6 @@ #include "Event.h" #include "EventHandler.h" #include "EventNames.h" -#include "ExceptionCode.h" #include "FrameSelection.h" #include "FrameTree.h" #include "FrameView.h" @@ -45,11 +44,12 @@ #include "HTMLImageElement.h" #include "HTMLInputElement.h" #include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "HTMLSlotElement.h" #include "HTMLTextAreaElement.h" #include "HitTestResult.h" #include "KeyboardEvent.h" #include "MainFrame.h" -#include "NodeRenderingTraversal.h" #include "Page.h" #include "Range.h" #include "RenderWidget.h" @@ -60,56 +60,209 @@ #include "Widget.h" #include "htmlediting.h" // For firstPositionInOrBeforeNode #include +#include #include namespace WebCore { using namespace HTMLNames; -FocusNavigationScope::FocusNavigationScope(TreeScope* treeScope) - : m_rootTreeScope(treeScope) +static inline bool hasCustomFocusLogic(const Element& element) { - ASSERT(treeScope); + return is(element) && downcast(element).hasCustomFocusLogic(); } -ContainerNode* FocusNavigationScope::rootNode() const +static inline bool isFocusScopeOwner(const Element& element) { - return m_rootTreeScope->rootNode(); + if (element.shadowRoot() && !hasCustomFocusLogic(element)) + return true; + if (is(element) && downcast(element).assignedNodes()) { + ShadowRoot* root = element.containingShadowRoot(); + if (root && root->host() && !hasCustomFocusLogic(*root->host())) + return true; + } + return false; } -Element* FocusNavigationScope::owner() const +class FocusNavigationScope { +public: + Element* owner() const; + WEBCORE_EXPORT static FocusNavigationScope scopeOf(Node&); + static FocusNavigationScope scopeOwnedByScopeOwner(Element&); + static FocusNavigationScope scopeOwnedByIFrame(HTMLFrameOwnerElement&); + + Node* firstNodeInScope() const; + Node* lastNodeInScope() const; + Node* nextInScope(const Node*) const; + Node* previousInScope(const Node*) const; + Node* lastChildInScope(const Node&) const; + +private: + Node* firstChildInScope(const Node&) const; + + Node* parentInScope(const Node&) const; + + Node* nextSiblingInScope(const Node&) const; + Node* previousSiblingInScope(const Node&) const; + + explicit FocusNavigationScope(TreeScope&); + + explicit FocusNavigationScope(HTMLSlotElement&); + + TreeScope* m_rootTreeScope { nullptr }; + HTMLSlotElement* m_slotElement { nullptr }; +}; + +// FIXME: Focus navigation should work with shadow trees that have slots. +Node* FocusNavigationScope::firstChildInScope(const Node& node) const { - ContainerNode* root = rootNode(); - if (root->isShadowRoot()) - return toShadowRoot(root)->hostElement(); - if (Frame* frame = root->document().frame()) - return frame->ownerElement(); - return 0; + if (is(node) && isFocusScopeOwner(downcast(node))) + return nullptr; + return node.firstChild(); +} + +Node* FocusNavigationScope::lastChildInScope(const Node& node) const +{ + if (is(node) && isFocusScopeOwner(downcast(node))) + return nullptr; + return node.lastChild(); } -FocusNavigationScope FocusNavigationScope::focusNavigationScopeOf(Node* node) +Node* FocusNavigationScope::parentInScope(const Node& node) const +{ + if (m_rootTreeScope && &m_rootTreeScope->rootNode() == &node) + return nullptr; + + if (UNLIKELY(m_slotElement && m_slotElement == node.assignedSlot())) + return nullptr; + + return node.parentNode(); +} + +Node* FocusNavigationScope::nextSiblingInScope(const Node& node) const +{ + if (UNLIKELY(m_slotElement && m_slotElement == node.assignedSlot())) { + for (Node* current = node.nextSibling(); current; current = current->nextSibling()) { + if (current->assignedSlot() == m_slotElement) + return current; + } + return nullptr; + } + return node.nextSibling(); +} + +Node* FocusNavigationScope::previousSiblingInScope(const Node& node) const +{ + if (UNLIKELY(m_slotElement && m_slotElement == node.assignedSlot())) { + for (Node* current = node.previousSibling(); current; current = current->previousSibling()) { + if (current->assignedSlot() == m_slotElement) + return current; + } + return nullptr; + } + return node.previousSibling(); +} + +Node* FocusNavigationScope::firstNodeInScope() const +{ + if (UNLIKELY(m_slotElement)) { + auto* assigneNodes = m_slotElement->assignedNodes(); + ASSERT(assigneNodes); + return assigneNodes->first(); + } + ASSERT(m_rootTreeScope); + return &m_rootTreeScope->rootNode(); +} + +Node* FocusNavigationScope::lastNodeInScope() const +{ + if (UNLIKELY(m_slotElement)) { + auto* assigneNodes = m_slotElement->assignedNodes(); + ASSERT(assigneNodes); + return assigneNodes->last(); + } + ASSERT(m_rootTreeScope); + return &m_rootTreeScope->rootNode(); +} + +Node* FocusNavigationScope::nextInScope(const Node* node) const { ASSERT(node); - Node* root = node; - for (Node* n = node; n; n = NodeRenderingTraversal::parentInScope(n)) - root = n; - // The result is not always a ShadowRoot nor a DocumentNode since - // a starting node is in an orphaned tree in composed shadow tree. - return FocusNavigationScope(&root->treeScope()); + if (Node* next = firstChildInScope(*node)) + return next; + if (Node* next = nextSiblingInScope(*node)) + return next; + const Node* current = node; + while (current && !nextSiblingInScope(*current)) + current = parentInScope(*current); + return current ? nextSiblingInScope(*current) : nullptr; } -FocusNavigationScope FocusNavigationScope::focusNavigationScopeOwnedByShadowHost(Node* node) +Node* FocusNavigationScope::previousInScope(const Node* node) const { ASSERT(node); - ASSERT(toElement(node)->shadowRoot()); - return FocusNavigationScope(toElement(node)->shadowRoot()); + if (Node* current = previousSiblingInScope(*node)) { + while (Node* child = lastChildInScope(*current)) + current = child; + return current; + } + return parentInScope(*node); +} + +FocusNavigationScope::FocusNavigationScope(TreeScope& treeScope) + : m_rootTreeScope(&treeScope) +{ +} + +FocusNavigationScope::FocusNavigationScope(HTMLSlotElement& slotElement) + : m_slotElement(&slotElement) +{ } -FocusNavigationScope FocusNavigationScope::focusNavigationScopeOwnedByIFrame(HTMLFrameOwnerElement* frame) +Element* FocusNavigationScope::owner() const { - ASSERT(frame); - ASSERT(frame->contentFrame()); - return FocusNavigationScope(frame->contentFrame()->document()); + if (m_slotElement) + return m_slotElement; + + ASSERT(m_rootTreeScope); + ContainerNode& root = m_rootTreeScope->rootNode(); + if (is(root)) + return downcast(root).host(); + if (Frame* frame = root.document().frame()) + return frame->ownerElement(); + return nullptr; +} + +FocusNavigationScope FocusNavigationScope::scopeOf(Node& startingNode) +{ + ASSERT(startingNode.isInTreeScope()); + Node* root = nullptr; + for (Node* currentNode = &startingNode; currentNode; currentNode = currentNode->parentNode()) { + root = currentNode; + if (HTMLSlotElement* slot = currentNode->assignedSlot()) { + if (isFocusScopeOwner(*slot)) + return FocusNavigationScope(*slot); + } + if (is(currentNode)) + return FocusNavigationScope(downcast(*currentNode)); + } + ASSERT(root); + return FocusNavigationScope(root->treeScope()); +} + +FocusNavigationScope FocusNavigationScope::scopeOwnedByScopeOwner(Element& element) +{ + ASSERT(element.shadowRoot() || is(element)); + if (is(element)) + return FocusNavigationScope(downcast(element)); + return FocusNavigationScope(*element.shadowRoot()); +} + +FocusNavigationScope FocusNavigationScope::scopeOwnedByIFrame(HTMLFrameOwnerElement& frame) +{ + ASSERT(frame.contentFrame()); + ASSERT(frame.contentFrame()->document()); + return FocusNavigationScope(*frame.contentFrame()->document()); } static inline void dispatchEventsOnWindowAndFocusedElement(Document* document, bool focused) @@ -125,49 +278,45 @@ static inline void dispatchEventsOnWindowAndFocusedElement(Document* document, b } if (!focused && document->focusedElement()) - document->focusedElement()->dispatchBlurEvent(0); + document->focusedElement()->dispatchBlurEvent(nullptr); document->dispatchWindowEvent(Event::create(focused ? eventNames().focusEvent : eventNames().blurEvent, false, false)); if (focused && document->focusedElement()) - document->focusedElement()->dispatchFocusEvent(0, FocusDirectionNone); -} - -static inline bool hasCustomFocusLogic(Element& element) -{ - return element.isHTMLElement() && toHTMLElement(element).hasCustomFocusLogic(); + document->focusedElement()->dispatchFocusEvent(nullptr, FocusDirectionNone); } -static inline bool isNonFocusableShadowHost(Element& element, KeyboardEvent& event) +static inline bool isFocusableElementOrScopeOwner(Element& element, KeyboardEvent& event) { - return !element.isKeyboardFocusable(&event) && element.shadowRoot() && !hasCustomFocusLogic(element); + return element.isKeyboardFocusable(event) || isFocusScopeOwner(element); } -static inline bool isFocusableShadowHost(Node& node, KeyboardEvent& event) +static inline bool isNonFocusableScopeOwner(Element& element, KeyboardEvent& event) { - return node.isElementNode() && toElement(node).isKeyboardFocusable(&event) && toElement(node).shadowRoot() && !hasCustomFocusLogic(toElement(node)); + return !element.isKeyboardFocusable(event) && isFocusScopeOwner(element); } -static inline int adjustedTabIndex(Node& node, KeyboardEvent& event) +static inline bool isFocusableScopeOwner(Element& element, KeyboardEvent& event) { - if (!node.isElementNode()) - return 0; - return isNonFocusableShadowHost(toElement(node), event) ? 0 : toElement(node).tabIndex(); + return element.isKeyboardFocusable(event) && isFocusScopeOwner(element); } -static inline bool shouldVisit(Element& element, KeyboardEvent& event) +static inline int shadowAdjustedTabIndex(Element& element, KeyboardEvent& event) { - return element.isKeyboardFocusable(&event) || isNonFocusableShadowHost(element, event); + if (isNonFocusableScopeOwner(element, event)) { + if (!element.tabIndexSetExplicitly()) + return 0; // Treat a shadow host without tabindex if it has tabindex=0 even though HTMLElement::tabIndex returns -1 on such an element. + } + return element.tabIndex(); } -FocusController::FocusController(Page& page) +FocusController::FocusController(Page& page, ActivityState::Flags activityState) : m_page(page) - , m_isActive(false) - , m_isFocused(false) , m_isChangingFocusedFrame(false) - , m_contentIsVisible(false) + , m_activityState(activityState) + , m_focusRepaintTimer(*this, &FocusController::focusRepaintTimerFired) { } -void FocusController::setFocusedFrame(PassRefPtr frame) +void FocusController::setFocusedFrame(Frame* frame) { ASSERT(!frame || frame->page() == &m_page); if (m_focusedFrame == frame || m_isChangingFocusedFrame) @@ -205,12 +354,12 @@ Frame& FocusController::focusedOrMainFrame() const void FocusController::setFocused(bool focused) { - if (isFocused() == focused) - return; - - m_isFocused = focused; + m_page.setActivityState(focused ? m_activityState | ActivityState::IsFocused : m_activityState & ~ActivityState::IsFocused); +} - if (!m_isFocused) +void FocusController::setFocusedInternal(bool focused) +{ + if (!isFocused()) focusedOrMainFrame().eventHandler().stopAutoscrollTimer(); if (!m_focusedFrame) @@ -222,16 +371,17 @@ void FocusController::setFocused(bool focused) } } -Element* FocusController::findFocusableElementDescendingDownIntoFrameDocument(FocusDirection direction, Element* element, KeyboardEvent* event) +Element* FocusController::findFocusableElementDescendingDownIntoFrameDocument(FocusDirection direction, Element* element, KeyboardEvent& event) { // The node we found might be a HTMLFrameOwnerElement, so descend down the tree until we find either: // 1) a focusable node, or // 2) the deepest-nested HTMLFrameOwnerElement. - while (element && element->isFrameOwnerElement()) { - HTMLFrameOwnerElement& owner = toHTMLFrameOwnerElement(*element); - if (!owner.contentFrame()) + while (is(element)) { + HTMLFrameOwnerElement& owner = downcast(*element); + if (!owner.contentFrame() || !owner.contentFrame()->document()) break; - Element* foundElement = findFocusableElement(direction, FocusNavigationScope::focusNavigationScopeOwnedByIFrame(&owner), 0, event); + owner.contentFrame()->document()->updateLayoutIgnorePendingStylesheets(); + Element* foundElement = findFocusableElementWithinScope(direction, FocusNavigationScope::scopeOwnedByIFrame(owner), nullptr, event); if (!foundElement) break; ASSERT(element != foundElement); @@ -240,9 +390,13 @@ Element* FocusController::findFocusableElementDescendingDownIntoFrameDocument(Fo return element; } -bool FocusController::setInitialFocus(FocusDirection direction, KeyboardEvent* event) +bool FocusController::setInitialFocus(FocusDirection direction, KeyboardEvent* providedEvent) { - bool didAdvanceFocus = advanceFocus(direction, event, true); + RefPtr event = providedEvent; + if (!event) + event = KeyboardEvent::createForDummy(); + + bool didAdvanceFocus = advanceFocus(direction, *event, true); // If focus is being set initially, accessibility needs to be informed that system focus has moved // into the web area again, even if focus did not change within WebCore. PostNotification is called instead @@ -253,7 +407,7 @@ bool FocusController::setInitialFocus(FocusDirection direction, KeyboardEvent* e return didAdvanceFocus; } -bool FocusController::advanceFocus(FocusDirection direction, KeyboardEvent* event, bool initialFocus) +bool FocusController::advanceFocus(FocusDirection direction, KeyboardEvent& event, bool initialFocus) { switch (direction) { case FocusDirectionForward: @@ -271,34 +425,33 @@ bool FocusController::advanceFocus(FocusDirection direction, KeyboardEvent* even return false; } -bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, KeyboardEvent* event, bool initialFocus) +bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, KeyboardEvent& event, bool initialFocus) { Frame& frame = focusedOrMainFrame(); Document* document = frame.document(); - Node* currentNode = document->focusedElement(); + Node* currentNode = document->focusNavigationStartingNode(direction); // FIXME: Not quite correct when it comes to focus transitions leaving/entering the WebView itself bool caretBrowsing = frame.settings().caretBrowsingEnabled(); if (caretBrowsing && !currentNode) - currentNode = frame.selection().start().deprecatedNode(); + currentNode = frame.selection().selection().start().deprecatedNode(); document->updateLayoutIgnorePendingStylesheets(); - RefPtr element = findFocusableElementAcrossFocusScope(direction, FocusNavigationScope::focusNavigationScopeOf(currentNode ? currentNode : document), currentNode, event); + RefPtr element = findFocusableElementAcrossFocusScope(direction, FocusNavigationScope::scopeOf(currentNode ? *currentNode : *document), currentNode, event); if (!element) { // We didn't find a node to focus, so we should try to pass focus to Chrome. if (!initialFocus && m_page.chrome().canTakeFocus(direction)) { - document->setFocusedElement(0); - setFocusedFrame(0); + document->setFocusedElement(nullptr); + setFocusedFrame(nullptr); m_page.chrome().takeFocus(direction); return true; } // Chrome doesn't want focus, so we should wrap focus. - element = findFocusableElementRecursively(direction, FocusNavigationScope::focusNavigationScopeOf(m_page.mainFrame().document()), 0, event); - element = findFocusableElementDescendingDownIntoFrameDocument(direction, element.get(), event); + element = findFocusableElementAcrossFocusScope(direction, FocusNavigationScope::scopeOf(*m_page.mainFrame().document()), nullptr, event); if (!element) return false; @@ -311,10 +464,10 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb return true; } - if (element->isFrameOwnerElement() && (!element->isPluginElement() || !element->isKeyboardFocusable(event))) { + if (is(*element) && (!is(*element) || !element->isKeyboardFocusable(event))) { // We focus frames rather than frame owners. // FIXME: We should not focus frames that have no scrollbars, as focusing them isn't useful to the user. - HTMLFrameOwnerElement& owner = toHTMLFrameOwnerElement(*element); + HTMLFrameOwnerElement& owner = downcast(*element); if (!owner.contentFrame()) return false; @@ -339,114 +492,133 @@ bool FocusController::advanceFocusInDocumentOrder(FocusDirection direction, Keyb if (caretBrowsing) { Position position = firstPositionInOrBeforeNode(element.get()); VisibleSelection newSelection(position, position, DOWNSTREAM); - if (frame.selection().shouldChangeSelection(newSelection)) - frame.selection().setSelection(newSelection); + if (frame.selection().shouldChangeSelection(newSelection)) { + AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, true }); + frame.selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(UserTriggered), intent); + } } element->focus(false, direction); return true; } -Element* FocusController::findFocusableElementAcrossFocusScope(FocusDirection direction, FocusNavigationScope scope, Node* currentNode, KeyboardEvent* event) +Element* FocusController::findFocusableElementAcrossFocusScope(FocusDirection direction, const FocusNavigationScope& scope, Node* currentNode, KeyboardEvent& event) { - ASSERT(!currentNode || !currentNode->isElementNode() || !isNonFocusableShadowHost(*toElement(currentNode), *event)); - Element* found; - if (currentNode && direction == FocusDirectionForward && isFocusableShadowHost(*currentNode, *event)) { - Element* foundInInnerFocusScope = findFocusableElementRecursively(direction, FocusNavigationScope::focusNavigationScopeOwnedByShadowHost(currentNode), 0, event); - found = foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableElementRecursively(direction, scope, currentNode, event); - } else - found = findFocusableElementRecursively(direction, scope, currentNode, event); + ASSERT(!is(currentNode) || !isNonFocusableScopeOwner(downcast(*currentNode), event)); + + if (currentNode && direction == FocusDirectionForward && is(currentNode) && isFocusableScopeOwner(downcast(*currentNode), event)) { + if (Element* candidateInInnerScope = findFocusableElementWithinScope(direction, FocusNavigationScope::scopeOwnedByScopeOwner(downcast(*currentNode)), 0, event)) + return candidateInInnerScope; + } + + if (Element* candidateInCurrentScope = findFocusableElementWithinScope(direction, scope, currentNode, event)) + return candidateInCurrentScope; // If there's no focusable node to advance to, move up the focus scopes until we find one. - while (!found) { - Element* owner = scope.owner(); - if (!owner) - break; - scope = FocusNavigationScope::focusNavigationScopeOf(owner); - if (direction == FocusDirectionBackward && isFocusableShadowHost(*owner, *event)) { - found = owner; - break; - } - found = findFocusableElementRecursively(direction, scope, owner, event); + Element* owner = scope.owner(); + while (owner) { + if (direction == FocusDirectionBackward && isFocusableScopeOwner(*owner, event)) + return findFocusableElementDescendingDownIntoFrameDocument(direction, owner, event); + + auto outerScope = FocusNavigationScope::scopeOf(*owner); + if (Element* candidateInOuterScope = findFocusableElementWithinScope(direction, outerScope, owner, event)) + return candidateInOuterScope; + owner = outerScope.owner(); } - found = findFocusableElementDescendingDownIntoFrameDocument(direction, found, event); - return found; + return nullptr; } -Element* FocusController::findFocusableElementRecursively(FocusDirection direction, FocusNavigationScope scope, Node* start, KeyboardEvent* event) +Element* FocusController::findFocusableElementWithinScope(FocusDirection direction, const FocusNavigationScope& scope, Node* start, KeyboardEvent& event) { // Starting node is exclusive. - Element* found = findFocusableElement(direction, scope, start, event); + Element* candidate = direction == FocusDirectionForward + ? nextFocusableElementWithinScope(scope, start, event) + : previousFocusableElementWithinScope(scope, start, event); + return findFocusableElementDescendingDownIntoFrameDocument(direction, candidate, event); +} + +Element* FocusController::nextFocusableElementWithinScope(const FocusNavigationScope& scope, Node* start, KeyboardEvent& event) +{ + Element* found = nextFocusableElementOrScopeOwner(scope, start, event); if (!found) return nullptr; - if (direction == FocusDirectionForward) { - if (!isNonFocusableShadowHost(*found, *event)) - return found; - Element* foundInInnerFocusScope = findFocusableElementRecursively(direction, FocusNavigationScope::focusNavigationScopeOwnedByShadowHost(found), 0, event); - return foundInInnerFocusScope ? foundInInnerFocusScope : findFocusableElementRecursively(direction, scope, found, event); + if (isNonFocusableScopeOwner(*found, event)) { + if (Element* foundInInnerFocusScope = nextFocusableElementWithinScope(FocusNavigationScope::scopeOwnedByScopeOwner(*found), 0, event)) + return foundInInnerFocusScope; + return nextFocusableElementWithinScope(scope, found, event); } - ASSERT(direction == FocusDirectionBackward); - if (isFocusableShadowHost(*found, *event)) { - Element* foundInInnerFocusScope = findFocusableElementRecursively(direction, FocusNavigationScope::focusNavigationScopeOwnedByShadowHost(found), 0, event); - return foundInInnerFocusScope ? foundInInnerFocusScope : found; + return found; +} + +Element* FocusController::previousFocusableElementWithinScope(const FocusNavigationScope& scope, Node* start, KeyboardEvent& event) +{ + Element* found = previousFocusableElementOrScopeOwner(scope, start, event); + if (!found) + return nullptr; + if (isFocusableScopeOwner(*found, event)) { + // Search an inner focusable element in the shadow tree from the end. + if (Element* foundInInnerFocusScope = previousFocusableElementWithinScope(FocusNavigationScope::scopeOwnedByScopeOwner(*found), 0, event)) + return foundInInnerFocusScope; + return found; } - if (isNonFocusableShadowHost(*found, *event)) { - Element* foundInInnerFocusScope = findFocusableElementRecursively(direction, FocusNavigationScope::focusNavigationScopeOwnedByShadowHost(found), 0, event); - return foundInInnerFocusScope ? foundInInnerFocusScope :findFocusableElementRecursively(direction, scope, found, event); + if (isNonFocusableScopeOwner(*found, event)) { + if (Element* foundInInnerFocusScope = previousFocusableElementWithinScope(FocusNavigationScope::scopeOwnedByScopeOwner(*found), 0, event)) + return foundInInnerFocusScope; + return previousFocusableElementWithinScope(scope, found, event); } return found; } -Element* FocusController::findFocusableElement(FocusDirection direction, FocusNavigationScope scope, Node* node, KeyboardEvent* event) +Element* FocusController::findFocusableElementOrScopeOwner(FocusDirection direction, const FocusNavigationScope& scope, Node* node, KeyboardEvent& event) { return (direction == FocusDirectionForward) - ? nextFocusableElement(scope, node, event) - : previousFocusableElement(scope, node, event); + ? nextFocusableElementOrScopeOwner(scope, node, event) + : previousFocusableElementOrScopeOwner(scope, node, event); } -Element* FocusController::findElementWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent* event, FocusDirection direction) +Element* FocusController::findElementWithExactTabIndex(const FocusNavigationScope& scope, Node* start, int tabIndex, KeyboardEvent& event, FocusDirection direction) { // Search is inclusive of start - using namespace NodeRenderingTraversal; - for (Node* node = start; node; node = direction == FocusDirectionForward ? nextInScope(node) : previousInScope(node)) { - if (!node->isElementNode()) + for (Node* node = start; node; node = direction == FocusDirectionForward ? scope.nextInScope(node) : scope.previousInScope(node)) { + if (!is(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) == tabIndex) + Element& element = downcast(*node); + if (isFocusableElementOrScopeOwner(element, event) && shadowAdjustedTabIndex(element, event) == tabIndex) return &element; } return nullptr; } -static Element* nextElementWithGreaterTabIndex(Node* start, int tabIndex, KeyboardEvent& event) +static Element* nextElementWithGreaterTabIndex(const FocusNavigationScope& scope, int tabIndex, KeyboardEvent& event) { // Search is inclusive of start - int winningTabIndex = std::numeric_limits::max() + 1; + int winningTabIndex = std::numeric_limits::max(); Element* winner = nullptr; - for (Node* node = start; node; node = NodeRenderingTraversal::nextInScope(node)) { - if (!node->isElementNode()) + for (Node* node = scope.firstNodeInScope(); node; node = scope.nextInScope(node)) { + if (!is(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, event) && element.tabIndex() > tabIndex && element.tabIndex() < winningTabIndex) { - winner = &element; - winningTabIndex = element.tabIndex(); + Element& candidate = downcast(*node); + int candidateTabIndex = candidate.tabIndex(); + if (isFocusableElementOrScopeOwner(candidate, event) && candidateTabIndex > tabIndex && (!winner || candidateTabIndex < winningTabIndex)) { + winner = &candidate; + winningTabIndex = candidateTabIndex; } } return winner; } -static Element* previousElementWithLowerTabIndex(Node* start, int tabIndex, KeyboardEvent& event) +static Element* previousElementWithLowerTabIndex(const FocusNavigationScope& scope, Node* start, int tabIndex, KeyboardEvent& event) { // Search is inclusive of start int winningTabIndex = 0; Element* winner = nullptr; - for (Node* node = start; node; node = NodeRenderingTraversal::previousInScope(node)) { - if (!node->isElementNode()) + for (Node* node = start; node; node = scope.previousInScope(node)) { + if (!is(*node)) continue; - Element& element = toElement(*node); - int currentTabIndex = adjustedTabIndex(element, event); - if ((shouldVisit(element, event) || isNonFocusableShadowHost(element, event)) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) { + Element& element = downcast(*node); + int currentTabIndex = shadowAdjustedTabIndex(element, event); + if (isFocusableElementOrScopeOwner(element, event) && currentTabIndex < tabIndex && currentTabIndex > winningTabIndex) { winner = &element; winningTabIndex = currentTabIndex; } @@ -454,83 +626,94 @@ static Element* previousElementWithLowerTabIndex(Node* start, int tabIndex, Keyb return winner; } -Element* FocusController::nextFocusableElement(FocusNavigationScope scope, Node* start, KeyboardEvent* event) +Element* FocusController::nextFocusableElement(Node& start) +{ + // FIXME: This can return a non-focusable shadow host. + Ref keyEvent = KeyboardEvent::createForDummy(); + return nextFocusableElementOrScopeOwner(FocusNavigationScope::scopeOf(start), &start, keyEvent.get()); +} + +Element* FocusController::previousFocusableElement(Node& start) { - using namespace NodeRenderingTraversal; + // FIXME: This can return a non-focusable shadow host. + Ref keyEvent = KeyboardEvent::createForDummy(); + return previousFocusableElementOrScopeOwner(FocusNavigationScope::scopeOf(start), &start, keyEvent.get()); +} + +Element* FocusController::nextFocusableElementOrScopeOwner(const FocusNavigationScope& scope, Node* start, KeyboardEvent& event) +{ + int startTabIndex = 0; + if (start && is(*start)) + startTabIndex = shadowAdjustedTabIndex(downcast(*start), event); if (start) { - int tabIndex = adjustedTabIndex(*start, *event); // If a node is excluded from the normal tabbing cycle, the next focusable node is determined by tree order - if (tabIndex < 0) { - for (Node* node = nextInScope(start); node; node = nextInScope(node)) { - if (!node->isElementNode()) + if (startTabIndex < 0) { + for (Node* node = scope.nextInScope(start); node; node = scope.nextInScope(node)) { + if (!is(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) >= 0) + Element& element = downcast(*node); + if (isFocusableElementOrScopeOwner(element, event) && shadowAdjustedTabIndex(element, event) >= 0) return &element; } } // First try to find a node with the same tabindex as start that comes after start in the scope. - if (Element* winner = findElementWithExactTabIndex(nextInScope(start), tabIndex, event, FocusDirectionForward)) + if (Element* winner = findElementWithExactTabIndex(scope, scope.nextInScope(start), startTabIndex, event, FocusDirectionForward)) return winner; - if (!tabIndex) - // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order. - return 0; + if (!startTabIndex) + return nullptr; // We've reached the last node in the document with a tabindex of 0. This is the end of the tabbing order. } // Look for the first Element in the scope that: // 1) has the lowest tabindex that is higher than start's tabindex (or 0, if start is null), and // 2) comes first in the scope, if there's a tie. - if (Element* winner = nextElementWithGreaterTabIndex(scope.rootNode(), start ? adjustedTabIndex(*start, *event) : 0, *event)) + if (Element* winner = nextElementWithGreaterTabIndex(scope, startTabIndex, event)) return winner; // There are no nodes with a tabindex greater than start's tabindex, // so find the first node with a tabindex of 0. - return findElementWithExactTabIndex(scope.rootNode(), 0, event, FocusDirectionForward); + return findElementWithExactTabIndex(scope, scope.firstNodeInScope(), 0, event, FocusDirectionForward); } -Element* FocusController::previousFocusableElement(FocusNavigationScope scope, Node* start, KeyboardEvent* event) +Element* FocusController::previousFocusableElementOrScopeOwner(const FocusNavigationScope& scope, Node* start, KeyboardEvent& event) { - using namespace NodeRenderingTraversal; - Node* last = nullptr; - for (Node* node = scope.rootNode(); node; node = lastChildInScope(node)) + for (Node* node = scope.lastNodeInScope(); node; node = scope.lastChildInScope(*node)) last = node; ASSERT(last); // First try to find the last node in the scope that comes before start and has the same tabindex as start. // If start is null, find the last node in the scope with a tabindex of 0. Node* startingNode; - int startingTabIndex; + int startingTabIndex = 0; if (start) { - startingNode = previousInScope(start); - startingTabIndex = adjustedTabIndex(*start, *event); - } else { + startingNode = scope.previousInScope(start); + if (is(*start)) + startingTabIndex = shadowAdjustedTabIndex(downcast(*start), event); + } else startingNode = last; - startingTabIndex = 0; - } // However, if a node is excluded from the normal tabbing cycle, the previous focusable node is determined by tree order if (startingTabIndex < 0) { - for (Node* node = startingNode; node; node = previousInScope(node)) { - if (!node->isElementNode()) + for (Node* node = startingNode; node; node = scope.previousInScope(node)) { + if (!is(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) >= 0) + Element& element = downcast(*node); + if (isFocusableElementOrScopeOwner(element, event) && shadowAdjustedTabIndex(element, event) >= 0) return &element; } } - if (Element* winner = findElementWithExactTabIndex(startingNode, startingTabIndex, event, FocusDirectionBackward)) + if (Element* winner = findElementWithExactTabIndex(scope, startingNode, startingTabIndex, event, FocusDirectionBackward)) return winner; // There are no nodes before start with the same tabindex as start, so look for a node that: // 1) has the highest non-zero tabindex (that is less than start's tabindex), and // 2) comes last in the scope, if there's a tie. - startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits::max(); - return previousElementWithLowerTabIndex(last, startingTabIndex, *event); + startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits::max(); + return previousElementWithLowerTabIndex(scope, last, startingTabIndex, event); } static bool relinquishesEditingFocus(Node *node) @@ -543,7 +726,7 @@ static bool relinquishesEditingFocus(Node *node) if (!frame || !root) return false; - return frame->editor().shouldEndEditing(rangeOfContents(*root).get()); + return frame->editor().shouldEndEditing(rangeOfContents(*root).ptr()); } static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFrame, Node* newFocusedNode) @@ -553,8 +736,8 @@ static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFram if (oldFocusedFrame->document() != newFocusedFrame->document()) return; - - FrameSelection& selection = oldFocusedFrame->selection(); + + const VisibleSelection& selection = oldFocusedFrame->selection().selection(); if (selection.isNone()) return; @@ -562,7 +745,7 @@ static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFram if (caretBrowsing) return; - Node* selectionStartNode = selection.selection().start().deprecatedNode(); + Node* selectionStartNode = selection.start().deprecatedNode(); if (selectionStartNode == newFocusedNode || selectionStartNode->isDescendantOf(newFocusedNode) || selectionStartNode->deprecatedShadowAncestorNode() == newFocusedNode) return; @@ -574,21 +757,22 @@ static void clearSelectionIfNeeded(Frame* oldFocusedFrame, Frame* newFocusedFram return; if (Node* shadowAncestorNode = root->deprecatedShadowAncestorNode()) { - if (!isHTMLInputElement(shadowAncestorNode) && !isHTMLTextAreaElement(shadowAncestorNode)) + if (!is(*shadowAncestorNode) && !is(*shadowAncestorNode)) return; } } } - - selection.clear(); + + oldFocusedFrame->selection().clear(); } -bool FocusController::setFocusedElement(Element* element, PassRefPtr newFocusedFrame, FocusDirection direction) +bool FocusController::setFocusedElement(Element* element, Frame& newFocusedFrame, FocusDirection direction) { + Ref protectedNewFocusedFrame = newFocusedFrame; RefPtr oldFocusedFrame = focusedFrame(); - RefPtr oldDocument = oldFocusedFrame ? oldFocusedFrame->document() : 0; + RefPtr oldDocument = oldFocusedFrame ? oldFocusedFrame->document() : nullptr; - Element* oldFocusedElement = oldDocument ? oldDocument->focusedElement() : 0; + Element* oldFocusedElement = oldDocument ? oldDocument->focusedElement() : nullptr; if (oldFocusedElement == element) return true; @@ -596,32 +780,32 @@ bool FocusController::setFocusedElement(Element* element, PassRefPtr newF if (oldFocusedElement && oldFocusedElement->isRootEditableElement() && !relinquishesEditingFocus(oldFocusedElement)) return false; - m_page.editorClient()->willSetInputMethodState(); + m_page.editorClient().willSetInputMethodState(); - clearSelectionIfNeeded(oldFocusedFrame.get(), newFocusedFrame.get(), element); + clearSelectionIfNeeded(oldFocusedFrame.get(), &newFocusedFrame, element); if (!element) { if (oldDocument) - oldDocument->setFocusedElement(0); - m_page.editorClient()->setInputMethodState(false); + oldDocument->setFocusedElement(nullptr); + m_page.editorClient().setInputMethodState(false); return true; } Ref newDocument(element->document()); if (newDocument->focusedElement() == element) { - m_page.editorClient()->setInputMethodState(element->shouldUseInputMethod()); + m_page.editorClient().setInputMethodState(element->shouldUseInputMethod()); return true; } - if (oldDocument && oldDocument != &newDocument.get()) - oldDocument->setFocusedElement(0); + if (oldDocument && oldDocument != newDocument.ptr()) + oldDocument->setFocusedElement(nullptr); - if (newFocusedFrame && !newFocusedFrame->page()) { - setFocusedFrame(0); + if (!newFocusedFrame.page()) { + setFocusedFrame(nullptr); return false; } - setFocusedFrame(newFocusedFrame); + setFocusedFrame(&newFocusedFrame); Ref protect(*element); @@ -630,18 +814,35 @@ bool FocusController::setFocusedElement(Element* element, PassRefPtr newF return false; if (newDocument->focusedElement() == element) - m_page.editorClient()->setInputMethodState(element->shouldUseInputMethod()); + m_page.editorClient().setInputMethodState(element->shouldUseInputMethod()); + + m_focusSetTime = monotonicallyIncreasingTime(); + m_focusRepaintTimer.stop(); return true; } -void FocusController::setActive(bool active) +void FocusController::setActivityState(ActivityState::Flags activityState) { - if (m_isActive == active) - return; + ActivityState::Flags changed = m_activityState ^ activityState; + m_activityState = activityState; + + if (changed & ActivityState::IsFocused) + setFocusedInternal(activityState & ActivityState::IsFocused); + if (changed & ActivityState::WindowIsActive) { + setActiveInternal(activityState & ActivityState::WindowIsActive); + if (changed & ActivityState::IsVisible) + setIsVisibleAndActiveInternal(activityState & ActivityState::WindowIsActive); + } +} - m_isActive = active; +void FocusController::setActive(bool active) +{ + m_page.setActivityState(active ? m_activityState | ActivityState::WindowIsActive : m_activityState & ~ActivityState::WindowIsActive); +} +void FocusController::setActiveInternal(bool active) +{ if (FrameView* view = m_page.mainFrame().view()) { if (!view->platformWidget()) { view->updateLayoutAndStyleIfNeededRecursive(); @@ -663,13 +864,8 @@ static void contentAreaDidShowOrHide(ScrollableArea* scrollableArea, bool didSho scrollableArea->contentAreaDidHide(); } -void FocusController::setContentIsVisible(bool contentIsVisible) +void FocusController::setIsVisibleAndActiveInternal(bool contentIsVisible) { - if (m_contentIsVisible == contentIsVisible) - return; - - m_contentIsVisible = contentIsVisible; - FrameView* view = m_page.mainFrame().view(); if (!view) return; @@ -685,8 +881,7 @@ void FocusController::setContentIsVisible(bool contentIsVisible) if (!scrollableAreas) continue; - for (HashSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { - ScrollableArea* scrollableArea = *it; + for (auto& scrollableArea : *scrollableAreas) { ASSERT(scrollableArea->scrollbarsCanBeActive() || m_page.shouldSuppressScrollbarAnimations()); contentAreaDidShowOrHide(scrollableArea, contentIsVisible); @@ -724,7 +919,7 @@ static void updateFocusCandidateIfNeeded(FocusDirection direction, const FocusCa // If 2 nodes are intersecting, do hit test to find which node in on top. LayoutUnit x = intersectionRect.x() + intersectionRect.width() / 2; LayoutUnit y = intersectionRect.y() + intersectionRect.height() / 2; - HitTestResult result = candidate.visibleNode->document().page()->mainFrame().eventHandler().hitTestResultAtPoint(IntPoint(x, y), HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent); + HitTestResult result = candidate.visibleNode->document().page()->mainFrame().eventHandler().hitTestResultAtPoint(IntPoint(x, y), HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowUserAgentShadowContent); if (candidate.visibleNode->contains(result.innerNode())) { closest = candidate; return; @@ -743,9 +938,8 @@ static void updateFocusCandidateIfNeeded(FocusDirection direction, const FocusCa closest = candidate; } -void FocusController::findFocusCandidateInContainer(Node* container, const LayoutRect& startingRect, FocusDirection direction, KeyboardEvent* event, FocusCandidate& closest) +void FocusController::findFocusCandidateInContainer(Node& container, const LayoutRect& startingRect, FocusDirection direction, KeyboardEvent& event, FocusCandidate& closest) { - ASSERT(container); Node* focusedNode = (focusedFrame() && focusedFrame()->document()) ? focusedFrame()->document()->focusedElement() : 0; Element* element = ElementTraversal::firstWithin(container); @@ -756,8 +950,8 @@ void FocusController::findFocusCandidateInContainer(Node* container, const Layou unsigned candidateCount = 0; for (; element; element = (element->isFrameOwnerElement() || canScrollInDirection(element, direction)) - ? ElementTraversal::nextSkippingChildren(element, container) - : ElementTraversal::next(element, container)) { + ? ElementTraversal::nextSkippingChildren(*element, &container) + : ElementTraversal::next(*element, &container)) { if (element == focusedNode) continue; @@ -772,7 +966,7 @@ void FocusController::findFocusCandidateInContainer(Node* container, const Layou continue; candidateCount++; - candidate.enclosingScrollableBox = container; + candidate.enclosingScrollableBox = &container; updateFocusCandidateIfNeeded(direction, current, candidate, closest); } @@ -784,7 +978,7 @@ void FocusController::findFocusCandidateInContainer(Node* container, const Layou } } -bool FocusController::advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection direction, KeyboardEvent* event) +bool FocusController::advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection direction, KeyboardEvent& event) { if (!container) return false; @@ -796,7 +990,7 @@ bool FocusController::advanceFocusDirectionallyInContainer(Node* container, cons // Find the closest node within current container in the direction of the navigation. FocusCandidate focusCandidate; - findFocusCandidateInContainer(container, newStartingRect, direction, event, focusCandidate); + findFocusCandidateInContainer(*container, newStartingRect, direction, event, focusCandidate); if (focusCandidate.isNull()) { // Nothing to focus, scroll if possible. @@ -847,14 +1041,14 @@ bool FocusController::advanceFocusDirectionallyInContainer(Node* container, cons } // We found a new focus node, navigate to it. - Element* element = toElement(focusCandidate.focusableNode); + Element* element = downcast(focusCandidate.focusableNode); ASSERT(element); element->focus(false, direction); return true; } -bool FocusController::advanceFocusDirectionally(FocusDirection direction, KeyboardEvent* event) +bool FocusController::advanceFocusDirectionally(FocusDirection direction, KeyboardEvent& event) { Document* focusedDocument = focusedOrMainFrame().document(); if (!focusedDocument) @@ -863,8 +1057,8 @@ bool FocusController::advanceFocusDirectionally(FocusDirection direction, Keyboa Element* focusedElement = focusedDocument->focusedElement(); Node* container = focusedDocument; - if (container->isDocumentNode()) - toDocument(container)->updateLayoutIgnorePendingStylesheets(); + if (is(*container)) + downcast(*container).updateLayoutIgnorePendingStylesheets(); // Figure out the starting rect. LayoutRect startingRect; @@ -872,10 +1066,10 @@ bool FocusController::advanceFocusDirectionally(FocusDirection direction, Keyboa if (!hasOffscreenRect(focusedElement)) { container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, focusedElement); startingRect = nodeRectInAbsoluteCoordinates(focusedElement, true /* ignore border */); - } else if (isHTMLAreaElement(focusedElement)) { - HTMLAreaElement* area = toHTMLAreaElement(focusedElement); - container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, area->imageElement()); - startingRect = virtualRectForAreaElementAndDirection(area, direction); + } else if (is(*focusedElement)) { + HTMLAreaElement& area = downcast(*focusedElement); + container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, area.imageElement()); + startingRect = virtualRectForAreaElementAndDirection(&area, direction); } } @@ -887,11 +1081,35 @@ bool FocusController::advanceFocusDirectionally(FocusDirection direction, Keyboa consumed = advanceFocusDirectionallyInContainer(container, startingRect, direction, event); startingRect = nodeRectInAbsoluteCoordinates(container, true /* ignore border */); container = scrollableEnclosingBoxOrParentFrameForNodeInDirection(direction, container); - if (container && container->isDocumentNode()) - toDocument(container)->updateLayoutIgnorePendingStylesheets(); + if (is(container)) + downcast(*container).updateLayoutIgnorePendingStylesheets(); } while (!consumed && container); return consumed; } +void FocusController::setFocusedElementNeedsRepaint() +{ + m_focusRepaintTimer.startOneShot(0.033); +} + +void FocusController::focusRepaintTimerFired() +{ + Document* focusedDocument = focusedOrMainFrame().document(); + if (!focusedDocument) + return; + + Element* focusedElement = focusedDocument->focusedElement(); + if (!focusedElement) + return; + + if (focusedElement->renderer()) + focusedElement->renderer()->repaint(); +} + +double FocusController::timeSinceFocusWasSet() const +{ + return monotonicallyIncreasingTime() - m_focusSetTime; +} + } // namespace WebCore diff --git a/Source/WebCore/page/FocusController.h b/Source/WebCore/page/FocusController.h index b991130ae..c6dcf2220 100644 --- a/Source/WebCore/page/FocusController.h +++ b/Source/WebCore/page/FocusController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,21 +23,21 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FocusController_h -#define FocusController_h +#pragma once +#include "ActivityState.h" #include "FocusDirection.h" #include "LayoutRect.h" +#include "Timer.h" #include -#include #include namespace WebCore { -struct FocusCandidate; class ContainerNode; class Document; class Element; +class FocusNavigationScope; class Frame; class HTMLFrameOwnerElement; class IntRect; @@ -46,52 +46,54 @@ class Node; class Page; class TreeScope; -class FocusNavigationScope { -public: - ContainerNode* rootNode() const; - Element* owner() const; - static FocusNavigationScope focusNavigationScopeOf(Node*); - static FocusNavigationScope focusNavigationScopeOwnedByShadowHost(Node*); - static FocusNavigationScope focusNavigationScopeOwnedByIFrame(HTMLFrameOwnerElement*); - -private: - explicit FocusNavigationScope(TreeScope*); - TreeScope* m_rootTreeScope; -}; +struct FocusCandidate; class FocusController { - WTF_MAKE_NONCOPYABLE(FocusController); WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_FAST_ALLOCATED; public: - explicit FocusController(Page&); + explicit FocusController(Page&, ActivityState::Flags); - void setFocusedFrame(PassRefPtr); + WEBCORE_EXPORT void setFocusedFrame(Frame*); Frame* focusedFrame() const { return m_focusedFrame.get(); } - Frame& focusedOrMainFrame() const; + WEBCORE_EXPORT Frame& focusedOrMainFrame() const; + + WEBCORE_EXPORT bool setInitialFocus(FocusDirection, KeyboardEvent*); + bool advanceFocus(FocusDirection, KeyboardEvent&, bool initialFocus = false); - bool setInitialFocus(FocusDirection, KeyboardEvent*); - bool advanceFocus(FocusDirection, KeyboardEvent*, bool initialFocus = false); + WEBCORE_EXPORT bool setFocusedElement(Element*, Frame&, FocusDirection = FocusDirectionNone); - bool setFocusedElement(Element*, PassRefPtr, FocusDirection = FocusDirectionNone); + void setActivityState(ActivityState::Flags); - void setActive(bool); - bool isActive() const { return m_isActive; } + WEBCORE_EXPORT void setActive(bool); + bool isActive() const { return m_activityState & ActivityState::WindowIsActive; } - void setFocused(bool); - bool isFocused() const { return m_isFocused; } + WEBCORE_EXPORT void setFocused(bool); + bool isFocused() const { return m_activityState & ActivityState::IsFocused; } - void setContentIsVisible(bool); + bool contentIsVisible() const { return m_activityState & ActivityState::IsVisible; } // These methods are used in WebCore/bindings/objc/DOM.mm. - Element* nextFocusableElement(FocusNavigationScope, Node* start, KeyboardEvent*); - Element* previousFocusableElement(FocusNavigationScope, Node* start, KeyboardEvent*); + WEBCORE_EXPORT Element* nextFocusableElement(Node&); + WEBCORE_EXPORT Element* previousFocusableElement(Node&); + + void setFocusedElementNeedsRepaint(); + double timeSinceFocusWasSet() const; private: - bool advanceFocusDirectionally(FocusDirection, KeyboardEvent*); - bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent*, bool initialFocus); + void setActiveInternal(bool); + void setFocusedInternal(bool); + void setIsVisibleAndActiveInternal(bool); + + bool advanceFocusDirectionally(FocusDirection, KeyboardEvent&); + bool advanceFocusInDocumentOrder(FocusDirection, KeyboardEvent&, bool initialFocus); + + Element* findFocusableElementAcrossFocusScope(FocusDirection, const FocusNavigationScope& startScope, Node* start, KeyboardEvent&); + + Element* findFocusableElementWithinScope(FocusDirection, const FocusNavigationScope&, Node* start, KeyboardEvent&); + Element* nextFocusableElementWithinScope(const FocusNavigationScope&, Node* start, KeyboardEvent&); + Element* previousFocusableElementWithinScope(const FocusNavigationScope&, Node* start, KeyboardEvent&); - Element* findFocusableElementAcrossFocusScope(FocusDirection, FocusNavigationScope startScope, Node* start, KeyboardEvent*); - Element* findFocusableElementRecursively(FocusDirection, FocusNavigationScope, Node* start, KeyboardEvent*); - Element* findFocusableElementDescendingDownIntoFrameDocument(FocusDirection, Element*, KeyboardEvent*); + Element* findFocusableElementDescendingDownIntoFrameDocument(FocusDirection, Element*, KeyboardEvent&); // Searches through the given tree scope, starting from start node, for the next/previous selectable element that comes after/before start node. // The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes @@ -102,22 +104,25 @@ private: // @return The focus node that comes after/before start node. // // See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1 - Element* findFocusableElement(FocusDirection, FocusNavigationScope, Node* start, KeyboardEvent*); + Element* findFocusableElementOrScopeOwner(FocusDirection, const FocusNavigationScope&, Node* start, KeyboardEvent&); - Element* findElementWithExactTabIndex(Node* start, int tabIndex, KeyboardEvent*, FocusDirection); + Element* findElementWithExactTabIndex(const FocusNavigationScope&, Node* start, int tabIndex, KeyboardEvent&, FocusDirection); + + Element* nextFocusableElementOrScopeOwner(const FocusNavigationScope&, Node* start, KeyboardEvent&); + Element* previousFocusableElementOrScopeOwner(const FocusNavigationScope&, Node* start, KeyboardEvent&); + + bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent&); + void findFocusCandidateInContainer(Node& container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent&, FocusCandidate& closest); - bool advanceFocusDirectionallyInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*); - void findFocusCandidateInContainer(Node* container, const LayoutRect& startingRect, FocusDirection, KeyboardEvent*, FocusCandidate& closest); + void focusRepaintTimerFired(); Page& m_page; RefPtr m_focusedFrame; - bool m_isActive; - bool m_isFocused; bool m_isChangingFocusedFrame; - bool m_contentIsVisible; + ActivityState::Flags m_activityState; + Timer m_focusRepaintTimer; + double m_focusSetTime; }; } // namespace WebCore - -#endif // FocusController_h diff --git a/Source/WebCore/page/FocusDirection.h b/Source/WebCore/page/FocusDirection.h index 0645a6f9a..816421d67 100644 --- a/Source/WebCore/page/FocusDirection.h +++ b/Source/WebCore/page/FocusDirection.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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 @@ -23,19 +23,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FocusDirection_h -#define FocusDirection_h +#pragma once namespace WebCore { - enum FocusDirection { - FocusDirectionNone = 0, - FocusDirectionForward, - FocusDirectionBackward, - FocusDirectionUp, - FocusDirectionDown, - FocusDirectionLeft, - FocusDirectionRight - }; -} -#endif // FocusDirection_h +enum FocusDirection { + FocusDirectionNone = 0, + FocusDirectionForward, + FocusDirectionBackward, + FocusDirectionUp, + FocusDirectionDown, + FocusDirectionLeft, + FocusDirectionRight +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/Frame.cpp b/Source/WebCore/page/Frame.cpp index 4456c0d5a..990885027 100644 --- a/Source/WebCore/page/Frame.cpp +++ b/Source/WebCore/page/Frame.cpp @@ -5,7 +5,7 @@ * 2000 Simon Hausmann * 2000 Stefan Schimanski <1Stein@gmx.de> * 2001 George Staikos - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2004-2016 Apple Inc. All rights reserved. * Copyright (C) 2005 Alexey Proskuryakov * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008 Eric Seidel @@ -30,9 +30,9 @@ #include "config.h" #include "Frame.h" -#include "AnimationController.h" #include "ApplyStyleCommand.h" #include "BackForwardController.h" +#include "CSSAnimationController.h" #include "CSSComputedStyleDeclaration.h" #include "CSSPropertyNames.h" #include "CachedCSSStyleSheet.h" @@ -61,6 +61,7 @@ #include "HTMLFrameElementBase.h" #include "HTMLNames.h" #include "HTMLTableCellElement.h" +#include "HTMLTableRowElement.h" #include "HitTestResult.h" #include "ImageBuffer.h" #include "InspectorInstrumentation.h" @@ -74,7 +75,7 @@ #include "NodeTraversal.h" #include "Page.h" #include "PageCache.h" -#include "PageGroup.h" +#include "RenderLayerCompositor.h" #include "RenderTableCell.h" #include "RenderText.h" #include "RenderTextControl.h" @@ -82,17 +83,20 @@ #include "RenderView.h" #include "RenderWidget.h" #include "RuntimeEnabledFeatures.h" +#include "SVGDocument.h" +#include "SVGDocumentExtensions.h" #include "SVGNames.h" #include "ScriptController.h" #include "ScriptSourceCode.h" #include "ScrollingCoordinator.h" #include "Settings.h" #include "StyleProperties.h" -#include "TextIterator.h" +#include "StyleScope.h" #include "TextNodeTraversal.h" #include "TextResourceDecoder.h" #include "UserContentController.h" #include "UserContentURLPattern.h" +#include "UserScript.h" #include "UserTypingGestureIndicator.h" #include "VisibleUnits.h" #include "WebKitFontFamilyNames.h" @@ -103,25 +107,11 @@ #include "markup.h" #include "npruntime_impl.h" #include "runtime_root.h" -#include -#include #include #include +#include #include -#if USE(ACCELERATED_COMPOSITING) -#include "RenderLayerCompositor.h" -#endif - -#if ENABLE(SVG) -#include "SVGDocument.h" -#include "SVGDocumentExtensions.h" -#endif - -#if USE(TILED_BACKING_STORE) -#include "TiledBackingStore.h" -#endif - #if PLATFORM(IOS) #include "WKContentObservation.h" #endif @@ -168,22 +158,17 @@ Frame::Frame(Page& page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient& , m_navigationScheduler(*this) , m_ownerElement(ownerElement) , m_script(std::make_unique(*this)) - , m_editor(Editor::create(*this)) - , m_selection(adoptPtr(new FrameSelection(this))) - , m_eventHandler(adoptPtr(new EventHandler(*this))) - , m_animationController(std::make_unique(*this)) + , m_editor(std::make_unique(*this)) + , m_selection(std::make_unique(this)) + , m_animationController(std::make_unique(*this)) #if PLATFORM(IOS) - , m_overflowAutoScrollTimer(this, &Frame::overflowAutoScrollTimerFired) + , m_overflowAutoScrollTimer(*this, &Frame::overflowAutoScrollTimerFired) , m_selectionChangeCallbacksDisabled(false) - , m_timersPausedCount(0) #endif , m_pageZoomFactor(parentPageZoomFactor(this)) , m_textZoomFactor(parentTextZoomFactor(this)) -#if ENABLE(ORIENTATION_EVENTS) - , m_orientation(0) -#endif - , m_inViewSourceMode(false) , m_activeDOMObjectsAndAnimationsSuspendedCount(0) + , m_eventHandler(std::make_unique(*this)) { AtomicString::init(); HTMLNames::init(); @@ -196,12 +181,7 @@ Frame::Frame(Page& page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient& XMLNames::init(); WebKitFontFamilyNames::init(); - if (!ownerElement) { -#if USE(TILED_BACKING_STORE) - // Top level frame only for now. - setTiledBackingStoreEnabled(settings().tiledBackingStoreEnabled()); -#endif - } else { + if (ownerElement) { m_mainFrame.selfOnlyRef(); page.incrementSubframeCount(); ownerElement->setContentFrame(this); @@ -211,29 +191,22 @@ Frame::Frame(Page& page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient& frameCounter.increment(); #endif - // FIXME: We should reconcile the iOS and OpenSource code below. - Frame* parent = parentFromOwnerElement(ownerElement); -#if PLATFORM(IOS) - // Pause future timers if this frame is created when page is in pending state. - if (parent && parent->timersPaused()) - setTimersPaused(true); -#else // Pause future ActiveDOMObjects if this frame is being created while the page is in a paused state. + Frame* parent = parentFromOwnerElement(ownerElement); if (parent && parent->activeDOMObjectsAndAnimationsSuspended()) suspendActiveDOMObjectsAndAnimations(); -#endif } -PassRefPtr Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) +Ref Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) { ASSERT(page); ASSERT(client); - return adoptRef(new Frame(*page, ownerElement, *client)); + return adoptRef(*new Frame(*page, ownerElement, *client)); } Frame::~Frame() { - setView(0); + setView(nullptr); loader().cancelAndClear(); // FIXME: We should not be doing all this work inside the destructor @@ -244,8 +217,8 @@ Frame::~Frame() disconnectOwnerElement(); - for (auto& observer : m_destructionObservers) - observer->frameDestroyed(); + while (auto* destructionObserver = m_destructionObservers.takeAny()) + destructionObserver->frameDestroyed(); if (!isMainFrame()) m_mainFrame.selfOnlyDeref(); @@ -261,7 +234,7 @@ void Frame::removeDestructionObserver(FrameDestructionObserver* observer) m_destructionObservers.remove(observer); } -void Frame::setView(PassRefPtr view) +void Frame::setView(RefPtr&& view) { // We the custom scroll bars as early as possible to prevent m_doc->detach() // from messing with the view such that its scroll bars won't be torn down. @@ -272,35 +245,44 @@ void Frame::setView(PassRefPtr view) // Prepare for destruction now, so any unload event handlers get run and the DOMWindow is // notified. If we wait until the view is destroyed, then things won't be hooked up enough for // these calls to work. - if (!view && m_doc && m_doc->hasLivingRenderTree() && !m_doc->inPageCache()) + if (!view && m_doc && m_doc->pageCacheState() != Document::InPageCache) m_doc->prepareForDestruction(); if (m_view) m_view->unscheduleRelayout(); - eventHandler().clear(); + // This may be called during destruction, so need to do a null check. + if (m_eventHandler) + m_eventHandler->clear(); + + bool hadLivingRenderTree = m_doc ? m_doc->hasLivingRenderTree() : false; + if (hadLivingRenderTree) + m_doc->destroyRenderTree(); - m_view = view; + m_view = WTFMove(view); + if (hadLivingRenderTree && m_view) + m_doc->didBecomeCurrentDocumentInView(); + // Only one form submission is allowed per view of a part. // Since this part may be getting reused as a result of being // pulled from the back/forward cache, reset this flag. loader().resetMultipleFormSubmissionProtection(); - -#if USE(TILED_BACKING_STORE) - if (m_view && tiledBackingStore()) - m_view->setPaintsEntireContents(true); -#endif } -void Frame::setDocument(PassRefPtr newDocument) +void Frame::setDocument(RefPtr&& newDocument) { ASSERT(!newDocument || newDocument->frame() == this); - if (m_doc && m_doc->hasLivingRenderTree() && !m_doc->inPageCache()) + if (m_documentIsBeingReplaced) + return; + + m_documentIsBeingReplaced = true; + + if (m_doc && m_doc->pageCacheState() != Document::InPageCache) m_doc->prepareForDestruction(); - m_doc = newDocument.get(); + m_doc = newDocument.copyRef(); ASSERT(!m_doc || m_doc->domWindow()); ASSERT(!m_doc || m_doc->domWindow()->frame() == this); @@ -308,24 +290,41 @@ void Frame::setDocument(PassRefPtr newDocument) // that the document is not destroyed during this function call. if (newDocument) newDocument->didBecomeCurrentDocumentInFrame(); + + InspectorInstrumentation::frameDocumentUpdated(*this); + + m_documentIsBeingReplaced = false; } #if ENABLE(ORIENTATION_EVENTS) -void Frame::sendOrientationChangeEvent(int orientation) +void Frame::orientationChanged() +{ + Vector> frames; + for (Frame* frame = this; frame; frame = frame->tree().traverseNext()) + frames.append(*frame); + + for (auto& frame : frames) { + if (Document* document = frame->document()) + document->dispatchWindowEvent(Event::create(eventNames().orientationchangeEvent, false, false)); + } +} + +int Frame::orientation() const { - m_orientation = orientation; - if (Document* doc = document()) - doc->dispatchWindowEvent(Event::create(eventNames().orientationchangeEvent, false, false)); + if (m_page) + return m_page->chrome().client().deviceOrientation(); + return 0; } #endif // ENABLE(ORIENTATION_EVENTS) -static PassOwnPtr createRegExpForLabels(const Vector& labels) +static JSC::Yarr::RegularExpression createRegExpForLabels(const Vector& labels) { // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being // the same across calls. We can't do that. - DEFINE_STATIC_LOCAL(JSC::Yarr::RegularExpression, wordRegExp, ("\\w", TextCaseSensitive)); - String pattern("("); + static NeverDestroyed wordRegExp("\\w", TextCaseSensitive); + StringBuilder pattern; + pattern.append('('); unsigned int numLabels = labels.size(); unsigned int i; for (i = 0; i < numLabels; i++) { @@ -334,41 +333,41 @@ static PassOwnPtr createRegExpForLabels(const Vect bool startsWithWordChar = false; bool endsWithWordChar = false; if (label.length()) { - startsWithWordChar = wordRegExp.match(label.substring(0, 1)) >= 0; - endsWithWordChar = wordRegExp.match(label.substring(label.length() - 1, 1)) >= 0; + startsWithWordChar = wordRegExp.get().match(label.substring(0, 1)) >= 0; + endsWithWordChar = wordRegExp.get().match(label.substring(label.length() - 1, 1)) >= 0; } if (i) - pattern.append("|"); + pattern.append('|'); // Search for word boundaries only if label starts/ends with "word characters". // If we always searched for word boundaries, this wouldn't work for languages // such as Japanese. if (startsWithWordChar) - pattern.append("\\b"); + pattern.appendLiteral("\\b"); pattern.append(label); if (endsWithWordChar) - pattern.append("\\b"); + pattern.appendLiteral("\\b"); } - pattern.append(")"); - return adoptPtr(new JSC::Yarr::RegularExpression(pattern, TextCaseInsensitive)); + pattern.append(')'); + return JSC::Yarr::RegularExpression(pattern.toString(), TextCaseInsensitive); } -String Frame::searchForLabelsAboveCell(JSC::Yarr::RegularExpression* regExp, HTMLTableCellElement* cell, size_t* resultDistanceFromStartOfCell) +String Frame::searchForLabelsAboveCell(const JSC::Yarr::RegularExpression& regExp, HTMLTableCellElement* cell, size_t* resultDistanceFromStartOfCell) { HTMLTableCellElement* aboveCell = cell->cellAbove(); if (aboveCell) { // search within the above cell we found for a match size_t lengthSearched = 0; - for (Text* textNode = TextNodeTraversal::firstWithin(aboveCell); textNode; textNode = TextNodeTraversal::next(textNode, aboveCell)) { + for (Text* textNode = TextNodeTraversal::firstWithin(*aboveCell); textNode; textNode = TextNodeTraversal::next(*textNode, aboveCell)) { if (!textNode->renderer() || textNode->renderer()->style().visibility() != VISIBLE) continue; // For each text chunk, run the regexp String nodeString = textNode->data(); - int pos = regExp->searchRev(nodeString); + int pos = regExp.searchRev(nodeString); if (pos >= 0) { if (resultDistanceFromStartOfCell) *resultDistanceFromStartOfCell = lengthSearched; - return nodeString.substring(pos, regExp->matchedLength()); + return nodeString.substring(pos, regExp.matchedLength()); } lengthSearched += nodeString.length(); } @@ -380,16 +379,18 @@ String Frame::searchForLabelsAboveCell(JSC::Yarr::RegularExpression* regExp, HTM return String(); } +// FIXME: This should take an Element&. String Frame::searchForLabelsBeforeElement(const Vector& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove) { - OwnPtr regExp(createRegExpForLabels(labels)); + ASSERT(element); + JSC::Yarr::RegularExpression regExp = createRegExpForLabels(labels); // We stop searching after we've seen this many chars const unsigned int charsSearchedThreshold = 500; // This is the absolute max we search. We allow a little more slop than // charsSearchedThreshold, to make it more likely that we'll search whole nodes. const unsigned int maxCharsSearched = 600; // If the starting element is within a table, the cell that contains it - HTMLTableCellElement* startingTableCell = 0; + HTMLTableCellElement* startingTableCell = nullptr; bool searchedCellAbove = false; if (resultDistance) @@ -400,15 +401,15 @@ String Frame::searchForLabelsBeforeElement(const Vector& labels, Element // walk backwards in the node tree, until another element, or form, or end of tree int unsigned lengthSearched = 0; Node* n; - for (n = NodeTraversal::previous(element); n && lengthSearched < charsSearchedThreshold; n = NodeTraversal::previous(n)) { + for (n = NodeTraversal::previous(*element); n && lengthSearched < charsSearchedThreshold; n = NodeTraversal::previous(*n)) { // We hit another form element or the start of the form - bail out - if (isHTMLFormElement(n) || (n->isHTMLElement() && toElement(n)->isFormControlElement())) + if (is(*n) || is(*n)) break; - if (n->hasTagName(tdTag) && !startingTableCell) { - startingTableCell = toHTMLTableCellElement(n); - } else if (n->hasTagName(trTag) && startingTableCell) { - String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); + if (n->hasTagName(tdTag) && !startingTableCell) + startingTableCell = downcast(n); + else if (is(*n) && startingTableCell) { + String result = searchForLabelsAboveCell(regExp, startingTableCell, resultDistance); if (!result.isEmpty()) { if (resultIsInCellAbove) *resultIsInCellAbove = true; @@ -421,11 +422,11 @@ String Frame::searchForLabelsBeforeElement(const Vector& labels, Element // add 100 for slop, to make it more likely that we'll search whole nodes if (lengthSearched + nodeString.length() > maxCharsSearched) nodeString = nodeString.right(charsSearchedThreshold - lengthSearched); - int pos = regExp->searchRev(nodeString); + int pos = regExp.searchRev(nodeString); if (pos >= 0) { if (resultDistance) *resultDistance = lengthSearched; - return nodeString.substring(pos, regExp->matchedLength()); + return nodeString.substring(pos, regExp.matchedLength()); } lengthSearched += nodeString.length(); } @@ -434,7 +435,7 @@ String Frame::searchForLabelsBeforeElement(const Vector& labels, Element // If we started in a cell, but bailed because we found the start of the form or the // previous element, we still might need to search the row above us for a label. if (startingTableCell && !searchedCellAbove) { - String result = searchForLabelsAboveCell(regExp.get(), startingTableCell, resultDistance); + String result = searchForLabelsAboveCell(regExp, startingTableCell, resultDistance); if (!result.isEmpty()) { if (resultIsInCellAbove) *resultIsInCellAbove = true; @@ -455,7 +456,7 @@ static String matchLabelsAgainstString(const Vector& labels, const Strin replace(mutableStringToMatch, JSC::Yarr::RegularExpression("\\d", TextCaseSensitive), " "); mutableStringToMatch.replace('_', ' '); - OwnPtr regExp(createRegExpForLabels(labels)); + JSC::Yarr::RegularExpression regExp = createRegExpForLabels(labels); // Use the largest match we can find in the whole string int pos; int length; @@ -463,9 +464,9 @@ static String matchLabelsAgainstString(const Vector& labels, const Strin int bestLength = -1; int start = 0; do { - pos = regExp->match(mutableStringToMatch, start); + pos = regExp.match(mutableStringToMatch, start); if (pos != -1) { - length = regExp->matchedLength(); + length = regExp.matchedLength(); if (length >= bestLength) { bestPos = pos; bestLength = length; @@ -489,7 +490,7 @@ String Frame::matchLabelsAgainstElement(const Vector& labels, Element* e if (!resultFromNameAttribute.isEmpty()) return resultFromNameAttribute; - return matchLabelsAgainstString(labels, element->getAttribute(idAttr)); + return matchLabelsAgainstString(labels, element->attributeWithoutSynchronization(idAttr)); } #if PLATFORM(IOS) @@ -505,30 +506,30 @@ void Frame::scrollOverflowLayer(RenderLayer* layer, const IntRect& visibleRect, if (visibleRect.intersects(exposeRect)) return; - int x = layer->scrollXOffset(); + // FIXME: Why isn't this just calling RenderLayer::scrollRectToVisible()? + ScrollOffset scrollOffset = layer->scrollOffset(); int exposeLeft = exposeRect.x(); int exposeRight = exposeLeft + exposeRect.width(); - int clientWidth = box->clientWidth(); + int clientWidth = roundToInt(box->clientWidth()); if (exposeLeft <= 0) - x = std::max(0, x + exposeLeft - clientWidth / 2); + scrollOffset.setX(std::max(0, scrollOffset.x() + exposeLeft - clientWidth / 2)); else if (exposeRight >= clientWidth) - x = std::min(box->scrollWidth() - clientWidth, x + clientWidth / 2); + scrollOffset.setX(std::min(box->scrollWidth() - clientWidth, scrollOffset.x() + clientWidth / 2)); - int y = layer->scrollYOffset(); int exposeTop = exposeRect.y(); int exposeBottom = exposeTop + exposeRect.height(); - int clientHeight = box->clientHeight(); + int clientHeight = roundToInt(box->clientHeight()); if (exposeTop <= 0) - y = std::max(0, y + exposeTop - clientHeight / 2); + scrollOffset.setY(std::max(0, scrollOffset.y() + exposeTop - clientHeight / 2)); else if (exposeBottom >= clientHeight) - y = std::min(box->scrollHeight() - clientHeight, y + clientHeight / 2); + scrollOffset.setY(std::min(box->scrollHeight() - clientHeight, scrollOffset.y() + clientHeight / 2)); - layer->scrollToOffset(IntSize(x, y)); + layer->scrollToOffset(scrollOffset); selection().setCaretRectNeedsUpdate(); selection().updateAppearance(); } -void Frame::overflowAutoScrollTimerFired(Timer*) +void Frame::overflowAutoScrollTimerFired() { if (!eventHandler().mousePressed() || checkOverflowScroll(PerformOverflowScroll) == OverflowScrollNone) { if (m_overflowAutoScrollTimer.isActive()) @@ -616,11 +617,13 @@ int Frame::checkOverflowScroll(OverflowScrollAction action) } } + Ref protectedThis(*this); + if (action == PerformOverflowScroll && (deltaX || deltaY)) { - layer->scrollToOffset(IntSize(layer->scrollXOffset() + deltaX, layer->scrollYOffset() + deltaY)); + layer->scrollToOffset(layer->scrollOffset() + IntSize(deltaX, deltaY)); // Handle making selection. - VisiblePosition visiblePosition(renderer->positionForPoint(selectionPosition)); + VisiblePosition visiblePosition(renderer->positionForPoint(selectionPosition, nullptr)); if (visiblePosition.isNotNull()) { VisibleSelection visibleSelection = selection().selection(); visibleSelection.setExtent(visiblePosition); @@ -653,15 +656,17 @@ void Frame::setPrinting(bool printing, const FloatSize& pageSize, const FloatSiz ResourceCacheValidationSuppressor validationSuppressor(m_doc->cachedResourceLoader()); m_doc->setPrinting(printing); - view()->adjustMediaTypeForPrinting(printing); - - m_doc->styleResolverChanged(RecalcStyleImmediately); - if (shouldUsePrintingLayout()) { - view()->forceLayoutForPagination(pageSize, originalPageSize, maximumShrinkRatio, shouldAdjustViewSize); - } else { - view()->forceLayout(); - if (shouldAdjustViewSize == AdjustViewSize) - view()->adjustViewSize(); + if (auto* frameView = view()) { + frameView->adjustMediaTypeForPrinting(printing); + + m_doc->styleScope().didChangeStyleSheetEnvironment(); + if (shouldUsePrintingLayout()) + frameView->forceLayoutForPagination(pageSize, originalPageSize, maximumShrinkRatio, shouldAdjustViewSize); + else { + frameView->forceLayout(); + if (shouldAdjustViewSize == AdjustViewSize) + frameView->adjustViewSize(); + } } // Subframes of the one we're printing don't lay out to the page size. @@ -701,76 +706,52 @@ void Frame::injectUserScripts(UserScriptInjectionTime injectionTime) if (!m_page) return; - if (loader().stateMachine()->creatingInitialEmptyDocument() && !settings().shouldInjectUserScriptsInInitialEmptyDocument()) - return; - - const auto* userContentController = m_page->userContentController(); - if (!userContentController) - return; - - // Walk the hashtable. Inject by world. - const UserScriptMap* userScripts = userContentController->userScripts(); - if (!userScripts) + if (loader().stateMachine().creatingInitialEmptyDocument() && !settings().shouldInjectUserScriptsInInitialEmptyDocument()) return; - for (const auto& worldAndUserScript : *userScripts) - injectUserScriptsForWorld(*worldAndUserScript.key, *worldAndUserScript.value, injectionTime); -} - -void Frame::injectUserScriptsForWorld(DOMWrapperWorld& world, const UserScriptVector& userScripts, UserScriptInjectionTime injectionTime) -{ - if (userScripts.isEmpty()) - return; - - Document* doc = document(); - if (!doc) + Document* document = this->document(); + if (!document) return; - Vector sourceCode; - unsigned count = userScripts.size(); - for (unsigned i = 0; i < count; ++i) { - UserScript* script = userScripts[i].get(); - if (script->injectedFrames() == InjectInTopFrameOnly && ownerElement()) - continue; + m_page->userContentProvider().forEachUserScript([&](DOMWrapperWorld& world, const UserScript& script) { + if (script.injectedFrames() == InjectInTopFrameOnly && ownerElement()) + return; - if (script->injectionTime() == injectionTime && UserContentURLPattern::matchesPatterns(doc->url(), script->whitelist(), script->blacklist())) - m_script->evaluateInWorld(ScriptSourceCode(script->source(), script->url()), world); - } + if (script.injectionTime() == injectionTime && UserContentURLPattern::matchesPatterns(document->url(), script.whitelist(), script.blacklist())) { + m_page->setAsRunningUserScripts(); + m_script->evaluateInWorld(ScriptSourceCode(script.source(), script.url()), world); + } + }); } RenderView* Frame::contentRenderer() const { - return document() ? document()->renderView() : 0; + return document() ? document()->renderView() : nullptr; } RenderWidget* Frame::ownerRenderer() const { - HTMLFrameOwnerElement* ownerElement = m_ownerElement; + auto* ownerElement = m_ownerElement; if (!ownerElement) - return 0; - auto object = ownerElement->renderer(); - if (!object) - return 0; + return nullptr; + auto* object = ownerElement->renderer(); // FIXME: If is ever fixed to disassociate itself from frames // that it has started but canceled, then this can turn into an ASSERT - // since m_ownerElement would be 0 when the load is canceled. + // since m_ownerElement would be nullptr when the load is canceled. // https://bugs.webkit.org/show_bug.cgi?id=18585 - if (!object->isWidget()) - return 0; - return toRenderWidget(object); + if (!is(object)) + return nullptr; + return downcast(object); } -Frame* Frame::frameForWidget(const Widget* widget) +Frame* Frame::frameForWidget(const Widget& widget) { - ASSERT_ARG(widget, widget); - - if (RenderWidget* renderer = RenderWidget::find(widget)) + if (auto* renderer = RenderWidget::find(widget)) return renderer->frameOwnerElement().document().frame(); // Assume all widgets are either a FrameView or owned by a RenderWidget. // FIXME: That assumption is not right for scroll bars! - ASSERT_WITH_SECURITY_IMPLICATION(widget->isFrameView()); - return &toFrameView(widget)->frame(); + return &downcast(widget).frame(); } void Frame::clearTimers(FrameView *view, Document *document) @@ -798,35 +779,34 @@ void Frame::willDetachPage() // FIXME: It's unclear as to why this is called more than once, but it is, // so page() could be NULL. if (page() && page()->focusController().focusedFrame() == this) - page()->focusController().setFocusedFrame(0); + page()->focusController().setFocusedFrame(nullptr); if (page() && page()->scrollingCoordinator() && m_view) - page()->scrollingCoordinator()->willDestroyScrollableArea(m_view.get()); + page()->scrollingCoordinator()->willDestroyScrollableArea(*m_view); #if PLATFORM(IOS) if (WebThreadCountOfObservedContentModifiers() > 0 && m_page) - m_page->chrome().client().clearContentChangeObservers(this); + m_page->chrome().client().clearContentChangeObservers(*this); #endif script().clearScriptObjects(); script().updatePlatformScriptObjects(); + + // We promise that the Frame is always connected to a Page while the render tree is live. + // + // The render tree can be torn down in a few different ways, but the two important ones are: + // + // - When calling Frame::setView() with a null FrameView*. This is always done before calling + // Frame::willDetachPage (this function.) Hence the assertion below. + // + // - When adding a document to the page cache, the tree is torn down before instantiating + // the CachedPage+CachedFrame object tree. + ASSERT(!document() || !document()->renderView()); } void Frame::disconnectOwnerElement() { if (m_ownerElement) { - // We use the ownerElement's document to retrieve the cache, because the contentDocument for this - // frame is already detached (and can't access the top level AX cache). - // However, we pass in the current document to clearTextMarkerNodesInUse so we can identify the - // nodes inside this document that need to be removed from the cache. - - // We don't clear the AXObjectCache here because we don't want to clear the top level cache - // when a sub-frame is removed. -#if HAVE(ACCESSIBILITY) - if (AXObjectCache* cache = m_ownerElement->document().existingAXObjectCache()) - cache->clearTextMarkerNodesInUse(document()); -#endif - m_ownerElement->clearContentFrame(); if (m_page) m_page->decrementSubframeCount(); @@ -839,7 +819,7 @@ String Frame::displayStringModifiedByEncoding(const String& str) const return document() ? document()->displayStringModifiedByEncoding(str) : str; } -VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) +VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) const { HitTestResult result = eventHandler().hitTestResultAtPoint(framePoint, HitTestRequest::ReadOnly | HitTestRequest::Active); Node* node = result.innerNonSharedNode(); @@ -848,7 +828,7 @@ VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) auto renderer = node->renderer(); if (!renderer) return VisiblePosition(); - VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint()); + VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint(), nullptr); if (visiblePos.isNull()) visiblePos = firstPositionInOrBeforeNode(node); return visiblePos; @@ -857,7 +837,7 @@ VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) Document* Frame::documentAtPoint(const IntPoint& point) { if (!view()) - return 0; + return nullptr; IntPoint pt = view()->windowToContents(point); HitTestResult result = HitTestResult(pt); @@ -867,28 +847,33 @@ Document* Frame::documentAtPoint(const IntPoint& point) return result.innerNode() ? &result.innerNode()->document() : 0; } -PassRefPtr Frame::rangeForPoint(const IntPoint& framePoint) +RefPtr Frame::rangeForPoint(const IntPoint& framePoint) { VisiblePosition position = visiblePositionForPoint(framePoint); if (position.isNull()) - return 0; + return nullptr; + + Position deepPosition = position.deepEquivalent(); + Text* containerText = deepPosition.containerText(); + if (!containerText || !containerText->renderer() || containerText->renderer()->style().userSelect() == SELECT_NONE) + return nullptr; VisiblePosition previous = position.previous(); if (previous.isNotNull()) { RefPtr previousCharacterRange = makeRange(previous, position); LayoutRect rect = editor().firstRectForRange(previousCharacterRange.get()); if (rect.contains(framePoint)) - return previousCharacterRange.release(); + return previousCharacterRange; } VisiblePosition next = position.next(); if (RefPtr nextCharacterRange = makeRange(position, next)) { LayoutRect rect = editor().firstRectForRange(nextCharacterRange.get()); if (rect.contains(framePoint)) - return nextCharacterRange.release(); + return nextCharacterRange; } - return 0; + return nullptr; } void Frame::createView(const IntSize& viewportSize, const Color& backgroundColor, bool transparent, @@ -896,7 +881,6 @@ void Frame::createView(const IntSize& viewportSize, const Color& backgroundColor bool useFixedLayout, ScrollbarMode horizontalScrollbarMode, bool horizontalLock, ScrollbarMode verticalScrollbarMode, bool verticalLock) { - ASSERT(this); ASSERT(m_page); bool isMainFrame = this->isMainFrame(); @@ -904,13 +888,13 @@ void Frame::createView(const IntSize& viewportSize, const Color& backgroundColor if (isMainFrame && view()) view()->setParentVisible(false); - setView(0); + setView(nullptr); RefPtr frameView; if (isMainFrame) { frameView = FrameView::create(*this, viewportSize); frameView->setFixedLayoutSize(fixedLayoutSize); -#if USE(TILED_BACKING_STORE) +#if USE(COORDINATED_GRAPHICS) frameView->setFixedVisibleContentRect(fixedVisibleContentRect); #else UNUSED_PARAM(fixedVisibleContentRect); @@ -921,7 +905,7 @@ void Frame::createView(const IntSize& viewportSize, const Color& backgroundColor frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode, horizontalLock, verticalLock); - setView(frameView); + setView(frameView.copyRef()); if (backgroundColor.isValid()) frameView->updateBackgroundRecursively(backgroundColor, transparent); @@ -936,80 +920,14 @@ void Frame::createView(const IntSize& viewportSize, const Color& backgroundColor view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff); } -#if USE(TILED_BACKING_STORE) -void Frame::setTiledBackingStoreEnabled(bool enabled) -{ - if (!enabled) { - m_tiledBackingStore.clear(); - return; - } - if (m_tiledBackingStore) - return; - m_tiledBackingStore = adoptPtr(new TiledBackingStore(this)); - m_tiledBackingStore->setCommitTileUpdatesOnIdleEventLoop(true); - if (m_view) - m_view->setPaintsEntireContents(true); -} - -void Frame::tiledBackingStorePaintBegin() -{ - if (!m_view) - return; - m_view->updateLayoutAndStyleIfNeededRecursive(); -} - -void Frame::tiledBackingStorePaint(GraphicsContext* context, const IntRect& rect) -{ - if (!m_view) - return; - m_view->paintContents(context, rect); -} - -void Frame::tiledBackingStorePaintEnd(const Vector& paintedArea) -{ - if (!m_page || !m_view) - return; - unsigned size = paintedArea.size(); - // Request repaint from the system - for (unsigned n = 0; n < size; ++n) - m_page->chrome().invalidateContentsAndRootView(m_view->contentsToRootView(paintedArea[n]), false); -} - -IntRect Frame::tiledBackingStoreContentsRect() -{ - if (!m_view) - return IntRect(); - return IntRect(IntPoint(), m_view->contentsSize()); -} - -IntRect Frame::tiledBackingStoreVisibleRect() -{ - if (!m_page) - return IntRect(); - return m_page->chrome().client().visibleRectForTiledBackingStore(); -} - -Color Frame::tiledBackingStoreBackgroundColor() const -{ - if (!m_view) - return Color(); - return m_view->baseBackgroundColor(); -} -#endif - String Frame::layerTreeAsText(LayerTreeFlags flags) const { -#if USE(ACCELERATED_COMPOSITING) document()->updateLayout(); if (!contentRenderer()) return String(); return contentRenderer()->compositor().layerTreeAsText(flags); -#else - UNUSED_PARAM(flags); - return String(); -#endif } String Frame::trackedRepaintRectsAsText() const @@ -1044,14 +962,10 @@ void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor m_editor->dismissCorrectionPanelAsIgnored(); -#if ENABLE(SVG) // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents. // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG WG clarification. - if (document->isSVGDocument()) { - if (!toSVGDocument(document)->zoomAndPanEnabled()) - return; - } -#endif + if (is(*document) && !downcast(*document).zoomAndPanEnabled()) + return; if (m_pageZoomFactor != pageZoomFactor) { if (FrameView* view = this->view()) { @@ -1074,9 +988,6 @@ void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor if (document->renderView() && document->renderView()->needsLayout() && view->didFirstLayout()) view->layout(); } - - if (isMainFrame()) - pageCache()->markPagesForFullStyleRecalc(page); } float Frame::frameScaleFactor() const @@ -1100,30 +1011,33 @@ void Frame::suspendActiveDOMObjectsAndAnimations() return; // FIXME: Suspend/resume calls will not match if the frame is navigated, and gets a new document. - if (document()) { - document()->suspendScriptedAnimationControllerCallbacks(); - animation().suspendAnimationsForDocument(document()); - document()->suspendActiveDOMObjects(ActiveDOMObject::PageWillBeSuspended); - } + clearTimers(); // Suspends animations and pending relayouts. + if (m_doc) + m_doc->suspendScheduledTasks(ActiveDOMObject::PageWillBeSuspended); } void Frame::resumeActiveDOMObjectsAndAnimations() { - ASSERT(activeDOMObjectsAndAnimationsSuspended()); + if (!activeDOMObjectsAndAnimationsSuspended()) + return; m_activeDOMObjectsAndAnimationsSuspendedCount--; if (activeDOMObjectsAndAnimationsSuspended()) return; - if (document()) { - document()->resumeActiveDOMObjects(ActiveDOMObject::PageWillBeSuspended); - animation().resumeAnimationsForDocument(document()); - document()->resumeScriptedAnimationControllerCallbacks(); - } + if (!m_doc) + return; + + // FIXME: Suspend/resume calls will not match if the frame is navigated, and gets a new document. + m_doc->resumeScheduledTasks(ActiveDOMObject::PageWillBeSuspended); + + // Frame::clearTimers() suspended animations and pending relayouts. + animation().resumeAnimationsForDocument(m_doc.get()); + if (m_view) + m_view->scheduleRelayout(); } -#if USE(ACCELERATED_COMPOSITING) void Frame::deviceOrPageScaleFactorChanged() { for (RefPtr child = tree().firstChild(); child; child = child->tree().nextSibling()) @@ -1132,7 +1046,6 @@ void Frame::deviceOrPageScaleFactorChanged() if (RenderView* root = contentRenderer()) root->compositor().deviceOrPageScaleFactorChanged(); } -#endif bool Frame::isURLAllowed(const URL& url) const { @@ -1151,4 +1064,9 @@ bool Frame::isURLAllowed(const URL& url) const return true; } +bool Frame::isAlwaysOnLoggingAllowed() const +{ + return page() && page()->isAlwaysOnLoggingAllowed(); +} + } // namespace WebCore diff --git a/Source/WebCore/page/Frame.h b/Source/WebCore/page/Frame.h index eff8e141d..fb40aa267 100644 --- a/Source/WebCore/page/Frame.h +++ b/Source/WebCore/page/Frame.h @@ -5,7 +5,7 @@ * 2000-2001 Simon Hausmann * 2000-2001 Dirk Mueller * 2000 Stefan Schimanski <1Stein@gmx.de> - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2004-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008 Eric Seidel * @@ -25,8 +25,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef Frame_h -#define Frame_h +#pragma once #include "AdjustViewSizeOrNot.h" #include "FrameLoader.h" @@ -35,7 +34,8 @@ #include "NavigationScheduler.h" #include "ScrollTypes.h" #include "UserScriptTypes.h" -#include +#include +#include #if PLATFORM(IOS) #include "ViewportArguments.h" @@ -46,15 +46,8 @@ #include "FrameWin.h" #endif -#if USE(TILED_BACKING_STORE) -#include "TiledBackingStoreClient.h" -#endif - -#if PLATFORM(IOS) -OBJC_CLASS DOMCSSStyleDeclaration; -OBJC_CLASS DOMNode; +#if PLATFORM(COCOA) OBJC_CLASS NSArray; -OBJC_CLASS NSString; #endif #if PLATFORM(WIN) @@ -67,389 +60,357 @@ class RegularExpression; namespace WebCore { - class AnimationController; - class Color; - class Document; - class Editor; - class Element; - class EventHandler; - class FloatSize; - class FrameDestructionObserver; - class FrameSelection; - class FrameView; - class HTMLFrameOwnerElement; - class HTMLTableCellElement; - class HitTestResult; - class ImageBuffer; - class IntRect; - class MainFrame; - class Node; - class Range; - class RenderLayer; - class RenderView; - class RenderWidget; - class ScriptController; - class Settings; - class TiledBackingStore; - class VisiblePosition; - class Widget; +class CSSAnimationController; +class Color; +class Document; +class Editor; +class Element; +class EventHandler; +class FloatSize; +class FrameDestructionObserver; +class FrameSelection; +class FrameView; +class HTMLFrameOwnerElement; +class HTMLTableCellElement; +class HitTestResult; +class ImageBuffer; +class IntRect; +class MainFrame; +class Node; +class Range; +class RenderLayer; +class RenderView; +class RenderWidget; +class ScriptController; +class Settings; +class VisiblePosition; +class Widget; #if PLATFORM(IOS) - enum { - OverflowScrollNone = 0, - OverflowScrollLeft = 1 << 0, - OverflowScrollRight = 1 << 1, - OverflowScrollUp = 1 << 2, - OverflowScrollDown = 1 << 3 - }; - - enum OverflowScrollAction { DoNotPerformOverflowScroll, PerformOverflowScroll }; - typedef Node* (*NodeQualifier)(const HitTestResult&, Node* terminationNode, IntRect* nodeBounds); +enum { + OverflowScrollNone = 0, + OverflowScrollLeft = 1 << 0, + OverflowScrollRight = 1 << 1, + OverflowScrollUp = 1 << 2, + OverflowScrollDown = 1 << 3 +}; + +enum OverflowScrollAction { DoNotPerformOverflowScroll, PerformOverflowScroll }; +typedef Node* (*NodeQualifier)(const HitTestResult&, Node* terminationNode, IntRect* nodeBounds); #endif -#if !USE(TILED_BACKING_STORE) - class TiledBackingStoreClient { }; -#endif - - enum { - LayerTreeFlagsIncludeDebugInfo = 1 << 0, - LayerTreeFlagsIncludeVisibleRects = 1 << 1, - LayerTreeFlagsIncludeTileCaches = 1 << 2, - LayerTreeFlagsIncludeRepaintRects = 1 << 3, - LayerTreeFlagsIncludePaintingPhases = 1 << 4, - LayerTreeFlagsIncludeContentLayers = 1 << 5 - }; - typedef unsigned LayerTreeFlags; - - class Frame : public RefCounted, public TiledBackingStoreClient { - public: - static PassRefPtr create(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); - - void init(); +enum { + LayerTreeFlagsIncludeDebugInfo = 1 << 0, + LayerTreeFlagsIncludeVisibleRects = 1 << 1, + LayerTreeFlagsIncludeTileCaches = 1 << 2, + LayerTreeFlagsIncludeRepaintRects = 1 << 3, + LayerTreeFlagsIncludePaintingPhases = 1 << 4, + LayerTreeFlagsIncludeContentLayers = 1 << 5, + LayerTreeFlagsIncludeAcceleratesDrawing = 1 << 6, +}; +typedef unsigned LayerTreeFlags; + +class Frame : public ThreadSafeRefCounted { +public: + WEBCORE_EXPORT static Ref create(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); + + void init(); #if PLATFORM(IOS) - // Creates doing minimal amount of work. - void initWithSimpleHTMLDocument(const String& style, const URL&); + // Creates doing minimal amount of work. + WEBCORE_EXPORT void initWithSimpleHTMLDocument(const String& style, const URL&); #endif - void setView(PassRefPtr); - void createView(const IntSize&, const Color&, bool, - const IntSize& fixedLayoutSize = IntSize(), const IntRect& fixedVisibleContentRect = IntRect(), - bool useFixedLayout = false, ScrollbarMode = ScrollbarAuto, bool horizontalLock = false, - ScrollbarMode = ScrollbarAuto, bool verticalLock = false); + WEBCORE_EXPORT void setView(RefPtr&&); + WEBCORE_EXPORT void createView(const IntSize&, const Color&, bool, + const IntSize& fixedLayoutSize = IntSize(), const IntRect& fixedVisibleContentRect = IntRect(), + bool useFixedLayout = false, ScrollbarMode = ScrollbarAuto, bool horizontalLock = false, + ScrollbarMode = ScrollbarAuto, bool verticalLock = false); - virtual ~Frame(); + WEBCORE_EXPORT virtual ~Frame(); - void addDestructionObserver(FrameDestructionObserver*); - void removeDestructionObserver(FrameDestructionObserver*); + void addDestructionObserver(FrameDestructionObserver*); + void removeDestructionObserver(FrameDestructionObserver*); - void willDetachPage(); - void detachFromPage(); - void disconnectOwnerElement(); + void willDetachPage(); + void detachFromPage(); + void disconnectOwnerElement(); - MainFrame& mainFrame() const; - bool isMainFrame() const; + MainFrame& mainFrame() const; + bool isMainFrame() const { return this == static_cast(&m_mainFrame); } - Page* page() const; - HTMLFrameOwnerElement* ownerElement() const; + Page* page() const; + HTMLFrameOwnerElement* ownerElement() const; - Document* document() const; - FrameView* view() const; + Document* document() const; + FrameView* view() const; - Editor& editor() const; - EventHandler& eventHandler() const; - FrameLoader& loader() const; - NavigationScheduler& navigationScheduler() const; - FrameSelection& selection() const; - FrameTree& tree() const; - AnimationController& animation() const; - ScriptController& script(); - - RenderView* contentRenderer() const; // Root of the render tree for the document contained in this frame. - RenderWidget* ownerRenderer() const; // Renderer for the element that contains this frame. + Editor& editor() const; + EventHandler& eventHandler() const; + EventHandler* eventHandlerPtr() const; + FrameLoader& loader() const; + NavigationScheduler& navigationScheduler() const; + FrameSelection& selection() const; + FrameTree& tree() const; + CSSAnimationController& animation() const; + ScriptController& script(); + + WEBCORE_EXPORT RenderView* contentRenderer() const; // Root of the render tree for the document contained in this frame. + WEBCORE_EXPORT RenderWidget* ownerRenderer() const; // Renderer for the element that contains this frame. - // ======== All public functions below this point are candidates to move out of Frame into another class. ======== + bool documentIsBeingReplaced() const { return m_documentIsBeingReplaced; } - void injectUserScripts(UserScriptInjectionTime); - - String layerTreeAsText(LayerTreeFlags = 0) const; - String trackedRepaintRectsAsText() const; +// ======== All public functions below this point are candidates to move out of Frame into another class. ======== - static Frame* frameForWidget(const Widget*); + void injectUserScripts(UserScriptInjectionTime); + + WEBCORE_EXPORT String layerTreeAsText(LayerTreeFlags = 0) const; + WEBCORE_EXPORT String trackedRepaintRectsAsText() const; - Settings& settings() const { return *m_settings; } + WEBCORE_EXPORT static Frame* frameForWidget(const Widget&); - void setPrinting(bool printing, const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkRatio, AdjustViewSizeOrNot); - bool shouldUsePrintingLayout() const; - FloatSize resizePageRectsKeepingRatio(const FloatSize& originalSize, const FloatSize& expectedSize); + Settings& settings() const { return *m_settings; } - bool inViewSourceMode() const; - void setInViewSourceMode(bool = true); + void setPrinting(bool printing, const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkRatio, AdjustViewSizeOrNot); + bool shouldUsePrintingLayout() const; + WEBCORE_EXPORT FloatSize resizePageRectsKeepingRatio(const FloatSize& originalSize, const FloatSize& expectedSize); - void setDocument(PassRefPtr); + void setDocument(RefPtr&&); - void setPageZoomFactor(float factor); - float pageZoomFactor() const { return m_pageZoomFactor; } - void setTextZoomFactor(float factor); - float textZoomFactor() const { return m_textZoomFactor; } - void setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor); + WEBCORE_EXPORT void setPageZoomFactor(float); + float pageZoomFactor() const { return m_pageZoomFactor; } + WEBCORE_EXPORT void setTextZoomFactor(float); + float textZoomFactor() const { return m_textZoomFactor; } + WEBCORE_EXPORT void setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor); - // Scale factor of this frame with respect to the container. - float frameScaleFactor() const; + // Scale factor of this frame with respect to the container. + WEBCORE_EXPORT float frameScaleFactor() const; -#if USE(ACCELERATED_COMPOSITING) - void deviceOrPageScaleFactorChanged(); + void deviceOrPageScaleFactorChanged(); + +#if ENABLE(DATA_DETECTION) + void setDataDetectionResults(NSArray *results) { m_dataDetectionResults = results; } + NSArray *dataDetectionResults() const { return m_dataDetectionResults.get(); } #endif #if PLATFORM(IOS) - const ViewportArguments& viewportArguments() const; - void setViewportArguments(const ViewportArguments&); + const ViewportArguments& viewportArguments() const; + WEBCORE_EXPORT void setViewportArguments(const ViewportArguments&); - Node* deepestNodeAtLocation(const FloatPoint& viewportLocation); - Node* nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation); - Node* nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation); + WEBCORE_EXPORT Node* deepestNodeAtLocation(const FloatPoint& viewportLocation); + WEBCORE_EXPORT Node* nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation); + WEBCORE_EXPORT Node* nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation); - int indexCountOfWordPrecedingSelection(NSString* word) const; - NSArray* wordsInCurrentParagraph() const; - CGRect renderRectForPoint(CGPoint, bool* isReplaced, float* fontSize) const; + WEBCORE_EXPORT NSArray *wordsInCurrentParagraph() const; + WEBCORE_EXPORT CGRect renderRectForPoint(CGPoint, bool* isReplaced, float* fontSize) const; - void setSelectionChangeCallbacksDisabled(bool = true); - bool selectionChangeCallbacksDisabled() const; + WEBCORE_EXPORT void setSelectionChangeCallbacksDisabled(bool = true); + bool selectionChangeCallbacksDisabled() const; - enum ViewportOffsetChangeType { IncrementalScrollOffset, CompletedScrollOffset }; - void viewportOffsetChanged(ViewportOffsetChangeType); - bool containsTiledBackingLayers() const; + enum ViewportOffsetChangeType { IncrementalScrollOffset, CompletedScrollOffset }; + WEBCORE_EXPORT void viewportOffsetChanged(ViewportOffsetChangeType); + bool containsTiledBackingLayers() const; - void overflowScrollPositionChangedForNode(const IntPoint&, Node*, bool isUserScroll); + WEBCORE_EXPORT void overflowScrollPositionChangedForNode(const IntPoint&, Node*, bool isUserScroll); - void resetAllGeolocationPermission(); + WEBCORE_EXPORT void resetAllGeolocationPermission(); #endif #if ENABLE(ORIENTATION_EVENTS) - // Orientation is the interface orientation in degrees. Some examples are: - // 0 is straight up; -90 is when the device is rotated 90 clockwise; - // 90 is when rotated counter clockwise. - void sendOrientationChangeEvent(int orientation); - int orientation() const { return m_orientation; } + // Orientation is the interface orientation in degrees. Some examples are: + // 0 is straight up; -90 is when the device is rotated 90 clockwise; + // 90 is when rotated counter clockwise. + WEBCORE_EXPORT void orientationChanged(); + int orientation() const; #endif - void clearTimers(); - static void clearTimers(FrameView*, Document*); + void clearTimers(); + static void clearTimers(FrameView*, Document*); - String displayStringModifiedByEncoding(const String&) const; + WEBCORE_EXPORT String displayStringModifiedByEncoding(const String&) const; - VisiblePosition visiblePositionForPoint(const IntPoint& framePoint); - Document* documentAtPoint(const IntPoint& windowPoint); - PassRefPtr rangeForPoint(const IntPoint& framePoint); + WEBCORE_EXPORT VisiblePosition visiblePositionForPoint(const IntPoint& framePoint) const; + Document* documentAtPoint(const IntPoint& windowPoint); + WEBCORE_EXPORT RefPtr rangeForPoint(const IntPoint& framePoint); - String searchForLabelsAboveCell(JSC::Yarr::RegularExpression*, HTMLTableCellElement*, size_t* resultDistanceFromStartOfCell); - String searchForLabelsBeforeElement(const Vector& labels, Element*, size_t* resultDistance, bool* resultIsInCellAbove); - String matchLabelsAgainstElement(const Vector& labels, Element*); - -#if ENABLE(IOS_TEXT_AUTOSIZING) - void setTextAutosizingWidth(float); - float textAutosizingWidth() const; -#endif + WEBCORE_EXPORT String searchForLabelsAboveCell(const JSC::Yarr::RegularExpression&, HTMLTableCellElement*, size_t* resultDistanceFromStartOfCell); + String searchForLabelsBeforeElement(const Vector& labels, Element*, size_t* resultDistance, bool* resultIsInCellAbove); + String matchLabelsAgainstElement(const Vector& labels, Element*); #if PLATFORM(IOS) - // Scroll the selection in an overflow layer on iOS. - void scrollOverflowLayer(RenderLayer* , const IntRect& visibleRect, const IntRect& exposeRect); - - int preferredHeight() const; - int innerLineHeight(DOMNode*) const; - void updateLayout() const; - NSRect caretRect() const; - NSRect rectForScrollToVisible() const; - NSRect rectForSelection(VisibleSelection&) const; - DOMCSSStyleDeclaration* styleAtSelectionStart() const; - unsigned formElementsCharacterCount() const; - void setTimersPaused(bool); - bool timersPaused() const { return m_timersPausedCount; } - void dispatchPageHideEventBeforePause(); - void dispatchPageShowEventBeforeResume(); - void setRangedSelectionBaseToCurrentSelection(); - void setRangedSelectionBaseToCurrentSelectionStart(); - void setRangedSelectionBaseToCurrentSelectionEnd(); - void clearRangedSelectionInitialExtent(); - void setRangedSelectionInitialExtentToCurrentSelectionStart(); - void setRangedSelectionInitialExtentToCurrentSelectionEnd(); - VisibleSelection rangedSelectionBase() const; - VisibleSelection rangedSelectionInitialExtent() const; - void recursiveSetUpdateAppearanceEnabled(bool); - NSArray* interpretationsForCurrentRoot() const; + // Scroll the selection in an overflow layer. + void scrollOverflowLayer(RenderLayer*, const IntRect& visibleRect, const IntRect& exposeRect); + + WEBCORE_EXPORT int preferredHeight() const; + WEBCORE_EXPORT void updateLayout() const; + WEBCORE_EXPORT NSRect caretRect() const; + WEBCORE_EXPORT NSRect rectForScrollToVisible() const; + WEBCORE_EXPORT unsigned formElementsCharacterCount() const; + + // This function is used by Legacy WebKit. + WEBCORE_EXPORT void setTimersPaused(bool); + + WEBCORE_EXPORT void dispatchPageHideEventBeforePause(); + WEBCORE_EXPORT void dispatchPageShowEventBeforeResume(); + WEBCORE_EXPORT void setRangedSelectionBaseToCurrentSelection(); + WEBCORE_EXPORT void setRangedSelectionBaseToCurrentSelectionStart(); + WEBCORE_EXPORT void setRangedSelectionBaseToCurrentSelectionEnd(); + WEBCORE_EXPORT void clearRangedSelectionInitialExtent(); + WEBCORE_EXPORT void setRangedSelectionInitialExtentToCurrentSelectionStart(); + WEBCORE_EXPORT void setRangedSelectionInitialExtentToCurrentSelectionEnd(); + WEBCORE_EXPORT VisibleSelection rangedSelectionBase() const; + WEBCORE_EXPORT VisibleSelection rangedSelectionInitialExtent() const; + WEBCORE_EXPORT void recursiveSetUpdateAppearanceEnabled(bool); + WEBCORE_EXPORT NSArray *interpretationsForCurrentRoot() const; #endif - void suspendActiveDOMObjectsAndAnimations(); - void resumeActiveDOMObjectsAndAnimations(); - bool activeDOMObjectsAndAnimationsSuspended() const { return m_activeDOMObjectsAndAnimationsSuspendedCount > 0; } - - bool isURLAllowed(const URL&) const; - - // ======== - - protected: - Frame(Page&, HTMLFrameOwnerElement*, FrameLoaderClient&); - - private: - void injectUserScriptsForWorld(DOMWrapperWorld&, const UserScriptVector&, UserScriptInjectionTime); + void suspendActiveDOMObjectsAndAnimations(); + void resumeActiveDOMObjectsAndAnimations(); + bool activeDOMObjectsAndAnimationsSuspended() const { return m_activeDOMObjectsAndAnimationsSuspendedCount > 0; } - HashSet m_destructionObservers; + bool isURLAllowed(const URL&) const; + WEBCORE_EXPORT bool isAlwaysOnLoggingAllowed() const; - MainFrame& m_mainFrame; - Page* m_page; - const RefPtr m_settings; - mutable FrameTree m_treeNode; - mutable FrameLoader m_loader; - mutable NavigationScheduler m_navigationScheduler; +// ======== - HTMLFrameOwnerElement* m_ownerElement; - RefPtr m_view; - RefPtr m_doc; +protected: + Frame(Page&, HTMLFrameOwnerElement*, FrameLoaderClient&); + void setMainFrameWasDestroyed(); - const std::unique_ptr m_script; - const OwnPtr m_editor; - const OwnPtr m_selection; - const OwnPtr m_eventHandler; - const std::unique_ptr m_animationController; +private: + HashSet m_destructionObservers; -#if PLATFORM(IOS) - void betterApproximateNode(const IntPoint& testPoint, NodeQualifier, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect); - bool hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult&, IntPoint& center); - Node* qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, NodeQualifier, bool shouldApproximate); - - void overflowAutoScrollTimerFired(Timer*); - void startOverflowAutoScroll(const IntPoint&); - int checkOverflowScroll(OverflowScrollAction); - - void setTimersPausedInternal(bool); - - Timer m_overflowAutoScrollTimer; - float m_overflowAutoScrollDelta; - IntPoint m_overflowAutoScrollPos; - ViewportArguments m_viewportArguments; - bool m_selectionChangeCallbacksDisabled; - int m_timersPausedCount; - VisibleSelection m_rangedSelectionBase; - VisibleSelection m_rangedSelectionInitialExtent; -#endif + MainFrame& m_mainFrame; + Page* m_page; + const RefPtr m_settings; + mutable FrameTree m_treeNode; + mutable FrameLoader m_loader; + mutable NavigationScheduler m_navigationScheduler; -#if ENABLE(IOS_TEXT_AUTOSIZING) - float m_textAutosizingWidth; -#endif + HTMLFrameOwnerElement* m_ownerElement; + RefPtr m_view; + RefPtr m_doc; - float m_pageZoomFactor; - float m_textZoomFactor; + const std::unique_ptr m_script; + const std::unique_ptr m_editor; + const std::unique_ptr m_selection; + const std::unique_ptr m_animationController; -#if ENABLE(ORIENTATION_EVENTS) - int m_orientation; +#if ENABLE(DATA_DETECTION) + RetainPtr m_dataDetectionResults; #endif - - bool m_inViewSourceMode; - -#if USE(TILED_BACKING_STORE) - // FIXME: The tiled backing store belongs in FrameView, not Frame. - - public: - TiledBackingStore* tiledBackingStore() const { return m_tiledBackingStore.get(); } - void setTiledBackingStoreEnabled(bool); - - private: - // TiledBackingStoreClient interface - virtual void tiledBackingStorePaintBegin() override final; - virtual void tiledBackingStorePaint(GraphicsContext*, const IntRect&) override final; - virtual void tiledBackingStorePaintEnd(const Vector& paintedArea) override final; - virtual IntRect tiledBackingStoreContentsRect() override final; - virtual IntRect tiledBackingStoreVisibleRect() override final; - virtual Color tiledBackingStoreBackgroundColor() const override final; - - OwnPtr m_tiledBackingStore; +#if PLATFORM(IOS) + void betterApproximateNode(const IntPoint& testPoint, NodeQualifier, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect); + bool hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult&, IntPoint& center); + Node* qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, NodeQualifier, bool shouldApproximate); + + void overflowAutoScrollTimerFired(); + void startOverflowAutoScroll(const IntPoint&); + int checkOverflowScroll(OverflowScrollAction); + + void setTimersPausedInternal(bool); + + Timer m_overflowAutoScrollTimer; + float m_overflowAutoScrollDelta; + IntPoint m_overflowAutoScrollPos; + ViewportArguments m_viewportArguments; + bool m_selectionChangeCallbacksDisabled; + VisibleSelection m_rangedSelectionBase; + VisibleSelection m_rangedSelectionInitialExtent; #endif - int m_activeDOMObjectsAndAnimationsSuspendedCount; - }; - - inline void Frame::init() - { - m_loader.init(); - } - - inline FrameLoader& Frame::loader() const - { - return m_loader; - } - - inline NavigationScheduler& Frame::navigationScheduler() const - { - return m_navigationScheduler; - } - - inline FrameView* Frame::view() const - { - return m_view.get(); - } - - inline ScriptController& Frame::script() - { - return *m_script; - } - - inline Document* Frame::document() const - { - return m_doc.get(); - } - - inline FrameSelection& Frame::selection() const - { - return *m_selection; - } - - inline Editor& Frame::editor() const - { - return *m_editor; - } - - inline AnimationController& Frame::animation() const - { - return *m_animationController; - } - - inline HTMLFrameOwnerElement* Frame::ownerElement() const - { - return m_ownerElement; - } - - inline bool Frame::inViewSourceMode() const - { - return m_inViewSourceMode; - } - - inline void Frame::setInViewSourceMode(bool mode) - { - m_inViewSourceMode = mode; - } - - inline FrameTree& Frame::tree() const - { - return m_treeNode; - } - - inline Page* Frame::page() const - { - return m_page; - } - - inline void Frame::detachFromPage() - { - m_page = 0; - } - - inline EventHandler& Frame::eventHandler() const - { - return *m_eventHandler; - } - - inline MainFrame& Frame::mainFrame() const - { - return m_mainFrame; - } + float m_pageZoomFactor; + float m_textZoomFactor; + + int m_activeDOMObjectsAndAnimationsSuspendedCount; + bool m_mainFrameWasDestroyed { false }; + bool m_documentIsBeingReplaced { false }; + +protected: + std::unique_ptr m_eventHandler; +}; + +inline void Frame::init() +{ + m_loader.init(); +} + +inline FrameLoader& Frame::loader() const +{ + return m_loader; +} + +inline NavigationScheduler& Frame::navigationScheduler() const +{ + return m_navigationScheduler; +} + +inline FrameView* Frame::view() const +{ + return m_view.get(); +} + +inline ScriptController& Frame::script() +{ + return *m_script; +} + +inline Document* Frame::document() const +{ + return m_doc.get(); +} + +inline FrameSelection& Frame::selection() const +{ + return *m_selection; +} + +inline Editor& Frame::editor() const +{ + return *m_editor; +} + +inline CSSAnimationController& Frame::animation() const +{ + return *m_animationController; +} + +inline HTMLFrameOwnerElement* Frame::ownerElement() const +{ + return m_ownerElement; +} + +inline FrameTree& Frame::tree() const +{ + return m_treeNode; +} + +inline Page* Frame::page() const +{ + return m_page; +} + +inline void Frame::detachFromPage() +{ + m_page = nullptr; +} + +inline EventHandler& Frame::eventHandler() const +{ + return *m_eventHandler; +} + +inline EventHandler* Frame::eventHandlerPtr() const +{ + return m_eventHandler.get(); +} + +inline MainFrame& Frame::mainFrame() const +{ + ASSERT_WITH_SECURITY_IMPLICATION(!m_mainFrameWasDestroyed); + return m_mainFrame; +} + +inline void Frame::setMainFrameWasDestroyed() +{ + m_mainFrameWasDestroyed = false; +} } // namespace WebCore - -#endif // Frame_h diff --git a/Source/WebCore/page/FrameDestructionObserver.cpp b/Source/WebCore/page/FrameDestructionObserver.cpp index 94bf33687..6b0ae533b 100644 --- a/Source/WebCore/page/FrameDestructionObserver.cpp +++ b/Source/WebCore/page/FrameDestructionObserver.cpp @@ -31,14 +31,14 @@ namespace WebCore { FrameDestructionObserver::FrameDestructionObserver(Frame* frame) - : m_frame(0) + : m_frame(nullptr) { observeFrame(frame); } FrameDestructionObserver::~FrameDestructionObserver() { - observeFrame(0); + observeFrame(nullptr); } @@ -55,7 +55,7 @@ void FrameDestructionObserver::observeFrame(Frame* frame) void FrameDestructionObserver::frameDestroyed() { - m_frame = 0; + m_frame = nullptr; } void FrameDestructionObserver::willDetachPage() diff --git a/Source/WebCore/page/FrameDestructionObserver.h b/Source/WebCore/page/FrameDestructionObserver.h index 094b521f3..e159a567c 100644 --- a/Source/WebCore/page/FrameDestructionObserver.h +++ b/Source/WebCore/page/FrameDestructionObserver.h @@ -23,8 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameDestructionObserver_h -#define FrameDestructionObserver_h +#pragma once #include "PlatformExportMacros.h" @@ -34,20 +33,18 @@ class Frame; class FrameDestructionObserver { public: - WEBCORE_TESTING explicit FrameDestructionObserver(Frame*); + WEBCORE_EXPORT explicit FrameDestructionObserver(Frame*); - WEBCORE_TESTING virtual void frameDestroyed(); - WEBCORE_TESTING virtual void willDetachPage(); + WEBCORE_EXPORT virtual void frameDestroyed(); + WEBCORE_EXPORT virtual void willDetachPage(); Frame* frame() const { return m_frame; } protected: - WEBCORE_TESTING virtual ~FrameDestructionObserver(); - WEBCORE_TESTING void observeFrame(Frame*); + WEBCORE_EXPORT virtual ~FrameDestructionObserver(); + WEBCORE_EXPORT void observeFrame(Frame*); Frame* m_frame; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/FrameSnapshotting.cpp b/Source/WebCore/page/FrameSnapshotting.cpp index 495406b82..3d34cd929 100644 --- a/Source/WebCore/page/FrameSnapshotting.cpp +++ b/Source/WebCore/page/FrameSnapshotting.cpp @@ -32,12 +32,15 @@ #include "FrameSnapshotting.h" #include "Document.h" +#include "FloatRect.h" #include "Frame.h" #include "FrameSelection.h" #include "FrameView.h" +#include "GraphicsContext.h" #include "ImageBuffer.h" #include "Page.h" #include "RenderObject.h" +#include "Settings.h" namespace WebCore { @@ -65,6 +68,12 @@ struct ScopedFramePaintingState { }; std::unique_ptr snapshotFrameRect(Frame& frame, const IntRect& imageRect, SnapshotOptions options) +{ + Vector clipRects; + return snapshotFrameRectWithClip(frame, imageRect, clipRects, options); +} + +std::unique_ptr snapshotFrameRectWithClip(Frame& frame, const IntRect& imageRect, Vector& clipRects, SnapshotOptions options) { if (!frame.page()) return nullptr; @@ -86,14 +95,28 @@ std::unique_ptr snapshotFrameRect(Frame& frame, const IntRect& imag paintBehavior |= PaintBehaviorForceBlackText; if (options & SnapshotOptionsPaintSelectionOnly) paintBehavior |= PaintBehaviorSelectionOnly; + if (options & SnapshotOptionsPaintSelectionAndBackgroundsOnly) + paintBehavior |= PaintBehaviorSelectionAndBackgroundsOnly; // Other paint behaviors are set by paintContentsForSnapshot. frame.view()->setPaintBehavior(paintBehavior); - std::unique_ptr buffer = ImageBuffer::create(imageRect.size(), frame.page()->deviceScaleFactor(), ColorSpaceDeviceRGB); + float scaleFactor = frame.page()->deviceScaleFactor(); + + if (frame.settings().delegatesPageScaling()) + scaleFactor *= frame.page()->pageScaleFactor(); + + std::unique_ptr buffer = ImageBuffer::create(imageRect.size(), Unaccelerated, scaleFactor); if (!buffer) return nullptr; - buffer->context()->translate(-imageRect.x(), -imageRect.y()); + buffer->context().translate(-imageRect.x(), -imageRect.y()); + + if (!clipRects.isEmpty()) { + Path clipPath; + for (auto& rect : clipRects) + clipPath.addRect(rect); + buffer->context().clipPath(clipPath); + } frame.view()->paintContentsForSnapshot(buffer->context(), imageRect, shouldIncludeSelection, coordinateSpace); return buffer; @@ -101,11 +124,19 @@ std::unique_ptr snapshotFrameRect(Frame& frame, const IntRect& imag std::unique_ptr snapshotSelection(Frame& frame, SnapshotOptions options) { - if (!frame.selection().isRange()) + auto& selection = frame.selection(); + + if (!selection.isRange()) + return nullptr; + + FloatRect selectionBounds = selection.selectionBounds(); + + // It is possible for the selection bounds to be empty; see https://bugs.webkit.org/show_bug.cgi?id=56645. + if (selectionBounds.isEmpty()) return nullptr; options |= SnapshotOptionsPaintSelectionOnly; - return snapshotFrameRect(frame, enclosingIntRect(frame.selection().bounds()), options); + return snapshotFrameRect(frame, enclosingIntRect(selectionBounds), options); } std::unique_ptr snapshotNode(Frame& frame, Node& node) @@ -119,7 +150,7 @@ std::unique_ptr snapshotNode(Frame& frame, Node& node) frame.view()->setNodeToDraw(&node); LayoutRect topLevelRect; - return snapshotFrameRect(frame, pixelSnappedIntRect(node.renderer()->paintingRootRect(topLevelRect))); + return snapshotFrameRect(frame, snappedIntRect(node.renderer()->paintingRootRect(topLevelRect))); } } // namespace WebCore diff --git a/Source/WebCore/page/FrameSnapshotting.h b/Source/WebCore/page/FrameSnapshotting.h index f74767b28..dc6356886 100644 --- a/Source/WebCore/page/FrameSnapshotting.h +++ b/Source/WebCore/page/FrameSnapshotting.h @@ -27,13 +27,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FrameSnapshotting_h -#define FrameSnapshotting_h +#pragma once #include +#include namespace WebCore { +class FloatRect; class Frame; class IntRect; class ImageBuffer; @@ -45,13 +46,13 @@ enum { SnapshotOptionsPaintSelectionOnly = 1 << 1, SnapshotOptionsInViewCoordinates = 1 << 2, SnapshotOptionsForceBlackText = 1 << 3, + SnapshotOptionsPaintSelectionAndBackgroundsOnly = 1 << 4, }; typedef unsigned SnapshotOptions; -std::unique_ptr snapshotFrameRect(Frame&, const IntRect&, SnapshotOptions = SnapshotOptionsNone); +WEBCORE_EXPORT std::unique_ptr snapshotFrameRect(Frame&, const IntRect&, SnapshotOptions = SnapshotOptionsNone); +std::unique_ptr snapshotFrameRectWithClip(Frame&, const IntRect&, Vector& clipRects, SnapshotOptions = SnapshotOptionsNone); std::unique_ptr snapshotNode(Frame&, Node&); -std::unique_ptr snapshotSelection(Frame&, SnapshotOptions = SnapshotOptionsNone); +WEBCORE_EXPORT std::unique_ptr snapshotSelection(Frame&, SnapshotOptions = SnapshotOptionsNone); } // namespace WebCore - -#endif // FrameSnapshotting_h diff --git a/Source/WebCore/page/FrameTree.cpp b/Source/WebCore/page/FrameTree.cpp index 2ef23b335..4e1bc29b7 100644 --- a/Source/WebCore/page/FrameTree.cpp +++ b/Source/WebCore/page/FrameTree.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2010. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -38,7 +38,7 @@ namespace WebCore { FrameTree::~FrameTree() { for (Frame* child = firstChild(); child; child = child->tree().nextSibling()) - child->setView(0); + child->setView(nullptr); } void FrameTree::setName(const AtomicString& name) @@ -48,14 +48,14 @@ void FrameTree::setName(const AtomicString& name) m_uniqueName = name; return; } - m_uniqueName = AtomicString(); // Remove our old frame name so it's not considered in uniqueChildName. + m_uniqueName = nullAtom; // Remove our old frame name so it's not considered in uniqueChildName. m_uniqueName = parent()->tree().uniqueChildName(name); } void FrameTree::clearName() { - m_name = AtomicString(); - m_uniqueName = AtomicString(); + m_name = nullAtom; + m_uniqueName = nullAtom; } Frame* FrameTree::parent() const @@ -63,80 +63,61 @@ Frame* FrameTree::parent() const return m_parent; } -bool FrameTree::transferChild(PassRefPtr child) +unsigned FrameTree::indexInParent() const { - Frame* oldParent = child->tree().parent(); - if (oldParent == &m_thisFrame) - return false; // |child| is already a child of m_thisFrame. - - if (oldParent) - oldParent->tree().removeChild(child.get()); - - ASSERT(child->page() == m_thisFrame.page()); - child->tree().m_parent = &m_thisFrame; - - // We need to ensure that the child still has a unique frame name with respect to its new parent. - child->tree().setName(child->tree().m_name); - - actuallyAppendChild(child); // Note, on return |child| is null. - return true; -} - -void FrameTree::appendChild(PassRefPtr child) -{ - ASSERT(child->page() == m_thisFrame.page()); - child->tree().m_parent = &m_thisFrame; - actuallyAppendChild(child); // Note, on return |child| is null. + if (!m_parent) + return 0; + unsigned index = 0; + for (Frame* frame = m_parent->tree().firstChild(); frame; frame = frame->tree().nextSibling()) { + if (&frame->tree() == this) + return index; + ++index; + } + RELEASE_ASSERT_NOT_REACHED(); } -void FrameTree::actuallyAppendChild(PassRefPtr child) +void FrameTree::appendChild(Frame& child) { - ASSERT(child->tree().m_parent == &m_thisFrame); + ASSERT(child.page() == m_thisFrame.page()); + child.tree().m_parent = &m_thisFrame; Frame* oldLast = m_lastChild; - m_lastChild = child.get(); + m_lastChild = &child; if (oldLast) { - child->tree().m_previousSibling = oldLast; - oldLast->tree().m_nextSibling = child; + child.tree().m_previousSibling = oldLast; + oldLast->tree().m_nextSibling = &child; } else - m_firstChild = child; + m_firstChild = &child; m_scopedChildCount = invalidCount; ASSERT(!m_lastChild->tree().m_nextSibling); } -void FrameTree::removeChild(Frame* child) +void FrameTree::removeChild(Frame& child) { - child->tree().m_parent = 0; - - // Slightly tricky way to prevent deleting the child until we are done with it, w/o - // extra refs. These swaps leave the child in a circular list by itself. Clearing its - // previous and next will then finally deref it. + Frame*& newLocationForPrevious = m_lastChild == &child ? m_lastChild : child.tree().m_nextSibling->tree().m_previousSibling; + RefPtr& newLocationForNext = m_firstChild == &child ? m_firstChild : child.tree().m_previousSibling->tree().m_nextSibling; - RefPtr& newLocationForNext = m_firstChild == child ? m_firstChild : child->tree().m_previousSibling->tree().m_nextSibling; - Frame*& newLocationForPrevious = m_lastChild == child ? m_lastChild : child->tree().m_nextSibling->tree().m_previousSibling; - swap(newLocationForNext, child->tree().m_nextSibling); - // For some inexplicable reason, the following line does not compile without the explicit std:: namespace - std::swap(newLocationForPrevious, child->tree().m_previousSibling); - - child->tree().m_previousSibling = 0; - child->tree().m_nextSibling = 0; + child.tree().m_parent = nullptr; + newLocationForPrevious = std::exchange(child.tree().m_previousSibling, nullptr); + newLocationForNext = WTFMove(child.tree().m_nextSibling); m_scopedChildCount = invalidCount; } AtomicString FrameTree::uniqueChildName(const AtomicString& requestedName) const { + // If the requested name (the frame's "name" attribute) is unique, just use that. if (!requestedName.isEmpty() && !child(requestedName) && requestedName != "_blank") return requestedName; - // Create a repeatable name for a child about to be added to us. The name must be - // unique within the frame tree. The string we generate includes a "path" of names - // from the root frame down to us. For this path to be unique, each set of siblings must - // contribute a unique name to the path, which can't collide with any HTML-assigned names. - // We generate this path component by index in the child list along with an unlikely - // frame name that can't be set in HTML because it collides with comment syntax. + // The "name" attribute was not unique or absent. Generate a name based on the + // new frame's location in the frame tree. The name uses HTML comment syntax to + // avoid collisions with author names. + + // An example path for the third child of the second child of the root frame: + // /--> const char framePathPrefix[] = ""); + } } name.appendLiteral("/