diff options
Diffstat (limited to 'Source/WebCore/page')
353 files changed, 34078 insertions, 17400 deletions
diff --git a/Source/WebCore/page/ViewState.h b/Source/WebCore/page/ActivityState.h index 788a7ff82..0e534cc66 100644 --- a/Source/WebCore/page/ViewState.h +++ b/Source/WebCore/page/ActivityState.h @@ -23,26 +23,32 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ViewState_h -#define ViewState_h +#pragma once namespace WebCore { -struct ViewState { +struct ActivityState { enum { WindowIsActive = 1 << 0, IsFocused = 1 << 1, IsVisible = 1 << 2, - IsInWindow = 1 << 3, - IsVisuallyIdle = 1 << 4, + 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 | IsInWindow | IsVisuallyIdle; + static const Flags AllFlags = WindowIsActive | IsFocused | IsVisible | IsVisibleOrOccluded | IsInWindow | IsVisuallyIdle | IsAudible | IsLoading; }; -} // namespace WebCore +enum class ActivityStateForCPUSampling { + NonVisible, + VisibleNonActive, + VisibleAndActive +}; -#endif // ViewState_h +} // namespace WebCore diff --git a/Source/WebCore/page/VisitedLinkProvider.h b/Source/WebCore/page/ActivityStateChangeObserver.h index bea670504..33437cce6 100644 --- a/Source/WebCore/page/VisitedLinkProvider.h +++ b/Source/WebCore/page/ActivityStateChangeObserver.h @@ -23,31 +23,19 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef VisitedLinkProvider_h -#define VisitedLinkProvider_h +#pragma once -#include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> +#include "ActivityState.h" namespace WebCore { -typedef uint64_t LinkHash; -class Page; -class URL; - -class VisitedLinkProvider : public RefCounted<VisitedLinkProvider> { +class ActivityStateChangeObserver { public: - static PassRefPtr<VisitedLinkProvider> create() + virtual ~ActivityStateChangeObserver() { - return adoptRef(new VisitedLinkProvider); } - ~VisitedLinkProvider(); - -private: - VisitedLinkProvider(); + + virtual void activityStateDidChange(ActivityState::Flags oldActivityState, ActivityState::Flags newActivityState) = 0; }; -} - -#endif // VisitedLinkProvider_h +} // 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 <wtf/Vector.h> #include <wtf/text/WTFString.h> -#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<String>& 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<RenderBox>(*renderer) && downcast<RenderBox>(*renderer).canAutoscroll())) renderer = renderer->parent(); - m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0; + m_autoscrollRenderer = is<RenderBox>(renderer) ? downcast<RenderBox>(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<AutoscrollController>&) +void AutoscrollController::autoscrollTimerFired() { if (!m_autoscrollRenderer) { stopAutoscrollTimer(); @@ -252,7 +252,7 @@ void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>&) #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<AutoscrollController>&); + void autoscrollTimerFired(); void startAutoscrollTimer(); #if ENABLE(PAN_SCROLLING) void updatePanScrollState(FrameView*, const IntPoint&); #endif - Timer<AutoscrollController> 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 <wtf/PassRefPtr.h> +#include <wtf/Ref.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -42,7 +41,7 @@ class BarProp : public ScriptWrappable, public RefCounted<BarProp>, public DOMWi public: enum Type { Locationbar, Menubar, Personalbar, Scrollbars, Statusbar, Toolbar }; - static PassRefPtr<BarProp> create(Frame* frame, Type type) { return adoptRef(new BarProp(frame, type)); } + static Ref<BarProp> 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 <wtf/text/Base64.h> + +namespace WebCore { + +ExceptionOr<String> Base64Utilities::btoa(const String& stringToEncode) +{ + if (stringToEncode.isNull()) + return String(); + + if (!stringToEncode.containsOnlyLatin1()) + return Exception { INVALID_CHARACTER_ERR }; + + return base64Encode(stringToEncode.latin1()); +} + +ExceptionOr<String> Base64Utilities::atob(const String& encodedString) +{ + if (encodedString.isNull()) + return String(); + + Vector<char> 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<String> btoa(const String&); + static ExceptionOr<String> 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 <heap/HeapInlines.h> +#include <runtime/JSCellInlines.h> +#include <runtime/StructureInlines.h> +#include <wtf/NeverDestroyed.h> 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<CaptionUserPreferences>&) +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<String> CaptionUserPreferences::preferredAudioCharacteristics() const +{ + Vector<String> 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<RefPtr<TextTrack>> 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<TextTrack>& a, const RefPtr<TextTrack>& 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<RefPtr<TextTrack>> 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<RefPtr<AudioTrack>> CaptionUserPreferences::sortedTrackListForMenu(AudioTrackList* trackList) +{ + ASSERT(trackList); + + Vector<RefPtr<AudioTrack>> 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<String>& 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<String>(), - Vector<String>(), 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 <wtf/PassOwnPtr.h> #include <wtf/text/AtomicString.h> 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<String> preferredLanguages() const; + virtual void setPreferredAudioCharacteristic(const String&); + virtual Vector<String> preferredAudioCharacteristics() const; + virtual String displayNameForTrack(TextTrack*) const; virtual Vector<RefPtr<TextTrack>> sortedTrackListForMenu(TextTrackList*); + virtual String displayNameForTrack(AudioTrack*) const; + virtual Vector<RefPtr<AudioTrack>> 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<CaptionUserPreferences>&); + void timerFired(); void notify(); + Page* currentPage() const; PageGroup& m_pageGroup; - CaptionDisplayMode m_displayMode; - Timer<CaptionUserPreferences> 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 <wtf/NeverDestroyed.h> +#include <wtf/PlatformUserPreferredLanguages.h> +#include <wtf/RetainPtr.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +#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 <CoreText/CoreText.h> +#include <MediaAccessibility/MediaAccessibility.h> + +#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<CaptionUserPreferencesMediaAF*>(observer)->captionPreferencesChanged(); +#else + WebThreadRun(^{ + static_cast<CaptionUserPreferencesMediaAF*>(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<CFArrayRef> captioningMediaCharacteristics = adoptCF(MACaptionAppearanceCopyPreferredCaptioningMediaCharacteristics(kMACaptionAppearanceDomainUser)); + return captioningMediaCharacteristics && CFArrayGetCount(captioningMediaCharacteristics.get()); +} + +bool CaptionUserPreferencesMediaAF::userPrefersSubtitles() const +{ + bool subtitlesSetting = CaptionUserPreferences::userPrefersSubtitles(); + if (subtitlesSetting || testingMode() || !MediaAccessibilityLibrary()) + return subtitlesSetting; + + RetainPtr<CFArrayRef> 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<CGColorRef> 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<int>(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<Color> defaultBackgroundColor(0, 0, 0, 0.8 * 255); + + MACaptionAppearanceBehavior behavior; + + RetainPtr<CGColorRef> 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<int>(opacity * 255)), important); +} + +Color CaptionUserPreferencesMediaAF::captionsTextColor(bool& important) const +{ + MACaptionAppearanceBehavior behavior; + RetainPtr<CGColorRef> 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<int>(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<const String> 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<CFNumberRef>(strokeWidthAttribute.get()), kCFNumberIntType, &strokeWidth)) + return strokeWidthDefault; + + return String::format(" %dpx ", strokeWidth); +} + +String CaptionUserPreferencesMediaAF::captionsTextEdgeCSS() const +{ + static NeverDestroyed<const String> edgeStyleRaised(ASCIILiteral(" -.05em -.05em 0 ")); + static NeverDestroyed<const String> edgeStyleDepressed(ASCIILiteral(" .05em .05em 0 ")); + static NeverDestroyed<const String> 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<CTFontDescriptorRef> font = adoptCF(MACaptionAppearanceCopyFontDescriptorForStyle(kMACaptionAppearanceDomainUser, &behavior, kMACaptionAppearanceFontStyleDefault)); + if (!font) + return emptyString(); + + RetainPtr<CFTypeRef> name = adoptCF(CTFontDescriptorCopyAttribute(font.get(), kCTFontNameAttribute)); + if (!name) + return emptyString(); + + StringBuilder builder; + + builder.append(getPropertyNameString(CSSPropertyFontFamily)); + builder.appendLiteral(": \""); + builder.append(static_cast<CFStringRef>(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<String> CaptionUserPreferencesMediaAF::preferredLanguages() const +{ + if (testingMode() || !MediaAccessibilityLibrary()) + return CaptionUserPreferences::preferredLanguages(); + + Vector<String> platformLanguages = platformUserPreferredLanguages(); + Vector<String> 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<CFArrayRef> languages = adoptCF(MACaptionAppearanceCopySelectedLanguages(kMACaptionAppearanceDomainUser)); + if (languages) + languageCount = CFArrayGetCount(languages.get()); + + if (!languageCount) + return CaptionUserPreferences::preferredLanguages(); + + Vector<String> userPreferredLanguages; + userPreferredLanguages.reserveCapacity(languageCount + platformLanguages.size()); + for (CFIndex i = 0; i < languageCount; i++) + userPreferredLanguages.append(static_cast<CFStringRef>(CFArrayGetValueAtIndex(languages.get(), i))); + + userPreferredLanguages.appendVector(platformLanguages); + + return userPreferredLanguages; +} + +void CaptionUserPreferencesMediaAF::setPreferredAudioCharacteristic(const String& characteristic) +{ + if (testingMode() || !MediaAccessibilityLibrary()) + CaptionUserPreferences::setPreferredAudioCharacteristic(characteristic); +} + +Vector<String> CaptionUserPreferencesMediaAF::preferredAudioCharacteristics() const +{ + if (testingMode() || !MediaAccessibilityLibrary() || !canLoad_MediaAccessibility_MAAudibleMediaCopyPreferredCharacteristics()) + return CaptionUserPreferences::preferredAudioCharacteristics(); + + CFIndex characteristicCount = 0; + RetainPtr<CFArrayRef> characteristics = adoptCF(MAAudibleMediaCopyPreferredCharacteristics()); + if (characteristics) + characteristicCount = CFArrayGetCount(characteristics.get()); + + if (!characteristicCount) + return CaptionUserPreferences::preferredAudioCharacteristics(); + + Vector<String> userPreferredAudioCharacteristics; + userPreferredAudioCharacteristics.reserveCapacity(characteristicCount); + for (CFIndex i = 0; i < characteristicCount; i++) + userPreferredAudioCharacteristics.append(static_cast<CFStringRef>(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<CFLocaleRef> currentLocale = adoptCF(CFLocaleCreate(kCFAllocatorDefault, defaultLanguage().createCFString().get())); + RetainPtr<CFStringRef> localeIdentifier = adoptCF(CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, trackLanguageIdentifier.createCFString().get())); + RetainPtr<CFStringRef> 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<CFDictionaryRef> localeDict = adoptCF(CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorDefault, localeIdentifier.get())); + if (localeDict) { + CFStringRef countryCode = 0; + String countryName; + + CFDictionaryGetValueIfPresent(localeDict.get(), kCFLocaleCountryCode, (const void **)&countryCode); + if (countryCode) { + RetainPtr<CFStringRef> 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<String> userPreferredCaptionLanguages = preferredLanguages(); + + if ((displayMode == Automatic && !legacyOverride) || trackHasOnlyForcedSubtitles) { + + if (!mediaElement || !mediaElement->player()) + return 0; + + String textTrackLanguage = track->validBCP47Language(); + if (textTrackLanguage.isEmpty()) + return 0; + + Vector<String> 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<TextTrack>& a, const RefPtr<TextTrack>& 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<RefPtr<AudioTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(AudioTrackList* trackList) +{ + ASSERT(trackList); + + Vector<RefPtr<AudioTrack>> 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<RefPtr<TextTrack>> CaptionUserPreferencesMediaAF::sortedTrackListForMenu(TextTrackList* trackList) +{ + ASSERT(trackList); + + Vector<RefPtr<TextTrack>> tracksForMenu; + HashSet<String> 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<String> preferredLanguages() const override; + + void setPreferredAudioCharacteristic(const String&) override; + Vector<String> 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<RefPtr<AudioTrack>> sortedTrackListForMenu(AudioTrackList*) override; + Vector<RefPtr<TextTrack>> 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 <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> +#include <runtime/VM.h> +#include <wtf/SetForScope.h> #include <wtf/Vector.h> -#include <wtf/text/StringBuilder.h> #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<JSC::VMEntryScope*> 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<HTMLInputElement>(*element)) { + HTMLInputElement& input = downcast<HTMLInputElement>(*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 <input type="file"> 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<HTMLInputElement>(*element)) { + toolTip = downcast<HTMLInputElement>(*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<ColorChooser> Chrome::createColorChooser(ColorChooserClient* client, const Color& initialColor) + +std::unique_ptr<ColorChooser> 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<DateTimeChooser> Chrome::openDateTimeChooser(DateTimeChooserClient* client, const DateTimeChooserParameters& parameters) -{ - notifyPopupOpeningObservers(); - return m_client.openDateTimeChooser(client, parameters); -} #endif -void Chrome::runOpenPanel(Frame* frame, PassRefPtr<FileChooser> fileChooser) +void Chrome::runOpenPanel(Frame& frame, FileChooser& fileChooser) { notifyPopupOpeningObservers(); m_client.runOpenPanel(frame, fileChooser); } -void Chrome::loadIconForFiles(const Vector<String>& filenames, FileIconLoader* loader) +void Chrome::loadIconForFiles(const Vector<String>& 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<PopupMenu> Chrome::createPopupMenu(PopupMenuClient* client) const +RefPtr<PopupMenu> Chrome::createPopupMenu(PopupMenuClient& client) const { notifyPopupOpeningObservers(); return m_client.createPopupMenu(client); } -PassRefPtr<SearchPopupMenu> Chrome::createSearchPopupMenu(PopupMenuClient* client) const +RefPtr<SearchPopupMenu> 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 <!DOCTYPE> is removed so that - // it can adjust the viewport accordingly. See <rdar://problem/15417894>. +#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<PopupOpeningObserver*> 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 <wtf/Forward.h> #include <wtf/RefPtr.h> -#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<ColorChooser> createColorChooser(ColorChooserClient*, const Color& initialColor); + std::unique_ptr<ColorChooser> createColorChooser(ColorChooserClient&, const Color& initialColor); #endif -#if ENABLE(DATE_AND_TIME_INPUT_TYPES) && !PLATFORM(IOS) - PassRefPtr<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) -#endif - - void runOpenPanel(Frame*, PassRefPtr<FileChooser>); - void loadIconForFiles(const Vector<String>&, FileIconLoader*); -#if ENABLE(DIRECTORY_UPLOAD) - void enumerateChosenDirectory(FileChooser*); -#endif + void runOpenPanel(Frame&, FileChooser&); + void loadIconForFiles(const Vector<String>&, 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<PopupMenu> createPopupMenu(PopupMenuClient*) const; - PassRefPtr<SearchPopupMenu> createSearchPopupMenu(PopupMenuClient*) const; + RefPtr<PopupMenu> createPopupMenu(PopupMenuClient&) const; + RefPtr<SearchPopupMenu> 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<PopupOpeningObserver*> 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 <runtime/ConsoleTypes.h> #include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/Seconds.h> #include <wtf/Vector.h> +#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<ChromeClient&>(*this).windowRect().size(); } + virtual FloatSize availableScreenSize() const { return const_cast<ChromeClient&>(*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<PlatformLayer*, OwnPtr<ViewportConstraints>>&, HashMap<PlatformLayer*, PlatformLayer*>&) { } + virtual void updateViewportConstrainedLayers(HashMap<PlatformLayer*, std::unique_ptr<ViewportConstraints>>&, HashMap<PlatformLayer*, PlatformLayer*>&) { } 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<ColorChooser> 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<DateTimeChooser> openDateTimeChooser(DateTimeChooserClient*, const DateTimeChooserParameters&) = 0; +#if ENABLE(INPUT_TYPE_COLOR) + virtual std::unique_ptr<ColorChooser> createColorChooser(ColorChooserClient&, const Color&) = 0; #endif - virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) = 0; + virtual void runOpenPanel(Frame&, FileChooser&) = 0; // Asynchronous request to load an icon for specified filenames. - virtual void loadIconForFiles(const Vector<String>&, 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<String>&, 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<DisplayRefreshMonitor> 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<ScrollingCoordinator> createScrollingCoordinator(Page*) const { return nullptr; } + virtual bool adjustLayerFlushThrottling(LayerFlushThrottleState::Flags) { return false; } + + virtual RefPtr<ScrollingCoordinator> 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<PopupMenu> createPopupMenu(PopupMenuClient*) const = 0; - virtual PassRefPtr<SearchPopupMenu> createSearchPopupMenu(PopupMenuClient*) const = 0; + virtual RefPtr<PopupMenu> createPopupMenu(PopupMenuClient&) const = 0; + virtual RefPtr<SearchPopupMenu> 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<ScrollbarOverlayStyle> 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<RefPtr<Element>>&) { }; 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<uint8_t>&, Vector<uint8_t>&) const { return false; } + virtual bool unwrapCryptoKey(const Vector<uint8_t>&, Vector<uint8_t>&) 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<String>&, 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 <bindings/ScriptValue.h> -#include <stdio.h> -#include <wtf/text/CString.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -Console::Console(Frame* frame) - : DOMWindowProperty(frame) -{ -} - -Console::~Console() -{ -} - -static void internalAddMessage(Page* page, MessageType type, MessageLevel level, JSC::ExecState* state, PassRefPtr<ScriptArguments> prpArguments, bool acceptNoArguments = false, bool printTrace = false) -{ - RefPtr<ScriptArguments> arguments = prpArguments; - - if (!page) - return; - - if (!acceptNoArguments && !arguments->argumentCount()) - return; - - size_t stackSize = printTrace ? ScriptCallStack::maxCallStackSizeToCapture : 1; - RefPtr<ScriptCallStack> 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<unsigned long>(i), functionName.utf8().data()); - - PageConsole::printSourceURLAndPosition(callFrame.sourceURL(), callFrame.lineNumber()); - - printf(")\n"); - } -} - -void Console::debug(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), LogMessageType, DebugMessageLevel, state, arguments); -} - -void Console::error(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), LogMessageType, ErrorMessageLevel, state, arguments); -} - -void Console::info(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - log(state, arguments); -} - -void Console::log(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), LogMessageType, LogMessageLevel, state, arguments); -} - -void Console::warn(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), LogMessageType, WarningMessageLevel, state, arguments); -} - -void Console::dir(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), DirMessageType, LogMessageLevel, state, arguments); -} - -void Console::dirxml(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), DirXMLMessageType, LogMessageLevel, state, arguments); -} - -void Console::table(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), TableMessageType, LogMessageLevel, state, arguments); -} - -void Console::clear(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), ClearMessageType, LogMessageLevel, state, arguments, true); -} - -void Console::trace(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - internalAddMessage(page(), TraceMessageType, LogMessageLevel, state, arguments, true, true); -} - -void Console::assertCondition(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments, bool condition) -{ - if (condition) - return; - - internalAddMessage(page(), AssertMessageType, ErrorMessageLevel, state, arguments, true); -} - -void Console::count(JSC::ExecState* state, PassRefPtr<ScriptArguments> 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<ScriptCallStack> 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<ScriptProfile> profile = ScriptProfiler::stop(state, title); - if (!profile) - return; - - m_profiles.append(profile); - RefPtr<ScriptCallStack> 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<ScriptCallStack> callStack(createScriptCallStackForConsole(state)); - InspectorInstrumentation::stopConsoleTiming(m_frame, title, callStack.release()); -} - -void Console::timeStamp(PassRefPtr<ScriptArguments> arguments) -{ - InspectorInstrumentation::consoleTimeStamp(m_frame, arguments); -} - -void Console::group(JSC::ExecState* state, PassRefPtr<ScriptArguments> arguments) -{ - InspectorInstrumentation::addMessageToConsole(page(), ConsoleAPIMessageSource, StartGroupMessageType, LogMessageLevel, String(), state, arguments); -} - -void Console::groupCollapsed(JSC::ExecState* state, PassRefPtr<ScriptArguments> 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 <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> - -namespace WebCore { - -class Frame; -class Page; -class ScriptArguments; - -typedef Vector<RefPtr<ScriptProfile>> ProfilesArray; - -class Console : public ScriptWrappable, public RefCounted<Console>, public DOMWindowProperty { -public: - static PassRefPtr<Console> create(Frame* frame) { return adoptRef(new Console(frame)); } - virtual ~Console(); - - void debug(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void error(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void info(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void log(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void clear(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void warn(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void dir(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void dirxml(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void table(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void trace(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void assertCondition(JSC::ExecState*, PassRefPtr<ScriptArguments>, bool condition); - void count(JSC::ExecState*, PassRefPtr<ScriptArguments>); - 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<ScriptArguments>); - void group(JSC::ExecState*, PassRefPtr<ScriptArguments>); - void groupCollapsed(JSC::ExecState*, PassRefPtr<ScriptArguments>); - 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/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 <inspector/InspectorValues.h> -#include <wtf/HashSet.h> -#include <wtf/text/TextPosition.h> -#include <wtf/text/WTFString.h> - -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<ScriptCallStack> 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<bool characterPredicate(UChar)> -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<bool characterPredicate(UChar)> -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<isASCIISpace>(begin, end); - - const UChar* position = begin; - skipWhile<isSourceCharacter>(position, end); - if (!equalIgnoringCase("'none'", begin, position - begin)) - return false; - - skipWhile<isASCIISpace>(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<CSPSource> 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<isASCIISpace>(position, end); - if (position == end) - return; - - const UChar* beginSource = position; - skipWhile<isSourceCharacter>(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<isNotColonOrSlash>(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<isNotColonOrSlash>(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; -} - -// ; <scheme> 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<isASCIIAlpha>(position, end)) - return false; - - skipWhile<isSchemeContinuationCharacter>(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<isHostCharacter>(position, end)) - return false; - - skipWhile<isHostCharacter>(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<isPathComponentCharacter>(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<isASCIIDigit>(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<isASCIISpace>(position, end); - const UChar* nonceBegin = position; - if (position == end) { - policy()->reportInvalidNonce(String()); - m_scriptNonce = ""; - return; - } - skipWhile<isNonceCharacter>(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<isASCIISpace>(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<isASCIISpace>(position, end); - if (position == end) - return; - - // mime1/mime1 mime2/mime2 - // ^ - begin = position; - if (!skipExactly<isMediaTypeCharacter>(position, end)) { - skipWhile<isNotASCIISpace>(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - skipWhile<isMediaTypeCharacter>(position, end); - - // mime1/mime1 mime2/mime2 - // ^ - if (!skipExactly(position, end, '/')) { - skipWhile<isNotASCIISpace>(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - - // mime1/mime1 mime2/mime2 - // ^ - if (!skipExactly<isMediaTypeCharacter>(position, end)) { - skipWhile<isNotASCIISpace>(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - skipWhile<isMediaTypeCharacter>(position, end); - - // mime1/mime1 mime2/mime2 OR mime1/mime1 OR mime1/mime1/error - // ^ ^ ^ - if (position < end && isNotASCIISpace(*position)) { - skipWhile<isNotASCIISpace>(position, end); - policy()->reportInvalidPluginTypes(String(begin, position - begin)); - continue; - } - m_pluginTypes.add(String(begin, position - begin)); - - ASSERT(position == end || isASCIISpace(*position)); - } - } - - HashSet<String> 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<CSPDirectiveList> 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<URL>& 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 <class CSPDirectiveType> - void setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>&); - - 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<MediaListDirective> m_pluginTypes; - OwnPtr<NonceDirective> m_scriptNonce; - OwnPtr<SourceListDirective> m_baseURI; - OwnPtr<SourceListDirective> m_connectSrc; - OwnPtr<SourceListDirective> m_defaultSrc; - OwnPtr<SourceListDirective> m_fontSrc; - OwnPtr<SourceListDirective> m_formAction; - OwnPtr<SourceListDirective> m_frameSrc; - OwnPtr<SourceListDirective> m_imgSrc; - OwnPtr<SourceListDirective> m_mediaSrc; - OwnPtr<SourceListDirective> m_objectSrc; - OwnPtr<SourceListDirective> m_scriptSrc; - OwnPtr<SourceListDirective> m_styleSrc; - - Vector<URL> 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> CSPDirectiveList::create(ContentSecurityPolicy* policy, const String& header, ContentSecurityPolicy::HeaderType type) -{ - OwnPtr<CSPDirectiveList> 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. '<object type=\"[TYPE GOES HERE]\" ...>')."; - - 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 / <VCHAR except ";"> ) -// -bool CSPDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) -{ - ASSERT(name.isEmpty()); - ASSERT(value.isEmpty()); - - const UChar* position = begin; - skipWhile<isASCIISpace>(position, end); - - // Empty directive (e.g. ";;;"). Exit early. - if (position == end) - return false; - - const UChar* nameBegin = position; - skipWhile<isDirectiveNameCharacter>(position, end); - - // The directive-name must be non-empty. - if (nameBegin == position) { - skipWhile<isNotASCIISpace>(position, end); - m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); - return false; - } - - name = String(nameBegin, position - nameBegin); - - if (position == end) - return true; - - if (!skipExactly<isASCIISpace>(position, end)) { - skipWhile<isNotASCIISpace>(position, end); - m_policy->reportUnsupportedDirective(String(nameBegin, position - nameBegin)); - return false; - } - - skipWhile<isASCIISpace>(position, end); - - const UChar* valueBegin = position; - skipWhile<isDirectiveValueCharacter>(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<isASCIISpace>(position, end); - - const UChar* urlBegin = position; - skipWhile<isNotASCIISpace>(position, end); - - if (urlBegin < position) { - String url = String(urlBegin, position - urlBegin); - m_reportURIs.append(m_policy->completeURL(url)); - } - } -} - - -template<class CSPDirectiveType> -void CSPDirectiveList::setCSPDirective(const String& name, const String& value, OwnPtr<CSPDirectiveType>& 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<isASCIISpace>(position, end); - const UChar* begin = position; - skipWhile<isNotASCIISpace>(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<isASCIISpace>(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<SourceListDirective>(name, value, m_defaultSrc); - else if (equalIgnoringCase(name, scriptSrc)) - setCSPDirective<SourceListDirective>(name, value, m_scriptSrc); - else if (equalIgnoringCase(name, objectSrc)) - setCSPDirective<SourceListDirective>(name, value, m_objectSrc); - else if (equalIgnoringCase(name, frameSrc)) - setCSPDirective<SourceListDirective>(name, value, m_frameSrc); - else if (equalIgnoringCase(name, imgSrc)) - setCSPDirective<SourceListDirective>(name, value, m_imgSrc); - else if (equalIgnoringCase(name, styleSrc)) - setCSPDirective<SourceListDirective>(name, value, m_styleSrc); - else if (equalIgnoringCase(name, fontSrc)) - setCSPDirective<SourceListDirective>(name, value, m_fontSrc); - else if (equalIgnoringCase(name, mediaSrc)) - setCSPDirective<SourceListDirective>(name, value, m_mediaSrc); - else if (equalIgnoringCase(name, connectSrc)) - setCSPDirective<SourceListDirective>(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<SourceListDirective>(name, value, m_baseURI); - else if (equalIgnoringCase(name, formAction)) - setCSPDirective<SourceListDirective>(name, value, m_formAction); - else if (equalIgnoringCase(name, pluginTypes)) - setCSPDirective<MediaListDirective>(name, value, m_pluginTypes); - else if (equalIgnoringCase(name, scriptNonce)) - setCSPDirective<NonceDirective>(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<CSPDirectiveList> 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 (CSPDirectiveList::*allowed)(ContentSecurityPolicy::ReportingStatus) const> -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 (CSPDirectiveList::*allowed)(JSC::ExecState* state, ContentSecurityPolicy::ReportingStatus) const> -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 (CSPDirectiveList::*allowed)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const> -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 (CSPDirectiveList::*allowed)(const String&, const String&, const WTF::OrdinalNumber&, const URL&) const> -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 (CSPDirectiveList::*allowFromURL)(const URL&, ContentSecurityPolicy::ReportingStatus) const> -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<ScriptCallStack> 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<URL>& 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<InspectorObject> 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<ScriptCallStack> 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<InspectorObject> reportObject = InspectorObject::create(); - reportObject->setObject("csp-report", cspReport.release()); - - RefPtr<FormData> 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: <http://webkit.org/b/114317> 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 <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> -#include <wtf/text/TextPosition.h> -#include <wtf/text/WTFString.h> - -namespace WTF { -class OrdinalNumber; -} - -namespace WebCore { - -class CSPDirectiveList; -class DOMStringList; -class ScriptExecutionContext; -class SecurityOrigin; - -typedef int SandboxFlags; -typedef Vector<OwnPtr<CSPDirectiveList>> CSPDirectiveListVector; - -class ContentSecurityPolicy { - WTF_MAKE_FAST_ALLOCATED; -public: - static PassOwnPtr<ContentSecurityPolicy> 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<URL>& 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 <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> 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<ContextMenu> customizeMenu(PassOwnPtr<ContextMenu>) = 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/DragSession.h b/Source/WebCore/page/ContextMenuContext.cpp index 0e9375196..bc81dc0fb 100644 --- a/Source/WebCore/page/DragSession.h +++ b/Source/WebCore/page/ContextMenuContext.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * 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 @@ -10,39 +10,39 @@ * 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 DragSession_h -#define DragSession_h +#include "config.h" +#include "ContextMenuContext.h" -#include "DragActions.h" +#if ENABLE(CONTEXT_MENUS) namespace WebCore { - -struct DragSession { - DragOperation operation; - bool mouseIsOverFileInput; - unsigned numberOfItemsToBeAccepted; - - DragSession() - : operation(DragOperationNone) - , mouseIsOverFileInput(false) - , numberOfItemsToBeAccepted(0) - { - } -}; +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<Image> 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 <wtf/unicode/CharacterNames.h> -#include <wtf/unicode/Unicode.h> 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<ContextMenuItem> separatorItem() +static std::unique_ptr<ContextMenuItem> separatorItem() { - return adoptPtr(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String())); + return std::unique_ptr<ContextMenuItem>(new ContextMenuItem(SeparatorType, ContextMenuItemTagNoAction, String())); } -void ContextMenuController::showContextMenu(Event* event, PassRefPtr<ContextMenuProvider> 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<ContextMenu> ContextMenuController::createContextMenu(Event* event) +#if ENABLE(SERVICE_CONTROLS) + +static Image* imageFromImageElementNode(Node& node) { - ASSERT(event); - - if (!event->isMouseEvent()) + auto* renderer = node.renderer(); + if (!is<RenderImage>(renderer)) return nullptr; + auto* image = downcast<RenderImage>(*renderer).cachedImage(); + if (!image || image->errorOccurred()) + return nullptr; + return image->imageForRenderer(renderer); +} - MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - HitTestResult result(mouseEvent->absoluteLocation()); +#endif + +std::unique_ptr<ContextMenu> ContextMenuController::maybeCreateContextMenu(Event& event) +{ + if (!is<MouseEvent>(event)) + return nullptr; - if (Frame* frame = event->target()->toNode()->document().frame()) - result = frame->eventHandler().hitTestResultAtPoint(mouseEvent->absoluteLocation()); + auto& mouseEvent = downcast<MouseEvent>(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<ContextMenu>(); } -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<Frame> 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<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(*document, createFragmentFromMarkup(*document, item->title(), ""), replaceOptions); + RefPtr<ReplaceSelectionCommand> 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<Range> 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<HTMLFormControlElement>(*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()); @@ -856,18 +873,20 @@ void ContextMenuController::populate() #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<String> 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<String> dictationAlternatives = m_hitTestResult.dictationAlternatives(); + Vector<String> 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 <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> +#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<ContextMenuProvider>); + 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<ContextMenu> 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<ContextMenu> 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<ContextMenu> m_contextMenu; - RefPtr<ContextMenuProvider> m_menuProvider; - HitTestResult m_hitTestResult; - }; + Page& m_page; + ContextMenuClient& m_client; + std::unique_ptr<ContextMenu> m_contextMenu; + RefPtr<ContextMenuProvider> 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 <wtf/RefCounted.h> namespace WebCore { class ContextMenu; -class ContextMenuItem; class ContextMenuProvider : public RefCounted<ContextMenuProvider> { 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 <runtime/ArrayBufferView.h> #include <wtf/CryptographicallyRandomNumber.h> 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<void> 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<WebKitSubtleCrypto&> 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<Document>(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 <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> +#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<Crypto> { public: - static PassRefPtr<Crypto> create(Document& document) { return adoptRef(new Crypto(document)); } + static Ref<Crypto> create(ScriptExecutionContext& context) { return adoptRef(*new Crypto(context)); } virtual ~Crypto(); - Document* document() const; - - void getRandomValues(JSC::ArrayBufferView*, ExceptionCode&); + ExceptionOr<void> getRandomValues(JSC::ArrayBufferView&); #if ENABLE(SUBTLE_CRYPTO) - SubtleCrypto* subtle(); + SubtleCrypto& subtle(); + + // Will be deprecated. + ExceptionOr<WebKitSubtleCrypto&> webkitSubtle(); #endif private: - Crypto(Document&); + Crypto(ScriptExecutionContext&); #if ENABLE(SUBTLE_CRYPTO) - RefPtr<SubtleCrypto> m_subtle; + Ref<SubtleCrypto> m_subtle; + + // Will be deprecated. + RefPtr<WebKitSubtleCrypto> 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 <wtf/text/TextPosition.h> -#include <wtf/text/WTFString.h> - -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 (ContentSecurityPolicy::*allowWithType)(const String&, const String&, const URL&, ContentSecurityPolicy::ReportingStatus) const> -bool isAllowedWithType(ScriptExecutionContext* context, const String& type) -{ - if (!isPolicyActiveInContext(context)) - return true; - - return (context->contentSecurityPolicy()->*allowWithType)(type, type, URL(), ContentSecurityPolicy::SuppressReport); -} - -template<bool (ContentSecurityPolicy::*allowWithURL)(const URL&, ContentSecurityPolicy::ReportingStatus) const> -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 (ContentSecurityPolicy::*allowWithContext)(const String&, const WTF::OrdinalNumber&, ContentSecurityPolicy::ReportingStatus) const> -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<DOMStringList> DOMSecurityPolicy::reportURIs() const -{ - RefPtr<DOMStringList> 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.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 <wtf/text/WTFString.h> 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<Frame> protector(*m_frame); + m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM); } -void DOMSelection::collapseToEnd(ExceptionCode& ec) +ExceptionOr<void> 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<Frame> protector(*m_frame); + selection.moveTo(selection.selection().end(), DOWNSTREAM); + return { }; } -void DOMSelection::collapseToStart(ExceptionCode& ec) +ExceptionOr<void> 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<Frame> 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<Frame> 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<Frame> 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<Frame> protector(*m_frame); m_frame->selection().modify(alter, direction, granularity); } -void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec) +ExceptionOr<void> 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<Frame> protector(*m_frame); + m_frame->selection().setExtent(createLegacyEditingPosition(&node, offset), DOWNSTREAM); + return { }; } -PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec) +ExceptionOr<Ref<Range>> 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<Frame> protector(*m_frame); + auto& selection = m_frame->selection(); if (selection.isNone()) { - selection.setSelection(VisibleSelection(r)); + selection.moveTo(&range); return; } - RefPtr<Range> 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<Range> 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<Frame> 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> node = n; - RefPtr<Range> selectedRange = selection.selection().toNormalizedRange(); + Ref<Node> 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 <wtf/Forward.h> -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> namespace WebCore { - class Frame; - class Node; - class Position; - class Range; - class TreeScope; - class VisibleSelection; - - typedef int ExceptionCode; - - class DOMSelection : public RefCounted<DOMSelection>, public DOMWindowProperty { - public: - static PassRefPtr<DOMSelection> 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<Range> 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<DOMSelection>, public DOMWindowProperty { +public: + static Ref<DOMSelection> 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<void> collapseToEnd(); + ExceptionOr<void> collapseToStart(); + ExceptionOr<void> extend(Node&, unsigned offset); + ExceptionOr<Ref<Range>> 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 <wtf/CurrentTime.h> -#include <wtf/HashSet.h> +#include <wtf/HashMap.h> +#include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/RandomNumber.h> #include <wtf/StdLibExtras.h> #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<Document>(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<Document>(context).domTreeVersion(); + m_previous = current; + current = this; + } + } + + ~DOMTimerFireState() + { + if (m_contextIsDocument) + current = m_previous; + } + + Document* contextDocument() const { return m_contextIsDocument ? &downcast<Document>(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<int, DOMTimer*>::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<Document>(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<NestedTimersMap> map; + return map; + } + + static bool isTrackingNestedTimers; + HashMap<int /* timeoutId */, DOMTimer*> 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<UserGestureToken> userGestureTokenToForward(std::chrono::milliseconds interval, int nestingLevel) +{ + if (!shouldForwardUserGesture(interval, nestingLevel)) + return nullptr; + + return UserGestureIndicator::currentUserGesture(); } -DOMTimer::DOMTimer(ScriptExecutionContext* context, PassOwnPtr<ScheduledAction> action, int interval, bool singleShot) +DOMTimer::DOMTimer(ScriptExecutionContext& context, std::unique_ptr<ScheduledAction> 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<DOMTimer> 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<ScheduledAction> action, int timeout, bool singleShot) +int DOMTimer::install(ScriptExecutionContext& context, std::unique_ptr<ScheduledAction> 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<Document>(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, PassOwnPtr<ScheduledActio timer->suspendIfNeeded(); 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<DOMTimer> 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<ScheduledAction> 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<Document>(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<Document>(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<std::chrono::milliseconds> 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<std::chrono::milliseconds>(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 <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include "UserGestureIndicator.h" +#include <memory> +#include <wtf/RefCounted.h> 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<ScheduledAction>, int timeout, bool singleShot); - static void removeById(ScriptExecutionContext*, int timeoutId); +class DOMTimer final : public RefCounted<DOMTimer>, 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<ScheduledAction>, 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<ScheduledAction>, 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<ScheduledAction>, 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<ScheduledAction> 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<std::chrono::milliseconds> 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<ScheduledAction> m_action; + std::chrono::milliseconds m_originalInterval; + TimerThrottleState m_throttleState; + std::chrono::milliseconds m_currentTimerInterval; + RefPtr<UserGestureToken> 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 <algorithm> +#include <inspector/ScriptCallStack.h> +#include <inspector/ScriptCallStackFactory.h> +#include <memory> #include <wtf/CurrentTime.h> #include <wtf/MainThread.h> #include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Ref.h> -#include <wtf/text/Base64.h> +#include <wtf/Variant.h> #include <wtf/text/WTFString.h> -#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<SerializedScriptValue> message, const String& sourceOrigin, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortChannelArray> channels, SecurityOrigin* targetOrigin, PassRefPtr<ScriptCallStack> stackTrace) + PostMessageTimer(DOMWindow& window, Ref<SerializedScriptValue>&& message, const String& sourceOrigin, DOMWindow& source, std::unique_ptr<MessagePortChannelArray> channels, RefPtr<SecurityOrigin>&& targetOrigin, RefPtr<ScriptCallStack>&& 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<MessageEvent> event(ScriptExecutionContext* context) + Ref<MessageEvent> event(ScriptExecutionContext& context) { - OwnPtr<MessagePortArray> 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<DOMWindow>(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<PostMessageTimer> timer(this); + + UserGestureIndicator userGestureIndicator(m_userGestureToForward); + m_window->postMessageTimerFired(*timer); } - RefPtr<DOMWindow> m_window; - RefPtr<SerializedScriptValue> m_message; + Ref<DOMWindow> m_window; + Ref<SerializedScriptValue> m_message; String m_origin; - RefPtr<DOMWindow> m_source; - OwnPtr<MessagePortChannelArray> m_channels; + Ref<DOMWindow> m_source; + std::unique_ptr<MessagePortChannelArray> m_channels; RefPtr<SecurityOrigin> m_targetOrigin; RefPtr<ScriptCallStack> m_stackTrace; + RefPtr<UserGestureToken> m_userGestureToForward; }; typedef HashCountedSet<DOMWindow*> DOMWindowSet; static DOMWindowSet& windowsWithUnloadEventListeners() { - DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithUnloadEventListeners, ()); + static NeverDestroyed<DOMWindowSet> windowsWithUnloadEventListeners; return windowsWithUnloadEventListeners; } static DOMWindowSet& windowsWithBeforeUnloadEventListeners() { - DEFINE_STATIC_LOCAL(DOMWindowSet, windowsWithBeforeUnloadEventListeners, ()); + static NeverDestroyed<DOMWindowSet> windowsWithBeforeUnloadEventListeners; return windowsWithBeforeUnloadEventListeners; } @@ -237,22 +265,21 @@ bool DOMWindow::dispatchAllPendingBeforeUnloadEvents() Vector<Ref<DOMWindow>> 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<Ref<DOMWindow>> 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<DOMWindow*>(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<MediaQueryList> DOMWindow::matchMedia(const String& media) +RefPtr<MediaQueryList> 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<DOMWindow> 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<DOMWindowProperty*> 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<DOMWindowProperty*> 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<DOMWindowProperty*> 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<DOMWindowProperty*> 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<DOMWindowProperty*> 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<SelectorQuery&> 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<Ref<NodeList>> DOMWindow::collectMatchingElementsInFlatTree(Node& scope, const String& selectors) +{ + auto queryOrException = selectorQueryInFrame(m_frame, selectors); + if (queryOrException.hasException()) + return queryOrException.releaseException(); + + if (!is<ContainerNode>(scope)) + return Ref<NodeList> { StaticElementList::create() }; + + SelectorQuery& query = queryOrException.releaseReturnValue(); + + Vector<Ref<Element>> result; + for (auto& node : composedTreeDescendants(downcast<ContainerNode>(scope))) { + if (is<Element>(node) && query.matches(downcast<Element>(node)) && !node.isInUserAgentShadowTree()) + result.append(downcast<Element>(node)); + } + + return Ref<NodeList> { StaticElementList::create(WTFMove(result)) }; +} + +ExceptionOr<RefPtr<Element>> DOMWindow::matchingElementInFlatTree(Node& scope, const String& selectors) +{ + auto queryOrException = selectorQueryInFrame(m_frame, selectors); + if (queryOrException.hasException()) + return queryOrException.releaseException(); + + if (!is<ContainerNode>(scope)) + return RefPtr<Element> { nullptr }; + + SelectorQuery& query = queryOrException.releaseReturnValue(); + + for (auto& node : composedTreeDescendants(downcast<ContainerNode>(scope))) { + if (is<Element>(node) && query.matches(downcast<Element>(node)) && !node.isInUserAgentShadowTree()) + return &downcast<Element>(node); + } + + return RefPtr<Element> { 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<Storage*> 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> 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<Storage*> 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 <https://bugs.webkit.org/show_bug.cgi?id=135330>. + 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> 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<SerializedScriptValue> 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<SerializedScriptValue> message, const MessagePortArray* ports, const String& targetOrigin, DOMWindow& source, ExceptionCode& ec) +ExceptionOr<void> DOMWindow::postMessage(JSC::ExecState& state, DOMWindow& callerWindow, JSC::JSValue messageValue, const String& targetOrigin, Vector<JSC::Strong<JSC::JSObject>>&& 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<SecurityOrigin> 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<MessagePortChannelArray> channels = MessagePort::disentanglePorts(ports, ec); - if (ec) - return; + Vector<RefPtr<MessagePort>> 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<ScriptCallStack> 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<PostMessageTimer> t) +void DOMWindow::postMessageTimerFired(PostMessageTimer& timer) { - OwnPtr<PostMessageTimer> timer(t); - if (!document() || !isCurrentlyDisplayedInFrame()) return; - dispatchMessageEventWithOriginCheck(timer->targetOrigin(), timer->event(document()), timer->stackTrace()); -} - -void DOMWindow::dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtr<Event> event, PassRefPtr<ScriptCallStack> 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<char> 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<int>(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<int>(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<int>(view->visibleContentRectIncludingScrollbars().height())); + return view->mapFromLayoutToCSSUnits(static_cast<int>(view->unobscuredContentRectIncludingScrollbars().height())); } int DOMWindow::innerWidth() const @@ -1165,7 +1276,7 @@ int DOMWindow::innerWidth() const if (!view) return 0; - return view->mapFromLayoutToCSSUnits(static_cast<int>(view->visibleContentRectIncludingScrollbars().width())); + return view->mapFromLayoutToCSSUnits(static_cast<int>(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<int>(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<int>(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<Document>(ContextDestructionObserver::scriptExecutionContext()); } -PassRefPtr<StyleMedia> DOMWindow::styleMedia() const +RefPtr<StyleMedia> 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<CSSStyleDeclaration> DOMWindow::getComputedStyle(Element* elt, const String& pseudoElt) const +Ref<CSSStyleDeclaration> 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<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* element, const String& pseudoElement, bool authorOnly) const +RefPtr<CSSRuleList> 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<CSSRuleList> DOMWindow::getMatchedCSSRules(Element* element, const St PseudoId pseudoId = CSSSelector::pseudoId(pseudoType); - Vector<RefPtr<StyleRuleBase>> 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<StaticCSSRuleList> 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<WebKitPoint> DOMWindow::webkitConvertPointFromNodeToPage(Node* node, const WebKitPoint* p) const +RefPtr<WebKitPoint> 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<WebKitPoint> DOMWindow::webkitConvertPointFromNodeToPage(Node* node, return WebKitPoint::create(pagePoint.x(), pagePoint.y()); } -PassRefPtr<WebKitPoint> DOMWindow::webkitConvertPointFromPageToNode(Node* node, const WebKitPoint* p) const +RefPtr<WebKitPoint> 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<FrameView> 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<FrameView> 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<int>(x * m_frame->pageZoomFactor() * m_frame->frameScaleFactor()); - int zoomedY = static_cast<int>(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<ScheduledAction> action, int timeout, ExceptionCode& ec) +ExceptionOr<int> DOMWindow::setTimeout(std::unique_ptr<ScheduledAction> 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<ScheduledAction> action, int timeout, ExceptionCode& ec) +ExceptionOr<int> DOMWindow::setInterval(std::unique_ptr<ScheduledAction> 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<RequestAnimationFrameCallback> callback) +int DOMWindow::requestAnimationFrame(Ref<RequestAnimationFrameCallback>&& 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<RequestAnimationFrameCallback> callback) +int DOMWindow::webkitRequestAnimationFrame(Ref<RequestAnimationFrameCallback>&& 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<EventListener> 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<EventListener>&& 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, PassRefPtr<Event addBeforeUnloadEventListener(this); #if ENABLE(DEVICE_ORIENTATION) #if PLATFORM(IOS) - else if (eventType == eventNames().devicemotionEvent && document()) - document()->deviceMotionController()->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, PassRefPtr<Event else if (eventNames().isGestureEventType(eventType)) ++m_touchEventListenerCount; #endif - +#if ENABLE(GAMEPAD) + else if (eventNames().isGamepadEventType(eventType)) + incrementGamepadEventListenerCount(); +#endif #if ENABLE(PROXIMITY_EVENTS) else if (eventType == eventNames().webkitdeviceproximityEvent) { if (DeviceProximityController* controller = DeviceProximityController::from(page())) @@ -1692,13 +1831,14 @@ bool DOMWindow::addEventListener(const AtomicString& eventType, PassRefPtr<Event } #if PLATFORM(IOS) + void DOMWindow::incrementScrollEventListenersCount() { Document* document = this->document(); 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<Event> 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<Event> 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> 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<Event> prpEvent, PassRefPtr<EventTarget> prpTarget) +bool DOMWindow::dispatchEvent(Event& event, EventTarget* target) { - Ref<EventTarget> protect(*this); - RefPtr<Event> event = prpEvent; + Ref<EventTarget> 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: // <http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#event-pageshow>. - 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<Frame> DOMWindow::createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures& windowFeatures, DOMWindow& activeWindow, Frame* firstFrame, Frame* openerFrame, std::function<void (DOMWindow&)> prepareDialogFunction) +RefPtr<Frame> DOMWindow::createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures& windowFeatures, DOMWindow& activeWindow, Frame& firstFrame, Frame& openerFrame, std::function<void (DOMWindow&)> 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<Frame> newFrame = WebCore::createWindow(activeFrame, openerFrame, frameRequest, windowFeatures, created); + RefPtr<Frame> 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> DOMWindow::open(const String& urlString, const AtomicString& frameName, const String& windowFeaturesString, - DOMWindow& activeWindow, DOMWindow& firstWindow) +RefPtr<DOMWindow> 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> 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> 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<Frame> result = createWindow(urlString, frameName, windowFeatures, activeWindow, firstFrame, m_frame); - return result ? result->document()->domWindow() : 0; + RefPtr<Frame> 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<void (DOMWindow&)> 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<Frame> dialogFrame = createWindow(urlString, emptyAtom, windowFeatures, activeWindow, firstFrame, m_frame, std::move(prepareDialogFunction)); + RefPtr<Frame> 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 <functional> +#include <wtf/HashSet.h> +#include <wtf/WeakPtr.h> -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<RefPtr<MessagePort>, 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<DOMWindow> - , public EventTargetWithInlineData - , public ContextDestructionObserver - , public FrameDestructionObserver - , public Supplementable<DOMWindow> { - public: - static PassRefPtr<DOMWindow> 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<typename> 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<MediaQueryList> 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<DOMWindow> + , public EventTargetWithInlineData + , public ContextDestructionObserver + , public FrameDestructionObserver + , public Base64Utilities + , public Supplementable<DOMWindow> { +public: + static Ref<DOMWindow> 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<MediaQueryList> 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<DOMWindow> 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<void(DOMWindow&)> 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<DOMWindow> 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<void (DOMWindow&)> 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> 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<CSSStyleDeclaration> getComputedStyle(Element&, const String& pseudoElt) const; - unsigned length() const; + // WebKit extensions - String name() const; - void setName(const String&); + WEBCORE_EXPORT RefPtr<CSSRuleList> 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<WebKitPoint> webkitConvertPointFromPageToNode(Node*, const WebKitPoint*) const; + RefPtr<WebKitPoint> 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<void> postMessage(JSC::ExecState&, DOMWindow& callerWindow, JSC::JSValue message, const String& targetOrigin, Vector<JSC::Strong<JSC::JSObject>>&&); + 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> styleMedia() const; + void resizeBy(float x, float y) const; + void resizeTo(float width, float height) const; - // DOM Level 2 Style Interface + // Timers + ExceptionOr<int> setTimeout(std::unique_ptr<ScheduledAction>, int timeout); + void clearTimeout(int timeoutId); + ExceptionOr<int> setInterval(std::unique_ptr<ScheduledAction>, int timeout); + void clearInterval(int timeoutId); - PassRefPtr<CSSStyleDeclaration> getComputedStyle(Element*, const String& pseudoElt) const; + int requestAnimationFrame(Ref<RequestAnimationFrameCallback>&&); + int webkitRequestAnimationFrame(Ref<RequestAnimationFrameCallback>&&); + void cancelAnimationFrame(int id); - // WebKit extensions + // Events + // EventTarget API + bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) final; + bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) final; + void removeAllEventListeners() final; - PassRefPtr<CSSRuleList> getMatchedCSSRules(Element*, const String& pseudoElt, bool authorOnly = true) const; - double devicePixelRatio() const; + using EventTarget::dispatchEvent; + bool dispatchEvent(Event&, EventTarget*); - PassRefPtr<WebKitPoint> webkitConvertPointFromPageToNode(Node*, const WebKitPoint*) const; - PassRefPtr<WebKitPoint> 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<SerializedScriptValue> message, const MessagePortArray*, const String& targetOrigin, DOMWindow& source, ExceptionCode&); - // Needed for Objective-C bindings (see bug 28774). - void postMessage(PassRefPtr<SerializedScriptValue> message, MessagePort*, const String& targetOrigin, DOMWindow& source, ExceptionCode&); - void postMessageTimerFired(PassOwnPtr<PostMessageTimer>); - void dispatchMessageEventWithOriginCheck(SecurityOrigin* intendedTargetOrigin, PassRefPtr<Event>, PassRefPtr<ScriptCallStack>); + 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<Storage*> sessionStorage() const; + ExceptionOr<Storage*> 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<ScheduledAction>, int timeout, ExceptionCode&); - void clearTimeout(int timeoutId); - int setInterval(PassOwnPtr<ScheduledAction>, int timeout, ExceptionCode&); - void clearInterval(int timeoutId); + ExceptionOr<Ref<NodeList>> collectMatchingElementsInFlatTree(Node&, const String& selectors); + ExceptionOr<RefPtr<Element>> matchingElementInFlatTree(Node&, const String& selectors); - // WebKit animation extensions -#if ENABLE(REQUEST_ANIMATION_FRAME) - int requestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>); - int webkitRequestAnimationFrame(PassRefPtr<RequestAnimationFrameCallback>); - 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<EventListener>, bool useCapture) override; - virtual bool removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture) override; - virtual void removeAllEventListeners() override; - - using EventTarget::dispatchEvent; - bool dispatchEvent(PassRefPtr<Event> prpEvent, PassRefPtr<EventTarget> 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<DOMWindow>::ref; - using RefCounted<DOMWindow>::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<DOMWindow> 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<Frame> createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures&, DOMWindow& activeWindow, Frame& firstFrame, Frame& openerFrame, std::function<void(DOMWindow&)> 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<Frame> createWindow(const String& urlString, const AtomicString& frameName, const WindowFeatures&, DOMWindow& activeWindow, Frame* firstFrame, Frame* openerFrame, std::function<void (DOMWindow&)> 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<bool> m_canShowModalDialogOverride; - bool m_shouldPrintWhenFinishedLoading; - bool m_suspendedForPageCache; + HashSet<DOMWindowProperty*> m_properties; - HashSet<DOMWindowProperty*> m_properties; + mutable RefPtr<Crypto> m_crypto; + mutable RefPtr<History> m_history; + mutable RefPtr<BarProp> m_locationbar; + mutable RefPtr<StyleMedia> m_media; + mutable RefPtr<BarProp> m_menubar; + mutable RefPtr<Navigator> m_navigator; + mutable RefPtr<BarProp> m_personalbar; + mutable RefPtr<Screen> m_screen; + mutable RefPtr<BarProp> m_scrollbars; + mutable RefPtr<DOMSelection> m_selection; + mutable RefPtr<BarProp> m_statusbar; + mutable RefPtr<BarProp> m_toolbar; + mutable RefPtr<Location> m_location; - mutable RefPtr<Screen> m_screen; - mutable RefPtr<History> m_history; - mutable RefPtr<Crypto> m_crypto; - mutable RefPtr<BarProp> m_locationbar; - mutable RefPtr<BarProp> m_menubar; - mutable RefPtr<BarProp> m_personalbar; - mutable RefPtr<BarProp> m_scrollbars; - mutable RefPtr<BarProp> m_statusbar; - mutable RefPtr<BarProp> m_toolbar; - mutable RefPtr<Console> m_console; - mutable RefPtr<Navigator> m_navigator; - mutable RefPtr<Location> m_location; - mutable RefPtr<StyleMedia> 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<DOMWindow> 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<Storage> m_sessionStorage; - mutable RefPtr<Storage> m_localStorage; - mutable RefPtr<DOMApplicationCache> m_applicationCache; +#if ENABLE(GAMEPAD) + unsigned m_gamepadEventListenerCount { 0 }; +#endif + + mutable RefPtr<Storage> m_sessionStorage; + mutable RefPtr<Storage> m_localStorage; + mutable RefPtr<DOMApplicationCache> m_applicationCache; + + RefPtr<CustomElementRegistry> m_customElementRegistry; #if ENABLE(WEB_TIMING) - mutable RefPtr<Performance> m_performance; + mutable RefPtr<Performance> m_performance; #endif -#if ENABLE(CSS3_CONDITIONAL_RULES) - mutable RefPtr<DOMWindowCSS> m_css; +#if ENABLE(USER_MESSAGE_HANDLERS) + mutable RefPtr<WebKitNamespace> 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<object> 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<DOMWindowExtension> protect(*this); + Ref<DOMWindowExtension> 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<DOMWindowExtension> protect(*this); + Ref<DOMWindowExtension> 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<DOMWindowExtension> protect(*this); + Ref<DOMWindowExtension> 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<DOMWindowExtension> protect(*this); + Ref<DOMWindowExtension> 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 <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -39,27 +37,25 @@ class Frame; class DOMWindowExtension : public RefCounted<DOMWindowExtension>, public DOMWindowProperty { public: - static PassRefPtr<DOMWindowExtension> create(Frame* frame, DOMWrapperWorld& world) + static Ref<DOMWindowExtension> 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<DOMWrapperWorld> m_world; RefPtr<Frame> 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. <rdar://problem/11567132> 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/PageActivityAssertionToken.h b/Source/WebCore/page/DatabaseProvider.h index 0f3584b27..a76c791c8 100644 --- a/Source/WebCore/page/PageActivityAssertionToken.h +++ b/Source/WebCore/page/DatabaseProvider.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * 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 @@ -23,27 +23,25 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageActivityAssertionToken_h -#define PageActivityAssertionToken_h +#pragma once -#include <wtf/Noncopyable.h> +#include <wtf/RefCounted.h> namespace WebCore { -class PageThrottler; +class SessionID; -class PageActivityAssertionToken { - WTF_MAKE_NONCOPYABLE(PageActivityAssertionToken); -public: - PageActivityAssertionToken(PageThrottler&); - ~PageActivityAssertionToken(); +namespace IDBClient { +class IDBConnectionToServer; +} - void invalidate(); +class WEBCORE_EXPORT DatabaseProvider : public RefCounted<DatabaseProvider> { +public: + virtual ~DatabaseProvider(); -private: - PageThrottler* m_throttler; +#if ENABLE(INDEXED_DATABASE) + virtual IDBClient::IDBConnectionToServer& idbConnectionToServerForSession(const SessionID&) = 0; +#endif }; } // namespace WebCore - -#endif // PageActivityAssertionToken_h 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<RegionOverlay>, public PageOverlay::Client { +public: + static Ref<RegionOverlay> 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<PageOverlay> m_overlay; + std::unique_ptr<Region> m_region; + Color m_color; +}; + +class MouseWheelRegionOverlay final : public RegionOverlay { +public: + static Ref<MouseWheelRegionOverlay> 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<Region>(); + + 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<NonFastScrollableRegionOverlay> 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<Region>(); + + 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> 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<size_t>(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<RefPtr<RegionOverlay>> 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 <wtf/HashMap.h> +#include <wtf/Vector.h> + +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<MainFrame*, Vector<RefPtr<RegionOverlay>>> 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<Event> prpEvent) +void DeviceController::dispatchDeviceEvent(Event& event) { - RefPtr<Event> event = prpEvent; Vector<RefPtr<DOMWindow>> 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<DeviceController>& timer) +void DeviceController::fireDeviceEvent() { - ASSERT_UNUSED(timer, &timer == &m_timer); ASSERT(hasLastData()); m_timer.stop(); Vector<RefPtr<DOMWindow>> 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<Page> { + WTF_MAKE_FAST_ALLOCATED; public: explicit DeviceController(DeviceClient*); - ~DeviceController() { } + virtual ~DeviceController() { } void addDeviceEventListener(DOMWindow*); void removeDeviceEventListener(DOMWindow*); void removeAllDeviceEventListeners(DOMWindow*); - void dispatchDeviceEvent(PassRefPtr<Event>); + void dispatchDeviceEvent(Event&); bool isActive() { return !m_listeners.isEmpty(); } DeviceClient* client() { return m_client; } virtual bool hasLastData() { return false; } - virtual PassRefPtr<Event> getLastEvent() { return 0; } + virtual RefPtr<Event> getLastEvent() { return nullptr; } protected: - void fireDeviceEvent(Timer<DeviceController>&); + void fireDeviceEvent(); HashCountedSet<RefPtr<DOMWindow>> m_listeners; HashCountedSet<RefPtr<DOMWindow>> m_lastEventListeners; DeviceClient* m_client; - Timer<DeviceController> 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 <wtf/FastMalloc.h> +#include <wtf/Forward.h> +#include <wtf/RandomNumber.h> + +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 <wtf/text/WTFString.h> @@ -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 <limits.h> @@ -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 <wtf/CurrentTime.h> #include <wtf/RefPtr.h> +#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<bool>(keyState & PlatformEvent::ShiftKey); - ctrlKey = static_cast<bool>(keyState & PlatformEvent::CtrlKey); - altKey = static_cast<bool>(keyState & PlatformEvent::AltKey); - metaKey = static_cast<bool>(keyState & PlatformEvent::MetaKey); + if (is<HTMLAnchorElement>(element)) + return downcast<HTMLAnchorElement>(element).isLiveLink(); + if (is<SVGAElement>(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<DocumentFragment> documentFragmentFromDragData(DragData& dragData, Frame* frame, Range& context, bool allowPlainText, bool& chosePlainText) +static RefPtr<DocumentFragment> documentFragmentFromDragData(const DragData& dragData, Frame& frame, Range& context, bool allowPlainText, bool& chosePlainText) { chosePlainText = false; Document& document = context.ownerDocument(); if (dragData.containsCompatibleContent()) { - if (PassRefPtr<DocumentFragment> 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<HTMLAnchorElement> 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<Node> anchorText = document.createTextNode(title); - anchor->appendChild(anchorText, IGNORE_EXCEPTION); - RefPtr<DocumentFragment> 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<FrameView> v = m_page.mainFrame().view()) { - ClipboardAccessPolicy policy = (!m_documentUnderMouse || m_documentUnderMouse->securityOrigin()->isLocal()) ? ClipboardReadable : ClipboardTypesReadable; - RefPtr<Clipboard> 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> 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 = 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<HTMLInputElement>(node)) + return nullptr; - HTMLInputElement* inputElement = node->toInputElement(); + auto* inputElement = &downcast<HTMLInputElement>(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<ShadowRoot>(inputElement->treeScope().rootNode())) { + auto& host = *downcast<ShadowRoot>(inputElement->treeScope().rootNode()).host(); + inputElement = is<HTMLInputElement>(host) ? &downcast<HTMLInputElement>(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<Element>(*node)) + node = node->parentNode(); + if (node) + node = node->deprecatedShadowAncestorNode(); - return toElement(n); + return downcast<Element>(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<PluginDocument>(document)) { + const Widget* widget = downcast<PluginDocument>(*document).pluginWidget(); + const PluginViewBase* pluginView = is<PluginViewBase>(widget) ? downcast<PluginViewBase>(widget) : nullptr; if (pluginView) pluginDocumentAcceptsDrags = pluginView->shouldAllowNavigationFromDrags(); } - if (doc && (m_didInitiateDrag || (doc->isPluginDocument() && !pluginDocumentAcceptsDrags) || doc->hasEditableStyle())) + if (document && (m_didInitiateDrag || (is<PluginDocument>(*document) && !pluginDocumentAcceptsDrags) || document->hasEditableStyle())) return DragOperationNone; return dragOperation(dragData); } static bool setSelectionToDragCaret(Frame* frame, VisibleSelection& dragCaret, RefPtr<Range>& range, const IntPoint& point) { + Ref<Frame> 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<HTMLInputElement> 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> range = dragCaret.toNormalizedRange(); - RefPtr<Element> rootEditableElement = innerFrame->selection().rootEditableElement(); + RefPtr<Element> 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<DocumentFragment> fragment = documentFragmentFromDragData(dragData, innerFrame.get(), *range, true, chosePlainText); - if (!fragment || !innerFrame->editor().shouldInsertFragment(fragment, range, EditorInsertActionDropped)) { + RefPtr<DocumentFragment> 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<HTMLPlugInElement>(*result.innerNonSharedNode())) { + if (!downcast<HTMLPlugInElement>(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> 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 = 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<HTMLAttachmentElement>(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<DragSourceAction>(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<HTMLImageElement>(*element) && sourceFrame->settings().loadsImagesAutomatically()) { state.type = static_cast<DragSourceAction>(state.type | DragSourceActionImage); return element; } - if ((m_dragSourceAction & DragSourceActionLink) - && isHTMLAnchorElement(element) - && toHTMLAnchorElement(element)->isLiveLink()) { + if ((m_dragSourceAction & DragSourceActionLink) && isDraggableLink(*element)) { state.type = static_cast<DragSourceAction>(state.type | DragSourceActionLink); return element; } +#if ENABLE(ATTACHMENT_ELEMENT) + if ((m_dragSourceAction & DragSourceActionAttachment) + && is<HTMLAttachmentElement>(*element) + && downcast<HTMLAttachmentElement>(*element).file()) { + state.type = static_cast<DragSourceAction>(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<RenderImage>(renderer)) + return nullptr; + auto& image = downcast<RenderImage>(*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> 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<Frame> 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<DragOperation>(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<Range> selectionRange = src.selection().toNormalizedRange(); ASSERT(selectionRange); +#if ENABLE(DATA_INTERACTION) + Vector<SelectionRect> 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<MainFrame> frameProtector(m_page.mainFrame()); RefPtr<FrameView> 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<DragController> create(Page*, DragClient*); + static std::unique_ptr<DragController> 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<Document> m_documentUnderMouse; // The document the mouse was last dragged over. RefPtr<Document> m_dragInitiator; // The Document (if any) that initiated the drag. RefPtr<HTMLInputElement> 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/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<Element> source; // Element that may be a drag source, for the current mouse gesture. bool shouldDispatchEvents; DragSourceAction type; - RefPtr<Clipboard> clipboard; // Used on only the source side of dragging. + RefPtr<DataTransfer> 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 <wtf/Forward.h> #include <wtf/Vector.h> -#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<String>& pasteboardTypes, Vector<RefPtr<SharedBuffer>>& pasteboardData) = 0; - - virtual void registerUndoStep(PassRefPtr<UndoStep>) = 0; - virtual void registerRedoStep(PassRefPtr<UndoStep>) = 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<DocumentFragment> 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<ArchiveResource>>&) = 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 <wtf/Assertions.h> #include <wtf/CurrentTime.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> -#include <wtf/TemporaryChange.h> -#include <wtf/WeakPtr.h> -#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<RenderListBox*>(&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<AutoscrollController>()) +#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<DragState> 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<Frame> 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<Frame> 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<SVGDocument>(*m_frame.document()) && downcast<SVGDocument>(*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<SVGDocument>(*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<Frame> 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<Frame> 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<Frame> 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<RenderBox>(renderer)) return; - m_autoscrollController->startPanScrolling(toRenderBox(renderer), lastKnownMousePosition()); + m_autoscrollController->startPanScrolling(&downcast<RenderBox>(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<Frame> 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<LayoutUnit>(0, padding.width()).toUnsigned(); + unsigned nonNegativePaddingHeight = std::max<LayoutUnit>(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> 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<Frame> 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<Frame> 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<RenderWidget>(renderer)) + return nullptr; - Widget* widget = toRenderWidget(renderer)->widget(); - if (!widget || !widget->isFrameView()) - return 0; + Widget* widget = downcast<RenderWidget>(*renderer).widget(); + if (!is<FrameView>(widget)) + return nullptr; - return &toFrameView(widget)->frame(); + return &downcast<FrameView>(*widget).frame(); } #if ENABLE(CURSOR_SUPPORT) static bool isSubmitImage(Node* node) { - return node && isHTMLInputElement(node) && toHTMLInputElement(node)->isImageButton(); + return is<HTMLInputElement>(node) && downcast<HTMLInputElement>(*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<EventHandler>&) +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<Cursor> 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<Frame> 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<EventHandler>& 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<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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<Frame> 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<Frame> 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 <input> 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<ShadowRoot>(*mouseEvent.targetNode()) && is<HTMLInputElement>(*downcast<ShadowRoot>(*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<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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<HTMLIFrameElement>(*ancestor) || is<HTMLHtmlElement>(*ancestor) || is<HTMLDocument>(*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<RenderListBox>(*renderer)) + return downcast<RenderListBox>(renderer); - return layer; + return renderer->enclosingLayer(); + } + + return nullptr; } bool EventHandler::mouseMoved(const PlatformMouseEvent& event) { + Ref<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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<SVGDocument>(*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<Frame> newSubframe = m_capturingMouseEventsElement.get() ? subframeForTargetNode(m_capturingMouseEventsElement.get()) : subframeForHitTestResult(mev); + RefPtr<Frame> 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,102 +1987,153 @@ 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 (!mousePressNode || !mouseReleaseNode) + return nullptr; - if (!targetNode) - return false; + if (mousePressNode == mouseReleaseNode) + return mouseReleaseNode; - ShadowRoot* containingShadowRoot = targetNode->containingShadowRoot(); - if (!containingShadowRoot) - return false; - - // FIXME: When an element in UA ShadowDOM (e.g. inner element in <input>) is clicked, - // we assume that the host element is clicked. This is necessary for implementing <input type="range"> 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; - - 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 <input type="range"> + // 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<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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<SVGDocument>(*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) +{ + Ref<Frame> protectedFrame(m_frame); + RefPtr<FrameView> 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. @@ -1991,10 +2152,10 @@ bool EventHandler::handlePasteGlobalSelection(const PlatformMouseEvent& mouseEve // 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<Frame> 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<MouseEvent> me = MouseEvent::create(eventType, + Ref<MouseEvent> 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<HTMLFrameElementBase>(target)) return false; - if (!target->hasTagName(frameTag) && !target->hasTagName(iframeTag)) - return false; - - frame = toHTMLFrameElementBase(target)->contentFrame(); - + frame = downcast<HTMLFrameElementBase>(*target).contentFrame(); return true; } @@ -2070,85 +2228,62 @@ static String convertDragOperationToDropZoneOperation(DragOperation operation) } } -static inline bool hasFileOfType(Clipboard& clipboard, const String& type) -{ - RefPtr<FileList> 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<Element>(*target) ? downcast<Element>(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<Frame> 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<Element> 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<Element>(*targetNode)) newTarget = targetNode->parentOrShadowHostElement(); else - newTarget = toElement(targetNode); + newTarget = downcast<Element>(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<Frame> 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<Frame> 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> 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<Frame> 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<Frame> 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. <rdar://problem/4196646> - if (!targetNode->isElementNode()) - targetElement = targetNode->parentOrShadowHostElement(); - else - targetElement = toElement(targetNode); + while (targetNode && !is<Element>(*targetNode)) + targetNode = targetNode->parentInComposedTree(); + targetElement = downcast<Element>(targetNode); } m_elementUnderMouse = targetElement; -#if ENABLE(SVG) - m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(targetElement); - - // <use> 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<SVGElementInstance*> instances = lastCorrespondingElement->instancesForElement(); - - // Locate the recloned shadow tree element for our corresponding instance - HashSet<SVGElementInstance*>::iterator end = instances.end(); - for (HashSet<SVGElementInstance*>::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<Ref<Element>, 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<Ref<Element>, 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<Frame> 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 <rdar://problem/4895428> 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 <rdar://problem/4895428> 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<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, 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<ScrollableArea>&) { - bool allVertical = m_recentWheelEventDeltas.size(); - bool allHorizontal = m_recentWheelEventDeltas.size(); + Ref<Frame> protectedFrame(m_frame); - Deque<FloatSize>::const_iterator end = m_recentWheelEventDeltas.end(); - for (Deque<FloatSize>::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<ScrollableArea>&) { - Document* document = m_frame.document(); +} + +IntPoint EventHandler::effectiveMousePositionForSelectionAutoscroll() const +{ + return m_lastKnownMousePosition; +} + +void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&) +{ + clearLatchedState(); +} +#endif + +Widget* EventHandler::widgetForEventTarget(Element* eventTarget) +{ + if (!eventTarget) + return nullptr; - if (!document->renderView()) + auto* target = eventTarget->renderer(); + if (!is<RenderWidget>(target)) + return nullptr; + + return downcast<RenderWidget>(*target).widget(); +} + +static WeakPtr<Widget> widgetForElement(const Element& element) +{ + auto target = element.renderer(); + if (!is<RenderWidget>(target) || !downcast<RenderWidget>(*target).widget()) + return { }; + + return downcast<RenderWidget>(*target).widget()->createWeakPtr(); +} + +bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& 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<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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> element = result.targetElement(); + RefPtr<ContainerNode> scrollableContainer; + WeakPtr<ScrollableArea> 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> 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::clearLatchedState() +{ +#if PLATFORM(MAC) + m_frame.mainFrame().resetLatchingState(); +#endif + if (auto filter = m_frame.mainFrame().wheelEventDeltaFilter()) + filter->endFilteringDeltas(); } -void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent* wheelEvent) +void EventHandler::defaultWheelEventHandler(Node* startNode, WheelEvent& wheelEvent) { - if (!startNode || !wheelEvent) + if (!startNode) return; - Element* stopElement = m_previousWheelScrolledElement.get(); - ScrollGranularity granularity = wheelGranularityToScrollGranularity(wheelEvent->deltaMode()); + Ref<Frame> 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 (<rdar://problem/14758615>). - 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<Frame> 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<Frame> 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<Range> 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<EventHandler>& 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<EventHandler>& 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<EventHandler>&) +void EventHandler::hoverTimerFired() { m_hoverTimer.stop(); ASSERT(m_frame.document()); - if (RenderView* renderer = m_frame.contentRenderer()) { + Ref<Frame> 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<Frame> protectedFrame(m_frame); RefPtr<FrameView> 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<HTMLInputElement>(*element)) + downcast<HTMLInputElement>(*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<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame.document()->defaultView()); + Ref<KeyboardEvent> 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<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame.document()->defaultView()); + Ref<KeyboardEvent> 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<AtomicString> Down("Down", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> Up("Up", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> Left("Left", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> 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<Frame> 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<Frame> 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<KeyboardEvent*>(underlyingEvent)->type() == eventNames().keypressEvent); + ASSERT(!is<KeyboardEvent>(underlyingEvent) || downcast<KeyboardEvent>(*underlyingEvent).type() == eventNames().keypressEvent); + + Ref<Frame> 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<TextEvent> event = TextEvent::create(m_frame.document()->domWindow(), text, inputType); + Ref<TextEvent> 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<Frame> 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<Frame> 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<Frame> 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<Frame> 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<PlatformTouchPoint>& 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 = 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<TouchList>& effectiveTouches(isTouchCancelEvent ? emptyList : touches); const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(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<TouchList> targetTouches(isTouchCancelEvent ? emptyList : touchesByTarget.get(touchEventTarget)); ASSERT(targetTouches); - RefPtr<TouchEvent> touchEvent = + Ref<TouchEvent> 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 <wtf/Deque.h> +#include <memory> #include <wtf/Forward.h> -#include <wtf/OwnPtr.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> #include <wtf/RefPtr.h> #include <wtf/WeakPtr.h> -#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 <wtf/HashMap.h> +#if PLATFORM(MAC) +OBJC_CLASS NSEvent; #endif -#if ENABLE(IOS_TOUCH_EVENTS) -#include <wtf/HashSet.h> -#include <wtf/Vector.h> +#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<Node>); - #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<Element>); // 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<Element>& eventTarget, RefPtr<ContainerNode>& scrollableContainer, WeakPtr<ScrollableArea>&, bool& isOverWidget); + void platformRecordWheelEvent(const PlatformWheelEvent&); + bool platformCompleteWheelEvent(const PlatformWheelEvent&, ContainerNode* scrollableContainer, const WeakPtr<ScrollableArea>&); + bool platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode* scrollableContainer); + void platformNotifyIfEndGesture(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&); + #if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) - typedef Vector<RefPtr<Touch>> TouchArray; - typedef HashMap<EventTarget*, TouchArray*> EventTargetTouchMap; - typedef HashSet<RefPtr<EventTarget>> EventTargetSet; + using TouchArray = Vector<RefPtr<Touch>>; + using EventTargetTouchMap = HashMap<EventTarget*, TouchArray*>; +#endif + +#if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) || ENABLE(MAC_GESTURE_EVENTS) + using EventTargetSet = HashSet<RefPtr<EventTarget>>; #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<PlatformEvent::Modifier> 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<Clipboard> createDraggingClipboard() const; -#endif // ENABLE(DRAG_SUPPORT) + Ref<DataTransfer> 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<Cursor> selectCursor(const HitTestResult&, bool shiftKey); + void updateCursor(FrameView&, const HitTestResult&, bool shiftKey); + + void hoverTimerFired(); - void hoverTimerFired(Timer<EventHandler>&); #if ENABLE(CURSOR_SUPPORT) - void cursorUpdateTimerFired(Timer<EventHandler>&); + 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<EventHandler>&); +#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<Widget>&, const WeakPtr<ScrollableArea>&, 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<EventHandler>&); + 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<Node> 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<EventHandler> m_hoverTimer; #if ENABLE(CURSOR_SUPPORT) - Timer<EventHandler> m_cursorUpdateTimer; + Timer m_cursorUpdateTimer; #endif - OwnPtr<AutoscrollController> m_autoscrollController; - bool m_mouseDownMayStartAutoscroll; - bool m_mouseDownWasInSubframe; +#if PLATFORM(MAC) + Timer m_pendingMomentumWheelEventsTimer; +#endif - Timer<EventHandler> m_fakeMouseMoveEventTimer; + std::unique_ptr<AutoscrollController> m_autoscrollController; + bool m_mouseDownMayStartAutoscroll { false }; + bool m_mouseDownWasInSubframe { false }; -#if ENABLE(SVG) - bool m_svgPan; - RefPtr<SVGElementInstance> m_instanceUnderMouse; - RefPtr<SVGElementInstance> m_lastInstanceUnderMouse; +#if !ENABLE(IOS_TOUCH_EVENTS) + Timer m_fakeMouseMoveEventTimer; #endif - RenderLayer* m_resizeLayer; + bool m_svgPan { false }; + + RenderLayer* m_resizeLayer { nullptr }; RefPtr<Element> m_capturingMouseEventsElement; - bool m_eventHandlerWillResetCapturingMouseEventsElement; + bool m_eventHandlerWillResetCapturingMouseEventsElement { false }; RefPtr<Element> m_elementUnderMouse; RefPtr<Element> m_lastElementUnderMouse; @@ -488,73 +524,73 @@ private: WeakPtr<Scrollbar> m_lastScrollbarUnderMouse; Cursor m_currentMouseCursor; - int m_clickCount; + int m_clickCount { 0 }; RefPtr<Node> 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<Frame> m_touchEventTargetSubframe; #endif #if ENABLE(DRAG_SUPPORT) RefPtr<Element> m_dragTarget; - bool m_shouldOnlyFireDragOverEvent; + bool m_shouldOnlyFireDragOverEvent { false }; #endif RefPtr<HTMLFrameSetElement> 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<FloatSize> m_recentWheelEventDeltas; - RefPtr<Element> m_latchedWheelEventElement; - bool m_inTrackingScrollGesturePhase; - bool m_widgetIsLatched; - - RefPtr<Element> 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<int, RefPtr<EventTarget>> TouchTargetMap; + using TouchTargetMap = HashMap<int, RefPtr<EventTarget>>; TouchTargetMap m_originatingTouchPointTargets; RefPtr<Document> 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<EventHandler> 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 <wtf/text/StringBuilder.h> 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> EventSource::create(ScriptExecutionContext& context, const String& url, const Dictionary& eventSourceInit, ExceptionCode& ec) +ExceptionOr<Ref<EventSource>> 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<EventSource> 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<EventSource>&) -{ - 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<unsigned> lineLength; + std::optional<unsigned> 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<unsigned> 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<MessageEvent> EventSource::createMessageEvent() +const char* EventSource::activeDOMObjectName() const { - RefPtr<MessageEvent> 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 <wtf/RefPtr.h> #include <wtf/Vector.h> namespace WebCore { -class Dictionary; class MessageEvent; -class ResourceResponse; class TextResourceDecoder; class ThreadableLoader; class EventSource final : public RefCounted<EventSource>, public EventTargetWithInlineData, private ThreadableLoaderClient, public ActiveDOMObject { WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<EventSource> create(ScriptExecutionContext&, const String& url, const Dictionary&, ExceptionCode&); + struct Init { + bool withCredentials; + }; + static ExceptionOr<Ref<EventSource>> 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<EventSource>::ref; - using RefCounted<EventSource>::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<EventSource>&); void abortConnectionAttempt(); void parseEventStream(); - void parseEventStreamLine(unsigned pos, int fieldLength, int lineLength); - PassRefPtr<MessageEvent> createMessageEvent(); + void parseEventStreamLine(unsigned position, std::optional<unsigned> 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<TextResourceDecoder> m_decoder; + Ref<TextResourceDecoder> m_decoder; RefPtr<ThreadableLoader> m_loader; - Timer<EventSource> m_connectTimer; - Vector<UChar> m_receiveBuf; - bool m_discardTrailingNewline; - bool m_requestInFlight; + Timer m_connectTimer; + Vector<UChar> m_receiveBuffer; + bool m_discardTrailingNewline { false }; + bool m_requestInFlight { false }; - String m_eventName; + AtomicString m_eventName; Vector<UChar> 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.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 <wtf/BitVector.h> -#include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> - -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<BitVector> 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 <limits> +#include <wtf/CurrentTime.h> #include <wtf/Ref.h> namespace WebCore { using namespace HTMLNames; -FocusNavigationScope::FocusNavigationScope(TreeScope* treeScope) - : m_rootTreeScope(treeScope) +static inline bool hasCustomFocusLogic(const Element& element) { - ASSERT(treeScope); + return is<HTMLElement>(element) && downcast<HTMLElement>(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<HTMLSlotElement>(element) && downcast<HTMLSlotElement>(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<Element>(node) && isFocusScopeOwner(downcast<Element>(node))) + return nullptr; + return node.firstChild(); +} + +Node* FocusNavigationScope::lastChildInScope(const Node& node) const +{ + if (is<Element>(node) && isFocusScopeOwner(downcast<Element>(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<ShadowRoot>(root)) + return downcast<ShadowRoot>(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<ShadowRoot>(currentNode)) + return FocusNavigationScope(downcast<ShadowRoot>(*currentNode)); + } + ASSERT(root); + return FocusNavigationScope(root->treeScope()); +} + +FocusNavigationScope FocusNavigationScope::scopeOwnedByScopeOwner(Element& element) +{ + ASSERT(element.shadowRoot() || is<HTMLSlotElement>(element)); + if (is<HTMLSlotElement>(element)) + return FocusNavigationScope(downcast<HTMLSlotElement>(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> 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<HTMLFrameOwnerElement>(element)) { + HTMLFrameOwnerElement& owner = downcast<HTMLFrameOwnerElement>(*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<KeyboardEvent> 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> element = findFocusableElementAcrossFocusScope(direction, FocusNavigationScope::focusNavigationScopeOf(currentNode ? currentNode : document), currentNode, event); + RefPtr<Element> 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<HTMLFrameOwnerElement>(*element) && (!is<HTMLPlugInElement>(*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<HTMLFrameOwnerElement>(*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<Element>(currentNode) || !isNonFocusableScopeOwner(downcast<Element>(*currentNode), event)); + + if (currentNode && direction == FocusDirectionForward && is<Element>(currentNode) && isFocusableScopeOwner(downcast<Element>(*currentNode), event)) { + if (Element* candidateInInnerScope = findFocusableElementWithinScope(direction, FocusNavigationScope::scopeOwnedByScopeOwner(downcast<Element>(*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<Element>(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) == tabIndex) + Element& element = downcast<Element>(*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<short>::max() + 1; + int winningTabIndex = std::numeric_limits<int>::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<Element>(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, event) && element.tabIndex() > tabIndex && element.tabIndex() < winningTabIndex) { - winner = &element; - winningTabIndex = element.tabIndex(); + Element& candidate = downcast<Element>(*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<Element>(*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<Element>(*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<KeyboardEvent> 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<KeyboardEvent> 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<Element>(*start)) + startTabIndex = shadowAdjustedTabIndex(downcast<Element>(*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<Element>(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) >= 0) + Element& element = downcast<Element>(*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<Element>(*start)) + startingTabIndex = shadowAdjustedTabIndex(downcast<Element>(*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<Element>(*node)) continue; - Element& element = toElement(*node); - if (shouldVisit(element, *event) && adjustedTabIndex(element, *event) >= 0) + Element& element = downcast<Element>(*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<short>::max(); - return previousElementWithLowerTabIndex(last, startingTabIndex, *event); + startingTabIndex = (start && startingTabIndex) ? startingTabIndex : std::numeric_limits<int>::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<HTMLInputElement>(*shadowAncestorNode) && !is<HTMLTextAreaElement>(*shadowAncestorNode)) return; } } } - - selection.clear(); + + oldFocusedFrame->selection().clear(); } -bool FocusController::setFocusedElement(Element* element, PassRefPtr<Frame> newFocusedFrame, FocusDirection direction) +bool FocusController::setFocusedElement(Element* element, Frame& newFocusedFrame, FocusDirection direction) { + Ref<Frame> protectedNewFocusedFrame = newFocusedFrame; RefPtr<Frame> oldFocusedFrame = focusedFrame(); - RefPtr<Document> oldDocument = oldFocusedFrame ? oldFocusedFrame->document() : 0; + RefPtr<Document> 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<Frame> 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<Document> 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<Element> protect(*element); @@ -630,18 +814,35 @@ bool FocusController::setFocusedElement(Element* element, PassRefPtr<Frame> 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<ScrollableArea*>::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<Element>(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<Document>(*container)) + downcast<Document>(*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<HTMLAreaElement>(*focusedElement)) { + HTMLAreaElement& area = downcast<HTMLAreaElement>(*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<Document>(container)) + downcast<Document>(*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 <wtf/Forward.h> -#include <wtf/Noncopyable.h> #include <wtf/RefPtr.h> 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<Frame>); + 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<Frame>, 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<Frame> 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 <hausmann@kde.org> * 2000 Stefan Schimanski <1Stein@gmx.de> * 2001 George Staikos <staikos@kde.org> - * 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 <ap@nypop.com> * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008 Eric Seidel <eric@webkit.org> @@ -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 <bindings/ScriptValue.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> #include <yarr/RegularExpression.h> -#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<ScriptController>(*this)) - , m_editor(Editor::create(*this)) - , m_selection(adoptPtr(new FrameSelection(this))) - , m_eventHandler(adoptPtr(new EventHandler(*this))) - , m_animationController(std::make_unique<AnimationController>(*this)) + , m_editor(std::make_unique<Editor>(*this)) + , m_selection(std::make_unique<FrameSelection>(this)) + , m_animationController(std::make_unique<CSSAnimationController>(*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<EventHandler>(*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> Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client) +Ref<Frame> 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<FrameView> view) +void Frame::setView(RefPtr<FrameView>&& 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<FrameView> 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<Document> newDocument) +void Frame::setDocument(RefPtr<Document>&& 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<Document> 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<Ref<Frame>> 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<JSC::Yarr::RegularExpression> createRegExpForLabels(const Vector<String>& labels) +static JSC::Yarr::RegularExpression createRegExpForLabels(const Vector<String>& 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<JSC::Yarr::RegularExpression> 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<JSC::Yarr::RegularExpression> 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<String>& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove) { - OwnPtr<JSC::Yarr::RegularExpression> 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<String>& 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<HTMLFormElement>(*n) || is<HTMLFormControlElement>(*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<HTMLTableCellElement>(n); + else if (is<HTMLTableRowElement>(*n) && startingTableCell) { + String result = searchForLabelsAboveCell(regExp, startingTableCell, resultDistance); if (!result.isEmpty()) { if (resultIsInCellAbove) *resultIsInCellAbove = true; @@ -421,11 +422,11 @@ String Frame::searchForLabelsBeforeElement(const Vector<String>& 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<String>& 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<String>& labels, const Strin replace(mutableStringToMatch, JSC::Yarr::RegularExpression("\\d", TextCaseSensitive), " "); mutableStringToMatch.replace('_', ' '); - OwnPtr<JSC::Yarr::RegularExpression> 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<String>& 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<String>& 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<Frame>*) +void Frame::overflowAutoScrollTimerFired() { if (!eventHandler().mousePressed() || checkOverflowScroll(PerformOverflowScroll) == OverflowScrollNone) { if (m_overflowAutoScrollTimer.isActive()) @@ -616,11 +617,13 @@ int Frame::checkOverflowScroll(OverflowScrollAction action) } } + Ref<Frame> 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<ScriptSourceCode> 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 <object> 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<RenderWidget>(object)) + return nullptr; + return downcast<RenderWidget>(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<FrameView>(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<Range> Frame::rangeForPoint(const IntPoint& framePoint) +RefPtr<Range> 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<Range> 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<Range> 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> 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<IntRect>& 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<SVGDocument>(*document) && !downcast<SVGDocument>(*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<Frame> 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 <hausmann@kde.org> * 2000-2001 Dirk Mueller <mueller@kde.org> * 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 <eric@webkit.org> * @@ -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 <wtf/RefCounted.h> +#include <memory> +#include <wtf/ThreadSafeRefCounted.h> #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<Frame>, public TiledBackingStoreClient { - public: - static PassRefPtr<Frame> 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<Frame> { +public: + WEBCORE_EXPORT static Ref<Frame> create(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*); + + void init(); #if PLATFORM(IOS) - // Creates <html><body style="..."></body></html> doing minimal amount of work. - void initWithSimpleHTMLDocument(const String& style, const URL&); + // Creates <html><body style="..."></body></html> doing minimal amount of work. + WEBCORE_EXPORT void initWithSimpleHTMLDocument(const String& style, const URL&); #endif - void setView(PassRefPtr<FrameView>); - 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<FrameView>&&); + 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<void*>(&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<Document>); + void setDocument(RefPtr<Document>&&); - 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<Range> rangeForPoint(const IntPoint& framePoint); + WEBCORE_EXPORT VisiblePosition visiblePositionForPoint(const IntPoint& framePoint) const; + Document* documentAtPoint(const IntPoint& windowPoint); + WEBCORE_EXPORT RefPtr<Range> rangeForPoint(const IntPoint& framePoint); - String searchForLabelsAboveCell(JSC::Yarr::RegularExpression*, HTMLTableCellElement*, size_t* resultDistanceFromStartOfCell); - String searchForLabelsBeforeElement(const Vector<String>& labels, Element*, size_t* resultDistance, bool* resultIsInCellAbove); - String matchLabelsAgainstElement(const Vector<String>& 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<String>& labels, Element*, size_t* resultDistance, bool* resultIsInCellAbove); + String matchLabelsAgainstElement(const Vector<String>& 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<FrameDestructionObserver*> m_destructionObservers; + bool isURLAllowed(const URL&) const; + WEBCORE_EXPORT bool isAlwaysOnLoggingAllowed() const; - MainFrame& m_mainFrame; - Page* m_page; - const RefPtr<Settings> m_settings; - mutable FrameTree m_treeNode; - mutable FrameLoader m_loader; - mutable NavigationScheduler m_navigationScheduler; +// ======== - HTMLFrameOwnerElement* m_ownerElement; - RefPtr<FrameView> m_view; - RefPtr<Document> m_doc; +protected: + Frame(Page&, HTMLFrameOwnerElement*, FrameLoaderClient&); + void setMainFrameWasDestroyed(); - const std::unique_ptr<ScriptController> m_script; - const OwnPtr<Editor> m_editor; - const OwnPtr<FrameSelection> m_selection; - const OwnPtr<EventHandler> m_eventHandler; - const std::unique_ptr<AnimationController> m_animationController; +private: + HashSet<FrameDestructionObserver*> 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<Frame>*); - void startOverflowAutoScroll(const IntPoint&); - int checkOverflowScroll(OverflowScrollAction); - - void setTimersPausedInternal(bool); - - Timer<Frame> 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<Settings> 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<FrameView> m_view; + RefPtr<Document> m_doc; - float m_pageZoomFactor; - float m_textZoomFactor; + const std::unique_ptr<ScriptController> m_script; + const std::unique_ptr<Editor> m_editor; + const std::unique_ptr<FrameSelection> m_selection; + const std::unique_ptr<CSSAnimationController> m_animationController; -#if ENABLE(ORIENTATION_EVENTS) - int m_orientation; +#if ENABLE(DATA_DETECTION) + RetainPtr<NSArray> 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<IntRect>& paintedArea) override final; - virtual IntRect tiledBackingStoreContentsRect() override final; - virtual IntRect tiledBackingStoreVisibleRect() override final; - virtual Color tiledBackingStoreBackgroundColor() const override final; - - OwnPtr<TiledBackingStore> 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<EventHandler> 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 { @@ -66,6 +69,12 @@ struct ScopedFramePaintingState { std::unique_ptr<ImageBuffer> snapshotFrameRect(Frame& frame, const IntRect& imageRect, SnapshotOptions options) { + Vector<FloatRect> clipRects; + return snapshotFrameRectWithClip(frame, imageRect, clipRects, options); +} + +std::unique_ptr<ImageBuffer> snapshotFrameRectWithClip(Frame& frame, const IntRect& imageRect, Vector<FloatRect>& clipRects, SnapshotOptions options) +{ if (!frame.page()) return nullptr; @@ -86,14 +95,28 @@ std::unique_ptr<ImageBuffer> 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<ImageBuffer> 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<ImageBuffer> 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<ImageBuffer> snapshotFrameRect(Frame& frame, const IntRect& imag std::unique_ptr<ImageBuffer> 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<ImageBuffer> snapshotNode(Frame& frame, Node& node) @@ -119,7 +150,7 @@ std::unique_ptr<ImageBuffer> 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 <memory> +#include <wtf/Vector.h> 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<ImageBuffer> snapshotFrameRect(Frame&, const IntRect&, SnapshotOptions = SnapshotOptionsNone); +WEBCORE_EXPORT std::unique_ptr<ImageBuffer> snapshotFrameRect(Frame&, const IntRect&, SnapshotOptions = SnapshotOptionsNone); +std::unique_ptr<ImageBuffer> snapshotFrameRectWithClip(Frame&, const IntRect&, Vector<FloatRect>& clipRects, SnapshotOptions = SnapshotOptionsNone); std::unique_ptr<ImageBuffer> snapshotNode(Frame&, Node&); -std::unique_ptr<ImageBuffer> snapshotSelection(Frame&, SnapshotOptions = SnapshotOptionsNone); +WEBCORE_EXPORT std::unique_ptr<ImageBuffer> 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<Frame> 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<Frame> 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<Frame> 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<Frame>& newLocationForNext = m_firstChild == &child ? m_firstChild : child.tree().m_previousSibling->tree().m_nextSibling; - RefPtr<Frame>& 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: + // <!--framePath //<!--frame1-->/<!--frame2-->--> const char framePathPrefix[] = "<!--framePath "; const int framePathPrefixLength = 14; @@ -159,7 +140,11 @@ AtomicString FrameTree::uniqueChildName(const AtomicString& requestedName) const for (int i = chain.size() - 1; i >= 0; --i) { frame = chain[i]; name.append('/'); - name.append(frame->tree().uniqueName()); + if (frame->tree().parent()) { + name.appendLiteral("<!--frame"); + name.appendNumber(frame->tree().indexInParent()); + name.appendLiteral("-->"); + } } name.appendLiteral("/<!--frame"); @@ -356,6 +341,68 @@ Frame* FrameTree::traverseNext(const Frame* stayWithin) const return nullptr; } +Frame* FrameTree::firstRenderedChild() const +{ + Frame* child = firstChild(); + if (!child) + return nullptr; + + if (child->ownerRenderer()) + return child; + + while ((child = child->tree().nextSibling())) { + if (child->ownerRenderer()) + return child; + } + + return nullptr; +} + +Frame* FrameTree::nextRenderedSibling() const +{ + Frame* sibling = &m_thisFrame; + + while ((sibling = sibling->tree().nextSibling())) { + if (sibling->ownerRenderer()) + return sibling; + } + + return nullptr; +} + +Frame* FrameTree::traverseNextRendered(const Frame* stayWithin) const +{ + Frame* child = firstRenderedChild(); + if (child) { + ASSERT(!stayWithin || child->tree().isDescendantOf(stayWithin)); + return child; + } + + if (&m_thisFrame == stayWithin) + return nullptr; + + Frame* sibling = nextRenderedSibling(); + if (sibling) { + ASSERT(!stayWithin || sibling->tree().isDescendantOf(stayWithin)); + return sibling; + } + + Frame* frame = &m_thisFrame; + while (!sibling && (!stayWithin || frame->tree().parent() != stayWithin)) { + frame = frame->tree().parent(); + if (!frame) + return nullptr; + sibling = frame->tree().nextRenderedSibling(); + } + + if (frame) { + ASSERT(!stayWithin || !sibling || sibling->tree().isDescendantOf(stayWithin)); + return sibling; + } + + return nullptr; +} + Frame* FrameTree::traverseNextWithWrap(bool wrap) const { if (Frame* result = traverseNext()) @@ -384,6 +431,25 @@ Frame* FrameTree::traversePreviousWithWrap(bool wrap) const return nullptr; } +Frame* FrameTree::traverseNextInPostOrderWithWrap(bool wrap) const +{ + if (m_nextSibling) + return m_nextSibling->tree().deepFirstChild(); + if (m_parent) + return m_parent; + if (wrap) + return deepFirstChild(); + return nullptr; +} + +Frame* FrameTree::deepFirstChild() const +{ + Frame* result = &m_thisFrame; + while (auto* next = result->tree().firstChild()) + result = next; + return result; +} + Frame* FrameTree::deepLastChild() const { Frame* result = &m_thisFrame; @@ -424,11 +490,15 @@ static void printFrames(const WebCore::Frame& frame, const WebCore::Frame* targe printIndent(indent); printf(" ownerElement=%p\n", frame.ownerElement()); printIndent(indent); - printf(" frameView=%p\n", view); + printf(" frameView=%p (needs layout %d)\n", view, view ? view->needsLayout() : false); + printIndent(indent); + printf(" renderView=%p\n", view ? view->renderView() : nullptr); + printIndent(indent); + printf(" ownerRenderer=%p\n", frame.ownerRenderer()); printIndent(indent); - printf(" document=%p\n", frame.document()); + printf(" document=%p (needs style recalc %d)\n", frame.document(), frame.document() ? frame.document()->childNeedsStyleRecalc() : false); printIndent(indent); - printf(" uri=%s\n\n", frame.document()->documentURI().utf8().data()); + printf(" uri=%s\n", frame.document()->documentURI().utf8().data()); for (WebCore::Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) printFrames(*child, targetFrame, indent + 1); diff --git a/Source/WebCore/page/FrameTree.h b/Source/WebCore/page/FrameTree.h index c6b5c0191..052f3609e 100644 --- a/Source/WebCore/page/FrameTree.h +++ b/Source/WebCore/page/FrameTree.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,94 +17,100 @@ * Boston, MA 02110-1301, USA. */ -#ifndef FrameTree_h -#define FrameTree_h +#pragma once #include <wtf/text/AtomicString.h> namespace WebCore { - class Frame; - class TreeScope; - - class FrameTree { - WTF_MAKE_NONCOPYABLE(FrameTree); - public: - const static unsigned invalidCount = static_cast<unsigned>(-1); - - FrameTree(Frame& thisFrame, Frame* parentFrame) - : m_thisFrame(thisFrame) - , m_parent(parentFrame) - , m_previousSibling(0) - , m_lastChild(0) - , m_scopedChildCount(invalidCount) - { - } - - ~FrameTree(); - - const AtomicString& name() const { return m_name; } - const AtomicString& uniqueName() const { return m_uniqueName; } - void setName(const AtomicString&); - void clearName(); - Frame* parent() const; - void setParent(Frame* parent) { m_parent = parent; } - - Frame* nextSibling() const { return m_nextSibling.get(); } - Frame* previousSibling() const { return m_previousSibling; } - Frame* firstChild() const { return m_firstChild.get(); } - Frame* lastChild() const { return m_lastChild; } - - bool isDescendantOf(const Frame* ancestor) const; - Frame* traverseNext(const Frame* stayWithin = 0) const; - Frame* traverseNextWithWrap(bool) const; - Frame* traversePreviousWithWrap(bool) const; - - void appendChild(PassRefPtr<Frame>); - bool transferChild(PassRefPtr<Frame>); - void detachFromParent() { m_parent = 0; } - void removeChild(Frame*); - - Frame* child(unsigned index) const; - Frame* child(const AtomicString& name) const; - Frame* find(const AtomicString& name) const; - unsigned childCount() const; - - AtomicString uniqueChildName(const AtomicString& requestedName) const; - - Frame& top() const; - - Frame* scopedChild(unsigned index) const; - Frame* scopedChild(const AtomicString& name) const; - unsigned scopedChildCount() const; - - private: - Frame* deepLastChild() const; - void actuallyAppendChild(PassRefPtr<Frame>); - - bool scopedBy(TreeScope*) const; - Frame* scopedChild(unsigned index, TreeScope*) const; - Frame* scopedChild(const AtomicString& name, TreeScope*) const; - unsigned scopedChildCount(TreeScope*) const; - - Frame& m_thisFrame; - - Frame* m_parent; - AtomicString m_name; // The actual frame name (may be empty). - AtomicString m_uniqueName; - - RefPtr<Frame> m_nextSibling; - Frame* m_previousSibling; - RefPtr<Frame> m_firstChild; - Frame* m_lastChild; - mutable unsigned m_scopedChildCount; - }; +class Frame; +class TreeScope; + +class FrameTree { + WTF_MAKE_NONCOPYABLE(FrameTree); +public: + const static unsigned invalidCount = static_cast<unsigned>(-1); + + FrameTree(Frame& thisFrame, Frame* parentFrame) + : m_thisFrame(thisFrame) + , m_parent(parentFrame) + , m_previousSibling(nullptr) + , m_lastChild(nullptr) + , m_scopedChildCount(invalidCount) + { + } + + ~FrameTree(); + + const AtomicString& name() const { return m_name; } + const AtomicString& uniqueName() const { return m_uniqueName; } + WEBCORE_EXPORT void setName(const AtomicString&); + WEBCORE_EXPORT void clearName(); + WEBCORE_EXPORT Frame* parent() const; + void setParent(Frame* parent) { m_parent = parent; } + + Frame* nextSibling() const { return m_nextSibling.get(); } + Frame* previousSibling() const { return m_previousSibling; } + Frame* firstChild() const { return m_firstChild.get(); } + Frame* lastChild() const { return m_lastChild; } + + Frame* firstRenderedChild() const; + Frame* nextRenderedSibling() const; + + WEBCORE_EXPORT bool isDescendantOf(const Frame* ancestor) const; + + WEBCORE_EXPORT Frame* traverseNext(const Frame* stayWithin = nullptr) const; + // Rendered means being the main frame or having an ownerRenderer. It may not have been parented in the Widget tree yet (see WidgetHierarchyUpdatesSuspensionScope). + WEBCORE_EXPORT Frame* traverseNextRendered(const Frame* stayWithin = nullptr) const; + WEBCORE_EXPORT Frame* traverseNextWithWrap(bool) const; + WEBCORE_EXPORT Frame* traversePreviousWithWrap(bool) const; + + Frame* traverseNextInPostOrderWithWrap(bool) const; + + WEBCORE_EXPORT void appendChild(Frame&); + void detachFromParent() { m_parent = nullptr; } + void removeChild(Frame&); + + Frame* child(unsigned index) const; + Frame* child(const AtomicString& name) const; + WEBCORE_EXPORT Frame* find(const AtomicString& name) const; + WEBCORE_EXPORT unsigned childCount() const; + + AtomicString uniqueChildName(const AtomicString& requestedName) const; + + WEBCORE_EXPORT Frame& top() const; + + WEBCORE_EXPORT Frame* scopedChild(unsigned index) const; + WEBCORE_EXPORT Frame* scopedChild(const AtomicString& name) const; + unsigned scopedChildCount() const; + + unsigned indexInParent() const; + +private: + Frame* deepFirstChild() const; + Frame* deepLastChild() const; + + bool scopedBy(TreeScope*) const; + Frame* scopedChild(unsigned index, TreeScope*) const; + Frame* scopedChild(const AtomicString& name, TreeScope*) const; + unsigned scopedChildCount(TreeScope*) const; + + Frame& m_thisFrame; + + Frame* m_parent; + AtomicString m_name; // The actual frame name (may be empty). + AtomicString m_uniqueName; + + RefPtr<Frame> m_nextSibling; + Frame* m_previousSibling; + RefPtr<Frame> m_firstChild; + Frame* m_lastChild; + mutable unsigned m_scopedChildCount; +}; } // namespace WebCore -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. -void showFrameTree(const WebCore::Frame*); +#if ENABLE(TREE_DEBUGGING) +// Outside the WebCore namespace for ease of invocation from the debugger. +WEBCORE_EXPORT void showFrameTree(const WebCore::Frame*); #endif - -#endif // FrameTree_h diff --git a/Source/WebCore/page/FrameView.cpp b/Source/WebCore/page/FrameView.cpp index 804c0420f..725c62f32 100644 --- a/Source/WebCore/page/FrameView.cpp +++ b/Source/WebCore/page/FrameView.cpp @@ -3,7 +3,7 @@ * 1999 Lars Knoll <knoll@kde.org> * 1999 Antti Koivisto <koivisto@kde.org> * 2000 Dirk Mueller <mueller@kde.org> - * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * (C) 2006 Graham Dennis (graham.dennis@gmail.com) * (C) 2006 Alexey Proskuryakov (ap@nypop.com) * Copyright (C) 2009 Google Inc. All rights reserved. @@ -28,40 +28,55 @@ #include "FrameView.h" #include "AXObjectCache.h" -#include "AnimationController.h" #include "BackForwardController.h" +#include "CSSAnimationController.h" #include "CachedImage.h" #include "CachedResourceLoader.h" #include "Chrome.h" #include "ChromeClient.h" #include "DOMWindow.h" +#include "DebugPageOverlays.h" #include "DocumentMarkerController.h" #include "EventHandler.h" +#include "EventNames.h" #include "FloatRect.h" #include "FocusController.h" -#include "FontCache.h" -#include "FontLoader.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "FrameSelection.h" #include "FrameTree.h" #include "GraphicsContext.h" +#include "HTMLBodyElement.h" #include "HTMLDocument.h" +#include "HTMLEmbedElement.h" #include "HTMLFrameElement.h" #include "HTMLFrameSetElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLIFrameElement.h" #include "HTMLNames.h" +#include "HTMLObjectElement.h" #include "HTMLPlugInImageElement.h" +#include "ImageDocument.h" #include "InspectorClient.h" #include "InspectorController.h" #include "InspectorInstrumentation.h" +#include "Logging.h" #include "MainFrame.h" +#include "MemoryCache.h" +#include "MemoryPressureHandler.h" #include "OverflowEvent.h" +#include "Page.h" +#include "PageCache.h" +#include "PageOverlayController.h" #include "ProgressTracker.h" #include "RenderEmbeddedObject.h" #include "RenderFullScreen.h" #include "RenderIFrame.h" +#include "RenderInline.h" #include "RenderLayer.h" #include "RenderLayerBacking.h" +#include "RenderLayerCompositor.h" +#include "RenderSVGRoot.h" #include "RenderScrollbar.h" #include "RenderScrollbarPart.h" #include "RenderStyle.h" @@ -69,47 +84,35 @@ #include "RenderTheme.h" #include "RenderView.h" #include "RenderWidget.h" +#include "SVGDocument.h" +#include "SVGSVGElement.h" +#include "ScriptedAnimationController.h" #include "ScrollAnimator.h" #include "ScrollingCoordinator.h" #include "Settings.h" #include "StyleResolver.h" +#include "StyleScope.h" #include "TextResourceDecoder.h" #include "TextStream.h" +#include "TiledBacking.h" +#include "WheelEventTestTrigger.h" #include <wtf/CurrentTime.h> #include <wtf/Ref.h> -#include <wtf/TemporaryChange.h> - -#if USE(ACCELERATED_COMPOSITING) -#include "RenderLayerCompositor.h" -#include "TiledBacking.h" -#endif +#include <wtf/SetForScope.h> +#include <wtf/SystemTracing.h> -#if ENABLE(SVG) -#include "RenderSVGRoot.h" -#include "SVGDocument.h" -#include "SVGSVGElement.h" -#endif - -#if USE(TILED_BACKING_STORE) +#if USE(COORDINATED_GRAPHICS) #include "TiledBackingStore.h" #endif -#if ENABLE(TEXT_AUTOSIZING) -#include "TextAutosizer.h" +#if ENABLE(CSS_SCROLL_SNAP) +#include "AxisScrollSnapOffsets.h" #endif #if PLATFORM(IOS) #include "DocumentLoader.h" -#include "Logging.h" -#include "MemoryCache.h" -#include "MemoryPressureHandler.h" -#include "SystemMemory.h" -#include "TileCache.h" -#endif - -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -#include "HTMLMediaElement.h" +#include "LegacyTileCache.h" #endif namespace WebCore { @@ -128,7 +131,7 @@ static RenderLayer::UpdateLayerPositionsFlags updateLayerPositionFlags(RenderLay flags &= ~RenderLayer::CheckForRepaint; flags |= RenderLayer::NeedsFullRepaintInBacking; } - if (isRelayoutingSubtree && layer->isPaginated()) + if (isRelayoutingSubtree && layer->enclosingPaginationLayer(RenderLayer::IncludeCompositedPaginatedLayers)) flags |= RenderLayer::UpdatePagination; return flags; } @@ -160,33 +163,107 @@ Pagination::Mode paginationModeForRenderStyle(const RenderStyle& style) return Pagination::BottomToTopPaginated; } +class SubtreeLayoutStateMaintainer { +public: + SubtreeLayoutStateMaintainer(RenderElement* subtreeLayoutRoot) + : m_layoutRoot(subtreeLayoutRoot) + { + if (m_layoutRoot) { + RenderView& view = m_layoutRoot->view(); + view.pushLayoutState(*m_layoutRoot); + if (shouldDisableLayoutStateForSubtree()) { + view.disableLayoutState(); + m_didDisableLayoutState = true; + } + } + } + + ~SubtreeLayoutStateMaintainer() + { + if (m_layoutRoot) { + RenderView& view = m_layoutRoot->view(); + view.popLayoutState(*m_layoutRoot); + if (m_didDisableLayoutState) + view.enableLayoutState(); + } + } + + bool shouldDisableLayoutStateForSubtree() + { + for (auto* renderer = m_layoutRoot; renderer; renderer = renderer->container()) { + if (renderer->hasTransform() || renderer->hasReflection()) + return true; + } + return false; + } + +private: + RenderElement* m_layoutRoot { nullptr }; + bool m_didDisableLayoutState { false }; +}; + +#ifndef NDEBUG +class RenderTreeNeedsLayoutChecker { +public : + RenderTreeNeedsLayoutChecker(const RenderElement& layoutRoot) + : m_layoutRoot(layoutRoot) + { + } + + ~RenderTreeNeedsLayoutChecker() + { + auto reportNeedsLayoutError = [] (const RenderObject& renderer) { + WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, "post-layout: dirty renderer(s)"); + renderer.showRenderTreeForThis(); + ASSERT_NOT_REACHED(); + }; + + if (m_layoutRoot.needsLayout()) { + reportNeedsLayoutError(m_layoutRoot); + return; + } + + for (auto* descendant = m_layoutRoot.firstChild(); descendant; descendant = descendant->nextInPreOrder(&m_layoutRoot)) { + if (!descendant->needsLayout()) + continue; + + reportNeedsLayoutError(*descendant); + return; + } + } + +private: + const RenderElement& m_layoutRoot; +}; +#endif + FrameView::FrameView(Frame& frame) - : m_frame(&frame) + : m_frame(frame) , m_canHaveScrollbars(true) - , m_layoutTimer(this, &FrameView::layoutTimerFired) - , m_layoutRoot(0) + , m_layoutTimer(*this, &FrameView::layoutTimerFired) , m_layoutPhase(OutsideLayout) , m_inSynchronousPostLayout(false) - , m_postLayoutTasksTimer(this, &FrameView::postLayoutTimerFired) - , m_updateEmbeddedObjectsTimer(this, &FrameView::updateEmbeddedObjectsTimerFired) + , m_postLayoutTasksTimer(*this, &FrameView::performPostLayoutTasks) + , m_updateEmbeddedObjectsTimer(*this, &FrameView::updateEmbeddedObjectsTimerFired) , m_isTransparent(false) , m_baseBackgroundColor(Color::white) , m_mediaType("screen") , m_overflowStatusDirty(true) - , m_viewportRenderer(0) , m_wasScrolledByUser(false) , m_inProgrammaticScroll(false) , m_safeToPropagateScrollToParent(true) + , m_delayedScrollEventTimer(*this, &FrameView::sendScrollEvent) , m_isTrackingRepaints(false) , m_shouldUpdateWhileOffscreen(true) - , m_exposedRect(FloatRect::infiniteRect()) - , m_deferSetNeedsLayouts(0) + , m_deferSetNeedsLayoutCount(0) , m_setNeedsLayoutWasDeferred(false) , m_speculativeTilingEnabled(false) - , m_speculativeTilingEnableTimer(this, &FrameView::speculativeTilingEnableTimerFired) + , m_speculativeTilingEnableTimer(*this, &FrameView::speculativeTilingEnableTimerFired) #if PLATFORM(IOS) , m_useCustomFixedPositionLayoutRect(false) + , m_useCustomSizeForResizeEvent(false) #endif + , m_hasOverrideViewportSize(false) , m_shouldAutoSize(false) , m_inAutoSize(false) , m_didRunAutosize(false) @@ -195,29 +272,40 @@ FrameView::FrameView(Frame& frame) , m_footerHeight(0) , m_milestonesPendingPaint(0) , m_visualUpdatesAllowedByClient(true) + , m_hasFlippedBlockRenderers(false) , m_scrollPinningBehavior(DoNotPin) { init(); - if (frame.isMainFrame()) { - ScrollableArea::setVerticalScrollElasticity(ScrollElasticityAllowed); - ScrollableArea::setHorizontalScrollElasticity(ScrollElasticityAutomatic); +#if ENABLE(RUBBER_BANDING) + ScrollElasticity verticalElasticity = ScrollElasticityNone; + ScrollElasticity horizontalElasticity = ScrollElasticityNone; + if (m_frame->isMainFrame()) { + verticalElasticity = m_frame->page() ? m_frame->page()->verticalScrollElasticity() : ScrollElasticityAllowed; + horizontalElasticity = m_frame->page() ? m_frame->page()->horizontalScrollElasticity() : ScrollElasticityAllowed; + } else if (m_frame->settings().rubberBandingForSubScrollableRegionsEnabled()) { + verticalElasticity = ScrollElasticityAutomatic; + horizontalElasticity = ScrollElasticityAutomatic; } + + ScrollableArea::setVerticalScrollElasticity(verticalElasticity); + ScrollableArea::setHorizontalScrollElasticity(horizontalElasticity); +#endif } -PassRefPtr<FrameView> FrameView::create(Frame& frame) +Ref<FrameView> FrameView::create(Frame& frame) { - RefPtr<FrameView> view = adoptRef(new FrameView(frame)); + Ref<FrameView> view = adoptRef(*new FrameView(frame)); view->show(); - return view.release(); + return view; } -PassRefPtr<FrameView> FrameView::create(Frame& frame, const IntSize& initialSize) +Ref<FrameView> FrameView::create(Frame& frame, const IntSize& initialSize) { - RefPtr<FrameView> view = adoptRef(new FrameView(frame)); + Ref<FrameView> view = adoptRef(*new FrameView(frame)); view->Widget::setFrameRect(IntRect(view->location(), initialSize)); view->show(); - return view.release(); + return view; } FrameView::~FrameView() @@ -245,10 +333,8 @@ void FrameView::reset() m_cannotBlitToWindow = false; m_isOverlapped = false; m_contentIsOpaque = false; - m_borderX = 30; - m_borderY = 30; m_layoutTimer.stop(); - m_layoutRoot = 0; + m_layoutRoot = nullptr; m_delayedLayout = false; m_needsFullRepaint = true; m_layoutSchedulingEnabled = true; @@ -262,6 +348,7 @@ void FrameView::reset() m_firstLayoutCallbackPending = false; m_wasScrolledByUser = false; m_safeToPropagateScrollToParent = true; + m_delayedScrollEventTimer.stop(); m_lastViewportSize = IntSize(); m_lastZoomFactor = 1.0f; m_isTrackingRepaints = false; @@ -273,13 +360,17 @@ void FrameView::reset() m_visuallyNonEmptyPixelCount = 0; m_isVisuallyNonEmpty = false; m_firstVisuallyNonEmptyLayoutCallbackPending = true; - m_maintainScrollPositionAnchor = 0; + m_needsDeferredScrollbarsUpdate = false; + m_maintainScrollPositionAnchor = nullptr; } void FrameView::removeFromAXObjectCache() { - if (AXObjectCache* cache = axObjectCache()) + if (AXObjectCache* cache = axObjectCache()) { + if (HTMLFrameOwnerElement* owner = frame().ownerElement()) + cache->childrenChanged(owner->renderer()); cache->remove(this); + } } void FrameView::resetScrollbars() @@ -312,12 +403,12 @@ void FrameView::init() // Propagate the marginwidth/height and scrolling modes to the view. Element* ownerElement = frame().ownerElement(); - if (ownerElement && (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))) { - HTMLFrameElementBase* frameElt = toHTMLFrameElementBase(ownerElement); - if (frameElt->scrollingMode() == ScrollbarAlwaysOff) + if (is<HTMLFrameElementBase>(ownerElement)) { + HTMLFrameElementBase& frameElement = downcast<HTMLFrameElementBase>(*ownerElement); + if (frameElement.scrollingMode() == ScrollbarAlwaysOff) setCanHaveScrollbars(false); - LayoutUnit marginWidth = frameElt->marginWidth(); - LayoutUnit marginHeight = frameElt->marginHeight(); + LayoutUnit marginWidth = frameElement.marginWidth(); + LayoutUnit marginHeight = frameElement.marginHeight(); if (marginWidth != -1) setMarginWidth(marginWidth); if (marginHeight != -1) @@ -338,7 +429,7 @@ void FrameView::prepareForDetach() if (frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = frame().page()->scrollingCoordinator()) - scrollingCoordinator->willDestroyScrollableArea(this); + scrollingCoordinator->willDestroyScrollableArea(*this); } } @@ -358,7 +449,14 @@ void FrameView::detachCustomScrollbars() void FrameView::recalculateScrollbarOverlayStyle() { ScrollbarOverlayStyle oldOverlayStyle = scrollbarOverlayStyle(); - ScrollbarOverlayStyle overlayStyle = ScrollbarOverlayStyleDefault; + std::optional<ScrollbarOverlayStyle> clientOverlayStyle = frame().page() ? frame().page()->chrome().client().preferredScrollbarOverlayStyle() : ScrollbarOverlayStyleDefault; + if (clientOverlayStyle) { + if (clientOverlayStyle.value() != oldOverlayStyle) + setScrollbarOverlayStyle(clientOverlayStyle.value()); + return; + } + + ScrollbarOverlayStyle computedOverlayStyle = ScrollbarOverlayStyleDefault; Color backgroundColor = documentBackgroundColor(); if (backgroundColor.isValid()) { @@ -367,12 +465,12 @@ void FrameView::recalculateScrollbarOverlayStyle() // heuristic. double hue, saturation, lightness; backgroundColor.getHSL(hue, saturation, lightness); - if (lightness <= .5 && backgroundColor.alpha() > 0) - overlayStyle = ScrollbarOverlayStyleLight; + if (lightness <= .5 && backgroundColor.isVisible()) + computedOverlayStyle = ScrollbarOverlayStyleLight; } - if (oldOverlayStyle != overlayStyle) - setScrollbarOverlayStyle(overlayStyle); + if (oldOverlayStyle != computedOverlayStyle) + setScrollbarOverlayStyle(computedOverlayStyle); } void FrameView::clear() @@ -386,11 +484,20 @@ void FrameView::clear() #if PLATFORM(IOS) // To avoid flashes of white, disable tile updates immediately when view is cleared at the beginning of a page load. // Tiling will be re-enabled from UIKit via [WAKWindow setTilingMode:] when we have content to draw. - if (TileCache* tileCache = this->tileCache()) - tileCache->setTilingMode(TileCache::Disabled); + if (LegacyTileCache* tileCache = legacyTileCache()) + tileCache->setTilingMode(LegacyTileCache::Disabled); #endif } +#if PLATFORM(IOS) +void FrameView::didReplaceMultipartContent() +{ + // Re-enable tile updates that were disabled in clear(). + if (LegacyTileCache* tileCache = legacyTileCache()) + tileCache->setTilingMode(LegacyTileCache::Normal); +} +#endif + bool FrameView::didFirstLayout() const { return !m_firstLayout; @@ -399,63 +506,50 @@ bool FrameView::didFirstLayout() const void FrameView::invalidateRect(const IntRect& rect) { if (!parent()) { - if (HostWindow* window = hostWindow()) - window->invalidateContentsAndRootView(rect, false /*immediate*/); + if (auto* page = frame().page()) + page->chrome().invalidateContentsAndRootView(rect); return; } - RenderWidget* renderer = frame().ownerRenderer(); + auto* renderer = frame().ownerRenderer(); if (!renderer) return; IntRect repaintRect = rect; - repaintRect.move(renderer->borderLeft() + renderer->paddingLeft(), - renderer->borderTop() + renderer->paddingTop()); + repaintRect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); renderer->repaintRectangle(repaintRect); } void FrameView::setFrameRect(const IntRect& newRect) { + Ref<FrameView> protectedThis(*this); IntRect oldRect = frameRect(); if (newRect == oldRect) return; -#if ENABLE(TEXT_AUTOSIZING) - // Autosized font sizes depend on the width of the viewing area. - if (newRect.width() != oldRect.width()) { - Page* page = frame().page(); - if (frame().isMainFrame() && page->settings().textAutosizingEnabled()) { - for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) - frame().document()->textAutosizer()->recalculateMultipliers(); - } - } -#endif - ScrollView::setFrameRect(newRect); updateScrollableAreaSet(); -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) { if (renderView->usesCompositing()) renderView->compositor().frameViewDidChangeSize(); } -#endif - if (!frameFlatteningEnabled()) - sendResizeEventIfNeeded(); + if (frame().isMainFrame()) + frame().mainFrame().pageOverlayController().didChangeViewSize(); + + viewportContentsChanged(); } -#if ENABLE(REQUEST_ANIMATION_FRAME) bool FrameView::scheduleAnimation() { - if (HostWindow* window = hostWindow()) { - window->scheduleAnimation(); - return true; - } - return false; + auto* page = frame().page(); + if (!page) + return false; + page->chrome().scheduleAnimation(); + return true; } -#endif void FrameView::setMarginWidth(LayoutUnit w) { @@ -512,42 +606,67 @@ void FrameView::updateCanHaveScrollbars() setCanHaveScrollbars(true); } -PassRefPtr<Scrollbar> FrameView::createScrollbar(ScrollbarOrientation orientation) +Ref<Scrollbar> FrameView::createScrollbar(ScrollbarOrientation orientation) { - if (!frame().settings().allowCustomScrollbarInMainFrame() && frame().isMainFrame()) - return ScrollView::createScrollbar(orientation); - // FIXME: We need to update the scrollbar dynamically as documents change (or as doc elements and bodies get discovered that have custom styles). Document* doc = frame().document(); // Try the <body> element first as a scrollbar source. - Element* body = doc ? doc->body() : 0; + HTMLElement* body = doc ? doc->bodyOrFrameset() : nullptr; if (body && body->renderer() && body->renderer()->style().hasPseudoStyle(SCROLLBAR)) - return RenderScrollbar::createCustomScrollbar(this, orientation, body); + return RenderScrollbar::createCustomScrollbar(*this, orientation, body); // If the <body> didn't have a custom style, then the root element might. - Element* docElement = doc ? doc->documentElement() : 0; + Element* docElement = doc ? doc->documentElement() : nullptr; if (docElement && docElement->renderer() && docElement->renderer()->style().hasPseudoStyle(SCROLLBAR)) - return RenderScrollbar::createCustomScrollbar(this, orientation, docElement); + return RenderScrollbar::createCustomScrollbar(*this, orientation, docElement); // If we have an owning iframe/frame element, then it can set the custom scrollbar also. RenderWidget* frameRenderer = frame().ownerRenderer(); if (frameRenderer && frameRenderer->style().hasPseudoStyle(SCROLLBAR)) - return RenderScrollbar::createCustomScrollbar(this, orientation, 0, &frame()); + return RenderScrollbar::createCustomScrollbar(*this, orientation, nullptr, &frame()); // Nobody set a custom style, so we just use a native scrollbar. return ScrollView::createScrollbar(orientation); } +void FrameView::didRestoreFromPageCache() +{ + // When restoring from page cache, the main frame stays in place while subframes get swapped in. + // We update the scrollable area set to ensure that scrolling data structures get invalidated. + updateScrollableAreaSet(); +} + +void FrameView::willDestroyRenderTree() +{ + detachCustomScrollbars(); + m_layoutRoot = nullptr; +} + +void FrameView::didDestroyRenderTree() +{ + ASSERT(!m_layoutRoot); + ASSERT(m_widgetsInRenderTree.isEmpty()); + + // If the render tree is destroyed below FrameView::updateEmbeddedObjects(), there will still be a null sentinel in the set. + // Everything else should have removed itself as the tree was felled. + ASSERT(!m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty() || (m_embeddedObjectsToUpdate->size() == 1 && m_embeddedObjectsToUpdate->first() == nullptr)); + + ASSERT(!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmpty()); + ASSERT(!m_slowRepaintObjects || m_slowRepaintObjects->isEmpty()); + + ASSERT(!frame().animation().hasAnimations()); +} + void FrameView::setContentsSize(const IntSize& size) { if (size == contentsSize()) return; - m_deferSetNeedsLayouts++; + m_deferSetNeedsLayoutCount++; ScrollView::setContentsSize(size); - ScrollView::contentsResized(); + contentsResized(); Page* page = frame().page(); if (!page) @@ -555,12 +674,17 @@ void FrameView::setContentsSize(const IntSize& size) updateScrollableAreaSet(); - page->chrome().contentsSizeChanged(&frame(), size); // Notify only. + page->chrome().contentsSizeChanged(frame(), size); // Notify only. - ASSERT(m_deferSetNeedsLayouts); - m_deferSetNeedsLayouts--; + if (frame().isMainFrame()) { + frame().mainFrame().pageOverlayController().didChangeDocumentSize(); + PageCache::singleton().markPagesForContentsSizeChanged(*page); + } + + ASSERT(m_deferSetNeedsLayoutCount); + m_deferSetNeedsLayoutCount--; - if (!m_deferSetNeedsLayouts) + if (!m_deferSetNeedsLayoutCount) m_setNeedsLayoutWasDeferred = false; // FIXME: Find a way to make the deferred layout actually happen. } @@ -575,11 +699,13 @@ void FrameView::adjustViewSize() const IntRect rect = renderView->documentRect(); const IntSize& size = rect.size(); ScrollView::setScrollOrigin(IntPoint(-rect.x(), -rect.y()), !frame().document()->printing(), size == contentsSize()); - + + LOG_WITH_STREAM(Layout, stream << "FrameView " << this << " adjustViewSize: unscaled document rect changed to " << renderView->unscaledDocumentRect() << " (scaled to " << size << ")"); + setContentsSize(size); } -void FrameView::applyOverflowToViewport(RenderElement* o, ScrollbarMode& hMode, ScrollbarMode& vMode) +void FrameView::applyOverflowToViewport(const RenderElement& renderer, ScrollbarMode& hMode, ScrollbarMode& vMode) { // Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats // overflow:hidden and overflow:scroll on <body> as applying to the document's @@ -592,18 +718,17 @@ void FrameView::applyOverflowToViewport(RenderElement* o, ScrollbarMode& hMode, bool overrideHidden = frame().isMainFrame() && ((frame().frameScaleFactor() > 1) || headerHeight() || footerHeight()); - EOverflow overflowX = o->style().overflowX(); - EOverflow overflowY = o->style().overflowY(); + EOverflow overflowX = renderer.style().overflowX(); + EOverflow overflowY = renderer.style().overflowY(); -#if ENABLE(SVG) - if (o->isSVGRoot()) { - // overflow is ignored in stand-alone SVG documents. - if (!toRenderSVGRoot(o)->isEmbeddedThroughFrameContainingSVGDocument()) - return; - overflowX = OHIDDEN; - overflowY = OHIDDEN; + if (is<RenderSVGRoot>(renderer)) { + // FIXME: evaluate if we can allow overflow for these cases too. + // Overflow is always hidden when stand-alone SVG documents are embedded. + if (downcast<RenderSVGRoot>(renderer).isEmbeddedThroughFrameContainingSVGDocument()) { + overflowX = OHIDDEN; + overflowY = OHIDDEN; + } } -#endif switch (overflowX) { case OHIDDEN: @@ -640,41 +765,38 @@ void FrameView::applyOverflowToViewport(RenderElement* o, ScrollbarMode& hMode, // Don't set it at all. Values of OPAGEDX and OPAGEDY are handled by applyPaginationToViewPort(). ; } - - m_viewportRenderer = o; } void FrameView::applyPaginationToViewport() { - Document* document = frame().document(); - auto documentElement = document->documentElement(); - RenderElement* documentRenderer = documentElement ? documentElement->renderer() : nullptr; - RenderElement* documentOrBodyRenderer = documentRenderer; - auto body = document->body(); - if (body && body->renderer()) { - if (body->hasTagName(bodyTag)) - documentOrBodyRenderer = documentRenderer->style().overflowX() == OVISIBLE && documentElement->hasTagName(htmlTag) ? body->renderer() : documentRenderer; + auto* document = frame().document(); + auto* documentElement = document ? document->documentElement() : nullptr; + if (!documentElement || !documentElement->renderer()) { + setPagination(Pagination()); + return; } - Pagination pagination; + auto& documentRenderer = *documentElement->renderer(); + auto* documentOrBodyRenderer = &documentRenderer; - if (!documentOrBodyRenderer) { - setPagination(pagination); - return; + auto* body = document->body(); + if (body && body->renderer()) { + documentOrBodyRenderer = documentRenderer.style().overflowX() == OVISIBLE && is<HTMLHtmlElement>(*documentElement) ? + body->renderer() : &documentRenderer; } + Pagination pagination; EOverflow overflowY = documentOrBodyRenderer->style().overflowY(); if (overflowY == OPAGEDX || overflowY == OPAGEDY) { pagination.mode = WebCore::paginationModeForRenderStyle(documentOrBodyRenderer->style()); pagination.gap = static_cast<unsigned>(documentOrBodyRenderer->style().columnGap()); } - setPagination(pagination); } void FrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode, ScrollbarModesCalculationStrategy strategy) { - m_viewportRenderer = 0; + m_viewportRendererType = ViewportRendererType::None; const HTMLFrameOwnerElement* owner = frame().ownerElement(); if (owner && (owner->scrollingMode() == ScrollbarAlwaysOff)) { @@ -685,51 +807,75 @@ void FrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, Scrollbar if (m_canHaveScrollbars || strategy == RulesFromWebContentOnly) { hMode = ScrollbarAuto; - // Seamless documents begin with heights of 0; we special case that here - // to correctly render documents that don't need scrollbars. - IntSize fullVisibleSize = visibleContentRectIncludingScrollbars(LegacyIOSDocumentVisibleRect).size(); - bool isSeamlessDocument = frame().document() && frame().document()->shouldDisplaySeamlesslyWithParent(); - vMode = (isSeamlessDocument && !fullVisibleSize.height()) ? ScrollbarAlwaysOff : ScrollbarAuto; + vMode = ScrollbarAuto; } else { hMode = ScrollbarAlwaysOff; vMode = ScrollbarAlwaysOff; } - if (!m_layoutRoot) { - Document* document = frame().document(); - auto documentElement = document->documentElement(); - RenderElement* rootRenderer = documentElement ? documentElement->renderer() : nullptr; - auto body = document->body(); - if (body && body->renderer()) { - if (body->hasTagName(framesetTag) && !frameFlatteningEnabled()) { - vMode = ScrollbarAlwaysOff; - hMode = ScrollbarAlwaysOff; - } else if (body->hasTagName(bodyTag)) { - // It's sufficient to just check the X overflow, - // since it's illegal to have visible in only one direction. - RenderElement* o = rootRenderer->style().overflowX() == OVISIBLE && document->documentElement()->hasTagName(htmlTag) ? body->renderer() : rootRenderer; - applyOverflowToViewport(o, hMode, vMode); + if (m_layoutRoot) + return; + + auto* document = frame().document(); + if (!document) + return; + + auto* documentElement = document->documentElement(); + if (!documentElement) + return; + + auto* bodyOrFrameset = document->bodyOrFrameset(); + auto* rootRenderer = documentElement->renderer(); + if (!bodyOrFrameset || !bodyOrFrameset->renderer()) { + if (rootRenderer) { + applyOverflowToViewport(*rootRenderer, hMode, vMode); + m_viewportRendererType = ViewportRendererType::Document; + } + return; + } + + if (is<HTMLFrameSetElement>(*bodyOrFrameset) && !frameFlatteningEnabled()) { + vMode = ScrollbarAlwaysOff; + hMode = ScrollbarAlwaysOff; + return; + } + + if (is<HTMLBodyElement>(*bodyOrFrameset) && rootRenderer) { + // It's sufficient to just check the X overflow, + // since it's illegal to have visible in only one direction. + if (rootRenderer->style().overflowX() == OVISIBLE && is<HTMLHtmlElement>(documentElement)) { + auto* bodyRenderer = bodyOrFrameset->renderer(); + if (bodyRenderer) { + applyOverflowToViewport(*bodyRenderer, hMode, vMode); + m_viewportRendererType = ViewportRendererType::Body; } - } else if (rootRenderer) - applyOverflowToViewport(rootRenderer, hMode, vMode); - } + } else { + applyOverflowToViewport(*rootRenderer, hMode, vMode); + m_viewportRendererType = ViewportRendererType::Document; + } + } } -#if USE(ACCELERATED_COMPOSITING) -void FrameView::updateCompositingLayersAfterStyleChange() +void FrameView::willRecalcStyle() { RenderView* renderView = this->renderView(); if (!renderView) return; + renderView->compositor().willRecalcStyle(); +} + +bool FrameView::updateCompositingLayersAfterStyleChange() +{ + RenderView* renderView = this->renderView(); + if (!renderView) + return false; + // If we expect to update compositing after an incipient layout, don't do so here. if (inPreLayoutStyleUpdate() || layoutPending() || renderView->needsLayout()) - return; + return false; - RenderLayerCompositor& compositor = renderView->compositor(); - // This call will make sure the cached hasAcceleratedCompositing is updated from the pref - compositor.cacheAcceleratedCompositingFlags(); - compositor.updateCompositingLayers(CompositingUpdateAfterStyleChange); + return renderView->compositor().didRecalcStyleWithNoPendingLayout(); } void FrameView::updateCompositingLayersAfterLayout() @@ -755,32 +901,11 @@ void FrameView::clearBackingStores() compositor.clearBackingForAllLayers(); } -void FrameView::restoreBackingStores() -{ - RenderView* renderView = this->renderView(); - if (!renderView) - return; - - RenderLayerCompositor& compositor = renderView->compositor(); - compositor.enableCompositingMode(true); - compositor.updateCompositingLayers(CompositingUpdateAfterLayout); -} - -bool FrameView::usesCompositedScrolling() const -{ - RenderView* renderView = this->renderView(); - if (!renderView) - return false; - if (frame().settings().compositedScrollingForFramesEnabled()) - return renderView->compositor().inForcedCompositingMode(); - return false; -} - GraphicsLayer* FrameView::layerForScrolling() const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().scrollLayer(); } @@ -788,7 +913,7 @@ GraphicsLayer* FrameView::layerForHorizontalScrollbar() const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().layerForHorizontalScrollbar(); } @@ -796,7 +921,7 @@ GraphicsLayer* FrameView::layerForVerticalScrollbar() const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().layerForVerticalScrollbar(); } @@ -804,7 +929,7 @@ GraphicsLayer* FrameView::layerForScrollCorner() const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().layerForScrollCorner(); } @@ -812,11 +937,11 @@ TiledBacking* FrameView::tiledBacking() const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; RenderLayerBacking* backing = renderView->layer()->backing(); if (!backing) - return 0; + return nullptr; return backing->graphicsLayer()->tiledBacking(); } @@ -831,7 +956,16 @@ uint64_t FrameView::scrollLayerID() const if (!backing) return 0; - return backing->scrollLayerID(); + return backing->scrollingNodeIDForRole(Scrolling); +} + +ScrollableArea* FrameView::scrollableAreaForScrollLayerID(uint64_t nodeID) const +{ + RenderView* renderView = this->renderView(); + if (!renderView) + return nullptr; + + return renderView->compositor().scrollableAreaForScrollLayerID(nodeID); } #if ENABLE(RUBBER_BANDING) @@ -839,7 +973,7 @@ GraphicsLayer* FrameView::layerForOverhangAreas() const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().layerForOverhangAreas(); } @@ -847,7 +981,7 @@ GraphicsLayer* FrameView::setWantsLayerForTopOverHangArea(bool wantsLayer) const { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().updateLayerForTopOverhangArea(wantsLayer); } @@ -856,14 +990,56 @@ GraphicsLayer* FrameView::setWantsLayerForBottomOverHangArea(bool wantsLayer) co { RenderView* renderView = this->renderView(); if (!renderView) - return 0; + return nullptr; return renderView->compositor().updateLayerForBottomOverhangArea(wantsLayer); } #endif // ENABLE(RUBBER_BANDING) -bool FrameView::flushCompositingStateForThisFrame(Frame* rootFrameForFlush) +#if ENABLE(CSS_SCROLL_SNAP) +void FrameView::updateSnapOffsets() +{ + if (!frame().document()) + return; + + // FIXME: Should we allow specifying snap points through <html> tags too? + HTMLElement* body = frame().document()->bodyOrFrameset(); + if (!renderView() || !body || !body->renderer()) + return; + + updateSnapOffsetsForScrollableArea(*this, *body, *renderView(), body->renderer()->style()); +} + +bool FrameView::isScrollSnapInProgress() const +{ + if (scrollbarsSuppressed()) + return false; + + // If the scrolling thread updates the scroll position for this FrameView, then we should return + // ScrollingCoordinator::isScrollSnapInProgress(). + if (Page* page = frame().page()) { + if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) { + if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this)) + return scrollingCoordinator->isScrollSnapInProgress(); + } + } + + // If the main thread updates the scroll position for this FrameView, we should return + // ScrollAnimator::isScrollSnapInProgress(). + if (ScrollAnimator* scrollAnimator = existingScrollAnimator()) + return scrollAnimator->isScrollSnapInProgress(); + + return false; +} + +void FrameView::updateScrollingCoordinatorScrollSnapProperties() const +{ + renderView()->compositor().updateScrollSnapPropertiesWithFrameView(*this); +} +#endif + +bool FrameView::flushCompositingStateForThisFrame(const Frame& rootFrameForFlush) { RenderView* renderView = this->renderView(); if (!renderView) @@ -877,12 +1053,11 @@ bool FrameView::flushCompositingStateForThisFrame(Frame* rootFrameForFlush) return false; #if PLATFORM(IOS) - if (TileCache* tileCache = this->tileCache()) + if (LegacyTileCache* tileCache = legacyTileCache()) tileCache->doPendingRepaints(); #endif - renderView->compositor().flushPendingLayerChanges(rootFrameForFlush == &frame()); - + renderView->compositor().flushPendingLayerChanges(&rootFrameForFlush == m_frame.ptr()); return true; } @@ -896,22 +1071,22 @@ GraphicsLayer* FrameView::graphicsLayerForPlatformWidget(PlatformWidget platform { // To find the Widget that corresponds with platformWidget we have to do a linear // search of our child widgets. - Widget* foundWidget = nullptr; + const Widget* foundWidget = nullptr; for (auto& widget : children()) { if (widget->platformWidget() != platformWidget) continue; - foundWidget = widget.get(); + foundWidget = widget.ptr(); break; } if (!foundWidget) return nullptr; - auto* renderWidget = RenderWidget::find(foundWidget); + auto* renderWidget = RenderWidget::find(*foundWidget); if (!renderWidget) return nullptr; - RenderLayer* widgetLayer = renderWidget->layer(); + auto* widgetLayer = renderWidget->layer(); if (!widgetLayer || !widgetLayer->isComposited()) return nullptr; @@ -925,7 +1100,36 @@ void FrameView::scheduleLayerFlushAllowingThrottling() return; view->compositor().scheduleLayerFlush(true /* canThrottle */); } -#endif // USE(ACCELERATED_COMPOSITING) + +LayoutRect FrameView::fixedScrollableAreaBoundsInflatedForScrolling(const LayoutRect& uninflatedBounds) const +{ + LayoutPoint scrollPosition; + LayoutSize topLeftExpansion; + LayoutSize bottomRightExpansion; + + if (frame().settings().visualViewportEnabled()) { + // FIXME: this is wrong under zooming; uninflatedBounds is scaled but the scroll positions are not. + scrollPosition = layoutViewportRect().location(); + topLeftExpansion = scrollPosition - unscaledMinimumScrollPosition(); + bottomRightExpansion = unscaledMaximumScrollPosition() - scrollPosition; + } else { + scrollPosition = scrollPositionRespectingCustomFixedPosition(); + topLeftExpansion = scrollPosition - minimumScrollPosition(); + bottomRightExpansion = maximumScrollPosition() - scrollPosition; + } + + return LayoutRect(uninflatedBounds.location() - topLeftExpansion, uninflatedBounds.size() + topLeftExpansion + bottomRightExpansion); +} + +LayoutPoint FrameView::scrollPositionRespectingCustomFixedPosition() const +{ +#if PLATFORM(IOS) + if (!frame().settings().visualViewportEnabled()) + return useCustomFixedPositionLayoutRect() ? customFixedPositionLayoutRect().location() : scrollPosition(); +#endif + + return scrollPositionForFixedPosition(); +} void FrameView::setHeaderHeight(int headerHeight) { @@ -947,103 +1151,101 @@ void FrameView::setFooterHeight(int footerHeight) renderView->setNeedsLayout(); } -bool FrameView::hasCompositedContent() const +float FrameView::topContentInset(TopContentInsetType contentInsetTypeToReturn) const { -#if USE(ACCELERATED_COMPOSITING) - if (RenderView* renderView = this->renderView()) - return renderView->compositor().inCompositingMode(); -#endif - return false; -} + if (platformWidget() && contentInsetTypeToReturn == TopContentInsetType::WebCoreOrPlatformContentInset) + return platformTopContentInset(); -bool FrameView::hasCompositedContentIncludingDescendants() const + if (!frame().isMainFrame()) + return 0; + + Page* page = frame().page(); + return page ? page->topContentInset() : 0; +} + +void FrameView::topContentInsetDidChange(float newTopContentInset) { -#if USE(ACCELERATED_COMPOSITING) - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { - RenderView* renderView = frame->contentRenderer(); - if (RenderLayerCompositor* compositor = renderView ? &renderView->compositor() : 0) { - if (compositor->inCompositingMode()) - return true; + RenderView* renderView = this->renderView(); + if (!renderView) + return; - if (!RenderLayerCompositor::allowsIndependentlyCompositedFrames(this)) - break; - } - } -#endif - return false; + if (platformWidget()) + platformSetTopContentInset(newTopContentInset); + + layout(); + + updateScrollbars(scrollPosition()); + if (renderView->usesCompositing()) + renderView->compositor().frameViewDidChangeSize(); + + if (TiledBacking* tiledBacking = this->tiledBacking()) + tiledBacking->setTopContentInset(newTopContentInset); } -bool FrameView::hasCompositingAncestor() const +void FrameView::topContentDirectionDidChange() { -#if USE(ACCELERATED_COMPOSITING) - for (Frame* frame = this->frame().tree().parent(); frame; frame = frame->tree().parent()) { - if (FrameView* view = frame->view()) { - if (view->hasCompositedContent()) - return true; - } - } -#endif + m_needsDeferredScrollbarsUpdate = true; +} + +void FrameView::handleDeferredScrollbarsUpdateAfterDirectionChange() +{ + if (!m_needsDeferredScrollbarsUpdate) + return; + + m_needsDeferredScrollbarsUpdate = false; + + ASSERT(m_layoutPhase == InPostLayerPositionsUpdatedAfterLayout); + updateScrollbars(scrollPosition()); + positionScrollbarLayers(); +} + +bool FrameView::hasCompositedContent() const +{ + if (RenderView* renderView = this->renderView()) + return renderView->compositor().inCompositingMode(); return false; } // Sometimes (for plug-ins) we need to eagerly go into compositing mode. void FrameView::enterCompositingMode() { -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) { renderView->compositor().enableCompositingMode(); if (!needsLayout()) renderView->compositor().scheduleCompositingLayerUpdate(); } -#endif } bool FrameView::isEnclosedInCompositingLayer() const { -#if USE(ACCELERATED_COMPOSITING) auto frameOwnerRenderer = frame().ownerRenderer(); if (frameOwnerRenderer && frameOwnerRenderer->containerForRepaint()) return true; if (FrameView* parentView = parentFrameView()) return parentView->isEnclosedInCompositingLayer(); -#endif return false; } bool FrameView::flushCompositingStateIncludingSubframes() { -#if USE(ACCELERATED_COMPOSITING) - bool allFramesFlushed = flushCompositingStateForThisFrame(&frame()); - - for (Frame* child = frame().tree().firstChild(); child; child = child->tree().traverseNext(&frame())) { - bool flushed = child->view()->flushCompositingStateForThisFrame(&frame()); + InspectorInstrumentation::willComposite(frame()); + + bool allFramesFlushed = flushCompositingStateForThisFrame(frame()); + + for (Frame* child = frame().tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) { + if (!child->view()) + continue; + bool flushed = child->view()->flushCompositingStateForThisFrame(frame()); allFramesFlushed &= flushed; } return allFramesFlushed; -#else // USE(ACCELERATED_COMPOSITING) - return true; -#endif } bool FrameView::isSoftwareRenderable() const { -#if USE(ACCELERATED_COMPOSITING) RenderView* renderView = this->renderView(); return !renderView || !renderView->compositor().has3DContent(); -#else - return true; -#endif -} - -void FrameView::didMoveOnscreen() -{ - contentAreaDidShow(); -} - -void FrameView::willMoveOffscreen() -{ - contentAreaDidHide(); } void FrameView::setIsInWindow(bool isInWindow) @@ -1052,14 +1254,8 @@ void FrameView::setIsInWindow(bool isInWindow) renderView->setIsInWindow(isInWindow); } -RenderObject* FrameView::layoutRoot(bool onlyDuringLayout) const -{ - return onlyDuringLayout && layoutPending() ? 0 : m_layoutRoot; -} - inline void FrameView::forceLayoutParentViewIfNeeded() { -#if ENABLE(SVG) RenderWidget* ownerRenderer = frame().ownerRenderer(); if (!ownerRenderer) return; @@ -1068,50 +1264,59 @@ inline void FrameView::forceLayoutParentViewIfNeeded() if (!contentBox) return; - RenderSVGRoot* svgRoot = toRenderSVGRoot(contentBox); - if (svgRoot->everHadLayout() && !svgRoot->needsLayout()) + auto& svgRoot = downcast<RenderSVGRoot>(*contentBox); + if (svgRoot.everHadLayout() && !svgRoot.needsLayout()) return; + LOG(Layout, "FrameView %p forceLayoutParentViewIfNeeded scheduling layout on parent FrameView %p", this, &ownerRenderer->view().frameView()); + // If the embedded SVG document appears the first time, the ownerRenderer has already finished // layout without knowing about the existence of the embedded SVG document, because RenderReplaced - // embeddedContentBox() returns 0, as long as the embedded document isn't loaded yet. Before + // embeddedContentBox() returns nullptr, as long as the embedded document isn't loaded yet. Before // bothering to lay out the SVG document, mark the ownerRenderer needing layout and ask its // FrameView for a layout. After that the RenderEmbeddedObject (ownerRenderer) carries the // correct size, which RenderSVGRoot::computeReplacedLogicalWidth/Height rely on, when laying // out for the first time, or when the RenderSVGRoot size has changed dynamically (eg. via <script>). - Ref<FrameView> frameView(ownerRenderer->view().frameView()); - // Mark the owner renderer as needing layout. ownerRenderer->setNeedsLayoutAndPrefWidthsRecalc(); - - // Synchronously enter layout, to layout the view containing the host object/embed/iframe. - frameView->layout(); -#endif + ownerRenderer->view().frameView().scheduleRelayout(); } void FrameView::layout(bool allowSubtree) { - if (isInLayout()) + ASSERT_WITH_SECURITY_IMPLICATION(!frame().document()->inRenderTreeUpdate()); + + LOG(Layout, "FrameView %p (%dx%d) layout, main frameview %d, allowSubtree=%d", this, size().width(), size().height(), frame().isMainFrame(), allowSubtree); + if (isInRenderTreeLayout()) { + LOG(Layout, " in layout, bailing"); + return; + } + + if (layoutDisallowed()) { + LOG(Layout, " layout is disallowed, bailing"); return; + } + + // Protect the view from being deleted during layout (in recalcStyle). + Ref<FrameView> protectedThis(*this); // Many of the tasks performed during layout can cause this function to be re-entered, // so save the layout phase now and restore it on exit. - TemporaryChange<LayoutPhase> layoutPhaseRestorer(m_layoutPhase, InPreLayout); - - // Protect the view from being deleted during layout (in recalcStyle) - Ref<FrameView> protect(*this); + SetForScope<LayoutPhase> layoutPhaseRestorer(m_layoutPhase, InPreLayout); // Every scroll that happens during layout is programmatic. - TemporaryChange<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true); + SetForScope<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true); bool inChildFrameLayoutWithFrameFlattening = isInChildFrameWithFrameFlattening(); if (inChildFrameLayoutWithFrameFlattening) { startLayoutAtMainFrameViewIfNeeded(allowSubtree); RenderElement* root = m_layoutRoot ? m_layoutRoot : frame().document()->renderView(); - if (!root->needsLayout()) + if (!root || !root->needsLayout()) return; } + + TraceScope tracingScope(LayoutStart, LayoutEnd); #if PLATFORM(IOS) if (updateFixedPositionLayoutRect()) @@ -1127,40 +1332,37 @@ void FrameView::layout(bool allowSubtree) if (isPainting()) return; - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLayout(&frame()); - - if (!allowSubtree && m_layoutRoot) { - m_layoutRoot->markContainingBlocksForLayout(false); - m_layoutRoot = 0; - } + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willLayout(frame()); + AnimationUpdateBlock animationUpdateBlock(&frame().animation()); + + if (!allowSubtree && m_layoutRoot) + convertSubtreeLayoutToFullLayout(); ASSERT(frame().view() == this); ASSERT(frame().document()); Document& document = *frame().document(); - ASSERT(!document.inPageCache()); - - bool subtree; - RenderElement* root; + ASSERT(document.pageCacheState() == Document::NotInPageCache); { - TemporaryChange<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false); + SetForScope<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false); if (!m_nestedLayoutCount && !m_inSynchronousPostLayout && m_postLayoutTasksTimer.isActive() && !inChildFrameLayoutWithFrameFlattening) { // This is a new top-level layout. If there are any remaining tasks from the previous // layout, finish them now. - TemporaryChange<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true); + SetForScope<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true); performPostLayoutTasks(); } m_layoutPhase = InPreLayoutStyleUpdate; // Viewport-dependent media queries may cause us to need completely different style information. - StyleResolver* styleResolver = document.styleResolverIfExists(); - if (!styleResolver || styleResolver->affectedByViewportChange()) { - document.styleResolverChanged(DeferRecalcStyle); + auto* styleResolver = document.styleScope().resolverIfExists(); + if (!styleResolver || styleResolver->hasMediaQueriesAffectedByViewportChange()) { + LOG(Layout, " hasMediaQueriesAffectedByViewportChange, enqueueing style recalc"); + document.styleScope().didChangeStyleSheetEnvironment(); // FIXME: This instrumentation event is not strictly accurate since cached media query results do not persist across StyleResolver rebuilds. - InspectorInstrumentation::mediaQueryResultChanged(&document); + InspectorInstrumentation::mediaQueryResultChanged(document); } else document.evaluateMediaQueryList(); @@ -1171,54 +1373,52 @@ void FrameView::layout(bool allowSubtree) // Always ensure our style info is up-to-date. This can happen in situations where // the layout beats any sort of style recalc update that needs to occur. document.updateStyleIfNeeded(); - m_layoutPhase = InPreLayout; - - subtree = m_layoutRoot; - - // If there is only one ref to this view left, then its going to be destroyed as soon as we exit, + // If there is only one ref to this view left, then its going to be destroyed as soon as we exit, // so there's no point to continuing to layout if (hasOneRef()) return; - root = subtree ? m_layoutRoot : document.renderView(); - if (!root) { - // FIXME: Do we need to set m_size here? - return; - } - // Close block here so we can set up the font cache purge preventer, which we will still // want in scope even after we want m_layoutSchedulingEnabled to be restored again. // The next block sets m_layoutSchedulingEnabled back to false once again. } - FontCachePurgePreventer fontCachePurgePreventer; - RenderLayer* layer; + m_layoutPhase = InPreLayout; + + RenderLayer* layer = nullptr; + bool subtree = false; + RenderElement* root = nullptr; ++m_nestedLayoutCount; { - TemporaryChange<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false); + SetForScope<bool> changeSchedulingEnabled(m_layoutSchedulingEnabled, false); + + autoSizeIfEnabled(); + + root = m_layoutRoot ? m_layoutRoot : document.renderView(); + if (!root) + return; + subtree = m_layoutRoot; if (!m_layoutRoot) { - HTMLElement* body = document.body(); + auto* body = document.bodyOrFrameset(); if (body && body->renderer()) { - if (body->hasTagName(framesetTag) && !frameFlatteningEnabled()) { + if (is<HTMLFrameSetElement>(*body) && !frameFlatteningEnabled()) { body->renderer()->setChildNeedsLayout(); - } else if (body->hasTagName(bodyTag)) { - if (!m_firstLayout && m_size.height() != layoutHeight() && body->renderer()->enclosingBox()->stretchesToViewport()) + } else if (is<HTMLBodyElement>(*body)) { + if (!m_firstLayout && m_size.height() != layoutHeight() && body->renderer()->enclosingBox().stretchesToViewport()) body->renderer()->setChildNeedsLayout(); } } -#ifdef INSTRUMENT_LAYOUT_SCHEDULING +#if !LOG_DISABLED if (m_firstLayout && !frame().ownerElement()) - printf("Elapsed time before first layout: %lld\n", document.elapsedTime().count()); -#endif + LOG(Layout, "FrameView %p elapsed time before first layout: %.3fs\n", this, document.timeSinceDocumentCreation().value()); +#endif } - autoSizeIfEnabled(); - - m_needsFullRepaint = !subtree && (m_firstLayout || toRenderView(*root).printing()); + m_needsFullRepaint = !subtree && (m_firstLayout || downcast<RenderView>(*root).printing()); if (!subtree) { ScrollbarMode hMode; @@ -1231,11 +1431,7 @@ void FrameView::layout(bool allowSubtree) m_firstLayout = false; m_firstLayoutCallbackPending = true; - if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling()) - m_lastViewportSize = fixedLayoutSize(); - else - m_lastViewportSize = visibleContentRectIncludingScrollbars().size(); - + m_lastViewportSize = sizeForResizeEvent(); m_lastZoomFactor = root->style().zoom(); // Set the initial vMode to AlwaysOn if we're auto. @@ -1244,7 +1440,9 @@ void FrameView::layout(bool allowSubtree) // Set the initial hMode to AlwaysOff if we're auto. if (hMode == ScrollbarAuto) setHorizontalScrollbarMode(ScrollbarAlwaysOff); // This causes a horizontal scrollbar to disappear. - + Page* page = frame().page(); + if (page && page->expectsWheelEventTriggers()) + scrollAnimator().setWheelEventTestTrigger(page->testTrigger()); setScrollbarModes(hMode, vMode); setScrollbarsSuppressed(false, true); } else @@ -1252,14 +1450,15 @@ void FrameView::layout(bool allowSubtree) } LayoutSize oldSize = m_size; - m_size = layoutSize(); if (oldSize != m_size) { + LOG(Layout, " layout size changed from %.3fx%.3f to %.3fx%.3f", oldSize.width().toFloat(), oldSize.height().toFloat(), m_size.width().toFloat(), m_size.height().toFloat()); m_needsFullRepaint = true; if (!m_firstLayout) { - RenderBox* rootRenderer = document.documentElement() ? document.documentElement()->renderBox() : 0; - RenderBox* bodyRenderer = rootRenderer && document.body() ? document.body()->renderBox() : 0; + RenderBox* rootRenderer = document.documentElement() ? document.documentElement()->renderBox() : nullptr; + auto* body = document.bodyOrFrameset(); + RenderBox* bodyRenderer = rootRenderer && body ? body->renderBox() : nullptr; if (bodyRenderer && bodyRenderer->stretchesToViewport()) bodyRenderer->setChildNeedsLayout(); else if (rootRenderer && rootRenderer->stretchesToViewport()) @@ -1271,53 +1470,49 @@ void FrameView::layout(bool allowSubtree) } layer = root->enclosingLayer(); + SubtreeLayoutStateMaintainer subtreeLayoutStateMaintainer(m_layoutRoot); - bool disableLayoutState = false; - if (subtree) { - disableLayoutState = root->view().shouldDisableLayoutStateForSubtree(root); - root->view().pushLayoutState(*root); - } - LayoutStateDisabler layoutStateDisabler(disableLayoutState ? &root->view() : 0); RenderView::RepaintRegionAccumulator repaintRegionAccumulator(&root->view()); ASSERT(m_layoutPhase == InPreLayout); - m_layoutPhase = InLayout; + m_layoutPhase = InRenderTreeLayout; forceLayoutParentViewIfNeeded(); - ASSERT(m_layoutPhase == InLayout); - - root->layout(); -#if ENABLE(IOS_TEXT_AUTOSIZING) - float minZoomFontSize = frame().settings().minimumZoomFontSize(); - float visWidth = frame().page()->mainFrame().textAutosizingWidth(); - if (minZoomFontSize && visWidth && !root->view().printing()) { - root->adjustComputedFontSizesOnBlocks(minZoomFontSize, visWidth); - bool needsLayout = root->needsLayout(); - if (needsLayout) - root->layout(); - } + ASSERT(m_layoutPhase == InRenderTreeLayout); +#ifndef NDEBUG + RenderTreeNeedsLayoutChecker checker(*root); #endif + root->layout(); + ASSERT(!root->view().renderTreeIsBeingMutatedInternally()); + #if ENABLE(TEXT_AUTOSIZING) - if (document.textAutosizer()->processSubtree(root) && root->needsLayout()) - root->layout(); + if (frame().settings().textAutosizingEnabled() && !root->view().printing()) { + float minimumZoomFontSize = frame().settings().minimumZoomFontSize(); + float textAutosizingWidth = frame().page() ? frame().page()->textAutosizingWidth() : 0; + if (int overrideWidth = frame().settings().textAutosizingWindowSizeOverride().width()) + textAutosizingWidth = overrideWidth; + + LOG(TextAutosizing, "Text Autosizing: minimumZoomFontSize=%.2f textAutosizingWidth=%.2f", minimumZoomFontSize, textAutosizingWidth); + + if (minimumZoomFontSize && textAutosizingWidth) { + root->adjustComputedFontSizesOnBlocks(minimumZoomFontSize, textAutosizingWidth); + if (root->needsLayout()) + root->layout(); + } + } #endif - ASSERT(m_layoutPhase == InLayout); - - if (subtree) - root->view().popLayoutState(*root); - - m_layoutRoot = 0; - - // Close block here to end the scope of changeSchedulingEnabled and layoutStateDisabler. + ASSERT(m_layoutPhase == InRenderTreeLayout); + m_layoutRoot = nullptr; + // Close block here to end the scope of changeSchedulingEnabled and SubtreeLayoutStateMaintainer. } m_layoutPhase = InViewSizeAdjust; bool neededFullRepaint = m_needsFullRepaint; - if (!subtree && !toRenderView(*root).printing()) + if (!subtree && !downcast<RenderView>(*root).printing()) adjustViewSize(); m_layoutPhase = InPostLayout; @@ -1328,15 +1523,19 @@ void FrameView::layout(bool allowSubtree) if (m_needsFullRepaint) root->view().repaintRootContents(); + root->view().releaseProtectedRenderWidgets(); + + ASSERT(!root->needsLayout()); + layer->updateLayerPositionsAfterLayout(renderView()->layer(), updateLayerPositionFlags(layer, subtree, m_needsFullRepaint)); -#if USE(ACCELERATED_COMPOSITING) updateCompositingLayersAfterLayout(); -#endif - + + m_layoutPhase = InPostLayerPositionsUpdatedAfterLayout; + m_layoutCount++; -#if PLATFORM(MAC) || PLATFORM(WIN) || PLATFORM(GTK) || PLATFORM(EFL) +#if PLATFORM(COCOA) || PLATFORM(WIN) || PLATFORM(GTK) if (AXObjectCache* cache = root->document().existingAXObjectCache()) cache->postNotification(root, AXObjectCache::AXLayoutComplete); #endif @@ -1349,19 +1548,23 @@ void FrameView::layout(bool allowSubtree) document.dirtyTouchEventRects(); #endif - ASSERT(!root->needsLayout()); - updateCanBlitOnScrollRecursively(); + handleDeferredScrollUpdateAfterContentSizeChange(); + + handleDeferredScrollbarsUpdateAfterDirectionChange(); + if (document.hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) updateOverflowStatus(layoutWidth() < contentsWidth(), layoutHeight() < contentsHeight()); + frame().document()->markers().invalidateRectsForAllMarkers(); + if (!m_postLayoutTasksTimer.isActive()) { if (!m_inSynchronousPostLayout) { if (inChildFrameLayoutWithFrameFlattening) updateWidgetPositions(); else { - TemporaryChange<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true); + SetForScope<bool> inSynchronousPostLayoutChange(m_inSynchronousPostLayout, true); performPostLayoutTasks(); // Calls resumeScheduledEvents(). } } @@ -1372,37 +1575,33 @@ void FrameView::layout(bool allowSubtree) // can make us need to update again, and we can get stuck in a nasty cycle unless // we call it through the timer here. m_postLayoutTasksTimer.startOneShot(0); - if (needsLayout()) - layout(); } + if (needsLayout()) + layout(); } - InspectorInstrumentation::didLayout(cookie, root); + InspectorInstrumentation::didLayout(cookie, *root); + DebugPageOverlays::didLayout(frame()); --m_nestedLayoutCount; +} - if (m_nestedLayoutCount) - return; - - if (Page* page = frame().page()) - page->chrome().client().layoutUpdated(&frame()); +bool FrameView::shouldDeferScrollUpdateAfterContentSizeChange() +{ + return (m_layoutPhase < InPostLayout) && (m_layoutPhase != OutsideLayout); } RenderBox* FrameView::embeddedContentBox() const { -#if ENABLE(SVG) RenderView* renderView = this->renderView(); if (!renderView) return nullptr; RenderObject* firstChild = renderView->firstChild(); - if (!firstChild || !firstChild->isBox()) - return nullptr; // Curently only embedded SVG documents participate in the size-negotiation logic. - if (toRenderBox(firstChild)->isSVGRoot()) - return toRenderBox(firstChild); -#endif + if (is<RenderSVGRoot>(firstChild)) + return downcast<RenderSVGRoot>(firstChild); return nullptr; } @@ -1410,12 +1609,12 @@ RenderBox* FrameView::embeddedContentBox() const void FrameView::addEmbeddedObjectToUpdate(RenderEmbeddedObject& embeddedObject) { if (!m_embeddedObjectsToUpdate) - m_embeddedObjectsToUpdate = adoptPtr(new ListHashSet<RenderEmbeddedObject*>); + m_embeddedObjectsToUpdate = std::make_unique<ListHashSet<RenderEmbeddedObject*>>(); HTMLFrameOwnerElement& element = embeddedObject.frameOwnerElement(); - if (isHTMLObjectElement(element) || isHTMLEmbedElement(element)) { + if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) { // Tell the DOM element that it needs a widget update. - HTMLPlugInImageElement& pluginElement = toHTMLPlugInImageElement(element); + HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element); if (!pluginElement.needsCheckForSizeChange()) pluginElement.setNeedsWidgetUpdate(true); } @@ -1440,7 +1639,7 @@ String FrameView::mediaType() const { // See if we have an override type. String overrideType = frame().loader().client().overrideMediaType(); - InspectorInstrumentation::applyEmulatedMedia(&frame(), &overrideType); + InspectorInstrumentation::applyEmulatedMedia(frame(), overrideType); if (!overrideType.isNull()) return overrideType; return m_mediaType; @@ -1451,7 +1650,7 @@ void FrameView::adjustMediaTypeForPrinting(bool printing) if (printing) { if (m_mediaTypeWhenNotPrinting.isNull()) m_mediaTypeWhenNotPrinting = mediaType(); - setMediaType("print"); + setMediaType("print"); } else { if (!m_mediaTypeWhenNotPrinting.isNull()) setMediaType(m_mediaTypeWhenNotPrinting); @@ -1467,7 +1666,7 @@ bool FrameView::useSlowRepaints(bool considerOverlap) const // m_contentIsOpaque, so don't take the fast path for composited layers // if they are a platform widget in order to get painting correctness // for transparent layers. See the comment in WidgetMac::paint. - if (contentsInCompositedLayer() && !platformWidget()) + if (usesCompositedScrolling() && !platformWidget()) return mustBeSlow; bool isOverlapped = m_isOverlapped && considerOverlap; @@ -1488,25 +1687,53 @@ bool FrameView::useSlowRepaintsIfNotOverlapped() const void FrameView::updateCanBlitOnScrollRecursively() { - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) { if (FrameView* view = frame->view()) view->setCanBlitOnScroll(!view->useSlowRepaints()); } } -bool FrameView::contentsInCompositedLayer() const +bool FrameView::usesCompositedScrolling() const { -#if USE(ACCELERATED_COMPOSITING) RenderView* renderView = this->renderView(); if (renderView && renderView->isComposited()) { GraphicsLayer* layer = renderView->layer()->backing()->graphicsLayer(); if (layer && layer->drawsContent()) return true; } + + return false; +} + +bool FrameView::usesAsyncScrolling() const +{ +#if ENABLE(ASYNC_SCROLLING) + if (Page* page = frame().page()) { + if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) + return scrollingCoordinator->coordinatesScrollingForFrameView(*this); + } #endif return false; } +bool FrameView::usesMockScrollAnimator() const +{ + return Settings::usesMockScrollAnimator(); +} + +void FrameView::logMockScrollAnimatorMessage(const String& message) const +{ + Document* document = frame().document(); + if (!document) + return; + StringBuilder builder; + if (frame().isMainFrame()) + builder.appendLiteral("Main"); + builder.appendLiteral("FrameView: "); + builder.append(message); + document->addConsoleMessage(MessageSource::Other, MessageLevel::Debug, builder.toString()); +} + void FrameView::setCannotBlitToWindow() { m_cannotBlitToWindow = true; @@ -1518,7 +1745,7 @@ void FrameView::addSlowRepaintObject(RenderElement* o) bool hadSlowRepaintObjects = hasSlowRepaintObjects(); if (!m_slowRepaintObjects) - m_slowRepaintObjects = adoptPtr(new HashSet<RenderElement*>); + m_slowRepaintObjects = std::make_unique<HashSet<const RenderElement*>>(); m_slowRepaintObjects->add(o); @@ -1527,7 +1754,7 @@ void FrameView::addSlowRepaintObject(RenderElement* o) if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(this); + scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(*this); } } } @@ -1544,7 +1771,7 @@ void FrameView::removeSlowRepaintObject(RenderElement* o) if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(this); + scrollingCoordinator->frameViewHasSlowRepaintObjectsDidChange(*this); } } } @@ -1552,7 +1779,7 @@ void FrameView::removeSlowRepaintObject(RenderElement* o) void FrameView::addViewportConstrainedObject(RenderElement* object) { if (!m_viewportConstrainedObjects) - m_viewportConstrainedObjects = adoptPtr(new ViewportConstrainedObjectSet); + m_viewportConstrainedObjects = std::make_unique<ViewportConstrainedObjectSet>(); if (!m_viewportConstrainedObjects->contains(object)) { m_viewportConstrainedObjects->add(object); @@ -1561,7 +1788,7 @@ void FrameView::addViewportConstrainedObject(RenderElement* object) if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingCoordinator->frameViewFixedObjectsDidChange(this); + scrollingCoordinator->frameViewFixedObjectsDidChange(*this); } } } @@ -1571,7 +1798,7 @@ void FrameView::removeViewportConstrainedObject(RenderElement* object) if (m_viewportConstrainedObjects && m_viewportConstrainedObjects->remove(object)) { if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingCoordinator->frameViewFixedObjectsDidChange(this); + scrollingCoordinator->frameViewFixedObjectsDidChange(*this); } // FIXME: In addFixedObject() we only call this if there's a platform widget, @@ -1580,20 +1807,214 @@ void FrameView::removeViewportConstrainedObject(RenderElement* object) } } +// visualViewport and layoutViewport are both in content coordinates (unzoomed). +LayoutPoint FrameView::computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport, ScrollBehaviorForFixedElements fixedBehavior) +{ + LayoutPoint layoutViewportOrigin = layoutViewport.location(); + bool allowRubberBanding = fixedBehavior == StickToViewportBounds; + + if (visualViewport.width() > layoutViewport.width()) + layoutViewportOrigin.setX(visualViewport.x()); + else { + bool rubberbandingAtLeft = allowRubberBanding && visualViewport.x() < stableLayoutViewportOriginMin.x(); + bool rubberbandingAtRight = allowRubberBanding && (visualViewport.maxX() - layoutViewport.width()) > stableLayoutViewportOriginMax.x(); + + if (visualViewport.x() < layoutViewport.x() || rubberbandingAtLeft) + layoutViewportOrigin.setX(visualViewport.x()); + + if (visualViewport.maxX() > layoutViewport.maxX() || rubberbandingAtRight) + layoutViewportOrigin.setX(visualViewport.maxX() - layoutViewport.width()); + + if (!rubberbandingAtLeft && layoutViewportOrigin.x() < stableLayoutViewportOriginMin.x()) + layoutViewportOrigin.setX(stableLayoutViewportOriginMin.x()); + + if (!rubberbandingAtRight && layoutViewportOrigin.x() > stableLayoutViewportOriginMax.x()) + layoutViewportOrigin.setX(stableLayoutViewportOriginMax.x()); + } + + if (visualViewport.height() > layoutViewport.height()) + layoutViewportOrigin.setY(visualViewport.y()); + else { + bool rubberbandingAtTop = allowRubberBanding && visualViewport.y() < stableLayoutViewportOriginMin.y(); + bool rubberbandingAtBottom = allowRubberBanding && (visualViewport.maxY() - layoutViewport.height()) > stableLayoutViewportOriginMax.y(); + + if (visualViewport.y() < layoutViewport.y() || rubberbandingAtTop) + layoutViewportOrigin.setY(visualViewport.y()); + + if (visualViewport.maxY() > layoutViewport.maxY() || rubberbandingAtBottom) + layoutViewportOrigin.setY(visualViewport.maxY() - layoutViewport.height()); + + if (!rubberbandingAtTop && layoutViewportOrigin.y() < stableLayoutViewportOriginMin.y()) + layoutViewportOrigin.setY(stableLayoutViewportOriginMin.y()); + + if (!rubberbandingAtBottom && layoutViewportOrigin.y() > stableLayoutViewportOriginMax.y()) + layoutViewportOrigin.setY(stableLayoutViewportOriginMax.y()); + } + + return layoutViewportOrigin; +} + +void FrameView::setBaseLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot layoutTriggering) +{ + ASSERT(frame().settings().visualViewportEnabled()); + + if (origin == m_layoutViewportOrigin) + return; + + m_layoutViewportOrigin = origin; + if (layoutTriggering == TriggerLayoutOrNot::Yes) + setViewportConstrainedObjectsNeedLayout(); + + if (TiledBacking* tiledBacking = this->tiledBacking()) { + FloatRect layoutViewport = layoutViewportRect(); + layoutViewport.moveBy(unscaledScrollOrigin()); // tiledBacking deals in top-left relative coordinates. + tiledBacking->setLayoutViewportRect(layoutViewport); + } +} + +void FrameView::setLayoutViewportOverrideRect(std::optional<LayoutRect> rect) +{ + if (rect == m_layoutViewportOverrideRect) + return; + + LayoutRect oldRect = layoutViewportRect(); + m_layoutViewportOverrideRect = rect; + + LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " setLayoutViewportOverrideRect() - changing layout viewport from " << oldRect << " to " << m_layoutViewportOverrideRect.value()); + + if (oldRect != layoutViewportRect()) + setViewportConstrainedObjectsNeedLayout(); +} + +LayoutSize FrameView::baseLayoutViewportSize() const +{ + return renderView() ? renderView()->size() : size(); +} + +void FrameView::updateLayoutViewport() +{ + if (!frame().settings().visualViewportEnabled()) + return; + + // Don't update the layout viewport if we're in the middle of adjusting scrollbars. We'll get another call + // as a post-layout task. + if (m_layoutPhase == InViewSizeAdjust) + return; + + if (m_layoutViewportOverrideRect) { + LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() - has layoutViewportOverrideRect" << m_layoutViewportOverrideRect.value()); + return; + } + + LayoutRect layoutViewport = layoutViewportRect(); + + LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() totalContentSize " << totalContentsSize() << " unscaledDocumentRect " << (renderView() ? renderView()->unscaledDocumentRect() : IntRect()) << " header height " << headerHeight() << " footer height " << footerHeight() << " fixed behavior " << scrollBehaviorForFixedElements()); + LOG_WITH_STREAM(Scrolling, stream << "layoutViewport: " << layoutViewport); + LOG_WITH_STREAM(Scrolling, stream << "visualViewport: " << visualViewportRect()); + LOG_WITH_STREAM(Scrolling, stream << "scroll positions: min: " << unscaledMinimumScrollPosition() << " max: "<< unscaledMaximumScrollPosition()); + + LayoutPoint newLayoutViewportOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, scrollBehaviorForFixedElements()); + if (newLayoutViewportOrigin != m_layoutViewportOrigin) { + setBaseLayoutViewportOrigin(newLayoutViewportOrigin); + LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect()); + } +} + +LayoutPoint FrameView::minStableLayoutViewportOrigin() const +{ + return unscaledMinimumScrollPosition(); +} + +LayoutPoint FrameView::maxStableLayoutViewportOrigin() const +{ + LayoutPoint maxPosition = unscaledMaximumScrollPosition(); + maxPosition = (maxPosition - LayoutSize(0, headerHeight() + footerHeight())).expandedTo({ }); + return maxPosition; +} + +IntPoint FrameView::unscaledScrollOrigin() const +{ + if (RenderView* renderView = this->renderView()) + return -renderView->unscaledDocumentRect().location(); // Akin to code in adjustViewSize(). + + return { }; +} + +LayoutRect FrameView::layoutViewportRect() const +{ + if (m_layoutViewportOverrideRect) + return m_layoutViewportOverrideRect.value(); + + // Size of initial containing block, anchored at scroll position, in document coordinates (unchanged by scale factor). + return LayoutRect(m_layoutViewportOrigin, renderView() ? renderView()->size() : size()); +} + +// visibleContentRect is in the bounds of the scroll view content. That consists of an +// optional header, the document, and an optional footer. Only the document is scaled, +// so we have to compute the visible part of the document in unscaled document coordinates. +// On iOS, pageScaleFactor is always 1 here, and we never have headers and footers. +LayoutRect FrameView::visibleDocumentRect(const FloatRect& visibleContentRect, float headerHeight, float footerHeight, const FloatSize& totalContentsSize, float pageScaleFactor) +{ + float contentsHeight = totalContentsSize.height() - headerHeight - footerHeight; + + float rubberBandTop = std::min<float>(visibleContentRect.y(), 0); + float visibleScaledDocumentTop = std::max<float>(visibleContentRect.y() - headerHeight, 0) + rubberBandTop; + + float rubberBandBottom = std::min<float>((totalContentsSize.height() - visibleContentRect.y()) - visibleContentRect.height(), 0); + float visibleScaledDocumentBottom = std::min<float>(visibleContentRect.maxY() - headerHeight, contentsHeight) - rubberBandBottom; + + FloatRect visibleDocumentRect = visibleContentRect; + visibleDocumentRect.setY(visibleScaledDocumentTop); + visibleDocumentRect.setHeight(std::max<float>(visibleScaledDocumentBottom - visibleScaledDocumentTop, 0)); + visibleDocumentRect.scale(1 / pageScaleFactor); + + return LayoutRect(visibleDocumentRect); +} + +LayoutRect FrameView::visualViewportRect() const +{ + FloatRect visibleContentRect = this->visibleContentRect(LegacyIOSDocumentVisibleRect); + return visibleDocumentRect(visibleContentRect, headerHeight(), footerHeight(), totalContentsSize(), frameScaleFactor()); +} + LayoutRect FrameView::viewportConstrainedVisibleContentRect() const { + ASSERT(!frame().settings().visualViewportEnabled()); + #if PLATFORM(IOS) if (useCustomFixedPositionLayoutRect()) return customFixedPositionLayoutRect(); #endif LayoutRect viewportRect = visibleContentRect(); - viewportRect.setLocation(toPoint(scrollOffsetForFixedPosition())); + + viewportRect.setLocation(scrollPositionForFixedPosition()); return viewportRect; } -IntSize FrameView::scrollOffsetForFixedPosition(const IntRect& visibleContentRect, const IntSize& totalContentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements behaviorForFixed, int headerHeight, int footerHeight) +LayoutRect FrameView::rectForFixedPositionLayout() const { - IntPoint position; + if (frame().settings().visualViewportEnabled()) + return layoutViewportRect(); + + return viewportConstrainedVisibleContentRect(); +} + +float FrameView::frameScaleFactor() const +{ + return frame().frameScaleFactor(); +} + +LayoutPoint FrameView::scrollPositionForFixedPosition() const +{ + if (frame().settings().visualViewportEnabled()) + return layoutViewportRect().location(); + + return scrollPositionForFixedPosition(visibleContentRect(), totalContentsSize(), scrollPosition(), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements(), headerHeight(), footerHeight()); +} + +LayoutPoint FrameView::scrollPositionForFixedPosition(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements behaviorForFixed, int headerHeight, int footerHeight) +{ + LayoutPoint position; if (behaviorForFixed == StickToDocumentBounds) position = ScrollableArea::constrainScrollPositionForOverhang(visibleContentRect, totalContentsSize, scrollPosition, scrollOrigin, headerHeight, footerHeight); else { @@ -1601,28 +2022,110 @@ IntSize FrameView::scrollOffsetForFixedPosition(const IntRect& visibleContentRec position.setY(position.y() - headerHeight); } - IntSize maxSize = totalContentsSize - visibleContentRect.size(); + LayoutSize maxSize = totalContentsSize - visibleContentRect.size(); float dragFactorX = (fixedElementsLayoutRelativeToFrame || !maxSize.width()) ? 1 : (totalContentsSize.width() - visibleContentRect.width() * frameScaleFactor) / maxSize.width(); float dragFactorY = (fixedElementsLayoutRelativeToFrame || !maxSize.height()) ? 1 : (totalContentsSize.height() - visibleContentRect.height() * frameScaleFactor) / maxSize.height(); - return IntSize(position.x() * dragFactorX / frameScaleFactor, position.y() * dragFactorY / frameScaleFactor); + return LayoutPoint(position.x() * dragFactorX / frameScaleFactor, position.y() * dragFactorY / frameScaleFactor); +} + +float FrameView::yPositionForInsetClipLayer(const FloatPoint& scrollPosition, float topContentInset) +{ + if (!topContentInset) + return 0; + + // The insetClipLayer should not move for negative scroll values. + float scrollY = std::max<float>(0, scrollPosition.y()); + + if (scrollY >= topContentInset) + return 0; + + return topContentInset - scrollY; } -IntSize FrameView::scrollOffsetForFixedPosition() const +float FrameView::yPositionForHeaderLayer(const FloatPoint& scrollPosition, float topContentInset) +{ + if (!topContentInset) + return 0; + + float scrollY = std::max<float>(0, scrollPosition.y()); + + if (scrollY >= topContentInset) + return topContentInset; + + return scrollY; +} + +float FrameView::yPositionForFooterLayer(const FloatPoint& scrollPosition, float topContentInset, float totalContentsHeight, float footerHeight) +{ + return yPositionForHeaderLayer(scrollPosition, topContentInset) + totalContentsHeight - footerHeight; +} + +FloatPoint FrameView::positionForRootContentLayer(const FloatPoint& scrollPosition, const FloatPoint& scrollOrigin, float topContentInset, float headerHeight) +{ + return FloatPoint(0, yPositionForHeaderLayer(scrollPosition, topContentInset) + headerHeight) - toFloatSize(scrollOrigin); +} + +FloatPoint FrameView::positionForRootContentLayer() const +{ + return positionForRootContentLayer(scrollPosition(), scrollOrigin(), topContentInset(), headerHeight()); +} + +#if PLATFORM(IOS) +LayoutRect FrameView::rectForViewportConstrainedObjects(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements scrollBehavior) +{ + if (fixedElementsLayoutRelativeToFrame) + return visibleContentRect; + + if (totalContentsSize.isEmpty()) + return visibleContentRect; + + // We impose an lower limit on the size (so an upper limit on the scale) of + // the rect used to position fixed objects so that they don't crowd into the + // center of the screen at larger scales. + const LayoutUnit maxContentWidthForZoomThreshold = LayoutUnit::fromPixel(1024); + float zoomedOutScale = frameScaleFactor * visibleContentRect.width() / std::min(maxContentWidthForZoomThreshold, totalContentsSize.width()); + float constraintThresholdScale = 1.5 * zoomedOutScale; + float maxPostionedObjectsRectScale = std::min(frameScaleFactor, constraintThresholdScale); + + LayoutRect viewportConstrainedObjectsRect = visibleContentRect; + + if (frameScaleFactor > constraintThresholdScale) { + FloatRect contentRect(FloatPoint(), totalContentsSize); + FloatRect viewportRect = visibleContentRect; + + // Scale the rect up from a point that is relative to its position in the viewport. + FloatSize sizeDelta = contentRect.size() - viewportRect.size(); + + FloatPoint scaleOrigin; + scaleOrigin.setX(contentRect.x() + sizeDelta.width() > 0 ? contentRect.width() * (viewportRect.x() - contentRect.x()) / sizeDelta.width() : 0); + scaleOrigin.setY(contentRect.y() + sizeDelta.height() > 0 ? contentRect.height() * (viewportRect.y() - contentRect.y()) / sizeDelta.height() : 0); + + AffineTransform rescaleTransform = AffineTransform::translation(scaleOrigin.x(), scaleOrigin.y()); + rescaleTransform.scale(frameScaleFactor / maxPostionedObjectsRectScale, frameScaleFactor / maxPostionedObjectsRectScale); + rescaleTransform = CGAffineTransformTranslate(rescaleTransform, -scaleOrigin.x(), -scaleOrigin.y()); + + viewportConstrainedObjectsRect = enclosingLayoutRect(rescaleTransform.mapRect(visibleContentRect)); + } + + if (scrollBehavior == StickToDocumentBounds) { + LayoutRect documentBounds(LayoutPoint(), totalContentsSize); + viewportConstrainedObjectsRect.intersect(documentBounds); + } + + return viewportConstrainedObjectsRect; +} + +LayoutRect FrameView::viewportConstrainedObjectsRect() const { - IntRect visibleContentRect = this->visibleContentRect(); - IntSize totalContentsSize = this->totalContentsSize(); - IntPoint scrollPosition = this->scrollPosition(); - IntPoint scrollOrigin = this->scrollOrigin(); - float frameScaleFactor = frame().frameScaleFactor(); - ScrollBehaviorForFixedElements behaviorForFixed = scrollBehaviorForFixedElements(); - return scrollOffsetForFixedPosition(visibleContentRect, totalContentsSize, scrollPosition, scrollOrigin, frameScaleFactor, fixedElementsLayoutRelativeToFrame(), behaviorForFixed, headerHeight(), footerHeight()); + return rectForViewportConstrainedObjects(visibleContentRect(), totalContentsSize(), frame().frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements()); } +#endif -IntPoint FrameView::minimumScrollPosition() const +ScrollPosition FrameView::minimumScrollPosition() const { - IntPoint minimumPosition(ScrollView::minimumScrollPosition()); + ScrollPosition minimumPosition = ScrollView::minimumScrollPosition(); if (frame().isMainFrame() && m_scrollPinningBehavior == PinToBottom) minimumPosition.setY(maximumScrollPosition().y()); @@ -1630,16 +2133,66 @@ IntPoint FrameView::minimumScrollPosition() const return minimumPosition; } -IntPoint FrameView::maximumScrollPosition() const +ScrollPosition FrameView::maximumScrollPosition() const { - IntPoint maximumOffset(contentsWidth() - visibleWidth() - scrollOrigin().x(), totalContentsSize().height() - visibleHeight() - scrollOrigin().y()); - - maximumOffset.clampNegativeToZero(); + ScrollPosition maximumPosition = ScrollView::maximumScrollPosition(); if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop) - maximumOffset.setY(minimumScrollPosition().y()); + maximumPosition.setY(minimumScrollPosition().y()); - return maximumOffset; + return maximumPosition; +} + +ScrollPosition FrameView::unscaledMinimumScrollPosition() const +{ + if (RenderView* renderView = this->renderView()) { + IntRect unscaledDocumentRect = renderView->unscaledDocumentRect(); + ScrollPosition minimumPosition = unscaledDocumentRect.location(); + + if (frame().isMainFrame() && m_scrollPinningBehavior == PinToBottom) + minimumPosition.setY(unscaledMaximumScrollPosition().y()); + + return minimumPosition; + } + + return minimumScrollPosition(); +} + +ScrollPosition FrameView::unscaledMaximumScrollPosition() const +{ + if (RenderView* renderView = this->renderView()) { + IntRect unscaledDocumentRect = renderView->unscaledDocumentRect(); + unscaledDocumentRect.expand(0, headerHeight() + footerHeight()); + ScrollPosition maximumPosition = ScrollPosition(unscaledDocumentRect.maxXMaxYCorner() - visibleSize()).expandedTo({ 0, 0 }); + + if (frame().isMainFrame() && m_scrollPinningBehavior == PinToTop) + maximumPosition.setY(unscaledMinimumScrollPosition().y()); + + return maximumPosition; + } + + return maximumScrollPosition(); +} + +void FrameView::viewportContentsChanged() +{ + if (!frame().view()) { + // The frame is being destroyed. + return; + } + + if (auto* page = frame().page()) + page->updateValidationBubbleStateIfNeeded(); + + // When the viewport contents changes (scroll, resize, style recalc, layout, ...), + // check if we should resume animated images or unthrottle DOM timers. + applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) { + frameView.resumeVisibleImageAnimations(visibleRect); + frameView.updateScriptedAnimationsAndTimersThrottlingState(visibleRect); + + if (auto* renderView = frameView.frame().contentRenderer()) + renderView->updateVisibleViewportRect(visibleRect); + }); } bool FrameView::fixedElementsLayoutRelativeToFrame() const @@ -1666,42 +2219,38 @@ bool FrameView::shouldSetCursor() const bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) { if (!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmpty()) { - hostWindow()->scroll(scrollDelta, rectToScroll, clipRect); + frame().page()->chrome().scroll(scrollDelta, rectToScroll, clipRect); return true; } - const bool isCompositedContentLayer = contentsInCompositedLayer(); + bool isCompositedContentLayer = usesCompositedScrolling(); // Get the rects of the fixed objects visible in the rectToScroll Region regionToUpdate; for (auto& renderer : *m_viewportConstrainedObjects) { if (!renderer->style().hasViewportConstrainedPosition()) continue; -#if USE(ACCELERATED_COMPOSITING) if (renderer->isComposited()) continue; -#endif // Fixed items should always have layers. ASSERT(renderer->hasLayer()); - RenderLayer* layer = toRenderBoxModelObject(renderer)->layer(); + RenderLayer* layer = downcast<RenderBoxModelObject>(*renderer).layer(); -#if USE(ACCELERATED_COMPOSITING) if (layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForBoundsOutOfView || layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForNoVisibleContent) { // Don't invalidate for invisible fixed layers. continue; } -#endif -#if ENABLE(CSS_FILTERS) if (layer->hasAncestorWithFilterOutsets()) { // If the fixed layer has a blur/drop-shadow filter applied on at least one of its parents, we cannot // scroll using the fast path, otherwise the outsets of the filter will be moved around the page. return false; } -#endif - IntRect updateRect = pixelSnappedIntRect(layer->repaintRectIncludingNonCompositingDescendants()); + + // FIXME: use pixel snapping instead of enclosing when ScrollView has finished transitioning from IntRect to Float/LayoutRect. + IntRect updateRect = enclosingIntRect(layer->repaintRectIncludingNonCompositingDescendants()); updateRect = contentsToRootView(updateRect); if (!isCompositedContentLayer && clipsRepaints()) updateRect.intersect(rectToScroll); @@ -1710,27 +2259,22 @@ bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect } // 1) scroll - hostWindow()->scroll(scrollDelta, rectToScroll, clipRect); + frame().page()->chrome().scroll(scrollDelta, rectToScroll, clipRect); // 2) update the area of fixed objects that has been invalidated - Vector<IntRect> subRectsToUpdate = regionToUpdate.rects(); - size_t viewportConstrainedObjectsCount = subRectsToUpdate.size(); - for (size_t i = 0; i < viewportConstrainedObjectsCount; ++i) { - IntRect updateRect = subRectsToUpdate[i]; + for (auto& updateRect : regionToUpdate.rects()) { IntRect scrolledRect = updateRect; scrolledRect.move(scrollDelta); updateRect.unite(scrolledRect); -#if USE(ACCELERATED_COMPOSITING) if (isCompositedContentLayer) { updateRect = rootViewToContents(updateRect); ASSERT(renderView()); renderView()->layer()->setBackingNeedsRepaintInRect(updateRect); continue; } -#endif if (clipsRepaints()) updateRect.intersect(rectToScroll); - hostWindow()->invalidateContentsAndRootView(updateRect, false); + frame().page()->chrome().invalidateContentsAndRootView(updateRect); } return true; @@ -1738,31 +2282,16 @@ bool FrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect void FrameView::scrollContentsSlowPath(const IntRect& updateRect) { -#if USE(ACCELERATED_COMPOSITING) - if (contentsInCompositedLayer()) { - // FIXME: respect paintsEntireContents()? - IntRect updateRect = visibleContentRect(LegacyIOSDocumentVisibleRect); - - // Make sure to "apply" the scale factor here since we're converting from frame view - // coordinates to layer backing coordinates. - updateRect.scale(1 / frame().frameScaleFactor()); - - ASSERT(renderView()); - renderView()->layer()->setBackingNeedsRepaintInRect(updateRect); - } - repaintSlowRepaintObjects(); - if (RenderWidget* frameRenderer = frame().ownerRenderer()) { - if (isEnclosedInCompositingLayer()) { - LayoutRect rect(frameRenderer->borderLeft() + frameRenderer->paddingLeft(), - frameRenderer->borderTop() + frameRenderer->paddingTop(), - visibleWidth(), visibleHeight()); + if (!usesCompositedScrolling() && isEnclosedInCompositingLayer()) { + if (RenderWidget* frameRenderer = frame().ownerRenderer()) { + LayoutRect rect(frameRenderer->borderLeft() + frameRenderer->paddingLeft(), frameRenderer->borderTop() + frameRenderer->paddingTop(), + visibleWidth(), visibleHeight()); frameRenderer->repaintRectangle(rect); return; } } -#endif ScrollView::scrollContentsSlowPath(updateRect); } @@ -1775,7 +2304,7 @@ void FrameView::repaintSlowRepaintObjects() // Renderers with fixed backgrounds may be in compositing layers, so we need to explicitly // repaint them after scrolling. for (auto& renderer : *m_slowRepaintObjects) - renderer->repaint(); + renderer->repaintSlowRepaintObject(); } // Note that this gets called at painting time. @@ -1786,45 +2315,6 @@ void FrameView::setIsOverlapped(bool isOverlapped) m_isOverlapped = isOverlapped; updateCanBlitOnScrollRecursively(); - -#if USE(ACCELERATED_COMPOSITING) - if (hasCompositedContentIncludingDescendants()) { - // Overlap can affect compositing tests, so if it changes, we need to trigger - // a layer update in the parent document. - if (Frame* parentFrame = frame().tree().parent()) { - if (RenderView* parentView = parentFrame->contentRenderer()) { - RenderLayerCompositor& compositor = parentView->compositor(); - compositor.setCompositingLayersNeedRebuild(); - compositor.scheduleCompositingLayerUpdate(); - } - } - - if (RenderLayerCompositor::allowsIndependentlyCompositedFrames(this)) { - // We also need to trigger reevaluation for this and all descendant frames, - // since a frame uses compositing if any ancestor is compositing. - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { - if (RenderView* view = frame->contentRenderer()) { - RenderLayerCompositor& compositor = view->compositor(); - compositor.setCompositingLayersNeedRebuild(); - compositor.scheduleCompositingLayerUpdate(); - } - } - } - } -#endif -} - -bool FrameView::isOverlappedIncludingAncestors() const -{ - if (isOverlapped()) - return true; - - if (FrameView* parentView = parentFrameView()) { - if (parentView->isOverlapped()) - return true; - } - - return false; } void FrameView::setContentIsOpaque(bool contentIsOpaque) @@ -1847,8 +2337,10 @@ bool FrameView::scrollToFragment(const URL& url) // OTOH If CSS target was set previously, we want to set it to 0, recalc // and possibly repaint because :target pseudo class may have been // set (see bug 11321). - if (!url.hasFragmentIdentifier() && !frame().document()->cssTarget()) + if (!url.hasFragmentIdentifier()) { + frame().document()->setCSSTarget(nullptr); return false; + } String fragmentIdentifier = url.fragmentIdentifier(); if (scrollToAnchor(fragmentIdentifier)) @@ -1864,43 +2356,51 @@ bool FrameView::scrollToFragment(const URL& url) bool FrameView::scrollToAnchor(const String& name) { ASSERT(frame().document()); + auto& document = *frame().document(); - if (!frame().document()->haveStylesheetsLoaded()) { - frame().document()->setGotoAnchorNeededAfterStylesheetsLoad(true); + if (!document.haveStylesheetsLoaded()) { + document.setGotoAnchorNeededAfterStylesheetsLoad(true); return false; } - frame().document()->setGotoAnchorNeededAfterStylesheetsLoad(false); + document.setGotoAnchorNeededAfterStylesheetsLoad(false); - Element* anchorElement = frame().document()->findAnchor(name); + Element* anchorElement = document.findAnchor(name); // Setting to null will clear the current target. - frame().document()->setCSSTarget(anchorElement); + document.setCSSTarget(anchorElement); -#if ENABLE(SVG) - if (frame().document()->isSVGDocument()) { - if (SVGSVGElement* svg = toSVGDocument(frame().document())->rootElement()) { - svg->setupInitialView(name, anchorElement); + if (is<SVGDocument>(document)) { + if (auto* rootElement = SVGDocument::rootElement(document)) { + rootElement->scrollToAnchor(name, anchorElement); if (!anchorElement) return true; } } -#endif - + // Implement the rule that "" and "top" both mean top of page as in other browsers. - if (!anchorElement && !(name.isEmpty() || equalIgnoringCase(name, "top"))) + if (!anchorElement && !(name.isEmpty() || equalLettersIgnoringASCIICase(name, "top"))) return false; - maintainScrollPositionAtAnchor(anchorElement ? static_cast<Node*>(anchorElement) : frame().document()); + ContainerNode* scrollPositionAnchor = anchorElement; + if (!scrollPositionAnchor) + scrollPositionAnchor = frame().document(); + maintainScrollPositionAtAnchor(scrollPositionAnchor); // If the anchor accepts keyboard focus, move focus there to aid users relying on keyboard navigation. - if (anchorElement && anchorElement->isFocusable()) - frame().document()->setFocusedElement(anchorElement); + if (anchorElement) { + if (anchorElement->isFocusable()) + document.setFocusedElement(anchorElement); + else { + document.setFocusedElement(nullptr); + document.setFocusNavigationStartingNode(anchorElement); + } + } return true; } -void FrameView::maintainScrollPositionAtAnchor(Node* anchorNode) +void FrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode) { m_maintainScrollPositionAnchor = anchorNode; if (!m_maintainScrollPositionAnchor) @@ -1917,33 +2417,43 @@ void FrameView::maintainScrollPositionAtAnchor(Node* anchorNode) scrollToAnchor(); } -void FrameView::scrollElementToRect(Element* element, const IntRect& rect) +void FrameView::scrollElementToRect(const Element& element, const IntRect& rect) { frame().document()->updateLayoutIgnorePendingStylesheets(); - LayoutRect bounds = element->boundingBox(); + LayoutRect bounds; + if (RenderElement* renderer = element.renderer()) + bounds = renderer->absoluteAnchorRect(); int centeringOffsetX = (rect.width() - bounds.width()) / 2; int centeringOffsetY = (rect.height() - bounds.height()) / 2; setScrollPosition(IntPoint(bounds.x() - centeringOffsetX - rect.x(), bounds.y() - centeringOffsetY - rect.y())); } -void FrameView::setScrollPosition(const IntPoint& scrollPoint) +void FrameView::setScrollPosition(const ScrollPosition& scrollPosition) { - TemporaryChange<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true); - m_maintainScrollPositionAnchor = 0; - ScrollView::setScrollPosition(scrollPoint); + SetForScope<bool> changeInProgrammaticScroll(m_inProgrammaticScroll, true); + m_maintainScrollPositionAnchor = nullptr; + Page* page = frame().page(); + if (page && page->expectsWheelEventTriggers()) + scrollAnimator().setWheelEventTestTrigger(page->testTrigger()); + ScrollView::setScrollPosition(scrollPosition); +} + +void FrameView::contentsResized() +{ + // For non-delegated scrolling, updateTiledBackingAdaptiveSizing() is called via addedOrRemovedScrollbar() which occurs less often. + if (delegatesScrolling()) + updateTiledBackingAdaptiveSizing(); } void FrameView::delegatesScrollingDidChange() { -#if USE(ACCELERATED_COMPOSITING) // When we switch to delgatesScrolling mode, we should destroy the scrolling/clipping layers in RenderLayerCompositor. if (hasCompositedContent()) clearBackingStores(); -#endif } -#if USE(TILED_BACKING_STORE) +#if USE(COORDINATED_GRAPHICS) void FrameView::setFixedVisibleContentRect(const IntRect& visibleContentRect) { bool visibleContentSizeDidChange = false; @@ -1954,20 +2464,21 @@ void FrameView::setFixedVisibleContentRect(const IntRect& visibleContentRect) visibleContentSizeDidChange = true; } - IntSize offset = scrollOffset(); + IntPoint oldPosition = scrollPosition(); ScrollView::setFixedVisibleContentRect(visibleContentRect); - if (offset != scrollOffset()) { + IntPoint newPosition = scrollPosition(); + if (oldPosition != newPosition) { updateLayerPositionsAfterScrolling(); - if (frame().page()->settings().acceleratedCompositingForFixedPositionEnabled()) + if (frame().settings().acceleratedCompositingForFixedPositionEnabled()) updateCompositingLayersAfterScrolling(); - scrollAnimator()->setCurrentPosition(scrollPosition()); - scrollPositionChanged(); + scrollAnimator().setCurrentPosition(newPosition); + scrollPositionChanged(oldPosition, newPosition); } if (visibleContentSizeDidChange) { // Update the scroll-bars to calculate new page-step size. - updateScrollbars(scrollOffset()); + updateScrollbars(scrollPosition()); } - frame().loader().client().didChangeScrollOffset(); + didChangeScrollOffset(); } #endif @@ -1980,25 +2491,92 @@ void FrameView::setViewportConstrainedObjectsNeedLayout() renderer->setNeedsLayout(); } -void FrameView::scrollPositionChangedViaPlatformWidget() +void FrameView::didChangeScrollOffset() +{ + frame().mainFrame().pageOverlayController().didScrollFrame(frame()); + frame().loader().client().didChangeScrollOffset(); +} + +void FrameView::scrollOffsetChangedViaPlatformWidgetImpl(const ScrollOffset& oldOffset, const ScrollOffset& newOffset) { updateLayerPositionsAfterScrolling(); updateCompositingLayersAfterScrolling(); repaintSlowRepaintObjects(); - scrollPositionChanged(); + scrollPositionChanged(scrollPositionFromOffset(oldOffset), scrollPositionFromOffset(newOffset)); } -void FrameView::scrollPositionChanged() +// These scroll positions are affected by zooming. +void FrameView::scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition) { - frame().eventHandler().sendScrollEvent(); - frame().eventHandler().dispatchFakeMouseMoveEventSoon(); + Page* page = frame().page(); + Seconds throttlingDelay = page ? page->chrome().client().eventThrottlingDelay() : 0_s; + + if (throttlingDelay == 0_s) { + m_delayedScrollEventTimer.stop(); + sendScrollEvent(); + } else if (!m_delayedScrollEventTimer.isActive()) + m_delayedScrollEventTimer.startOneShot(throttlingDelay.value()); + + if (Document* document = frame().document()) + document->sendWillRevealEdgeEventsIfNeeded(oldPosition, newPosition, visibleContentRect(), contentsSize()); -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) { if (renderView->usesCompositing()) renderView->compositor().frameViewDidScroll(); } -#endif + + LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " scrollPositionChanged from " << oldPosition << " to " << newPosition << " (scale " << frameScaleFactor() << " )"); + updateLayoutViewport(); + viewportContentsChanged(); +} + +void FrameView::applyRecursivelyWithVisibleRect(const std::function<void (FrameView& frameView, const IntRect& visibleRect)>& apply) +{ + IntRect windowClipRect = this->windowClipRect(); + auto visibleRect = windowToContents(windowClipRect); + apply(*this, visibleRect); + + // Recursive call for subframes. We cache the current FrameView's windowClipRect to avoid recomputing it for every subframe. + SetForScope<IntRect*> windowClipRectCache(m_cachedWindowClipRect, &windowClipRect); + for (Frame* childFrame = frame().tree().firstChild(); childFrame; childFrame = childFrame->tree().nextSibling()) { + if (auto* childView = childFrame->view()) + childView->applyRecursivelyWithVisibleRect(apply); + } +} + +void FrameView::resumeVisibleImageAnimations(const IntRect& visibleRect) +{ + if (visibleRect.isEmpty()) + return; + + if (auto* renderView = frame().contentRenderer()) + renderView->resumePausedImageAnimationsIfNeeded(visibleRect); +} + +void FrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect) +{ + if (frame().isMainFrame()) + return; + + auto* document = frame().document(); + if (!document) + return; + + // We don't throttle zero-size or display:none frames because those are usually utility frames. + bool shouldThrottle = visibleRect.isEmpty() && !m_size.isEmpty() && frame().ownerRenderer(); + + if (auto* scriptedAnimationController = document->scriptedAnimationController()) + scriptedAnimationController->setThrottled(shouldThrottle); + + document->setTimerThrottlingEnabled(shouldThrottle); +} + + +void FrameView::resumeVisibleImageAnimationsIncludingSubframes() +{ + applyRecursivelyWithVisibleRect([] (FrameView& frameView, const IntRect& visibleRect) { + frameView.resumeVisibleImageAnimations(visibleRect); + }); } void FrameView::updateLayerPositionsAfterScrolling() @@ -2024,7 +2602,7 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const if (!page) return true; - if (&page->mainFrame() != &frame()) + if (&page->mainFrame() != m_frame.ptr()) return true; ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator(); @@ -2034,7 +2612,7 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const if (!scrollingCoordinator->supportsFixedPositionLayers()) return true; - if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously()) + if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this)) return true; if (inProgrammaticScroll()) @@ -2047,7 +2625,8 @@ bool FrameView::shouldUpdateCompositingLayersAfterScrolling() const void FrameView::updateCompositingLayersAfterScrolling() { -#if USE(ACCELERATED_COMPOSITING) + ASSERT(m_layoutPhase >= InPostLayout || m_layoutPhase == OutsideLayout); + if (!shouldUpdateCompositingLayersAfterScrolling()) return; @@ -2055,7 +2634,6 @@ void FrameView::updateCompositingLayersAfterScrolling() if (RenderView* renderView = this->renderView()) renderView->compositor().updateCompositingLayers(CompositingUpdateOnScroll); } -#endif } bool FrameView::isRubberBandInProgress() const @@ -2067,7 +2645,7 @@ bool FrameView::isRubberBandInProgress() const // ScrollingCoordinator::isRubberBandInProgress(). if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) { - if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously()) + if (!scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this)) return scrollingCoordinator->isRubberBandInProgress(); } } @@ -2080,18 +2658,19 @@ bool FrameView::isRubberBandInProgress() const return false; } -bool FrameView::requestScrollPositionUpdate(const IntPoint& position) +bool FrameView::requestScrollPositionUpdate(const ScrollPosition& position) { + LOG_WITH_STREAM(Scrolling, stream << "FrameView::requestScrollPositionUpdate " << position); + #if ENABLE(ASYNC_SCROLLING) - if (TiledBacking* tiledBacking = this->tiledBacking()) { - IntRect visibleRect = visibleContentRect(); - visibleRect.setLocation(position); - tiledBacking->prepopulateRect(visibleRect); - } + if (TiledBacking* tiledBacking = this->tiledBacking()) + tiledBacking->prepopulateRect(FloatRect(position, visibleContentRect().size())); +#endif +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - return scrollingCoordinator->requestScrollPositionUpdate(this, position); + return scrollingCoordinator->requestScrollPositionUpdate(*this, position); } #else UNUSED_PARAM(position); @@ -2102,74 +2681,73 @@ bool FrameView::requestScrollPositionUpdate(const IntPoint& position) HostWindow* FrameView::hostWindow() const { - if (Page* page = frame().page()) - return &page->chrome(); - return 0; + auto* page = frame().page(); + if (!page) + return nullptr; + return &page->chrome(); } -void FrameView::addTrackedRepaintRect(const IntRect& r) +void FrameView::addTrackedRepaintRect(const FloatRect& r) { if (!m_isTrackingRepaints || r.isEmpty()) return; - IntRect repaintRect = r; - repaintRect.move(-scrollOffset()); + FloatRect repaintRect = r; + repaintRect.moveBy(-scrollPosition()); m_trackedRepaintRects.append(repaintRect); } -void FrameView::repaintContentRectangle(const IntRect& r, bool immediate) +void FrameView::repaintContentRectangle(const IntRect& r) { ASSERT(!frame().ownerElement()); - if (!shouldUpdate(immediate)) + if (!shouldUpdate()) return; -#if USE(TILED_BACKING_STORE) - if (frame().tiledBackingStore()) { - frame().tiledBackingStore()->invalidate(r); - return; - } -#endif - ScrollView::repaintContentRectangle(r, immediate); + ScrollView::repaintContentRectangle(r); } -static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderObject& renderer, unsigned countSoFar, unsigned threshold) +static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderElement& renderer, unsigned threshold) { - // FIXME: Consider writing this using RenderObject::nextInPreOrder() instead of using recursion. - if (renderer.isText()) - countSoFar += toRenderText(renderer).text()->length(); - - for (RenderObject* child = renderer.firstChildSlow(); child; child = child->nextSibling()) { - if (countSoFar >= threshold) - break; - countSoFar = countRenderedCharactersInRenderObjectWithThreshold(*child, countSoFar, threshold); + unsigned count = 0; + for (const RenderObject* descendant = &renderer; descendant; descendant = descendant->nextInPreOrder()) { + if (is<RenderText>(*descendant)) { + count += downcast<RenderText>(*descendant).text()->length(); + if (count >= threshold) + break; + } } - return countSoFar; + return count; } bool FrameView::renderedCharactersExceed(unsigned threshold) { - if (!m_frame->contentRenderer()) + if (!frame().contentRenderer()) return false; - return countRenderedCharactersInRenderObjectWithThreshold(*m_frame->contentRenderer(), 0, threshold) >= threshold; + return countRenderedCharactersInRenderObjectWithThreshold(*frame().contentRenderer(), threshold) >= threshold; } -void FrameView::contentsResized() +void FrameView::availableContentSizeChanged(AvailableSizeChangeReason reason) { - ScrollView::contentsResized(); + if (Document* document = frame().document()) { + // FIXME: Merge this logic with m_setNeedsLayoutWasDeferred and find a more appropriate + // way of handling potential recursive layouts when the viewport is resized to accomodate + // the content but the content always overflows the viewport. See webkit.org/b/165781. + if (!(layoutPhase() == InViewSizeAdjust && useFixedLayout())) + document->updateViewportUnitsOnResize(); + } + + updateLayoutViewport(); setNeedsLayout(); + ScrollView::availableContentSizeChanged(reason); } -void FrameView::fixedLayoutSizeChanged() +bool FrameView::shouldLayoutAfterContentsResized() const { - // Can be triggered before the view is set, see comment in FrameView::visibleContentsResized(). - // An ASSERT is triggered when a view schedules a layout before being attached to a frame. - if (!frame().view()) - return; - ScrollView::fixedLayoutSizeChanged(); + return !useFixedLayout() || useCustomFixedPositionLayoutRect(); } -void FrameView::visibleContentsResized() +void FrameView::updateContentsSize() { // We check to make sure the view is attached to a frame() as this method can // be triggered before the view is attached by Frame::createView(...) setting @@ -2190,44 +2768,134 @@ void FrameView::visibleContentsResized() } #endif - if (!useFixedLayout() && needsLayout()) + if (shouldLayoutAfterContentsResized() && needsLayout()) layout(); -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) { if (renderView->usesCompositing()) renderView->compositor().frameViewDidChangeSize(); } -#endif } void FrameView::addedOrRemovedScrollbar() { -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) { if (renderView->usesCompositing()) renderView->compositor().frameViewDidAddOrRemoveScrollbars(); } + + updateTiledBackingAdaptiveSizing(); +} + +TiledBacking::Scrollability FrameView::computeScrollability() const +{ + auto* page = frame().page(); + + // Use smaller square tiles if the Window is not active to facilitate app napping. + if (!page || !page->isWindowActive()) + return TiledBacking::HorizontallyScrollable | TiledBacking::VerticallyScrollable; + + bool horizontallyScrollable; + bool verticallyScrollable; + bool clippedByAncestorView = static_cast<bool>(m_viewExposedRect); + +#if PLATFORM(IOS) + if (page) + clippedByAncestorView |= page->enclosedInScrollableAncestorView(); +#endif + + if (delegatesScrolling()) { + IntSize documentSize = contentsSize(); + IntSize visibleSize = this->visibleSize(); + + horizontallyScrollable = clippedByAncestorView || documentSize.width() > visibleSize.width(); + verticallyScrollable = clippedByAncestorView || documentSize.height() > visibleSize.height(); + } else { + horizontallyScrollable = clippedByAncestorView || horizontalScrollbar(); + verticallyScrollable = clippedByAncestorView || verticalScrollbar(); + } + + TiledBacking::Scrollability scrollability = TiledBacking::NotScrollable; + if (horizontallyScrollable) + scrollability = TiledBacking::HorizontallyScrollable; + + if (verticallyScrollable) + scrollability |= TiledBacking::VerticallyScrollable; + + return scrollability; +} + +void FrameView::updateTiledBackingAdaptiveSizing() +{ + auto* tiledBacking = this->tiledBacking(); + if (!tiledBacking) + return; + + tiledBacking->setScrollability(computeScrollability()); +} + +#if PLATFORM(IOS) + +void FrameView::unobscuredContentSizeChanged() +{ + updateTiledBackingAdaptiveSizing(); +} + #endif + +static LayerFlushThrottleState::Flags determineLayerFlushThrottleState(Page& page) +{ + // We only throttle when constantly receiving new data during the inital page load. + if (!page.progress().isMainLoadProgressing()) + return 0; + // Scrolling during page loading disables throttling. + if (page.mainFrame().view()->wasScrolledByUser()) + return 0; + // Disable for image documents so large GIF animations don't get throttled during loading. + auto* document = page.mainFrame().document(); + if (!document || is<ImageDocument>(*document)) + return 0; + return LayerFlushThrottleState::Enabled; } void FrameView::disableLayerFlushThrottlingTemporarilyForInteraction() { -#if USE(ACCELERATED_COMPOSITING) + if (!frame().page()) + return; + auto& page = *frame().page(); + + LayerFlushThrottleState::Flags flags = LayerFlushThrottleState::UserIsInteracting | determineLayerFlushThrottleState(page); + if (page.chrome().client().adjustLayerFlushThrottling(flags)) + return; + if (RenderView* view = renderView()) view->compositor().disableLayerFlushThrottlingTemporarilyForInteraction(); -#endif } -void FrameView::updateLayerFlushThrottlingInAllFrames() +void FrameView::loadProgressingStatusChanged() { -#if USE(ACCELERATED_COMPOSITING) - bool isMainLoadProgressing = frame().page()->progress().isMainLoadProgressing(); - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { + updateLayerFlushThrottling(); + adjustTiledBackingCoverage(); +} + +void FrameView::updateLayerFlushThrottling() +{ + Page* page = frame().page(); + if (!page) + return; + + ASSERT(frame().isMainFrame()); + + LayerFlushThrottleState::Flags flags = determineLayerFlushThrottleState(*page); + + // See if the client is handling throttling. + if (page->chrome().client().adjustLayerFlushThrottling(flags)) + return; + + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) { if (RenderView* renderView = frame->contentRenderer()) - renderView->compositor().setLayerFlushThrottlingEnabled(isMainLoadProgressing); + renderView->compositor().setLayerFlushThrottlingEnabled(flags & LayerFlushThrottleState::Enabled); } -#endif } void FrameView::adjustTiledBackingCoverage() @@ -2235,20 +2903,19 @@ void FrameView::adjustTiledBackingCoverage() if (!m_speculativeTilingEnabled) enableSpeculativeTilingIfNeeded(); -#if USE(ACCELERATED_COMPOSITING) RenderView* renderView = this->renderView(); if (renderView && renderView->layer()->backing()) renderView->layer()->backing()->adjustTiledBackingCoverage(); -#endif #if PLATFORM(IOS) - if (TileCache* tileCache = this->tileCache()) + if (LegacyTileCache* tileCache = legacyTileCache()) tileCache->setSpeculativeTileCreationEnabled(m_speculativeTilingEnabled); #endif } static bool shouldEnableSpeculativeTilingDuringLoading(const FrameView& view) { - return view.isVisuallyNonEmpty() && !view.frame().page()->progress().isMainLoadProgressing(); + Page* page = view.frame().page(); + return page && view.isVisuallyNonEmpty() && !page->progress().isMainLoadProgressing(); } void FrameView::enableSpeculativeTilingIfNeeded() @@ -2267,7 +2934,7 @@ void FrameView::enableSpeculativeTilingIfNeeded() m_speculativeTilingEnableTimer.startOneShot(speculativeTilingEnableDelay); } -void FrameView::speculativeTilingEnableTimerFired(Timer<FrameView>&) +void FrameView::speculativeTilingEnableTimerFired() { if (m_speculativeTilingEnabled) return; @@ -2275,11 +2942,30 @@ void FrameView::speculativeTilingEnableTimerFired(Timer<FrameView>&) adjustTiledBackingCoverage(); } -void FrameView::layoutTimerFired(Timer<FrameView>&) +void FrameView::show() { -#ifdef INSTRUMENT_LAYOUT_SCHEDULING + ScrollView::show(); + + if (frame().isMainFrame()) { + // Turn off speculative tiling for a brief moment after a FrameView appears on screen. + // Note that adjustTiledBackingCoverage() kicks the (500ms) timer to re-enable it. + m_speculativeTilingEnabled = false; + m_wasScrolledByUser = false; + adjustTiledBackingCoverage(); + } +} +void FrameView::convertSubtreeLayoutToFullLayout() +{ + ASSERT(m_layoutRoot); + m_layoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No); + m_layoutRoot = nullptr; +} + +void FrameView::layoutTimerFired() +{ +#if !LOG_DISABLED if (!frame().document()->ownerElement()) - printf("Layout timer fired at %lld\n", frame().document()->elapsedTime().count()); + LOG(Layout, "FrameView %p layout timer fired at %.3fs", this, frame().document()->timeSinceDocumentCreation().value()); #endif layout(); } @@ -2290,33 +2976,32 @@ void FrameView::scheduleRelayout() // too many false assertions. See <rdar://problem/7218118>. ASSERT(frame().view() == this); - if (m_layoutRoot) { - m_layoutRoot->markContainingBlocksForLayout(false); - m_layoutRoot = 0; - } + if (m_layoutRoot) + convertSubtreeLayoutToFullLayout(); if (!m_layoutSchedulingEnabled) return; if (!needsLayout()) return; if (!frame().document()->shouldScheduleLayout()) return; - InspectorInstrumentation::didInvalidateLayout(&frame()); + InspectorInstrumentation::didInvalidateLayout(frame()); // When frame flattening is enabled, the contents of the frame could affect the layout of the parent frames. // Also invalidate parent frame starting from the owner element of this frame. if (frame().ownerRenderer() && isInChildFrameWithFrameFlattening()) frame().ownerRenderer()->setNeedsLayout(MarkContainingBlockChain); - std::chrono::milliseconds delay = frame().document()->minimumLayoutDelay(); - if (m_layoutTimer.isActive() && m_delayedLayout && !delay.count()) + Seconds delay = frame().document()->minimumLayoutDelay(); + if (m_layoutTimer.isActive() && m_delayedLayout && !delay) unscheduleRelayout(); + if (m_layoutTimer.isActive()) return; - m_delayedLayout = delay.count(); + m_delayedLayout = delay.value(); -#ifdef INSTRUMENT_LAYOUT_SCHEDULING +#if !LOG_DISABLED if (!frame().document()->ownerElement()) - printf("Scheduling layout for %d\n", delay); + LOG(Layout, "FrameView %p scheduling layout for %.3fs", this, delay.value()); #endif m_layoutTimer.startOneShot(delay); @@ -2334,23 +3019,26 @@ static bool isObjectAncestorContainerOf(RenderObject* ancestor, RenderObject* de void FrameView::scheduleRelayoutOfSubtree(RenderElement& newRelayoutRoot) { ASSERT(renderView()); - RenderView& renderView = *this->renderView(); + const RenderView& renderView = *this->renderView(); // Try to catch unnecessary work during render tree teardown. - ASSERT(!renderView.documentBeingDestroyed()); + ASSERT(!renderView.renderTreeBeingDestroyed()); ASSERT(frame().view() == this); - if (renderView.needsLayout()) { - newRelayoutRoot.markContainingBlocksForLayout(false); + // When m_layoutRoot is already set, ignore the renderView's needsLayout bit + // since we need to resolve the conflict between the m_layoutRoot and newRelayoutRoot layouts. + if (renderView.needsLayout() && !m_layoutRoot) { + m_layoutRoot = &newRelayoutRoot; + convertSubtreeLayoutToFullLayout(); return; } if (!layoutPending() && m_layoutSchedulingEnabled) { - std::chrono::milliseconds delay = renderView.document().minimumLayoutDelay(); - ASSERT(!newRelayoutRoot.container() || !newRelayoutRoot.container()->needsLayout()); + Seconds delay = renderView.document().minimumLayoutDelay(); + ASSERT(!newRelayoutRoot.container() || is<RenderView>(newRelayoutRoot.container()) || !newRelayoutRoot.container()->needsLayout()); m_layoutRoot = &newRelayoutRoot; - InspectorInstrumentation::didInvalidateLayout(&frame()); - m_delayedLayout = delay.count(); + InspectorInstrumentation::didInvalidateLayout(frame()); + m_delayedLayout = delay.value(); m_layoutTimer.startOneShot(delay); return; } @@ -2359,33 +3047,31 @@ void FrameView::scheduleRelayoutOfSubtree(RenderElement& newRelayoutRoot) return; if (!m_layoutRoot) { - // Just relayout the subtree. - newRelayoutRoot.markContainingBlocksForLayout(false); - InspectorInstrumentation::didInvalidateLayout(&frame()); + // We already have a pending (full) layout. Just mark the subtree for layout. + newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No); + InspectorInstrumentation::didInvalidateLayout(frame()); return; } if (isObjectAncestorContainerOf(m_layoutRoot, &newRelayoutRoot)) { // Keep the current root. - newRelayoutRoot.markContainingBlocksForLayout(false, m_layoutRoot); - ASSERT(!m_layoutRoot->container() || !m_layoutRoot->container()->needsLayout()); + newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No, m_layoutRoot); + ASSERT(!m_layoutRoot->container() || is<RenderView>(m_layoutRoot->container()) || !m_layoutRoot->container()->needsLayout()); return; } if (isObjectAncestorContainerOf(&newRelayoutRoot, m_layoutRoot)) { // Re-root at newRelayoutRoot. - m_layoutRoot->markContainingBlocksForLayout(false, &newRelayoutRoot); + m_layoutRoot->markContainingBlocksForLayout(ScheduleRelayout::No, &newRelayoutRoot); m_layoutRoot = &newRelayoutRoot; - ASSERT(!m_layoutRoot->container() || !m_layoutRoot->container()->needsLayout()); - InspectorInstrumentation::didInvalidateLayout(&frame()); + ASSERT(!m_layoutRoot->container() || is<RenderView>(m_layoutRoot->container()) || !m_layoutRoot->container()->needsLayout()); + InspectorInstrumentation::didInvalidateLayout(frame()); return; } - - // Just do a full relayout. - m_layoutRoot->markContainingBlocksForLayout(false); - m_layoutRoot = 0; - newRelayoutRoot.markContainingBlocksForLayout(false); - InspectorInstrumentation::didInvalidateLayout(&frame()); + // Two disjoint subtrees need layout. Mark both of them and issue a full layout instead. + convertSubtreeLayoutToFullLayout(); + newRelayoutRoot.markContainingBlocksForLayout(ScheduleRelayout::No); + InspectorInstrumentation::didInvalidateLayout(frame()); } bool FrameView::layoutPending() const @@ -2393,6 +3079,25 @@ bool FrameView::layoutPending() const return m_layoutTimer.isActive(); } +bool FrameView::needsStyleRecalcOrLayout(bool includeSubframes) const +{ + if (frame().document() && frame().document()->childNeedsStyleRecalc()) + return true; + + if (needsLayout()) + return true; + + if (!includeSubframes) + return false; + + for (auto& frameView : renderedChildFrameViews()) { + if (frameView->needsStyleRecalcOrLayout()) + return true; + } + + return false; +} + bool FrameView::needsLayout() const { // This can return true in cases where the document does not have a body yet. @@ -2402,50 +3107,57 @@ bool FrameView::needsLayout() const return layoutPending() || (renderView && renderView->needsLayout()) || m_layoutRoot - || (m_deferSetNeedsLayouts && m_setNeedsLayoutWasDeferred); + || (m_deferSetNeedsLayoutCount && m_setNeedsLayoutWasDeferred); } void FrameView::setNeedsLayout() { - if (m_deferSetNeedsLayouts) { + if (m_deferSetNeedsLayoutCount) { m_setNeedsLayoutWasDeferred = true; return; } - if (RenderView* renderView = this->renderView()) + if (auto* renderView = this->renderView()) { + ASSERT(!renderView->inHitTesting()); renderView->setNeedsLayout(); + } } void FrameView::unscheduleRelayout() { + if (m_postLayoutTasksTimer.isActive()) + m_postLayoutTasksTimer.stop(); + if (!m_layoutTimer.isActive()) return; -#ifdef INSTRUMENT_LAYOUT_SCHEDULING +#if !LOG_DISABLED if (!frame().document()->ownerElement()) - printf("Layout timer unscheduled at %d\n", frame().document()->elapsedTime()); + LOG(Layout, "FrameView %p layout timer unscheduled at %.3fs", this, frame().document()->timeSinceDocumentCreation().value()); #endif m_layoutTimer.stop(); m_delayedLayout = false; } -#if ENABLE(REQUEST_ANIMATION_FRAME) -void FrameView::serviceScriptedAnimations(double monotonicAnimationStartTime) +void FrameView::serviceScriptedAnimations() { - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext()) { + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext()) { frame->view()->serviceScrollAnimations(); frame->animation().serviceAnimations(); } + if (!frame().document() || !frame().document()->domWindow()) + return; + Vector<RefPtr<Document>> documents; - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext()) + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext()) documents.append(frame->document()); - for (size_t i = 0; i < documents.size(); ++i) - documents[i]->serviceScriptedAnimations(monotonicAnimationStartTime); + double timestamp = frame().document()->domWindow()->nowTimestamp(); + for (auto& document : documents) + document->serviceScriptedAnimations(timestamp); } -#endif bool FrameView::isTransparent() const { @@ -2454,12 +3166,24 @@ bool FrameView::isTransparent() const void FrameView::setTransparent(bool isTransparent) { + if (m_isTransparent == isTransparent) + return; + m_isTransparent = isTransparent; + + // setTransparent can be called in the window between FrameView initialization + // and switching in the new Document; this means that the RenderView that we + // retrieve is actually attached to the previous Document, which is going away, + // and must not update compositing layers. + if (!isViewForDocumentInFrame()) + return; + + renderView()->compositor().rootBackgroundTransparencyChanged(); } bool FrameView::hasOpaqueBackground() const { - return !m_isTransparent && !m_baseBackgroundColor.hasAlpha(); + return !m_isTransparent && m_baseBackgroundColor.isOpaque(); } Color FrameView::baseBackgroundColor() const @@ -2469,17 +3193,25 @@ Color FrameView::baseBackgroundColor() const void FrameView::setBaseBackgroundColor(const Color& backgroundColor) { + bool wasOpaque = m_baseBackgroundColor.isOpaque(); + if (!backgroundColor.isValid()) m_baseBackgroundColor = Color::white; else m_baseBackgroundColor = backgroundColor; + if (!isViewForDocumentInFrame()) + return; + recalculateScrollbarOverlayStyle(); + + if (m_baseBackgroundColor.isOpaque() != wasOpaque) + renderView()->compositor().rootBackgroundTransparencyChanged(); } void FrameView::updateBackgroundRecursively(const Color& backgroundColor, bool transparent) { - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) { if (FrameView* view = frame->view()) { view->setTransparent(transparent); view->setBaseBackgroundColor(backgroundColor); @@ -2487,9 +3219,8 @@ void FrameView::updateBackgroundRecursively(const Color& backgroundColor, bool t } } -bool FrameView::hasExtendedBackground() const +bool FrameView::hasExtendedBackgroundRectForPainting() const { -#if USE(ACCELERATED_COMPOSITING) if (!frame().settings().backgroundShouldExtendBeyondPage()) return false; @@ -2498,31 +3229,64 @@ bool FrameView::hasExtendedBackground() const return false; return tiledBacking->hasMargins(); -#else - return false; -#endif } -IntRect FrameView::extendedBackgroundRect() const +void FrameView::updateExtendBackgroundIfNecessary() { -#if USE(ACCELERATED_COMPOSITING) - TiledBacking* tiledBacking = this->tiledBacking(); - if (!tiledBacking) - return IntRect(); + ExtendedBackgroundMode mode = calculateExtendedBackgroundMode(); + if (mode == ExtendedBackgroundModeNone) + return; - return tiledBacking->bounds(); + updateTilesForExtendedBackgroundMode(mode); +} + +FrameView::ExtendedBackgroundMode FrameView::calculateExtendedBackgroundMode() const +{ + // Just because Settings::backgroundShouldExtendBeyondPage() is true does not necessarily mean + // that the background rect needs to be extended for painting. Simple backgrounds can be extended + // just with RenderLayerCompositor::setRootExtendedBackgroundColor(). More complicated backgrounds, + // such as images, require extending the background rect to continue painting into the extended + // region. This function finds out if it is necessary to extend the background rect for painting. + +#if PLATFORM(IOS) + // <rdar://problem/16201373> + return ExtendedBackgroundModeNone; #else - RenderView* renderView = this->renderView(); - if (!renderView) - return IntRect(); + if (!frame().settings().backgroundShouldExtendBeyondPage()) + return ExtendedBackgroundModeNone; - return renderView->unscaledDocumentRect(); + if (!frame().isMainFrame()) + return ExtendedBackgroundModeNone; + + Document* document = frame().document(); + if (!document) + return ExtendedBackgroundModeNone; + + if (!renderView()) + return ExtendedBackgroundModeNone; + + auto* rootBackgroundRenderer = renderView()->rendererForRootBackground(); + if (!rootBackgroundRenderer) + return ExtendedBackgroundModeNone; + + if (!rootBackgroundRenderer->style().hasBackgroundImage()) + return ExtendedBackgroundModeNone; + + ExtendedBackgroundMode mode = ExtendedBackgroundModeNone; + if (rootBackgroundRenderer->style().backgroundRepeatX() == RepeatFill) + mode |= ExtendedBackgroundModeHorizontal; + if (rootBackgroundRenderer->style().backgroundRepeatY() == RepeatFill) + mode |= ExtendedBackgroundModeVertical; + + return mode; #endif } -#if USE(ACCELERATED_COMPOSITING) -void FrameView::setBackgroundExtendsBeyondPage(bool extendBackground) +void FrameView::updateTilesForExtendedBackgroundMode(ExtendedBackgroundMode mode) { + if (!frame().settings().backgroundShouldExtendBeyondPage()) + return; + RenderView* renderView = this->renderView(); if (!renderView) return; @@ -2531,9 +3295,41 @@ void FrameView::setBackgroundExtendsBeyondPage(bool extendBackground) if (!backing) return; - backing->setTiledBackingHasMargins(extendBackground); + TiledBacking* tiledBacking = backing->graphicsLayer()->tiledBacking(); + if (!tiledBacking) + return; + + ExtendedBackgroundMode existingMode = ExtendedBackgroundModeNone; + if (tiledBacking->hasVerticalMargins()) + existingMode |= ExtendedBackgroundModeVertical; + if (tiledBacking->hasHorizontalMargins()) + existingMode |= ExtendedBackgroundModeHorizontal; + + if (existingMode == mode) + return; + + renderView->compositor().setRootExtendedBackgroundColor(mode == ExtendedBackgroundModeAll ? Color() : documentBackgroundColor()); + backing->setTiledBackingHasMargins(mode & ExtendedBackgroundModeHorizontal, mode & ExtendedBackgroundModeVertical); +} + +IntRect FrameView::extendedBackgroundRectForPainting() const +{ + TiledBacking* tiledBacking = this->tiledBacking(); + if (!tiledBacking) + return IntRect(); + + RenderView* renderView = this->renderView(); + if (!renderView) + return IntRect(); + + LayoutRect extendedRect = renderView->unextendedBackgroundRect(); + if (!tiledBacking->hasMargins()) + return snappedIntRect(extendedRect); + + extendedRect.moveBy(LayoutPoint(-tiledBacking->leftMarginWidth(), -tiledBacking->topMarginHeight())); + extendedRect.expand(LayoutSize(tiledBacking->leftMarginWidth() + tiledBacking->rightMarginWidth(), tiledBacking->topMarginHeight() + tiledBacking->bottomMarginHeight())); + return snappedIntRect(extendedRect); } -#endif bool FrameView::shouldUpdateWhileOffscreen() const { @@ -2545,16 +3341,16 @@ void FrameView::setShouldUpdateWhileOffscreen(bool shouldUpdateWhileOffscreen) m_shouldUpdateWhileOffscreen = shouldUpdateWhileOffscreen; } -bool FrameView::shouldUpdate(bool immediateRequested) const +bool FrameView::shouldUpdate() const { - if (!immediateRequested && isOffscreen() && !shouldUpdateWhileOffscreen()) + if (isOffscreen() && !shouldUpdateWhileOffscreen()) return false; return true; } void FrameView::scrollToAnchor() { - RefPtr<Node> anchorNode = m_maintainScrollPositionAnchor; + RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor; if (!anchorNode) return; @@ -2562,12 +3358,18 @@ void FrameView::scrollToAnchor() return; LayoutRect rect; - if (anchorNode != frame().document()) - rect = anchorNode->boundingBox(); + bool insideFixed = false; + if (anchorNode != frame().document() && anchorNode->renderer()) + rect = anchorNode->renderer()->absoluteAnchorRect(&insideFixed); // Scroll nested layers and frames to reveal the anchor. // Align to the top and to the closest side (this matches other browsers). - anchorNode->renderer()->scrollRectToVisible(rect, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); + if (anchorNode->renderer()->style().isHorizontalWritingMode()) + anchorNode->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, rect, insideFixed, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); + else if (anchorNode->renderer()->style().isFlippedBlocksWritingMode()) + anchorNode->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, rect, insideFixed, ScrollAlignment::alignRightAlways, ScrollAlignment::alignToEdgeIfNeeded); + else + anchorNode->renderer()->scrollRectToVisible(SelectionRevealMode::Reveal, rect, insideFixed, ScrollAlignment::alignLeftAlways, ScrollAlignment::alignToEdgeIfNeeded); if (AXObjectCache* cache = frame().document()->existingAXObjectCache()) cache->handleScrolledToAnchor(anchorNode.get()); @@ -2585,8 +3387,8 @@ void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject) HTMLFrameOwnerElement& element = embeddedObject.frameOwnerElement(); if (embeddedObject.isSnapshottedPlugIn()) { - if (isHTMLObjectElement(element) || isHTMLEmbedElement(element)) { - HTMLPlugInImageElement& pluginElement = toHTMLPlugInImageElement(element); + if (is<HTMLObjectElement>(element) || is<HTMLEmbedElement>(element)) { + HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element); pluginElement.checkSnapshotStatus(); } return; @@ -2596,29 +3398,23 @@ void FrameView::updateEmbeddedObject(RenderEmbeddedObject& embeddedObject) // FIXME: This could turn into a real virtual dispatch if we defined // updateWidget(PluginCreationOption) on HTMLElement. - if (isHTMLObjectElement(element) || isHTMLEmbedElement(element) || isHTMLAppletElement(element)) { - HTMLPlugInImageElement& pluginElement = toHTMLPlugInImageElement(element); + if (is<HTMLPlugInImageElement>(element)) { + HTMLPlugInImageElement& pluginElement = downcast<HTMLPlugInImageElement>(element); if (pluginElement.needsCheckForSizeChange()) { pluginElement.checkSnapshotStatus(); return; } if (pluginElement.needsWidgetUpdate()) - pluginElement.updateWidget(CreateAnyWidgetType); - } - - // FIXME: It is not clear that Media elements need or want this updateWidget() call. -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - else if (element.isMediaElement()) - toHTMLMediaElement(element).updateWidget(CreateAnyWidgetType); -#endif - else + pluginElement.updateWidget(CreatePlugins::Yes); + } else ASSERT_NOT_REACHED(); // It's possible the renderer was destroyed below updateWidget() since loading a plugin may execute arbitrary JavaScript. if (!weakRenderer) return; - embeddedObject.updateWidgetPosition(); + auto ignoreWidgetState = embeddedObject.updateWidgetPosition(); + UNUSED_PARAM(ignoreWidgetState); } bool FrameView::updateEmbeddedObjects() @@ -2642,9 +3438,9 @@ bool FrameView::updateEmbeddedObjects() return m_embeddedObjectsToUpdate->isEmpty(); } -void FrameView::updateEmbeddedObjectsTimerFired(Timer<FrameView>*) +void FrameView::updateEmbeddedObjectsTimerFired() { - RefPtr<FrameView> protect(this); + RefPtr<FrameView> protectedThis(this); m_updateEmbeddedObjectsTimer.stop(); for (unsigned i = 0; i < maxUpdateEmbeddedObjectsIterations; i++) { if (updateEmbeddedObjects()) @@ -2657,57 +3453,55 @@ void FrameView::flushAnyPendingPostLayoutTasks() if (m_postLayoutTasksTimer.isActive()) performPostLayoutTasks(); if (m_updateEmbeddedObjectsTimer.isActive()) - updateEmbeddedObjectsTimerFired(nullptr); + updateEmbeddedObjectsTimerFired(); +} + +void FrameView::queuePostLayoutCallback(Function<void()>&& callback) +{ + m_postLayoutCallbackQueue.append(WTFMove(callback)); +} + +void FrameView::flushPostLayoutTasksQueue() +{ + if (m_nestedLayoutCount > 1) + return; + + if (!m_postLayoutCallbackQueue.size()) + return; + + Vector<Function<void()>> queue = WTFMove(m_postLayoutCallbackQueue); + for (auto& task : queue) + task(); } void FrameView::performPostLayoutTasks() { + LOG(Layout, "FrameView %p performPostLayoutTasks", this); + // FIXME: We should not run any JavaScript code in this function. m_postLayoutTasksTimer.stop(); - frame().selection().setCaretRectNeedsUpdate(); - frame().selection().updateAppearance(); - - LayoutMilestones requestedMilestones = 0; - LayoutMilestones milestonesAchieved = 0; - Page* page = frame().page(); - if (page) - requestedMilestones = page->requestedLayoutMilestones(); + frame().selection().updateAppearanceAfterLayout(); - if (m_nestedLayoutCount <= 1 && frame().document()->documentElement()) { - if (m_firstLayoutCallbackPending) { - m_firstLayoutCallbackPending = false; - frame().loader().didFirstLayout(); - if (requestedMilestones & DidFirstLayout) - milestonesAchieved |= DidFirstLayout; - if (frame().isMainFrame()) - page->startCountingRelevantRepaintedObjects(); - } - updateIsVisuallyNonEmpty(); + flushPostLayoutTasksQueue(); - // If the layout was done with pending sheets, we are not in fact visually non-empty yet. - if (m_isVisuallyNonEmpty && !frame().document()->didLayoutWithPendingStylesheets() && m_firstVisuallyNonEmptyLayoutCallbackPending) { - m_firstVisuallyNonEmptyLayoutCallbackPending = false; - if (requestedMilestones & DidFirstVisuallyNonEmptyLayout) - milestonesAchieved |= DidFirstVisuallyNonEmptyLayout; - } - } + if (m_nestedLayoutCount <= 1 && frame().document()->documentElement()) + fireLayoutRelatedMilestonesIfNeeded(); #if PLATFORM(IOS) // Only send layout-related delegate callbacks synchronously for the main frame to // avoid re-entering layout for the main frame while delivering a layout-related delegate // callback for a subframe. - if (frame().isMainFrame()) - page->chrome().client().didLayout(); + if (frame().isMainFrame()) { + if (Page* page = frame().page()) + page->chrome().client().didLayout(); + } #endif - if (milestonesAchieved && frame().isMainFrame()) - frame().loader().didLayout(milestonesAchieved); - #if ENABLE(FONT_LOAD_EVENTS) if (RuntimeEnabledFeatures::sharedFeatures().fontLoadEventsEnabled()) - frame().document()->fontloader()->didLayout(); + frame().document()->fonts()->didLayout(); #endif // FIXME: We should consider adding DidLayout as a LayoutMilestone. That would let us merge this @@ -2715,51 +3509,73 @@ void FrameView::performPostLayoutTasks() frame().loader().client().dispatchDidLayout(); updateWidgetPositions(); - + +#if ENABLE(CSS_SCROLL_SNAP) + updateSnapOffsets(); +#endif + // layout() protects FrameView, but it still can get destroyed when updateEmbeddedObjects() // is called through the post layout timer. - Ref<FrameView> protect(*this); + Ref<FrameView> protectedThis(*this); m_updateEmbeddedObjectsTimer.startOneShot(0); - if (page) { - if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingCoordinator->frameViewLayoutUpdated(this); + if (auto* page = frame().page()) { + if (auto* scrollingCoordinator = page->scrollingCoordinator()) + scrollingCoordinator->frameViewLayoutUpdated(*this); } -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) { if (renderView->usesCompositing()) renderView->compositor().frameViewDidLayout(); } -#endif scrollToAnchor(); sendResizeEventIfNeeded(); + + updateLayoutViewport(); + viewportContentsChanged(); + + updateScrollSnapState(); + + if (AXObjectCache* cache = frame().document()->existingAXObjectCache()) + cache->performDeferredIsIgnoredChange(); +} + +IntSize FrameView::sizeForResizeEvent() const +{ +#if PLATFORM(IOS) + if (m_useCustomSizeForResizeEvent) + return m_customSizeForResizeEvent; +#endif + if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling()) + return fixedLayoutSize(); + return visibleContentRectIncludingScrollbars().size(); } void FrameView::sendResizeEventIfNeeded() { + if (isInRenderTreeLayout() || needsLayout()) + return; + RenderView* renderView = this->renderView(); if (!renderView || renderView->printing()) return; + if (frame().page() && frame().page()->chrome().client().isSVGImageChromeClient()) return; - IntSize currentSize; - if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling()) - currentSize = fixedLayoutSize(); - else - currentSize = visibleContentRectIncludingScrollbars().size(); - + IntSize currentSize = sizeForResizeEvent(); float currentZoomFactor = renderView->style().zoom(); - bool shouldSendResizeEvent = !m_firstLayout && (currentSize != m_lastViewportSize || currentZoomFactor != m_lastZoomFactor); + + if (currentSize == m_lastViewportSize && currentZoomFactor == m_lastZoomFactor) + return; m_lastViewportSize = currentSize; m_lastZoomFactor = currentZoomFactor; - if (!shouldSendResizeEvent) + if (m_firstLayout) return; #if PLATFORM(IOS) @@ -2773,22 +3589,24 @@ void FrameView::sendResizeEventIfNeeded() #endif bool isMainFrame = frame().isMainFrame(); - bool canSendResizeEventSynchronously = !m_shouldAutoSize && isMainFrame && !isInLayout(); + bool canSendResizeEventSynchronously = isMainFrame && !m_shouldAutoSize; - // If we resized during layout, queue up a resize event for later, otherwise fire it right away. - RefPtr<Event> resizeEvent = Event::create(eventNames().resizeEvent, false, false); + Ref<Event> resizeEvent = Event::create(eventNames().resizeEvent, false, false); if (canSendResizeEventSynchronously) - frame().document()->dispatchWindowEvent(resizeEvent.release(), frame().document()->domWindow()); - else - frame().document()->enqueueWindowEvent(resizeEvent.release()); + frame().document()->dispatchWindowEvent(resizeEvent); + else { + // FIXME: Queueing this event for an unpredictable time in the future seems + // intrinsically racy. By the time this resize event fires, the frame might + // be resized again, so we could end up with two resize events for the same size. + frame().document()->enqueueWindowEvent(WTFMove(resizeEvent)); + } -#if ENABLE(INSPECTOR) - Page* page = frame().page(); if (InspectorInstrumentation::hasFrontends() && isMainFrame) { - if (InspectorClient* inspectorClient = page ? page->inspectorController().inspectorClient() : nullptr) - inspectorClient->didResizeMainFrame(&frame()); + if (Page* page = frame().page()) { + if (InspectorClient* inspectorClient = page->inspectorController().inspectorClient()) + inspectorClient->didResizeMainFrame(&frame()); + } } -#endif } void FrameView::willStartLiveResize() @@ -2803,11 +3621,6 @@ void FrameView::willEndLiveResize() adjustTiledBackingCoverage(); } -void FrameView::postLayoutTimerFired(Timer<FrameView>&) -{ - performPostLayoutTasks(); -} - void FrameView::autoSizeIfEnabled() { if (!m_shouldAutoSize) @@ -2816,7 +3629,9 @@ void FrameView::autoSizeIfEnabled() if (m_inAutoSize) return; - TemporaryChange<bool> changeInAutoSize(m_inAutoSize, true); + LOG(Layout, "FrameView %p autoSizeIfEnabled", this); + + SetForScope<bool> changeInAutoSize(m_inAutoSize, true); Document* document = frame().document(); if (!document) @@ -2827,6 +3642,8 @@ void FrameView::autoSizeIfEnabled() if (!documentView || !documentElement) return; + if (m_layoutRoot) + convertSubtreeLayoutToFullLayout(); // Start from the minimum size and allow it to grow. resize(m_minAutoSize.width(), m_minAutoSize.height()); @@ -2849,8 +3666,7 @@ void FrameView::autoSizeIfEnabled() RefPtr<Scrollbar> localHorizontalScrollbar = horizontalScrollbar(); if (!localHorizontalScrollbar) localHorizontalScrollbar = createScrollbar(HorizontalScrollbar); - if (!localHorizontalScrollbar->isOverlayScrollbar()) - newSize.setHeight(newSize.height() + localHorizontalScrollbar->height()); + newSize.expand(0, localHorizontalScrollbar->occupiedHeight()); // Don't bother checking for a vertical scrollbar because the width is at // already greater the maximum. @@ -2858,8 +3674,7 @@ void FrameView::autoSizeIfEnabled() RefPtr<Scrollbar> localVerticalScrollbar = verticalScrollbar(); if (!localVerticalScrollbar) localVerticalScrollbar = createScrollbar(VerticalScrollbar); - if (!localVerticalScrollbar->isOverlayScrollbar()) - newSize.setWidth(newSize.width() + localVerticalScrollbar->width()); + newSize.expand(localVerticalScrollbar->occupiedWidth(), 0); // Don't bother checking for a horizontal scrollbar because the height is // already greater the maximum. @@ -2889,7 +3704,10 @@ void FrameView::autoSizeIfEnabled() && !frame().loader().isComplete() && (newSize.height() < size.height() || newSize.width() < size.width())) break; - resize(newSize.width(), newSize.height()); + // The first time around, resize to the minimum height again; otherwise, + // on pages (e.g. quirks mode) where the body/document resize to the view size, + // we'll end up not shrinking back down after resizing to the computed preferred width. + resize(newSize.width(), i ? newSize.height() : m_minAutoSize.height()); // Force the scrollbar state to avoid the scrollbar code adding them and causing them to be needed. For example, // a vertical scrollbar may cause text to wrap and thus increase the height (which is the only reason the scollbar is needed). setVerticalScrollbarLock(false); @@ -2897,6 +3715,9 @@ void FrameView::autoSizeIfEnabled() setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true); } + // All the resizing above may have invalidated style (for example if viewport units are being used). + document->updateStyleIfNeeded(); + m_autoSizeContentSize = contentsSize(); if (m_autoSizeFixedMinimumHeight) { @@ -2917,9 +3738,37 @@ void FrameView::setAutoSizeFixedMinimumHeight(int fixedMinimumHeight) setNeedsLayout(); } +RenderElement* FrameView::viewportRenderer() const +{ + if (m_viewportRendererType == ViewportRendererType::None) + return nullptr; + + auto* document = frame().document(); + if (!document) + return nullptr; + + if (m_viewportRendererType == ViewportRendererType::Document) { + auto* documentElement = document->documentElement(); + if (!documentElement) + return nullptr; + return documentElement->renderer(); + } + + if (m_viewportRendererType == ViewportRendererType::Body) { + auto* body = document->body(); + if (!body) + return nullptr; + return body->renderer(); + } + + ASSERT_NOT_REACHED(); + return nullptr; +} + void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow) { - if (!m_viewportRenderer) + auto* viewportRenderer = this->viewportRenderer(); + if (!viewportRenderer) return; if (m_overflowStatusDirty) { @@ -2936,13 +3785,12 @@ void FrameView::updateOverflowStatus(bool horizontalOverflow, bool verticalOverf m_horizontalOverflow = horizontalOverflow; m_verticalOverflow = verticalOverflow; - RefPtr<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, + Ref<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow); - overflowEvent->setTarget(m_viewportRenderer->element()); + overflowEvent->setTarget(viewportRenderer->element()); - frame().document()->enqueueOverflowEvent(overflowEvent.release()); + frame().document()->enqueueOverflowEvent(WTFMove(overflowEvent)); } - } const Pagination& FrameView::pagination() const @@ -2950,8 +3798,10 @@ const Pagination& FrameView::pagination() const if (m_pagination != Pagination()) return m_pagination; - if (frame().isMainFrame()) - return frame().page()->pagination(); + if (frame().isMainFrame()) { + if (Page* page = frame().page()) + return page->pagination(); + } return m_pagination; } @@ -2963,22 +3813,22 @@ void FrameView::setPagination(const Pagination& pagination) m_pagination = pagination; - frame().document()->styleResolverChanged(DeferRecalcStyle); + frame().document()->styleScope().didChangeStyleSheetEnvironment(); } -IntRect FrameView::windowClipRect(bool clipToContents) const +IntRect FrameView::windowClipRect() const { ASSERT(frame().view() == this); + if (m_cachedWindowClipRect) + return *m_cachedWindowClipRect; + if (paintsEntireContents()) - return IntRect(IntPoint(), totalContentsSize()); + return contentsToWindow(IntRect(IntPoint(), totalContentsSize())); // Set our clip rect to be our contents. - IntRect clipRect; - if (clipToContents) - clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect)); - else - clipRect = contentsToWindow(visibleContentRectIncludingScrollbars(LegacyIOSDocumentVisibleRect)); + IntRect clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect)); + if (!frame().ownerElement()) return clipRect; @@ -3004,9 +3854,9 @@ IntRect FrameView::windowClipRectForFrameOwner(const HTMLFrameOwnerElement* owne // Apply the clip from the layer. IntRect clipRect; if (clipToLayerContents) - clipRect = pixelSnappedIntRect(enclosingLayer->childrenClipRect()); + clipRect = snappedIntRect(enclosingLayer->childrenClipRect()); else - clipRect = pixelSnappedIntRect(enclosingLayer->selfClipRect()); + clipRect = snappedIntRect(enclosingLayer->selfClipRect()); clipRect = contentsToWindow(clipRect); return intersection(clipRect, windowClipRect()); } @@ -3017,46 +3867,79 @@ bool FrameView::isActive() const return page && page->focusController().isActive(); } -bool FrameView::updatesScrollLayerPositionOnMainThread() const +bool FrameView::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const { - if (Page* page = frame().page()) { - if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - return scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(); - } + Page* page = frame().page(); + return page && page->settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting(); +} - return true; +void FrameView::scrollTo(const ScrollPosition& newPosition) +{ + IntPoint oldPosition = scrollPosition(); + ScrollView::scrollTo(newPosition); + if (oldPosition != scrollPosition()) + scrollPositionChanged(oldPosition, scrollPosition()); + + didChangeScrollOffset(); } -void FrameView::scrollTo(const IntSize& newOffset) +float FrameView::adjustScrollStepForFixedContent(float step, ScrollbarOrientation orientation, ScrollGranularity granularity) { - LayoutSize offset = scrollOffset(); - ScrollView::scrollTo(newOffset); - if (offset != scrollOffset()) - scrollPositionChanged(); - frame().loader().client().didChangeScrollOffset(); + if (granularity != ScrollByPage || orientation == HorizontalScrollbar) + return step; + + TrackedRendererListHashSet* positionedObjects = nullptr; + if (RenderView* root = frame().contentRenderer()) { + if (!root->hasPositionedObjects()) + return step; + positionedObjects = root->positionedObjects(); + } + + FloatRect unobscuredContentRect = this->unobscuredContentRect(); + float topObscuredArea = 0; + float bottomObscuredArea = 0; + for (const auto& positionedObject : *positionedObjects) { + const RenderStyle& style = positionedObject->style(); + if (style.position() != FixedPosition || style.visibility() == HIDDEN || !style.opacity()) + continue; + + FloatQuad contentQuad = positionedObject->absoluteContentQuad(); + if (!contentQuad.isRectilinear()) + continue; + + FloatRect contentBoundingBox = contentQuad.boundingBox(); + FloatRect fixedRectInView = intersection(unobscuredContentRect, contentBoundingBox); + + if (fixedRectInView.width() < unobscuredContentRect.width()) + continue; + + if (fixedRectInView.y() == unobscuredContentRect.y()) + topObscuredArea = std::max(topObscuredArea, fixedRectInView.height()); + else if (fixedRectInView.maxY() == unobscuredContentRect.maxY()) + bottomObscuredArea = std::max(bottomObscuredArea, fixedRectInView.height()); + } + + return Scrollbar::pageStep(unobscuredContentRect.height(), unobscuredContentRect.height() - topObscuredArea - bottomObscuredArea); } -void FrameView::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +void FrameView::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect) { // Add in our offset within the FrameView. IntRect dirtyRect = rect; - dirtyRect.moveBy(scrollbar->location()); + dirtyRect.moveBy(scrollbar.location()); invalidateRect(dirtyRect); } -IntRect FrameView::windowResizerRect() const -{ - if (Page* page = frame().page()) - return page->chrome().windowResizerRect(); - return IntRect(); -} - float FrameView::visibleContentScaleFactor() const { if (!frame().isMainFrame() || !frame().settings().delegatesPageScaling()) return 1; - return frame().page()->pageScaleFactor(); + Page* page = frame().page(); + if (!page) + return 1; + + return page->pageScaleFactor(); } void FrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb) @@ -3064,16 +3947,17 @@ void FrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb) if (!frame().isMainFrame()) return; - frame().page()->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb); + if (Page* page = frame().page()) + page->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb); } ScrollableArea* FrameView::enclosingScrollableArea() const { // FIXME: Walk up the frame tree and look for a scrollable parent frame or RenderLayer. - return 0; + return nullptr; } -IntRect FrameView::scrollableAreaBoundingBox() const +IntRect FrameView::scrollableAreaBoundingBox(bool*) const { RenderWidget* ownerRenderer = frame().ownerRenderer(); if (!ownerRenderer) @@ -3082,7 +3966,7 @@ IntRect FrameView::scrollableAreaBoundingBox() const return ownerRenderer->absoluteContentQuad().enclosingBoundingBox(); } -bool FrameView::isScrollable() +bool FrameView::isScrollable(Scrollability definitionOfScrollable) { // Check for: // 1) If there an actual overflow. @@ -3090,11 +3974,18 @@ bool FrameView::isScrollable() // 3) overflow{-x,-y}: hidden; // 4) scrolling: no; + bool requiresActualOverflowToBeConsideredScrollable = !frame().isMainFrame() || definitionOfScrollable != Scrollability::ScrollableOrRubberbandable; +#if !ENABLE(RUBBER_BANDING) + requiresActualOverflowToBeConsideredScrollable = true; +#endif + // Covers #1 - IntSize totalContentsSize = this->totalContentsSize(); - IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size(); - if ((totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width())) - return false; + if (requiresActualOverflowToBeConsideredScrollable) { + IntSize totalContentsSize = this->totalContentsSize(); + IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size(); + if (totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width()) + return false; + } // Covers #2. HTMLFrameOwnerElement* owner = frame().ownerElement(); @@ -3111,6 +4002,25 @@ bool FrameView::isScrollable() return true; } +bool FrameView::isScrollableOrRubberbandable() +{ + return isScrollable(Scrollability::ScrollableOrRubberbandable); +} + +bool FrameView::hasScrollableOrRubberbandableAncestor() +{ + if (frame().isMainFrame()) + return isScrollableOrRubberbandable(); + + for (FrameView* parent = this->parentFrameView(); parent; parent = parent->parentFrameView()) { + Scrollability frameScrollability = parent->frame().isMainFrame() ? Scrollability::ScrollableOrRubberbandable : Scrollability::Scrollable; + if (parent->isScrollable(frameScrollability)) + return true; + } + + return false; +} + void FrameView::updateScrollableAreaSet() { // That ensures that only inner frames are cached. @@ -3131,15 +4041,15 @@ bool FrameView::shouldSuspendScrollAnimations() const return frame().loader().state() != FrameStateComplete; } -void FrameView::scrollbarStyleChanged(int newStyle, bool forceUpdate) +void FrameView::scrollbarStyleChanged(ScrollbarStyle newStyle, bool forceUpdate) { if (!frame().isMainFrame()) return; - frame().page()->chrome().client().recommendedScrollbarStyleDidChange(newStyle); + if (Page* page = frame().page()) + page->chrome().client().recommendedScrollbarStyleDidChange(newStyle); - if (forceUpdate) - ScrollView::scrollbarStyleChanged(newStyle, forceUpdate); + ScrollView::scrollbarStyleChanged(newStyle, forceUpdate); } void FrameView::notifyPageThatContentAreaWillPaint() const @@ -3187,14 +4097,14 @@ void FrameView::updateAnnotatedRegions() void FrameView::updateScrollCorner() { - RenderElement* renderer = 0; - RefPtr<RenderStyle> cornerStyle; + RenderElement* renderer = nullptr; + std::unique_ptr<RenderStyle> cornerStyle; IntRect cornerRect = scrollCornerRect(); if (!cornerRect.isEmpty()) { // Try the <body> element first as a scroll corner source. Document* doc = frame().document(); - Element* body = doc ? doc->body() : 0; + Element* body = doc ? doc->bodyOrFrameset() : nullptr; if (body && body->renderer()) { renderer = body->renderer(); cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &renderer->style()); @@ -3202,7 +4112,7 @@ void FrameView::updateScrollCorner() if (!cornerStyle) { // If the <body> didn't have a custom style, then the root element might. - Element* docElement = doc ? doc->documentElement() : 0; + Element* docElement = doc ? doc->documentElement() : nullptr; if (docElement && docElement->renderer()) { renderer = docElement->renderer(); cornerStyle = renderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &renderer->style()); @@ -3220,26 +4130,24 @@ void FrameView::updateScrollCorner() m_scrollCorner = nullptr; else { if (!m_scrollCorner) { - m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer->document(), cornerStyle.releaseNonNull()); + m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer->document(), WTFMove(*cornerStyle)); m_scrollCorner->initializeStyle(); } else - m_scrollCorner->setStyle(cornerStyle.releaseNonNull()); + m_scrollCorner->setStyle(WTFMove(*cornerStyle)); invalidateScrollCorner(cornerRect); } - - ScrollView::updateScrollCorner(); } -void FrameView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect) +void FrameView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect) { - if (context->updatingControlTints()) { + if (context.updatingControlTints()) { updateScrollCorner(); return; } if (m_scrollCorner) { if (frame().isMainFrame()) - context->fillRect(cornerRect, baseBackgroundColor(), ColorSpaceDeviceRGB); + context.fillRect(cornerRect, baseBackgroundColor()); m_scrollCorner->paintIntoRect(context, cornerRect.location(), cornerRect); return; } @@ -3247,12 +4155,12 @@ void FrameView::paintScrollCorner(GraphicsContext* context, const IntRect& corne ScrollView::paintScrollCorner(context, cornerRect); } -void FrameView::paintScrollbar(GraphicsContext* context, Scrollbar* bar, const IntRect& rect) +void FrameView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect) { - if (bar->isCustomScrollbar() && frame().isMainFrame()) { - IntRect toFill = bar->frameRect(); + if (bar.isCustomScrollbar() && frame().isMainFrame()) { + IntRect toFill = bar.frameRect(); toFill.intersect(rect); - context->fillRect(toFill, baseBackgroundColor(), ColorSpaceDeviceRGB); + context.fillRect(toFill, baseBackgroundColor()); } ScrollView::paintScrollbar(context, bar, rect); @@ -3268,8 +4176,8 @@ Color FrameView::documentBackgroundColor() const if (!frame().document()) return Color(); - Element* htmlElement = frame().document()->documentElement(); - Element* bodyElement = frame().document()->body(); + auto* htmlElement = frame().document()->documentElement(); + auto* bodyElement = frame().document()->bodyOrFrameset(); // Start with invalid colors. Color htmlBackgroundColor; @@ -3301,46 +4209,48 @@ Color FrameView::documentBackgroundColor() const bool FrameView::hasCustomScrollbars() const { for (auto& widget : children()) { - if (widget->isFrameView()) { - if (toFrameView(*widget).hasCustomScrollbars()) + if (is<FrameView>(widget.get())) { + if (downcast<FrameView>(widget.get()).hasCustomScrollbars()) return true; - } else if (widget->isScrollbar()) { - if (toScrollbar(*widget).isCustomScrollbar()) + } else if (is<Scrollbar>(widget.get())) { + if (downcast<Scrollbar>(widget.get()).isCustomScrollbar()) return true; } } - return false; } FrameView* FrameView::parentFrameView() const { if (!parent()) - return 0; - - if (Frame* parentFrame = frame().tree().parent()) - return parentFrame->view(); - - return 0; + return nullptr; + auto* parentFrame = frame().tree().parent(); + if (!parentFrame) + return nullptr; + return parentFrame->view(); } bool FrameView::isInChildFrameWithFrameFlattening() const { - if (!parent() || !frame().ownerElement()) + if (!frameFlatteningEnabled()) return false; - // Frame flattening applies when the owner element is either in a frameset or - // an iframe with flattening parameters. - if (frame().ownerElement()->hasTagName(iframeTag)) { - RenderIFrame* iframeRenderer = toRenderIFrame(frame().ownerElement()->renderWidget()); - if (iframeRenderer->flattenFrame() || iframeRenderer->isSeamless()) - return true; - } + if (!parent()) + return false; - if (!frameFlatteningEnabled()) + HTMLFrameOwnerElement* ownerElement = frame().ownerElement(); + if (!ownerElement) + return false; + + if (!ownerElement->renderWidget()) return false; - if (frame().ownerElement()->hasTagName(frameTag)) + // Frame flattening applies when the owner element is either in a frameset or + // an iframe with flattening parameters. + if (is<HTMLIFrameElement>(*ownerElement)) + return downcast<RenderIFrame>(*ownerElement->renderWidget()).flattenFrame(); + + if (is<HTMLFrameElement>(*ownerElement)) return true; return false; @@ -3366,10 +4276,8 @@ void FrameView::startLayoutAtMainFrameViewIfNeeded(bool allowSubtree) while (parentView->parentFrameView()) parentView = parentView->parentFrameView(); + LOG(Layout, " frame flattening, starting from root"); parentView->layout(allowSubtree); - - RenderElement* root = m_layoutRoot ? m_layoutRoot : frame().document()->renderView(); - ASSERT_UNUSED(root, !root->needsLayout()); } void FrameView::updateControlTints() @@ -3383,23 +4291,33 @@ void FrameView::updateControlTints() if (frame().document()->url().isEmpty()) return; + // As noted above, this is a "fake" paint, so we should pause counting relevant repainted objects. + Page* page = frame().page(); + bool isCurrentlyCountingRelevantRepaintedObject = false; + if (page) { + isCurrentlyCountingRelevantRepaintedObject = page->isCountingRelevantRepaintedObjects(); + page->setIsCountingRelevantRepaintedObjects(false); + } + RenderView* renderView = this->renderView(); if ((renderView && renderView->theme().supportsControlTints()) || hasCustomScrollbars()) paintControlTints(); + + if (page) + page->setIsCountingRelevantRepaintedObjects(isCurrentlyCountingRelevantRepaintedObject); } void FrameView::paintControlTints() { if (needsLayout()) layout(); - PlatformGraphicsContext* const noContext = 0; - GraphicsContext context(noContext); - context.setUpdatingControlTints(true); + + GraphicsContext context(GraphicsContext::NonPaintingReasons::UpdatingControlTints); if (platformWidget()) { // FIXME: consult paintsEntireContents(). - paintContents(&context, visibleContentRect(LegacyIOSDocumentVisibleRect)); + paintContents(context, visibleContentRect(LegacyIOSDocumentVisibleRect)); } else - paint(&context, frameRect()); + paint(context, frameRect()); } bool FrameView::wasScrolledByUser() const @@ -3411,134 +4329,141 @@ void FrameView::setWasScrolledByUser(bool wasScrolledByUser) { if (m_inProgrammaticScroll) return; - m_maintainScrollPositionAnchor = 0; + m_maintainScrollPositionAnchor = nullptr; if (m_wasScrolledByUser == wasScrolledByUser) return; m_wasScrolledByUser = wasScrolledByUser; + if (frame().isMainFrame()) + updateLayerFlushThrottling(); adjustTiledBackingCoverage(); } -void FrameView::paintContents(GraphicsContext* p, const IntRect& rect) +void FrameView::willPaintContents(GraphicsContext& context, const IntRect&, PaintingState& paintingState) { Document* document = frame().document(); -#ifndef NDEBUG - bool fillWithRed; - if (document->printing()) - fillWithRed = false; // Printing, don't fill with red (can't remember why). - else if (frame().ownerElement()) - fillWithRed = false; // Subframe, don't fill with red. - else if (isTransparent()) - fillWithRed = false; // Transparent, don't fill with red. - else if (m_paintBehavior & PaintBehaviorSelectionOnly) - fillWithRed = false; // Selections are transparent, don't fill with red. - else if (m_nodeToDraw) - fillWithRed = false; // Element images are transparent, don't fill with red. - else - fillWithRed = true; - - if (fillWithRed) - p->fillRect(rect, Color(0xFF, 0, 0), ColorSpaceDeviceRGB); -#endif - - RenderView* renderView = this->renderView(); - if (!renderView) { - LOG_ERROR("called FrameView::paint with nil renderer"); - return; - } - - ASSERT(!needsLayout()); - if (needsLayout()) - return; + if (!context.paintingDisabled()) + InspectorInstrumentation::willPaint(*renderView()); - if (!p->paintingDisabled()) - InspectorInstrumentation::willPaint(renderView); + paintingState.isTopLevelPainter = !sCurrentPaintTimeStamp; - bool isTopLevelPainter = !sCurrentPaintTimeStamp; -#if PLATFORM(IOS) - // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to MemoryPressureHandler.h. - if (isTopLevelPainter && memoryPressureHandler().hasReceivedMemoryPressure()) { - LOG(MemoryPressure, "Under memory pressure: %s", __PRETTY_FUNCTION__); + if (paintingState.isTopLevelPainter && MemoryPressureHandler::singleton().isUnderMemoryPressure()) { + LOG(MemoryPressure, "Under memory pressure: %s", WTF_PRETTY_FUNCTION); // To avoid unnecessary image decoding, we don't prune recently-decoded live resources here since // we might need some live bitmaps on painting. - memoryCache()->prune(); + MemoryCache::singleton().prune(); } -#endif - if (isTopLevelPainter) - sCurrentPaintTimeStamp = monotonicallyIncreasingTime(); - - FontCachePurgePreventer fontCachePurgePreventer; -#if USE(ACCELERATED_COMPOSITING) - if (!p->paintingDisabled() && !document->printing()) - flushCompositingStateForThisFrame(&frame()); -#endif + if (paintingState.isTopLevelPainter) + sCurrentPaintTimeStamp = monotonicallyIncreasingTime(); - PaintBehavior oldPaintBehavior = m_paintBehavior; + paintingState.paintBehavior = m_paintBehavior; if (FrameView* parentView = parentFrameView()) { if (parentView->paintBehavior() & PaintBehaviorFlattenCompositingLayers) m_paintBehavior |= PaintBehaviorFlattenCompositingLayers; } - - if (m_paintBehavior == PaintBehaviorNormal) - document->markers().invalidateRenderedRectsForMarkersInRect(rect); if (document->printing()) m_paintBehavior |= PaintBehaviorFlattenCompositingLayers; - bool flatteningPaint = m_paintBehavior & PaintBehaviorFlattenCompositingLayers; - bool isRootFrame = !frame().ownerElement(); - if (flatteningPaint && isRootFrame) + paintingState.isFlatteningPaintOfRootFrame = (m_paintBehavior & PaintBehaviorFlattenCompositingLayers) && !frame().ownerElement(); + if (paintingState.isFlatteningPaintOfRootFrame) notifyWidgetsInAllFrames(WillPaintFlattened); ASSERT(!m_isPainting); m_isPainting = true; +} - // m_nodeToDraw is used to draw only one element (and its descendants) - RenderObject* eltRenderer = m_nodeToDraw ? m_nodeToDraw->renderer() : 0; - RenderLayer* rootLayer = renderView->layer(); - -#ifndef NDEBUG - RenderElement::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(&rootLayer->renderer()); -#endif - - rootLayer->paint(p, rect, m_paintBehavior, eltRenderer); - - if (rootLayer->containsDirtyOverlayScrollbars()) - rootLayer->paintOverlayScrollbars(p, rect, m_paintBehavior, eltRenderer); - +void FrameView::didPaintContents(GraphicsContext& context, const IntRect& dirtyRect, PaintingState& paintingState) +{ m_isPainting = false; - if (flatteningPaint && isRootFrame) + if (paintingState.isFlatteningPaintOfRootFrame) notifyWidgetsInAllFrames(DidPaintFlattened); - m_paintBehavior = oldPaintBehavior; + m_paintBehavior = paintingState.paintBehavior; m_lastPaintTime = monotonicallyIncreasingTime(); -#if PLATFORM(IOS) // Painting can lead to decoding of large amounts of bitmaps // If we are low on memory, wipe them out after the paint. - // FIXME: Remove PLATFORM(IOS)-guard once we upstream the iOS changes to MemoryPressureHandler.h. - if (isTopLevelPainter && memoryPressureHandler().hasReceivedMemoryPressure()) - memoryCache()->pruneLiveResources(true); -#endif + if (paintingState.isTopLevelPainter && MemoryPressureHandler::singleton().isUnderMemoryPressure()) + MemoryCache::singleton().pruneLiveResources(true); // Regions may have changed as a result of the visibility/z-index of element changing. #if ENABLE(DASHBOARD_SUPPORT) - if (document->annotatedRegionsDirty()) + if (frame().document()->annotatedRegionsDirty()) updateAnnotatedRegions(); #endif - if (isTopLevelPainter) + if (paintingState.isTopLevelPainter) sCurrentPaintTimeStamp = 0; - if (!p->paintingDisabled()) { - InspectorInstrumentation::didPaint(renderView, p, rect); + if (!context.paintingDisabled()) { + InspectorInstrumentation::didPaint(*renderView(), dirtyRect); // FIXME: should probably not fire milestones for snapshot painting. https://bugs.webkit.org/show_bug.cgi?id=117623 - firePaintRelatedMilestones(); + firePaintRelatedMilestonesIfNeeded(); + } +} + +void FrameView::paintContents(GraphicsContext& context, const IntRect& dirtyRect, SecurityOriginPaintPolicy securityOriginPaintPolicy) +{ +#ifndef NDEBUG + bool fillWithRed; + if (frame().document()->printing()) + fillWithRed = false; // Printing, don't fill with red (can't remember why). + else if (frame().ownerElement()) + fillWithRed = false; // Subframe, don't fill with red. + else if (isTransparent()) + fillWithRed = false; // Transparent, don't fill with red. + else if (m_paintBehavior & PaintBehaviorSelectionOnly) + fillWithRed = false; // Selections are transparent, don't fill with red. + else if (m_nodeToDraw) + fillWithRed = false; // Element images are transparent, don't fill with red. + else + fillWithRed = true; + + if (fillWithRed) + context.fillRect(dirtyRect, Color(0xFF, 0, 0)); +#endif + + RenderView* renderView = this->renderView(); + if (!renderView) { + LOG_ERROR("called FrameView::paint with nil renderer"); + return; } + + if (!inPaintableState()) + return; + + TraceScope tracingScope(PaintViewStart, PaintViewEnd); + + ASSERT(!needsLayout()); + if (needsLayout()) + return; + + PaintingState paintingState; + willPaintContents(context, dirtyRect, paintingState); + + // m_nodeToDraw is used to draw only one element (and its descendants) + RenderObject* renderer = m_nodeToDraw ? m_nodeToDraw->renderer() : nullptr; + RenderLayer* rootLayer = renderView->layer(); + +#ifndef NDEBUG + RenderElement::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(&rootLayer->renderer()); +#endif + + // To work around http://webkit.org/b/135106, ensure that the paint root isn't an inline with culled line boxes. + // FIXME: This can cause additional content to be included in the snapshot, so remove this once that bug is fixed. + while (is<RenderInline>(renderer) && !downcast<RenderInline>(*renderer).firstLineBox()) + renderer = renderer->parent(); + + rootLayer->paint(context, dirtyRect, LayoutSize(), m_paintBehavior, renderer, 0, securityOriginPaintPolicy == SecurityOriginPaintPolicy::AnyOrigin ? RenderLayer::SecurityOriginPaintPolicy::AnyOrigin : RenderLayer::SecurityOriginPaintPolicy::AccessibleOriginOnly); + if (rootLayer->containsDirtyOverlayScrollbars()) + rootLayer->paintOverlayScrollbars(context, dirtyRect, m_paintBehavior, renderer); + + didPaintContents(context, dirtyRect, paintingState); } void FrameView::setPaintBehavior(PaintBehavior behavior) @@ -3562,7 +4487,7 @@ void FrameView::setNodeToDraw(Node* node) m_nodeToDraw = node; } -void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace) +void FrameView::paintContentsForSnapshot(GraphicsContext& context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace) { updateLayoutAndStyleIfNeededRecursive(); @@ -3574,7 +4499,7 @@ void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect // in the render tree only. This will allow us to restore the selection from the DOM // after we paint the snapshot. if (shouldPaintSelection == ExcludeSelection) { - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) { if (RenderView* root = frame->contentRenderer()) root->clearSelection(); } @@ -3590,7 +4515,7 @@ void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect // Restore selection. if (shouldPaintSelection == ExcludeSelection) { - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) frame->selection().updateAppearance(); } @@ -3598,9 +4523,9 @@ void FrameView::paintContentsForSnapshot(GraphicsContext* context, const IntRect setPaintBehavior(oldBehavior); } -void FrameView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect) +void FrameView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect) { - if (context->paintingDisabled()) + if (context.paintingDisabled()) return; if (frame().document()->printing()) @@ -3609,6 +4534,17 @@ void FrameView::paintOverhangAreas(GraphicsContext* context, const IntRect& hori ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect); } +FrameView::FrameViewList FrameView::renderedChildFrameViews() const +{ + FrameViewList childViews; + for (Frame* frame = m_frame->tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) { + if (frame->view()) + childViews.append(*frame->view()); + } + + return childViews; +} + void FrameView::updateLayoutAndStyleIfNeededRecursive() { // We have to crawl our entire tree looking for any FrameViews that need @@ -3620,28 +4556,24 @@ void FrameView::updateLayoutAndStyleIfNeededRecursive() // region but then become included later by the second frame adding rects to the dirty region // when it lays out. + AnimationUpdateBlock animationUpdateBlock(&frame().animation()); + frame().document()->updateStyleIfNeeded(); if (needsLayout()) layout(); - // Grab a copy of the children() set, as it may be mutated by the following updateLayoutAndStyleIfNeededRecursive - // calls, as they can potentially re-enter a layout of the parent frame view, which may add/remove scrollbars - // and thus mutates the children() set. - Vector<Ref<FrameView>, 16> childViews; - childViews.reserveInitialCapacity(children().size()); - for (auto& widget : children()) { - if (widget->isFrameView()) - childViews.uncheckedAppend(toFrameView(*widget)); - } + // Grab a copy of the child views, as the list may be mutated by the following updateLayoutAndStyleIfNeededRecursive + // calls, as they can potentially re-enter a layout of the parent frame view. + for (auto& frameView : renderedChildFrameViews()) + frameView->updateLayoutAndStyleIfNeededRecursive(); - for (unsigned i = 0; i < childViews.size(); ++i) - childViews[i]->updateLayoutAndStyleIfNeededRecursive(); + // A child frame may have dirtied us during its layout. + frame().document()->updateStyleIfNeeded(); + if (needsLayout()) + layout(); - // When frame flattening is on, child frame can mark parent frame dirty. In such case, child frame - // needs to call layout on parent frame recursively. - // This assert ensures that parent frames are clean, when child frames finished updating layout and style. - ASSERT(!needsLayout()); + ASSERT(!frame().isMainFrame() || !needsStyleRecalcOrLayout()); } bool FrameView::qualifiesAsVisuallyNonEmpty() const @@ -3652,12 +4584,14 @@ bool FrameView::qualifiesAsVisuallyNonEmpty() const return false; // Ensure that we always get marked visually non-empty eventually. - if (!frame().document()->parsing() && frame().loader().stateMachine()->committedFirstRealDocumentLoad()) + if (!frame().document()->parsing() && frame().loader().stateMachine().committedFirstRealDocumentLoad()) return true; // Require the document to grow a bit. - static const int documentHeightThreshold = 200; - if (documentElement->renderBox()->layoutOverflowRect().pixelSnappedHeight() < documentHeightThreshold) + // Using a value of 48 allows the header on Google's search page to render immediately before search results populate later. + static const int documentHeightThreshold = 48; + LayoutRect overflowRect = documentElement->renderBox()->layoutOverflowRect(); + if (snappedIntRect(overflowRect).height() < documentHeightThreshold) return false; // The first few hundred characters rarely contain the interesting content of the page. @@ -3679,6 +4613,15 @@ void FrameView::updateIsVisuallyNonEmpty() adjustTiledBackingCoverage(); } +bool FrameView::isViewForDocumentInFrame() const +{ + RenderView* renderView = this->renderView(); + if (!renderView) + return false; + + return &renderView->frameView() == this; +} + void FrameView::enableAutoSizeMode(bool enable, const IntSize& minSize, const IntSize& maxSize) { ASSERT(!enable || !minSize.isEmpty()); @@ -3717,10 +4660,8 @@ void FrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatS float pageLogicalWidth = renderView->style().isHorizontalWritingMode() ? pageSize.width() : pageSize.height(); float pageLogicalHeight = renderView->style().isHorizontalWritingMode() ? pageSize.height() : pageSize.width(); - LayoutUnit flooredPageLogicalWidth = static_cast<LayoutUnit>(pageLogicalWidth); - LayoutUnit flooredPageLogicalHeight = static_cast<LayoutUnit>(pageLogicalHeight); - renderView->setLogicalWidth(flooredPageLogicalWidth); - renderView->setPageLogicalHeight(flooredPageLogicalHeight); + renderView->setLogicalWidth(floor(pageLogicalWidth)); + renderView->setPageLogicalHeight(floor(pageLogicalHeight)); renderView->setNeedsLayoutAndPrefWidthsRecalc(); forceLayout(); @@ -3738,10 +4679,8 @@ void FrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatS pageLogicalWidth = horizontalWritingMode ? maxPageSize.width() : maxPageSize.height(); pageLogicalHeight = horizontalWritingMode ? maxPageSize.height() : maxPageSize.width(); - flooredPageLogicalWidth = static_cast<LayoutUnit>(pageLogicalWidth); - flooredPageLogicalHeight = static_cast<LayoutUnit>(pageLogicalHeight); - renderView->setLogicalWidth(flooredPageLogicalWidth); - renderView->setPageLogicalHeight(flooredPageLogicalHeight); + renderView->setLogicalWidth(floor(pageLogicalWidth)); + renderView->setPageLogicalHeight(floor(pageLogicalHeight)); renderView->setNeedsLayoutAndPrefWidthsRecalc(); forceLayout(); @@ -3774,35 +4713,34 @@ void FrameView::adjustPageHeightDeprecated(float *newBottom, float oldTop, float } // Use a context with painting disabled. - GraphicsContext context((PlatformGraphicsContext*)0); + GraphicsContext context((PlatformGraphicsContext*)nullptr); renderView->setTruncatedAt(static_cast<int>(floorf(oldBottom))); IntRect dirtyRect(0, static_cast<int>(floorf(oldTop)), renderView->layoutOverflowRect().maxX(), static_cast<int>(ceilf(oldBottom - oldTop))); renderView->setPrintRect(dirtyRect); - renderView->layer()->paint(&context, dirtyRect); + renderView->layer()->paint(context, dirtyRect); *newBottom = renderView->bestTruncatedAt(); if (!*newBottom) *newBottom = oldBottom; renderView->setPrintRect(IntRect()); } -IntRect FrameView::convertFromRenderer(const RenderElement* renderer, const IntRect& rendererRect) const +IntRect FrameView::convertFromRendererToContainingView(const RenderElement* renderer, const IntRect& rendererRect) const { - IntRect rect = pixelSnappedIntRect(enclosingLayoutRect(renderer->localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox())); + IntRect rect = snappedIntRect(enclosingLayoutRect(renderer->localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox())); - // Convert from page ("absolute") to FrameView coordinates. if (!delegatesScrolling()) - rect.moveBy(-scrollPosition() + IntPoint(0, headerHeight())); + rect = contentsToView(rect); return rect; } -IntRect FrameView::convertToRenderer(const RenderElement* renderer, const IntRect& viewRect) const +IntRect FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const IntRect& viewRect) const { IntRect rect = viewRect; // Convert from FrameView coords into page ("absolute") coordinates. if (!delegatesScrolling()) - rect.moveBy(scrollPositionRelativeToDocument()); + rect = viewToContents(rect); // FIXME: we don't have a way to map an absolute rect down to a local quad, so just // move the rect for now. @@ -3810,23 +4748,24 @@ IntRect FrameView::convertToRenderer(const RenderElement* renderer, const IntRec return rect; } -IntPoint FrameView::convertFromRenderer(const RenderElement* renderer, const IntPoint& rendererPoint) const +IntPoint FrameView::convertFromRendererToContainingView(const RenderElement* renderer, const IntPoint& rendererPoint) const { IntPoint point = roundedIntPoint(renderer->localToAbsolute(rendererPoint, UseTransforms)); // Convert from page ("absolute") to FrameView coordinates. if (!delegatesScrolling()) - point.moveBy(-scrollPosition() + IntPoint(0, headerHeight())); + point = contentsToView(point); + return point; } -IntPoint FrameView::convertToRenderer(const RenderElement* renderer, const IntPoint& viewPoint) const +IntPoint FrameView::convertFromContainingViewToRenderer(const RenderElement* renderer, const IntPoint& viewPoint) const { IntPoint point = viewPoint; // Convert from FrameView coords into page ("absolute") coordinates. if (!delegatesScrolling()) - point = point + scrollPositionRelativeToDocument(); + point = viewToContents(point); return roundedIntPoint(renderer->absoluteToLocal(point, UseTransforms)); } @@ -3834,8 +4773,8 @@ IntPoint FrameView::convertToRenderer(const RenderElement* renderer, const IntPo IntRect FrameView::convertToContainingView(const IntRect& localRect) const { if (const ScrollView* parentScrollView = parent()) { - if (parentScrollView->isFrameView()) { - const FrameView* parentView = toFrameView(parentScrollView); + if (is<FrameView>(*parentScrollView)) { + const FrameView& parentView = downcast<FrameView>(*parentScrollView); // Get our renderer in the parent view RenderWidget* renderer = frame().ownerRenderer(); if (!renderer) @@ -3845,7 +4784,7 @@ IntRect FrameView::convertToContainingView(const IntRect& localRect) const // Add borders and padding?? rect.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); - return parentView->convertFromRenderer(renderer, rect); + return parentView.convertFromRendererToContainingView(renderer, rect); } return Widget::convertToContainingView(localRect); @@ -3857,15 +4796,15 @@ IntRect FrameView::convertToContainingView(const IntRect& localRect) const IntRect FrameView::convertFromContainingView(const IntRect& parentRect) const { if (const ScrollView* parentScrollView = parent()) { - if (parentScrollView->isFrameView()) { - const FrameView* parentView = toFrameView(parentScrollView); + if (is<FrameView>(*parentScrollView)) { + const FrameView& parentView = downcast<FrameView>(*parentScrollView); // Get our renderer in the parent view RenderWidget* renderer = frame().ownerRenderer(); if (!renderer) return parentRect; - IntRect rect = parentView->convertToRenderer(renderer, parentRect); + IntRect rect = parentView.convertFromContainingViewToRenderer(renderer, parentRect); // Subtract borders and padding rect.move(-renderer->borderLeft() - renderer->paddingLeft(), -renderer->borderTop() - renderer->paddingTop()); @@ -3881,8 +4820,8 @@ IntRect FrameView::convertFromContainingView(const IntRect& parentRect) const IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const { if (const ScrollView* parentScrollView = parent()) { - if (parentScrollView->isFrameView()) { - const FrameView* parentView = toFrameView(parentScrollView); + if (is<FrameView>(*parentScrollView)) { + const FrameView& parentView = downcast<FrameView>(*parentScrollView); // Get our renderer in the parent view RenderWidget* renderer = frame().ownerRenderer(); @@ -3894,7 +4833,7 @@ IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const // Add borders and padding point.move(renderer->borderLeft() + renderer->paddingLeft(), renderer->borderTop() + renderer->paddingTop()); - return parentView->convertFromRenderer(renderer, point); + return parentView.convertFromRendererToContainingView(renderer, point); } return Widget::convertToContainingView(localPoint); @@ -3906,15 +4845,15 @@ IntPoint FrameView::convertToContainingView(const IntPoint& localPoint) const IntPoint FrameView::convertFromContainingView(const IntPoint& parentPoint) const { if (const ScrollView* parentScrollView = parent()) { - if (parentScrollView->isFrameView()) { - const FrameView* parentView = toFrameView(parentScrollView); + if (is<FrameView>(*parentScrollView)) { + const FrameView& parentView = downcast<FrameView>(*parentScrollView); // Get our renderer in the parent view RenderWidget* renderer = frame().ownerRenderer(); if (!renderer) return parentPoint; - IntPoint point = parentView->convertToRenderer(renderer, parentPoint); + IntPoint point = parentView.convertFromContainingViewToRenderer(renderer, parentPoint); // Subtract borders and padding point.move(-renderer->borderLeft() - renderer->paddingLeft(), -renderer->borderTop() - renderer->paddingTop()); @@ -3938,12 +4877,10 @@ void FrameView::setTracksRepaints(bool trackRepaints) frame().document()->updateLayout(); } -#if USE(ACCELERATED_COMPOSITING) for (Frame* frame = &m_frame->tree().top(); frame; frame = frame->tree().traverseNext()) { if (RenderView* renderView = frame->contentRenderer()) renderView->compositor().setTracksRepaints(trackRepaints); } -#endif resetTrackedRepaints(); m_isTrackingRepaints = trackRepaints; @@ -3952,10 +4889,8 @@ void FrameView::setTracksRepaints(bool trackRepaints) void FrameView::resetTrackedRepaints() { m_trackedRepaintRects.clear(); -#if USE(ACCELERATED_COMPOSITING) if (RenderView* renderView = this->renderView()) renderView->compositor().resetTrackedRepaintRects(); -#endif } String FrameView::trackedRepaintRectsAsText() const @@ -3966,8 +4901,8 @@ String FrameView::trackedRepaintRectsAsText() const TextStream ts; if (!m_trackedRepaintRects.isEmpty()) { ts << "(repaint rects\n"; - for (size_t i = 0; i < m_trackedRepaintRects.size(); ++i) - ts << " (rect " << m_trackedRepaintRects[i].x() << " " << m_trackedRepaintRects[i].y() << " " << m_trackedRepaintRects[i].width() << " " << m_trackedRepaintRects[i].height() << ")\n"; + for (auto& rect : m_trackedRepaintRects) + ts << " (rect " << LayoutUnit(rect.x()) << " " << LayoutUnit(rect.y()) << " " << LayoutUnit(rect.width()) << " " << LayoutUnit(rect.height()) << ")\n"; ts << ")\n"; } return ts.release(); @@ -3976,13 +4911,23 @@ String FrameView::trackedRepaintRectsAsText() const bool FrameView::addScrollableArea(ScrollableArea* scrollableArea) { if (!m_scrollableAreas) - m_scrollableAreas = adoptPtr(new ScrollableAreaSet); - return m_scrollableAreas->add(scrollableArea).isNewEntry; + m_scrollableAreas = std::make_unique<ScrollableAreaSet>(); + + if (m_scrollableAreas->add(scrollableArea).isNewEntry) { + scrollableAreaSetChanged(); + return true; + } + + return false; } bool FrameView::removeScrollableArea(ScrollableArea* scrollableArea) { - return m_scrollableAreas && m_scrollableAreas->remove(scrollableArea); + if (m_scrollableAreas && m_scrollableAreas->remove(scrollableArea)) { + scrollableAreaSetChanged(); + return true; + } + return false; } bool FrameView::containsScrollableArea(ScrollableArea* scrollableArea) const @@ -3990,10 +4935,27 @@ bool FrameView::containsScrollableArea(ScrollableArea* scrollableArea) const return m_scrollableAreas && m_scrollableAreas->contains(scrollableArea); } -void FrameView::removeChild(Widget* widget) +void FrameView::scrollableAreaSetChanged() +{ + if (auto* page = frame().page()) { + if (auto* scrollingCoordinator = page->scrollingCoordinator()) + scrollingCoordinator->frameViewEventTrackingRegionsChanged(*this); + } +} + +void FrameView::sendScrollEvent() +{ + frame().eventHandler().sendScrollEvent(); + frame().eventHandler().dispatchFakeMouseMoveEventSoon(); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + frame().animation().scrollWasUpdated(); +#endif +} + +void FrameView::removeChild(Widget& widget) { - if (widget->isFrameView()) - removeScrollableArea(toFrameView(widget)); + if (is<FrameView>(widget)) + removeScrollableArea(&downcast<FrameView>(widget)); ScrollView::removeChild(widget); } @@ -4008,12 +4970,12 @@ bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent) #endif if (delegatesScrolling()) { - IntSize offset = scrollOffset(); - IntSize newOffset = IntSize(offset.width() - wheelEvent.deltaX(), offset.height() - wheelEvent.deltaY()); - if (offset != newOffset) { - ScrollView::scrollTo(newOffset); - scrollPositionChanged(); - frame().loader().client().didChangeScrollOffset(); + ScrollPosition oldPosition = scrollPosition(); + ScrollPosition newPosition = oldPosition - IntSize(wheelEvent.deltaX(), wheelEvent.deltaY()); + if (oldPosition != newPosition) { + ScrollView::scrollTo(newPosition); + scrollPositionChanged(oldPosition, scrollPosition()); + didChangeScrollOffset(); } return true; } @@ -4028,8 +4990,8 @@ bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent) #if ENABLE(ASYNC_SCROLLING) if (Page* page = frame().page()) { if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) { - if (scrollingCoordinator->coordinatesScrollingForFrameView(this)) - return scrollingCoordinator->handleWheelEvent(this, wheelEvent); + if (scrollingCoordinator->coordinatesScrollingForFrameView(*this)) + return scrollingCoordinator->handleWheelEvent(*this, wheelEvent); } } #endif @@ -4058,7 +5020,7 @@ bool FrameView::isFlippedDocument() const void FrameView::notifyWidgetsInAllFrames(WidgetNotification notification) { - for (Frame* frame = m_frame.get(); frame; frame = frame->tree().traverseNext(m_frame.get())) { + for (auto* frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) { if (FrameView* view = frame->view()) view->notifyWidgets(notification); } @@ -4068,16 +5030,13 @@ AXObjectCache* FrameView::axObjectCache() const { if (frame().document()) return frame().document()->existingAXObjectCache(); - return 0; + return nullptr; } - + #if PLATFORM(IOS) -void FrameView::setUseCustomFixedPositionLayoutRect(bool useCustomFixedPositionLayoutRect) +bool FrameView::useCustomFixedPositionLayoutRect() const { - if (m_useCustomFixedPositionLayoutRect == useCustomFixedPositionLayoutRect) - return; - m_useCustomFixedPositionLayoutRect = useCustomFixedPositionLayoutRect; - visibleContentsResized(); + return !frame().settings().visualViewportEnabled() && m_useCustomFixedPositionLayoutRect; } void FrameView::setCustomFixedPositionLayoutRect(const IntRect& rect) @@ -4086,7 +5045,7 @@ void FrameView::setCustomFixedPositionLayoutRect(const IntRect& rect) return; m_useCustomFixedPositionLayoutRect = true; m_customFixedPositionLayoutRect = rect; - visibleContentsResized(); + updateContentsSize(); } bool FrameView::updateFixedPositionLayoutRect() @@ -4106,21 +5065,33 @@ bool FrameView::updateFixedPositionLayoutRect() } return false; } + +void FrameView::setCustomSizeForResizeEvent(IntSize customSize) +{ + m_useCustomSizeForResizeEvent = true; + m_customSizeForResizeEvent = customSize; + sendResizeEventIfNeeded(); +} + +void FrameView::setScrollVelocity(double horizontalVelocity, double verticalVelocity, double scaleChangeRate, MonotonicTime timestamp) +{ + if (TiledBacking* tiledBacking = this->tiledBacking()) + tiledBacking->setVelocity(VelocityData(horizontalVelocity, verticalVelocity, scaleChangeRate, timestamp)); +} #endif // PLATFORM(IOS) void FrameView::setScrollingPerformanceLoggingEnabled(bool flag) { -#if USE(ACCELERATED_COMPOSITING) if (TiledBacking* tiledBacking = this->tiledBacking()) tiledBacking->setScrollingPerformanceLoggingEnabled(flag); -#else - UNUSED_PARAM(flag); -#endif } void FrameView::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation) { ScrollableArea::didAddScrollbar(scrollbar, orientation); + Page* page = frame().page(); + if (page && page->expectsWheelEventTriggers()) + scrollAnimator().setWheelEventTestTrigger(page->testTrigger()); if (AXObjectCache* cache = axObjectCache()) cache->handleScrollbarUpdate(this); } @@ -4139,7 +5110,36 @@ void FrameView::addPaintPendingMilestones(LayoutMilestones milestones) m_milestonesPendingPaint |= milestones; } -void FrameView::firePaintRelatedMilestones() +void FrameView::fireLayoutRelatedMilestonesIfNeeded() +{ + LayoutMilestones requestedMilestones = 0; + LayoutMilestones milestonesAchieved = 0; + Page* page = frame().page(); + if (page) + requestedMilestones = page->requestedLayoutMilestones(); + + if (m_firstLayoutCallbackPending) { + m_firstLayoutCallbackPending = false; + frame().loader().didFirstLayout(); + if (requestedMilestones & DidFirstLayout) + milestonesAchieved |= DidFirstLayout; + if (frame().isMainFrame()) + page->startCountingRelevantRepaintedObjects(); + } + updateIsVisuallyNonEmpty(); + + // If the layout was done with pending sheets, we are not in fact visually non-empty yet. + if (m_isVisuallyNonEmpty && !frame().document()->didLayoutWithPendingStylesheets() && m_firstVisuallyNonEmptyLayoutCallbackPending) { + m_firstVisuallyNonEmptyLayoutCallbackPending = false; + if (requestedMilestones & DidFirstVisuallyNonEmptyLayout) + milestonesAchieved |= DidFirstVisuallyNonEmptyLayout; + } + + if (milestonesAchieved && frame().isMainFrame()) + frame().loader().didReachLayoutMilestone(milestonesAchieved); +} + +void FrameView::firePaintRelatedMilestonesIfNeeded() { Page* page = frame().page(); if (!page) @@ -4161,7 +5161,7 @@ void FrameView::firePaintRelatedMilestones() m_milestonesPendingPaint = 0; if (milestonesAchieved) - page->mainFrame().loader().didLayout(milestonesAchieved); + page->mainFrame().loader().didReachLayoutMilestone(milestonesAchieved); } void FrameView::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowed) @@ -4178,10 +5178,12 @@ void FrameView::setScrollPinningBehavior(ScrollPinningBehavior pinning) { m_scrollPinningBehavior = pinning; - if (ScrollingCoordinator* scrollingCoordinator = frame().page()->scrollingCoordinator()) - scrollingCoordinator->setScrollPinningBehavior(pinning); + if (Page* page = frame().page()) { + if (auto* scrollingCoordinator = page->scrollingCoordinator()) + scrollingCoordinator->setScrollPinningBehavior(pinning); + } - updateScrollbars(scrollOffset()); + updateScrollbars(scrollPosition()); } ScrollBehaviorForFixedElements FrameView::scrollBehaviorForFixedElements() const @@ -4228,36 +5230,75 @@ void FrameView::updateWidgetPositions() // updateWidgetPosition() can possibly cause layout to be re-entered (via plug-ins running // scripts in response to NPP_SetWindow, for example), so we need to keep the Widgets // alive during enumeration. - auto protectedWidgets = collectAndProtectWidgets(m_widgetsInRenderTree); - - for (unsigned i = 0, size = protectedWidgets.size(); i < size; ++i) { - if (RenderWidget* renderWidget = RenderWidget::find(protectedWidgets[i].get())) - renderWidget->updateWidgetPosition(); + for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree)) { + if (auto* renderer = RenderWidget::find(*widget)) { + auto ignoreWidgetState = renderer->updateWidgetPosition(); + UNUSED_PARAM(ignoreWidgetState); + } } } void FrameView::notifyWidgets(WidgetNotification notification) { - auto protectedWidgets = collectAndProtectWidgets(m_widgetsInRenderTree); - - for (unsigned i = 0, size = protectedWidgets.size(); i < size; ++i) - protectedWidgets[i]->notifyWidget(notification); + for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree)) + widget->notifyWidget(notification); } -void FrameView::setExposedRect(FloatRect exposedRect) +void FrameView::setViewExposedRect(std::optional<FloatRect> viewExposedRect) { - if (m_exposedRect == exposedRect) + if (m_viewExposedRect == viewExposedRect) return; - m_exposedRect = exposedRect; + LOG_WITH_STREAM(Scrolling, stream << "FrameView " << this << " setViewExposedRect " << (viewExposedRect ? viewExposedRect.value() : FloatRect())); + + bool hasRectChanged = !m_viewExposedRect == !viewExposedRect; + m_viewExposedRect = viewExposedRect; -#if USE(ACCELERATED_COMPOSITING) // FIXME: We should support clipping to the exposed rect for subframes as well. - if (m_frame->isMainFrame()) { - if (TiledBacking* tiledBacking = this->tiledBacking()) - tiledBacking->setExposedRect(exposedRect); + if (!frame().isMainFrame()) + return; + + if (TiledBacking* tiledBacking = this->tiledBacking()) { + if (hasRectChanged) + updateTiledBackingAdaptiveSizing(); + adjustTiledBackingCoverage(); + tiledBacking->setTiledScrollingIndicatorPosition(m_viewExposedRect ? m_viewExposedRect.value().location() : FloatPoint()); } -#endif + + if (auto* view = renderView()) + view->compositor().scheduleLayerFlush(false /* canThrottle */); + + frame().mainFrame().pageOverlayController().didChangeViewExposedRect(); +} + +void FrameView::setViewportSizeForCSSViewportUnits(IntSize size) +{ + if (m_hasOverrideViewportSize && m_overrideViewportSize == size) + return; + + m_overrideViewportSize = size; + m_hasOverrideViewportSize = true; + + if (Document* document = frame().document()) + document->styleScope().didChangeStyleSheetEnvironment(); +} + +IntSize FrameView::viewportSizeForCSSViewportUnits() const +{ + if (m_hasOverrideViewportSize) + return m_overrideViewportSize; + + if (useFixedLayout()) + return fixedLayoutSize(); + + // FIXME: the value returned should take into account the value of the overflow + // property on the root element. + return visibleContentRectIncludingScrollbars().size(); } +bool FrameView::shouldPlaceBlockDirectionScrollbarOnLeft() const +{ + return renderView() && renderView()->shouldPlaceBlockDirectionScrollbarOnLeft(); +} + } // namespace WebCore diff --git a/Source/WebCore/page/FrameView.h b/Source/WebCore/page/FrameView.h index 8ece6e51d..ecd097604 100644 --- a/Source/WebCore/page/FrameView.h +++ b/Source/WebCore/page/FrameView.h @@ -4,7 +4,7 @@ (C) 1998, 1999 Torben Weis (weis@kde.org) (C) 1999 Lars Knoll (knoll@kde.org) (C) 1999 Antti Koivisto (koivisto@kde.org) - Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + Copyright (C) 2004-2017 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -22,20 +22,23 @@ Boston, MA 02110-1301, USA. */ -#ifndef FrameView_h -#define FrameView_h +#pragma once #include "AdjustViewSizeOrNot.h" #include "Color.h" +#include "ContainerNode.h" #include "LayoutMilestones.h" #include "LayoutRect.h" #include "Pagination.h" #include "PaintPhase.h" #include "RenderPtr.h" #include "ScrollView.h" +#include "TiledBacking.h" +#include <memory> #include <wtf/Forward.h> +#include <wtf/Function.h> +#include <wtf/HashSet.h> #include <wtf/ListHashSet.h> -#include <wtf/OwnPtr.h> #include <wtf/text/WTFString.h> namespace WebCore { @@ -67,23 +70,21 @@ public: friend class RenderView; friend class Internals; - static PassRefPtr<FrameView> create(Frame&); - static PassRefPtr<FrameView> create(Frame&, const IntSize& initialSize); + WEBCORE_EXPORT static Ref<FrameView> create(Frame&); + static Ref<FrameView> create(Frame&, const IntSize& initialSize); virtual ~FrameView(); - virtual HostWindow* hostWindow() const override; + HostWindow* hostWindow() const final; - virtual void invalidateRect(const IntRect&) override; - virtual void setFrameRect(const IntRect&) override; + WEBCORE_EXPORT void invalidateRect(const IntRect&) final; + void setFrameRect(const IntRect&) final; -#if ENABLE(REQUEST_ANIMATION_FRAME) - virtual bool scheduleAnimation() override; -#endif + bool scheduleAnimation() final; - Frame& frame() const { return *m_frame; } + Frame& frame() const { return m_frame; } - RenderView* renderView() const; + WEBCORE_EXPORT RenderView* renderView() const; int mapFromLayoutToCSSUnits(LayoutUnit) const; LayoutUnit mapFromCSSToLayoutUnits(int) const; @@ -93,156 +94,199 @@ public: void setMarginWidth(LayoutUnit); void setMarginHeight(LayoutUnit); - virtual void setCanHaveScrollbars(bool) override; - void updateCanHaveScrollbars(); + WEBCORE_EXPORT void setCanHaveScrollbars(bool) final; + WEBCORE_EXPORT void updateCanHaveScrollbars(); - virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarOrientation) override; + Ref<Scrollbar> createScrollbar(ScrollbarOrientation) final; - virtual bool avoidScrollbarCreation() const override; + bool avoidScrollbarCreation() const final; - virtual void setContentsSize(const IntSize&) override; + void setContentsSize(const IntSize&) final; + void updateContentsSize() final; void layout(bool allowSubtree = true); - bool didFirstLayout() const; - void layoutTimerFired(Timer<FrameView>&); + WEBCORE_EXPORT bool didFirstLayout() const; + void layoutTimerFired(); void scheduleRelayout(); void scheduleRelayoutOfSubtree(RenderElement&); void unscheduleRelayout(); + void queuePostLayoutCallback(WTF::Function<void ()>&&); bool layoutPending() const; - bool isInLayout() const { return m_layoutPhase == InLayout; } + bool isInLayout() const { return m_layoutPhase != OutsideLayout; } + bool isInRenderTreeLayout() const { return m_layoutPhase == InRenderTreeLayout; } + bool inPaintableState() { return m_layoutPhase != InRenderTreeLayout && m_layoutPhase != InViewSizeAdjust && m_layoutPhase != InPostLayout; } - RenderObject* layoutRoot(bool onlyDuringLayout = false) const; + RenderElement* layoutRoot() const { return m_layoutRoot; } void clearLayoutRoot() { m_layoutRoot = nullptr; } int layoutCount() const { return m_layoutCount; } - bool needsLayout() const; - void setNeedsLayout(); + WEBCORE_EXPORT bool needsLayout() const; + WEBCORE_EXPORT void setNeedsLayout(); void setViewportConstrainedObjectsNeedLayout(); + bool needsStyleRecalcOrLayout(bool includeSubframes = true) const; + bool needsFullRepaint() const { return m_needsFullRepaint; } - bool renderedCharactersExceed(unsigned threshold); + WEBCORE_EXPORT bool renderedCharactersExceed(unsigned threshold); #if PLATFORM(IOS) - bool useCustomFixedPositionLayoutRect() const { return m_useCustomFixedPositionLayoutRect; } - void setUseCustomFixedPositionLayoutRect(bool); + bool useCustomFixedPositionLayoutRect() const; IntRect customFixedPositionLayoutRect() const { return m_customFixedPositionLayoutRect; } - void setCustomFixedPositionLayoutRect(const IntRect&); + WEBCORE_EXPORT void setCustomFixedPositionLayoutRect(const IntRect&); bool updateFixedPositionLayoutRect(); -#endif -#if ENABLE(REQUEST_ANIMATION_FRAME) - void serviceScriptedAnimations(double monotonicAnimationStartTime); + IntSize customSizeForResizeEvent() const { return m_customSizeForResizeEvent; } + WEBCORE_EXPORT void setCustomSizeForResizeEvent(IntSize); + + WEBCORE_EXPORT void setScrollVelocity(double horizontalVelocity, double verticalVelocity, double scaleChangeRate, MonotonicTime timestamp); +#else + bool useCustomFixedPositionLayoutRect() const { return false; } #endif -#if USE(ACCELERATED_COMPOSITING) - void updateCompositingLayersAfterStyleChange(); + WEBCORE_EXPORT void serviceScriptedAnimations(); + + void willRecalcStyle(); + bool updateCompositingLayersAfterStyleChange(); void updateCompositingLayersAfterLayout(); - bool flushCompositingStateForThisFrame(Frame* rootFrameForFlush); void clearBackingStores(); - void restoreBackingStores(); // Called when changes to the GraphicsLayer hierarchy have to be synchronized with // content rendered via the normal painting path. void setNeedsOneShotDrawingSynchronization(); - GraphicsLayer* graphicsLayerForPlatformWidget(PlatformWidget); - void scheduleLayerFlushAllowingThrottling(); + WEBCORE_EXPORT GraphicsLayer* graphicsLayerForPlatformWidget(PlatformWidget); + WEBCORE_EXPORT void scheduleLayerFlushAllowingThrottling(); - virtual TiledBacking* tiledBacking() const override; + WEBCORE_EXPORT TiledBacking* tiledBacking() const final; // In the future when any ScrollableArea can have a node in th ScrollingTree, this should // become a virtual function on ScrollableArea. uint64_t scrollLayerID() const; -#endif + ScrollableArea* scrollableAreaForScrollLayerID(uint64_t) const; bool hasCompositedContent() const; - bool hasCompositedContentIncludingDescendants() const; - bool hasCompositingAncestor() const; - void enterCompositingMode(); - bool isEnclosedInCompositingLayer() const; + WEBCORE_EXPORT void enterCompositingMode(); + WEBCORE_EXPORT bool isEnclosedInCompositingLayer() const; // Only used with accelerated compositing, but outside the #ifdef to make linkage easier. // Returns true if the flush was completed. - bool flushCompositingStateIncludingSubframes(); + WEBCORE_EXPORT bool flushCompositingStateIncludingSubframes(); // Returns true when a paint with the PaintBehaviorFlattenCompositingLayers flag set gives // a faithful representation of the content. - bool isSoftwareRenderable() const; + WEBCORE_EXPORT bool isSoftwareRenderable() const; - void didMoveOnscreen(); - void willMoveOffscreen(); void setIsInWindow(bool); void resetScrollbars(); void resetScrollbarsAndClearContentsSize(); void prepareForDetach(); void detachCustomScrollbars(); - void recalculateScrollbarOverlayStyle(); + WEBCORE_EXPORT void recalculateScrollbarOverlayStyle(); void clear(); - bool isTransparent() const; - void setTransparent(bool isTransparent); + WEBCORE_EXPORT bool isTransparent() const; + WEBCORE_EXPORT void setTransparent(bool isTransparent); // True if the FrameView is not transparent, and the base background color is opaque. bool hasOpaqueBackground() const; - Color baseBackgroundColor() const; - void setBaseBackgroundColor(const Color&); + WEBCORE_EXPORT Color baseBackgroundColor() const; + WEBCORE_EXPORT void setBaseBackgroundColor(const Color&); void updateBackgroundRecursively(const Color&, bool); - // extendedBackgroundRect() is in the viewport's coordinate space. - bool hasExtendedBackground() const; - IntRect extendedBackgroundRect() const; - -#if USE(ACCELERATED_COMPOSITING) - void setBackgroundExtendsBeyondPage(bool); -#endif + enum ExtendedBackgroundModeFlags { + ExtendedBackgroundModeNone = 0, + ExtendedBackgroundModeVertical = 1 << 0, + ExtendedBackgroundModeHorizontal = 1 << 1, + ExtendedBackgroundModeAll = ExtendedBackgroundModeVertical | ExtendedBackgroundModeHorizontal, + }; + typedef unsigned ExtendedBackgroundMode; + + void updateExtendBackgroundIfNecessary(); + void updateTilesForExtendedBackgroundMode(ExtendedBackgroundMode); + ExtendedBackgroundMode calculateExtendedBackgroundMode() const; + + bool hasExtendedBackgroundRectForPainting() const; + IntRect extendedBackgroundRectForPainting() const; bool shouldUpdateWhileOffscreen() const; - void setShouldUpdateWhileOffscreen(bool); - bool shouldUpdate(bool = false) const; + WEBCORE_EXPORT void setShouldUpdateWhileOffscreen(bool); + bool shouldUpdate() const; - void adjustViewSize(); + WEBCORE_EXPORT void adjustViewSize(); - virtual IntRect windowClipRect(bool clipToContents = true) const override; - IntRect windowClipRectForFrameOwner(const HTMLFrameOwnerElement*, bool clipToLayerContents) const; - - virtual IntRect windowResizerRect() const override; + WEBCORE_EXPORT void setViewportSizeForCSSViewportUnits(IntSize); + IntSize viewportSizeForCSSViewportUnits() const; + + IntRect windowClipRect() const final; + WEBCORE_EXPORT IntRect windowClipRectForFrameOwner(const HTMLFrameOwnerElement*, bool clipToLayerContents) const; - virtual float visibleContentScaleFactor() const override; + float visibleContentScaleFactor() const final; -#if USE(TILED_BACKING_STORE) - virtual void setFixedVisibleContentRect(const IntRect&) override; +#if USE(COORDINATED_GRAPHICS) + void setFixedVisibleContentRect(const IntRect&) final; #endif - virtual void setScrollPosition(const IntPoint&) override; - void scrollPositionChangedViaPlatformWidget(); - virtual void updateLayerPositionsAfterScrolling() override; - virtual void updateCompositingLayersAfterScrolling() override; - virtual bool requestScrollPositionUpdate(const IntPoint&) override; - virtual bool isRubberBandInProgress() const override; - virtual IntPoint minimumScrollPosition() const override; - virtual IntPoint maximumScrollPosition() const override; + WEBCORE_EXPORT void setScrollPosition(const ScrollPosition&) final; + void updateLayerPositionsAfterScrolling() final; + void updateCompositingLayersAfterScrolling() final; + bool requestScrollPositionUpdate(const ScrollPosition&) final; + bool isRubberBandInProgress() const final; + WEBCORE_EXPORT ScrollPosition minimumScrollPosition() const final; + WEBCORE_EXPORT ScrollPosition maximumScrollPosition() const final; + + // The scrollOrigin, scrollPosition, minimumScrollPosition and maximumScrollPosition are all affected by frame scale, + // but layoutViewport computations require unscaled scroll positions. + ScrollPosition unscaledMinimumScrollPosition() const; + ScrollPosition unscaledMaximumScrollPosition() const; + + IntPoint unscaledScrollOrigin() const; + + WEBCORE_EXPORT LayoutPoint minStableLayoutViewportOrigin() const; + WEBCORE_EXPORT LayoutPoint maxStableLayoutViewportOrigin() const; + + enum class TriggerLayoutOrNot { + No, + Yes + }; + // This origin can be overridden by setLayoutViewportOverrideRect. + void setBaseLayoutViewportOrigin(LayoutPoint, TriggerLayoutOrNot = TriggerLayoutOrNot::Yes); + // This size can be overridden by setLayoutViewportOverrideRect. + WEBCORE_EXPORT LayoutSize baseLayoutViewportSize() const; + + // If set, overrides the default "m_layoutViewportOrigin, size of initial containing block" rect. + // Used with delegated scrolling (i.e. iOS). + WEBCORE_EXPORT void setLayoutViewportOverrideRect(std::optional<LayoutRect>); + + // These are in document coordinates, unaffected by zooming. + WEBCORE_EXPORT LayoutRect layoutViewportRect() const; + WEBCORE_EXPORT LayoutRect visualViewportRect() const; + + static LayoutRect visibleDocumentRect(const FloatRect& visibleContentRect, float headerHeight, float footerHeight, const FloatSize& totalContentsSize, float pageScaleFactor); // This is different than visibleContentRect() in that it ignores negative (or overly positive) // offsets from rubber-banding, and it takes zooming into account. LayoutRect viewportConstrainedVisibleContentRect() const; + + LayoutRect rectForFixedPositionLayout() const; + + void viewportContentsChanged(); + WEBCORE_EXPORT void resumeVisibleImageAnimationsIncludingSubframes(); String mediaType() const; - void setMediaType(const String&); + WEBCORE_EXPORT void setMediaType(const String&); void adjustMediaTypeForPrinting(bool printing); void setCannotBlitToWindow(); void setIsOverlapped(bool); - bool isOverlapped() const { return m_isOverlapped; } - bool isOverlappedIncludingAncestors() const; void setContentIsOpaque(bool); void addSlowRepaintObject(RenderElement*); void removeSlowRepaintObject(RenderElement*); - bool hasSlowRepaintObject(RenderElement* o) const { return m_slowRepaintObjects && m_slowRepaintObjects->contains(o); } + bool hasSlowRepaintObject(const RenderElement& renderer) const { return m_slowRepaintObjects && m_slowRepaintObjects->contains(&renderer); } bool hasSlowRepaintObjects() const { return m_slowRepaintObjects && m_slowRepaintObjects->size(); } // Includes fixed- and sticky-position objects. @@ -251,31 +295,48 @@ public: void removeViewportConstrainedObject(RenderElement*); const ViewportConstrainedObjectSet* viewportConstrainedObjects() const { return m_viewportConstrainedObjects.get(); } bool hasViewportConstrainedObjects() const { return m_viewportConstrainedObjects && m_viewportConstrainedObjects->size() > 0; } + + float frameScaleFactor() const; // Functions for querying the current scrolled position, negating the effects of overhang // and adjusting for page scale. - IntSize scrollOffsetForFixedPosition() const; + LayoutPoint scrollPositionForFixedPosition() const; + // Static function can be called from another thread. - static IntSize scrollOffsetForFixedPosition(const IntRect& visibleContentRect, const IntSize& totalContentsSize, const IntPoint& scrollPosition, const IntPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements, int headerHeight, int footerHeight); + static LayoutPoint scrollPositionForFixedPosition(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements, int headerHeight, int footerHeight); + + WEBCORE_EXPORT static LayoutPoint computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport, ScrollBehaviorForFixedElements fixedBehavior); + + // These layers are positioned differently when there is a topContentInset, a header, or a footer. These value need to be computed + // on both the main thread and the scrolling thread. + static float yPositionForInsetClipLayer(const FloatPoint& scrollPosition, float topContentInset); + WEBCORE_EXPORT static FloatPoint positionForRootContentLayer(const FloatPoint& scrollPosition, const FloatPoint& scrollOrigin, float topContentInset, float headerHeight); + WEBCORE_EXPORT FloatPoint positionForRootContentLayer() const; + + static float yPositionForHeaderLayer(const FloatPoint& scrollPosition, float topContentInset); + static float yPositionForFooterLayer(const FloatPoint& scrollPosition, float topContentInset, float totalContentsHeight, float footerHeight); +#if PLATFORM(IOS) + WEBCORE_EXPORT LayoutRect viewportConstrainedObjectsRect() const; + // Static function can be called from another thread. + WEBCORE_EXPORT static LayoutRect rectForViewportConstrainedObjects(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements); +#endif + bool fixedElementsLayoutRelativeToFrame() const; - void disableLayerFlushThrottlingTemporarilyForInteraction(); - void updateLayerFlushThrottlingInAllFrames(); - void adjustTiledBackingCoverage(); + WEBCORE_EXPORT void disableLayerFlushThrottlingTemporarilyForInteraction(); bool speculativeTilingEnabled() const { return m_speculativeTilingEnabled; } + void loadProgressingStatusChanged(); #if ENABLE(DASHBOARD_SUPPORT) void updateAnnotatedRegions(); #endif - void updateControlTints(); + WEBCORE_EXPORT void updateControlTints(); void restoreScrollbar(); - void postLayoutTimerFired(Timer<FrameView>&); - - bool wasScrolledByUser() const; - void setWasScrolledByUser(bool); + WEBCORE_EXPORT bool wasScrolledByUser() const; + WEBCORE_EXPORT void setWasScrolledByUser(bool); bool safeToPropagateScrollToParent() const { return m_safeToPropagateScrollToParent; } void setSafeToPropagateScrollToParent(bool isSafe) { m_safeToPropagateScrollToParent = isSafe; } @@ -283,40 +344,64 @@ public: void addEmbeddedObjectToUpdate(RenderEmbeddedObject&); void removeEmbeddedObjectToUpdate(RenderEmbeddedObject&); - virtual void paintContents(GraphicsContext*, const IntRect& damageRect) override; - void setPaintBehavior(PaintBehavior); - PaintBehavior paintBehavior() const; + WEBCORE_EXPORT void paintContents(GraphicsContext&, const IntRect& dirtyRect, SecurityOriginPaintPolicy = SecurityOriginPaintPolicy::AnyOrigin) final; + + struct PaintingState { + PaintBehavior paintBehavior; + bool isTopLevelPainter; + bool isFlatteningPaintOfRootFrame; + PaintingState() + : paintBehavior() + , isTopLevelPainter(false) + , isFlatteningPaintOfRootFrame(false) + { + } + }; + + void willPaintContents(GraphicsContext&, const IntRect& dirtyRect, PaintingState&); + void didPaintContents(GraphicsContext&, const IntRect& dirtyRect, PaintingState&); + +#if PLATFORM(IOS) + WEBCORE_EXPORT void didReplaceMultipartContent(); +#endif + + WEBCORE_EXPORT void setPaintBehavior(PaintBehavior); + WEBCORE_EXPORT PaintBehavior paintBehavior() const; bool isPainting() const; bool hasEverPainted() const { return m_lastPaintTime; } void setLastPaintTime(double lastPaintTime) { m_lastPaintTime = lastPaintTime; } - void setNodeToDraw(Node*); + WEBCORE_EXPORT void setNodeToDraw(Node*); enum SelectionInSnapshot { IncludeSelection, ExcludeSelection }; enum CoordinateSpaceForSnapshot { DocumentCoordinates, ViewCoordinates }; - void paintContentsForSnapshot(GraphicsContext*, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot); + WEBCORE_EXPORT void paintContentsForSnapshot(GraphicsContext&, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot); - virtual void paintOverhangAreas(GraphicsContext*, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect) override; - virtual void paintScrollCorner(GraphicsContext*, const IntRect& cornerRect) override; - virtual void paintScrollbar(GraphicsContext*, Scrollbar*, const IntRect&) override; + void paintOverhangAreas(GraphicsContext&, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect) final; + void paintScrollCorner(GraphicsContext&, const IntRect& cornerRect) final; + void paintScrollbar(GraphicsContext&, Scrollbar&, const IntRect&) final; - Color documentBackgroundColor() const; + WEBCORE_EXPORT Color documentBackgroundColor() const; bool isInChildFrameWithFrameFlattening() const; + void startDisallowingLayout() { ++m_layoutDisallowedCount; } + void endDisallowingLayout() { ASSERT(m_layoutDisallowedCount > 0); --m_layoutDisallowedCount; } + bool layoutDisallowed() const { return m_layoutDisallowedCount; } + static double currentPaintTimeStamp() { return sCurrentPaintTimeStamp; } // returns 0 if not painting - void updateLayoutAndStyleIfNeededRecursive(); + WEBCORE_EXPORT void updateLayoutAndStyleIfNeededRecursive(); void incrementVisuallyNonEmptyCharacterCount(unsigned); void incrementVisuallyNonEmptyPixelCount(const IntSize&); void updateIsVisuallyNonEmpty(); bool isVisuallyNonEmpty() const { return m_isVisuallyNonEmpty; } - void enableAutoSizeMode(bool enable, const IntSize& minSize, const IntSize& maxSize); - void setAutoSizeFixedMinimumHeight(int fixedMinimumHeight); + WEBCORE_EXPORT void enableAutoSizeMode(bool enable, const IntSize& minSize, const IntSize& maxSize); + WEBCORE_EXPORT void setAutoSizeFixedMinimumHeight(int); IntSize autoSizingIntrinsicContentSize() const { return m_autoSizeContentSize; } - void forceLayout(bool allowSubtree = false); - void forceLayoutForPagination(const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkFactor, AdjustViewSizeOrNot); + WEBCORE_EXPORT void forceLayout(bool allowSubtree = false); + WEBCORE_EXPORT void forceLayoutForPagination(const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkFactor, AdjustViewSizeOrNot); // FIXME: This method is retained because of embedded WebViews in AppKit. When a WebView is embedded inside // some enclosing view with auto-pagination, no call happens to resize the view. The new pagination model @@ -334,60 +419,75 @@ public: // but that doesn't solve the general problem of how other AppKit views could opt in to the better model. // // NO OTHER PLATFORM BESIDES MAC SHOULD USE THIS METHOD. - void adjustPageHeightDeprecated(float* newBottom, float oldTop, float oldBottom, float bottomLimit); + WEBCORE_EXPORT void adjustPageHeightDeprecated(float* newBottom, float oldTop, float oldBottom, float bottomLimit); bool scrollToFragment(const URL&); bool scrollToAnchor(const String&); - void maintainScrollPositionAtAnchor(Node*); - void scrollElementToRect(Element*, const IntRect&); + void maintainScrollPositionAtAnchor(ContainerNode*); + WEBCORE_EXPORT void scrollElementToRect(const Element&, const IntRect&); // Methods to convert points and rects between the coordinate space of the renderer, and this view. - IntRect convertFromRenderer(const RenderElement*, const IntRect&) const; - IntRect convertToRenderer(const RenderElement*, const IntRect&) const; - IntPoint convertFromRenderer(const RenderElement*, const IntPoint&) const; - IntPoint convertToRenderer(const RenderElement*, const IntPoint&) const; - - bool isFrameViewScrollCorner(RenderScrollbarPart* scrollCorner) const { return m_scrollCorner == scrollCorner; } - - bool isScrollable(); + WEBCORE_EXPORT IntRect convertFromRendererToContainingView(const RenderElement*, const IntRect&) const; + WEBCORE_EXPORT IntRect convertFromContainingViewToRenderer(const RenderElement*, const IntRect&) const; + WEBCORE_EXPORT IntPoint convertFromRendererToContainingView(const RenderElement*, const IntPoint&) const; + WEBCORE_EXPORT IntPoint convertFromContainingViewToRenderer(const RenderElement*, const IntPoint&) const; + + // Override ScrollView methods to do point conversion via renderers, in order to take transforms into account. + IntRect convertToContainingView(const IntRect&) const final; + IntRect convertFromContainingView(const IntRect&) const final; + IntPoint convertToContainingView(const IntPoint&) const final; + IntPoint convertFromContainingView(const IntPoint&) const final; + + bool isFrameViewScrollCorner(const RenderScrollbarPart& scrollCorner) const { return m_scrollCorner == &scrollCorner; } + + // isScrollable() takes an optional Scrollability parameter that allows the caller to define what they mean by 'scrollable.' + // Most callers are interested in the default value, Scrollability::Scrollable, which means that there is actually content + // to scroll to, and a scrollbar that will allow you to access it. In some cases, callers want to know if the FrameView is allowed + // to rubber-band, which the main frame might be allowed to do even if there is no content to scroll to. In that case, + // callers use Scrollability::ScrollableOrRubberbandable. + enum class Scrollability { Scrollable, ScrollableOrRubberbandable }; + WEBCORE_EXPORT bool isScrollable(Scrollability definitionOfScrollable = Scrollability::Scrollable); + + bool isScrollableOrRubberbandable() final; + bool hasScrollableOrRubberbandableAncestor() final; enum ScrollbarModesCalculationStrategy { RulesFromWebContentOnly, AnyRule }; void calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode, ScrollbarModesCalculationStrategy = AnyRule); - virtual IntPoint lastKnownMousePosition() const override; - virtual bool isHandlingWheelEvent() const override; + IntPoint lastKnownMousePosition() const final; + bool isHandlingWheelEvent() const final; bool shouldSetCursor() const; // FIXME: Remove this method once plugin loading is decoupled from layout. void flushAnyPendingPostLayoutTasks(); - virtual bool shouldSuspendScrollAnimations() const override; - virtual void scrollbarStyleChanged(int newStyle, bool forceUpdate) override; + bool shouldSuspendScrollAnimations() const final; + void scrollbarStyleChanged(ScrollbarStyle, bool forceUpdate) override; RenderBox* embeddedContentBox() const; - void setTracksRepaints(bool); + WEBCORE_EXPORT void setTracksRepaints(bool); bool isTrackingRepaints() const { return m_isTrackingRepaints; } - void resetTrackedRepaints(); - const Vector<IntRect>& trackedRepaintRects() const { return m_trackedRepaintRects; } + WEBCORE_EXPORT void resetTrackedRepaints(); + const Vector<FloatRect>& trackedRepaintRects() const { return m_trackedRepaintRects; } String trackedRepaintRectsAsText() const; typedef HashSet<ScrollableArea*> ScrollableAreaSet; // Returns whether the scrollable area has just been newly added. - bool addScrollableArea(ScrollableArea*); + WEBCORE_EXPORT bool addScrollableArea(ScrollableArea*); // Returns whether the scrollable area has just been removed. - bool removeScrollableArea(ScrollableArea*); + WEBCORE_EXPORT bool removeScrollableArea(ScrollableArea*); bool containsScrollableArea(ScrollableArea*) const; const ScrollableAreaSet* scrollableAreas() const { return m_scrollableAreas.get(); } - virtual void removeChild(Widget*) override; + void removeChild(Widget&) final; // This function exists for ports that need to handle wheel events manually. // On Mac WebKit1 the underlying NSScrollView just does the scrolling, but on most other platforms // we need this function in order to do the scroll ourselves. bool wheelEvent(const PlatformWheelEvent&); - void setScrollingPerformanceLoggingEnabled(bool); + WEBCORE_EXPORT void setScrollingPerformanceLoggingEnabled(bool); // Page and FrameView both store a Pagination value. Page::pagination() is set only by API, // and FrameView::pagination() is set only by CSS. Page::pagination() will affect all @@ -398,7 +498,7 @@ public: const Pagination& pagination() const; void setPagination(const Pagination&); - bool inProgrammaticScroll() const { return m_inProgrammaticScroll; } + bool inProgrammaticScroll() const final { return m_inProgrammaticScroll; } void setInProgrammaticScroll(bool programmaticScroll) { m_inProgrammaticScroll = programmaticScroll; } #if ENABLE(CSS_DEVICE_ADAPTATION) @@ -406,54 +506,98 @@ public: void setInitialViewportSize(const IntSize& size) { m_initialViewportSize = size; } #endif - virtual bool isActive() const override; - virtual bool updatesScrollLayerPositionOnMainThread() const override; + bool isActive() const final; + bool forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const final; #if ENABLE(RUBBER_BANDING) - GraphicsLayer* setWantsLayerForTopOverHangArea(bool) const; - GraphicsLayer* setWantsLayerForBottomOverHangArea(bool) const; + WEBCORE_EXPORT GraphicsLayer* setWantsLayerForTopOverHangArea(bool) const; + WEBCORE_EXPORT GraphicsLayer* setWantsLayerForBottomOverHangArea(bool) const; #endif - virtual int headerHeight() const override { return m_headerHeight; } - void setHeaderHeight(int); - virtual int footerHeight() const override { return m_footerHeight; } - void setFooterHeight(int); + // This function "smears" the "position:fixed" uninflatedBounds for scrolling, returning a rect that is the union of + // all possible locations of the given rect under page scrolling. + LayoutRect fixedScrollableAreaBoundsInflatedForScrolling(const LayoutRect& uninflatedBounds) const; + + LayoutPoint scrollPositionRespectingCustomFixedPosition() const; + + int headerHeight() const final { return m_headerHeight; } + WEBCORE_EXPORT void setHeaderHeight(int); + int footerHeight() const final { return m_footerHeight; } + WEBCORE_EXPORT void setFooterHeight(int); + + WEBCORE_EXPORT float topContentInset(TopContentInsetType = TopContentInsetType::WebCoreContentInset) const final; + void topContentInsetDidChange(float newTopContentInset); + + void topContentDirectionDidChange(); - virtual void willStartLiveResize() override; - virtual void willEndLiveResize() override; + WEBCORE_EXPORT void willStartLiveResize() final; + WEBCORE_EXPORT void willEndLiveResize() final; + + WEBCORE_EXPORT void availableContentSizeChanged(AvailableSizeChangeReason) final; + + void updateTiledBackingAdaptiveSizing(); + TiledBacking::Scrollability computeScrollability() const; void addPaintPendingMilestones(LayoutMilestones); - void firePaintRelatedMilestones(); + void firePaintRelatedMilestonesIfNeeded(); + void fireLayoutRelatedMilestonesIfNeeded(); LayoutMilestones milestonesPendingPaint() const { return m_milestonesPendingPaint; } bool visualUpdatesAllowedByClient() const { return m_visualUpdatesAllowedByClient; } - void setVisualUpdatesAllowedByClient(bool); + WEBCORE_EXPORT void setVisualUpdatesAllowedByClient(bool); - void setScrollPinningBehavior(ScrollPinningBehavior); + WEBCORE_EXPORT void setScrollPinningBehavior(ScrollPinningBehavior); ScrollBehaviorForFixedElements scrollBehaviorForFixedElements() const; + bool hasFlippedBlockRenderers() const { return m_hasFlippedBlockRenderers; } + void setHasFlippedBlockRenderers(bool b) { m_hasFlippedBlockRenderers = b; } + void updateWidgetPositions(); void didAddWidgetToRenderTree(Widget&); void willRemoveWidgetFromRenderTree(Widget&); - void addTrackedRepaintRect(const IntRect&); + const HashSet<Widget*>& widgetsInRenderTree() const { return m_widgetsInRenderTree; } + + typedef Vector<Ref<FrameView>, 16> FrameViewList; + FrameViewList renderedChildFrameViews() const; + + void addTrackedRepaintRect(const FloatRect&); // exposedRect represents WebKit's understanding of what part // of the view is actually exposed on screen (taking into account // clipping by other UI elements), whereas visibleContentRect is // internal to WebCore and doesn't respect those things. - void setExposedRect(FloatRect); - FloatRect exposedRect() const { return m_exposedRect; } + WEBCORE_EXPORT void setViewExposedRect(std::optional<FloatRect>); + std::optional<FloatRect> viewExposedRect() const { return m_viewExposedRect; } + +#if ENABLE(CSS_SCROLL_SNAP) + void updateSnapOffsets() final; + bool isScrollSnapInProgress() const final; + void updateScrollingCoordinatorScrollSnapProperties() const; +#endif + + float adjustScrollStepForFixedContent(float step, ScrollbarOrientation, ScrollGranularity) final; + + void didChangeScrollOffset(); + + void show() final; + + bool shouldPlaceBlockDirectionScrollbarOnLeft() const final; + + void didRestoreFromPageCache(); + + void willDestroyRenderTree(); + void didDestroyRenderTree(); protected: - virtual bool scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) override; - virtual void scrollContentsSlowPath(const IntRect& updateRect) override; + bool scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) final; + void scrollContentsSlowPath(const IntRect& updateRect) final; void repaintSlowRepaintObjects(); - virtual bool isVerticalDocument() const override; - virtual bool isFlippedDocument() const override; + bool isVerticalDocument() const final; + bool isFlippedDocument() const final; private: explicit FrameView(Frame&); @@ -465,90 +609,107 @@ private: OutsideLayout, InPreLayout, InPreLayoutStyleUpdate, - InLayout, + InRenderTreeLayout, InViewSizeAdjust, InPostLayout, + InPostLayerPositionsUpdatedAfterLayout, }; LayoutPhase layoutPhase() const { return m_layoutPhase; } bool inPreLayoutStyleUpdate() const { return m_layoutPhase == InPreLayoutStyleUpdate; } - virtual bool isFrameView() const override { return true; } + bool isFrameView() const final { return true; } friend class RenderWidget; bool useSlowRepaints(bool considerOverlap = true) const; bool useSlowRepaintsIfNotOverlapped() const; void updateCanBlitOnScrollRecursively(); - bool contentsInCompositedLayer() const; + bool shouldLayoutAfterContentsResized() const; bool shouldUpdateCompositingLayersAfterScrolling() const; + bool flushCompositingStateForThisFrame(const Frame& rootFrameForFlush); - void applyOverflowToViewport(RenderElement*, ScrollbarMode& hMode, ScrollbarMode& vMode); + bool shouldDeferScrollUpdateAfterContentSizeChange() final; + + void scrollOffsetChangedViaPlatformWidgetImpl(const ScrollOffset& oldOffset, const ScrollOffset& newOffset) final; + + void applyOverflowToViewport(const RenderElement&, ScrollbarMode& hMode, ScrollbarMode& vMode); void applyPaginationToViewport(); void updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow); - void paintControlTints(); + WEBCORE_EXPORT void paintControlTints(); void forceLayoutParentViewIfNeeded(); + void flushPostLayoutTasksQueue(); void performPostLayoutTasks(); void autoSizeIfEnabled(); - virtual void repaintContentRectangle(const IntRect&, bool immediate) override; - virtual void contentsResized() override; - virtual void visibleContentsResized() override; - virtual void addedOrRemovedScrollbar() override; - virtual void fixedLayoutSizeChanged() override; + void applyRecursivelyWithVisibleRect(const std::function<void (FrameView& frameView, const IntRect& visibleRect)>&); + void resumeVisibleImageAnimations(const IntRect& visibleRect); + void updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect); + + void updateLayerFlushThrottling(); + WEBCORE_EXPORT void adjustTiledBackingCoverage(); - virtual void delegatesScrollingDidChange() override; + void repaintContentRectangle(const IntRect&) final; + void addedOrRemovedScrollbar() final; - // Override ScrollView methods to do point conversion via renderers, in order to - // take transforms into account. - virtual IntRect convertToContainingView(const IntRect&) const override; - virtual IntRect convertFromContainingView(const IntRect&) const override; - virtual IntPoint convertToContainingView(const IntPoint&) const override; - virtual IntPoint convertFromContainingView(const IntPoint&) const override; + void delegatesScrollingDidChange() final; // ScrollableArea interface - virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&) override; - virtual void scrollTo(const IntSize&) override; - virtual void setVisibleScrollerThumbRect(const IntRect&) override; - virtual ScrollableArea* enclosingScrollableArea() const override; - virtual IntRect scrollableAreaBoundingBox() const override; - virtual bool scrollAnimatorEnabled() const override; -#if USE(ACCELERATED_COMPOSITING) - virtual bool usesCompositedScrolling() const override; - virtual GraphicsLayer* layerForScrolling() const override; - virtual GraphicsLayer* layerForHorizontalScrollbar() const override; - virtual GraphicsLayer* layerForVerticalScrollbar() const override; - virtual GraphicsLayer* layerForScrollCorner() const override; + void invalidateScrollbarRect(Scrollbar&, const IntRect&) final; + void scrollTo(const ScrollPosition&) final; + void setVisibleScrollerThumbRect(const IntRect&) final; + ScrollableArea* enclosingScrollableArea() const final; + IntRect scrollableAreaBoundingBox(bool* = nullptr) const final; + bool scrollAnimatorEnabled() const final; + GraphicsLayer* layerForScrolling() const final; + GraphicsLayer* layerForHorizontalScrollbar() const final; + GraphicsLayer* layerForVerticalScrollbar() const final; + GraphicsLayer* layerForScrollCorner() const final; #if ENABLE(RUBBER_BANDING) - virtual GraphicsLayer* layerForOverhangAreas() const override; + GraphicsLayer* layerForOverhangAreas() const final; #endif + void contentsResized() final; + +#if PLATFORM(IOS) + void unobscuredContentSizeChanged() final; #endif + bool usesCompositedScrolling() const final; + bool usesAsyncScrolling() const final; + bool usesMockScrollAnimator() const final; + void logMockScrollAnimatorMessage(const String&) const final; + // Override scrollbar notifications to update the AXObject cache. - virtual void didAddScrollbar(Scrollbar*, ScrollbarOrientation) override; - virtual void willRemoveScrollbar(Scrollbar*, ScrollbarOrientation) override; + void didAddScrollbar(Scrollbar*, ScrollbarOrientation) final; + void willRemoveScrollbar(Scrollbar*, ScrollbarOrientation) final; + IntSize sizeForResizeEvent() const; void sendResizeEventIfNeeded(); + void handleDeferredScrollbarsUpdateAfterDirectionChange(); + void updateScrollableAreaSet(); + void updateLayoutViewport(); - virtual void notifyPageThatContentAreaWillPaint() const override; + void notifyPageThatContentAreaWillPaint() const final; void enableSpeculativeTilingIfNeeded(); - void speculativeTilingEnableTimerFired(Timer<FrameView>&); + void speculativeTilingEnableTimerFired(); - void updateEmbeddedObjectsTimerFired(Timer<FrameView>*); + void updateEmbeddedObjectsTimerFired(); bool updateEmbeddedObjects(); void updateEmbeddedObject(RenderEmbeddedObject&); void scrollToAnchor(); - void scrollPositionChanged(); + void scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition); + void scrollableAreaSetChanged(); + void sendScrollEvent(); bool hasCustomScrollbars() const; - virtual void updateScrollCorner() override; + void updateScrollCorner() final; FrameView* parentFrameView() const; @@ -557,12 +718,17 @@ private: bool isFrameFlatteningValidForThisFrame() const; bool qualifiesAsVisuallyNonEmpty() const; + bool isViewForDocumentInFrame() const; AXObjectCache* axObjectCache() const; void notifyWidgetsInAllFrames(WidgetNotification); void removeFromAXObjectCache(); void notifyWidgets(WidgetNotification); + void convertSubtreeLayoutToFullLayout(); + + RenderElement* viewportRenderer() const; + HashSet<Widget*> m_widgetsInRenderTree; static double sCurrentPaintTimeStamp; // used for detecting decoded resource thrash in the cache @@ -570,32 +736,29 @@ private: LayoutSize m_size; LayoutSize m_margins; - OwnPtr<ListHashSet<RenderEmbeddedObject*>> m_embeddedObjectsToUpdate; - const RefPtr<Frame> m_frame; + std::unique_ptr<ListHashSet<RenderEmbeddedObject*>> m_embeddedObjectsToUpdate; + const Ref<Frame> m_frame; - OwnPtr<HashSet<RenderElement*>> m_slowRepaintObjects; + std::unique_ptr<HashSet<const RenderElement*>> m_slowRepaintObjects; bool m_needsFullRepaint; bool m_canHaveScrollbars; bool m_cannotBlitToWindow; - bool m_isOverlapped; + bool m_isOverlapped { false }; bool m_contentIsOpaque; - int m_borderX; - int m_borderY; - - Timer<FrameView> m_layoutTimer; + Timer m_layoutTimer; bool m_delayedLayout; - RenderElement* m_layoutRoot; + RenderElement* m_layoutRoot { nullptr }; LayoutPhase m_layoutPhase; bool m_layoutSchedulingEnabled; bool m_inSynchronousPostLayout; int m_layoutCount; unsigned m_nestedLayoutCount; - Timer<FrameView> m_postLayoutTasksTimer; - Timer<FrameView> m_updateEmbeddedObjectsTimer; + Timer m_postLayoutTasksTimer; + Timer m_updateEmbeddedObjectsTimer; bool m_firstLayoutCallbackPending; bool m_firstLayout; @@ -609,26 +772,32 @@ private: bool m_overflowStatusDirty; bool m_horizontalOverflow; - bool m_verticalOverflow; - RenderElement* m_viewportRenderer; + bool m_verticalOverflow; + enum class ViewportRendererType { None, Document, Body }; + ViewportRendererType m_viewportRendererType { ViewportRendererType::None }; Pagination m_pagination; bool m_wasScrolledByUser; bool m_inProgrammaticScroll; bool m_safeToPropagateScrollToParent; + Timer m_delayedScrollEventTimer; double m_lastPaintTime; bool m_isTrackingRepaints; // Used for testing. - Vector<IntRect> m_trackedRepaintRects; + Vector<FloatRect> m_trackedRepaintRects; bool m_shouldUpdateWhileOffscreen; - FloatRect m_exposedRect; + std::optional<FloatRect> m_viewExposedRect; + + LayoutPoint m_layoutViewportOrigin; + std::optional<LayoutRect> m_layoutViewportOverrideRect; - unsigned m_deferSetNeedsLayouts; + unsigned m_deferSetNeedsLayoutCount; bool m_setNeedsLayoutWasDeferred; + int m_layoutDisallowedCount { 0 }; RefPtr<Node> m_nodeToDraw; PaintBehavior m_paintBehavior; @@ -639,19 +808,27 @@ private: bool m_isVisuallyNonEmpty; bool m_firstVisuallyNonEmptyLayoutCallbackPending; - RefPtr<Node> m_maintainScrollPositionAnchor; + bool m_needsDeferredScrollbarsUpdate { false }; + + RefPtr<ContainerNode> m_maintainScrollPositionAnchor; // Renderer to hold our custom scroll corner. RenderPtr<RenderScrollbarPart> m_scrollCorner; bool m_speculativeTilingEnabled; - Timer<FrameView> m_speculativeTilingEnableTimer; + Timer m_speculativeTilingEnableTimer; #if PLATFORM(IOS) bool m_useCustomFixedPositionLayoutRect; IntRect m_customFixedPositionLayoutRect; + + bool m_useCustomSizeForResizeEvent; + IntSize m_customSizeForResizeEvent; #endif + IntSize m_overrideViewportSize; + bool m_hasOverrideViewportSize; + // If true, automatically resize the frame view around its content. bool m_shouldAutoSize; bool m_inAutoSize; @@ -666,8 +843,8 @@ private: // The intrinsic content size decided by autosizing. IntSize m_autoSizeContentSize; - OwnPtr<ScrollableAreaSet> m_scrollableAreas; - OwnPtr<ViewportConstrainedObjectSet> m_viewportConstrainedObjects; + std::unique_ptr<ScrollableAreaSet> m_scrollableAreas; + std::unique_ptr<ViewportConstrainedObjectSet> m_viewportConstrainedObjects; int m_headerHeight; int m_footerHeight; @@ -684,8 +861,12 @@ private: #endif bool m_visualUpdatesAllowedByClient; - + bool m_hasFlippedBlockRenderers; + ScrollPinningBehavior m_scrollPinningBehavior; + + IntRect* m_cachedWindowClipRect { nullptr }; + Vector<WTF::Function<void ()>> m_postLayoutCallbackQueue; }; inline void FrameView::incrementVisuallyNonEmptyCharacterCount(unsigned count) @@ -708,8 +889,6 @@ inline void FrameView::incrementVisuallyNonEmptyPixelCount(const IntSize& size) updateIsVisuallyNonEmpty(); } -WIDGET_TYPE_CASTS(FrameView, isFrameView()); - } // namespace WebCore -#endif // FrameView_h +SPECIALIZE_TYPE_TRAITS_WIDGET(FrameView, isFrameView()) diff --git a/Source/WebCore/page/GestureTapHighlighter.cpp b/Source/WebCore/page/GestureTapHighlighter.cpp deleted file mode 100644 index 7b1c0abb5..000000000 --- a/Source/WebCore/page/GestureTapHighlighter.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 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 "GestureTapHighlighter.h" - -#include "Element.h" -#include "FrameView.h" -#include "GraphicsContext.h" -#include "GraphicsTypes.h" -#include "MainFrame.h" -#include "Node.h" -#include "Page.h" -#include "RenderBoxModelObject.h" -#include "RenderInline.h" -#include "RenderLayer.h" -#include "RenderObject.h" -#include "RenderView.h" - -namespace WebCore { - -namespace { - -inline LayoutPoint ownerFrameToMainFrameOffset(const RenderObject* o) -{ - ASSERT(o->node()); - Frame& containingFrame = o->frame(); - - Frame& mainFrame = containingFrame.page()->mainFrame(); - - LayoutPoint mainFramePoint = mainFrame.view()->windowToContents(containingFrame.view()->contentsToWindow(IntPoint())); - return mainFramePoint; -} - -AffineTransform localToAbsoluteTransform(const RenderObject* o) -{ - AffineTransform transform; - LayoutPoint referencePoint; - - while (o) { - RenderObject* nextContainer = o->container(); - if (!nextContainer) - break; - - LayoutSize containerOffset = o->offsetFromContainer(nextContainer, referencePoint); - TransformationMatrix t; - o->getTransformFromContainer(nextContainer, containerOffset, t); - - transform = t.toAffineTransform() * transform; - referencePoint.move(containerOffset); - o = nextContainer; - } - - return transform; -} - -inline bool contains(const LayoutRect& rect, int x) -{ - return !rect.isEmpty() && x >= rect.x() && x <= rect.maxX(); -} - -inline bool strikes(const LayoutRect& a, const LayoutRect& b) -{ - return !a.isEmpty() && !b.isEmpty() - && a.x() <= b.maxX() && b.x() <= a.maxX() - && a.y() <= b.maxY() && b.y() <= a.maxY(); -} - -inline void shiftXEdgesToContainIfStrikes(LayoutRect& rect, LayoutRect& other, bool isFirst) -{ - if (rect.isEmpty()) - return; - - if (other.isEmpty() || !strikes(rect, other)) - return; - - LayoutUnit leftSide = std::min(rect.x(), other.x()); - LayoutUnit rightSide = std::max(rect.maxX(), other.maxX()); - - rect.shiftXEdgeTo(leftSide); - rect.shiftMaxXEdgeTo(rightSide); - - if (isFirst) - other.shiftMaxXEdgeTo(rightSide); - else - other.shiftXEdgeTo(leftSide); -} - -inline void addHighlightRect(Path& path, const LayoutRect& rect, const LayoutRect& prev, const LayoutRect& next) -{ - // The rounding check depends on the rects not intersecting eachother, - // or being contained for that matter. - ASSERT(!rect.intersects(prev)); - ASSERT(!rect.intersects(next)); - - if (rect.isEmpty()) - return; - - const int rounding = 4; - - FloatRect copy(rect); - copy.inflateX(rounding); - copy.inflateY(rounding / 2); - - FloatSize rounded(rounding * 1.8, rounding * 1.8); - FloatSize squared(0, 0); - - path.addBeziersForRoundedRect(copy, - contains(prev, rect.x()) ? squared : rounded, - contains(prev, rect.maxX()) ? squared : rounded, - contains(next, rect.x()) ? squared : rounded, - contains(next, rect.maxX()) ? squared : rounded); -} - -Path absolutePathForRenderer(RenderObject* const o) -{ - ASSERT(o); - - Vector<IntRect> rects; - LayoutPoint frameOffset = ownerFrameToMainFrameOffset(o); - o->addFocusRingRects(rects, frameOffset); - - if (rects.isEmpty()) - return Path(); - - // The basic idea is to allow up to three different boxes in order to highlight - // text with line breaks more nicer than using a bounding box. - - // Merge all center boxes (all but the first and the last). - LayoutRect mid; - - // Set the end value to integer. It ensures that no unsigned int overflow occurs - // in the test expression, in case of empty rects vector. - int end = rects.size() - 1; - for (int i = 1; i < end; ++i) - mid.uniteIfNonZero(rects.at(i)); - - LayoutRect first; - LayoutRect last; - - // Add the first box, but merge it with the center boxes if it intersects or if the center box is empty. - if (rects.size() && !rects.first().isEmpty()) { - // If the mid box is empty at this point, unite it with the first box. This allows the first box to be - // united with the last box if they intersect in the following check for last. Not uniting them would - // trigger in assert in addHighlighRect due to the first and the last box intersecting, but being passed - // as two separate boxes. - if (mid.isEmpty() || mid.intersects(rects.first())) - mid.unite(rects.first()); - else { - first = rects.first(); - shiftXEdgesToContainIfStrikes(mid, first, /* isFirst */ true); - } - } - - // Add the last box, but merge it with the center boxes if it intersects. - if (rects.size() > 1 && !rects.last().isEmpty()) { - // Adjust center boxes to boundary of last - if (mid.intersects(rects.last())) - mid.unite(rects.last()); - else { - last = rects.last(); - shiftXEdgesToContainIfStrikes(mid, last, /* isFirst */ false); - } - } - - Vector<LayoutRect> drawableRects; - if (!first.isEmpty()) - drawableRects.append(first); - if (!mid.isEmpty()) - drawableRects.append(mid); - if (!last.isEmpty()) - drawableRects.append(last); - - // Clip the overflow rects if needed, before the ring path is formed to - // ensure rounded highlight rects. - for (int i = drawableRects.size() - 1; i >= 0; --i) { - LayoutRect& ringRect = drawableRects.at(i); - LayoutPoint ringRectLocation = ringRect.location(); - - ringRect.moveBy(-frameOffset); - - RenderLayer* layer = o->enclosingLayer(); - RenderObject* currentRenderer = o; - - // Check ancestor layers for overflow clip and intersect them. - for (; layer; layer = layer->parent()) { - RenderLayerModelObject* layerRenderer = &layer->renderer(); - - if (layerRenderer->hasOverflowClip() && layerRenderer != currentRenderer) { - bool containerSkipped = false; - // Skip ancestor layers that are not containers for the current renderer. - currentRenderer->container(layerRenderer, &containerSkipped); - if (containerSkipped) - continue; - FloatQuad ringQuad = currentRenderer->localToContainerQuad(FloatQuad(ringRect), layerRenderer); - // Ignore quads that are not rectangular, since we can not currently highlight them nicely. - if (ringQuad.isRectilinear()) - ringRect = ringQuad.enclosingBoundingBox(); - else - ringRect = LayoutRect(); - currentRenderer = layerRenderer; - - ASSERT(layerRenderer->isBox()); - ringRect.intersect(toRenderBox(layerRenderer)->borderBoxRect()); - - if (ringRect.isEmpty()) - break; - } - } - - if (ringRect.isEmpty()) { - drawableRects.remove(i); - continue; - } - // After clipping, reset the original position so that parents' transforms apply correctly. - ringRect.setLocation(ringRectLocation); - } - - Path path; - for (size_t i = 0; i < drawableRects.size(); ++i) { - LayoutRect prev = i ? drawableRects.at(i - 1) : LayoutRect(); - LayoutRect next = i < (drawableRects.size() - 1) ? drawableRects.at(i + 1) : LayoutRect(); - addHighlightRect(path, drawableRects.at(i), prev, next); - } - - path.transform(localToAbsoluteTransform(o)); - return path; -} - -} // anonymous namespace - -namespace GestureTapHighlighter { - -Path pathForNodeHighlight(const Node* node) -{ - RenderObject* renderer = node->renderer(); - - if (!renderer || (!renderer->isBox() && !renderer->isRenderInline())) - return Path(); - - return absolutePathForRenderer(renderer); -} - -} // namespace GestureTapHighlighter - -} // namespace WebCore diff --git a/Source/WebCore/page/GlobalCrypto.idl b/Source/WebCore/page/GlobalCrypto.idl new file mode 100644 index 000000000..0b879e639 --- /dev/null +++ b/Source/WebCore/page/GlobalCrypto.idl @@ -0,0 +1,30 @@ +/* +* 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. +*/ + +[ + NoInterfaceObject, +] interface GlobalCrypto { + readonly attribute Crypto crypto; +}; diff --git a/Source/WebCore/page/AbstractView.idl b/Source/WebCore/page/GlobalPerformance.idl index 4c39566ea..298ab52e6 100644 --- a/Source/WebCore/page/AbstractView.idl +++ b/Source/WebCore/page/GlobalPerformance.idl @@ -1,6 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 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 @@ -11,25 +10,24 @@ * 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. */ -// Introduced in DOM Level 2: +// https://w3c.github.io/hr-time/ + [ NoInterfaceObject, - ObjCCustomImplementation, -] interface AbstractView { - readonly attribute Document document; - readonly attribute StyleMedia styleMedia; + Exposed=(Window,Worker) +] interface GlobalPerformance { + [Conditional=WEB_TIMING, Replaceable] readonly attribute Performance performance; }; - diff --git a/Source/WebCore/page/History.cpp b/Source/WebCore/page/History.cpp index 5b5539cb5..fbc52f3c9 100644 --- a/Source/WebCore/page/History.cpp +++ b/Source/WebCore/page/History.cpp @@ -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 @@ -34,16 +34,18 @@ #include "FrameLoaderClient.h" #include "HistoryController.h" #include "HistoryItem.h" +#include "Logging.h" +#include "MainFrame.h" #include "Page.h" +#include "ScriptController.h" #include "SecurityOrigin.h" -#include "SerializedScriptValue.h" +#include <wtf/CheckedArithmetic.h> #include <wtf/MainThread.h> namespace WebCore { -History::History(Frame* frame) - : DOMWindowProperty(frame) - , m_lastStateObjectRequested(0) +History::History(Frame& frame) + : DOMWindowProperty(&frame) { } @@ -51,26 +53,26 @@ unsigned History::length() const { if (!m_frame) return 0; - if (!m_frame->page()) + auto* page = m_frame->page(); + if (!page) return 0; - return m_frame->page()->backForward().count(); + return page->backForward().count(); } -PassRefPtr<SerializedScriptValue> History::state() +SerializedScriptValue* History::state() { m_lastStateObjectRequested = stateInternal(); - return m_lastStateObjectRequested; + return m_lastStateObjectRequested.get(); } -PassRefPtr<SerializedScriptValue> History::stateInternal() const +SerializedScriptValue* History::stateInternal() const { if (!m_frame) - return 0; - - if (HistoryItem* historyItem = m_frame->loader().history().currentItem()) - return historyItem->stateObject(); - - return 0; + return nullptr; + auto* historyItem = m_frame->loader().history().currentItem(); + if (!historyItem) + return nullptr; + return historyItem->stateObject(); } bool History::stateChanged() const @@ -80,7 +82,7 @@ bool History::stateChanged() const bool History::isSameAsCurrentState(SerializedScriptValue* state) const { - return state == stateInternal().get(); + return state == stateInternal(); } void History::back() @@ -88,9 +90,9 @@ void History::back() go(-1); } -void History::back(ScriptExecutionContext* context) +void History::back(Document& document) { - go(context, -1); + go(document, -1); } void History::forward() @@ -98,30 +100,31 @@ void History::forward() go(1); } -void History::forward(ScriptExecutionContext* context) +void History::forward(Document& document) { - go(context, 1); + go(document, 1); } void History::go(int distance) { + LOG(History, "History %p go(%d) frame %p (main frame %d)", this, distance, m_frame, m_frame ? m_frame->isMainFrame() : false); + if (!m_frame) return; m_frame->navigationScheduler().scheduleHistoryNavigation(distance); } -void History::go(ScriptExecutionContext* context, int distance) +void History::go(Document& document, int distance) { + LOG(History, "History %p go(%d) in document %p frame %p (main frame %d)", this, distance, &document, m_frame, m_frame ? m_frame->isMainFrame() : false); + if (!m_frame) return; ASSERT(isMainThread()); - Document* activeDocument = toDocument(context); - if (!activeDocument) - return; - if (!activeDocument->canNavigate(m_frame)) + if (!document.canNavigate(m_frame)) return; m_frame->navigationScheduler().scheduleHistoryNavigation(distance); @@ -136,29 +139,87 @@ URL History::urlForState(const String& urlString) return URL(baseURL, urlString); } -void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const String& title, const String& urlString, StateObjectType stateObjectType, ExceptionCode& ec) +ExceptionOr<void> History::stateObjectAdded(RefPtr<SerializedScriptValue>&& data, const String& title, const String& urlString, StateObjectType stateObjectType) { + // Each unique main-frame document is only allowed to send 64MB of state object payload to the UI client/process. + static uint32_t totalStateObjectPayloadLimit = 0x4000000; + static double stateObjectTimeSpan = 30.0; + static unsigned perStateObjectTimeSpanLimit = 100; + if (!m_frame || !m_frame->page()) - return; - + return { }; + URL fullURL = urlForState(urlString); - if (!fullURL.isValid() || !m_frame->document()->securityOrigin()->canRequest(fullURL)) { - ec = SECURITY_ERR; - return; + if (!fullURL.isValid() || !m_frame->document()->securityOrigin().canRequest(fullURL)) + return Exception { SECURITY_ERR }; + + if (fullURL.hasUsername() || fullURL.hasPassword()) { + if (stateObjectType == StateObjectType::Replace) + return Exception { SECURITY_ERR, "Attempt to use history.replaceState() to change session history URL to " + fullURL.string() + " is insecure; Username/passwords aren't allowed in state object URLs" }; + return Exception { SECURITY_ERR, "Attempt to use history.pushState() to add URL " + fullURL.string() + " to session history is insecure; Username/passwords aren't allowed in state object URLs" }; + } + + Document* mainDocument = m_frame->page()->mainFrame().document(); + History* mainHistory = nullptr; + if (mainDocument) { + if (auto* mainDOMWindow = mainDocument->domWindow()) + mainHistory = mainDOMWindow->history(); + } + + if (!mainHistory) + return { }; + + double currentTimestamp = currentTime(); + if (currentTimestamp - mainHistory->m_currentStateObjectTimeSpanStart > stateObjectTimeSpan) { + mainHistory->m_currentStateObjectTimeSpanStart = currentTimestamp; + mainHistory->m_currentStateObjectTimeSpanObjectsAdded = 0; + } + + if (mainHistory->m_currentStateObjectTimeSpanObjectsAdded >= perStateObjectTimeSpanLimit) { + if (stateObjectType == StateObjectType::Replace) + return Exception { SECURITY_ERR, String::format("Attempt to use history.replaceState() more than %u times per %f seconds", perStateObjectTimeSpanLimit, stateObjectTimeSpan) }; + return Exception { SECURITY_ERR, String::format("Attempt to use history.pushState() more than %u times per %f seconds", perStateObjectTimeSpanLimit, stateObjectTimeSpan) }; } - if (stateObjectType == StateObjectType::Push) - m_frame->loader().history().pushState(data, title, fullURL.string()); - else if (stateObjectType == StateObjectType::Replace) - m_frame->loader().history().replaceState(data, title, fullURL.string()); - + Checked<unsigned> titleSize = title.length(); + titleSize *= 2; + + Checked<unsigned> urlSize = fullURL.string().length(); + urlSize *= 2; + + Checked<uint64_t> payloadSize = titleSize; + payloadSize += urlSize; + payloadSize += data ? data->data().size() : 0; + + Checked<uint64_t> newTotalUsage = mainHistory->m_totalStateObjectUsage; + + if (stateObjectType == StateObjectType::Replace) + newTotalUsage -= m_mostRecentStateObjectUsage; + newTotalUsage += payloadSize; + + if (newTotalUsage > totalStateObjectPayloadLimit) { + if (stateObjectType == StateObjectType::Replace) + return Exception { QUOTA_EXCEEDED_ERR, ASCIILiteral("Attempt to store more data than allowed using history.replaceState()") }; + return Exception { QUOTA_EXCEEDED_ERR, ASCIILiteral("Attempt to store more data than allowed using history.pushState()") }; + } + + m_mostRecentStateObjectUsage = payloadSize.unsafeGet(); + + mainHistory->m_totalStateObjectUsage = newTotalUsage.unsafeGet(); + ++mainHistory->m_currentStateObjectTimeSpanObjectsAdded; + if (!urlString.isEmpty()) m_frame->document()->updateURLForPushOrReplaceState(fullURL); - if (stateObjectType == StateObjectType::Push) + if (stateObjectType == StateObjectType::Push) { + m_frame->loader().history().pushState(WTFMove(data), title, fullURL.string()); m_frame->loader().client().dispatchDidPushStateWithinPage(); - else if (stateObjectType == StateObjectType::Replace) + } else if (stateObjectType == StateObjectType::Replace) { + m_frame->loader().history().replaceState(WTFMove(data), title, fullURL.string()); m_frame->loader().client().dispatchDidReplaceStateWithinPage(); + } + + return { }; } } // namespace WebCore diff --git a/Source/WebCore/page/History.h b/Source/WebCore/page/History.h index 1e8568594..e722a08be 100644 --- a/Source/WebCore/page/History.h +++ b/Source/WebCore/page/History.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,56 +23,56 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef History_h -#define History_h +#pragma once #include "DOMWindowProperty.h" +#include "ExceptionOr.h" #include "ScriptWrappable.h" #include "SerializedScriptValue.h" -#include "URL.h" -#include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> namespace WebCore { +class Document; class Frame; -class ScriptExecutionContext; -typedef int ExceptionCode; +class URL; -class History : public ScriptWrappable, public RefCounted<History>, public DOMWindowProperty { +class History final : public ScriptWrappable, public RefCounted<History>, public DOMWindowProperty { public: - static PassRefPtr<History> create(Frame* frame) { return adoptRef(new History(frame)); } + static Ref<History> create(Frame& frame) { return adoptRef(*new History(frame)); } unsigned length() const; - PassRefPtr<SerializedScriptValue> state(); + SerializedScriptValue* state(); void back(); void forward(); - void go(int distance); + void go(int); - void back(ScriptExecutionContext*); - void forward(ScriptExecutionContext*); - void go(ScriptExecutionContext*, int distance); + void back(Document&); + void forward(Document&); + void go(Document&, int); bool stateChanged() const; bool isSameAsCurrentState(SerializedScriptValue*) const; - enum class StateObjectType { - Push, - Replace - }; - void stateObjectAdded(PassRefPtr<SerializedScriptValue>, const String& title, const String& url, StateObjectType, ExceptionCode&); + enum class StateObjectType { Push, Replace }; + ExceptionOr<void> stateObjectAdded(RefPtr<SerializedScriptValue>&&, const String& title, const String& url, StateObjectType); private: - explicit History(Frame*); + explicit History(Frame&); URL urlForState(const String& url); - PassRefPtr<SerializedScriptValue> stateInternal() const; + SerializedScriptValue* stateInternal() const; RefPtr<SerializedScriptValue> m_lastStateObjectRequested; + + unsigned m_currentStateObjectTimeSpanObjectsAdded { 0 }; + double m_currentStateObjectTimeSpanStart { 0.0 }; + + // For the main frame's History object to keep track of all state object usage. + uint64_t m_totalStateObjectUsage { 0 }; + + // For each individual History object to keep track of the most recent state object added. + uint64_t m_mostRecentStateObjectUsage { 0 }; }; } // namespace WebCore - -#endif // History_h diff --git a/Source/WebCore/page/History.idl b/Source/WebCore/page/History.idl index 2fc8c07f0..8d9a669b7 100644 --- a/Source/WebCore/page/History.idl +++ b/Source/WebCore/page/History.idl @@ -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,19 +24,15 @@ */ [ - JSCustomGetOwnPropertySlotAndDescriptor, - CustomNamedSetter, GenerateIsReachable=ImplFrame, - CustomDeleteProperty, - CustomEnumerateProperty, ] interface History { readonly attribute unsigned long length; [CachedAttribute, Custom] readonly attribute SerializedScriptValue state; - [DoNotCheckSecurity, CallWith=ScriptExecutionContext] void back(); - [DoNotCheckSecurity, CallWith=ScriptExecutionContext] void forward(); - [DoNotCheckSecurity, CallWith=ScriptExecutionContext] void go([Default=Undefined] optional long distance); + [CallWith=Document, ForwardDeclareInHeader] void back(); + [CallWith=Document, ForwardDeclareInHeader] void forward(); + [CallWith=Document, ForwardDeclareInHeader] void go(optional long distance = 0); - [Custom, RaisesException] void pushState(any data, DOMString title, optional DOMString url); - [Custom, RaisesException] void replaceState(any data, DOMString title, optional DOMString url); + [Custom, MayThrowException] void pushState(any data, DOMString title, optional USVString? url = null); + [Custom, MayThrowException] void replaceState(any data, DOMString title, optional USVString? url = null); }; diff --git a/Source/WebCore/page/IntersectionObserver.cpp b/Source/WebCore/page/IntersectionObserver.cpp new file mode 100644 index 000000000..07fa8a889 --- /dev/null +++ b/Source/WebCore/page/IntersectionObserver.cpp @@ -0,0 +1,69 @@ +/* + * 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" + +#if ENABLE(INTERSECTION_OBSERVER) +#include "IntersectionObserver.h" + +#include "Element.h" +#include "IntersectionObserverCallback.h" +#include "IntersectionObserverEntry.h" +#include <wtf/Vector.h> + +namespace WebCore { + +IntersectionObserver::IntersectionObserver(Ref<IntersectionObserverCallback>&& callback, Init&& init) + : m_root(init.root) + , m_rootMargin(WTFMove(init.rootMargin)) + , m_callback(WTFMove(callback)) +{ + if (WTF::holds_alternative<double>(init.threshold)) + m_thresholds.append(WTF::get<double>(init.threshold)); + else + m_thresholds = WTF::get<Vector<double>>(WTFMove(init.threshold)); +} + +void IntersectionObserver::observe(Element&) +{ +} + +void IntersectionObserver::unobserve(Element&) +{ +} + +void IntersectionObserver::disconnect() +{ +} + +Vector<RefPtr<IntersectionObserverEntry>> IntersectionObserver::takeRecords() +{ + return { }; +} + + +} // namespace WebCore + +#endif // ENABLE(INTERSECTION_OBSERVER) diff --git a/Source/WebCore/page/IntersectionObserver.h b/Source/WebCore/page/IntersectionObserver.h new file mode 100644 index 000000000..13d58f3cf --- /dev/null +++ b/Source/WebCore/page/IntersectionObserver.h @@ -0,0 +1,75 @@ +/* + * 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 + +#if ENABLE(INTERSECTION_OBSERVER) + +#include "IntersectionObserverCallback.h" +#include "IntersectionObserverEntry.h" +#include <wtf/RefCounted.h> +#include <wtf/Variant.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Element; + +class IntersectionObserver : public RefCounted<IntersectionObserver> { +public: + struct Init { + RefPtr<Element> root; + String rootMargin; + Variant<double, Vector<double>> threshold; + }; + + static Ref<IntersectionObserver> create(Ref<IntersectionObserverCallback>&& callback, Init&& init) + { + return adoptRef(*new IntersectionObserver(WTFMove(callback), WTFMove(init))); + } + + Element* root() const { return m_root.get(); } + String rootMargin() const { return m_rootMargin; } + const Vector<double>& thresholds() const { return m_thresholds; } + + void observe(Element&); + void unobserve(Element&); + void disconnect(); + + Vector<RefPtr<IntersectionObserverEntry>> takeRecords(); + +private: + IntersectionObserver(Ref<IntersectionObserverCallback>&&, Init&&); + + RefPtr<Element> m_root; + String m_rootMargin; + Vector<double> m_thresholds; + Ref<IntersectionObserverCallback> m_callback; +}; + + +} // namespace WebCore + +#endif // ENABLE(INTERSECTION_OBSERVER) diff --git a/Source/WebCore/page/IntersectionObserver.idl b/Source/WebCore/page/IntersectionObserver.idl new file mode 100644 index 000000000..757de6ee1 --- /dev/null +++ b/Source/WebCore/page/IntersectionObserver.idl @@ -0,0 +1,52 @@ +/* + * 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. + */ + +// https://wicg.github.io/IntersectionObserver/ + +[ + Conditional=INTERSECTION_OBSERVER, + Constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options), + ImplementationLacksVTable, + EnabledAtRuntime=IntersectionObserver +] interface IntersectionObserver { + readonly attribute Element? root; + readonly attribute DOMString rootMargin; + readonly attribute sequence<double> thresholds; + + void observe(Element target); + void unobserve(Element target); + void disconnect(); + sequence<IntersectionObserverEntry> takeRecords(); +}; + +[ + Conditional=INTERSECTION_OBSERVER, + EnabledBySetting=IntersectionObserver +] +dictionary IntersectionObserverInit { + Element? root = null; + DOMString rootMargin = "0px"; + (double or sequence<double>) threshold = 0.0; +}; diff --git a/Source/WebCore/page/IntersectionObserverCallback.h b/Source/WebCore/page/IntersectionObserverCallback.h new file mode 100644 index 000000000..17f9d1609 --- /dev/null +++ b/Source/WebCore/page/IntersectionObserverCallback.h @@ -0,0 +1,46 @@ +/* + * 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 + +#if ENABLE(INTERSECTION_OBSERVER) + +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class IntersectionObserver; +class IntersectionObserverEntry; + +class IntersectionObserverCallback : public RefCounted<IntersectionObserverCallback> { +public: + virtual ~IntersectionObserverCallback() { } + virtual bool handleEvent(Vector<RefPtr<IntersectionObserverEntry>>, IntersectionObserver*) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(INTERSECTION_OBSERVER) diff --git a/Source/WebCore/page/IntersectionObserverCallback.idl b/Source/WebCore/page/IntersectionObserverCallback.idl new file mode 100644 index 000000000..7bc9fa444 --- /dev/null +++ b/Source/WebCore/page/IntersectionObserverCallback.idl @@ -0,0 +1,30 @@ +/* + * 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. + */ + +// https://wicg.github.io/IntersectionObserver/ + +[ + Conditional=INTERSECTION_OBSERVER +] callback IntersectionObserverCallback = void (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer); diff --git a/Source/WebCore/page/ConsoleTypes.h b/Source/WebCore/page/IntersectionObserverEntry.cpp index 804a167cc..e3f9c3c9f 100644 --- a/Source/WebCore/page/ConsoleTypes.h +++ b/Source/WebCore/page/IntersectionObserverEntry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * 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 @@ -23,31 +23,25 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ConsoleTypes_h -#define ConsoleTypes_h +#include "config.h" + +#if ENABLE(INTERSECTION_OBSERVER) +#include "IntersectionObserverEntry.h" + +#include "Element.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 -}; +IntersectionObserverEntry::IntersectionObserverEntry(const Init& init) + : m_time(init.time) + , m_rootBounds(DOMRectReadOnly::fromRect(init.rootBounds)) + , m_boundingClientRect(DOMRectReadOnly::fromRect(init.boundingClientRect)) + , m_intersectionRect(DOMRectReadOnly::fromRect(init.intersectionRect)) + , m_target(init.target) +{ +} + } // namespace WebCore -#endif // ConsoleTypes_h +#endif // ENABLE(INTERSECTION_OBSERVER) diff --git a/Source/WebCore/page/IntersectionObserverEntry.h b/Source/WebCore/page/IntersectionObserverEntry.h new file mode 100644 index 000000000..638d4b814 --- /dev/null +++ b/Source/WebCore/page/IntersectionObserverEntry.h @@ -0,0 +1,79 @@ +/* + * 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 + +#if ENABLE(INTERSECTION_OBSERVER) + +#include "DOMRectReadOnly.h" +#include "Element.h" +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Element; + +class IntersectionObserverEntry : public RefCounted<IntersectionObserverEntry> { + WTF_MAKE_FAST_ALLOCATED; +public: + + struct Init { + double time; + DOMRectInit rootBounds; + DOMRectInit boundingClientRect; + DOMRectInit intersectionRect; + RefPtr<Element> target; + }; + + static Ref<IntersectionObserverEntry> create(const Init& init) + { + return WTF::adoptRef(*new IntersectionObserverEntry(init)); + } + + double time() const { return m_time; } + RefPtr<DOMRectReadOnly> rootBounds() const { return m_rootBounds; } + RefPtr<DOMRectReadOnly> boundingClientRect() const { return m_boundingClientRect; } + RefPtr<DOMRectReadOnly> intersectionRect() const { return m_intersectionRect; } + RefPtr<Element> target() const { return m_target; } + + double intersectionRatio() const { return m_intersectionRatio; } + +private: + IntersectionObserverEntry(const Init&); + + double m_time { 0 }; + RefPtr<DOMRectReadOnly> m_rootBounds; + RefPtr<DOMRectReadOnly> m_boundingClientRect; + RefPtr<DOMRectReadOnly> m_intersectionRect; + double m_intersectionRatio { 0 }; + RefPtr<Element> m_target; +}; + + +} // namespace WebCore + +#endif // ENABLE(INTERSECTION_OBSERVER) diff --git a/Source/WebCore/page/IntersectionObserverEntry.idl b/Source/WebCore/page/IntersectionObserverEntry.idl new file mode 100644 index 000000000..1b4338b98 --- /dev/null +++ b/Source/WebCore/page/IntersectionObserverEntry.idl @@ -0,0 +1,52 @@ +/* + * 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. + */ + +// https://wicg.github.io/IntersectionObserver/ + +typedef double DOMHighResTimeStamp; + +[ + Conditional=INTERSECTION_OBSERVER, + Constructor(IntersectionObserverEntryInit intersectionObserverEntryInit), + ImplementationLacksVTable, + EnabledAtRuntime=IntersectionObserver +] interface IntersectionObserverEntry { + readonly attribute DOMHighResTimeStamp time; + readonly attribute DOMRectReadOnly rootBounds; + readonly attribute DOMRectReadOnly boundingClientRect; + readonly attribute DOMRectReadOnly intersectionRect; + readonly attribute double intersectionRatio; + readonly attribute Element target; +}; + +[ + Conditional=INTERSECTION_OBSERVER, +] dictionary IntersectionObserverEntryInit { + required DOMHighResTimeStamp time; + required DOMRectInit rootBounds; + required DOMRectInit boundingClientRect; + required DOMRectInit intersectionRect; + required Element target; +}; diff --git a/Source/WebCore/page/LayerFlushThrottleState.h b/Source/WebCore/page/LayerFlushThrottleState.h new file mode 100644 index 000000000..796f218e1 --- /dev/null +++ b/Source/WebCore/page/LayerFlushThrottleState.h @@ -0,0 +1,38 @@ +/* + * 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 + +namespace WebCore { + +struct LayerFlushThrottleState { + enum { + Enabled = 1 << 0, + UserIsInteracting = 1 << 1 + }; + typedef unsigned Flags; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/LayoutMilestones.h b/Source/WebCore/page/LayoutMilestones.h index 7007434bf..1785b33a9 100644 --- a/Source/WebCore/page/LayoutMilestones.h +++ b/Source/WebCore/page/LayoutMilestones.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef LayoutMilestones_h -#define LayoutMilestones_h +#pragma once namespace WebCore { @@ -32,16 +31,15 @@ namespace WebCore { // We should either re-name them to something more generic, or split them into // two enums -- one for painting and one for layout. enum LayoutMilestoneFlag { - DidFirstLayout = 1 << 0, - DidFirstVisuallyNonEmptyLayout = 1 << 1, - DidHitRelevantRepaintedObjectsAreaThreshold = 1 << 2, - DidFirstFlushForHeaderLayer = 1 << 3, - DidFirstLayoutAfterSuppressedIncrementalRendering = 1 << 4, - DidFirstPaintAfterSuppressedIncrementalRendering = 1 << 5 + DidFirstLayout = 1 << 0, + DidFirstVisuallyNonEmptyLayout = 1 << 1, + DidHitRelevantRepaintedObjectsAreaThreshold = 1 << 2, + DidFirstFlushForHeaderLayer = 1 << 3, + DidFirstLayoutAfterSuppressedIncrementalRendering = 1 << 4, + DidFirstPaintAfterSuppressedIncrementalRendering = 1 << 5, + ReachedSessionRestorationRenderTreeSizeThreshold = 1 << 6 // FIXME: only implemented by WK2 currently. }; typedef unsigned LayoutMilestones; } // namespace WebCore - -#endif // LayoutMilestones_h diff --git a/Source/WebCore/page/Location.cpp b/Source/WebCore/page/Location.cpp index 6ae9af0bf..8eeeddae0 100644 --- a/Source/WebCore/page/Location.cpp +++ b/Source/WebCore/page/Location.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. * @@ -60,7 +60,15 @@ String Location::href() const if (!m_frame) return String(); - return url().string(); + auto& url = this->url(); + + if (!url.hasUsername() && !url.hasPassword()) + return url.string(); + + URL urlWithoutCredentials(url); + urlWithoutCredentials.setUser(WTF::emptyString()); + urlWithoutCredentials.setPass(WTF::emptyString()); + return urlWithoutCredentials.string(); } String Location::protocol() const @@ -68,7 +76,7 @@ String Location::protocol() const if (!m_frame) return String(); - return url().protocol() + ":"; + return makeString(url().protocol(), ":"); } String Location::host() const @@ -78,8 +86,7 @@ String Location::host() const // Note: this is the IE spec. The NS spec swaps the two, it says // "The hostname property is the concatenation of the host and port properties, separated by a colon." - const URL& url = this->url(); - return url.hasPort() ? url.host() + ":" + String::number(url.port()) : url.host(); + return url().hostAndPort(); } String Location::hostname() const @@ -96,7 +103,7 @@ String Location::port() const return String(); const URL& url = this->url(); - return url.hasPort() ? String::number(url.port()) : ""; + return url.port() ? String::number(url.port().value()) : emptyString(); } String Location::pathname() const @@ -124,14 +131,14 @@ String Location::origin() const return SecurityOrigin::create(url())->toString(); } -PassRefPtr<DOMStringList> Location::ancestorOrigins() const +Ref<DOMStringList> Location::ancestorOrigins() const { - RefPtr<DOMStringList> origins = DOMStringList::create(); + auto origins = DOMStringList::create(); if (!m_frame) - return origins.release(); + return origins; for (Frame* frame = m_frame->tree().parent(); frame; frame = frame->tree().parent()) - origins->append(frame->document()->securityOrigin()->toString()); - return origins.release(); + origins->append(frame->document()->securityOrigin().toString()); + return origins; } String Location::hash() const @@ -143,44 +150,43 @@ String Location::hash() const return fragmentIdentifier.isEmpty() ? emptyString() : "#" + fragmentIdentifier; } -void Location::setHref(const String& url, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setHref(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url) { if (!m_frame) return; - setLocation(url, activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url); } -void Location::setProtocol(const String& protocol, DOMWindow& activeWindow, DOMWindow& firstWindow, ExceptionCode& ec) +ExceptionOr<void> Location::setProtocol(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& protocol) { if (!m_frame) - return; + return { }; URL url = m_frame->document()->url(); - if (!url.setProtocol(protocol)) { - ec = SYNTAX_ERR; - return; - } - setLocation(url.string(), activeWindow, firstWindow); + if (!url.setProtocol(protocol)) + return Exception { SYNTAX_ERR }; + setLocation(activeWindow, firstWindow, url.string()); + return { }; } -void Location::setHost(const String& host, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setHost(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& host) { if (!m_frame) return; URL url = m_frame->document()->url(); url.setHostAndPort(host); - setLocation(url.string(), activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url.string()); } -void Location::setHostname(const String& hostname, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setHostname(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& hostname) { if (!m_frame) return; URL url = m_frame->document()->url(); url.setHost(hostname); - setLocation(url.string(), activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url.string()); } -void Location::setPort(const String& portString, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setPort(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& portString) { if (!m_frame) return; @@ -190,34 +196,35 @@ void Location::setPort(const String& portString, DOMWindow& activeWindow, DOMWin url.removePort(); else url.setPort(port); - setLocation(url.string(), activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url.string()); } -void Location::setPathname(const String& pathname, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setPathname(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& pathname) { if (!m_frame) return; URL url = m_frame->document()->url(); url.setPath(pathname); - setLocation(url.string(), activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url.string()); } -void Location::setSearch(const String& search, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setSearch(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& search) { if (!m_frame) return; URL url = m_frame->document()->url(); url.setQuery(search); - setLocation(url.string(), activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url.string()); } -void Location::setHash(const String& hash, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setHash(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& hash) { if (!m_frame) return; - URL url = m_frame->document()->url(); - String oldFragmentIdentifier = url.fragmentIdentifier(); - String newFragmentIdentifier = hash; + ASSERT(m_frame->document()); + auto url = m_frame->document()->url(); + auto oldFragmentIdentifier = url.fragmentIdentifier(); + auto newFragmentIdentifier = hash; if (hash[0] == '#') newFragmentIdentifier = hash.substring(1); url.setFragmentIdentifier(newFragmentIdentifier); @@ -226,49 +233,62 @@ void Location::setHash(const String& hash, DOMWindow& activeWindow, DOMWindow& f // cases where fragment identifiers are ignored or invalid. if (equalIgnoringNullity(oldFragmentIdentifier, url.fragmentIdentifier())) return; - setLocation(url.string(), activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url.string()); } -void Location::assign(const String& url, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::assign(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url) { if (!m_frame) return; - setLocation(url, activeWindow, firstWindow); + setLocation(activeWindow, firstWindow, url); } -void Location::replace(const String& url, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::replace(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url) { if (!m_frame) return; - // Note: We call DOMWindow::setLocation directly here because replace() always operates on the current frame. - m_frame->document()->domWindow()->setLocation(url, activeWindow, firstWindow, LockHistoryAndBackForwardList); + ASSERT(m_frame->document()); + ASSERT(m_frame->document()->domWindow()); + // We call DOMWindow::setLocation directly here because replace() always operates on the current frame. + m_frame->document()->domWindow()->setLocation(activeWindow, firstWindow, url, LockHistoryAndBackForwardList); } void Location::reload(DOMWindow& activeWindow) { if (!m_frame) return; + + ASSERT(activeWindow.document()); + ASSERT(m_frame->document()); + ASSERT(m_frame->document()->domWindow()); + + auto& activeDocument = *activeWindow.document(); + auto& targetDocument = *m_frame->document(); + // FIXME: It's not clear this cross-origin security check is valuable. // We allow one page to change the location of another. Why block attempts to reload? // Other location operations simply block use of JavaScript URLs cross origin. - DOMWindow* targetWindow = m_frame->document()->domWindow(); - if (!activeWindow.document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin())) { - targetWindow->printErrorMessage(targetWindow->crossDomainAccessErrorMessage(activeWindow)); + if (!activeDocument.securityOrigin().canAccess(targetDocument.securityOrigin())) { + auto& targetWindow = *targetDocument.domWindow(); + targetWindow.printErrorMessage(targetWindow.crossDomainAccessErrorMessage(activeWindow)); return; } - if (protocolIsJavaScript(m_frame->document()->url())) + + if (protocolIsJavaScript(targetDocument.url())) return; - m_frame->navigationScheduler().scheduleRefresh(); + + m_frame->navigationScheduler().scheduleRefresh(activeDocument); } -void Location::setLocation(const String& url, DOMWindow& activeWindow, DOMWindow& firstWindow) +void Location::setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String& url) { ASSERT(m_frame); - // We call findFrameForNavigation to handle the case of a seamless iframe correctly. - Frame* frame = m_frame->loader().findFrameForNavigation(String(), activeWindow.document()); - if (!frame) + auto* targetFrame = m_frame->loader().findFrameForNavigation({ }, activeWindow.document()); + if (!targetFrame) return; - frame->document()->domWindow()->setLocation(url, activeWindow, firstWindow); + ASSERT(targetFrame->document()); + ASSERT(targetFrame->document()->domWindow()); + targetFrame->document()->domWindow()->setLocation(activeWindow, firstWindow, url); } } // namespace WebCore diff --git a/Source/WebCore/page/Location.h b/Source/WebCore/page/Location.h index b55c5a8f0..c70d9c87b 100644 --- a/Source/WebCore/page/Location.h +++ b/Source/WebCore/page/Location.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,15 +26,12 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Location_h -#define Location_h +#pragma once #include "DOMStringList.h" #include "DOMWindowProperty.h" +#include "ExceptionOr.h" #include "ScriptWrappable.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> namespace WebCore { @@ -42,47 +39,43 @@ class DOMWindow; class Frame; class URL; -typedef int ExceptionCode; - class Location : public ScriptWrappable, public RefCounted<Location>, public DOMWindowProperty { public: - static PassRefPtr<Location> create(Frame* frame) { return adoptRef(new Location(frame)); } + static Ref<Location> create(Frame* frame) { return adoptRef(*new Location(frame)); } - void setHref(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setHref(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String href() const; - void assign(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); - void replace(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void assign(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); + void replace(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); void reload(DOMWindow& activeWindow); - void setProtocol(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow, ExceptionCode&); + ExceptionOr<void> setProtocol(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String protocol() const; - void setHost(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setHost(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String host() const; - void setHostname(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setHostname(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String hostname() const; - void setPort(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setPort(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String port() const; - void setPathname(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setPathname(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String pathname() const; - void setSearch(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setSearch(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String search() const; - void setHash(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setHash(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); String hash() const; String origin() const; String toString() const { return href(); } - PassRefPtr<DOMStringList> ancestorOrigins() const; + Ref<DOMStringList> ancestorOrigins() const; private: explicit Location(Frame*); - void setLocation(const String&, DOMWindow& activeWindow, DOMWindow& firstWindow); + void setLocation(DOMWindow& activeWindow, DOMWindow& firstWindow, const String&); const URL& url() const; }; } // namespace WebCore - -#endif // Location_h diff --git a/Source/WebCore/page/Location.idl b/Source/WebCore/page/Location.idl index 7eeb6be92..750a1c4c7 100644 --- a/Source/WebCore/page/Location.idl +++ b/Source/WebCore/page/Location.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. * @@ -27,41 +27,38 @@ */ [ - JSCustomGetOwnPropertySlotAndDescriptor, - CustomNamedSetter, - GenerateIsReachable=ImplFrame, + CheckSecurity, CustomDeleteProperty, CustomEnumerateProperty, + CustomGetPrototype, + CustomNamedSetter, + CustomPreventExtensions, + CustomSetPrototype, + CustomToStringName, + GenerateIsReachable=ImplFrame, JSCustomDefineOwnProperty, - JSCustomNamedGetterOnPrototype, JSCustomDefineOwnPropertyOnPrototype, - OperationsNotDeletable + JSCustomGetOwnPropertySlotAndDescriptor, + JSCustomNamedGetterOnPrototype, + Unforgeable, ] interface Location { -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP - [DoNotCheckSecurityOnSetter, CustomSetter] attribute DOMString href; -#endif + [SetterCallWith=ActiveWindow&FirstWindow, DoNotCheckSecurityOnSetter] stringifier attribute USVString href; - [Custom] void assign([Default=Undefined] optional DOMString url); - [Custom] void replace([Default=Undefined] optional DOMString url); - [Custom] void reload(); + [CallWith=ActiveWindow&FirstWindow, ForwardDeclareInHeader] void assign(USVString url); + [DoNotCheckSecurity, CallWith=ActiveWindow&FirstWindow, ForwardDeclareInHeader] void replace(USVString url); + [CallWith=ActiveWindow, ForwardDeclareInHeader] void reload(); // URI decomposition attributes -#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP - [CustomSetter] attribute DOMString protocol; - [CustomSetter] attribute DOMString host; - [CustomSetter] attribute DOMString hostname; - [CustomSetter] attribute DOMString port; - [CustomSetter] attribute DOMString pathname; - [CustomSetter] attribute DOMString search; - [CustomSetter] attribute DOMString hash; + [SetterCallWith=ActiveWindow&FirstWindow, SetterMayThrowException] attribute USVString protocol; + [SetterCallWith=ActiveWindow&FirstWindow] attribute USVString host; + [SetterCallWith=ActiveWindow&FirstWindow] attribute USVString hostname; + [SetterCallWith=ActiveWindow&FirstWindow] attribute USVString port; + [SetterCallWith=ActiveWindow&FirstWindow] attribute USVString pathname; + [SetterCallWith=ActiveWindow&FirstWindow] attribute USVString search; + [SetterCallWith=ActiveWindow&FirstWindow] attribute USVString hash; - readonly attribute DOMString origin; -#endif + readonly attribute USVString origin; - readonly attribute DOMStringList ancestorOrigins; - -#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT - [NotEnumerable, Custom, ImplementedAs=toStringFunction] DOMString toString(); -#endif + // FIXME: Add support for SameObject. + [Unforgeable, CachedAttribute] readonly attribute DOMStringList ancestorOrigins; }; - diff --git a/Source/WebCore/page/MainFrame.cpp b/Source/WebCore/page/MainFrame.cpp index 054a0625c..8b6be3132 100644 --- a/Source/WebCore/page/MainFrame.cpp +++ b/Source/WebCore/page/MainFrame.cpp @@ -1,43 +1,72 @@ /* - -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. - -*/ + * 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 + * 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 "MainFrame.h" +#include "Element.h" +#include "PageConfiguration.h" +#include "PageOverlayController.h" +#include "PaymentCoordinator.h" +#include "PerformanceLogging.h" +#include "ScrollLatchingState.h" +#include "WheelEventDeltaFilter.h" +#include <wtf/NeverDestroyed.h> + +#if PLATFORM(MAC) +#include "ServicesOverlayController.h" +#endif + namespace WebCore { -inline MainFrame::MainFrame(Page& page, FrameLoaderClient& client) - : Frame(page, nullptr, client) +inline MainFrame::MainFrame(Page& page, PageConfiguration& configuration) + : Frame(page, nullptr, *configuration.loaderClientForMainFrame) , m_selfOnlyRefCount(0) +#if PLATFORM(MAC) && (ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION)) + , m_servicesOverlayController(std::make_unique<ServicesOverlayController>(*this)) +#endif + , m_recentWheelEventDeltaFilter(WheelEventDeltaFilter::create()) + , m_pageOverlayController(std::make_unique<PageOverlayController>(*this)) +#if ENABLE(APPLE_PAY) + , m_paymentCoordinator(std::make_unique<PaymentCoordinator>(*configuration.paymentCoordinatorClient)) +#endif + , m_performanceLogging(std::make_unique<PerformanceLogging>(*this)) { } -RefPtr<MainFrame> MainFrame::create(Page& page, FrameLoaderClient& client) +MainFrame::~MainFrame() +{ + m_recentWheelEventDeltaFilter = nullptr; + m_eventHandler = nullptr; + + setMainFrameWasDestroyed(); +} + +Ref<MainFrame> MainFrame::create(Page& page, PageConfiguration& configuration) { - return adoptRef(new MainFrame(page, client)); + return adoptRef(*new MainFrame(page, configuration)); } void MainFrame::selfOnlyRef() @@ -63,7 +92,52 @@ void MainFrame::selfOnlyDeref() void MainFrame::dropChildren() { while (Frame* child = tree().firstChild()) - tree().removeChild(child); + tree().removeChild(*child); +} + +void MainFrame::didCompleteLoad() +{ + m_timeOfLastCompletedLoad = MonotonicTime::now(); + performanceLogging().didReachPointOfInterest(PerformanceLogging::MainFrameLoadCompleted); +} + +#if PLATFORM(MAC) +ScrollLatchingState* MainFrame::latchingState() +{ + if (m_latchingState.isEmpty()) + return nullptr; + + return &m_latchingState.last(); +} + +void MainFrame::pushNewLatchingState() +{ + m_latchingState.append(ScrollLatchingState()); +} + +void MainFrame::resetLatchingState() +{ + m_latchingState.clear(); +} + +void MainFrame::popLatchingState() +{ + m_latchingState.removeLast(); +} + +void MainFrame::removeLatchingStateForTarget(Element& targetNode) +{ + if (m_latchingState.isEmpty()) + return; + + m_latchingState.removeAllMatching([&targetNode] (ScrollLatchingState& state) { + auto* wheelElement = state.wheelEventElement(); + if (!wheelElement) + return false; + + return targetNode.isEqualNode(wheelElement); + }); } +#endif } diff --git a/Source/WebCore/page/MainFrame.h b/Source/WebCore/page/MainFrame.h index 2e7828a71..369ddbaed 100644 --- a/Source/WebCore/page/MainFrame.h +++ b/Source/WebCore/page/MainFrame.h @@ -1,56 +1,101 @@ /* + * Copyright (C) 2013-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. + */ -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. - -*/ - -#ifndef MainFrame_h -#define MainFrame_h +#pragma once +#include "EventHandler.h" #include "Frame.h" +#include <wtf/Vector.h> namespace WebCore { +class PageConfiguration; +class PageOverlayController; +class PaymentCoordinator; +class PerformanceLogging; +class ScrollLatchingState; +class ServicesOverlayController; +class WheelEventDeltaFilter; + class MainFrame final : public Frame { public: - static RefPtr<MainFrame> create(Page&, FrameLoaderClient&); + static Ref<MainFrame> create(Page&, PageConfiguration&); + + virtual ~MainFrame(); void selfOnlyRef(); void selfOnlyDeref(); + WheelEventDeltaFilter* wheelEventDeltaFilter() { return m_recentWheelEventDeltaFilter.get(); } + PageOverlayController& pageOverlayController() { return *m_pageOverlayController; } + +#if PLATFORM(MAC) +#if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION) + ServicesOverlayController& servicesOverlayController() { return *m_servicesOverlayController; } +#endif // ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION) + + ScrollLatchingState* latchingState(); + void pushNewLatchingState(); + void popLatchingState(); + void resetLatchingState(); + void removeLatchingStateForTarget(Element&); +#endif // PLATFORM(MAC) + +#if ENABLE(APPLE_PAY) + PaymentCoordinator& paymentCoordinator() const { return *m_paymentCoordinator; } +#endif + + PerformanceLogging& performanceLogging() const { return *m_performanceLogging; } + + void didCompleteLoad(); + MonotonicTime timeOfLastCompletedLoad() const { return m_timeOfLastCompletedLoad; } + private: - MainFrame(Page&, FrameLoaderClient&); + MainFrame(Page&, PageConfiguration&); void dropChildren(); unsigned m_selfOnlyRefCount; -}; -inline bool Frame::isMainFrame() const -{ - return this == &m_mainFrame; -} +#if PLATFORM(MAC) + Vector<ScrollLatchingState> m_latchingState; +#if ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION) + std::unique_ptr<ServicesOverlayController> m_servicesOverlayController; +#endif +#endif -} + std::unique_ptr<WheelEventDeltaFilter> m_recentWheelEventDeltaFilter; + std::unique_ptr<PageOverlayController> m_pageOverlayController; +#if ENABLE(APPLE_PAY) + std::unique_ptr<PaymentCoordinator> m_paymentCoordinator; #endif + + std::unique_ptr<PerformanceLogging> m_performanceLogging; + + MonotonicTime m_timeOfLastCompletedLoad; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/MediaCanStartListener.h b/Source/WebCore/page/MediaCanStartListener.h index 317babf41..890e44167 100644 --- a/Source/WebCore/page/MediaCanStartListener.h +++ b/Source/WebCore/page/MediaCanStartListener.h @@ -23,18 +23,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MediaCanStartListener_h -#define MediaCanStartListener_h +#pragma once namespace WebCore { +class Document; + class MediaCanStartListener { public: - virtual void mediaCanStart() = 0; + virtual void mediaCanStart(Document&) = 0; protected: virtual ~MediaCanStartListener() { } }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/MediaProducer.h b/Source/WebCore/page/MediaProducer.h new file mode 100644 index 000000000..2f97296bd --- /dev/null +++ b/Source/WebCore/page/MediaProducer.h @@ -0,0 +1,65 @@ +/* + * 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 + +namespace WebCore { + +class MediaProducer { +public: + enum MediaState { + IsNotPlaying = 0, + IsPlayingAudio = 1 << 0, + IsPlayingVideo = 1 << 1, + IsPlayingToExternalDevice = 1 << 2, + RequiresPlaybackTargetMonitoring = 1 << 3, + ExternalDeviceAutoPlayCandidate = 1 << 4, + DidPlayToEnd = 1 << 5, + IsSourceElementPlaying = 1 << 6, + IsNextTrackControlEnabled = 1 << 7, + IsPreviousTrackControlEnabled = 1 << 8, + HasPlaybackTargetAvailabilityListener = 1 << 9, + HasAudioOrVideo = 1 << 10, + HasActiveAudioCaptureDevice = 1 << 11, + HasActiveVideoCaptureDevice = 1 << 12, + }; + typedef unsigned MediaStateFlags; + + virtual MediaStateFlags mediaState() const = 0; + + enum MutedState { + NoneMuted = 0, + AudioIsMuted = 1 << 0, + CaptureDevicesAreMuted = 1 << 1, + }; + typedef unsigned MutedStateFlags; + + virtual void pageMutedStateDidChange() = 0; + +protected: + virtual ~MediaProducer() { } +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/MemoryRelease.cpp b/Source/WebCore/page/MemoryRelease.cpp new file mode 100644 index 000000000..36612a81a --- /dev/null +++ b/Source/WebCore/page/MemoryRelease.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2011, 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. + */ + +#include "config.h" +#include "MemoryRelease.h" + +#include "CSSValuePool.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "CommonVM.h" +#include "Document.h" +#include "FontCache.h" +#include "GCController.h" +#include "HTMLMediaElement.h" +#include "InlineStyleSheetOwner.h" +#include "InspectorInstrumentation.h" +#include "Logging.h" +#include "MainFrame.h" +#include "MemoryCache.h" +#include "Page.h" +#include "PageCache.h" +#include "RenderTheme.h" +#include "ScrollingThread.h" +#include "StyleScope.h" +#include "StyledElement.h" +#include "WorkerThread.h" +#include <wtf/FastMalloc.h> + +#if PLATFORM(COCOA) +#include "ResourceUsageThread.h" +#endif + +namespace WebCore { + +static void releaseNoncriticalMemory() +{ + RenderTheme::defaultTheme()->purgeCaches(); + + FontCache::singleton().purgeInactiveFontData(); + + clearWidthCaches(); + + for (auto* document : Document::allDocuments()) + document->clearSelectorQueryCache(); + + MemoryCache::singleton().pruneDeadResourcesToSize(0); + + InlineStyleSheetOwner::clearCache(); +} + +static void releaseCriticalMemory(Synchronous synchronous) +{ + // Right now, the only reason we call release critical memory while not under memory pressure is if the process is about to be suspended. + PruningReason pruningReason = MemoryPressureHandler::singleton().isUnderMemoryPressure() ? PruningReason::MemoryPressure : PruningReason::ProcessSuspended; + PageCache::singleton().pruneToSizeNow(0, pruningReason); + + MemoryCache::singleton().pruneLiveResourcesToSize(0, /*shouldDestroyDecodedDataForAllLiveResources*/ true); + + CSSValuePool::singleton().drain(); + + Vector<RefPtr<Document>> documents; + copyToVector(Document::allDocuments(), documents); + for (auto& document : documents) + document->styleScope().clearResolver(); + + GCController::singleton().deleteAllCode(JSC::DeleteAllCodeIfNotCollecting); + +#if ENABLE(VIDEO) + for (auto* mediaElement : HTMLMediaElement::allMediaElements()) { + if (mediaElement->paused()) + mediaElement->purgeBufferedDataIfPossible(); + } +#endif + + if (synchronous == Synchronous::Yes) { + GCController::singleton().garbageCollectNow(); + } else { +#if PLATFORM(IOS) + GCController::singleton().garbageCollectNowIfNotDoneRecently(); +#else + GCController::singleton().garbageCollectSoon(); +#endif + } + + // We reduce tiling coverage while under memory pressure, so make sure to drop excess tiles ASAP. + Page::forEachPage([](Page& page) { + page.chrome().client().scheduleCompositingLayerFlush(); + }); +} + +void releaseMemory(Critical critical, Synchronous synchronous) +{ + if (critical == Critical::Yes) + releaseCriticalMemory(synchronous); + + releaseNoncriticalMemory(); + + platformReleaseMemory(critical); + + // FastMalloc has lock-free thread specific caches that can only be cleared from the thread itself. + WorkerThread::releaseFastMallocFreeMemoryInAllThreads(); +#if ENABLE(ASYNC_SCROLLING) && !PLATFORM(IOS) + ScrollingThread::dispatch([]() { + WTF::releaseFastMallocFreeMemory(); + }); +#endif + WTF::releaseFastMallocFreeMemory(); + +#if ENABLE(RESOURCE_USAGE) + Page::forEachPage([&](Page& page) { + InspectorInstrumentation::didHandleMemoryPressure(page, critical); + }); +#endif +} + +#if !RELEASE_LOG_DISABLED +static unsigned pageCount() +{ + unsigned count = 0; + Page::forEachPage([&] (Page& page) { + if (!page.isUtilityPage()) + ++count; + }); + return count; +} +#endif + +void logMemoryStatisticsAtTimeOfDeath() +{ +#if !RELEASE_LOG_DISABLED +#if PLATFORM(COCOA) + auto pageSize = vmPageSize(); + auto pages = pagesPerVMTag(); + + RELEASE_LOG(MemoryPressure, "Dirty memory per VM tag at time of death:"); + for (unsigned i = 0; i < 256; ++i) { + size_t dirty = pages[i].dirty * pageSize; + if (!dirty) + continue; + String tagName = displayNameForVMTag(i); + if (!tagName) + tagName = String::format("Tag %u", i); + RELEASE_LOG(MemoryPressure, "%16s: %lu MB", tagName.latin1().data(), dirty / MB); + } +#endif + + auto& vm = commonVM(); + RELEASE_LOG(MemoryPressure, "Memory usage statistics at time of death:"); + RELEASE_LOG(MemoryPressure, "GC heap size: %zu", vm.heap.size()); + RELEASE_LOG(MemoryPressure, "GC heap extra memory size: %zu", vm.heap.extraMemorySize()); +#if ENABLE(RESOURCE_USAGE) + RELEASE_LOG(MemoryPressure, "GC heap external memory: %zu", vm.heap.externalMemorySize()); +#endif + RELEASE_LOG(MemoryPressure, "Global object count: %zu", vm.heap.globalObjectCount()); + + RELEASE_LOG(MemoryPressure, "Page count: %u", pageCount()); + RELEASE_LOG(MemoryPressure, "Document count: %u", Document::allDocuments().size()); + RELEASE_LOG(MemoryPressure, "Live JavaScript objects:"); + for (auto& it : *vm.heap.objectTypeCounts()) + RELEASE_LOG(MemoryPressure, " %s: %d", it.key, it.value); +#endif +} + +void didExceedMemoryLimitAndFailedToRecover() +{ + RELEASE_LOG(MemoryPressure, "Crashing non-visible process due to excessive memory usage + inability to free up memory below panic threshold."); + logMemoryStatisticsAtTimeOfDeath(); + CRASH(); +} + +bool processIsEligibleForMemoryKill() +{ + bool hasVisiblePages = false; + bool hasAudiblePages = false; + bool hasMainFrameNavigatedInTheLastHour = false; + + auto now = MonotonicTime::now(); + Page::forEachPage([&] (Page& page) { + if (page.isUtilityPage()) + return; + if (page.isVisible()) + hasVisiblePages = true; + if (page.activityState() & ActivityState::IsAudible) + hasAudiblePages = true; + if (auto timeOfLastCompletedLoad = page.mainFrame().timeOfLastCompletedLoad()) { + if (now - timeOfLastCompletedLoad <= Seconds::fromMinutes(60)) + hasMainFrameNavigatedInTheLastHour = true; + } + }); + + bool eligible = !hasVisiblePages && !hasAudiblePages && !hasMainFrameNavigatedInTheLastHour; + if (!eligible) + RELEASE_LOG(MemoryPressure, "Process not eligible for panic memory kill. Reasons: hasVisiblePages=%u, hasAudiblePages=%u, hasMainFrameNavigatedInTheLastHour=%u", hasVisiblePages, hasAudiblePages, hasMainFrameNavigatedInTheLastHour); + + return eligible; +} + +#if !PLATFORM(COCOA) +void platformReleaseMemory(Critical) { } +void jettisonExpensiveObjectsOnTopLevelNavigation() { } +void registerMemoryReleaseNotifyCallbacks() { } +#endif + +} // namespace WebCore diff --git a/Source/WebCore/page/MemoryRelease.h b/Source/WebCore/page/MemoryRelease.h new file mode 100644 index 000000000..cd410a12c --- /dev/null +++ b/Source/WebCore/page/MemoryRelease.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * 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 "MemoryPressureHandler.h" + +namespace WebCore { + +WEBCORE_EXPORT void releaseMemory(Critical, Synchronous); +void platformReleaseMemory(Critical); +void jettisonExpensiveObjectsOnTopLevelNavigation(); +WEBCORE_EXPORT void registerMemoryReleaseNotifyCallbacks(); +WEBCORE_EXPORT void logMemoryStatisticsAtTimeOfDeath(); +WEBCORE_EXPORT NO_RETURN_DUE_TO_CRASH void didExceedMemoryLimitAndFailedToRecover(); +WEBCORE_EXPORT bool processIsEligibleForMemoryKill(); + +} // namespace WebCore diff --git a/Source/WebCore/page/MouseEventWithHitTestResults.cpp b/Source/WebCore/page/MouseEventWithHitTestResults.cpp index e0305687d..b93ac71b4 100644 --- a/Source/WebCore/page/MouseEventWithHitTestResults.cpp +++ b/Source/WebCore/page/MouseEventWithHitTestResults.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2006 Apple Computer, Inc. + Copyright (C) 2006 Apple Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/Source/WebCore/page/MouseEventWithHitTestResults.h b/Source/WebCore/page/MouseEventWithHitTestResults.h index e20790032..636187425 100644 --- a/Source/WebCore/page/MouseEventWithHitTestResults.h +++ b/Source/WebCore/page/MouseEventWithHitTestResults.h @@ -1,6 +1,6 @@ /* Copyright (C) 2000 Simon Hausmann <hausmann@kde.org> - Copyright (C) 2006 Apple Computer, Inc. + Copyright (C) 2006 Apple Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -18,8 +18,7 @@ Boston, MA 02110-1301, USA. */ -#ifndef MouseEventWithHitTestResults_h -#define MouseEventWithHitTestResults_h +#pragma once #include "HitTestResult.h" #include "PlatformMouseEvent.h" @@ -46,5 +45,3 @@ private: }; } // namespace WebCore - -#endif // MouseEventWithHitTestResults_h diff --git a/Source/WebCore/page/Navigator.cpp b/Source/WebCore/page/Navigator.cpp index b88015b79..ff60fcaaf 100644 --- a/Source/WebCore/page/Navigator.cpp +++ b/Source/WebCore/page/Navigator.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2000 Harri Porten (porten@kde.org) * Copyright (c) 2000 Daniel Molkentin (molkentin@kde.org) * Copyright (c) 2000 Stefan Schimanski (schimmi@kde.org) - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2003, 2004, 2005, 2006 Apple Inc. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or @@ -37,14 +37,14 @@ #include "ScriptController.h" #include "SecurityOrigin.h" #include "Settings.h" -#include "StorageNamespace.h" -#include <wtf/HashSet.h> #include <wtf/StdLibExtras.h> +using namespace WTF; + namespace WebCore { -Navigator::Navigator(Frame* frame) - : DOMWindowProperty(frame) +Navigator::Navigator(Frame& frame) + : DOMWindowProperty(&frame) { } @@ -56,14 +56,14 @@ Navigator::~Navigator() // appear in the appVersion string. This is to avoid problems with old versions of a // library called OpenCube QuickMenu, which as of this writing is still being used on // sites such as nwa.com -- the library thinks Safari is Netscape 4 if we don't do this! -static bool shouldHideFourDot(Frame* frame) +static bool shouldHideFourDot(Frame& frame) { - const String* sourceURL = frame->script().sourceURL(); + auto* sourceURL = frame.script().sourceURL(); if (!sourceURL) return false; if (!(sourceURL->endsWith("/dqm_script.js") || sourceURL->endsWith("/dqm_loader.js") || sourceURL->endsWith("/tdqm_loader.js"))) return false; - return frame->settings().needsSiteSpecificQuirks(); + return frame.settings().needsSiteSpecificQuirks(); } String Navigator::appVersion() const @@ -71,41 +71,36 @@ String Navigator::appVersion() const if (!m_frame) return String(); String appVersion = NavigatorBase::appVersion(); - if (shouldHideFourDot(m_frame)) + if (shouldHideFourDot(*m_frame)) appVersion.replace("4.", "4_"); return appVersion; } -String Navigator::language() const -{ - return defaultLanguage(); -} - String Navigator::userAgent() const { if (!m_frame) return String(); - + // If the frame is already detached, FrameLoader::userAgent may malfunction, because it calls a client method // that uses frame's WebView (at least, in Mac WebKit). if (!m_frame->page()) return String(); - + return m_frame->loader().userAgent(m_frame->document()->url()); } -DOMPluginArray* Navigator::plugins() const +DOMPluginArray& Navigator::plugins() { if (!m_plugins) m_plugins = DOMPluginArray::create(m_frame); - return m_plugins.get(); + return *m_plugins; } -DOMMimeTypeArray* Navigator::mimeTypes() const +DOMMimeTypeArray& Navigator::mimeTypes() { if (!m_mimeTypes) m_mimeTypes = DOMMimeTypeArray::create(m_frame); - return m_mimeTypes.get(); + return *m_mimeTypes; } bool Navigator::cookieEnabled() const @@ -116,7 +111,11 @@ bool Navigator::cookieEnabled() const if (m_frame->page() && !m_frame->page()->settings().cookieEnabled()) return false; - return cookiesEnabled(m_frame->document()); + auto* document = m_frame->document(); + if (!document) + return false; + + return cookiesEnabled(*document); } bool Navigator::javaEnabled() const @@ -126,22 +125,23 @@ bool Navigator::javaEnabled() const if (!m_frame->settings().isJavaEnabled()) return false; - if (m_frame->document()->securityOrigin()->isLocal() && !m_frame->settings().isJavaEnabledForLocalFiles()) + if (m_frame->document()->securityOrigin().isLocal() && !m_frame->settings().isJavaEnabledForLocalFiles()) return false; return true; } #if PLATFORM(IOS) + bool Navigator::standalone() const { return m_frame && m_frame->settings().standalone(); } + #endif void Navigator::getStorageUpdates() { - // FIXME: Remove this method or rename to yieldForStorageUpdates. } } // namespace WebCore diff --git a/Source/WebCore/page/Navigator.h b/Source/WebCore/page/Navigator.h index 044485e44..78d9e06be 100644 --- a/Source/WebCore/page/Navigator.h +++ b/Source/WebCore/page/Navigator.h @@ -17,56 +17,41 @@ Boston, MA 02110-1301, USA. */ -#ifndef Navigator_h -#define Navigator_h +#pragma once #include "DOMWindowProperty.h" #include "NavigatorBase.h" #include "ScriptWrappable.h" #include "Supplementable.h" -#include <wtf/Forward.h> -#include <wtf/HashMap.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> namespace WebCore { class DOMMimeTypeArray; class DOMPluginArray; -class Frame; -class PluginData; -typedef int ExceptionCode; - -class Navigator : public NavigatorBase, public ScriptWrappable, public RefCounted<Navigator>, public DOMWindowProperty, public Supplementable<Navigator> { +class Navigator final : public NavigatorBase, public ScriptWrappable, public DOMWindowProperty, public Supplementable<Navigator> { public: - static PassRefPtr<Navigator> create(Frame* frame) { return adoptRef(new Navigator(frame)); } + static Ref<Navigator> create(Frame& frame) { return adoptRef(*new Navigator(frame)); } virtual ~Navigator(); String appVersion() const; - String language() const; - DOMPluginArray* plugins() const; - DOMMimeTypeArray* mimeTypes() const; + DOMPluginArray& plugins(); + DOMMimeTypeArray& mimeTypes(); bool cookieEnabled() const; bool javaEnabled() const; - - virtual String userAgent() const; + String userAgent() const final; #if PLATFORM(IOS) bool standalone() const; #endif - // Relinquishes the storage lock, if one exists. void getStorageUpdates(); private: - explicit Navigator(Frame*); + explicit Navigator(Frame&); mutable RefPtr<DOMPluginArray> m_plugins; mutable RefPtr<DOMMimeTypeArray> m_mimeTypes; }; } - -#endif diff --git a/Source/WebCore/page/Navigator.idl b/Source/WebCore/page/Navigator.idl index 638d5a56c..f7c4ae28f 100644 --- a/Source/WebCore/page/Navigator.idl +++ b/Source/WebCore/page/Navigator.idl @@ -20,23 +20,11 @@ [ GenerateIsReachable=ImplFrame, ] interface Navigator { - readonly attribute DOMString appCodeName; - readonly attribute DOMString appName; - readonly attribute DOMString appVersion; - readonly attribute DOMString language; - readonly attribute DOMString userAgent; - readonly attribute DOMString platform; readonly attribute DOMPluginArray plugins; readonly attribute DOMMimeTypeArray mimeTypes; - readonly attribute DOMString product; - readonly attribute DOMString productSub; - readonly attribute DOMString vendor; - readonly attribute DOMString vendorSub; readonly attribute boolean cookieEnabled; boolean javaEnabled(); - readonly attribute boolean onLine; - // FIXME: Convert this #if'def to an IDL conditional attribute. #if defined(WTF_PLATFORM_IOS) && WTF_PLATFORM_IOS readonly attribute boolean standalone; @@ -45,3 +33,7 @@ void getStorageUpdates(); // FIXME: Remove this method or rename to yieldForStorageUpdates. }; +Navigator implements NavigatorConcurrentHardware; +Navigator implements NavigatorID; +Navigator implements NavigatorLanguage; +Navigator implements NavigatorOnLine; diff --git a/Source/WebCore/page/NavigatorBase.cpp b/Source/WebCore/page/NavigatorBase.cpp index eb2f8f7cc..a33214b80 100644 --- a/Source/WebCore/page/NavigatorBase.cpp +++ b/Source/WebCore/page/NavigatorBase.cpp @@ -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,7 +27,11 @@ #include "config.h" #include "NavigatorBase.h" +#include "Language.h" #include "NetworkStateNotifier.h" +#include <mutex> +#include <wtf/NeverDestroyed.h> +#include <wtf/NumberOfCores.h> #include <wtf/text/WTFString.h> #if OS(LINUX) @@ -36,49 +40,48 @@ #endif #if PLATFORM(IOS) -#include "WebCoreSystemInterface.h" +#include "Device.h" #endif #ifndef WEBCORE_NAVIGATOR_PLATFORM #if PLATFORM(IOS) -#define WEBCORE_NAVIGATOR_PLATFORM "" +#define WEBCORE_NAVIGATOR_PLATFORM deviceName() #elif OS(MAC_OS_X) && (CPU(PPC) || CPU(PPC64)) -#define WEBCORE_NAVIGATOR_PLATFORM "MacPPC" +#define WEBCORE_NAVIGATOR_PLATFORM ASCIILiteral("MacPPC") #elif OS(MAC_OS_X) && (CPU(X86) || CPU(X86_64)) -#define WEBCORE_NAVIGATOR_PLATFORM "MacIntel" +#define WEBCORE_NAVIGATOR_PLATFORM ASCIILiteral("MacIntel") #elif OS(WINDOWS) -#define WEBCORE_NAVIGATOR_PLATFORM "Win32" +#define WEBCORE_NAVIGATOR_PLATFORM ASCIILiteral("Win32") #else -#define WEBCORE_NAVIGATOR_PLATFORM "" +#define WEBCORE_NAVIGATOR_PLATFORM emptyString() #endif #endif // ifndef WEBCORE_NAVIGATOR_PLATFORM #ifndef WEBCORE_NAVIGATOR_PRODUCT -#define WEBCORE_NAVIGATOR_PRODUCT "Gecko" +#define WEBCORE_NAVIGATOR_PRODUCT ASCIILiteral("Gecko") #endif // ifndef WEBCORE_NAVIGATOR_PRODUCT #ifndef WEBCORE_NAVIGATOR_PRODUCT_SUB -#define WEBCORE_NAVIGATOR_PRODUCT_SUB "20030107" +#define WEBCORE_NAVIGATOR_PRODUCT_SUB ASCIILiteral("20030107") #endif // ifndef WEBCORE_NAVIGATOR_PRODUCT_SUB #ifndef WEBCORE_NAVIGATOR_VENDOR -#define WEBCORE_NAVIGATOR_VENDOR "Apple Computer, Inc." +#define WEBCORE_NAVIGATOR_VENDOR ASCIILiteral("Apple Computer, Inc.") #endif // ifndef WEBCORE_NAVIGATOR_VENDOR #ifndef WEBCORE_NAVIGATOR_VENDOR_SUB -#define WEBCORE_NAVIGATOR_VENDOR_SUB "" +#define WEBCORE_NAVIGATOR_VENDOR_SUB emptyString() #endif // ifndef WEBCORE_NAVIGATOR_VENDOR_SUB - namespace WebCore { NavigatorBase::~NavigatorBase() { } -String NavigatorBase::appName() const +String NavigatorBase::appName() { - return "Netscape"; + return ASCIILiteral("Netscape"); } String NavigatorBase::appVersion() const @@ -88,47 +91,84 @@ String NavigatorBase::appVersion() const return agent.substring(agent.find('/') + 1); } -String NavigatorBase::platform() const +String NavigatorBase::platform() { #if OS(LINUX) if (!String(WEBCORE_NAVIGATOR_PLATFORM).isEmpty()) return WEBCORE_NAVIGATOR_PLATFORM; struct utsname osname; - DEFINE_STATIC_LOCAL(String, platformName, (uname(&osname) >= 0 ? String(osname.sysname) + String(" ") + String(osname.machine) : emptyString())); + static NeverDestroyed<String> platformName(uname(&osname) >= 0 ? String(osname.sysname) + String(" ") + String(osname.machine) : emptyString()); return platformName; #else return WEBCORE_NAVIGATOR_PLATFORM; #endif } -String NavigatorBase::appCodeName() const +String NavigatorBase::appCodeName() { - return "Mozilla"; + return ASCIILiteral("Mozilla"); } -String NavigatorBase::product() const +String NavigatorBase::product() { return WEBCORE_NAVIGATOR_PRODUCT; } -String NavigatorBase::productSub() const +String NavigatorBase::productSub() { return WEBCORE_NAVIGATOR_PRODUCT_SUB; } -String NavigatorBase::vendor() const +String NavigatorBase::vendor() { return WEBCORE_NAVIGATOR_VENDOR; } -String NavigatorBase::vendorSub() const +String NavigatorBase::vendorSub() { return WEBCORE_NAVIGATOR_VENDOR_SUB; } -bool NavigatorBase::onLine() const +bool NavigatorBase::onLine() { return networkStateNotifier().onLine(); } +String NavigatorBase::language() +{ + return defaultLanguage(); +} + +Vector<String> NavigatorBase::languages() +{ + // We intentionally expose only the primary language for privacy reasons. + return { defaultLanguage() }; +} + +#if ENABLE(NAVIGATOR_HWCONCURRENCY) + +int NavigatorBase::hardwareConcurrency() +{ + static int numberOfCores; + + static std::once_flag once; + std::call_once(once, [] { + // Enforce a maximum for the number of cores reported to mitigate + // fingerprinting for the minority of machines with large numbers of cores. + // If machines with more than 8 cores become commonplace, we should bump this number. + // see https://bugs.webkit.org/show_bug.cgi?id=132588 for the + // rationale behind this decision. +#if PLATFORM(IOS) + const int maxCoresToReport = 2; +#else + const int maxCoresToReport = 8; +#endif + numberOfCores = std::min(WTF::numberOfProcessorCores(), maxCoresToReport); + }); + + return numberOfCores; +} + +#endif + } // namespace WebCore diff --git a/Source/WebCore/page/NavigatorBase.h b/Source/WebCore/page/NavigatorBase.h index 8f576e387..968a8e64d 100644 --- a/Source/WebCore/page/NavigatorBase.h +++ b/Source/WebCore/page/NavigatorBase.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,32 +23,37 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NavigatorBase_h -#define NavigatorBase_h +#pragma once #include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> namespace WebCore { - class NavigatorBase { - public: - String appName() const; - String appVersion() const; - virtual String userAgent() const = 0; - String platform() const; +class NavigatorBase : public RefCounted<NavigatorBase> { +public: + virtual ~NavigatorBase(); - String appCodeName() const; - String product() const; - String productSub() const; - String vendor() const; - String vendorSub() const; + static String appName(); + String appVersion() const; + virtual String userAgent() const = 0; + static String platform(); - bool onLine() const; + static String appCodeName(); + static String product(); + static String productSub(); + static String vendor(); + static String vendorSub(); - protected: - virtual ~NavigatorBase(); - }; + static bool onLine(); -} // namespace WebCore + static String language(); + static Vector<String> languages(); + +#if ENABLE(NAVIGATOR_HWCONCURRENCY) + static int hardwareConcurrency(); +#endif +}; -#endif // NavigatorBase_h +} // namespace WebCore diff --git a/Source/WebCore/page/GroupSettings.cpp b/Source/WebCore/page/NavigatorConcurrentHardware.idl index 0762861e4..b19219a33 100644 --- a/Source/WebCore/page/GroupSettings.cpp +++ b/Source/WebCore/page/NavigatorConcurrentHardware.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -10,6 +10,9 @@ * 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 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 @@ -23,31 +26,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "GroupSettings.h" - -namespace WebCore { - -GroupSettings::GroupSettings() - : m_localStorageQuotaBytes(5 * 1024 * 1024) // Suggested by the HTML5 spec. - , m_indexedDBQuotaBytes(5 * 1024 * 1024) -{ -} - -void GroupSettings::setLocalStorageQuotaBytes(unsigned quota) -{ - m_localStorageQuotaBytes = quota; -} - -void GroupSettings::setIndexedDBDatabasePath(const String& path) -{ - m_indexedDBDatabasePath = path; -} - -void GroupSettings::setIndexedDBQuotaBytes(int64_t quota) -{ - m_indexedDBQuotaBytes = quota; -} - - -} // namespace WebCore +[ + NoInterfaceObject +] interface NavigatorConcurrentHardware { + [Conditional=NAVIGATOR_HWCONCURRENCY] readonly attribute unsigned long long hardwareConcurrency; +}; diff --git a/Source/WebCore/page/NavigatorID.idl b/Source/WebCore/page/NavigatorID.idl new file mode 100644 index 000000000..642007e08 --- /dev/null +++ b/Source/WebCore/page/NavigatorID.idl @@ -0,0 +1,41 @@ +/* + * 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. + * 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. + * + * 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 +] interface NavigatorID { + [Nondeterministic] readonly attribute DOMString appCodeName; + [Nondeterministic] readonly attribute DOMString appName; + [Nondeterministic] readonly attribute DOMString appVersion; + [Nondeterministic] readonly attribute DOMString platform; + [Nondeterministic] readonly attribute DOMString product; + [Nondeterministic, Exposed=Window] readonly attribute DOMString productSub; + [Nondeterministic] readonly attribute DOMString userAgent; + [Nondeterministic, Exposed=Window] readonly attribute DOMString vendor; + [Nondeterministic, Exposed=Window] readonly attribute DOMString vendorSub; +}; diff --git a/Source/WebCore/page/GroupSettings.h b/Source/WebCore/page/NavigatorLanguage.idl index e094f09c2..5f5d22dc1 100644 --- a/Source/WebCore/page/GroupSettings.h +++ b/Source/WebCore/page/NavigatorLanguage.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. All rights reserved. + * 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 @@ -10,6 +10,9 @@ * 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 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 @@ -22,36 +25,10 @@ * (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 GroupSettings_h -#define GroupSettings_h -#include <wtf/text/WTFString.h> - -namespace WebCore { - -class PageGroup; - -class GroupSettings { - WTF_MAKE_NONCOPYABLE(GroupSettings); WTF_MAKE_FAST_ALLOCATED; -public: - GroupSettings(); - - void setLocalStorageQuotaBytes(unsigned); - unsigned localStorageQuotaBytes() const { return m_localStorageQuotaBytes; } - - void setIndexedDBQuotaBytes(int64_t); - int64_t indexedDBQuotaBytes() const { return m_indexedDBQuotaBytes; } - - void setIndexedDBDatabasePath(const String&); - const String& indexedDBDatabasePath() const { return m_indexedDBDatabasePath; } - -private: - unsigned m_localStorageQuotaBytes; - String m_indexedDBDatabasePath; - int64_t m_indexedDBQuotaBytes; +[ + NoInterfaceObject +] interface NavigatorLanguage { + [Nondeterministic] readonly attribute DOMString language; + readonly attribute FrozenArray<DOMString> languages; }; - -} // namespace WebCore - -#endif // GroupSettings_h diff --git a/Source/WebCore/page/NavigatorOnLine.idl b/Source/WebCore/page/NavigatorOnLine.idl new file mode 100644 index 000000000..c92763502 --- /dev/null +++ b/Source/WebCore/page/NavigatorOnLine.idl @@ -0,0 +1,33 @@ +/* + * 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. + * 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. + * + * 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 +] interface NavigatorOnLine { + readonly attribute boolean onLine; +}; diff --git a/Source/WebCore/page/OriginAccessEntry.cpp b/Source/WebCore/page/OriginAccessEntry.cpp index 7ff67cc46..8472d38eb 100644 --- a/Source/WebCore/page/OriginAccessEntry.cpp +++ b/Source/WebCore/page/OriginAccessEntry.cpp @@ -35,10 +35,11 @@ namespace WebCore { -OriginAccessEntry::OriginAccessEntry(const String& protocol, const String& host, SubdomainSetting subdomainSetting) - : m_protocol(protocol.lower()) - , m_host(host.lower()) +OriginAccessEntry::OriginAccessEntry(const String& protocol, const String& host, SubdomainSetting subdomainSetting, IPAddressSetting ipAddressSetting) + : m_protocol(protocol.convertToASCIILowercase()) + , m_host(host.convertToASCIILowercase()) , m_subdomainSettings(subdomainSetting) + , m_ipAddressSettings(ipAddressSetting) { ASSERT(subdomainSetting == AllowSubdomains || subdomainSetting == DisallowSubdomains); @@ -48,8 +49,8 @@ OriginAccessEntry::OriginAccessEntry(const String& protocol, const String& host, bool OriginAccessEntry::matchesOrigin(const SecurityOrigin& origin) const { - ASSERT(origin.host() == origin.host().lower()); - ASSERT(origin.protocol() == origin.protocol().lower()); + ASSERT(origin.host() == origin.host().convertToASCIILowercase()); + ASSERT(origin.protocol() == origin.protocol().convertToASCIILowercase()); if (m_protocol != origin.protocol()) return false; @@ -65,9 +66,10 @@ bool OriginAccessEntry::matchesOrigin(const SecurityOrigin& origin) const // Otherwise we can only match if we're matching subdomains. if (m_subdomainSettings == DisallowSubdomains) return false; - + + // IP addresses are not domains: https://url.spec.whatwg.org/#concept-domain // Don't try to do subdomain matching on IP addresses. - if (m_hostIsIPAddress) + if (m_hostIsIPAddress && m_ipAddressSettings == TreatIPAddressAsIPAddress) return false; // Match subdomains. diff --git a/Source/WebCore/page/OriginAccessEntry.h b/Source/WebCore/page/OriginAccessEntry.h index 183600bde..f55f7d938 100644 --- a/Source/WebCore/page/OriginAccessEntry.h +++ b/Source/WebCore/page/OriginAccessEntry.h @@ -28,8 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OriginAccessEntry_h -#define OriginAccessEntry_h +#pragma once #include <wtf/text/WTFString.h> @@ -44,24 +43,34 @@ public: DisallowSubdomains }; + enum IPAddressSetting { + TreatIPAddressAsDomain, + TreatIPAddressAsIPAddress + }; + // If host is empty string and SubdomainSetting is AllowSubdomains, the entry will match all domains in the specified protocol. - OriginAccessEntry(const String& protocol, const String& host, SubdomainSetting); + OriginAccessEntry(const String& protocol, const String& host, SubdomainSetting, IPAddressSetting); bool matchesOrigin(const SecurityOrigin&) const; const String& protocol() const { return m_protocol; } const String& host() const { return m_host; } SubdomainSetting subdomainSettings() const { return m_subdomainSettings; } + IPAddressSetting ipAddressSettings() const { return m_ipAddressSettings; } private: String m_protocol; String m_host; SubdomainSetting m_subdomainSettings; + IPAddressSetting m_ipAddressSettings; bool m_hostIsIPAddress; }; inline bool operator==(const OriginAccessEntry& a, const OriginAccessEntry& b) { - return equalIgnoringCase(a.protocol(), b.protocol()) && equalIgnoringCase(a.host(), b.host()) && a.subdomainSettings() == b.subdomainSettings(); + return equalIgnoringASCIICase(a.protocol(), b.protocol()) + && equalIgnoringASCIICase(a.host(), b.host()) + && a.subdomainSettings() == b.subdomainSettings() + && a.ipAddressSettings() == b.ipAddressSettings(); } inline bool operator!=(const OriginAccessEntry& a, const OriginAccessEntry& b) @@ -70,5 +79,3 @@ inline bool operator!=(const OriginAccessEntry& a, const OriginAccessEntry& b) } } // namespace WebCore - -#endif // OriginAccessEntry_h diff --git a/Source/WebCore/page/Page.cpp b/Source/WebCore/page/Page.cpp index d5d994919..1fd0aad74 100644 --- a/Source/WebCore/page/Page.cpp +++ b/Source/WebCore/page/Page.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All Rights Reserved. + * Copyright (C) 2006-2015 Apple Inc. All Rights Reserved. * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or @@ -20,25 +20,29 @@ #include "config.h" #include "Page.h" +#include "ActivityStateChangeObserver.h" #include "AlternativeTextClient.h" -#include "AnimationController.h" +#include "ApplicationCacheStorage.h" #include "BackForwardClient.h" #include "BackForwardController.h" +#include "CSSAnimationController.h" #include "Chrome.h" #include "ChromeClient.h" #include "ClientRectList.h" #include "ContextMenuClient.h" #include "ContextMenuController.h" -#include "DOMWindow.h" +#include "DatabaseProvider.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" +#include "DocumentLoader.h" #include "DocumentMarkerController.h" -#include "DocumentStyleSheetCollection.h" #include "DragController.h" #include "Editor.h" #include "EditorClient.h" +#include "EmptyClients.h" #include "Event.h" #include "EventNames.h" -#include "ExceptionCode.h" -#include "ExceptionCodePlaceholder.h" +#include "ExtensionStyleSheets.h" #include "FileSystem.h" #include "FocusController.h" #include "FrameLoader.h" @@ -51,104 +55,159 @@ #include "HistoryItem.h" #include "InspectorController.h" #include "InspectorInstrumentation.h" +#include "LibWebRTCProvider.h" #include "Logging.h" #include "MainFrame.h" #include "MediaCanStartListener.h" #include "Navigator.h" #include "NetworkStateNotifier.h" -#include "PageActivityAssertionToken.h" #include "PageCache.h" -#include "PageConsole.h" +#include "PageConfiguration.h" +#include "PageConsoleClient.h" #include "PageDebuggable.h" #include "PageGroup.h" -#include "PageThrottler.h" +#include "PageOverlayController.h" +#include "PerformanceMonitor.h" +#include "PlatformMediaSessionManager.h" #include "PlugInClient.h" #include "PluginData.h" -#include "PluginView.h" +#include "PluginInfoProvider.h" +#include "PluginViewBase.h" #include "PointerLockController.h" #include "ProgressTracker.h" #include "RenderLayerCompositor.h" #include "RenderTheme.h" #include "RenderView.h" #include "RenderWidget.h" +#include "ResourceUsageOverlay.h" #include "RuntimeEnabledFeatures.h" +#include "SVGDocumentExtensions.h" #include "SchemeRegistry.h" #include "ScriptController.h" #include "ScrollingCoordinator.h" #include "Settings.h" #include "SharedBuffer.h" +#include "SocketProvider.h" #include "StorageArea.h" #include "StorageNamespace.h" +#include "StorageNamespaceProvider.h" #include "StyleResolver.h" +#include "StyleScope.h" #include "SubframeLoader.h" #include "TextResourceDecoder.h" -#include "UserContentController.h" +#include "UserContentProvider.h" +#include "UserInputBridge.h" +#include "ValidationMessageClient.h" #include "VisitedLinkState.h" +#include "VisitedLinkStore.h" #include "VoidCallback.h" +#include "WebGLStateTracker.h" #include "Widget.h" -#include <wtf/HashMap.h> +#include <wtf/CurrentTime.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/StdLibExtras.h> #include <wtf/text/Base64.h> #include <wtf/text/StringHash.h> +#if ENABLE(WEB_REPLAY) +#include "ReplayController.h" +#include <replay/InputCursor.h> +#endif + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) +#include "HTMLVideoElement.h" +#include "MediaPlaybackTarget.h" +#endif + +#if ENABLE(MEDIA_SESSION) +#include "MediaSessionManager.h" +#endif + +#if ENABLE(INDEXED_DATABASE) +#include "IDBConnectionToServer.h" +#include "InProcessIDBServer.h" +#endif + +#if ENABLE(DATA_INTERACTION) +#include "SelectionRect.h" +#endif + namespace WebCore { static HashSet<Page*>* allPages; +static unsigned nonUtilityPageCount { 0 }; + +static inline bool isUtilityPageChromeClient(ChromeClient& chromeClient) +{ + return chromeClient.isEmptyChromeClient() || chromeClient.isSVGImageChromeClient(); +} DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, pageCounter, ("Page")); +void Page::forEachPage(std::function<void(Page&)> function) +{ + if (!allPages) + return; + for (Page* page : *allPages) + function(*page); +} + +void Page::updateValidationBubbleStateIfNeeded() +{ + if (auto* client = validationMessageClient()) + client->updateValidationBubbleStateIfNeeded(); +} + static void networkStateChanged(bool isOnLine) { Vector<Ref<Frame>> frames; // Get all the frames of all the pages in all the page groups - for (auto it = allPages->begin(), end = allPages->end(); it != end; ++it) { - for (Frame* frame = &(*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) + for (auto& page : *allPages) { + for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) frames.append(*frame); - InspectorInstrumentation::networkStateChanged(*it); + InspectorInstrumentation::networkStateChanged(*page); } AtomicString eventName = isOnLine ? eventNames().onlineEvent : eventNames().offlineEvent; - for (unsigned i = 0; i < frames.size(); i++) - frames[i]->document()->dispatchWindowEvent(Event::create(eventName, false, false)); + for (auto& frame : frames) { + if (!frame->document()) + continue; + frame->document()->dispatchWindowEvent(Event::create(eventName, false, false)); + } } -float deviceScaleFactor(Frame* frame) -{ - if (!frame) - return 1; - Page* page = frame->page(); - if (!page) - return 1; - return page->deviceScaleFactor(); -} +static const ActivityState::Flags PageInitialActivityState = ActivityState::IsVisible | ActivityState::IsInWindow; -Page::Page(PageClients& pageClients) - : m_chrome(std::make_unique<Chrome>(*this, *pageClients.chromeClient)) +Page::Page(PageConfiguration&& pageConfiguration) + : m_chrome(std::make_unique<Chrome>(*this, *pageConfiguration.chromeClient)) , m_dragCaretController(std::make_unique<DragCaretController>()) #if ENABLE(DRAG_SUPPORT) - , m_dragController(std::make_unique<DragController>(*this, *pageClients.dragClient)) + , m_dragController(std::make_unique<DragController>(*this, *pageConfiguration.dragClient)) #endif - , m_focusController(std::make_unique<FocusController>(*this)) + , m_focusController(std::make_unique<FocusController>(*this, PageInitialActivityState)) #if ENABLE(CONTEXT_MENUS) - , m_contextMenuController(std::make_unique<ContextMenuController>(*this, *pageClients.contextMenuClient)) + , m_contextMenuController(std::make_unique<ContextMenuController>(*this, *pageConfiguration.contextMenuClient)) #endif -#if ENABLE(INSPECTOR) - , m_inspectorController(std::make_unique<InspectorController>(*this, pageClients.inspectorClient)) + , m_userInputBridge(std::make_unique<UserInputBridge>(*this)) +#if ENABLE(WEB_REPLAY) + , m_replayController(std::make_unique<ReplayController>(*this)) #endif + , m_inspectorController(std::make_unique<InspectorController>(*this, pageConfiguration.inspectorClient)) #if ENABLE(POINTER_LOCK) - , m_pointerLockController(PointerLockController::create(this)) + , m_pointerLockController(std::make_unique<PointerLockController>(*this)) #endif , m_settings(Settings::create(this)) - , m_progress(std::make_unique<ProgressTracker>(*pageClients.progressTrackerClient)) - , m_backForwardController(std::make_unique<BackForwardController>(*this, pageClients.backForwardClient)) - , m_mainFrame(MainFrame::create(*this, *pageClients.loaderClientForMainFrame)) + , m_progress(std::make_unique<ProgressTracker>(*pageConfiguration.progressTrackerClient)) + , m_backForwardController(std::make_unique<BackForwardController>(*this, *WTFMove(pageConfiguration.backForwardClient))) + , m_mainFrame(MainFrame::create(*this, pageConfiguration)) , m_theme(RenderTheme::themeForPage(this)) - , m_editorClient(pageClients.editorClient) - , m_plugInClient(pageClients.plugInClient) - , m_validationMessageClient(pageClients.validationMessageClient) - , m_subframeCount(0) + , m_editorClient(WTFMove(pageConfiguration.editorClient)) + , m_plugInClient(pageConfiguration.plugInClient) + , m_validationMessageClient(WTFMove(pageConfiguration.validationMessageClient)) + , m_diagnosticLoggingClient(WTFMove(pageConfiguration.diagnosticLoggingClient)) + , m_webGLStateTracker(WTFMove(pageConfiguration.webGLStateTracker)) + , m_libWebRTCProvider(WTFMove(pageConfiguration.libWebRTCProvider)) , m_openedByDOM(false) , m_tabKeyCyclesThroughElements(true) , m_defersLoading(false) @@ -157,24 +216,27 @@ Page::Page(PageClients& pageClients) , m_areMemoryCacheClientCallsEnabled(true) , m_mediaVolume(1) , m_pageScaleFactor(1) - , m_deviceScaleFactor(1) + , m_zoomedOutPageScaleFactor(0) + , m_topContentInset(0) +#if ENABLE(TEXT_AUTOSIZING) + , m_textAutosizingWidth(0) +#endif , m_suppressScrollbarAnimations(false) + , m_verticalScrollElasticity(ScrollElasticityAllowed) + , m_horizontalScrollElasticity(ScrollElasticityAllowed) , m_didLoadUserStyleSheet(false) , m_userStyleSheetModificationTime(0) - , m_group(0) - , m_debugger(0) - , m_customHTMLTokenizerTimeDelay(-1) - , m_customHTMLTokenizerChunkSize(-1) + , m_group(nullptr) + , m_debugger(nullptr) , m_canStartMedia(true) #if ENABLE(VIEW_MODE_CSS_MEDIA) , m_viewMode(ViewModeWindowed) #endif // ENABLE(VIEW_MODE_CSS_MEDIA) - , m_minimumTimerInterval(Settings::defaultMinDOMTimerInterval()) - , m_timerAlignmentInterval(Settings::defaultDOMTimerAlignmentInterval()) + , m_timerAlignmentInterval(DOMTimer::defaultAlignmentInterval()) + , m_timerAlignmentIntervalIncreaseTimer(*this, &Page::timerAlignmentIntervalIncreaseTimerFired) , m_isEditable(false) - , m_isInWindow(true) - , m_isVisible(true) , m_isPrerender(false) + , m_activityState(PageInitialActivityState) , m_requestedLayoutMilestones(0) , m_headerHeight(0) , m_footerHeight(0) @@ -182,17 +244,32 @@ Page::Page(PageClients& pageClients) #ifndef NDEBUG , m_isPainting(false) #endif - , m_alternativeTextClient(pageClients.alternativeTextClient) + , m_alternativeTextClient(pageConfiguration.alternativeTextClient) , m_scriptedAnimationsSuspended(false) - , m_pageThrottler(std::make_unique<PageThrottler>(*this)) - , m_console(std::make_unique<PageConsole>(*this)) + , m_consoleClient(std::make_unique<PageConsoleClient>(*this)) #if ENABLE(REMOTE_INSPECTOR) , m_inspectorDebuggable(std::make_unique<PageDebuggable>(*this)) #endif , m_lastSpatialNavigationCandidatesCount(0) // NOTE: Only called from Internals for Spatial Navigation testing. - , m_framesHandlingBeforeUnloadEvent(0) -{ - ASSERT(m_editorClient); + , m_forbidPromptsDepth(0) + , m_socketProvider(WTFMove(pageConfiguration.socketProvider)) + , m_applicationCacheStorage(*WTFMove(pageConfiguration.applicationCacheStorage)) + , m_databaseProvider(*WTFMove(pageConfiguration.databaseProvider)) + , m_pluginInfoProvider(*WTFMove(pageConfiguration.pluginInfoProvider)) + , m_storageNamespaceProvider(*WTFMove(pageConfiguration.storageNamespaceProvider)) + , m_userContentProvider(*WTFMove(pageConfiguration.userContentProvider)) + , m_visitedLinkStore(*WTFMove(pageConfiguration.visitedLinkStore)) + , m_sessionID(SessionID::defaultSessionID()) + , m_isClosing(false) + , m_isUtilityPage(isUtilityPageChromeClient(chrome().client())) + , m_performanceMonitor(isUtilityPage() ? nullptr : std::make_unique<PerformanceMonitor>(*this)) +{ + updateTimerThrottlingState(); + + m_pluginInfoProvider->addPage(*this); + m_storageNamespaceProvider->addPage(*this); + m_userContentProvider->addPage(*this); + m_visitedLinkStore->addPage(*this); if (!allPages) { allPages = new HashSet<Page*>; @@ -202,6 +279,8 @@ Page::Page(PageClients& pageClients) ASSERT(!allPages->contains(this)); allPages->add(this); + if (!isUtilityPage()) + ++nonUtilityPageCount; #ifndef NDEBUG pageCounter.increment(); @@ -210,42 +289,67 @@ Page::Page(PageClients& pageClients) #if ENABLE(REMOTE_INSPECTOR) m_inspectorDebuggable->init(); #endif + +#if PLATFORM(COCOA) + platformInitialize(); +#endif } Page::~Page() { - m_mainFrame->setView(0); + ASSERT(!m_nestedRunLoopCount); + ASSERT(!m_unnestCallback); + + m_validationMessageClient = nullptr; + m_diagnosticLoggingClient = nullptr; + m_mainFrame->setView(nullptr); setGroupName(String()); allPages->remove(this); + if (!isUtilityPage()) + --nonUtilityPageCount; m_settings->pageDestroyed(); + m_inspectorController->inspectedPageDestroyed(); + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { frame->willDetachPage(); frame->detachFromPage(); } - m_editorClient->pageDestroyed(); if (m_plugInClient) m_plugInClient->pageDestroyed(); if (m_alternativeTextClient) m_alternativeTextClient->pageDestroyed(); -#if ENABLE(INSPECTOR) - m_inspectorController->inspectedPageDestroyed(); -#endif - if (m_scrollingCoordinator) m_scrollingCoordinator->pageDestroyed(); backForward().close(); + PageCache::singleton().removeAllItemsForPage(*this); #ifndef NDEBUG pageCounter.decrement(); #endif - if (m_userContentController) - m_userContentController->removePage(*this); + m_pluginInfoProvider->removePage(*this); + m_storageNamespaceProvider->removePage(*this); + m_userContentProvider->removePage(*this); + m_visitedLinkStore->removePage(*this); +} + +void Page::clearPreviousItemFromAllPages(HistoryItem* item) +{ + if (!allPages) + return; + + for (auto& page : *allPages) { + HistoryController& controller = page->mainFrame().loader().history(); + if (item == controller.previousItem()) { + controller.clearPreviousItem(); + return; + } + } } uint64_t Page::renderTreeSize() const @@ -267,7 +371,7 @@ ViewportArguments Page::viewportArguments() const ScrollingCoordinator* Page::scrollingCoordinator() { if (!m_scrollingCoordinator && m_settings->scrollingCoordinatorEnabled()) { - m_scrollingCoordinator = chrome().client().createScrollingCoordinator(this); + m_scrollingCoordinator = chrome().client().createScrollingCoordinator(*this); if (!m_scrollingCoordinator) m_scrollingCoordinator = ScrollingCoordinator::create(this); } @@ -297,18 +401,22 @@ String Page::synchronousScrollingReasonsAsText() return String(); } -PassRefPtr<ClientRectList> Page::nonFastScrollableRects(const Frame* frame) +Ref<ClientRectList> Page::nonFastScrollableRects() { if (Document* document = m_mainFrame->document()) document->updateLayout(); Vector<IntRect> rects; - if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) - rects = scrollingCoordinator->computeNonFastScrollableRegion(frame, IntPoint()).rects(); + if (ScrollingCoordinator* scrollingCoordinator = this->scrollingCoordinator()) { + const EventTrackingRegions& eventTrackingRegions = scrollingCoordinator->absoluteEventTrackingRegions(); + for (const auto& synchronousEventRegion : eventTrackingRegions.eventSpecificSynchronousDispatchRegions) + rects.appendVector(synchronousEventRegion.value.rects()); + } Vector<FloatQuad> quads(rects.size()); for (size_t i = 0; i < rects.size(); ++i) quads[i] = FloatRect(rects[i]); + return ClientRectList::create(quads); } @@ -318,7 +426,7 @@ struct ViewModeInfo { Page::ViewMode type; }; static const int viewModeMapSize = 5; -static ViewModeInfo viewModeMap[viewModeMapSize] = { +static const ViewModeInfo viewModeMap[viewModeMapSize] = { {"windowed", Page::ViewModeWindowed}, {"floating", Page::ViewModeFloating}, {"fullscreen", Page::ViewModeFullscreen}, @@ -328,9 +436,9 @@ static ViewModeInfo viewModeMap[viewModeMapSize] = { Page::ViewMode Page::stringToViewMode(const String& text) { - for (int i = 0; i < viewModeMapSize; ++i) { - if (text == viewModeMap[i].name) - return viewModeMap[i].type; + for (auto& mode : viewModeMap) { + if (text == mode.name) + return mode.type; } return Page::ViewModeInvalid; } @@ -342,14 +450,12 @@ void Page::setViewMode(ViewMode viewMode) m_viewMode = viewMode; - if (!m_mainFrame) - return; if (m_mainFrame->view()) m_mainFrame->view()->forceLayout(); if (m_mainFrame->document()) - m_mainFrame->document()->styleResolverChanged(RecalcStyleImmediately); + m_mainFrame->document()->styleScope().didChangeStyleSheetEnvironment(); } #endif // ENABLE(VIEW_MODE_CSS_MEDIA) @@ -363,11 +469,11 @@ void Page::setOpenedByDOM() m_openedByDOM = true; } -void Page::goToItem(HistoryItem* item, FrameLoadType type) +void Page::goToItem(HistoryItem& item, FrameLoadType type) { // stopAllLoaders may end up running onload handlers, which could cause further history traversals that may lead to the passed in HistoryItem // being deref()-ed. Make sure we can still use it with HistoryController::goToItem later. - RefPtr<HistoryItem> protector(item); + Ref<HistoryItem> protector(item); if (m_mainFrame->loader().history().shouldStopLoadingForHistoryItem(item)) m_mainFrame->loader().stopAllLoaders(); @@ -409,37 +515,26 @@ void Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment() { if (!allPages) return; - HashSet<Page*>::iterator end = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) - for (Frame* frame = &(*it)->mainFrame(); frame; frame = frame->tree().traverseNext()) { + for (auto& page : *allPages) { + for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) { // If a change in the global environment has occurred, we need to // make sure all the properties a recomputed, therefore we invalidate // the properties cache. - if (StyleResolver* styleResolver = frame->document()->styleResolverIfExists()) + if (!frame->document()) + continue; + if (StyleResolver* styleResolver = frame->document()->styleScope().resolverIfExists()) styleResolver->invalidateMatchedPropertiesCache(); frame->document()->scheduleForcedStyleRecalc(); } + } } void Page::setNeedsRecalcStyleInAllFrames() { + // FIXME: Figure out what this function is actually trying to add in different call sites. for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { if (Document* document = frame->document()) - document->styleResolverChanged(DeferRecalcStyle); - } -} - -void Page::jettisonStyleResolversInAllDocuments() -{ - if (!allPages) - return; - - for (auto it = allPages->begin(), end = allPages->end(); it != end; ++it) { - Page& page = **it; - for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (Document* document = frame->document()) - document->clearStyleResolver(); - } + document->styleScope().didChangeStyleSheetEnvironment(); } } @@ -448,41 +543,47 @@ void Page::refreshPlugins(bool reload) if (!allPages) return; - PluginData::refresh(); + HashSet<PluginInfoProvider*> pluginInfoProviders; - Vector<Ref<Frame>> framesNeedingReload; + for (auto& page : *allPages) + pluginInfoProviders.add(&page->pluginInfoProvider()); - for (auto it = allPages->begin(), end = allPages->end(); it != end; ++it) { - Page& page = **it; - page.m_pluginData.clear(); - - if (!reload) - continue; - - for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (frame->loader().subframeLoader().containsPlugins()) - framesNeedingReload.append(*frame); - } - } - - for (size_t i = 0; i < framesNeedingReload.size(); ++i) - framesNeedingReload[i]->loader().reload(); + for (auto& pluginInfoProvider : pluginInfoProviders) + pluginInfoProvider->refresh(reload); } -PluginData& Page::pluginData() const +PluginData& Page::pluginData() { if (!m_pluginData) - m_pluginData = PluginData::create(this); + m_pluginData = PluginData::create(*this); return *m_pluginData; } -inline MediaCanStartListener* Page::takeAnyMediaCanStartListener() +void Page::clearPluginData() +{ + m_pluginData = nullptr; +} + +bool Page::showAllPlugins() const +{ + if (m_showAllPlugins) + return true; + + if (Document* document = mainFrame().document()) + return document->securityOrigin().isLocal(); + + return false; +} + +inline std::optional<std::pair<MediaCanStartListener&, Document&>> Page::takeAnyMediaCanStartListener() { for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; if (MediaCanStartListener* listener = frame->document()->takeAnyMediaCanStartListener()) - return listener; + return { { *listener, *frame->document() } }; } - return 0; + return std::nullopt; } void Page::setCanStartMedia(bool canStartMedia) @@ -493,10 +594,10 @@ void Page::setCanStartMedia(bool canStartMedia) m_canStartMedia = canStartMedia; while (m_canStartMedia) { - MediaCanStartListener* listener = takeAnyMediaCanStartListener(); + auto listener = takeAnyMediaCanStartListener(); if (!listener) break; - listener->mediaCanStart(); + listener->first.mediaCanStart(listener->second); } } @@ -536,35 +637,37 @@ bool Page::findString(const String& target, FindOptions options) return false; } -void Page::findStringMatchingRanges(const String& target, FindOptions options, int limit, Vector<RefPtr<Range>>* matchRanges, int& indexForSelection) +void Page::findStringMatchingRanges(const String& target, FindOptions options, int limit, Vector<RefPtr<Range>>& matchRanges, int& indexForSelection) { indexForSelection = 0; Frame* frame = &mainFrame(); - Frame* frameWithSelection = 0; + Frame* frameWithSelection = nullptr; do { - frame->editor().countMatchesForText(target, 0, options, limit ? (limit - matchRanges->size()) : 0, true, matchRanges); + frame->editor().countMatchesForText(target, 0, options, limit ? (limit - matchRanges.size()) : 0, true, &matchRanges); if (frame->selection().isRange()) frameWithSelection = frame; frame = incrementFrame(frame, true, false); } while (frame); - if (matchRanges->isEmpty()) + if (matchRanges.isEmpty()) return; if (frameWithSelection) { indexForSelection = NoMatchAfterUserSelection; RefPtr<Range> selectedRange = frameWithSelection->selection().selection().firstRange(); if (options & Backwards) { - for (size_t i = matchRanges->size(); i > 0; --i) { - if (selectedRange->compareBoundaryPoints(Range::END_TO_START, matchRanges->at(i - 1).get(), IGNORE_EXCEPTION) > 0) { + for (size_t i = matchRanges.size(); i > 0; --i) { + auto result = selectedRange->compareBoundaryPoints(Range::END_TO_START, *matchRanges[i - 1]); + if (!result.hasException() && result.releaseReturnValue() > 0) { indexForSelection = i - 1; break; } } } else { - for (size_t i = 0; i < matchRanges->size(); ++i) { - if (selectedRange->compareBoundaryPoints(Range::START_TO_END, matchRanges->at(i).get(), IGNORE_EXCEPTION) < 0) { + for (size_t i = 0, size = matchRanges.size(); i < size; ++i) { + auto result = selectedRange->compareBoundaryPoints(Range::START_TO_END, *matchRanges[i]); + if (!result.hasException() && result.releaseReturnValue() < 0) { indexForSelection = i; break; } @@ -572,26 +675,26 @@ void Page::findStringMatchingRanges(const String& target, FindOptions options, i } } else { if (options & Backwards) - indexForSelection = matchRanges->size() - 1; + indexForSelection = matchRanges.size() - 1; else indexForSelection = 0; } } -PassRefPtr<Range> Page::rangeOfString(const String& target, Range* referenceRange, FindOptions options) +RefPtr<Range> Page::rangeOfString(const String& target, Range* referenceRange, FindOptions options) { if (target.isEmpty()) - return 0; + return nullptr; if (referenceRange && referenceRange->ownerDocument().page() != this) - return 0; + return nullptr; bool shouldWrap = options & WrapAround; Frame* frame = referenceRange ? referenceRange->ownerDocument().frame() : &mainFrame(); Frame* startFrame = frame; do { if (RefPtr<Range> resultRange = frame->editor().rangeOfString(target, frame == startFrame ? referenceRange : 0, options & ~WrapAround)) - return resultRange.release(); + return resultRange; frame = incrementFrame(frame, !(options & Backwards), shouldWrap); } while (frame && frame != startFrame); @@ -600,10 +703,10 @@ PassRefPtr<Range> Page::rangeOfString(const String& target, Range* referenceRang // We cheat a bit and just search again with wrap on. if (shouldWrap && referenceRange) { if (RefPtr<Range> resultRange = startFrame->editor().rangeOfString(target, referenceRange, options | WrapAround | StartInSelection)) - return resultRange.release(); + return resultRange; } - return 0; + return nullptr; } unsigned Page::findMatchesForText(const String& target, FindOptions options, unsigned maxMatchCount, ShouldHighlightMatches shouldHighlightMatches, ShouldMarkMatches shouldMarkMatches) @@ -685,6 +788,13 @@ void Page::setInLowQualityImageInterpolationMode(bool mode) m_inLowQualityInterpolationMode = mode; } +DiagnosticLoggingClient& Page::diagnosticLoggingClient() const +{ + if (!settings().diagnosticLoggingEnabled() || !m_diagnosticLoggingClient) + return emptyDiagnosticLoggingClient(); + return *m_diagnosticLoggingClient; +} + void Page::setMediaVolume(float volume) { if (volume < 0 || volume > 1) @@ -695,21 +805,47 @@ void Page::setMediaVolume(float volume) m_mediaVolume = volume; for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; frame->document()->mediaVolumeDidChange(); } } -void Page::setPageScaleFactor(float scale, const IntPoint& origin) +void Page::setZoomedOutPageScaleFactor(float scale) +{ + if (m_zoomedOutPageScaleFactor == scale) + return; + m_zoomedOutPageScaleFactor = scale; + + mainFrame().deviceOrPageScaleFactorChanged(); +} + +void Page::setPageScaleFactor(float scale, const IntPoint& origin, bool inStableState) { Document* document = mainFrame().document(); FrameView* view = document->view(); if (scale == m_pageScaleFactor) { - if (view && (view->scrollPosition() != origin || view->delegatesScrolling())) { + if (view && view->scrollPosition() != origin) { if (!m_settings->delegatesPageScaling()) document->updateLayoutIgnorePendingStylesheets(); - view->setScrollPosition(origin); + + if (!view->delegatesScrolling()) + view->setScrollPosition(origin); +#if USE(COORDINATED_GRAPHICS) + else + view->requestScrollPositionUpdate(origin); +#endif } +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + if (inStableState) { + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->pageScaleFactorChangedAndStable(); + } + } +#endif return; } @@ -725,9 +861,7 @@ void Page::setPageScaleFactor(float scale, const IntPoint& origin) mainFrame().view()->invalidateRect(IntRect(LayoutRect::infiniteRect())); } -#if USE(ACCELERATED_COMPOSITING) mainFrame().deviceOrPageScaleFactorChanged(); -#endif if (view && view->fixedElementsLayoutRelativeToFrame()) view->setViewportConstrainedObjectsNeedLayout(); @@ -735,29 +869,100 @@ void Page::setPageScaleFactor(float scale, const IntPoint& origin) if (view && view->scrollPosition() != origin) { if (!m_settings->delegatesPageScaling() && document->renderView() && document->renderView()->needsLayout() && view->didFirstLayout()) view->layout(); - view->setScrollPosition(origin); + + if (!view->delegatesScrolling()) + view->setScrollPosition(origin); +#if USE(COORDINATED_GRAPHICS) + else + view->requestScrollPositionUpdate(origin); +#endif + } + +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + if (inStableState) { + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->pageScaleFactorChangedAndStable(); + } } +#else + UNUSED_PARAM(inStableState); +#endif } +void Page::setViewScaleFactor(float scale) +{ + if (m_viewScaleFactor == scale) + return; + + m_viewScaleFactor = scale; + PageCache::singleton().markPagesForDeviceOrPageScaleChanged(*this); +} void Page::setDeviceScaleFactor(float scaleFactor) { + ASSERT(scaleFactor > 0); + if (scaleFactor <= 0) + return; + if (m_deviceScaleFactor == scaleFactor) return; m_deviceScaleFactor = scaleFactor; setNeedsRecalcStyleInAllFrames(); -#if USE(ACCELERATED_COMPOSITING) mainFrame().deviceOrPageScaleFactorChanged(); - pageCache()->markPagesForDeviceScaleChanged(this); + PageCache::singleton().markPagesForDeviceOrPageScaleChanged(*this); + + GraphicsContext::updateDocumentMarkerResources(); + + mainFrame().pageOverlayController().didChangeDeviceScaleFactor(); +} + +void Page::setUserInterfaceLayoutDirection(UserInterfaceLayoutDirection userInterfaceLayoutDirection) +{ + if (m_userInterfaceLayoutDirection == userInterfaceLayoutDirection) + return; + + m_userInterfaceLayoutDirection = userInterfaceLayoutDirection; +#if ENABLE(MEDIA_CONTROLS_SCRIPT) + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->userInterfaceLayoutDirectionChanged(); + } #endif +} - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) - frame->editor().deviceScaleFactorChanged(); +void Page::didStartProvisionalLoad() +{ + if (m_performanceMonitor) + m_performanceMonitor->didStartProvisionalLoad(); +} - pageCache()->markPagesForFullStyleRecalc(this); - GraphicsContext::updateDocumentMarkerResources(); +void Page::didFinishLoad() +{ + resetRelevantPaintedObjectCounter(); + + if (m_performanceMonitor) + m_performanceMonitor->didFinishLoad(); +} + +bool Page::isOnlyNonUtilityPage() const +{ + return !isUtilityPage() && nonUtilityPageCount == 1; +} + +void Page::setTopContentInset(float contentInset) +{ + if (m_topContentInset == contentInset) + return; + + m_topContentInset = contentInset; + + if (FrameView* view = mainFrame().view()) + view->topContentInsetDidChange(m_topContentInset); } void Page::setShouldSuppressScrollbarAnimations(bool suppressAnimations) @@ -786,12 +991,32 @@ void Page::lockAllOverlayScrollbarsToHidden(bool lockOverlayScrollbars) if (!scrollableAreas) continue; - for (HashSet<ScrollableArea*>::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { - ScrollableArea* scrollableArea = *it; + for (auto& scrollableArea : *scrollableAreas) scrollableArea->lockOverlayScrollbarStateToHidden(lockOverlayScrollbars); - } } } + +void Page::setVerticalScrollElasticity(ScrollElasticity elasticity) +{ + if (m_verticalScrollElasticity == elasticity) + return; + + m_verticalScrollElasticity = elasticity; + + if (FrameView* view = mainFrame().view()) + view->setVerticalScrollElasticity(elasticity); +} + +void Page::setHorizontalScrollElasticity(ScrollElasticity elasticity) +{ + if (m_horizontalScrollElasticity == elasticity) + return; + + m_horizontalScrollElasticity = elasticity; + + if (FrameView* view = mainFrame().view()) + view->setHorizontalScrollElasticity(elasticity); +} void Page::setPagination(const Pagination& pagination) { @@ -801,7 +1026,16 @@ void Page::setPagination(const Pagination& pagination) m_pagination = pagination; setNeedsRecalcStyleInAllFrames(); - pageCache()->markPagesForFullStyleRecalc(this); +} + +void Page::setPaginationLineGridEnabled(bool enabled) +{ + if (m_paginationLineGridEnabled == enabled) + return; + + m_paginationLineGridEnabled = enabled; + + setNeedsRecalcStyleInAllFrames(); } unsigned Page::pageCount() const @@ -813,16 +1047,16 @@ unsigned Page::pageCount() const document->updateLayoutIgnorePendingStylesheets(); RenderView* contentRenderer = mainFrame().contentRenderer(); - return contentRenderer ? contentRenderer->columnCount(contentRenderer->columnInfo()) : 0; + return contentRenderer ? contentRenderer->pageCount() : 0; } void Page::setIsInWindow(bool isInWindow) { - if (m_isInWindow == isInWindow) - return; - - m_isInWindow = isInWindow; + setActivityState(isInWindow ? m_activityState | ActivityState::IsInWindow : m_activityState & ~ActivityState::IsInWindow); +} +void Page::setIsInWindowInternal(bool isInWindow) +{ for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { if (FrameView* frameView = frame->view()) frameView->setIsInWindow(isInWindow); @@ -832,6 +1066,16 @@ void Page::setIsInWindow(bool isInWindow) resumeAnimatingImages(); } +void Page::addActivityStateChangeObserver(ActivityStateChangeObserver& observer) +{ + m_activityStateChangeObservers.add(&observer); +} + +void Page::removeActivityStateChangeObserver(ActivityStateChangeObserver& observer) +{ + m_activityStateChangeObservers.remove(&observer); +} + void Page::suspendScriptedAnimations() { m_scriptedAnimationsSuspended = true; @@ -850,9 +1094,12 @@ void Page::resumeScriptedAnimations() } } -void Page::setIsVisuallyIdle(bool isVisuallyIdle) +void Page::setIsVisuallyIdleInternal(bool isVisuallyIdle) { - m_pageThrottler->setIsVisuallyIdle(isVisuallyIdle); + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (frame->document()) + frame->document()->scriptedAnimationControllerSetThrottled(isVisuallyIdle); + } } void Page::userStyleSheetLocationChanged() @@ -862,7 +1109,7 @@ void Page::userStyleSheetLocationChanged() URL url = m_settings->userStyleSheetLocation(); // Allow any local file URL scheme to be loaded. - if (SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol())) + if (SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol().toStringWithoutCopying())) m_userStyleSheetPath = url.fileSystemPath(); else m_userStyleSheetPath = String(); @@ -877,13 +1124,13 @@ void Page::userStyleSheetLocationChanged() m_didLoadUserStyleSheet = true; Vector<char> styleSheetAsUTF8; - if (base64Decode(decodeURLEscapeSequences(url.string().substring(35)), styleSheetAsUTF8, Base64IgnoreWhitespace)) + if (base64Decode(decodeURLEscapeSequences(url.string().substring(35)), styleSheetAsUTF8, Base64IgnoreSpacesAndNewLines)) m_userStyleSheet = String::fromUTF8(styleSheetAsUTF8.data(), styleSheetAsUTF8.size()); } for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { if (frame->document()) - frame->document()->styleSheetCollection().updatePageUserSheet(); + frame->document()->extensionStyleSheets().updatePageUserSheet(); } } @@ -921,57 +1168,36 @@ const String& Page::userStyleSheet() const if (!data) return m_userStyleSheet; - RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/css"); - m_userStyleSheet = decoder->decode(data->data(), data->size()); - m_userStyleSheet.append(decoder->flush()); + m_userStyleSheet = TextResourceDecoder::create("text/css")->decodeAndFlush(data->data(), data->size()); return m_userStyleSheet; } -void Page::removeAllVisitedLinks() +void Page::invalidateStylesForAllLinks() { - if (!allPages) - return; - HashSet<PageGroup*> groups; - HashSet<Page*>::iterator pagesEnd = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { - if (PageGroup* group = (*it)->groupPtr()) - groups.add(group); + for (Frame* frame = &m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->visitedLinkState().invalidateStyleForAllLinks(); } - HashSet<PageGroup*>::iterator groupsEnd = groups.end(); - for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it) - (*it)->removeVisitedLinks(); } -void Page::allVisitedStateChanged(PageGroup* group) +void Page::invalidateStylesForLink(LinkHash linkHash) { - ASSERT(group); - if (!allPages) - return; - - HashSet<Page*>::iterator pagesEnd = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { - Page* page = *it; - if (page->m_group != group) + for (Frame* frame = &m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) continue; - for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) - frame->document()->visitedLinkState().invalidateStyleForAllLinks(); + frame->document()->visitedLinkState().invalidateStyleForLink(linkHash); } } -void Page::visitedStateChanged(PageGroup* group, LinkHash linkHash) +void Page::invalidateInjectedStyleSheetCacheInAllFrames() { - ASSERT(group); - if (!allPages) - return; - - HashSet<Page*>::iterator pagesEnd = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { - Page* page = *it; - if (page->m_group != group) + for (Frame* frame = &m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) { + Document* document = frame->document(); + if (!document) continue; - for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) - frame->document()->visitedLinkState().invalidateStyleForLink(linkHash); + document->extensionStyleSheets().invalidateInjectedStyleSheetCache(); } } @@ -982,39 +1208,45 @@ void Page::setDebugger(JSC::Debugger* debugger) m_debugger = debugger; - for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) + for (Frame* frame = &m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) frame->script().attachDebugger(m_debugger); } StorageNamespace* Page::sessionStorage(bool optionalCreate) { if (!m_sessionStorage && optionalCreate) - m_sessionStorage = StorageNamespace::sessionStorageNamespace(this); + m_sessionStorage = m_storageNamespaceProvider->createSessionStorageNamespace(*this, m_settings->sessionStorageQuota()); return m_sessionStorage.get(); } -void Page::setSessionStorage(PassRefPtr<StorageNamespace> newStorage) +void Page::setSessionStorage(RefPtr<StorageNamespace>&& newStorage) { - m_sessionStorage = newStorage; + m_sessionStorage = WTFMove(newStorage); } -void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay) +StorageNamespace* Page::ephemeralLocalStorage(bool optionalCreate) { - if (customHTMLTokenizerTimeDelay < 0) { - m_customHTMLTokenizerTimeDelay = -1; - return; - } - m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay; + if (!m_ephemeralLocalStorage && optionalCreate) + m_ephemeralLocalStorage = m_storageNamespaceProvider->createEphemeralLocalStorageNamespace(*this, m_settings->sessionStorageQuota()); + + return m_ephemeralLocalStorage.get(); } -void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize) +void Page::setEphemeralLocalStorage(RefPtr<StorageNamespace>&& newStorage) { - if (customHTMLTokenizerChunkSize < 0) { - m_customHTMLTokenizerChunkSize = -1; - return; - } - m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize; + m_ephemeralLocalStorage = WTFMove(newStorage); +} + +bool Page::hasCustomHTMLTokenizerTimeDelay() const +{ + return m_settings->maxParseDuration() != -1; +} + +double Page::customHTMLTokenizerTimeDelay() const +{ + ASSERT(m_settings->maxParseDuration() != -1); + return m_settings->maxParseDuration(); } void Page::setMemoryCacheClientCallsEnabled(bool enabled) @@ -1030,91 +1262,211 @@ void Page::setMemoryCacheClientCallsEnabled(bool enabled) frame->loader().tellClientAboutPastMemoryCacheLoads(); } -void Page::setMinimumTimerInterval(double minimumTimerInterval) +void Page::hiddenPageDOMTimerThrottlingStateChanged() { - double oldTimerInterval = m_minimumTimerInterval; - m_minimumTimerInterval = minimumTimerInterval; - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNextWithWrap(false)) { - if (frame->document()) - frame->document()->adjustMinimumTimerInterval(oldTimerInterval); - } + // Disable & reengage to ensure state is updated. + setTimerThrottlingState(TimerThrottlingState::Disabled); + updateTimerThrottlingState(); } -double Page::minimumTimerInterval() const +void Page::updateTimerThrottlingState() { - return m_minimumTimerInterval; + // Timer throttling disabled if page is visually active, or disabled by setting. + if (!m_settings->hiddenPageDOMTimerThrottlingEnabled() || !(m_activityState & ActivityState::IsVisuallyIdle)) { + setTimerThrottlingState(TimerThrottlingState::Disabled); + return; + } + + // If the page is visible (but idle), there is any activity (loading, media playing, etc), or per setting, + // we allow timer throttling, but not increasing timer throttling. + if (!m_settings->hiddenPageDOMTimerThrottlingAutoIncreases() + || m_activityState & (ActivityState::IsVisible | ActivityState::IsAudible | ActivityState::IsLoading)) { + setTimerThrottlingState(TimerThrottlingState::Enabled); + return; + } + + // If we get here increasing timer throttling is enabled. + setTimerThrottlingState(TimerThrottlingState::EnabledIncreasing); } -void Page::setTimerAlignmentInterval(double interval) +void Page::setTimerThrottlingState(TimerThrottlingState state) { - if (interval == m_timerAlignmentInterval) + if (state == m_timerThrottlingState) return; - m_timerAlignmentInterval = interval; - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNextWithWrap(false)) { - if (frame->document()) - frame->document()->didChangeTimerAlignmentInterval(); + m_timerThrottlingState = state; + m_timerThrottlingStateLastChangedTime = std::chrono::steady_clock::now(); + + updateDOMTimerAlignmentInterval(); + + // When throttling is disabled, release all throttled timers. + if (state == TimerThrottlingState::Disabled) { + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (auto* document = frame->document()) + document->didChangeTimerAlignmentInterval(); + } } } -double Page::timerAlignmentInterval() const +void Page::setTimerAlignmentIntervalIncreaseLimit(std::chrono::milliseconds limit) { - return m_timerAlignmentInterval; + m_timerAlignmentIntervalIncreaseLimit = limit; + + // If (m_timerAlignmentIntervalIncreaseLimit < m_timerAlignmentInterval) then we need + // to update m_timerAlignmentInterval, if greater then need to restart the increase timer. + if (m_timerThrottlingState == TimerThrottlingState::EnabledIncreasing) + updateDOMTimerAlignmentInterval(); +} + +void Page::updateDOMTimerAlignmentInterval() +{ + bool needsIncreaseTimer = false; + + switch (m_timerThrottlingState) { + case TimerThrottlingState::Disabled: + m_timerAlignmentInterval = DOMTimer::defaultAlignmentInterval(); + break; + + case TimerThrottlingState::Enabled: + m_timerAlignmentInterval = DOMTimer::hiddenPageAlignmentInterval(); + break; + + case TimerThrottlingState::EnabledIncreasing: + // For pages in prerender state maximum throttling kicks in immediately. + if (m_isPrerender) + m_timerAlignmentInterval = m_timerAlignmentIntervalIncreaseLimit; + else { + ASSERT(m_timerThrottlingStateLastChangedTime.time_since_epoch() != std::chrono::steady_clock::duration::zero()); + m_timerAlignmentInterval = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_timerThrottlingStateLastChangedTime); + // If we're below the limit, set the timer. If above, clamp to limit. + if (m_timerAlignmentInterval < m_timerAlignmentIntervalIncreaseLimit) + needsIncreaseTimer = true; + else + m_timerAlignmentInterval = m_timerAlignmentIntervalIncreaseLimit; + } + // Alignment interval should not be less than DOMTimer::hiddenPageAlignmentInterval(). + m_timerAlignmentInterval = std::max(m_timerAlignmentInterval, DOMTimer::hiddenPageAlignmentInterval()); + } + + // If throttling is enabled, auto-increasing of throttling is enabled, and the auto-increase + // limit has not yet been reached, and then arm the timer to consider an increase. Time to wait + // between increases is equal to the current throttle time. Since alinment interval increases + // exponentially, time between steps is exponential too. + if (!needsIncreaseTimer) + m_timerAlignmentIntervalIncreaseTimer.stop(); + else if (!m_timerAlignmentIntervalIncreaseTimer.isActive()) + m_timerAlignmentIntervalIncreaseTimer.startOneShot(m_timerAlignmentInterval); +} + +void Page::timerAlignmentIntervalIncreaseTimerFired() +{ + ASSERT(m_settings->hiddenPageDOMTimerThrottlingAutoIncreases()); + ASSERT(m_timerThrottlingState == TimerThrottlingState::EnabledIncreasing); + ASSERT(m_timerAlignmentInterval < m_timerAlignmentIntervalIncreaseLimit); + + // Alignment interval is increased to equal the time the page has been throttled, to a limit. + updateDOMTimerAlignmentInterval(); } void Page::dnsPrefetchingStateChanged() { - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; frame->document()->initDNSPrefetch(); + } } Vector<Ref<PluginViewBase>> Page::pluginViews() { Vector<Ref<PluginViewBase>> views; - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { - FrameView* view = frame->view(); + auto* view = frame->view(); if (!view) break; - - for (auto it = view->children().begin(), end = view->children().end(); it != end; ++it) { - Widget* widget = (*it).get(); - if (widget->isPluginViewBase()) - views.append(*toPluginViewBase(widget)); + for (auto& widget : view->children()) { + if (is<PluginViewBase>(widget.get())) + views.append(downcast<PluginViewBase>(widget.get())); } } - return views; } void Page::storageBlockingStateChanged() { - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; frame->document()->storageBlockingStateDidChange(); + } // Collect the PluginViews in to a vector to ensure that action the plug-in takes // from below storageBlockingStateChanged does not affect their lifetime. - auto views = pluginViews(); + for (auto& view : pluginViews()) + view->storageBlockingStateChanged(); +} + +void Page::enableLegacyPrivateBrowsing(bool privateBrowsingEnabled) +{ + // Don't allow changing the legacy private browsing state if we have set a session ID. + ASSERT(m_sessionID == SessionID::defaultSessionID() || m_sessionID == SessionID::legacyPrivateSessionID()); - for (unsigned i = 0; i < views.size(); ++i) - views[i]->storageBlockingStateChanged(); + setSessionID(privateBrowsingEnabled ? SessionID::legacyPrivateSessionID() : SessionID::defaultSessionID()); } -void Page::privateBrowsingStateChanged() +void Page::updateIsPlayingMedia(uint64_t sourceElementID) { - bool privateBrowsingEnabled = m_settings->privateBrowsingEnabled(); + MediaProducer::MediaStateFlags state = MediaProducer::IsNotPlaying; + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (Document* document = frame->document()) + state |= document->mediaState(); + } - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) - frame->document()->privateBrowsingStateDidChange(); + if (state == m_mediaState) + return; - // Collect the PluginViews in to a vector to ensure that action the plug-in takes - // from below privateBrowsingStateChanged does not affect their lifetime. - auto views = pluginViews(); + m_mediaState = state; - for (unsigned i = 0; i < views.size(); ++i) - views[i]->privateBrowsingStateChanged(privateBrowsingEnabled); + chrome().client().isPlayingMediaDidChange(state, sourceElementID); } +void Page::setMuted(MediaProducer::MutedStateFlags muted) +{ + if (m_mutedState == muted) + return; + + m_mutedState = muted; + + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->pageMutedStateDidChange(); + } +} + +#if ENABLE(MEDIA_SESSION) +void Page::handleMediaEvent(MediaEventType eventType) +{ + switch (eventType) { + case MediaEventType::PlayPause: + MediaSessionManager::singleton().togglePlayback(); + break; + case MediaEventType::TrackNext: + MediaSessionManager::singleton().skipToNextTrack(); + break; + case MediaEventType::TrackPrevious: + MediaSessionManager::singleton().skipToPreviousTrack(); + break; + } +} + +void Page::setVolumeOfMediaElement(double volume, uint64_t elementID) +{ + if (HTMLMediaElement* element = HTMLMediaElement::elementWithID(elementID)) + element->setVolume(volume, ASSERT_NO_EXCEPTION); +} +#endif + #if !ASSERT_DISABLED void Page::checkSubframeCountConsistency() const { @@ -1128,86 +1480,127 @@ void Page::checkSubframeCountConsistency() const } #endif -void Page::throttleTimers() +void Page::resumeAnimatingImages() { -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) - if (m_settings->hiddenPageDOMTimerThrottlingEnabled()) - setTimerAlignmentInterval(Settings::hiddenPageDOMTimerAlignmentInterval()); -#endif + // Drawing models which cache painted content while out-of-window (WebKit2's composited drawing areas, etc.) + // require that we repaint animated images to kickstart the animation loop. + if (FrameView* view = mainFrame().view()) + view->resumeVisibleImageAnimationsIncludingSubframes(); } -void Page::unthrottleTimers() +void Page::setActivityState(ActivityState::Flags activityState) { -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) - if (m_settings->hiddenPageDOMTimerThrottlingEnabled()) - setTimerAlignmentInterval(Settings::defaultDOMTimerAlignmentInterval()); -#endif + ActivityState::Flags changed = m_activityState ^ activityState; + if (!changed) + return; + + ActivityState::Flags oldActivityState = m_activityState; + + bool wasVisibleAndActive = isVisibleAndActive(); + m_activityState = activityState; + + m_focusController->setActivityState(activityState); + + if (changed & ActivityState::IsVisible) + setIsVisibleInternal(activityState & ActivityState::IsVisible); + if (changed & ActivityState::IsInWindow) + setIsInWindowInternal(activityState & ActivityState::IsInWindow); + if (changed & ActivityState::IsVisuallyIdle) + setIsVisuallyIdleInternal(activityState & ActivityState::IsVisuallyIdle); + if (changed & ActivityState::WindowIsActive) { + if (auto* view = m_mainFrame->view()) + view->updateTiledBackingAdaptiveSizing(); + } + + if (changed & (ActivityState::IsVisible | ActivityState::IsVisuallyIdle | ActivityState::IsAudible | ActivityState::IsLoading)) + updateTimerThrottlingState(); + + for (auto* observer : m_activityStateChangeObservers) + observer->activityStateDidChange(oldActivityState, m_activityState); + + if (wasVisibleAndActive != isVisibleAndActive()) + PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary(); + + if (m_performanceMonitor) + m_performanceMonitor->activityStateChanged(oldActivityState, activityState); } -void Page::resumeAnimatingImages() +bool Page::isVisibleAndActive() const { - // Drawing models which cache painted content while out-of-window (WebKit2's composited drawing areas, etc.) - // require that we repaint animated images to kickstart the animation loop. + return (m_activityState & ActivityState::IsVisible) && (m_activityState & ActivityState::WindowIsActive); +} + +bool Page::isWindowActive() const +{ + return m_activityState & ActivityState::WindowIsActive; +} + +void Page::setIsVisible(bool isVisible) +{ + if (isVisible) + setActivityState((m_activityState & ~ActivityState::IsVisuallyIdle) | ActivityState::IsVisible | ActivityState::IsVisibleOrOccluded); + else + setActivityState((m_activityState & ~(ActivityState::IsVisible | ActivityState::IsVisibleOrOccluded)) | ActivityState::IsVisuallyIdle); +} + +enum class SVGAnimationsState { Paused, Resumed }; +static inline void setSVGAnimationsState(Page& page, SVGAnimationsState state) +{ + for (Frame* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) { + auto* document = frame->document(); + if (!document) + continue; + + if (!document->svgExtensions()) + continue; - for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) - CachedImage::resumeAnimatingImagesForLoader(frame->document()->cachedResourceLoader()); + if (state == SVGAnimationsState::Paused) + document->accessSVGExtensions().pauseAnimations(); + else + document->accessSVGExtensions().unpauseAnimations(); + } } -void Page::setIsVisible(bool isVisible, bool isInitialState) +void Page::setIsVisibleInternal(bool isVisible) { // FIXME: The visibility state should be stored on the top-level document. // https://bugs.webkit.org/show_bug.cgi?id=116769 - if (m_isVisible == isVisible) - return; - m_isVisible = isVisible; - if (isVisible) { m_isPrerender = false; - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (FrameView* frameView = frame->view()) - frameView->didMoveOnscreen(); - } - resumeScriptedAnimations(); +#if PLATFORM(IOS) + resumeDeviceMotionAndOrientationUpdates(); +#endif if (FrameView* view = mainFrame().view()) view->show(); - unthrottleTimers(); - if (m_settings->hiddenPageCSSAnimationSuspensionEnabled()) mainFrame().animation().resumeAnimations(); + setSVGAnimationsState(*this, SVGAnimationsState::Resumed); + resumeAnimatingImages(); } -#if ENABLE(PAGE_VISIBILITY_API) - if (!isInitialState) { - Vector<Ref<Document>> documents; - for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) - documents.append(*frame->document()); + Vector<Ref<Document>> documents; + for (Frame* frame = &m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) + documents.append(*frame->document()); - for (size_t i = 0, size = documents.size(); i < size; ++i) - documents[i]->visibilityStateChanged(); - } -#else - UNUSED_PARAM(isInitialState); -#endif + for (auto& document : documents) + document->visibilityStateChanged(); if (!isVisible) { - if (m_pageThrottler->shouldThrottleTimers()) - throttleTimers(); - if (m_settings->hiddenPageCSSAnimationSuspensionEnabled()) mainFrame().animation().suspendAnimations(); - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (FrameView* frameView = frame->view()) - frameView->willMoveOffscreen(); - } + setSVGAnimationsState(*this, SVGAnimationsState::Paused); +#if PLATFORM(IOS) + suspendDeviceMotionAndOrientationUpdates(); +#endif suspendScriptedAnimations(); if (FrameView* view = mainFrame().view()) @@ -1218,18 +1611,17 @@ void Page::setIsVisible(bool isVisible, bool isInitialState) void Page::setIsPrerender() { m_isPrerender = true; + updateDOMTimerAlignmentInterval(); } -#if ENABLE(PAGE_VISIBILITY_API) PageVisibilityState Page::visibilityState() const { - if (m_isVisible) - return PageVisibilityStateVisible; + if (isVisible()) + return PageVisibilityState::Visible; if (m_isPrerender) - return PageVisibilityStatePrerender; - return PageVisibilityStateHidden; + return PageVisibilityState::Prerender; + return PageVisibilityState::Hidden; } -#endif #if ENABLE(RUBBER_BANDING) void Page::addHeaderWithHeight(int headerHeight) @@ -1265,6 +1657,41 @@ void Page::addFooterWithHeight(int footerHeight) } #endif +void Page::incrementNestedRunLoopCount() +{ + m_nestedRunLoopCount++; +} + +void Page::decrementNestedRunLoopCount() +{ + ASSERT(m_nestedRunLoopCount); + if (m_nestedRunLoopCount <= 0) + return; + + m_nestedRunLoopCount--; + + if (!m_nestedRunLoopCount && m_unnestCallback) { + callOnMainThread([this] { + if (insideNestedRunLoop()) + return; + + // This callback may destruct the Page. + if (m_unnestCallback) { + auto callback = m_unnestCallback; + m_unnestCallback = nullptr; + callback(); + } + }); + } +} + +void Page::whenUnnested(std::function<void()> callback) +{ + ASSERT(!m_unnestCallback); + + m_unnestCallback = callback; +} + #if ENABLE(REMOTE_INSPECTOR) bool Page::remoteInspectionAllowed() const { @@ -1276,6 +1703,16 @@ void Page::setRemoteInspectionAllowed(bool allowed) m_inspectorDebuggable->setRemoteDebuggingAllowed(allowed); } +String Page::remoteInspectionNameOverride() const +{ + return m_inspectorDebuggable->nameOverride(); +} + +void Page::setRemoteInspectionNameOverride(const String& name) +{ + m_inspectorDebuggable->setNameOverride(name); +} + void Page::remoteInspectorInformationDidChange() const { m_inspectorDebuggable->update(); @@ -1293,9 +1730,22 @@ void Page::removeLayoutMilestones(LayoutMilestones milestones) m_requestedLayoutMilestones &= ~milestones; } +Color Page::pageExtendedBackgroundColor() const +{ + FrameView* frameView = mainFrame().view(); + if (!frameView) + return Color(); + + RenderView* renderView = frameView->renderView(); + if (!renderView) + return Color(); + + return renderView->compositor().rootExtendedBackgroundColor(); +} + // These are magical constants that might be tweaked over time. -static double gMinimumPaintedAreaRatio = 0.1; -static double gMaximumUnpaintedAreaRatio = 0.04; +static const double gMinimumPaintedAreaRatio = 0.1; +static const double gMaximumUnpaintedAreaRatio = 0.04; bool Page::isCountingRelevantRepaintedObjects() const { @@ -1348,10 +1798,10 @@ void Page::addRelevantRepaintedObject(RenderObject* object, const LayoutRect& ob LayoutRect relevantRect = relevantViewRect(&object->view()); // The objects are only relevant if they are being painted within the viewRect(). - if (!objectPaintRect.intersects(pixelSnappedIntRect(relevantRect))) + if (!objectPaintRect.intersects(snappedIntRect(relevantRect))) return; - IntRect snappedPaintRect = pixelSnappedIntRect(objectPaintRect); + IntRect snappedPaintRect = snappedIntRect(objectPaintRect); // If this object was previously counted as an unpainted object, remove it from that HashSet // and corresponding Region. FIXME: This doesn't do the right thing if the objects overlap. @@ -1369,11 +1819,11 @@ void Page::addRelevantRepaintedObject(RenderObject* object, const LayoutRect& ob // If the rect straddles both Regions, split it appropriately. if (topRelevantRect.intersects(snappedPaintRect) && bottomRelevantRect.intersects(snappedPaintRect)) { IntRect topIntersection = snappedPaintRect; - topIntersection.intersect(pixelSnappedIntRect(topRelevantRect)); + topIntersection.intersect(snappedIntRect(topRelevantRect)); m_topRelevantPaintedRegion.unite(topIntersection); IntRect bottomIntersection = snappedPaintRect; - bottomIntersection.intersect(pixelSnappedIntRect(bottomRelevantRect)); + bottomIntersection.intersect(snappedIntRect(bottomRelevantRect)); m_bottomRelevantPaintedRegion.unite(bottomIntersection); } else if (topRelevantRect.intersects(snappedPaintRect)) m_topRelevantPaintedRegion.unite(snappedPaintRect); @@ -1393,7 +1843,7 @@ void Page::addRelevantRepaintedObject(RenderObject* object, const LayoutRect& ob m_isCountingRelevantRepaintedObjects = false; resetRelevantPaintedObjectCounter(); if (Frame* frame = &mainFrame()) - frame->loader().didLayout(DidHitRelevantRepaintedObjectsAreaThreshold); + frame->loader().didReachLayoutMilestone(DidHitRelevantRepaintedObjectsAreaThreshold); } } @@ -1403,11 +1853,27 @@ void Page::addRelevantUnpaintedObject(RenderObject* object, const LayoutRect& ob return; // The objects are only relevant if they are being painted within the relevantViewRect(). - if (!objectPaintRect.intersects(pixelSnappedIntRect(relevantViewRect(&object->view())))) + if (!objectPaintRect.intersects(snappedIntRect(relevantViewRect(&object->view())))) return; m_relevantUnpaintedRenderObjects.add(object); - m_relevantUnpaintedRegion.unite(pixelSnappedIntRect(objectPaintRect)); + m_relevantUnpaintedRegion.unite(snappedIntRect(objectPaintRect)); +} + +void Page::suspendDeviceMotionAndOrientationUpdates() +{ + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (Document* document = frame->document()) + document->suspendDeviceMotionAndOrientationUpdates(); + } +} + +void Page::resumeDeviceMotionAndOrientationUpdates() +{ + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (Document* document = frame->document()) + document->resumeDeviceMotionAndOrientationUpdates(); + } } void Page::suspendActiveDOMObjectsAndAnimations() @@ -1464,89 +1930,298 @@ void Page::resetSeenMediaEngines() m_seenMediaEngines.clear(); } -std::unique_ptr<PageActivityAssertionToken> Page::createActivityToken() -{ - return m_pageThrottler->createActivityToken(); -} - -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) -void Page::hiddenPageDOMTimerThrottlingStateChanged() -{ - if (m_settings->hiddenPageDOMTimerThrottlingEnabled()) { -#if ENABLE(PAGE_VISIBILITY_API) - if (m_pageThrottler->shouldThrottleTimers()) - setTimerAlignmentInterval(Settings::hiddenPageDOMTimerAlignmentInterval()); -#endif - } else - setTimerAlignmentInterval(Settings::defaultDOMTimerAlignmentInterval()); -} -#endif - -#if (ENABLE_PAGE_VISIBILITY_API) void Page::hiddenPageCSSAnimationSuspensionStateChanged() { - if (!m_isVisible) { + if (!isVisible()) { if (m_settings->hiddenPageCSSAnimationSuspensionEnabled()) mainFrame().animation().suspendAnimations(); else mainFrame().animation().resumeAnimations(); } } -#endif #if ENABLE(VIDEO_TRACK) void Page::captionPreferencesChanged() { - for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; frame->document()->captionPreferencesChanged(); + } } #endif -void Page::incrementFrameHandlingBeforeUnloadEventCount() +void Page::forbidPrompts() { - ++m_framesHandlingBeforeUnloadEvent; + ++m_forbidPromptsDepth; } -void Page::decrementFrameHandlingBeforeUnloadEventCount() +void Page::allowPrompts() { - ASSERT(m_framesHandlingBeforeUnloadEvent); - --m_framesHandlingBeforeUnloadEvent; + ASSERT(m_forbidPromptsDepth); + --m_forbidPromptsDepth; } -bool Page::isAnyFrameHandlingBeforeUnloadEvent() +bool Page::arePromptsAllowed() { - return m_framesHandlingBeforeUnloadEvent; + return !m_forbidPromptsDepth; } -void Page::setUserContentController(UserContentController* userContentController) +PluginInfoProvider& Page::pluginInfoProvider() { - if (m_userContentController) - m_userContentController->removePage(*this); + return m_pluginInfoProvider; +} - m_userContentController = userContentController; +UserContentProvider& Page::userContentProvider() +{ + return m_userContentProvider; +} - if (m_userContentController) - m_userContentController->addPage(*this); +void Page::setUserContentProvider(Ref<UserContentProvider>&& userContentProvider) +{ + m_userContentProvider->removePage(*this); + m_userContentProvider = WTFMove(userContentProvider); + m_userContentProvider->addPage(*this); + + invalidateInjectedStyleSheetCacheInAllFrames(); } -Page::PageClients::PageClients() - : alternativeTextClient(nullptr) - , chromeClient(nullptr) -#if ENABLE(CONTEXT_MENUS) - , contextMenuClient(nullptr) +void Page::setStorageNamespaceProvider(Ref<StorageNamespaceProvider>&& storageNamespaceProvider) +{ + m_storageNamespaceProvider->removePage(*this); + m_storageNamespaceProvider = WTFMove(storageNamespaceProvider); + m_storageNamespaceProvider->addPage(*this); + + // This needs to reset all the local storage namespaces of all the pages. +} + +VisitedLinkStore& Page::visitedLinkStore() +{ + return m_visitedLinkStore; +} + +void Page::setVisitedLinkStore(Ref<VisitedLinkStore>&& visitedLinkStore) +{ + m_visitedLinkStore->removePage(*this); + m_visitedLinkStore = WTFMove(visitedLinkStore); + m_visitedLinkStore->addPage(*this); + + invalidateStylesForAllLinks(); +} + +SessionID Page::sessionID() const +{ + return m_sessionID; +} + +void Page::setSessionID(SessionID sessionID) +{ + ASSERT(sessionID.isValid()); + +#if ENABLE(INDEXED_DATABASE) + if (sessionID != m_sessionID) + m_idbIDBConnectionToServer = nullptr; +#endif + + bool privateBrowsingStateChanged = (sessionID.isEphemeral() != m_sessionID.isEphemeral()); + + m_sessionID = sessionID; + + if (!privateBrowsingStateChanged) + return; + + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->privateBrowsingStateDidChange(); + } + + // Collect the PluginViews in to a vector to ensure that action the plug-in takes + // from below privateBrowsingStateChanged does not affect their lifetime. + + for (auto& view : pluginViews()) + view->privateBrowsingStateChanged(sessionID.isEphemeral()); +} + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) +void Page::addPlaybackTargetPickerClient(uint64_t contextId) +{ + chrome().client().addPlaybackTargetPickerClient(contextId); +} + +void Page::removePlaybackTargetPickerClient(uint64_t contextId) +{ + chrome().client().removePlaybackTargetPickerClient(contextId); +} + +void Page::showPlaybackTargetPicker(uint64_t contextId, const WebCore::IntPoint& location, bool isVideo) +{ +#if PLATFORM(IOS) + // FIXME: refactor iOS implementation. + UNUSED_PARAM(contextId); + UNUSED_PARAM(location); + chrome().client().showPlaybackTargetPicker(isVideo); +#else + chrome().client().showPlaybackTargetPicker(contextId, location, isVideo); +#endif +} + +void Page::playbackTargetPickerClientStateDidChange(uint64_t contextId, MediaProducer::MediaStateFlags state) +{ + chrome().client().playbackTargetPickerClientStateDidChange(contextId, state); +} + +void Page::setMockMediaPlaybackTargetPickerEnabled(bool enabled) +{ + chrome().client().setMockMediaPlaybackTargetPickerEnabled(enabled); +} + +void Page::setMockMediaPlaybackTargetPickerState(const String& name, MediaPlaybackTargetContext::State state) +{ + chrome().client().setMockMediaPlaybackTargetPickerState(name, state); +} + +void Page::setPlaybackTarget(uint64_t contextId, Ref<MediaPlaybackTarget>&& target) +{ + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->setPlaybackTarget(contextId, target.copyRef()); + } +} + +void Page::playbackTargetAvailabilityDidChange(uint64_t contextId, bool available) +{ + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->playbackTargetAvailabilityDidChange(contextId, available); + } +} + +void Page::setShouldPlayToPlaybackTarget(uint64_t clientId, bool shouldPlay) +{ + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (!frame->document()) + continue; + frame->document()->setShouldPlayToPlaybackTarget(clientId, shouldPlay); + } +} #endif - , editorClient(nullptr) - , dragClient(nullptr) - , inspectorClient(nullptr) - , plugInClient(nullptr) - , progressTrackerClient(nullptr) - , validationMessageClient(nullptr) - , loaderClientForMainFrame(nullptr) + +WheelEventTestTrigger& Page::ensureTestTrigger() { + if (!m_testTrigger) { + m_testTrigger = adoptRef(new WheelEventTestTrigger()); + // We need to update the scrolling coordinator so that the mainframe scrolling node can expect wheel event test triggers. + if (auto* frameView = mainFrame().view()) { + if (m_scrollingCoordinator) + m_scrollingCoordinator->updateExpectsWheelEventTestTriggerWithFrameView(*frameView); + } + } + + return *m_testTrigger; } -Page::PageClients::~PageClients() +#if ENABLE(VIDEO) +void Page::setAllowsMediaDocumentInlinePlayback(bool flag) +{ + if (m_allowsMediaDocumentInlinePlayback == flag) + return; + m_allowsMediaDocumentInlinePlayback = flag; + + Vector<Ref<Document>> documents; + for (Frame* frame = &m_mainFrame.get(); frame; frame = frame->tree().traverseNext()) + documents.append(*frame->document()); + + for (auto& document : documents) + document->allowsMediaDocumentInlinePlaybackChanged(); +} +#endif + +#if ENABLE(INDEXED_DATABASE) +IDBClient::IDBConnectionToServer& Page::idbConnection() +{ + if (!m_idbIDBConnectionToServer) + m_idbIDBConnectionToServer = &databaseProvider().idbConnectionToServerForSession(m_sessionID); + + return *m_idbIDBConnectionToServer; +} +#endif + +#if ENABLE(RESOURCE_USAGE) +void Page::setResourceUsageOverlayVisible(bool visible) +{ + if (!visible) { + m_resourceUsageOverlay = nullptr; + return; + } + + if (!m_resourceUsageOverlay && m_settings->acceleratedCompositingEnabled()) + m_resourceUsageOverlay = std::make_unique<ResourceUsageOverlay>(*this); +} +#endif + +bool Page::isAlwaysOnLoggingAllowed() const { + return m_sessionID.isAlwaysOnLoggingAllowed(); } +String Page::captionUserPreferencesStyleSheet() +{ + return m_captionUserPreferencesStyleSheet; +} + +void Page::setCaptionUserPreferencesStyleSheet(const String& styleSheet) +{ + if (m_captionUserPreferencesStyleSheet == styleSheet) + return; + + m_captionUserPreferencesStyleSheet = styleSheet; + + invalidateInjectedStyleSheetCacheInAllFrames(); +} + +void Page::accessibilitySettingsDidChange() +{ + bool neededRecalc = false; + + for (Frame* frame = &mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (Document* document = frame->document()) { + auto* styleResolver = document->styleScope().resolverIfExists(); + if (styleResolver && styleResolver->hasMediaQueriesAffectedByAccessibilitySettingsChange()) { + document->styleScope().didChangeStyleSheetEnvironment(); + neededRecalc = true; + // FIXME: This instrumentation event is not strictly accurate since cached media query results do not persist across StyleResolver rebuilds. + InspectorInstrumentation::mediaQueryResultChanged(*document); + } + } + } + + if (neededRecalc) + LOG(Layout, "hasMediaQueriesAffectedByAccessibilitySettingsChange, enqueueing style recalc"); +} + +#if ENABLE(DATA_INTERACTION) + +bool Page::hasDataInteractionAtPosition(const FloatPoint& position) const +{ + auto currentSelection = m_mainFrame->selection().selection(); + if (!currentSelection.isRange()) + return false; + + if (auto selectedRange = currentSelection.toNormalizedRange()) { + Vector<SelectionRect> selectionRects; + selectedRange->collectSelectionRects(selectionRects); + for (auto selectionRect : selectionRects) { + if (FloatRect(selectionRect.rect()).contains(position)) + return true; + } + } + + return false; +} + +#endif + } // namespace WebCore diff --git a/Source/WebCore/page/Page.h b/Source/WebCore/page/Page.h index f0b331737..9bcf98a6b 100644 --- a/Source/WebCore/page/Page.h +++ b/Source/WebCore/page/Page.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2010, 2013, 2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * This library is free software; you can redistribute it and/or @@ -18,38 +18,49 @@ * Boston, MA 02110-1301, USA. */ -#ifndef Page_h -#define Page_h +#pragma once -#include "FeatureObserver.h" +#include "ActivityState.h" #include "FindOptions.h" #include "FrameLoaderTypes.h" #include "LayoutMilestones.h" #include "LayoutRect.h" +#include "MediaProducer.h" #include "PageVisibilityState.h" #include "Pagination.h" #include "PlatformScreen.h" #include "Region.h" +#include "ScrollTypes.h" +#include "SessionID.h" #include "Supplementable.h" +#include "Timer.h" +#include "UserInterfaceLayoutDirection.h" #include "ViewportArguments.h" +#include "WheelEventTestTrigger.h" +#include <memory> #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/Ref.h> #include <wtf/RefCounted.h> +#include <wtf/UniqueRef.h> #include <wtf/text/WTFString.h> #if OS(SOLARIS) #include <sys/time.h> // For time_t structure. #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) #include <wtf/SchedulePair.h> #endif -#if PLATFORM(IOS) -#include "Settings.h" +#if ENABLE(MEDIA_SESSION) +#include "MediaSessionEvents.h" +#endif + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) +#include "MediaPlaybackTargetContext.h" #endif namespace JSC { @@ -58,15 +69,22 @@ class Debugger; namespace WebCore { +namespace IDBClient { +class IDBConnectionToServer; +} + class AlternativeTextClient; +class ApplicationCacheStorage; class BackForwardController; class BackForwardClient; class Chrome; class ChromeClient; class ClientRectList; +class Color; class ContextMenuClient; class ContextMenuController; -class Document; +class DatabaseProvider; +class DiagnosticLoggingClient; class DragCaretController; class DragClient; class DragController; @@ -74,21 +92,23 @@ class EditorClient; class FocusController; class Frame; class FrameLoaderClient; -class FrameSelection; -class HaltablePlugin; class HistoryItem; +class HTMLMediaElement; +class UserInputBridge; class InspectorClient; class InspectorController; +class LibWebRTCProvider; class MainFrame; class MediaCanStartListener; -class Node; -class PageActivityAssertionToken; -class PageConsole; +class MediaPlaybackTarget; +class PageConfiguration; +class PageConsoleClient; class PageDebuggable; class PageGroup; -class PageThrottler; +class PerformanceMonitor; class PlugInClient; class PluginData; +class PluginInfoProvider; class PluginViewBase; class PointerLockController; class ProgressTracker; @@ -96,92 +116,92 @@ class ProgressTrackerClient; class Range; class RenderObject; class RenderTheme; +class ReplayController; +class ResourceUsageOverlay; class VisibleSelection; class ScrollableArea; class ScrollingCoordinator; class Settings; +class SocketProvider; class StorageNamespace; -class UserContentController; +class StorageNamespaceProvider; +class UserContentProvider; class ValidationMessageClient; +class ActivityStateChangeObserver; +class VisitedLinkStore; +class WebGLStateTracker; typedef uint64_t LinkHash; -enum FindDirection { FindDirectionForward, FindDirectionBackward }; +enum FindDirection { + FindDirectionForward, + FindDirectionBackward +}; -float deviceScaleFactor(Frame*); +enum class EventThrottlingBehavior { + Responsive, + Unresponsive +}; class Page : public Supplementable<Page> { WTF_MAKE_NONCOPYABLE(Page); + WTF_MAKE_FAST_ALLOCATED; friend class Settings; - friend class PageThrottler; public: - static void updateStyleForAllPagesAfterGlobalChangeInEnvironment(); - static void jettisonStyleResolversInAllDocuments(); - - // It is up to the platform to ensure that non-null clients are provided where required. - struct PageClients { - WTF_MAKE_NONCOPYABLE(PageClients); WTF_MAKE_FAST_ALLOCATED; - public: - PageClients(); - ~PageClients(); - - AlternativeTextClient* alternativeTextClient; - ChromeClient* chromeClient; -#if ENABLE(CONTEXT_MENUS) - ContextMenuClient* contextMenuClient; -#endif - EditorClient* editorClient; - DragClient* dragClient; - InspectorClient* inspectorClient; - PlugInClient* plugInClient; - ProgressTrackerClient* progressTrackerClient; - RefPtr<BackForwardClient> backForwardClient; - ValidationMessageClient* validationMessageClient; - FrameLoaderClient* loaderClientForMainFrame; - }; + WEBCORE_EXPORT static void updateStyleForAllPagesAfterGlobalChangeInEnvironment(); + WEBCORE_EXPORT static void clearPreviousItemFromAllPages(HistoryItem*); - explicit Page(PageClients&); - ~Page(); + WEBCORE_EXPORT explicit Page(PageConfiguration&&); + WEBCORE_EXPORT ~Page(); - uint64_t renderTreeSize() const; + WEBCORE_EXPORT uint64_t renderTreeSize() const; void setNeedsRecalcStyleInAllFrames(); RenderTheme& theme() const { return *m_theme; } - ViewportArguments viewportArguments() const; + WEBCORE_EXPORT ViewportArguments viewportArguments() const; static void refreshPlugins(bool reload); - PluginData& pluginData() const; + WEBCORE_EXPORT PluginData& pluginData(); + void clearPluginData(); - void setCanStartMedia(bool); + WEBCORE_EXPORT void setCanStartMedia(bool); bool canStartMedia() const { return m_canStartMedia; } - EditorClient* editorClient() const { return m_editorClient; } + EditorClient& editorClient() { return m_editorClient.get(); } PlugInClient* plugInClient() const { return m_plugInClient; } - MainFrame& mainFrame() { ASSERT(m_mainFrame); return *m_mainFrame; } - const MainFrame& mainFrame() const { ASSERT(m_mainFrame); return *m_mainFrame; } + MainFrame& mainFrame() { return m_mainFrame.get(); } + const MainFrame& mainFrame() const { return m_mainFrame.get(); } bool openedByDOM() const; void setOpenedByDOM(); - void goToItem(HistoryItem*, FrameLoadType); + WEBCORE_EXPORT void goToItem(HistoryItem&, FrameLoadType); - void setGroupName(const String&); - const String& groupName() const; + WEBCORE_EXPORT void setGroupName(const String&); + WEBCORE_EXPORT const String& groupName() const; PageGroup& group(); - PageGroup* groupPtr() { return m_group; } // can return 0 + + static void forEachPage(std::function<void(Page&)>); void incrementSubframeCount() { ++m_subframeCount; } void decrementSubframeCount() { ASSERT(m_subframeCount); --m_subframeCount; } int subframeCount() const { checkSubframeCountConsistency(); return m_subframeCount; } + void incrementNestedRunLoopCount(); + void decrementNestedRunLoopCount(); + bool insideNestedRunLoop() const { return m_nestedRunLoopCount > 0; } + WEBCORE_EXPORT void whenUnnested(std::function<void()>); + #if ENABLE(REMOTE_INSPECTOR) - bool remoteInspectionAllowed() const; - void setRemoteInspectionAllowed(bool); + WEBCORE_EXPORT bool remoteInspectionAllowed() const; + WEBCORE_EXPORT void setRemoteInspectionAllowed(bool); + WEBCORE_EXPORT String remoteInspectionNameOverride() const; + WEBCORE_EXPORT void setRemoteInspectionNameOverride(const String&); void remoteInspectorInformationDidChange() const; #endif @@ -194,25 +214,30 @@ public: #if ENABLE(CONTEXT_MENUS) ContextMenuController& contextMenuController() const { return *m_contextMenuController; } #endif -#if ENABLE(INSPECTOR) - InspectorController& inspectorController() const { return *m_inspectorController; } + UserInputBridge& userInputBridge() const { return *m_userInputBridge; } +#if ENABLE(WEB_REPLAY) + ReplayController& replayController() const { return *m_replayController; } #endif + InspectorController& inspectorController() const { return *m_inspectorController; } #if ENABLE(POINTER_LOCK) - PointerLockController* pointerLockController() const { return m_pointerLockController.get(); } + PointerLockController& pointerLockController() const { return *m_pointerLockController; } #endif - ValidationMessageClient* validationMessageClient() const { return m_validationMessageClient; } + LibWebRTCProvider& libWebRTCProvider() { return m_libWebRTCProvider.get(); } - ScrollingCoordinator* scrollingCoordinator(); + ValidationMessageClient* validationMessageClient() const { return m_validationMessageClient.get(); } + void updateValidationBubbleStateIfNeeded(); - String scrollingStateTreeAsText(); - String synchronousScrollingReasonsAsText(); - PassRefPtr<ClientRectList> nonFastScrollableRects(const Frame*); + WEBCORE_EXPORT ScrollingCoordinator* scrollingCoordinator(); + + WEBCORE_EXPORT String scrollingStateTreeAsText(); + WEBCORE_EXPORT String synchronousScrollingReasonsAsText(); + WEBCORE_EXPORT Ref<ClientRectList> nonFastScrollableRects(); Settings& settings() const { return *m_settings; } ProgressTracker& progress() const { return *m_progress; } BackForwardController& backForward() const { return *m_backForwardController; } - FeatureObserver* featureObserver() { return &m_featureObserver; } + std::chrono::milliseconds domTimerAlignmentInterval() const { return m_timerAlignmentInterval; } #if ENABLE(VIEW_MODE_CSS_MEDIA) enum ViewMode { @@ -226,20 +251,20 @@ public: static ViewMode stringToViewMode(const String&); ViewMode viewMode() const { return m_viewMode; } - void setViewMode(ViewMode); + WEBCORE_EXPORT void setViewMode(ViewMode); #endif // ENABLE(VIEW_MODE_CSS_MEDIA) void setTabKeyCyclesThroughElements(bool b) { m_tabKeyCyclesThroughElements = b; } bool tabKeyCyclesThroughElements() const { return m_tabKeyCyclesThroughElements; } - bool findString(const String&, FindOptions); + WEBCORE_EXPORT bool findString(const String&, FindOptions); - PassRefPtr<Range> rangeOfString(const String&, Range*, FindOptions); + WEBCORE_EXPORT RefPtr<Range> rangeOfString(const String&, Range*, FindOptions); - unsigned countFindMatches(const String&, FindOptions, unsigned maxMatchCount); - unsigned markAllMatchesForText(const String&, FindOptions, bool shouldHighlight, unsigned maxMatchCount); + WEBCORE_EXPORT unsigned countFindMatches(const String&, FindOptions, unsigned maxMatchCount); + WEBCORE_EXPORT unsigned markAllMatchesForText(const String&, FindOptions, bool shouldHighlight, unsigned maxMatchCount); - void unmarkAllTextMatches(); + WEBCORE_EXPORT void unmarkAllTextMatches(); // find all the Ranges for the matching text. // Upon return, indexForSelection will be one of the following: @@ -247,95 +272,145 @@ public: // the index of the first range after the user selection // NoMatchAfterUserSelection if there is no matching text after the user selection. enum { NoMatchAfterUserSelection = -1 }; - void findStringMatchingRanges(const String&, FindOptions, int maxCount, Vector<RefPtr<Range>>*, int& indexForSelection); -#if PLATFORM(MAC) - void addSchedulePair(PassRefPtr<SchedulePair>); - void removeSchedulePair(PassRefPtr<SchedulePair>); + WEBCORE_EXPORT void findStringMatchingRanges(const String&, FindOptions, int maxCount, Vector<RefPtr<Range>>&, int& indexForSelection); +#if PLATFORM(COCOA) + void platformInitialize(); + WEBCORE_EXPORT void addSchedulePair(Ref<SchedulePair>&&); + WEBCORE_EXPORT void removeSchedulePair(Ref<SchedulePair>&&); SchedulePairHashSet* scheduledRunLoopPairs() { return m_scheduledRunLoopPairs.get(); } - OwnPtr<SchedulePairHashSet> m_scheduledRunLoopPairs; + std::unique_ptr<SchedulePairHashSet> m_scheduledRunLoopPairs; #endif - const VisibleSelection& selection() const; + WEBCORE_EXPORT const VisibleSelection& selection() const; - void setDefersLoading(bool); + WEBCORE_EXPORT void setDefersLoading(bool); bool defersLoading() const { return m_defersLoading; } - void clearUndoRedoOperations(); + WEBCORE_EXPORT void clearUndoRedoOperations(); - bool inLowQualityImageInterpolationMode() const; - void setInLowQualityImageInterpolationMode(bool = true); + WEBCORE_EXPORT bool inLowQualityImageInterpolationMode() const; + WEBCORE_EXPORT void setInLowQualityImageInterpolationMode(bool = true); float mediaVolume() const { return m_mediaVolume; } - void setMediaVolume(float); + WEBCORE_EXPORT void setMediaVolume(float); - void setPageScaleFactor(float scale, const IntPoint& origin); + WEBCORE_EXPORT void setPageScaleFactor(float scale, const IntPoint& origin, bool inStableState = true); float pageScaleFactor() const { return m_pageScaleFactor; } + UserInterfaceLayoutDirection userInterfaceLayoutDirection() const { return m_userInterfaceLayoutDirection; } + WEBCORE_EXPORT void setUserInterfaceLayoutDirection(UserInterfaceLayoutDirection); + + void didStartProvisionalLoad(); + void didFinishLoad(); // Called when the load has been committed in the main frame. + + // The view scale factor is multiplied into the page scale factor by all + // callers of setPageScaleFactor. + WEBCORE_EXPORT void setViewScaleFactor(float); + float viewScaleFactor() const { return m_viewScaleFactor; } + + WEBCORE_EXPORT void setZoomedOutPageScaleFactor(float); + float zoomedOutPageScaleFactor() const { return m_zoomedOutPageScaleFactor; } + float deviceScaleFactor() const { return m_deviceScaleFactor; } - void setDeviceScaleFactor(float); + WEBCORE_EXPORT void setDeviceScaleFactor(float); + + float topContentInset() const { return m_topContentInset; } + WEBCORE_EXPORT void setTopContentInset(float); +#if PLATFORM(IOS) + FloatSize obscuredInset() const { return m_obscuredInset; } + void setObscuredInset(FloatSize inset) { m_obscuredInset = inset; } + + bool enclosedInScrollableAncestorView() const { return m_enclosedInScrollableAncestorView; } + void setEnclosedInScrollableAncestorView(bool f) { m_enclosedInScrollableAncestorView = f; } +#endif + +#if ENABLE(TEXT_AUTOSIZING) + float textAutosizingWidth() const { return m_textAutosizingWidth; } + void setTextAutosizingWidth(float textAutosizingWidth) { m_textAutosizingWidth = textAutosizingWidth; } +#endif + bool shouldSuppressScrollbarAnimations() const { return m_suppressScrollbarAnimations; } - void setShouldSuppressScrollbarAnimations(bool suppressAnimations); + WEBCORE_EXPORT void setShouldSuppressScrollbarAnimations(bool suppressAnimations); void lockAllOverlayScrollbarsToHidden(bool lockOverlayScrollbars); + + WEBCORE_EXPORT void setVerticalScrollElasticity(ScrollElasticity); + ScrollElasticity verticalScrollElasticity() const { return static_cast<ScrollElasticity>(m_verticalScrollElasticity); } + + WEBCORE_EXPORT void setHorizontalScrollElasticity(ScrollElasticity); + ScrollElasticity horizontalScrollElasticity() const { return static_cast<ScrollElasticity>(m_horizontalScrollElasticity); } + + WEBCORE_EXPORT void accessibilitySettingsDidChange(); // Page and FrameView both store a Pagination value. Page::pagination() is set only by API, // and FrameView::pagination() is set only by CSS. Page::pagination() will affect all // FrameViews in the page cache, but FrameView::pagination() only affects the current // FrameView. const Pagination& pagination() const { return m_pagination; } - void setPagination(const Pagination&); + WEBCORE_EXPORT void setPagination(const Pagination&); + bool paginationLineGridEnabled() const { return m_paginationLineGridEnabled; } + WEBCORE_EXPORT void setPaginationLineGridEnabled(bool flag); - unsigned pageCount() const; + WEBCORE_EXPORT unsigned pageCount() const; + + WEBCORE_EXPORT DiagnosticLoggingClient& diagnosticLoggingClient() const; // Notifications when the Page starts and stops being presented via a native window. - void setIsVisible(bool isVisible, bool isInitial); - void setIsPrerender(); - bool isVisible() const { return m_isVisible; } + WEBCORE_EXPORT void setActivityState(ActivityState::Flags); + ActivityState::Flags activityState() const { return m_activityState; } + + bool isWindowActive() const; + bool isVisibleAndActive() const; + WEBCORE_EXPORT void setIsVisible(bool); + WEBCORE_EXPORT void setIsPrerender(); + bool isVisible() const { return m_activityState & ActivityState::IsVisible; } // Notification that this Page was moved into or out of a native window. - void setIsInWindow(bool); - bool isInWindow() const { return m_isInWindow; } + WEBCORE_EXPORT void setIsInWindow(bool); + bool isInWindow() const { return m_activityState & ActivityState::IsInWindow; } - void suspendScriptedAnimations(); - void resumeScriptedAnimations(); + void setIsClosing() { m_isClosing = true; } + bool isClosing() const { return m_isClosing; } + + void addActivityStateChangeObserver(ActivityStateChangeObserver&); + void removeActivityStateChangeObserver(ActivityStateChangeObserver&); + + WEBCORE_EXPORT void suspendScriptedAnimations(); + WEBCORE_EXPORT void resumeScriptedAnimations(); bool scriptedAnimationsSuspended() const { return m_scriptedAnimationsSuspended; } - void setIsVisuallyIdle(bool); void userStyleSheetLocationChanged(); const String& userStyleSheet() const; void dnsPrefetchingStateChanged(); void storageBlockingStateChanged(); - void privateBrowsingStateChanged(); + +#if ENABLE(RESOURCE_USAGE) + void setResourceUsageOverlayVisible(bool); +#endif + + void setAsRunningUserScripts() { m_isRunningUserScripts = true; } + bool isRunningUserScripts() const { return m_isRunningUserScripts; } void setDebugger(JSC::Debugger*); JSC::Debugger* debugger() const { return m_debugger; } - static void removeAllVisitedLinks(); + WEBCORE_EXPORT void invalidateStylesForAllLinks(); + WEBCORE_EXPORT void invalidateStylesForLink(LinkHash); - static void allVisitedStateChanged(PageGroup*); - static void visitedStateChanged(PageGroup*, LinkHash visitedHash); + void invalidateInjectedStyleSheetCacheInAllFrames(); StorageNamespace* sessionStorage(bool optionalCreate = true); - void setSessionStorage(PassRefPtr<StorageNamespace>); + void setSessionStorage(RefPtr<StorageNamespace>&&); - // FIXME: We should make Settings::maxParseDuration() platform-independent, remove {has, set}CustomHTMLTokenizerTimeDelay() - // and customHTMLTokenizerTimeDelay() and modify theirs callers to update or query Settings::maxParseDuration(). - void setCustomHTMLTokenizerTimeDelay(double); -#if PLATFORM(IOS) - bool hasCustomHTMLTokenizerTimeDelay() const { return m_settings->maxParseDuration() != -1; } - double customHTMLTokenizerTimeDelay() const { ASSERT(m_settings->maxParseDuration() != -1); return m_settings->maxParseDuration(); } -#else - bool hasCustomHTMLTokenizerTimeDelay() const { return m_customHTMLTokenizerTimeDelay != -1; } - double customHTMLTokenizerTimeDelay() const { ASSERT(m_customHTMLTokenizerTimeDelay != -1); return m_customHTMLTokenizerTimeDelay; } -#endif + StorageNamespace* ephemeralLocalStorage(bool optionalCreate = true); + void setEphemeralLocalStorage(RefPtr<StorageNamespace>&&); - void setCustomHTMLTokenizerChunkSize(int); - bool hasCustomHTMLTokenizerChunkSize() const { return m_customHTMLTokenizerChunkSize != -1; } - int customHTMLTokenizerChunkSize() const { ASSERT(m_customHTMLTokenizerChunkSize != -1); return m_customHTMLTokenizerChunkSize; } + bool hasCustomHTMLTokenizerTimeDelay() const; + double customHTMLTokenizerTimeDelay() const; - void setMemoryCacheClientCallsEnabled(bool); + WEBCORE_EXPORT void setMemoryCacheClientCallsEnabled(bool); bool areMemoryCacheClientCallsEnabled() const { return m_areMemoryCacheClientCallsEnabled; } // Don't allow more than a certain number of frames in a page. @@ -347,31 +422,35 @@ public: void setEditable(bool isEditable) { m_isEditable = isEditable; } bool isEditable() { return m_isEditable; } -#if ENABLE(PAGE_VISIBILITY_API) - PageVisibilityState visibilityState() const; -#endif - void resumeAnimatingImages(); + WEBCORE_EXPORT PageVisibilityState visibilityState() const; + WEBCORE_EXPORT void resumeAnimatingImages(); - void addLayoutMilestones(LayoutMilestones); - void removeLayoutMilestones(LayoutMilestones); + WEBCORE_EXPORT void addLayoutMilestones(LayoutMilestones); + WEBCORE_EXPORT void removeLayoutMilestones(LayoutMilestones); LayoutMilestones requestedLayoutMilestones() const { return m_requestedLayoutMilestones; } #if ENABLE(RUBBER_BANDING) - void addHeaderWithHeight(int); - void addFooterWithHeight(int); + WEBCORE_EXPORT void addHeaderWithHeight(int); + WEBCORE_EXPORT void addFooterWithHeight(int); #endif int headerHeight() const { return m_headerHeight; } int footerHeight() const { return m_footerHeight; } + WEBCORE_EXPORT Color pageExtendedBackgroundColor() const; + bool isCountingRelevantRepaintedObjects() const; + void setIsCountingRelevantRepaintedObjects(bool isCounting) { m_isCountingRelevantRepaintedObjects = isCounting; } void startCountingRelevantRepaintedObjects(); void resetRelevantPaintedObjectCounter(); void addRelevantRepaintedObject(RenderObject*, const LayoutRect& objectPaintRect); void addRelevantUnpaintedObject(RenderObject*, const LayoutRect& objectPaintRect); - void suspendActiveDOMObjectsAndAnimations(); - void resumeActiveDOMObjectsAndAnimations(); + WEBCORE_EXPORT void suspendActiveDOMObjectsAndAnimations(); + WEBCORE_EXPORT void resumeActiveDOMObjectsAndAnimations(); + void suspendDeviceMotionAndOrientationUpdates(); + void resumeDeviceMotionAndOrientationUpdates(); + #ifndef NDEBUG void setIsPainting(bool painting) { m_isPainting = painting; } bool isPainting() const { return m_isPainting; } @@ -380,7 +459,7 @@ public: AlternativeTextClient* alternativeTextClient() const { return m_alternativeTextClient; } bool hasSeenPlugin(const String& serviceType) const; - bool hasSeenAnyPlugin() const; + WEBCORE_EXPORT bool hasSeenAnyPlugin() const; void sawPlugin(const String& serviceType); void resetSeenPlugins(); @@ -389,33 +468,121 @@ public: void sawMediaEngine(const String& engineName); void resetSeenMediaEngines(); - PageThrottler& pageThrottler() { return *m_pageThrottler; } - std::unique_ptr<PageActivityAssertionToken> createActivityToken(); + PageConsoleClient& console() { return *m_consoleClient; } - PageConsole& console() { return *m_console; } - -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) - void hiddenPageDOMTimerThrottlingStateChanged(); +#if ENABLE(REMOTE_INSPECTOR) + PageDebuggable& inspectorDebuggable() const { return *m_inspectorDebuggable.get(); } #endif -#if ENABLE(PAGE_VISIBILITY_API) + void hiddenPageCSSAnimationSuspensionStateChanged(); -#endif #if ENABLE(VIDEO_TRACK) void captionPreferencesChanged(); #endif - void incrementFrameHandlingBeforeUnloadEventCount(); - void decrementFrameHandlingBeforeUnloadEventCount(); - bool isAnyFrameHandlingBeforeUnloadEvent(); + void forbidPrompts(); + void allowPrompts(); + bool arePromptsAllowed(); + void setLastSpatialNavigationCandidateCount(unsigned count) { m_lastSpatialNavigationCandidatesCount = count; } unsigned lastSpatialNavigationCandidateCount() const { return m_lastSpatialNavigationCandidatesCount; } - void setUserContentController(UserContentController*); - UserContentController* userContentController() { return m_userContentController.get(); } + ApplicationCacheStorage& applicationCacheStorage() { return m_applicationCacheStorage; } + DatabaseProvider& databaseProvider() { return m_databaseProvider; } + SocketProvider& socketProvider() { return m_socketProvider; } + + StorageNamespaceProvider& storageNamespaceProvider() { return m_storageNamespaceProvider.get(); } + void setStorageNamespaceProvider(Ref<StorageNamespaceProvider>&&); + + PluginInfoProvider& pluginInfoProvider(); + + UserContentProvider& userContentProvider(); + WEBCORE_EXPORT void setUserContentProvider(Ref<UserContentProvider>&&); + + VisitedLinkStore& visitedLinkStore(); + WEBCORE_EXPORT void setVisitedLinkStore(Ref<VisitedLinkStore>&&); + + WEBCORE_EXPORT SessionID sessionID() const; + WEBCORE_EXPORT void setSessionID(SessionID); + WEBCORE_EXPORT void enableLegacyPrivateBrowsing(bool privateBrowsingEnabled); + bool usesEphemeralSession() const { return m_sessionID.isEphemeral(); } + + MediaProducer::MediaStateFlags mediaState() const { return m_mediaState; } + void updateIsPlayingMedia(uint64_t); + MediaProducer::MutedStateFlags mutedState() const { return m_mutedState; } + bool isAudioMuted() const { return m_mutedState & MediaProducer::AudioIsMuted; } + bool isMediaCaptureMuted() const { return m_mutedState & MediaProducer::CaptureDevicesAreMuted; }; + WEBCORE_EXPORT void setMuted(MediaProducer::MutedStateFlags); + +#if ENABLE(MEDIA_SESSION) + WEBCORE_EXPORT void handleMediaEvent(MediaEventType); + WEBCORE_EXPORT void setVolumeOfMediaElement(double, uint64_t); +#endif + +#if ENABLE(WIRELESS_PLAYBACK_TARGET) + void addPlaybackTargetPickerClient(uint64_t); + void removePlaybackTargetPickerClient(uint64_t); + void showPlaybackTargetPicker(uint64_t, const IntPoint&, bool); + void playbackTargetPickerClientStateDidChange(uint64_t, MediaProducer::MediaStateFlags); + WEBCORE_EXPORT void setMockMediaPlaybackTargetPickerEnabled(bool); + WEBCORE_EXPORT void setMockMediaPlaybackTargetPickerState(const String&, MediaPlaybackTargetContext::State); + + WEBCORE_EXPORT void setPlaybackTarget(uint64_t, Ref<MediaPlaybackTarget>&&); + WEBCORE_EXPORT void playbackTargetAvailabilityDidChange(uint64_t, bool); + WEBCORE_EXPORT void setShouldPlayToPlaybackTarget(uint64_t, bool); +#endif + + RefPtr<WheelEventTestTrigger> testTrigger() const { return m_testTrigger; } + WEBCORE_EXPORT WheelEventTestTrigger& ensureTestTrigger(); + void clearTrigger() { m_testTrigger = nullptr; } + bool expectsWheelEventTriggers() const { return !!m_testTrigger; } + +#if ENABLE(VIDEO) + bool allowsMediaDocumentInlinePlayback() const { return m_allowsMediaDocumentInlinePlayback; } + WEBCORE_EXPORT void setAllowsMediaDocumentInlinePlayback(bool); +#endif + + bool allowsPlaybackControlsForAutoplayingAudio() const { return m_allowsPlaybackControlsForAutoplayingAudio; } + void setAllowsPlaybackControlsForAutoplayingAudio(bool allowsPlaybackControlsForAutoplayingAudio) { m_allowsPlaybackControlsForAutoplayingAudio = allowsPlaybackControlsForAutoplayingAudio; } + +#if ENABLE(INDEXED_DATABASE) + IDBClient::IDBConnectionToServer& idbConnection(); +#endif + + void setShowAllPlugins(bool showAll) { m_showAllPlugins = showAll; } + bool showAllPlugins() const; + + WEBCORE_EXPORT void setTimerAlignmentIntervalIncreaseLimit(std::chrono::milliseconds); + + bool isControlledByAutomation() const { return m_controlledByAutomation; } + void setControlledByAutomation(bool controlled) { m_controlledByAutomation = controlled; } + + WEBCORE_EXPORT bool isAlwaysOnLoggingAllowed() const; + + String captionUserPreferencesStyleSheet(); + void setCaptionUserPreferencesStyleSheet(const String&); + + bool isResourceCachingDisabled() const { return m_resourceCachingDisabled; } + void setResourceCachingDisabled(bool disabled) { m_resourceCachingDisabled = disabled; } + + std::optional<EventThrottlingBehavior> eventThrottlingBehaviorOverride() const { return m_eventThrottlingBehaviorOverride; } + void setEventThrottlingBehaviorOverride(std::optional<EventThrottlingBehavior> throttling) { m_eventThrottlingBehaviorOverride = throttling; } + + WebGLStateTracker* webGLStateTracker() const { return m_webGLStateTracker.get(); } + + bool isOnlyNonUtilityPage() const; + bool isUtilityPage() const { return m_isUtilityPage; } + +#if ENABLE(DATA_INTERACTION) + WEBCORE_EXPORT bool hasDataInteractionAtPosition(const FloatPoint&) const; +#endif private: - void initGroup(); + WEBCORE_EXPORT void initGroup(); + + void setIsInWindowInternal(bool); + void setIsVisibleInternal(bool); + void setIsVisuallyIdleInternal(bool); #if ASSERT_DISABLED void checkSubframeCountConsistency() const { } @@ -428,18 +595,16 @@ private: unsigned findMatchesForText(const String&, FindOptions, unsigned maxMatchCount, ShouldHighlightMatches, ShouldMarkMatches); - MediaCanStartListener* takeAnyMediaCanStartListener(); - - void setMinimumTimerInterval(double); - double minimumTimerInterval() const; - - void setTimerAlignmentInterval(double); - double timerAlignmentInterval() const; + std::optional<std::pair<MediaCanStartListener&, Document&>> takeAnyMediaCanStartListener(); Vector<Ref<PluginViewBase>> pluginViews(); - void throttleTimers(); - void unthrottleTimers(); + enum class TimerThrottlingState { Disabled, Enabled, EnabledIncreasing }; + void hiddenPageDOMTimerThrottlingStateChanged(); + void setTimerThrottlingState(TimerThrottlingState); + void updateTimerThrottlingState(); + void updateDOMTimerAlignmentInterval(); + void timerAlignmentIntervalIncreaseTimerFired(); const std::unique_ptr<Chrome> m_chrome; const std::unique_ptr<DragCaretController> m_dragCaretController; @@ -451,11 +616,13 @@ private: #if ENABLE(CONTEXT_MENUS) const std::unique_ptr<ContextMenuController> m_contextMenuController; #endif -#if ENABLE(INSPECTOR) - const std::unique_ptr<InspectorController> m_inspectorController; + const std::unique_ptr<UserInputBridge> m_userInputBridge; +#if ENABLE(WEB_REPLAY) + const std::unique_ptr<ReplayController> m_replayController; #endif + const std::unique_ptr<InspectorController> m_inspectorController; #if ENABLE(POINTER_LOCK) - OwnPtr<PointerLockController> m_pointerLockController; + const std::unique_ptr<PointerLockController> m_pointerLockController; #endif RefPtr<ScrollingCoordinator> m_scrollingCoordinator; @@ -463,19 +630,24 @@ private: const std::unique_ptr<ProgressTracker> m_progress; const std::unique_ptr<BackForwardController> m_backForwardController; - const RefPtr<MainFrame> m_mainFrame; + Ref<MainFrame> m_mainFrame; - mutable RefPtr<PluginData> m_pluginData; + RefPtr<PluginData> m_pluginData; RefPtr<RenderTheme> m_theme; - EditorClient* m_editorClient; + UniqueRef<EditorClient> m_editorClient; PlugInClient* m_plugInClient; - ValidationMessageClient* m_validationMessageClient; + std::unique_ptr<ValidationMessageClient> m_validationMessageClient; + std::unique_ptr<DiagnosticLoggingClient> m_diagnosticLoggingClient; + std::unique_ptr<WebGLStateTracker> m_webGLStateTracker; - FeatureObserver m_featureObserver; + UniqueRef<LibWebRTCProvider> m_libWebRTCProvider; - int m_subframeCount; + int m_nestedRunLoopCount { 0 }; + std::function<void()> m_unnestCallback; + + int m_subframeCount { 0 }; String m_groupName; bool m_openedByDOM; @@ -486,43 +658,63 @@ private: bool m_inLowQualityInterpolationMode; bool m_areMemoryCacheClientCallsEnabled; float m_mediaVolume; + MediaProducer::MutedStateFlags m_mutedState { MediaProducer::NoneMuted }; float m_pageScaleFactor; - float m_deviceScaleFactor; + float m_zoomedOutPageScaleFactor; + float m_deviceScaleFactor { 1 }; + float m_viewScaleFactor { 1 }; + + float m_topContentInset; + +#if PLATFORM(IOS) + // This is only used for history scroll position restoration. + FloatSize m_obscuredInset; + bool m_enclosedInScrollableAncestorView { false }; +#endif +#if ENABLE(TEXT_AUTOSIZING) + float m_textAutosizingWidth; +#endif + bool m_suppressScrollbarAnimations; + + unsigned m_verticalScrollElasticity : 2; // ScrollElasticity + unsigned m_horizontalScrollElasticity : 2; // ScrollElasticity Pagination m_pagination; + bool m_paginationLineGridEnabled { false }; String m_userStyleSheetPath; mutable String m_userStyleSheet; mutable bool m_didLoadUserStyleSheet; mutable time_t m_userStyleSheetModificationTime; + String m_captionUserPreferencesStyleSheet; + std::unique_ptr<PageGroup> m_singlePageGroup; PageGroup* m_group; JSC::Debugger* m_debugger; - double m_customHTMLTokenizerTimeDelay; - int m_customHTMLTokenizerChunkSize; - bool m_canStartMedia; RefPtr<StorageNamespace> m_sessionStorage; + RefPtr<StorageNamespace> m_ephemeralLocalStorage; #if ENABLE(VIEW_MODE_CSS_MEDIA) ViewMode m_viewMode; #endif // ENABLE(VIEW_MODE_CSS_MEDIA) - double m_minimumTimerInterval; - - double m_timerAlignmentInterval; + TimerThrottlingState m_timerThrottlingState { TimerThrottlingState::Disabled }; + std::chrono::steady_clock::time_point m_timerThrottlingStateLastChangedTime { std::chrono::steady_clock::duration::zero() }; + std::chrono::milliseconds m_timerAlignmentInterval; + Timer m_timerAlignmentIntervalIncreaseTimer; + std::chrono::milliseconds m_timerAlignmentIntervalIncreaseLimit { 0 }; bool m_isEditable; - bool m_isInWindow; - bool m_isVisible; bool m_isPrerender; + ActivityState::Flags m_activityState; LayoutMilestones m_requestedLayoutMilestones; @@ -540,20 +732,57 @@ private: AlternativeTextClient* m_alternativeTextClient; bool m_scriptedAnimationsSuspended; - const std::unique_ptr<PageThrottler> m_pageThrottler; - const std::unique_ptr<PageConsole> m_console; + const std::unique_ptr<PageConsoleClient> m_consoleClient; #if ENABLE(REMOTE_INSPECTOR) const std::unique_ptr<PageDebuggable> m_inspectorDebuggable; #endif +#if ENABLE(INDEXED_DATABASE) + RefPtr<IDBClient::IDBConnectionToServer> m_idbIDBConnectionToServer; +#endif + HashSet<String> m_seenPlugins; HashSet<String> m_seenMediaEngines; unsigned m_lastSpatialNavigationCandidatesCount; - unsigned m_framesHandlingBeforeUnloadEvent; + unsigned m_forbidPromptsDepth; + + Ref<SocketProvider> m_socketProvider; + Ref<ApplicationCacheStorage> m_applicationCacheStorage; + Ref<DatabaseProvider> m_databaseProvider; + Ref<PluginInfoProvider> m_pluginInfoProvider; + Ref<StorageNamespaceProvider> m_storageNamespaceProvider; + Ref<UserContentProvider> m_userContentProvider; + Ref<VisitedLinkStore> m_visitedLinkStore; + RefPtr<WheelEventTestTrigger> m_testTrigger; + + HashSet<ActivityStateChangeObserver*> m_activityStateChangeObservers; + +#if ENABLE(RESOURCE_USAGE) + std::unique_ptr<ResourceUsageOverlay> m_resourceUsageOverlay; +#endif + + SessionID m_sessionID; - RefPtr<UserContentController> m_userContentController; + bool m_isClosing; + + MediaProducer::MediaStateFlags m_mediaState { MediaProducer::IsNotPlaying }; + + bool m_allowsMediaDocumentInlinePlayback { false }; + bool m_allowsPlaybackControlsForAutoplayingAudio { false }; + bool m_showAllPlugins { false }; + bool m_controlledByAutomation { false }; + bool m_resourceCachingDisabled { false }; + bool m_isUtilityPage; + UserInterfaceLayoutDirection m_userInterfaceLayoutDirection { UserInterfaceLayoutDirection::LTR }; + + // For testing. + std::optional<EventThrottlingBehavior> m_eventThrottlingBehaviorOverride; + + std::unique_ptr<PerformanceMonitor> m_performanceMonitor; + + bool m_isRunningUserScripts { false }; }; inline PageGroup& Page::group() @@ -564,5 +793,3 @@ inline PageGroup& Page::group() } } // namespace WebCore - -#endif // Page_h diff --git a/Source/WebCore/page/PageConfiguration.cpp b/Source/WebCore/page/PageConfiguration.cpp new file mode 100644 index 000000000..edb9206a8 --- /dev/null +++ b/Source/WebCore/page/PageConfiguration.cpp @@ -0,0 +1,56 @@ +/* + * 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 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. + */ + +#include "config.h" +#include "PageConfiguration.h" + +#include "ApplicationCacheStorage.h" +#include "BackForwardClient.h" +#include "DatabaseProvider.h" +#include "DiagnosticLoggingClient.h" +#include "EditorClient.h" +#include "LibWebRTCProvider.h" +#include "PluginInfoProvider.h" +#include "SocketProvider.h" +#include "StorageNamespaceProvider.h" +#include "UserContentController.h" +#include "ValidationMessageClient.h" +#include "VisitedLinkStore.h" +#include "WebGLStateTracker.h" + +namespace WebCore { + +PageConfiguration::PageConfiguration(UniqueRef<EditorClient>&& editorClient, Ref<SocketProvider>&& socketProvider, UniqueRef<LibWebRTCProvider>&& libWebRTCProvider) + : editorClient(WTFMove(editorClient)) + , socketProvider(WTFMove(socketProvider)) + , libWebRTCProvider(WTFMove(libWebRTCProvider)) +{ +} + +PageConfiguration::~PageConfiguration() +{ +} + +} diff --git a/Source/WebCore/page/PageConfiguration.h b/Source/WebCore/page/PageConfiguration.h new file mode 100644 index 000000000..76ce2a0af --- /dev/null +++ b/Source/WebCore/page/PageConfiguration.h @@ -0,0 +1,94 @@ +/* + * 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 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. + */ + +#pragma once + +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> +#include <wtf/UniqueRef.h> + +namespace WebCore { + +class AlternativeTextClient; +class ApplicationCacheStorage; +class BackForwardClient; +class ChromeClient; +class ContextMenuClient; +class DatabaseProvider; +class DiagnosticLoggingClient; +class DragClient; +class EditorClient; +class FrameLoaderClient; +class InspectorClient; +class LibWebRTCProvider; +class PaymentCoordinatorClient; +class PlugInClient; +class PluginInfoProvider; +class ProgressTrackerClient; +class SocketProvider; +class StorageNamespaceProvider; +class UserContentProvider; +class ValidationMessageClient; +class VisitedLinkStore; +class WebGLStateTracker; + +class PageConfiguration { + WTF_MAKE_NONCOPYABLE(PageConfiguration); WTF_MAKE_FAST_ALLOCATED; +public: + WEBCORE_EXPORT PageConfiguration(UniqueRef<EditorClient>&&, Ref<SocketProvider>&&, UniqueRef<LibWebRTCProvider>&&); + WEBCORE_EXPORT ~PageConfiguration(); + + AlternativeTextClient* alternativeTextClient { nullptr }; + ChromeClient* chromeClient { nullptr }; +#if ENABLE(CONTEXT_MENUS) + ContextMenuClient* contextMenuClient { nullptr }; +#endif + UniqueRef<EditorClient> editorClient; + Ref<SocketProvider> socketProvider; + DragClient* dragClient { nullptr }; + InspectorClient* inspectorClient { nullptr }; +#if ENABLE(APPLE_PAY) + PaymentCoordinatorClient* paymentCoordinatorClient { nullptr }; +#endif + + UniqueRef<LibWebRTCProvider> libWebRTCProvider; + + PlugInClient* plugInClient { nullptr }; + ProgressTrackerClient* progressTrackerClient { nullptr }; + RefPtr<BackForwardClient> backForwardClient; + std::unique_ptr<ValidationMessageClient> validationMessageClient; + FrameLoaderClient* loaderClientForMainFrame { nullptr }; + std::unique_ptr<DiagnosticLoggingClient> diagnosticLoggingClient; + std::unique_ptr<WebGLStateTracker> webGLStateTracker; + + RefPtr<ApplicationCacheStorage> applicationCacheStorage; + RefPtr<DatabaseProvider> databaseProvider; + RefPtr<PluginInfoProvider> pluginInfoProvider; + RefPtr<StorageNamespaceProvider> storageNamespaceProvider; + RefPtr<UserContentProvider> userContentProvider; + RefPtr<VisitedLinkStore> visitedLinkStore; +}; + +} diff --git a/Source/WebCore/page/PageConsole.cpp b/Source/WebCore/page/PageConsole.cpp deleted file mode 100644 index 2914cbb06..000000000 --- a/Source/WebCore/page/PageConsole.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * 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. - * 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 "PageConsole.h" - -#include "Chrome.h" -#include "ChromeClient.h" -#include "ConsoleAPITypes.h" -#include "ConsoleTypes.h" -#include "Document.h" -#include "Frame.h" -#include "InspectorConsoleInstrumentation.h" -#include "InspectorController.h" -#include "JSMainThreadExecState.h" -#include "Page.h" -#include "ScriptArguments.h" -#include "ScriptCallStack.h" -#include "ScriptCallStackFactory.h" -#include "ScriptableDocumentParser.h" -#include "Settings.h" -#include <bindings/ScriptValue.h> -#include <stdio.h> -#include <wtf/text/CString.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -namespace { - int muteCount = 0; -} - -PageConsole::PageConsole(Page& page) - : m_page(page) -{ -} - -PageConsole::~PageConsole() -{ -} - -void PageConsole::printSourceURLAndPosition(const String& sourceURL, unsigned lineNumber, unsigned columnNumber) -{ - if (!sourceURL.isEmpty()) { - if (lineNumber > 0 && columnNumber > 0) - printf("%s:%u:%u", sourceURL.utf8().data(), lineNumber, columnNumber); - else if (lineNumber > 0) - printf("%s:%u", sourceURL.utf8().data(), lineNumber); - else - printf("%s", sourceURL.utf8().data()); - } -} - -void PageConsole::printMessageSourceAndLevelPrefix(MessageSource source, MessageLevel level, bool showAsTrace) -{ - const char* sourceString; - switch (source) { - case XMLMessageSource: - sourceString = "XML"; - break; - case JSMessageSource: - sourceString = "JS"; - break; - case NetworkMessageSource: - sourceString = "NETWORK"; - break; - case ConsoleAPIMessageSource: - sourceString = "CONSOLE"; - break; - case StorageMessageSource: - sourceString = "STORAGE"; - break; - case AppCacheMessageSource: - sourceString = "APPCACHE"; - break; - case RenderingMessageSource: - sourceString = "RENDERING"; - break; - case CSSMessageSource: - sourceString = "CSS"; - break; - case SecurityMessageSource: - sourceString = "SECURITY"; - break; - case OtherMessageSource: - sourceString = "OTHER"; - break; - default: - ASSERT_NOT_REACHED(); - sourceString = "UNKNOWN"; - break; - } - - const char* levelString; - switch (level) { - case DebugMessageLevel: - levelString = "DEBUG"; - break; - case LogMessageLevel: - levelString = "LOG"; - break; - case WarningMessageLevel: - levelString = "WARN"; - break; - case ErrorMessageLevel: - levelString = "ERROR"; - break; - default: - ASSERT_NOT_REACHED(); - levelString = "UNKNOWN"; - break; - } - - if (showAsTrace) - levelString = "TRACE"; - - printf("%s %s:", sourceString, levelString); -} - -void PageConsole::addMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier, Document* document) -{ - String url; - if (document) - url = document->url().string(); - // FIXME: The below code attempts to determine line numbers for parser generated errors, but this is not the only reason why we can get here. - // For example, if we are still parsing and get a WebSocket network error, it will be erroneously attributed to a line where parsing was paused. - // Also, we should determine line numbers for script generated messages (e.g. calling getImageData on a canvas). - // We probably need to split this function into multiple ones, as appropriate for different call sites. Or maybe decide based on MessageSource. - // https://bugs.webkit.org/show_bug.cgi?id=125340 - unsigned line = 0; - unsigned column = 0; - if (document && document->parsing() && !document->isInDocumentWrite() && document->scriptableDocumentParser()) { - ScriptableDocumentParser* parser = document->scriptableDocumentParser(); - if (!parser->isWaitingForScripts() && !JSMainThreadExecState::currentState()) { - TextPosition position = parser->textPosition(); - line = position.m_line.oneBasedInt(); - column = position.m_column.oneBasedInt(); - } - } - addMessage(source, level, message, url, line, column, 0, 0, requestIdentifier); -} - -void PageConsole::addMessage(MessageSource source, MessageLevel level, const String& message, PassRefPtr<ScriptCallStack> callStack) -{ - addMessage(source, level, message, String(), 0, 0, callStack, 0); -} - -void PageConsole::addMessage(MessageSource source, MessageLevel level, const String& message, const String& url, unsigned lineNumber, unsigned columnNumber, PassRefPtr<ScriptCallStack> callStack, JSC::ExecState* state, unsigned long requestIdentifier) -{ - if (muteCount && source != ConsoleAPIMessageSource) - return; - - if (callStack) - InspectorInstrumentation::addMessageToConsole(&m_page, source, LogMessageType, level, message, callStack, requestIdentifier); - else - InspectorInstrumentation::addMessageToConsole(&m_page, source, LogMessageType, level, message, url, lineNumber, columnNumber, state, requestIdentifier); - - if (source == CSSMessageSource) - return; - - if (m_page.settings().privateBrowsingEnabled()) - return; - - m_page.chrome().client().addMessageToConsole(source, level, message, lineNumber, columnNumber, url); - - if (!m_page.settings().logsPageMessagesToSystemConsoleEnabled() && !shouldPrintExceptions()) - return; - - printSourceURLAndPosition(url, lineNumber, columnNumber); - printMessageSourceAndLevelPrefix(source, level); - - printf(" %s\n", message.utf8().data()); -} - -// static -void PageConsole::mute() -{ - muteCount++; -} - -// static -void PageConsole::unmute() -{ - ASSERT(muteCount > 0); - muteCount--; -} - -static bool printExceptions = false; - -bool PageConsole::shouldPrintExceptions() -{ - return printExceptions; -} - -void PageConsole::setShouldPrintExceptions(bool print) -{ - printExceptions = print; -} - -} // namespace WebCore diff --git a/Source/WebCore/page/PageConsoleClient.cpp b/Source/WebCore/page/PageConsoleClient.cpp new file mode 100644 index 000000000..2093e7356 --- /dev/null +++ b/Source/WebCore/page/PageConsoleClient.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2013, 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. + * 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. + * + * 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 "PageConsoleClient.h" + +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "Frame.h" +#include "InspectorController.h" +#include "InspectorInstrumentation.h" +#include "JSMainThreadExecState.h" +#include "MainFrame.h" +#include "Page.h" +#include "ScriptableDocumentParser.h" +#include "Settings.h" +#include <inspector/ConsoleMessage.h> +#include <inspector/ScriptArguments.h> +#include <inspector/ScriptCallStack.h> +#include <inspector/ScriptCallStackFactory.h> + +using namespace Inspector; + +namespace WebCore { + +PageConsoleClient::PageConsoleClient(Page& page) + : m_page(page) +{ +} + +PageConsoleClient::~PageConsoleClient() +{ +} + +static int muteCount = 0; +static bool printExceptions = false; + +bool PageConsoleClient::shouldPrintExceptions() +{ + return printExceptions; +} + +void PageConsoleClient::setShouldPrintExceptions(bool print) +{ + printExceptions = print; +} + +void PageConsoleClient::mute() +{ + muteCount++; +} + +void PageConsoleClient::unmute() +{ + ASSERT(muteCount > 0); + muteCount--; +} + +static void getParserLocationForConsoleMessage(Document* document, String& url, unsigned& line, unsigned& column) +{ + if (!document) + return; + + // We definitely cannot associate the message with a location being parsed if we are not even parsing. + if (!document->parsing()) + return; + + ScriptableDocumentParser* parser = document->scriptableDocumentParser(); + if (!parser) + return; + + // When the parser waits for scripts, any messages must be coming from some other source, and are not related to the location of the script element that made the parser wait. + if (!parser->shouldAssociateConsoleMessagesWithTextPosition()) + return; + + url = document->url().string(); + TextPosition position = parser->textPosition(); + line = position.m_line.oneBasedInt(); + column = position.m_column.oneBasedInt(); +} + +void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier, Document* document) +{ + String url; + unsigned line = 0; + unsigned column = 0; + getParserLocationForConsoleMessage(document, url, line, column); + + addMessage(source, level, message, url, line, column, 0, JSMainThreadExecState::currentState(), requestIdentifier); +} + +void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& message, Ref<ScriptCallStack>&& callStack) +{ + addMessage(source, level, message, String(), 0, 0, WTFMove(callStack), 0); +} + +void PageConsoleClient::addMessage(MessageSource source, MessageLevel level, const String& messageText, const String& suggestedURL, unsigned suggestedLineNumber, unsigned suggestedColumnNumber, RefPtr<ScriptCallStack>&& callStack, JSC::ExecState* state, unsigned long requestIdentifier) +{ + if (muteCount && source != MessageSource::ConsoleAPI) + return; + + std::unique_ptr<Inspector::ConsoleMessage> message; + + if (callStack) + message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, callStack.releaseNonNull(), requestIdentifier); + else + message = std::make_unique<Inspector::ConsoleMessage>(source, MessageType::Log, level, messageText, suggestedURL, suggestedLineNumber, suggestedColumnNumber, state, requestIdentifier); + + String url = message->url(); + unsigned lineNumber = message->line(); + unsigned columnNumber = message->column(); + + InspectorInstrumentation::addMessageToConsole(m_page, WTFMove(message)); + + if (source == MessageSource::CSS) + return; + + if (m_page.usesEphemeralSession()) + return; + + m_page.chrome().client().addMessageToConsole(source, level, messageText, lineNumber, columnNumber, url); + + if (!m_page.settings().logsPageMessagesToSystemConsoleEnabled() && !shouldPrintExceptions()) + return; + + ConsoleClient::printConsoleMessage(MessageSource::ConsoleAPI, MessageType::Log, level, messageText, url, lineNumber, columnNumber); +} + + +void PageConsoleClient::messageWithTypeAndLevel(MessageType type, MessageLevel level, JSC::ExecState* exec, Ref<Inspector::ScriptArguments>&& arguments) +{ + String messageText; + bool gotMessage = arguments->getFirstArgumentAsString(messageText); + + auto message = std::make_unique<Inspector::ConsoleMessage>(MessageSource::ConsoleAPI, type, level, messageText, arguments.copyRef(), exec); + + String url = message->url(); + unsigned lineNumber = message->line(); + unsigned columnNumber = message->column(); + + InspectorInstrumentation::addMessageToConsole(m_page, WTFMove(message)); + + if (m_page.usesEphemeralSession()) + return; + + if (gotMessage) + m_page.chrome().client().addMessageToConsole(MessageSource::ConsoleAPI, level, messageText, lineNumber, columnNumber, url); + + if (m_page.settings().logsPageMessagesToSystemConsoleEnabled() || PageConsoleClient::shouldPrintExceptions()) + ConsoleClient::printConsoleMessageWithArguments(MessageSource::ConsoleAPI, type, level, exec, WTFMove(arguments)); +} + +void PageConsoleClient::count(JSC::ExecState* exec, Ref<ScriptArguments>&& arguments) +{ + InspectorInstrumentation::consoleCount(m_page, exec, WTFMove(arguments)); +} + +void PageConsoleClient::profile(JSC::ExecState* exec, const String& title) +{ + // FIXME: <https://webkit.org/b/153499> Web Inspector: console.profile should use the new Sampling Profiler + InspectorInstrumentation::startProfiling(m_page, exec, title); +} + +void PageConsoleClient::profileEnd(JSC::ExecState* exec, const String& title) +{ + // FIXME: <https://webkit.org/b/153499> Web Inspector: console.profile should use the new Sampling Profiler + InspectorInstrumentation::stopProfiling(m_page, exec, title); +} + +void PageConsoleClient::takeHeapSnapshot(JSC::ExecState*, const String& title) +{ + InspectorInstrumentation::takeHeapSnapshot(m_page.mainFrame(), title); +} + +void PageConsoleClient::time(JSC::ExecState*, const String& title) +{ + InspectorInstrumentation::startConsoleTiming(m_page.mainFrame(), title); +} + +void PageConsoleClient::timeEnd(JSC::ExecState* exec, const String& title) +{ + InspectorInstrumentation::stopConsoleTiming(m_page.mainFrame(), title, createScriptCallStackForConsole(exec, 1)); +} + +void PageConsoleClient::timeStamp(JSC::ExecState*, Ref<ScriptArguments>&& arguments) +{ + InspectorInstrumentation::consoleTimeStamp(m_page.mainFrame(), WTFMove(arguments)); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/PageConsole.h b/Source/WebCore/page/PageConsoleClient.h index 9a70e54d9..5e44f282c 100644 --- a/Source/WebCore/page/PageConsole.h +++ b/Source/WebCore/page/PageConsoleClient.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,13 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageConsole_h -#define PageConsole_h +#pragma once -#include "ConsoleTypes.h" -#include "ScriptCallStack.h" +#include <inspector/ScriptCallStack.h> +#include <runtime/ConsoleClient.h> #include <wtf/Forward.h> -#include <wtf/PassOwnPtr.h> namespace JSC { class ExecState; @@ -43,28 +41,34 @@ namespace WebCore { class Document; class Page; -class PageConsole { +class WEBCORE_EXPORT PageConsoleClient final : public JSC::ConsoleClient { + WTF_MAKE_FAST_ALLOCATED; public: - PageConsole(Page&); - ~PageConsole(); + explicit PageConsoleClient(Page&); + virtual ~PageConsoleClient(); - static void printSourceURLAndPosition(const String& sourceURL, unsigned lineNumber, unsigned columnNumber = 0); - static void printMessageSourceAndLevelPrefix(MessageSource, MessageLevel, bool showAsTrace = false); - - void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, PassRefPtr<ScriptCallStack> = 0, JSC::ExecState* = 0, unsigned long requestIdentifier = 0); - void addMessage(MessageSource, MessageLevel, const String& message, PassRefPtr<ScriptCallStack>); - void addMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0, Document* = 0); + static bool shouldPrintExceptions(); + static void setShouldPrintExceptions(bool); static void mute(); static void unmute(); - static bool shouldPrintExceptions(); - static void setShouldPrintExceptions(bool); + void addMessage(MessageSource, MessageLevel, const String& message, const String& sourceURL, unsigned lineNumber, unsigned columnNumber, RefPtr<Inspector::ScriptCallStack>&& = nullptr, JSC::ExecState* = nullptr, unsigned long requestIdentifier = 0); + void addMessage(MessageSource, MessageLevel, const String& message, Ref<Inspector::ScriptCallStack>&&); + void addMessage(MessageSource, MessageLevel, const String& message, unsigned long requestIdentifier = 0, Document* = nullptr); + +protected: + void messageWithTypeAndLevel(MessageType, MessageLevel, JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override; + void count(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override; + void profile(JSC::ExecState*, const String& title) override; + void profileEnd(JSC::ExecState*, const String& title) override; + void takeHeapSnapshot(JSC::ExecState*, const String& title) override; + void time(JSC::ExecState*, const String& title) override; + void timeEnd(JSC::ExecState*, const String& title) override; + void timeStamp(JSC::ExecState*, Ref<Inspector::ScriptArguments>&&) override; private: Page& m_page; }; } // namespace WebCore - -#endif // PageConsole_h diff --git a/Source/WebCore/page/PageDebuggable.cpp b/Source/WebCore/page/PageDebuggable.cpp index 067df5a1c..9c99f2279 100644 --- a/Source/WebCore/page/PageDebuggable.cpp +++ b/Source/WebCore/page/PageDebuggable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 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 @@ -29,11 +29,10 @@ #if ENABLE(REMOTE_INSPECTOR) #include "Document.h" -#include "InspectorClient.h" #include "InspectorController.h" -#include "InspectorForwarding.h" #include "MainFrame.h" #include "Page.h" +#include "Settings.h" #include <inspector/InspectorAgentBase.h> using namespace Inspector; @@ -42,11 +41,15 @@ namespace WebCore { PageDebuggable::PageDebuggable(Page& page) : m_page(page) + , m_forcedDeveloperExtrasEnabled(false) { } String PageDebuggable::name() const { + if (!m_nameOverride.isNull()) + return m_nameOverride; + if (!m_page.mainFrame().document()) return String(); @@ -67,29 +70,30 @@ bool PageDebuggable::hasLocalDebugger() const return m_page.inspectorController().hasLocalFrontend(); } -pid_t PageDebuggable::parentProcessIdentifier() const +void PageDebuggable::connect(Inspector::FrontendChannel* channel, bool isAutomaticConnection) { - if (InspectorClient* inspectorClient = m_page.inspectorController().inspectorClient()) - return inspectorClient->parentProcessIdentifier(); - - return 0; -} + if (!m_page.settings().developerExtrasEnabled()) { + m_forcedDeveloperExtrasEnabled = true; + m_page.settings().setDeveloperExtrasEnabled(true); + } else + m_forcedDeveloperExtrasEnabled = false; -void PageDebuggable::connect(Inspector::InspectorFrontendChannel* channel) -{ InspectorController& inspectorController = m_page.inspectorController(); - inspectorController.setHasRemoteFrontend(true); - inspectorController.connectFrontend(reinterpret_cast<WebCore::InspectorFrontendChannel*>(channel)); + inspectorController.connectFrontend(channel, isAutomaticConnection); } -void PageDebuggable::disconnect() +void PageDebuggable::disconnect(Inspector::FrontendChannel* channel) { InspectorController& inspectorController = m_page.inspectorController(); - inspectorController.disconnectFrontend(InspectorDisconnectReason::InspectorDestroyed); - inspectorController.setHasRemoteFrontend(false); + inspectorController.disconnectFrontend(channel); + + if (m_forcedDeveloperExtrasEnabled) { + m_forcedDeveloperExtrasEnabled = false; + m_page.settings().setDeveloperExtrasEnabled(false); + } } -void PageDebuggable::dispatchMessageFromRemoteFrontend(const String& message) +void PageDebuggable::dispatchMessageFromRemote(const String& message) { m_page.inspectorController().dispatchMessageFromFrontend(message); } @@ -99,6 +103,12 @@ void PageDebuggable::setIndicating(bool indicating) m_page.inspectorController().setIndicating(indicating); } +void PageDebuggable::setNameOverride(const String& name) +{ + m_nameOverride = name; + update(); +} + } // namespace WebCore #endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebCore/page/PageDebuggable.h b/Source/WebCore/page/PageDebuggable.h index bc1ff6458..1723ad072 100644 --- a/Source/WebCore/page/PageDebuggable.h +++ b/Source/WebCore/page/PageDebuggable.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 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 @@ -23,42 +23,46 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageDebuggable_h -#define PageDebuggable_h +#pragma once #if ENABLE(REMOTE_INSPECTOR) -#include <JavaScriptCore/RemoteInspectorDebuggable.h> +#include <JavaScriptCore/RemoteInspectionTarget.h> #include <wtf/Noncopyable.h> namespace WebCore { class Page; -class PageDebuggable final : public Inspector::RemoteInspectorDebuggable { +class PageDebuggable final : public Inspector::RemoteInspectionTarget { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(PageDebuggable); public: PageDebuggable(Page&); ~PageDebuggable() { } - virtual Inspector::RemoteInspectorDebuggable::DebuggableType type() const override { return Inspector::RemoteInspectorDebuggable::Web; } + Inspector::RemoteControllableTarget::Type type() const override { return Inspector::RemoteControllableTarget::Type::Web; } - virtual String name() const override; - virtual String url() const override; - virtual bool hasLocalDebugger() const override; - virtual pid_t parentProcessIdentifier() const override; + String name() const override; + String url() const override; + bool hasLocalDebugger() const override; - virtual void connect(Inspector::InspectorFrontendChannel*) override; - virtual void disconnect() override; - virtual void dispatchMessageFromRemoteFrontend(const String& message) override; - virtual void setIndicating(bool) override; + void connect(Inspector::FrontendChannel*, bool isAutomaticConnection = false) override; + void disconnect(Inspector::FrontendChannel*) override; + void dispatchMessageFromRemote(const String& message) override; + void setIndicating(bool) override; + + String nameOverride() const { return m_nameOverride; } + void setNameOverride(const String&); private: Page& m_page; + String m_nameOverride; + bool m_forcedDeveloperExtrasEnabled; }; } // namespace WebCore -#endif // ENABLE(REMOTE_INSPECTOR) +SPECIALIZE_TYPE_TRAITS_CONTROLLABLE_TARGET(WebCore::PageDebuggable, Web); -#endif // !defined(PageDebuggable_h) +#endif // ENABLE(REMOTE_INSPECTOR) diff --git a/Source/WebCore/page/PageGroup.cpp b/Source/WebCore/page/PageGroup.cpp index 8a1cd2081..5aa435141 100644 --- a/Source/WebCore/page/PageGroup.cpp +++ b/Source/WebCore/page/PageGroup.cpp @@ -26,24 +26,18 @@ #include "config.h" #include "PageGroup.h" -#include "Chrome.h" -#include "ChromeClient.h" #include "DOMWrapperWorld.h" #include "Document.h" -#include "DocumentStyleSheetCollection.h" -#include "GroupSettings.h" #include "MainFrame.h" #include "Page.h" #include "PageCache.h" -#include "SecurityOrigin.h" -#include "Settings.h" #include "StorageNamespace.h" -#include "UserContentController.h" -#include "VisitedLinkProvider.h" +#include <heap/HeapInlines.h> +#include <runtime/StructureInlines.h> #include <wtf/StdLibExtras.h> #if ENABLE(VIDEO_TRACK) -#if (PLATFORM(MAC) && !PLATFORM(IOS)) || HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) +#if PLATFORM(MAC) || HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) #include "CaptionUserPreferencesMediaAF.h" #else #include "CaptionUserPreferences.h" @@ -60,35 +54,24 @@ static unsigned getUniqueIdentifier() // -------- -static bool shouldTrackVisitedLinks = false; - PageGroup::PageGroup(const String& name) : m_name(name) - , m_visitedLinkProvider(VisitedLinkProvider::create()) - , m_visitedLinksPopulated(false) , m_identifier(getUniqueIdentifier()) - , m_userContentController(UserContentController::create()) - , m_groupSettings(std::make_unique<GroupSettings>()) { } PageGroup::PageGroup(Page& page) - : m_visitedLinkProvider(VisitedLinkProvider::create()) - , m_visitedLinksPopulated(false) - , m_identifier(getUniqueIdentifier()) - , m_userContentController(UserContentController::create()) - , m_groupSettings(std::make_unique<GroupSettings>()) + : m_identifier(getUniqueIdentifier()) { addPage(page); } PageGroup::~PageGroup() { - removeAllUserContent(); } typedef HashMap<String, PageGroup*> PageGroupMap; -static PageGroupMap* pageGroups = 0; +static PageGroupMap* pageGroups = nullptr; PageGroup* PageGroup::pageGroup(const String& groupName) { @@ -108,227 +91,37 @@ PageGroup* PageGroup::pageGroup(const String& groupName) return result.iterator->value; } -void PageGroup::closeLocalStorage() -{ - if (!pageGroups) - return; - - for (auto it = pageGroups->begin(), end = pageGroups->end(); it != end; ++it) { - if (it->value->hasLocalStorage()) - it->value->localStorage()->close(); - } -} - -void PageGroup::clearLocalStorageForAllOrigins() -{ - if (!pageGroups) - return; - - for (auto it = pageGroups->begin(), end = pageGroups->end(); it != end; ++it) { - if (it->value->hasLocalStorage()) - it->value->localStorage()->clearAllOriginsForDeletion(); - } -} - -void PageGroup::clearLocalStorageForOrigin(SecurityOrigin* origin) -{ - if (!pageGroups) - return; - - for (auto it = pageGroups->begin(), end = pageGroups->end(); it != end; ++it) { - if (it->value->hasLocalStorage()) - it->value->localStorage()->clearOriginForDeletion(origin); - } -} - -void PageGroup::closeIdleLocalStorageDatabases() -{ - if (!pageGroups) - return; - - for (auto it = pageGroups->begin(), end = pageGroups->end(); it != end; ++it) { - if (it->value->hasLocalStorage()) - it->value->localStorage()->closeIdleLocalStorageDatabases(); - } -} - -void PageGroup::syncLocalStorage() -{ - if (!pageGroups) - return; - - for (auto it = pageGroups->begin(), end = pageGroups->end(); it != end; ++it) { - if (it->value->hasLocalStorage()) - it->value->localStorage()->sync(); - } -} - void PageGroup::addPage(Page& page) { ASSERT(!m_pages.contains(&page)); m_pages.add(&page); - - page.setUserContentController(m_userContentController.get()); } void PageGroup::removePage(Page& page) { ASSERT(m_pages.contains(&page)); m_pages.remove(&page); - - page.setUserContentController(nullptr); -} - -bool PageGroup::isLinkVisited(LinkHash visitedLinkHash) -{ - if (!m_visitedLinksPopulated) { - m_visitedLinksPopulated = true; - ASSERT(!m_pages.isEmpty()); - (*m_pages.begin())->chrome().client().populateVisitedLinks(); - } - return m_visitedLinkHashes.contains(visitedLinkHash); -} - -void PageGroup::addVisitedLinkHash(LinkHash hash) -{ - if (shouldTrackVisitedLinks) - addVisitedLink(hash); -} - -inline void PageGroup::addVisitedLink(LinkHash hash) -{ - ASSERT(shouldTrackVisitedLinks); - if (!m_visitedLinkHashes.add(hash).isNewEntry) - return; - Page::visitedStateChanged(this, hash); - pageCache()->markPagesForVistedLinkStyleRecalc(); -} - -void PageGroup::addVisitedLink(const URL& url) -{ - if (!shouldTrackVisitedLinks) - return; - ASSERT(!url.isEmpty()); - addVisitedLink(visitedLinkHash(url.string())); -} - -void PageGroup::addVisitedLink(const UChar* characters, size_t length) -{ - if (!shouldTrackVisitedLinks) - return; - addVisitedLink(visitedLinkHash(characters, length)); -} - -void PageGroup::removeVisitedLink(const URL& url) -{ - LinkHash hash = visitedLinkHash(url.string()); - ASSERT(m_visitedLinkHashes.contains(hash)); - m_visitedLinkHashes.remove(hash); - - Page::allVisitedStateChanged(this); - pageCache()->markPagesForVistedLinkStyleRecalc(); -} - -void PageGroup::removeVisitedLinks() -{ - m_visitedLinksPopulated = false; - if (m_visitedLinkHashes.isEmpty()) - return; - m_visitedLinkHashes.clear(); - Page::allVisitedStateChanged(this); - pageCache()->markPagesForVistedLinkStyleRecalc(); -} - -void PageGroup::removeAllVisitedLinks() -{ - Page::removeAllVisitedLinks(); - pageCache()->markPagesForVistedLinkStyleRecalc(); -} - -void PageGroup::setShouldTrackVisitedLinks(bool shouldTrack) -{ - if (shouldTrackVisitedLinks == shouldTrack) - return; - shouldTrackVisitedLinks = shouldTrack; - if (!shouldTrackVisitedLinks) - removeAllVisitedLinks(); -} - -StorageNamespace* PageGroup::localStorage() -{ - if (!m_localStorage) - m_localStorage = StorageNamespace::localStorageNamespace(this); - - return m_localStorage.get(); -} - -StorageNamespace* PageGroup::transientLocalStorage(SecurityOrigin* topOrigin) -{ - auto result = m_transientLocalStorageMap.add(topOrigin, nullptr); - - if (result.isNewEntry) - result.iterator->value = StorageNamespace::transientLocalStorageNamespace(this, topOrigin); - - return result.iterator->value.get(); -} - -void PageGroup::addUserScriptToWorld(DOMWrapperWorld& world, const String& source, const URL& url, const Vector<String>& whitelist, const Vector<String>& blacklist, UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames) -{ - auto userScript = std::make_unique<UserScript>(source, url, whitelist, blacklist, injectionTime, injectedFrames); - m_userContentController->addUserScript(world, std::move(userScript)); -} - -void PageGroup::addUserStyleSheetToWorld(DOMWrapperWorld& world, const String& source, const URL& url, const Vector<String>& whitelist, const Vector<String>& blacklist, UserContentInjectedFrames injectedFrames, UserStyleLevel level, UserStyleInjectionTime injectionTime) -{ - auto userStyleSheet = std::make_unique<UserStyleSheet>(source, url, whitelist, blacklist, injectedFrames, level); - m_userContentController->addUserStyleSheet(world, std::move(userStyleSheet), injectionTime); - -} - -void PageGroup::removeUserScriptFromWorld(DOMWrapperWorld& world, const URL& url) -{ - m_userContentController->removeUserScript(world, url); -} - -void PageGroup::removeUserStyleSheetFromWorld(DOMWrapperWorld& world, const URL& url) -{ - m_userContentController->removeUserStyleSheet(world, url); -} - -void PageGroup::removeUserScriptsFromWorld(DOMWrapperWorld& world) -{ - m_userContentController->removeUserScripts(world); -} - -void PageGroup::removeUserStyleSheetsFromWorld(DOMWrapperWorld& world) -{ - m_userContentController->removeUserStyleSheets(world); -} - -void PageGroup::removeAllUserContent() -{ - m_userContentController->removeAllUserContent(); } #if ENABLE(VIDEO_TRACK) void PageGroup::captionPreferencesChanged() { - for (auto it = m_pages.begin(), end = m_pages.end(); it != end; ++it) - (*it)->captionPreferencesChanged(); - pageCache()->markPagesForCaptionPreferencesChanged(); + for (auto& page : m_pages) + page->captionPreferencesChanged(); + PageCache::singleton().markPagesForCaptionPreferencesChanged(); } -CaptionUserPreferences* PageGroup::captionPreferences() +CaptionUserPreferences& PageGroup::captionPreferences() { if (!m_captionPreferences) { -#if (PLATFORM(MAC) && !PLATFORM(IOS)) || HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) +#if PLATFORM(MAC) || HAVE(MEDIA_ACCESSIBILITY_FRAMEWORK) m_captionPreferences = std::make_unique<CaptionUserPreferencesMediaAF>(*this); #else m_captionPreferences = std::make_unique<CaptionUserPreferences>(*this); #endif } - return m_captionPreferences.get(); + return *m_captionPreferences.get(); } #endif diff --git a/Source/WebCore/page/PageGroup.h b/Source/WebCore/page/PageGroup.h index 01f2e33e8..3d9f274e1 100644 --- a/Source/WebCore/page/PageGroup.h +++ b/Source/WebCore/page/PageGroup.h @@ -23,115 +23,51 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageGroup_h -#define PageGroup_h +#pragma once -#include "LinkHash.h" -#include "SecurityOriginHash.h" #include "Supplementable.h" -#include "UserScript.h" -#include "UserStyleSheet.h" #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> +#include <wtf/text/WTFString.h> namespace WebCore { - class URL; - class GroupSettings; - class IDBFactoryBackendInterface; - class Page; - class SecurityOrigin; - class StorageNamespace; - class VisitedLinkProvider; - class UserContentController; - +class Page; #if ENABLE(VIDEO_TRACK) - class CaptionPreferencesChangedListener; - class CaptionUserPreferences; +class CaptionUserPreferences; #endif - class PageGroup : public Supplementable<PageGroup> { - WTF_MAKE_NONCOPYABLE(PageGroup); WTF_MAKE_FAST_ALLOCATED; - public: - explicit PageGroup(const String& name); - explicit PageGroup(Page&); - ~PageGroup(); - - static PageGroup* pageGroup(const String& groupName); - - static void closeLocalStorage(); - - static void clearLocalStorageForAllOrigins(); - static void clearLocalStorageForOrigin(SecurityOrigin*); - static void closeIdleLocalStorageDatabases(); - // DumpRenderTree helper that triggers a StorageArea sync. - static void syncLocalStorage(); - - const HashSet<Page*>& pages() const { return m_pages; } - - void addPage(Page&); - void removePage(Page&); - - VisitedLinkProvider& visitedLinkProvider() { return *m_visitedLinkProvider; } - - bool isLinkVisited(LinkHash); +class PageGroup { + WTF_MAKE_NONCOPYABLE(PageGroup); WTF_MAKE_FAST_ALLOCATED; +public: + WEBCORE_EXPORT explicit PageGroup(const String& name); + explicit PageGroup(Page&); + ~PageGroup(); - void addVisitedLink(const URL&); - void addVisitedLink(const UChar*, size_t); - void addVisitedLinkHash(LinkHash); - void removeVisitedLink(const URL&); - void removeVisitedLinks(); + WEBCORE_EXPORT static PageGroup* pageGroup(const String& groupName); - static void setShouldTrackVisitedLinks(bool); - static void removeAllVisitedLinks(); + const HashSet<Page*>& pages() const { return m_pages; } - const String& name() { return m_name; } - unsigned identifier() { return m_identifier; } + void addPage(Page&); + void removePage(Page&); - StorageNamespace* localStorage(); - bool hasLocalStorage() { return m_localStorage; } - - StorageNamespace* transientLocalStorage(SecurityOrigin* topOrigin); - - void addUserScriptToWorld(DOMWrapperWorld&, const String& source, const URL&, const Vector<String>& whitelist, const Vector<String>& blacklist, UserScriptInjectionTime, UserContentInjectedFrames); - void addUserStyleSheetToWorld(DOMWrapperWorld&, const String& source, const URL&, const Vector<String>& whitelist, const Vector<String>& blacklist, UserContentInjectedFrames, UserStyleLevel = UserStyleUserLevel, UserStyleInjectionTime = InjectInExistingDocuments); - void removeUserStyleSheetFromWorld(DOMWrapperWorld&, const URL&); - void removeUserScriptFromWorld(DOMWrapperWorld&, const URL&); - void removeUserScriptsFromWorld(DOMWrapperWorld&); - void removeUserStyleSheetsFromWorld(DOMWrapperWorld&); - void removeAllUserContent(); - - GroupSettings& groupSettings() const { return *m_groupSettings; } + const String& name() { return m_name; } + unsigned identifier() { return m_identifier; } #if ENABLE(VIDEO_TRACK) - void captionPreferencesChanged(); - CaptionUserPreferences* captionPreferences(); + WEBCORE_EXPORT void captionPreferencesChanged(); + WEBCORE_EXPORT CaptionUserPreferences& captionPreferences(); #endif - private: - void addVisitedLink(LinkHash); - - String m_name; - HashSet<Page*> m_pages; - - RefPtr<VisitedLinkProvider> m_visitedLinkProvider; - - HashSet<LinkHash, LinkHashHash> m_visitedLinkHashes; - bool m_visitedLinksPopulated; - - unsigned m_identifier; - RefPtr<StorageNamespace> m_localStorage; - HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageNamespace>> m_transientLocalStorageMap; - - RefPtr<UserContentController> m_userContentController; +private: + String m_name; + HashSet<Page*> m_pages; - const std::unique_ptr<GroupSettings> m_groupSettings; + unsigned m_identifier; #if ENABLE(VIDEO_TRACK) - std::unique_ptr<CaptionUserPreferences> m_captionPreferences; + std::unique_ptr<CaptionUserPreferences> m_captionPreferences; #endif - }; +}; } // namespace WebCore - -#endif // PageGroup_h diff --git a/Source/WebCore/page/PageGroupLoadDeferrer.cpp b/Source/WebCore/page/PageGroupLoadDeferrer.cpp index b5f1ca341..ed69c876d 100644 --- a/Source/WebCore/page/PageGroupLoadDeferrer.cpp +++ b/Source/WebCore/page/PageGroupLoadDeferrer.cpp @@ -27,17 +27,12 @@ #include "Page.h" #include "PageGroup.h" #include "ScriptRunner.h" -#include <wtf/HashSet.h> namespace WebCore { PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page& page, bool deferSelf) { - const HashSet<Page*>& pages = page.group().pages(); - - HashSet<Page*>::const_iterator end = pages.end(); - for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { - Page* otherPage = *it; + for (auto& otherPage : page.group().pages()) { if ((deferSelf || otherPage != &page)) { if (!otherPage->defersLoading()) { m_deferredFrames.append(&otherPage->mainFrame()); @@ -50,16 +45,16 @@ PageGroupLoadDeferrer::PageGroupLoadDeferrer(Page& page, bool deferSelf) } } - size_t count = m_deferredFrames.size(); - for (size_t i = 0; i < count; ++i) - if (Page* page = m_deferredFrames[i]->page()) + for (auto& deferredFrame : m_deferredFrames) { + if (Page* page = deferredFrame->page()) page->setDefersLoading(true); + } } PageGroupLoadDeferrer::~PageGroupLoadDeferrer() { - for (size_t i = 0; i < m_deferredFrames.size(); ++i) { - if (Page* page = m_deferredFrames[i]->page()) { + for (auto& deferredFrame : m_deferredFrames) { + if (Page* page = deferredFrame->page()) { page->setDefersLoading(false); for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) diff --git a/Source/WebCore/page/PageGroupLoadDeferrer.h b/Source/WebCore/page/PageGroupLoadDeferrer.h index b632b5dce..ad3e79130 100644 --- a/Source/WebCore/page/PageGroupLoadDeferrer.h +++ b/Source/WebCore/page/PageGroupLoadDeferrer.h @@ -17,8 +17,7 @@ * Boston, MA 02110-1301, USA. */ -#ifndef PageGroupLoadDeferrer_h -#define PageGroupLoadDeferrer_h +#pragma once #include <wtf/RefPtr.h> #include <wtf/Vector.h> @@ -37,6 +36,5 @@ namespace WebCore { private: Vector<RefPtr<Frame>, 16> m_deferredFrames; }; -} -#endif // PageGroupLoadDeferrer_h +} // namespace WebCore diff --git a/Source/WebCore/page/PageOverlay.cpp b/Source/WebCore/page/PageOverlay.cpp new file mode 100644 index 000000000..2ca86f982 --- /dev/null +++ b/Source/WebCore/page/PageOverlay.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2010 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 "PageOverlay.h" + +#include "FrameView.h" +#include "GraphicsContext.h" +#include "MainFrame.h" +#include "Page.h" +#include "PageOverlayController.h" +#include "PlatformMouseEvent.h" +#include "ScrollbarTheme.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +static const double fadeAnimationDuration = 0.2; +static const double fadeAnimationFrameRate = 30; + +static PageOverlay::PageOverlayID generatePageOverlayID() +{ + static PageOverlay::PageOverlayID pageOverlayID; + return ++pageOverlayID; +} + +Ref<PageOverlay> PageOverlay::create(Client& client, OverlayType overlayType) +{ + return adoptRef(*new PageOverlay(client, overlayType)); +} + +PageOverlay::PageOverlay(Client& client, OverlayType overlayType) + : m_client(client) + , m_fadeAnimationTimer(*this, &PageOverlay::fadeAnimationTimerFired) + , m_fadeAnimationDuration(fadeAnimationDuration) + , m_needsSynchronousScrolling(overlayType == OverlayType::View) + , m_overlayType(overlayType) + , m_pageOverlayID(generatePageOverlayID()) +{ +} + +PageOverlay::~PageOverlay() +{ +} + +PageOverlayController* PageOverlay::controller() const +{ + if (!m_page) + return nullptr; + return &m_page->mainFrame().pageOverlayController(); +} + +IntRect PageOverlay::bounds() const +{ + if (!m_overrideFrame.isEmpty()) + return { { }, m_overrideFrame.size() }; + + FrameView* frameView = m_page->mainFrame().view(); + + if (!frameView) + return IntRect(); + + switch (m_overlayType) { + case OverlayType::View: { + int width = frameView->width(); + int height = frameView->height(); + + if (!ScrollbarTheme::theme().usesOverlayScrollbars()) { + if (frameView->verticalScrollbar()) + width -= frameView->verticalScrollbar()->width(); + if (frameView->horizontalScrollbar()) + height -= frameView->horizontalScrollbar()->height(); + } + return IntRect(0, 0, width, height); + } + case OverlayType::Document: + return IntRect(IntPoint(), frameView->contentsSize()); + } + + ASSERT_NOT_REACHED(); + return IntRect(IntPoint(), frameView->contentsSize()); +} + +IntRect PageOverlay::frame() const +{ + if (!m_overrideFrame.isEmpty()) + return m_overrideFrame; + + return bounds(); +} + +void PageOverlay::setFrame(IntRect frame) +{ + if (m_overrideFrame == frame) + return; + + m_overrideFrame = frame; + + if (auto pageOverlayController = controller()) + pageOverlayController->didChangeOverlayFrame(*this); +} + +IntSize PageOverlay::viewToOverlayOffset() const +{ + switch (m_overlayType) { + case OverlayType::View: + return IntSize(); + + case OverlayType::Document: { + FrameView* frameView = m_page->mainFrame().view(); + return frameView ? toIntSize(frameView->viewToContents(IntPoint())) : IntSize(); + } + } + return IntSize(); +} + +void PageOverlay::setBackgroundColor(const Color& backgroundColor) +{ + if (m_backgroundColor == backgroundColor) + return; + + m_backgroundColor = backgroundColor; + + if (auto pageOverlayController = controller()) + pageOverlayController->didChangeOverlayBackgroundColor(*this); +} + +void PageOverlay::setPage(Page* page) +{ + m_client.willMoveToPage(*this, page); + m_page = page; + m_client.didMoveToPage(*this, page); + + m_fadeAnimationTimer.stop(); +} + +void PageOverlay::setNeedsDisplay(const IntRect& dirtyRect) +{ + if (auto pageOverlayController = controller()) { + if (m_fadeAnimationType != FadeAnimationType::NoAnimation) + pageOverlayController->setPageOverlayOpacity(*this, m_fractionFadedIn); + pageOverlayController->setPageOverlayNeedsDisplay(*this, dirtyRect); + } +} + +void PageOverlay::setNeedsDisplay() +{ + setNeedsDisplay(bounds()); +} + +void PageOverlay::drawRect(GraphicsContext& graphicsContext, const IntRect& dirtyRect) +{ + // If the dirty rect is outside the bounds, ignore it. + IntRect paintRect = intersection(dirtyRect, bounds()); + if (paintRect.isEmpty()) + return; + + GraphicsContextStateSaver stateSaver(graphicsContext); + + if (m_overlayType == PageOverlay::OverlayType::Document) { + if (FrameView* frameView = m_page->mainFrame().view()) { + auto offset = frameView->scrollOrigin(); + graphicsContext.translate(toFloatSize(offset)); + paintRect.moveBy(-offset); + } + } + + m_client.drawRect(*this, graphicsContext, paintRect); +} + +bool PageOverlay::mouseEvent(const PlatformMouseEvent& mouseEvent) +{ + IntPoint mousePositionInOverlayCoordinates(mouseEvent.position()); + + if (m_overlayType == PageOverlay::OverlayType::Document) + mousePositionInOverlayCoordinates = m_page->mainFrame().view()->windowToContents(mousePositionInOverlayCoordinates); + mousePositionInOverlayCoordinates.moveBy(-frame().location()); + + // Ignore events outside the bounds. + if (m_shouldIgnoreMouseEventsOutsideBounds && !bounds().contains(mousePositionInOverlayCoordinates)) + return false; + + return m_client.mouseEvent(*this, mouseEvent); +} + +void PageOverlay::didScrollFrame(Frame& frame) +{ + m_client.didScrollFrame(*this, frame); +} + +bool PageOverlay::copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint parameter, String& value) +{ + return m_client.copyAccessibilityAttributeStringValueForPoint(*this, attribute, parameter, value); +} + +bool PageOverlay::copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint parameter, bool& value) +{ + return m_client.copyAccessibilityAttributeBoolValueForPoint(*this, attribute, parameter, value); +} + +Vector<String> PageOverlay::copyAccessibilityAttributeNames(bool parameterizedNames) +{ + return m_client.copyAccessibilityAttributeNames(*this, parameterizedNames); +} + +void PageOverlay::startFadeInAnimation() +{ + m_fractionFadedIn = 0; + m_fadeAnimationType = FadeInAnimation; + + startFadeAnimation(); +} + +void PageOverlay::startFadeOutAnimation() +{ + m_fractionFadedIn = 1; + m_fadeAnimationType = FadeOutAnimation; + + startFadeAnimation(); +} + +void PageOverlay::stopFadeOutAnimation() +{ + m_fractionFadedIn = 1.0; + m_fadeAnimationTimer.stop(); +} + +void PageOverlay::startFadeAnimation() +{ + m_fadeAnimationStartTime = currentTime(); + m_fadeAnimationTimer.startRepeating(1 / fadeAnimationFrameRate); +} + +void PageOverlay::fadeAnimationTimerFired() +{ + float animationProgress = (currentTime() - m_fadeAnimationStartTime) / m_fadeAnimationDuration; + + if (animationProgress >= 1.0) + animationProgress = 1.0; + + double sine = sin(piOverTwoFloat * animationProgress); + float fadeAnimationValue = sine * sine; + + m_fractionFadedIn = (m_fadeAnimationType == FadeInAnimation) ? fadeAnimationValue : 1 - fadeAnimationValue; + controller()->setPageOverlayOpacity(*this, m_fractionFadedIn); + + if (animationProgress == 1.0) { + m_fadeAnimationTimer.stop(); + + bool wasFadingOut = m_fadeAnimationType == FadeOutAnimation; + m_fadeAnimationType = NoAnimation; + + // If this was a fade out, uninstall the page overlay. + if (wasFadingOut) + controller()->uninstallPageOverlay(*this, PageOverlay::FadeMode::DoNotFade); + } +} + +void PageOverlay::clear() +{ + if (auto pageOverlayController = controller()) + pageOverlayController->clearPageOverlay(*this); +} + +GraphicsLayer& PageOverlay::layer() +{ + return controller()->layerForOverlay(*this); +} + +} // namespace WebKit diff --git a/Source/WebCore/page/PageOverlay.h b/Source/WebCore/page/PageOverlay.h new file mode 100644 index 000000000..e16d6696f --- /dev/null +++ b/Source/WebCore/page/PageOverlay.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010 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 "Color.h" +#include "FloatPoint.h" +#include "IntRect.h" +#include "Timer.h" +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Frame; +class GraphicsContext; +class GraphicsLayer; +class Page; +class PageOverlayController; +class PlatformMouseEvent; + +class PageOverlay final : public RefCounted<PageOverlay> { + WTF_MAKE_NONCOPYABLE(PageOverlay); + WTF_MAKE_FAST_ALLOCATED; +public: + class Client { + protected: + virtual ~Client() { } + + public: + virtual void willMoveToPage(PageOverlay&, Page*) = 0; + virtual void didMoveToPage(PageOverlay&, Page*) = 0; + virtual void drawRect(PageOverlay&, GraphicsContext&, const IntRect& dirtyRect) = 0; + virtual bool mouseEvent(PageOverlay&, const PlatformMouseEvent&) = 0; + virtual void didScrollFrame(PageOverlay&, Frame&) { } + + virtual bool copyAccessibilityAttributeStringValueForPoint(PageOverlay&, String /* attribute */, FloatPoint, String&) { return false; } + virtual bool copyAccessibilityAttributeBoolValueForPoint(PageOverlay&, String /* attribute */, FloatPoint, bool&) { return false; } + virtual Vector<String> copyAccessibilityAttributeNames(PageOverlay&, bool /* parameterizedNames */) { return { }; } + }; + + enum class OverlayType { + View, // Fixed to the view size; does not scale or scroll with the document, repaints on scroll. + Document, // Scales and scrolls with the document. + }; + + WEBCORE_EXPORT static Ref<PageOverlay> create(Client&, OverlayType = OverlayType::View); + WEBCORE_EXPORT virtual ~PageOverlay(); + + WEBCORE_EXPORT PageOverlayController* controller() const; + + typedef uint64_t PageOverlayID; + virtual PageOverlayID pageOverlayID() const { return m_pageOverlayID; } + + void setPage(Page*); + Page* page() const { return m_page; } + WEBCORE_EXPORT void setNeedsDisplay(const IntRect& dirtyRect); + WEBCORE_EXPORT void setNeedsDisplay(); + + void drawRect(GraphicsContext&, const IntRect& dirtyRect); + bool mouseEvent(const PlatformMouseEvent&); + void didScrollFrame(Frame&); + + bool copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint parameter, String& value); + bool copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint parameter, bool& value); + Vector<String> copyAccessibilityAttributeNames(bool parameterizedNames); + + void startFadeInAnimation(); + void startFadeOutAnimation(); + WEBCORE_EXPORT void stopFadeOutAnimation(); + + WEBCORE_EXPORT void clear(); + + Client& client() const { return m_client; } + + enum class FadeMode { DoNotFade, Fade }; + + OverlayType overlayType() { return m_overlayType; } + + WEBCORE_EXPORT IntRect bounds() const; + WEBCORE_EXPORT IntRect frame() const; + WEBCORE_EXPORT void setFrame(IntRect); + + WEBCORE_EXPORT IntSize viewToOverlayOffset() const; + + const Color& backgroundColor() const { return m_backgroundColor; } + void setBackgroundColor(const Color&); + + void setShouldIgnoreMouseEventsOutsideBounds(bool flag) { m_shouldIgnoreMouseEventsOutsideBounds = flag; } + + // FIXME: PageOverlay should own its layer, instead of PageOverlayController. + WEBCORE_EXPORT GraphicsLayer& layer(); + + bool needsSynchronousScrolling() const { return m_needsSynchronousScrolling; } + void setNeedsSynchronousScrolling(bool needsSynchronousScrolling) { m_needsSynchronousScrolling = needsSynchronousScrolling; } + +private: + explicit PageOverlay(Client&, OverlayType); + + void startFadeAnimation(); + void fadeAnimationTimerFired(); + + Client& m_client; + Page* m_page { nullptr }; + + Timer m_fadeAnimationTimer; + double m_fadeAnimationStartTime { 0 }; + double m_fadeAnimationDuration; + + enum FadeAnimationType { + NoAnimation, + FadeInAnimation, + FadeOutAnimation, + }; + + FadeAnimationType m_fadeAnimationType { NoAnimation }; + float m_fractionFadedIn { 1 }; + + bool m_needsSynchronousScrolling; + + OverlayType m_overlayType; + IntRect m_overrideFrame; + + Color m_backgroundColor { Color::transparent }; + PageOverlayID m_pageOverlayID; + + bool m_shouldIgnoreMouseEventsOutsideBounds { true }; +}; + +} // namespace WebKit diff --git a/Source/WebCore/page/PageOverlayController.cpp b/Source/WebCore/page/PageOverlayController.cpp new file mode 100644 index 000000000..c221dc281 --- /dev/null +++ b/Source/WebCore/page/PageOverlayController.cpp @@ -0,0 +1,413 @@ +/* + * 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 "PageOverlayController.h" + +#include "Chrome.h" +#include "ChromeClient.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GraphicsLayer.h" +#include "MainFrame.h" +#include "Page.h" +#include "PageOverlay.h" +#include "ScrollingCoordinator.h" +#include "Settings.h" +#include "TiledBacking.h" + +// FIXME: Someone needs to call didChangeSettings() if we want dynamic updates of layer border/repaint counter settings. + +namespace WebCore { + +PageOverlayController::PageOverlayController(MainFrame& mainFrame) + : m_initialized(false) + , m_mainFrame(mainFrame) +{ +} + +PageOverlayController::~PageOverlayController() +{ + +} + +void PageOverlayController::createRootLayersIfNeeded() +{ + if (m_initialized) + return; + + m_initialized = true; + + ASSERT(!m_documentOverlayRootLayer); + ASSERT(!m_viewOverlayRootLayer); + + m_documentOverlayRootLayer = GraphicsLayer::create(m_mainFrame.page()->chrome().client().graphicsLayerFactory(), *this); + m_viewOverlayRootLayer = GraphicsLayer::create(m_mainFrame.page()->chrome().client().graphicsLayerFactory(), *this); + m_documentOverlayRootLayer->setName("Document overlay Container"); + m_viewOverlayRootLayer->setName("View overlay container"); +} + +GraphicsLayer* PageOverlayController::documentOverlayRootLayer() const +{ + return m_documentOverlayRootLayer.get(); +} + +GraphicsLayer* PageOverlayController::viewOverlayRootLayer() const +{ + return m_viewOverlayRootLayer.get(); +} + +static void updateOverlayGeometry(PageOverlay& overlay, GraphicsLayer& graphicsLayer) +{ + IntRect overlayFrame = overlay.frame(); + + if (overlayFrame.location() == graphicsLayer.position() && overlayFrame.size() == graphicsLayer.size()) + return; + + graphicsLayer.setPosition(overlayFrame.location()); + graphicsLayer.setSize(overlayFrame.size()); +} + +GraphicsLayer& PageOverlayController::layerWithDocumentOverlays() +{ + createRootLayersIfNeeded(); + + bool inWindow = m_mainFrame.page() ? m_mainFrame.page()->isInWindow() : false; + + for (auto& overlayAndLayer : m_overlayGraphicsLayers) { + PageOverlay& overlay = *overlayAndLayer.key; + if (overlay.overlayType() != PageOverlay::OverlayType::Document) + continue; + + GraphicsLayer& layer = *overlayAndLayer.value; + GraphicsLayer::traverse(layer, [inWindow](GraphicsLayer& layer) { + layer.setIsInWindow(inWindow); + }); + updateOverlayGeometry(overlay, layer); + + if (!layer.parent()) + m_documentOverlayRootLayer->addChild(&layer); + } + + return *m_documentOverlayRootLayer; +} + +GraphicsLayer& PageOverlayController::layerWithViewOverlays() +{ + createRootLayersIfNeeded(); + + bool inWindow = m_mainFrame.page() ? m_mainFrame.page()->isInWindow() : false; + + for (auto& overlayAndLayer : m_overlayGraphicsLayers) { + PageOverlay& overlay = *overlayAndLayer.key; + if (overlay.overlayType() != PageOverlay::OverlayType::View) + continue; + + GraphicsLayer& layer = *overlayAndLayer.value; + GraphicsLayer::traverse(layer, [inWindow](GraphicsLayer& layer) { + layer.setIsInWindow(inWindow); + }); + updateOverlayGeometry(overlay, layer); + + if (!layer.parent()) + m_viewOverlayRootLayer->addChild(&layer); + } + + return *m_viewOverlayRootLayer; +} + +void PageOverlayController::installPageOverlay(PageOverlay& overlay, PageOverlay::FadeMode fadeMode) +{ + createRootLayersIfNeeded(); + + if (m_pageOverlays.contains(&overlay)) + return; + + m_pageOverlays.append(&overlay); + + std::unique_ptr<GraphicsLayer> layer = GraphicsLayer::create(m_mainFrame.page()->chrome().client().graphicsLayerFactory(), *this); + layer->setAnchorPoint(FloatPoint3D()); + layer->setBackgroundColor(overlay.backgroundColor()); + layer->setName("Overlay content"); + + updateSettingsForLayer(*layer); + + switch (overlay.overlayType()) { + case PageOverlay::OverlayType::View: + m_viewOverlayRootLayer->addChild(layer.get()); + break; + case PageOverlay::OverlayType::Document: + m_documentOverlayRootLayer->addChild(layer.get()); + break; + } + + GraphicsLayer& rawLayer = *layer; + m_overlayGraphicsLayers.set(&overlay, WTFMove(layer)); + + updateForceSynchronousScrollLayerPositionUpdates(); + + overlay.setPage(m_mainFrame.page()); + + if (FrameView* frameView = m_mainFrame.view()) + frameView->enterCompositingMode(); + + updateOverlayGeometry(overlay, rawLayer); + + if (fadeMode == PageOverlay::FadeMode::Fade) + overlay.startFadeInAnimation(); +} + +void PageOverlayController::uninstallPageOverlay(PageOverlay& overlay, PageOverlay::FadeMode fadeMode) +{ + if (fadeMode == PageOverlay::FadeMode::Fade) { + overlay.startFadeOutAnimation(); + return; + } + + overlay.setPage(nullptr); + + m_overlayGraphicsLayers.take(&overlay)->removeFromParent(); + + bool removed = m_pageOverlays.removeFirst(&overlay); + ASSERT_UNUSED(removed, removed); + + updateForceSynchronousScrollLayerPositionUpdates(); +} + +void PageOverlayController::updateForceSynchronousScrollLayerPositionUpdates() +{ +#if ENABLE(ASYNC_SCROLLING) + bool forceSynchronousScrollLayerPositionUpdates = false; + + for (auto& overlay : m_pageOverlays) { + if (overlay->needsSynchronousScrolling()) + forceSynchronousScrollLayerPositionUpdates = true; + } + + if (ScrollingCoordinator* scrollingCoordinator = m_mainFrame.page()->scrollingCoordinator()) + scrollingCoordinator->setForceSynchronousScrollLayerPositionUpdates(forceSynchronousScrollLayerPositionUpdates); +#endif +} + +void PageOverlayController::setPageOverlayNeedsDisplay(PageOverlay& overlay, const WebCore::IntRect& dirtyRect) +{ + ASSERT(m_pageOverlays.contains(&overlay)); + GraphicsLayer& graphicsLayer = *m_overlayGraphicsLayers.get(&overlay); + + if (!graphicsLayer.drawsContent()) { + graphicsLayer.setDrawsContent(true); + updateOverlayGeometry(overlay, graphicsLayer); + } + + graphicsLayer.setNeedsDisplayInRect(dirtyRect); +} + +void PageOverlayController::setPageOverlayOpacity(PageOverlay& overlay, float opacity) +{ + ASSERT(m_pageOverlays.contains(&overlay)); + m_overlayGraphicsLayers.get(&overlay)->setOpacity(opacity); +} + +void PageOverlayController::clearPageOverlay(PageOverlay& overlay) +{ + ASSERT(m_pageOverlays.contains(&overlay)); + m_overlayGraphicsLayers.get(&overlay)->setDrawsContent(false); +} + +GraphicsLayer& PageOverlayController::layerForOverlay(PageOverlay& overlay) const +{ + ASSERT(m_pageOverlays.contains(&overlay)); + return *m_overlayGraphicsLayers.get(&overlay); +} + +void PageOverlayController::willDetachRootLayer() +{ + m_documentOverlayRootLayer = nullptr; + m_viewOverlayRootLayer = nullptr; + m_initialized = false; +} + +void PageOverlayController::didChangeViewSize() +{ + for (auto& overlayAndLayer : m_overlayGraphicsLayers) { + if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::View) + updateOverlayGeometry(*overlayAndLayer.key, *overlayAndLayer.value); + } +} + +void PageOverlayController::didChangeDocumentSize() +{ + for (auto& overlayAndLayer : m_overlayGraphicsLayers) { + if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::Document) + updateOverlayGeometry(*overlayAndLayer.key, *overlayAndLayer.value); + } +} + +void PageOverlayController::didChangeSettings() +{ + // FIXME: We should apply these settings to all overlay sublayers recursively. + for (auto& graphicsLayer : m_overlayGraphicsLayers.values()) + updateSettingsForLayer(*graphicsLayer); +} + +void PageOverlayController::didChangeDeviceScaleFactor() +{ + if (!m_initialized) + return; + + m_documentOverlayRootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants(); + m_viewOverlayRootLayer->noteDeviceOrPageScaleFactorChangedIncludingDescendants(); + + for (auto& graphicsLayer : m_overlayGraphicsLayers.values()) + graphicsLayer->setNeedsDisplay(); +} + +void PageOverlayController::didChangeViewExposedRect() +{ + m_mainFrame.page()->chrome().client().scheduleCompositingLayerFlush(); +} + +void PageOverlayController::didScrollFrame(Frame& frame) +{ + for (auto& overlayAndLayer : m_overlayGraphicsLayers) { + if (overlayAndLayer.key->overlayType() == PageOverlay::OverlayType::View || !frame.isMainFrame()) + overlayAndLayer.value->setNeedsDisplay(); + overlayAndLayer.key->didScrollFrame(frame); + } +} + +void PageOverlayController::updateSettingsForLayer(GraphicsLayer& layer) +{ + Settings& settings = m_mainFrame.settings(); + layer.setAcceleratesDrawing(settings.acceleratedDrawingEnabled()); + layer.setShowDebugBorder(settings.showDebugBorders()); + layer.setShowRepaintCounter(settings.showRepaintCounter()); +} + +bool PageOverlayController::handleMouseEvent(const PlatformMouseEvent& mouseEvent) +{ + if (m_pageOverlays.isEmpty()) + return false; + + for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) { + if ((*it)->mouseEvent(mouseEvent)) + return true; + } + + return false; +} + +bool PageOverlayController::copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint parameter, String& value) +{ + if (m_pageOverlays.isEmpty()) + return false; + + for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) { + if ((*it)->copyAccessibilityAttributeStringValueForPoint(attribute, parameter, value)) + return true; + } + + return false; +} + +bool PageOverlayController::copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint parameter, bool& value) +{ + if (m_pageOverlays.isEmpty()) + return false; + + for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) { + if ((*it)->copyAccessibilityAttributeBoolValueForPoint(attribute, parameter, value)) + return true; + } + + return false; +} + +Vector<String> PageOverlayController::copyAccessibilityAttributesNames(bool parameterizedNames) +{ + if (m_pageOverlays.isEmpty()) + return { }; + + for (auto it = m_pageOverlays.rbegin(), end = m_pageOverlays.rend(); it != end; ++it) { + Vector<String> names = (*it)->copyAccessibilityAttributeNames(parameterizedNames); + if (!names.isEmpty()) + return names; + } + + return { }; +} + +void PageOverlayController::paintContents(const WebCore::GraphicsLayer* graphicsLayer, WebCore::GraphicsContext& graphicsContext, WebCore::GraphicsLayerPaintingPhase, const WebCore::FloatRect& clipRect) +{ + for (auto& overlayAndGraphicsLayer : m_overlayGraphicsLayers) { + if (overlayAndGraphicsLayer.value.get() != graphicsLayer) + continue; + + GraphicsContextStateSaver stateSaver(graphicsContext); + graphicsContext.clip(clipRect); + overlayAndGraphicsLayer.key->drawRect(graphicsContext, enclosingIntRect(clipRect)); + + return; + } +} + +float PageOverlayController::deviceScaleFactor() const +{ + if (Page* page = m_mainFrame.page()) + return page->deviceScaleFactor(); + return 1; +} + +void PageOverlayController::notifyFlushRequired(const WebCore::GraphicsLayer*) +{ + if (Page* page = m_mainFrame.page()) + page->chrome().client().scheduleCompositingLayerFlush(); +} + +void PageOverlayController::didChangeOverlayFrame(PageOverlay& overlay) +{ + ASSERT(m_pageOverlays.contains(&overlay)); + updateOverlayGeometry(overlay, *m_overlayGraphicsLayers.get(&overlay)); +} + +void PageOverlayController::didChangeOverlayBackgroundColor(PageOverlay& overlay) +{ + ASSERT(m_pageOverlays.contains(&overlay)); + m_overlayGraphicsLayers.get(&overlay)->setBackgroundColor(overlay.backgroundColor()); +} + +bool PageOverlayController::shouldSkipLayerInDump(const GraphicsLayer*, LayerTreeAsTextBehavior behavior) const +{ + return !(behavior & LayerTreeAsTextIncludePageOverlayLayers); +} + +void PageOverlayController::tiledBackingUsageChanged(const GraphicsLayer* graphicsLayer, bool usingTiledBacking) +{ + if (usingTiledBacking) + graphicsLayer->tiledBacking()->setIsInWindow(m_mainFrame.page()->isInWindow()); +} + +} // namespace WebKit diff --git a/Source/WebCore/page/PageOverlayController.h b/Source/WebCore/page/PageOverlayController.h new file mode 100644 index 000000000..0546929ae --- /dev/null +++ b/Source/WebCore/page/PageOverlayController.h @@ -0,0 +1,105 @@ +/* + * 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 "GraphicsLayerClient.h" +#include "PageOverlay.h" +#include <wtf/HashMap.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Frame; +class MainFrame; +class Page; +class PlatformMouseEvent; + +class PageOverlayController final : public GraphicsLayerClient { + WTF_MAKE_FAST_ALLOCATED; +public: + PageOverlayController(MainFrame&); + virtual ~PageOverlayController(); + + GraphicsLayer& layerWithDocumentOverlays(); + GraphicsLayer& layerWithViewOverlays(); + + WEBCORE_EXPORT GraphicsLayer* documentOverlayRootLayer() const; + WEBCORE_EXPORT GraphicsLayer* viewOverlayRootLayer() const; + + const Vector<RefPtr<PageOverlay>>& pageOverlays() const { return m_pageOverlays; } + + WEBCORE_EXPORT void installPageOverlay(PageOverlay&, PageOverlay::FadeMode); + WEBCORE_EXPORT void uninstallPageOverlay(PageOverlay&, PageOverlay::FadeMode); + + void setPageOverlayNeedsDisplay(PageOverlay&, const IntRect&); + void setPageOverlayOpacity(PageOverlay&, float); + void clearPageOverlay(PageOverlay&); + GraphicsLayer& layerForOverlay(PageOverlay&) const; + + void willDetachRootLayer(); + + void didChangeViewSize(); + void didChangeDocumentSize(); + void didChangeSettings(); + void didChangeDeviceScaleFactor(); + void didChangeViewExposedRect(); + void didScrollFrame(Frame&); + + void didChangeOverlayFrame(PageOverlay&); + void didChangeOverlayBackgroundColor(PageOverlay&); + + int overlayCount() const { return m_overlayGraphicsLayers.size(); } + + bool handleMouseEvent(const PlatformMouseEvent&); + + WEBCORE_EXPORT bool copyAccessibilityAttributeStringValueForPoint(String attribute, FloatPoint, String& value); + WEBCORE_EXPORT bool copyAccessibilityAttributeBoolValueForPoint(String attribute, FloatPoint, bool& value); + WEBCORE_EXPORT Vector<String> copyAccessibilityAttributesNames(bool parameterizedNames); + +private: + void createRootLayersIfNeeded(); + + void updateSettingsForLayer(GraphicsLayer&); + void updateForceSynchronousScrollLayerPositionUpdates(); + + // GraphicsLayerClient + void notifyFlushRequired(const GraphicsLayer*) override; + void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const FloatRect& clipRect) override; + float deviceScaleFactor() const override; + bool shouldSkipLayerInDump(const GraphicsLayer*, LayerTreeAsTextBehavior) const override; + void tiledBackingUsageChanged(const GraphicsLayer*, bool) override; + + std::unique_ptr<GraphicsLayer> m_documentOverlayRootLayer; + std::unique_ptr<GraphicsLayer> m_viewOverlayRootLayer; + bool m_initialized; + + HashMap<PageOverlay*, std::unique_ptr<GraphicsLayer>> m_overlayGraphicsLayers; + Vector<RefPtr<PageOverlay>> m_pageOverlays; + MainFrame& m_mainFrame; +}; + +} // namespace WebKit diff --git a/Source/WebCore/page/PageSerializer.cpp b/Source/WebCore/page/PageSerializer.cpp index 55eeaa942..93d3777aa 100644 --- a/Source/WebCore/page/PageSerializer.cpp +++ b/Source/WebCore/page/PageSerializer.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "PageSerializer.h" +#include "CSSFontFaceRule.h" #include "CSSImageValue.h" #include "CSSImportRule.h" #include "CSSStyleRule.h" @@ -43,10 +44,10 @@ #include "HTMLLinkElement.h" #include "HTMLMetaCharsetParser.h" #include "HTMLNames.h" +#include "HTMLObjectElement.h" #include "HTMLStyleElement.h" #include "HTTPParsers.h" #include "Image.h" -#include "MIMETypeRegistry.h" #include "MainFrame.h" #include "MarkupAccumulator.h" #include "Page.h" @@ -66,10 +67,10 @@ namespace WebCore { static bool isCharsetSpecifyingNode(const Node& node) { - if (!node.isHTMLElement()) + if (!is<HTMLElement>(node)) return false; - const HTMLElement& element = toHTMLElement(node); + const HTMLElement& element = downcast<HTMLElement>(node); if (!element.hasTagName(HTMLNames::metaTag)) return false; HTMLMetaCharsetParser::AttributeList attributes; @@ -79,8 +80,7 @@ static bool isCharsetSpecifyingNode(const Node& node) attributes.append(std::make_pair(attribute.name().toString(), attribute.value().string())); } } - TextEncoding textEncoding = HTMLMetaCharsetParser::encodingFromMetaAttributes(attributes); - return textEncoding.isValid(); + return HTMLMetaCharsetParser::encodingFromMetaAttributes(attributes).isValid(); } static bool shouldIgnoreElement(const Element& element) @@ -91,65 +91,60 @@ static bool shouldIgnoreElement(const Element& element) static const QualifiedName& frameOwnerURLAttributeName(const HTMLFrameOwnerElement& frameOwner) { // FIXME: We should support all frame owners including applets. - return isHTMLObjectElement(frameOwner) ? HTMLNames::dataAttr : HTMLNames::srcAttr; + return is<HTMLObjectElement>(frameOwner) ? HTMLNames::dataAttr : HTMLNames::srcAttr; } -class SerializerMarkupAccumulator final : public WebCore::MarkupAccumulator { +class PageSerializer::SerializerMarkupAccumulator final : public WebCore::MarkupAccumulator { public: SerializerMarkupAccumulator(PageSerializer&, Document&, Vector<Node*>*); - virtual ~SerializerMarkupAccumulator(); private: PageSerializer& m_serializer; Document& m_document; - virtual void appendText(StringBuilder&, const Text&) override; - virtual void appendElement(StringBuilder&, const Element&, Namespaces*) override; - virtual void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override; - virtual void appendEndTag(const Node&) override; + void appendText(StringBuilder&, const Text&) override; + void appendElement(StringBuilder&, const Element&, Namespaces*) override; + void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override; + void appendEndTag(const Element&) override; }; -SerializerMarkupAccumulator::SerializerMarkupAccumulator(PageSerializer& serializer, Document& document, Vector<Node*>* nodes) +PageSerializer::SerializerMarkupAccumulator::SerializerMarkupAccumulator(PageSerializer& serializer, Document& document, Vector<Node*>* nodes) : MarkupAccumulator(nodes, ResolveAllURLs) , m_serializer(serializer) , m_document(document) { // MarkupAccumulator does not serialize the <?xml ... line, so we add it explicitely to ensure the right encoding is specified. - if (m_document.isXHTMLDocument() || m_document.xmlStandalone() || m_document.isSVGDocument()) + if (m_document.isXMLDocument() || m_document.xmlStandalone()) appendString("<?xml version=\"" + m_document.xmlVersion() + "\" encoding=\"" + m_document.charset() + "\"?>"); } -SerializerMarkupAccumulator::~SerializerMarkupAccumulator() -{ -} - -void SerializerMarkupAccumulator::appendText(StringBuilder& out, const Text& text) +void PageSerializer::SerializerMarkupAccumulator::appendText(StringBuilder& out, const Text& text) { Element* parent = text.parentElement(); if (parent && !shouldIgnoreElement(*parent)) MarkupAccumulator::appendText(out, text); } -void SerializerMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, Namespaces* namespaces) +void PageSerializer::SerializerMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, Namespaces* namespaces) { if (!shouldIgnoreElement(element)) MarkupAccumulator::appendElement(out, element, namespaces); if (element.hasTagName(HTMLNames::headTag)) { - out.append("<meta charset=\""); + out.appendLiteral("<meta charset=\""); out.append(m_document.charset()); - out.append("\">"); + out.appendLiteral("\">"); } // FIXME: For object (plugins) tags and video tag we could replace them by an image of their current contents. } -void SerializerMarkupAccumulator::appendCustomAttributes(StringBuilder& out, const Element& element, Namespaces* namespaces) +void PageSerializer::SerializerMarkupAccumulator::appendCustomAttributes(StringBuilder& out, const Element& element, Namespaces* namespaces) { - if (!element.isFrameOwnerElement()) + if (!is<HTMLFrameOwnerElement>(element)) return; - const HTMLFrameOwnerElement& frameOwner = toHTMLFrameOwnerElement(element); + const HTMLFrameOwnerElement& frameOwner = downcast<HTMLFrameOwnerElement>(element); Frame* frame = frameOwner.contentFrame(); if (!frame) return; @@ -163,32 +158,20 @@ void SerializerMarkupAccumulator::appendCustomAttributes(StringBuilder& out, con appendAttribute(out, element, Attribute(frameOwnerURLAttributeName(frameOwner), url.string()), namespaces); } -void SerializerMarkupAccumulator::appendEndTag(const Node& node) -{ - if (node.isElementNode() && !shouldIgnoreElement(toElement(node))) - MarkupAccumulator::appendEndTag(node); -} - -PageSerializer::Resource::Resource() -{ -} - -PageSerializer::Resource::Resource(const URL& url, const String& mimeType, PassRefPtr<SharedBuffer> data) - : url(url) - , mimeType(mimeType) - , data(data) +void PageSerializer::SerializerMarkupAccumulator::appendEndTag(const Element& element) { + if (!shouldIgnoreElement(element)) + MarkupAccumulator::appendEndTag(element); } -PageSerializer::PageSerializer(Vector<PageSerializer::Resource>* resources) +PageSerializer::PageSerializer(Vector<PageSerializer::Resource>& resources) : m_resources(resources) - , m_blankFrameCounter(0) { } -void PageSerializer::serialize(Page* page) +void PageSerializer::serialize(Page& page) { - serializeFrame(&page->mainFrame()); + serializeFrame(&page.mainFrame()); } void PageSerializer::serializeFrame(Frame* frame) @@ -215,35 +198,34 @@ void PageSerializer::serializeFrame(Frame* frame) // FIXME: iframes used as images trigger this. We should deal with them correctly. return; } - String text = accumulator.serializeNodes(*document->documentElement(), 0, IncludeNode); - CString frameHTML = textEncoding.encode(text.deprecatedCharacters(), text.length(), EntitiesForUnencodables); - m_resources->append(Resource(url, document->suggestedMIMEType(), SharedBuffer::create(frameHTML.data(), frameHTML.length()))); + String text = accumulator.serializeNodes(*document->documentElement(), IncludeNode); + CString frameHTML = textEncoding.encode(text, EntitiesForUnencodables); + m_resources.append({ url, document->suggestedMIMEType(), SharedBuffer::create(frameHTML.data(), frameHTML.length()) }); m_resourceURLs.add(url); - for (Vector<Node*>::iterator iter = nodes.begin(); iter != nodes.end(); ++iter) { - Node* node = *iter; - if (!node->isElementNode()) + for (auto& node : nodes) { + if (!is<Element>(*node)) continue; - Element* element = toElement(node); + Element& element = downcast<Element>(*node); // We have to process in-line style as it might contain some resources (typically background images). - if (element->isStyledElement()) - retrieveResourcesForProperties(toStyledElement(element)->inlineStyle(), document); - - if (isHTMLImageElement(element)) { - HTMLImageElement* imageElement = toHTMLImageElement(element); - URL url = document->completeURL(imageElement->getAttribute(HTMLNames::srcAttr)); - CachedImage* cachedImage = imageElement->cachedImage(); - addImageToResources(cachedImage, imageElement->renderer(), url); - } else if (element->hasTagName(HTMLNames::linkTag)) { - HTMLLinkElement* linkElement = toHTMLLinkElement(element); - if (CSSStyleSheet* sheet = linkElement->sheet()) { - URL url = document->completeURL(linkElement->getAttribute(HTMLNames::hrefAttr)); + if (is<StyledElement>(element)) + retrieveResourcesForProperties(downcast<StyledElement>(element).inlineStyle(), document); + + if (is<HTMLImageElement>(element)) { + HTMLImageElement& imageElement = downcast<HTMLImageElement>(element); + URL url = document->completeURL(imageElement.attributeWithoutSynchronization(HTMLNames::srcAttr)); + CachedImage* cachedImage = imageElement.cachedImage(); + addImageToResources(cachedImage, imageElement.renderer(), url); + } else if (is<HTMLLinkElement>(element)) { + HTMLLinkElement& linkElement = downcast<HTMLLinkElement>(element); + if (CSSStyleSheet* sheet = linkElement.sheet()) { + URL url = document->completeURL(linkElement.attributeWithoutSynchronization(HTMLNames::hrefAttr)); serializeCSSStyleSheet(sheet, url); ASSERT(m_resourceURLs.contains(url)); } - } else if (isHTMLStyleElement(element)) { - if (CSSStyleSheet* sheet = toHTMLStyleElement(element)->sheet()) + } else if (is<HTMLStyleElement>(element)) { + if (CSSStyleSheet* sheet = downcast<HTMLStyleElement>(element).sheet()) serializeCSSStyleSheet(sheet, URL()); } } @@ -261,21 +243,21 @@ void PageSerializer::serializeCSSStyleSheet(CSSStyleSheet* styleSheet, const URL if (!itemText.isEmpty()) { cssText.append(itemText); if (i < styleSheet->length() - 1) - cssText.append("\n\n"); + cssText.appendLiteral("\n\n"); } Document* document = styleSheet->ownerDocument(); // Some rules have resources associated with them that we need to retrieve. - if (rule->type() == CSSRule::IMPORT_RULE) { - CSSImportRule* importRule = static_cast<CSSImportRule*>(rule); - URL importURL = document->completeURL(importRule->href()); + if (is<CSSImportRule>(*rule)) { + CSSImportRule& importRule = downcast<CSSImportRule>(*rule); + URL importURL = document->completeURL(importRule.href()); if (m_resourceURLs.contains(importURL)) continue; - serializeCSSStyleSheet(importRule->styleSheet(), importURL); - } else if (rule->type() == CSSRule::FONT_FACE_RULE) { + serializeCSSStyleSheet(importRule.styleSheet(), importURL); + } else if (is<CSSFontFaceRule>(*rule)) { // FIXME: Add support for font face rule. It is not clear to me at this point if the actual otf/eot file can // be retrieved from the CSSFontFaceRule object. - } else if (rule->type() == CSSRule::STYLE_RULE) - retrieveResourcesForRule(static_cast<CSSStyleRule*>(rule)->styleRule(), document); + } else if (is<CSSStyleRule>(*rule)) + retrieveResourcesForRule(downcast<CSSStyleRule>(*rule).styleRule(), document); } if (url.isValid() && !m_resourceURLs.contains(url)) { @@ -283,8 +265,8 @@ void PageSerializer::serializeCSSStyleSheet(CSSStyleSheet* styleSheet, const URL TextEncoding textEncoding(styleSheet->contents().charset()); ASSERT(textEncoding.isValid()); String textString = cssText.toString(); - CString text = textEncoding.encode(textString.deprecatedCharacters(), textString.length(), EntitiesForUnencodables); - m_resources->append(Resource(url, String("text/css"), SharedBuffer::create(text.data(), text.length()))); + CString text = textEncoding.encode(textString, EntitiesForUnencodables); + m_resources.append({ url, ASCIILiteral { "text/css" }, SharedBuffer::create(text.data(), text.length()) }); m_resourceURLs.add(url); } } @@ -306,14 +288,13 @@ void PageSerializer::addImageToResources(CachedImage* image, RenderElement* imag return; } - String mimeType = image->response().mimeType(); - m_resources->append(Resource(url, mimeType, data)); + m_resources.append({ url, image->response().mimeType(), WTFMove(data) }); m_resourceURLs.add(url); } -void PageSerializer::retrieveResourcesForRule(StyleRule* rule, Document* document) +void PageSerializer::retrieveResourcesForRule(StyleRule& rule, Document* document) { - retrieveResourcesForProperties(&rule->properties(), document); + retrieveResourcesForProperties(&rule.properties(), document); } void PageSerializer::retrieveResourcesForProperties(const StyleProperties* styleDeclaration, Document* document) @@ -327,30 +308,25 @@ void PageSerializer::retrieveResourcesForProperties(const StyleProperties* style unsigned propertyCount = styleDeclaration->propertyCount(); for (unsigned i = 0; i < propertyCount; ++i) { RefPtr<CSSValue> cssValue = styleDeclaration->propertyAt(i).value(); - if (!cssValue->isImageValue()) + if (!is<CSSImageValue>(*cssValue)) continue; - StyleImage* styleImage = toCSSImageValue(cssValue.get())->cachedOrPendingImage(); - // Non cached-images are just place-holders and do not contain data. - if (!styleImage || !styleImage->isCachedImage()) + auto* image = downcast<CSSImageValue>(*cssValue).cachedImage(); + if (!image) continue; - CachedImage* image = static_cast<StyleCachedImage*>(styleImage)->cachedImage(); - - URL url = document->completeURL(image->url()); - addImageToResources(image, 0, url); + addImageToResources(image, nullptr, document->completeURL(image->url())); } } URL PageSerializer::urlForBlankFrame(Frame* frame) { - HashMap<Frame*, URL>::iterator iter = m_blankFrameURLs.find(frame); + auto iter = m_blankFrameURLs.find(frame); if (iter != m_blankFrameURLs.end()) return iter->value; String url = "wyciwyg://frame/" + String::number(m_blankFrameCounter++); URL fakeURL(ParsedURLString, url); m_blankFrameURLs.add(frame, fakeURL); - return fakeURL; } diff --git a/Source/WebCore/page/PageSerializer.h b/Source/WebCore/page/PageSerializer.h index f25488f7f..d5743bc4f 100644 --- a/Source/WebCore/page/PageSerializer.h +++ b/Source/WebCore/page/PageSerializer.h @@ -28,10 +28,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageSerializer_h -#define PageSerializer_h +#pragma once -#include "URL.h" #include "URLHash.h" #include "SharedBuffer.h" #include <wtf/HashMap.h> @@ -57,20 +55,20 @@ public: URL url; String mimeType; RefPtr<SharedBuffer> data; - Resource(); - Resource(const URL&, const String& mimeType, PassRefPtr<SharedBuffer> data); }; - explicit PageSerializer(Vector<Resource>*); + explicit PageSerializer(Vector<Resource>&); // Initiates the serialization of the frame's page. All serialized content and retrieved // resources are added to the Vector passed to the constructor. The first resource in that // vector is the top frame serialized content. - void serialize(Page*); + void serialize(Page&); + +private: + class SerializerMarkupAccumulator; URL urlForBlankFrame(Frame*); -private: void serializeFrame(Frame*); // Serializes the stylesheet back to text and adds it to the resources if URL is not-empty. @@ -79,14 +77,12 @@ private: void addImageToResources(CachedImage*, RenderElement*, const URL&); void retrieveResourcesForProperties(const StyleProperties*, Document*); - void retrieveResourcesForRule(StyleRule*, Document*); + void retrieveResourcesForRule(StyleRule&, Document*); - Vector<Resource>* m_resources; + Vector<Resource>& m_resources; ListHashSet<URL> m_resourceURLs; HashMap<Frame*, URL> m_blankFrameURLs; - unsigned m_blankFrameCounter; + unsigned m_blankFrameCounter { 0 }; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/PageThrottler.cpp b/Source/WebCore/page/PageThrottler.cpp deleted file mode 100644 index 28c63f1e1..000000000 --- a/Source/WebCore/page/PageThrottler.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 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. - */ - -#include "config.h" -#include "PageThrottler.h" - -#include "Chrome.h" -#include "ChromeClient.h" -#include "MainFrame.h" -#include "Page.h" -#include "PageActivityAssertionToken.h" -#include <wtf/StdLibExtras.h> - -namespace WebCore { - -static const double kThrottleHysteresisSeconds = 2.0; - -PageThrottler::PageThrottler(Page& page) - : m_page(page) - , m_throttleState(PageNotThrottledState) - , m_throttleHysteresisTimer(this, &PageThrottler::throttleHysteresisTimerFired) - , m_visuallyNonIdle("Page is not visually idle.") -{ - m_page.chrome().client().incrementActivePageCount(); -} - -PageThrottler::~PageThrottler() -{ - setIsVisuallyIdle(false); - - for (auto it = m_activityTokens.begin(), end = m_activityTokens.end(); it != end; ++it) - (*it)->invalidate(); - - if (m_throttleState != PageThrottledState) - m_page.chrome().client().decrementActivePageCount(); -} - -std::unique_ptr<PageActivityAssertionToken> PageThrottler::createActivityToken() -{ - return std::make_unique<PageActivityAssertionToken>(*this); -} - -void PageThrottler::throttlePage() -{ - m_throttleState = PageThrottledState; - - m_page.chrome().client().decrementActivePageCount(); - - for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (frame->document()) - frame->document()->scriptedAnimationControllerSetThrottled(true); - } - - m_page.throttleTimers(); -} - -void PageThrottler::unthrottlePage() -{ - PageThrottleState oldState = m_throttleState; - m_throttleState = PageNotThrottledState; - - if (oldState == PageNotThrottledState) - return; - - if (oldState == PageThrottledState) - m_page.chrome().client().incrementActivePageCount(); - - for (Frame* frame = &m_page.mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (frame->document()) - frame->document()->scriptedAnimationControllerSetThrottled(false); - } - - m_page.unthrottleTimers(); -} - -void PageThrottler::setIsVisuallyIdle(bool isVisuallyIdle) -{ - if (isVisuallyIdle) { - m_throttleState = PageWaitingToThrottleState; - startThrottleHysteresisTimer(); - if (m_visuallyNonIdle.isActive()) - m_visuallyNonIdle.endActivity(); - } else { - unthrottlePage(); - stopThrottleHysteresisTimer(); - if (!m_visuallyNonIdle.isActive()) - m_visuallyNonIdle.beginActivity(); - } -} - -void PageThrottler::stopThrottleHysteresisTimer() -{ - m_throttleHysteresisTimer.stop(); -} - -void PageThrottler::reportInterestingEvent() -{ - if (m_throttleState == PageNotThrottledState) - return; - if (m_throttleState == PageThrottledState) - unthrottlePage(); - m_throttleState = PageWaitingToThrottleState; - startThrottleHysteresisTimer(); -} - -void PageThrottler::startThrottleHysteresisTimer() -{ - if (m_throttleHysteresisTimer.isActive()) - m_throttleHysteresisTimer.stop(); - if (!m_activityTokens.size()) - m_throttleHysteresisTimer.startOneShot(kThrottleHysteresisSeconds); -} - -void PageThrottler::throttleHysteresisTimerFired(Timer<PageThrottler>&) -{ - ASSERT(!m_activityTokens.size()); - throttlePage(); -} - -void PageThrottler::addActivityToken(PageActivityAssertionToken& token) -{ - ASSERT(!m_activityTokens.contains(&token)); - - m_activityTokens.add(&token); - - // If we've already got events that block throttling we can return early - if (m_activityTokens.size() > 1) - return; - - if (m_throttleState == PageNotThrottledState) - return; - - if (m_throttleState == PageThrottledState) - unthrottlePage(); - - m_throttleState = PageWaitingToThrottleState; - stopThrottleHysteresisTimer(); -} - -void PageThrottler::removeActivityToken(PageActivityAssertionToken& token) -{ - ASSERT(m_activityTokens.contains(&token)); - - m_activityTokens.remove(&token); - - if (m_activityTokens.size()) - return; - - if (m_throttleState == PageNotThrottledState) - return; - - ASSERT(m_throttleState == PageWaitingToThrottleState); - startThrottleHysteresisTimer(); -} - -} diff --git a/Source/WebCore/page/PageVisibilityState.cpp b/Source/WebCore/page/PageVisibilityState.cpp deleted file mode 100644 index b06b0b85f..000000000 --- a/Source/WebCore/page/PageVisibilityState.cpp +++ /dev/null @@ -1,59 +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: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "PageVisibilityState.h" - -#if ENABLE(PAGE_VISIBILITY_API) - -namespace WebCore { - -String pageVisibilityStateString(PageVisibilityState state) -{ - DEFINE_STATIC_LOCAL(const String, visible, (ASCIILiteral("visible"))); - DEFINE_STATIC_LOCAL(const String, hidden, (ASCIILiteral("hidden"))); - DEFINE_STATIC_LOCAL(const String, prerender, (ASCIILiteral("prerender"))); - - switch (state) { - case PageVisibilityStateVisible: - return visible; - case PageVisibilityStateHidden: - return hidden; - case PageVisibilityStatePrerender: - return prerender; - } - - ASSERT_NOT_REACHED(); - return String(); -} - -} // namespace WebCore - -#endif // if ENABLE(PAGE_VISIBILITY_API) diff --git a/Source/WebCore/page/PageVisibilityState.h b/Source/WebCore/page/PageVisibilityState.h index 3fab2e42a..53f3ebf74 100644 --- a/Source/WebCore/page/PageVisibilityState.h +++ b/Source/WebCore/page/PageVisibilityState.h @@ -28,25 +28,16 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageVisibilityState_h -#define PageVisibilityState_h +#pragma once #include <wtf/text/WTFString.h> namespace WebCore { -// The enum is not flag protected as it is used in the WebKit chromium API -// without flag protection. -enum PageVisibilityState { - PageVisibilityStateVisible, - PageVisibilityStateHidden, - PageVisibilityStatePrerender +enum class PageVisibilityState { + Hidden, + Visible, + Prerender }; -#if ENABLE(PAGE_VISIBILITY_API) -String pageVisibilityStateString(PageVisibilityState); -#endif - } // namespace WebCore - -#endif // ifndef PageVisibilityState_h diff --git a/Source/WebCore/page/Performance.cpp b/Source/WebCore/page/Performance.cpp index b4372f618..c35b0efa0 100644 --- a/Source/WebCore/page/Performance.cpp +++ b/Source/WebCore/page/Performance.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. * Copyright (C) 2012 Intel Inc. All rights reserved. + * 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 @@ -32,203 +33,237 @@ #include "config.h" #include "Performance.h" +#if ENABLE(WEB_TIMING) + #include "Document.h" #include "DocumentLoader.h" +#include "EventNames.h" +#include "Frame.h" #include "PerformanceEntry.h" #include "PerformanceNavigation.h" +#include "PerformanceObserver.h" #include "PerformanceResourceTiming.h" #include "PerformanceTiming.h" #include "PerformanceUserTiming.h" #include "ResourceResponse.h" +#include "ScriptExecutionContext.h" #include <wtf/CurrentTime.h> -#if ENABLE(WEB_TIMING) - -#include "Frame.h" - namespace WebCore { -#if ENABLE(RESOURCE_TIMING) -static const size_t defaultResourceTimingBufferSize = 150; -#endif - -Performance::Performance(Frame* frame) - : DOMWindowProperty(frame) -#if ENABLE(RESOURCE_TIMING) - , m_resourceTimingBufferSize(defaultResourceTimingBufferSize) -#endif // ENABLE(RESOURCE_TIMING) -#if ENABLE(USER_TIMING) - , m_userTiming(0) -#endif // ENABLE(USER_TIMING) +Performance::Performance(ScriptExecutionContext& context, MonotonicTime timeOrigin) + : ContextDestructionObserver(&context) + , m_timeOrigin(timeOrigin) + , m_performanceTimelineTaskQueue(context) { + ASSERT(m_timeOrigin); } Performance::~Performance() { } -ScriptExecutionContext* Performance::scriptExecutionContext() const +void Performance::contextDestroyed() { - if (!frame()) - return 0; - return frame()->document(); + m_performanceTimelineTaskQueue.close(); + + ContextDestructionObserver::contextDestroyed(); } -PerformanceNavigation* Performance::navigation() const +double Performance::now() const { - if (!m_navigation) - m_navigation = PerformanceNavigation::create(m_frame); + Seconds now = MonotonicTime::now() - m_timeOrigin; + return reduceTimeResolution(now).milliseconds(); +} +Seconds Performance::reduceTimeResolution(Seconds seconds) +{ + double resolution = (100_us).seconds(); + double reduced = std::floor(seconds.seconds() / resolution) * resolution; + return Seconds(reduced); +} + +PerformanceNavigation* Performance::navigation() +{ + if (!is<Document>(scriptExecutionContext())) + return nullptr; + + ASSERT(isMainThread()); + if (!m_navigation) + m_navigation = PerformanceNavigation::create(downcast<Document>(*scriptExecutionContext()).frame()); return m_navigation.get(); } -PerformanceTiming* Performance::timing() const +PerformanceTiming* Performance::timing() { - if (!m_timing) - m_timing = PerformanceTiming::create(m_frame); + if (!is<Document>(scriptExecutionContext())) + return nullptr; + ASSERT(isMainThread()); + if (!m_timing) + m_timing = PerformanceTiming::create(downcast<Document>(*scriptExecutionContext()).frame()); return m_timing.get(); } -#if ENABLE(PERFORMANCE_TIMELINE) -PassRefPtr<PerformanceEntryList> Performance::webkitGetEntries() const +Vector<RefPtr<PerformanceEntry>> Performance::getEntries() const { - RefPtr<PerformanceEntryList> entries = PerformanceEntryList::create(); + Vector<RefPtr<PerformanceEntry>> entries; -#if ENABLE(RESOURCE_TIMING) - entries->appendAll(m_resourceTimingBuffer); -#endif // ENABLE(RESOURCE_TIMING) + entries.appendVector(m_resourceTimingBuffer); -#if ENABLE(USER_TIMING) if (m_userTiming) { - entries->appendAll(m_userTiming->getMarks()); - entries->appendAll(m_userTiming->getMeasures()); + entries.appendVector(m_userTiming->getMarks()); + entries.appendVector(m_userTiming->getMeasures()); } -#endif // ENABLE(USER_TIMING) - entries->sort(); + std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan); return entries; } -PassRefPtr<PerformanceEntryList> Performance::webkitGetEntriesByType(const String& entryType) +Vector<RefPtr<PerformanceEntry>> Performance::getEntriesByType(const String& entryType) const { - RefPtr<PerformanceEntryList> entries = PerformanceEntryList::create(); + Vector<RefPtr<PerformanceEntry>> entries; -#if ENABLE(RESOURCE_TIMING) - if (equalIgnoringCase(entryType, "resource")) - for (Vector<RefPtr<PerformanceEntry>>::const_iterator resource = m_resourceTimingBuffer.begin(); resource != m_resourceTimingBuffer.end(); ++resource) - entries->append(*resource); -#endif // ENABLE(RESOURCE_TIMING) + if (equalLettersIgnoringASCIICase(entryType, "resource")) + entries.appendVector(m_resourceTimingBuffer); -#if ENABLE(USER_TIMING) if (m_userTiming) { - if (equalIgnoringCase(entryType, "mark")) - entries->appendAll(m_userTiming->getMarks()); - else if (equalIgnoringCase(entryType, "measure")) - entries->appendAll(m_userTiming->getMeasures()); + if (equalLettersIgnoringASCIICase(entryType, "mark")) + entries.appendVector(m_userTiming->getMarks()); + else if (equalLettersIgnoringASCIICase(entryType, "measure")) + entries.appendVector(m_userTiming->getMeasures()); } -#endif // ENABLE(USER_TIMING) - entries->sort(); + std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan); return entries; } -PassRefPtr<PerformanceEntryList> Performance::webkitGetEntriesByName(const String& name, const String& entryType) +Vector<RefPtr<PerformanceEntry>> Performance::getEntriesByName(const String& name, const String& entryType) const { - RefPtr<PerformanceEntryList> entries = PerformanceEntryList::create(); + Vector<RefPtr<PerformanceEntry>> entries; -#if ENABLE(RESOURCE_TIMING) - if (entryType.isNull() || equalIgnoringCase(entryType, "resource")) - for (Vector<RefPtr<PerformanceEntry>>::const_iterator resource = m_resourceTimingBuffer.begin(); resource != m_resourceTimingBuffer.end(); ++resource) - if ((*resource)->name() == name) - entries->append(*resource); -#endif // ENABLE(RESOURCE_TIMING) + if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "resource")) { + for (auto& resource : m_resourceTimingBuffer) { + if (resource->name() == name) + entries.append(resource); + } + } -#if ENABLE(USER_TIMING) if (m_userTiming) { - if (entryType.isNull() || equalIgnoringCase(entryType, "mark")) - entries->appendAll(m_userTiming->getMarks(name)); - if (entryType.isNull() || equalIgnoringCase(entryType, "measure")) - entries->appendAll(m_userTiming->getMeasures(name)); + if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "mark")) + entries.appendVector(m_userTiming->getMarks(name)); + if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "measure")) + entries.appendVector(m_userTiming->getMeasures(name)); } -#endif // ENABLE(USER_TIMING) - entries->sort(); + std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan); return entries; } -#endif // ENABLE(PERFORMANCE_TIMELINE) - -#if ENABLE(RESOURCE_TIMING) - -void Performance::webkitClearResourceTimings() +void Performance::clearResourceTimings() { m_resourceTimingBuffer.clear(); } -void Performance::webkitSetResourceTimingBufferSize(unsigned size) +void Performance::setResourceTimingBufferSize(unsigned size) { m_resourceTimingBufferSize = size; - if (isResourceTimingBufferFull()) - dispatchEvent(Event::create(eventNames().webkitresourcetimingbufferfullEvent, false, false)); } -void Performance::addResourceTiming(const String& initiatorName, Document* initiatorDocument, const ResourceRequest& request, const ResourceResponse& response, double initiationTime, double finishTime) +void Performance::addResourceTiming(ResourceTiming&& resourceTiming) { + RefPtr<PerformanceResourceTiming> entry = PerformanceResourceTiming::create(m_timeOrigin, WTFMove(resourceTiming)); + + queueEntry(*entry); + if (isResourceTimingBufferFull()) return; - RefPtr<PerformanceEntry> entry = PerformanceResourceTiming::create(initiatorName, request, response, initiationTime, finishTime, initiatorDocument); - m_resourceTimingBuffer.append(entry); if (isResourceTimingBufferFull()) - dispatchEvent(Event::create(eventNames().webkitresourcetimingbufferfullEvent, false, false)); + dispatchEvent(Event::create(eventNames().resourcetimingbufferfullEvent, true, false)); } -bool Performance::isResourceTimingBufferFull() +bool Performance::isResourceTimingBufferFull() const { return m_resourceTimingBuffer.size() >= m_resourceTimingBufferSize; } -#endif // ENABLE(RESOURCE_TIMING) - -#if ENABLE(USER_TIMING) -void Performance::webkitMark(const String& markName, ExceptionCode& ec) +ExceptionOr<void> Performance::mark(const String& markName) { - ec = 0; if (!m_userTiming) - m_userTiming = UserTiming::create(this); - m_userTiming->mark(markName, ec); + m_userTiming = std::make_unique<UserTiming>(*this); + + auto result = m_userTiming->mark(markName); + if (result.hasException()) + return result.releaseException(); + + queueEntry(result.releaseReturnValue()); + + return { }; } -void Performance::webkitClearMarks(const String& markName) +void Performance::clearMarks(const String& markName) { if (!m_userTiming) - m_userTiming = UserTiming::create(this); + m_userTiming = std::make_unique<UserTiming>(*this); m_userTiming->clearMarks(markName); } -void Performance::webkitMeasure(const String& measureName, const String& startMark, const String& endMark, ExceptionCode& ec) +ExceptionOr<void> Performance::measure(const String& measureName, const String& startMark, const String& endMark) { - ec = 0; if (!m_userTiming) - m_userTiming = UserTiming::create(this); - m_userTiming->measure(measureName, startMark, endMark, ec); + m_userTiming = std::make_unique<UserTiming>(*this); + + auto result = m_userTiming->measure(measureName, startMark, endMark); + if (result.hasException()) + return result.releaseException(); + + queueEntry(result.releaseReturnValue()); + + return { }; } -void Performance::webkitClearMeasures(const String& measureName) +void Performance::clearMeasures(const String& measureName) { if (!m_userTiming) - m_userTiming = UserTiming::create(this); + m_userTiming = std::make_unique<UserTiming>(*this); m_userTiming->clearMeasures(measureName); } -#endif // ENABLE(USER_TIMING) +void Performance::registerPerformanceObserver(PerformanceObserver& observer) +{ + m_observers.add(&observer); +} -double Performance::now() const +void Performance::unregisterPerformanceObserver(PerformanceObserver& observer) +{ + m_observers.remove(&observer); +} + +void Performance::queueEntry(PerformanceEntry& entry) { - return 1000.0 * m_frame->document()->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(monotonicallyIncreasingTime()); + bool shouldScheduleTask = false; + for (auto& observer : m_observers) { + if (observer->typeFilter().contains(entry.type())) { + observer->queueEntry(entry); + shouldScheduleTask = true; + } + } + + if (!shouldScheduleTask) + return; + + if (m_performanceTimelineTaskQueue.hasPendingTasks()) + return; + + m_performanceTimelineTaskQueue.enqueueTask([this] () { + Vector<RefPtr<PerformanceObserver>> observers; + copyToVector(m_observers, observers); + for (auto& observer : observers) + observer->deliver(); + }); } } // namespace WebCore diff --git a/Source/WebCore/page/Performance.h b/Source/WebCore/page/Performance.h index 19c995bae..01654f392 100644 --- a/Source/WebCore/page/Performance.h +++ b/Source/WebCore/page/Performance.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. * Copyright (C) 2012 Intel Inc. All rights reserved. + * 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 @@ -29,89 +30,93 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Performance_h -#define Performance_h +#pragma once #if ENABLE(WEB_TIMING) -#include "DOMWindowProperty.h" +#include "ContextDestructionObserver.h" #include "EventTarget.h" -#include "PerformanceEntryList.h" -#include "PerformanceNavigation.h" -#include "PerformanceTiming.h" -#include "ScriptWrappable.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/text/WTFString.h> +#include "ExceptionOr.h" +#include "GenericTaskQueue.h" +#include <wtf/ListHashSet.h> namespace WebCore { -class Document; -class ResourceRequest; +class LoadTiming; +class PerformanceEntry; +class PerformanceNavigation; +class PerformanceObserver; +class PerformanceTiming; class ResourceResponse; +class ResourceTiming; +class ScriptExecutionContext; +class URL; class UserTiming; -class Performance final : public ScriptWrappable, public RefCounted<Performance>, public DOMWindowProperty, public EventTargetWithInlineData { +class Performance final : public RefCounted<Performance>, public ContextDestructionObserver, public EventTargetWithInlineData { public: - static PassRefPtr<Performance> create(Frame* frame) { return adoptRef(new Performance(frame)); } + static Ref<Performance> create(ScriptExecutionContext& context, MonotonicTime timeOrigin) { return adoptRef(*new Performance(context, timeOrigin)); } ~Performance(); - virtual EventTargetInterface eventTargetInterface() const override { return PerformanceEventTargetInterfaceType; } - virtual ScriptExecutionContext* scriptExecutionContext() const override; - - PerformanceNavigation* navigation() const; - PerformanceTiming* timing() const; double now() const; -#if ENABLE(PERFORMANCE_TIMELINE) - PassRefPtr<PerformanceEntryList> webkitGetEntries() const; - PassRefPtr<PerformanceEntryList> webkitGetEntriesByType(const String& entryType); - PassRefPtr<PerformanceEntryList> webkitGetEntriesByName(const String& name, const String& entryType); -#endif + PerformanceNavigation* navigation(); + PerformanceTiming* timing(); + + Vector<RefPtr<PerformanceEntry>> getEntries() const; + Vector<RefPtr<PerformanceEntry>> getEntriesByType(const String& entryType) const; + Vector<RefPtr<PerformanceEntry>> getEntriesByName(const String& name, const String& entryType) const; + + void clearResourceTimings(); + void setResourceTimingBufferSize(unsigned); -#if ENABLE(RESOURCE_TIMING) - void webkitClearResourceTimings(); - void webkitSetResourceTimingBufferSize(unsigned int); + ExceptionOr<void> mark(const String& markName); + void clearMarks(const String& markName); - DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitresourcetimingbufferfull); + ExceptionOr<void> measure(const String& measureName, const String& startMark, const String& endMark); + void clearMeasures(const String& measureName); - void addResourceTiming(const String& initiatorName, Document*, const ResourceRequest&, const ResourceResponse&, double initiationTime, double finishTime); -#endif + void addResourceTiming(ResourceTiming&&); - using RefCounted<Performance>::ref; - using RefCounted<Performance>::deref; + void registerPerformanceObserver(PerformanceObserver&); + void unregisterPerformanceObserver(PerformanceObserver&); -#if ENABLE(USER_TIMING) - void webkitMark(const String& markName, ExceptionCode&); - void webkitClearMarks(const String& markName); + static Seconds reduceTimeResolution(Seconds); - void webkitMeasure(const String& measureName, const String& startMark, const String& endMark, ExceptionCode&); - void webkitClearMeasures(const String& measureName); -#endif // ENABLE(USER_TIMING) + ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); } + + using RefCounted::ref; + using RefCounted::deref; private: - explicit Performance(Frame*); + Performance(ScriptExecutionContext&, MonotonicTime timeOrigin); + + void contextDestroyed() override; + + EventTargetInterface eventTargetInterface() const final { return PerformanceEventTargetInterfaceType; } - virtual void refEventTarget() override { ref(); } - virtual void derefEventTarget() override { deref(); } - bool isResourceTimingBufferFull(); + void refEventTarget() final { ref(); } + void derefEventTarget() final { deref(); } + + bool isResourceTimingBufferFull() const; + + void queueEntry(PerformanceEntry&); mutable RefPtr<PerformanceNavigation> m_navigation; mutable RefPtr<PerformanceTiming> m_timing; - -#if ENABLE(RESOURCE_TIMING) + + // https://w3c.github.io/resource-timing/#extensions-performance-interface recommends size of 150. Vector<RefPtr<PerformanceEntry>> m_resourceTimingBuffer; - unsigned m_resourceTimingBufferSize; -#endif + unsigned m_resourceTimingBufferSize { 150 }; -#if ENABLE(USER_TIMING) - RefPtr<UserTiming> m_userTiming; -#endif // ENABLE(USER_TIMING) + MonotonicTime m_timeOrigin; + + std::unique_ptr<UserTiming> m_userTiming; + + GenericTaskQueue<ScriptExecutionContext> m_performanceTimelineTaskQueue; + ListHashSet<RefPtr<PerformanceObserver>> m_observers; }; } #endif // ENABLE(WEB_TIMING) - -#endif // Performance_h diff --git a/Source/WebCore/page/Performance.idl b/Source/WebCore/page/Performance.idl index 53e8390db..47059b951 100644 --- a/Source/WebCore/page/Performance.idl +++ b/Source/WebCore/page/Performance.idl @@ -29,37 +29,37 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// See: http://dev.w3.org/2006/webapi/WebTiming/ +// https://w3c.github.io/hr-time/ + +typedef double DOMHighResTimeStamp; + [ Conditional=WEB_TIMING, - EventTarget, -] interface Performance { - readonly attribute PerformanceNavigation navigation; - readonly attribute PerformanceTiming timing; + Exposed=(Window,Worker), + GenerateIsReachable=ImplScriptExecutionContext, +] interface Performance : EventTarget { -#if defined(ENABLE_PERFORMANCE_TIMELINE) && ENABLE_PERFORMANCE_TIMELINE - PerformanceEntryList webkitGetEntries(); - PerformanceEntryList webkitGetEntriesByType(DOMString entryType); - PerformanceEntryList webkitGetEntriesByName(DOMString name, [Default=NullString] optional DOMString entryType); -#endif + DOMHighResTimeStamp now(); -#if defined(ENABLE_RESOURCE_TIMING) && ENABLE_RESOURCE_TIMING - void webkitClearResourceTimings(); - void webkitSetResourceTimingBufferSize(unsigned long maxSize); - - attribute EventListener onwebkitresourcetimingbufferfull; -#endif + // https://w3c.github.io/navigation-timing/ + readonly attribute PerformanceNavigation navigation; + readonly attribute PerformanceTiming timing; - // See http://www.w3.org/TR/2012/CR-user-timing-20120726/ -#if defined(ENABLE_USER_TIMING) && ENABLE_USER_TIMING - [RaisesException] void webkitMark(DOMString markName); - void webkitClearMarks([Default=NullString] optional DOMString markName); + // https://w3c.github.io/performance-timeline/ + [EnabledAtRuntime=PerformanceTimeline] PerformanceEntryList getEntries(); + [EnabledAtRuntime=PerformanceTimeline] PerformanceEntryList getEntriesByType(DOMString entryType); + [EnabledAtRuntime=PerformanceTimeline] PerformanceEntryList getEntriesByName(DOMString name, optional DOMString entryType); - [RaisesException] void webkitMeasure(DOMString measureName, [Default=NullString] optional DOMString startMark, [Default=NullString] optional DOMString endMark); - void webkitClearMeasures([Default=NullString] optional DOMString measureName); -#endif + // https://w3c.github.io/resource-timing/ + [EnabledAtRuntime=ResourceTiming] void clearResourceTimings(); + [EnabledAtRuntime=ResourceTiming] void setResourceTimingBufferSize(unsigned long maxSize); + [EnabledAtRuntime=ResourceTiming] attribute EventHandler onresourcetimingbufferfull; - // See http://www.w3.org/TR/hr-time/ for details. - double now(); + // https://w3c.github.io/user-timing/ + [EnabledAtRuntime=UserTiming, MayThrowException] void mark(DOMString markName); + [EnabledAtRuntime=UserTiming] void clearMarks(optional DOMString markName); + [EnabledAtRuntime=UserTiming, MayThrowException] void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark); + [EnabledAtRuntime=UserTiming] void clearMeasures(optional DOMString measureName); }; +typedef sequence<PerformanceEntry> PerformanceEntryList; diff --git a/Source/WebCore/page/PerformanceEntry.cpp b/Source/WebCore/page/PerformanceEntry.cpp index 32db7cfe5..a85fbdb5f 100644 --- a/Source/WebCore/page/PerformanceEntry.cpp +++ b/Source/WebCore/page/PerformanceEntry.cpp @@ -31,15 +31,18 @@ #include "config.h" #include "PerformanceEntry.h" -#if ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) +#if ENABLE(WEB_TIMING) + +#include "RuntimeEnabledFeatures.h" namespace WebCore { -PerformanceEntry::PerformanceEntry(const String& name, const String& entryType, double startTime, double finishTime) +PerformanceEntry::PerformanceEntry(Type type, const String& name, const String& entryType, double startTime, double finishTime) : m_name(name) , m_entryType(entryType) , m_startTime(startTime) , m_duration(finishTime - startTime) + , m_type(type) { } @@ -47,26 +50,26 @@ PerformanceEntry::~PerformanceEntry() { } -String PerformanceEntry::name() const +std::optional<PerformanceEntry::Type> PerformanceEntry::parseEntryTypeString(const String& entryType) { - return m_name; -} + if (entryType == "navigation") + return std::optional<Type>(Type::Navigation); -String PerformanceEntry::entryType() const -{ - return m_entryType; -} + if (RuntimeEnabledFeatures::sharedFeatures().userTimingEnabled()) { + if (entryType == "mark") + return std::optional<Type>(Type::Mark); + if (entryType == "measure") + return std::optional<Type>(Type::Measure); + } -double PerformanceEntry::startTime() const -{ - return m_startTime; -} + if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) { + if (entryType == "resource") + return std::optional<Type>(Type::Resource); + } -double PerformanceEntry::duration() const -{ - return m_duration; + return std::nullopt; } } // namespace WebCore -#endif // ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceEntry.h b/Source/WebCore/page/PerformanceEntry.h index 129a24640..7ebee6a4c 100644 --- a/Source/WebCore/page/PerformanceEntry.h +++ b/Source/WebCore/page/PerformanceEntry.h @@ -29,14 +29,14 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceEntry_h -#define PerformanceEntry_h +#pragma once -#if ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) +#if ENABLE(WEB_TIMING) #include "Performance.h" -#include <wtf/PassRefPtr.h> +#include <wtf/Optional.h> #include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> #include <wtf/text/WTFString.h> namespace WebCore { @@ -45,31 +45,42 @@ class PerformanceEntry : public RefCounted<PerformanceEntry> { public: virtual ~PerformanceEntry(); - String name() const; - String entryType() const; - double startTime() const; - double duration() const; + String name() const { return m_name; } + String entryType() const { return m_entryType; } + double startTime() const { return m_startTime; } + double duration() const { return m_duration; } - virtual bool isResource() { return false; } - virtual bool isMark() { return false; } - virtual bool isMeasure() { return false; } + enum class Type { + Navigation = 1 << 0, + Mark = 1 << 1, + Measure = 1 << 2, + Resource = 1 << 3, + }; - static bool startTimeCompareLessThan(PassRefPtr<PerformanceEntry> a, PassRefPtr<PerformanceEntry> b) + Type type() const { return m_type; } + + static std::optional<Type> parseEntryTypeString(const String& entryType); + + virtual bool isResource() const { return false; } + virtual bool isMark() const { return false; } + virtual bool isMeasure() const { return false; } + + static bool startTimeCompareLessThan(const RefPtr<PerformanceEntry>& a, const RefPtr<PerformanceEntry>& b) { return a->startTime() < b->startTime(); } protected: - PerformanceEntry(const String& name, const String& entryType, double startTime, double finishTime); + PerformanceEntry(Type, const String& name, const String& entryType, double startTime, double finishTime); private: const String m_name; const String m_entryType; const double m_startTime; const double m_duration; + const Type m_type; }; -} +} // namespace WebCore -#endif // !ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) -#endif // !defined(PerformanceEntry_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceEntry.idl b/Source/WebCore/page/PerformanceEntry.idl index 23f79479b..47d317c22 100644 --- a/Source/WebCore/page/PerformanceEntry.idl +++ b/Source/WebCore/page/PerformanceEntry.idl @@ -28,14 +28,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// See: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PerformanceTimeline/Overview.html +// https://w3c.github.io/performance-timeline/ + +typedef double DOMHighResTimeStamp; + [ Conditional=WEB_TIMING, - Conditional=PERFORMANCE_TIMELINE, CustomToJSObject, + EnabledAtRuntime=PerformanceTimeline, + Exposed=(Window,Worker), ] interface PerformanceEntry { readonly attribute DOMString name; readonly attribute DOMString entryType; - readonly attribute double startTime; - readonly attribute double duration; + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + serializer = { attribute }; }; diff --git a/Source/WebCore/page/PerformanceEntryList.h b/Source/WebCore/page/PerformanceEntryList.h deleted file mode 100644 index d900287ad..000000000 --- a/Source/WebCore/page/PerformanceEntryList.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * Copyright (C) 2012 Intel Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef PerformanceEntryList_h -#define PerformanceEntryList_h - -#if ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class PerformanceEntry; - -class PerformanceEntryList : public RefCounted<PerformanceEntryList> { -public: - static PassRefPtr<PerformanceEntryList> create() { return adoptRef(new PerformanceEntryList); } - ~PerformanceEntryList(); - - unsigned length() const; - PerformanceEntry* item(unsigned index); - - void append(PassRefPtr<PerformanceEntry>); - void appendAll(const Vector<RefPtr<PerformanceEntry>>&); - - void sort(); - -private: - PerformanceEntryList(); - - Vector<RefPtr<PerformanceEntry>> m_entries; -}; - -} // namespace WebCore - -#endif // !ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) -#endif // PerformanceEntryList_h diff --git a/Source/WebCore/page/PerformanceEntryList.idl b/Source/WebCore/page/PerformanceEntryList.idl deleted file mode 100644 index 7e25a85ce..000000000 --- a/Source/WebCore/page/PerformanceEntryList.idl +++ /dev/null @@ -1,41 +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: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// See: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PerformanceTimeline/Overview.html -[ - NoInterfaceObject, - Conditional=WEB_TIMING, - Conditional=PERFORMANCE_TIMELINE, - ImplementationLacksVTable, -] interface PerformanceEntryList { - readonly attribute unsigned long length; - getter PerformanceEntry item(unsigned long index); -}; - diff --git a/Source/WebCore/page/PerformanceLogging.cpp b/Source/WebCore/page/PerformanceLogging.cpp new file mode 100644 index 000000000..34af678b5 --- /dev/null +++ b/Source/WebCore/page/PerformanceLogging.cpp @@ -0,0 +1,109 @@ +/* + * 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. ``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 "PerformanceLogging.h" + +#include "CommonVM.h" +#include "DOMWindow.h" +#include "Document.h" +#include "FrameLoaderClient.h" +#include "JSDOMWindow.h" +#include "Logging.h" +#include "MainFrame.h" +#include "PageCache.h" + +namespace WebCore { + +#if !RELEASE_LOG_DISABLED +static const char* toString(PerformanceLogging::PointOfInterest poi) +{ + switch (poi) { + case PerformanceLogging::MainFrameLoadStarted: + return "MainFrameLoadStarted"; + case PerformanceLogging::MainFrameLoadCompleted: + return "MainFrameLoadCompleted"; + } +} +#endif + +HashMap<const char*, size_t> PerformanceLogging::memoryUsageStatistics(ShouldIncludeExpensiveComputations includeExpensive) +{ + HashMap<const char*, size_t> stats; + + auto& vm = commonVM(); + stats.add("javascript_gc_heap_capacity", vm.heap.capacity()); + stats.add("javascript_gc_heap_extra_memory_size", vm.heap.extraMemorySize()); + + auto& pageCache = PageCache::singleton(); + stats.add("pagecache_page_count", pageCache.pageCount()); + + stats.add("document_count", Document::allDocuments().size()); + + if (includeExpensive == ShouldIncludeExpensiveComputations::Yes) { + stats.add("javascript_gc_heap_size", vm.heap.size()); + stats.add("javascript_gc_object_count", vm.heap.objectCount()); + stats.add("javascript_gc_protected_object_count", vm.heap.protectedObjectCount()); + stats.add("javascript_gc_global_object_count", vm.heap.globalObjectCount()); + stats.add("javascript_gc_protected_global_object_count", vm.heap.protectedGlobalObjectCount()); + } + + return stats; +} + +HashCountedSet<const char*> PerformanceLogging::javaScriptObjectCounts() +{ + return WTFMove(*commonVM().heap.objectTypeCounts()); +} + +PerformanceLogging::PerformanceLogging(MainFrame& mainFrame) + : m_mainFrame(mainFrame) +{ +} + +void PerformanceLogging::didReachPointOfInterest(PointOfInterest poi) +{ +#if RELEASE_LOG_DISABLED + UNUSED_PARAM(poi); +#else + // Ignore synthetic main frames used internally by SVG and web inspector. + if (m_mainFrame.loader().client().isEmptyFrameLoaderClient()) + return; + + auto stats = memoryUsageStatistics(ShouldIncludeExpensiveComputations::No); + getPlatformMemoryUsageStatistics(stats); + + RELEASE_LOG(PerformanceLogging, "Memory usage info dump at %s:", toString(poi)); + for (auto& it : stats) + RELEASE_LOG(PerformanceLogging, " %s: %zu", it.key, it.value); +#endif +} + +#if !PLATFORM(COCOA) +void PerformanceLogging::getPlatformMemoryUsageStatistics(HashMap<const char*, size_t>&) { } +std::optional<uint64_t> PerformanceLogging::physicalFootprint() { return std::nullopt; } +#endif + +} diff --git a/Source/WebCore/page/PerformanceLogging.h b/Source/WebCore/page/PerformanceLogging.h new file mode 100644 index 000000000..9287a298c --- /dev/null +++ b/Source/WebCore/page/PerformanceLogging.h @@ -0,0 +1,60 @@ +/* + * 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. ``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 + +#include <wtf/HashCountedSet.h> +#include <wtf/HashMap.h> + +namespace WebCore { + +class MainFrame; + +enum class ShouldIncludeExpensiveComputations { No, Yes }; + +class PerformanceLogging { + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(PerformanceLogging); +public: + explicit PerformanceLogging(MainFrame&); + + enum PointOfInterest { + MainFrameLoadStarted, + MainFrameLoadCompleted, + }; + + void didReachPointOfInterest(PointOfInterest); + + WEBCORE_EXPORT static HashCountedSet<const char*> javaScriptObjectCounts(); + WEBCORE_EXPORT static HashMap<const char*, size_t> memoryUsageStatistics(ShouldIncludeExpensiveComputations); + WEBCORE_EXPORT static std::optional<uint64_t> physicalFootprint(); + +private: + static void getPlatformMemoryUsageStatistics(HashMap<const char*, size_t>&); + + MainFrame& m_mainFrame; +}; + +} diff --git a/Source/WebCore/page/PerformanceMark.h b/Source/WebCore/page/PerformanceMark.h index 64b39abfd..528ba1996 100644 --- a/Source/WebCore/page/PerformanceMark.h +++ b/Source/WebCore/page/PerformanceMark.h @@ -23,30 +23,34 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceMark_h -#define PerformanceMark_h +#pragma once -#if ENABLE(USER_TIMING) +#if ENABLE(WEB_TIMING) #include "PerformanceEntry.h" -#include <wtf/PassRefPtr.h> #include <wtf/text/WTFString.h> namespace WebCore { -class PerformanceMark : public PerformanceEntry { +class PerformanceMark final : public PerformanceEntry { public: - static PassRefPtr<PerformanceMark> create(const String& name, double startTime) { return adoptRef(new PerformanceMark(name, startTime)); } + static Ref<PerformanceMark> create(const String& name, double startTime) { return adoptRef(*new PerformanceMark(name, startTime)); } - virtual bool isMark() { return true; } + bool isMark() const override { return true; } private: - PerformanceMark(const String& name, double startTime) : PerformanceEntry(name, "mark", startTime, startTime) { } + PerformanceMark(const String& name, double startTime) + : PerformanceEntry(PerformanceEntry::Type::Mark, name, ASCIILiteral("mark"), startTime, startTime) + { + } + ~PerformanceMark() { } }; -} +} // namespace WebCore -#endif // ENABLE(USER_TIMING) +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::PerformanceMark) + static bool isType(const WebCore::PerformanceEntry& entry) { return entry.isMark(); } +SPECIALIZE_TYPE_TRAITS_END() -#endif // !defined(PerformanceMark_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceMark.idl b/Source/WebCore/page/PerformanceMark.idl index 45ba6b1b3..be44db03d 100644 --- a/Source/WebCore/page/PerformanceMark.idl +++ b/Source/WebCore/page/PerformanceMark.idl @@ -23,7 +23,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// https://w3c.github.io/user-timing/ + [ - Conditional=USER_TIMING, + Conditional=WEB_TIMING, + EnabledAtRuntime=UserTiming, + Exposed=(Window,Worker), ] interface PerformanceMark : PerformanceEntry { }; diff --git a/Source/WebCore/page/PerformanceMeasure.h b/Source/WebCore/page/PerformanceMeasure.h index 65564959b..cdb0b6c58 100644 --- a/Source/WebCore/page/PerformanceMeasure.h +++ b/Source/WebCore/page/PerformanceMeasure.h @@ -23,30 +23,34 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceMeasure_h -#define PerformanceMeasure_h +#pragma once -#if ENABLE(USER_TIMING) +#if ENABLE(WEB_TIMING) #include "PerformanceEntry.h" -#include <wtf/PassRefPtr.h> #include <wtf/text/WTFString.h> namespace WebCore { -class PerformanceMeasure : public PerformanceEntry { +class PerformanceMeasure final : public PerformanceEntry { public: - static PassRefPtr<PerformanceMeasure> create(const String& name, double startTime, double duration) { return adoptRef(new PerformanceMeasure(name, startTime, duration)); } + static Ref<PerformanceMeasure> create(const String& name, double startTime, double duration) { return adoptRef(*new PerformanceMeasure(name, startTime, duration)); } - virtual bool isMeasure() { return true; } + bool isMeasure() const override { return true; } private: - PerformanceMeasure(const String& name, double startTime, double duration) : PerformanceEntry(name, "measure", startTime, duration) { } + PerformanceMeasure(const String& name, double startTime, double duration) + : PerformanceEntry(PerformanceEntry::Type::Measure, name, ASCIILiteral("measure"), startTime, duration) + { + } + ~PerformanceMeasure() { } }; -} +} // namespace WebCore -#endif // ENABLE(USER_TIMING) +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::PerformanceMeasure) + static bool isType(const WebCore::PerformanceEntry& entry) { return entry.isMeasure(); } +SPECIALIZE_TYPE_TRAITS_END() -#endif // !defined(PerformanceMeasure_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceMeasure.idl b/Source/WebCore/page/PerformanceMeasure.idl index 5dfcbb7c7..60199db75 100644 --- a/Source/WebCore/page/PerformanceMeasure.idl +++ b/Source/WebCore/page/PerformanceMeasure.idl @@ -23,7 +23,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// https://w3c.github.io/user-timing/ + [ - Conditional=USER_TIMING, + Conditional=WEB_TIMING, + EnabledAtRuntime=UserTiming, + Exposed=(Window,Worker), ] interface PerformanceMeasure : PerformanceEntry { }; diff --git a/Source/WebCore/page/PerformanceMonitor.cpp b/Source/WebCore/page/PerformanceMonitor.cpp new file mode 100644 index 000000000..3bfe4571c --- /dev/null +++ b/Source/WebCore/page/PerformanceMonitor.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 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 "PerformanceMonitor.h" + +#include "Chrome.h" +#include "ChromeClient.h" +#include "DiagnosticLoggingClient.h" +#include "DiagnosticLoggingKeys.h" +#include "Logging.h" +#include "Page.h" +#include "PerformanceLogging.h" +#include "Settings.h" + +namespace WebCore { + +#define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(m_page.isAlwaysOnLoggingAllowed(), channel, "%p - PerformanceMonitor::" fmt, this, ##__VA_ARGS__) + +static const std::chrono::seconds cpuUsageMeasurementDelay { 5 }; +static const std::chrono::seconds postLoadCPUUsageMeasurementDuration { 10 }; +static const std::chrono::minutes backgroundCPUUsageMeasurementDuration { 5 }; +static const std::chrono::minutes cpuUsageSamplingInterval { 10 }; + +static const std::chrono::seconds memoryUsageMeasurementDelay { 10 }; + +static inline ActivityStateForCPUSampling activityStateForCPUSampling(ActivityState::Flags state) +{ + if (!(state & ActivityState::IsVisible)) + return ActivityStateForCPUSampling::NonVisible; + if (state & ActivityState::WindowIsActive) + return ActivityStateForCPUSampling::VisibleAndActive; + return ActivityStateForCPUSampling::VisibleNonActive; +} + +PerformanceMonitor::PerformanceMonitor(Page& page) + : m_page(page) + , m_postPageLoadCPUUsageTimer(*this, &PerformanceMonitor::measurePostLoadCPUUsage) + , m_postBackgroundingCPUUsageTimer(*this, &PerformanceMonitor::measurePostBackgroundingCPUUsage) + , m_perActivityStateCPUUsageTimer(*this, &PerformanceMonitor::measurePerActivityStateCPUUsage) + , m_postPageLoadMemoryUsageTimer(*this, &PerformanceMonitor::measurePostLoadMemoryUsage) + , m_postBackgroundingMemoryUsageTimer(*this, &PerformanceMonitor::measurePostBackgroundingMemoryUsage) +{ + ASSERT(!page.isUtilityPage()); + + if (Settings::isPerActivityStateCPUUsageMeasurementEnabled()) { + m_perActivityStateCPUTime = getCPUTime(); + m_perActivityStateCPUUsageTimer.startRepeating(cpuUsageSamplingInterval); + } +} + +void PerformanceMonitor::didStartProvisionalLoad() +{ + m_postLoadCPUTime = std::nullopt; + m_postPageLoadCPUUsageTimer.stop(); + m_postPageLoadMemoryUsageTimer.stop(); +} + +void PerformanceMonitor::didFinishLoad() +{ + // Only do post-load CPU usage measurement if there is a single Page in the process in order to reduce noise. + if (Settings::isPostLoadCPUUsageMeasurementEnabled() && m_page.isOnlyNonUtilityPage()) { + m_postLoadCPUTime = std::nullopt; + m_postPageLoadCPUUsageTimer.startOneShot(cpuUsageMeasurementDelay); + } + + // Likewise for post-load memory usage measurement. + if (Settings::isPostLoadMemoryUsageMeasurementEnabled() && m_page.isOnlyNonUtilityPage()) + m_postPageLoadMemoryUsageTimer.startOneShot(memoryUsageMeasurementDelay); +} + +void PerformanceMonitor::activityStateChanged(ActivityState::Flags oldState, ActivityState::Flags newState) +{ + ActivityState::Flags changed = oldState ^ newState; + bool visibilityChanged = changed & ActivityState::IsVisible; + + // Measure CPU usage of pages when they are no longer visible. + if (Settings::isPostBackgroundingCPUUsageMeasurementEnabled() && visibilityChanged) { + m_postBackgroundingCPUTime = std::nullopt; + if (newState & ActivityState::IsVisible) + m_postBackgroundingCPUUsageTimer.stop(); + else if (m_page.isOnlyNonUtilityPage()) + m_postBackgroundingCPUUsageTimer.startOneShot(cpuUsageMeasurementDelay); + } + + if (Settings::isPerActivityStateCPUUsageMeasurementEnabled()) { + // If visibility changed then report CPU usage right away because CPU usage is connected to visibility state. + auto oldActivityStateForCPUSampling = activityStateForCPUSampling(oldState); + if (oldActivityStateForCPUSampling != activityStateForCPUSampling(newState)) { + measureCPUUsageInActivityState(oldActivityStateForCPUSampling); + m_perActivityStateCPUUsageTimer.startRepeating(cpuUsageSamplingInterval); + } + } + + if (Settings::isPostBackgroundingMemoryUsageMeasurementEnabled() && visibilityChanged) { + if (newState & ActivityState::IsVisible) + m_postBackgroundingMemoryUsageTimer.stop(); + else if (m_page.isOnlyNonUtilityPage()) + m_postBackgroundingMemoryUsageTimer.startOneShot(memoryUsageMeasurementDelay); + } +} + +void PerformanceMonitor::measurePostLoadCPUUsage() +{ + if (!m_page.isOnlyNonUtilityPage()) { + m_postLoadCPUTime = std::nullopt; + return; + } + + if (!m_postLoadCPUTime) { + m_postLoadCPUTime = getCPUTime(); + if (m_postLoadCPUTime) + m_postPageLoadCPUUsageTimer.startOneShot(postLoadCPUUsageMeasurementDuration); + return; + } + std::optional<CPUTime> cpuTime = getCPUTime(); + if (!cpuTime) + return; + + double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_postLoadCPUTime); + RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostLoadCPUUsage: Process was using %.1f%% CPU after the page load.", cpuUsage); + m_page.diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::postPageLoadCPUUsageKey(), DiagnosticLoggingKeys::foregroundCPUUsageToDiagnosticLoggingKey(cpuUsage), ShouldSample::No); +} + +void PerformanceMonitor::measurePostLoadMemoryUsage() +{ + if (!m_page.isOnlyNonUtilityPage()) + return; + + std::optional<uint64_t> memoryUsage = PerformanceLogging::physicalFootprint(); + if (!memoryUsage) + return; + + RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostLoadMemoryUsage: Process was using %llu bytes of memory after the page load.", memoryUsage.value()); + m_page.diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::postPageLoadMemoryUsageKey(), DiagnosticLoggingKeys::memoryUsageToDiagnosticLoggingKey(memoryUsage.value()), ShouldSample::No); +} + +void PerformanceMonitor::measurePostBackgroundingMemoryUsage() +{ + if (!m_page.isOnlyNonUtilityPage()) + return; + + std::optional<uint64_t> memoryUsage = PerformanceLogging::physicalFootprint(); + if (!memoryUsage) + return; + + RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostBackgroundingMemoryUsage: Process was using %llu bytes of memory after becoming non visible.", memoryUsage.value()); + m_page.diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::postPageBackgroundingMemoryUsageKey(), DiagnosticLoggingKeys::memoryUsageToDiagnosticLoggingKey(memoryUsage.value()), ShouldSample::No); +} + +void PerformanceMonitor::measurePostBackgroundingCPUUsage() +{ + if (!m_page.isOnlyNonUtilityPage()) { + m_postBackgroundingCPUTime = std::nullopt; + return; + } + + if (!m_postBackgroundingCPUTime) { + m_postBackgroundingCPUTime = getCPUTime(); + if (m_postBackgroundingCPUTime) + m_postBackgroundingCPUUsageTimer.startOneShot(backgroundCPUUsageMeasurementDuration); + return; + } + std::optional<CPUTime> cpuTime = getCPUTime(); + if (!cpuTime) + return; + + double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_postBackgroundingCPUTime); + RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measurePostBackgroundingCPUUsage: Process was using %.1f%% CPU after becoming non visible.", cpuUsage); + m_page.diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::postPageBackgroundingCPUUsageKey(), DiagnosticLoggingKeys::backgroundCPUUsageToDiagnosticLoggingKey(cpuUsage), ShouldSample::No); +} + +void PerformanceMonitor::measurePerActivityStateCPUUsage() +{ + measureCPUUsageInActivityState(activityStateForCPUSampling(m_page.activityState())); +} + +#if !RELEASE_LOG_DISABLED + +static inline const char* stringForCPUSamplingActivityState(ActivityStateForCPUSampling activityState) +{ + switch (activityState) { + case ActivityStateForCPUSampling::NonVisible: + return "NonVisible"; + case ActivityStateForCPUSampling::VisibleNonActive: + return "VisibleNonActive"; + case ActivityStateForCPUSampling::VisibleAndActive: + return "VisibleAndActive"; + } +} + +#endif + +void PerformanceMonitor::measureCPUUsageInActivityState(ActivityStateForCPUSampling activityState) +{ + if (!m_page.isOnlyNonUtilityPage()) { + m_perActivityStateCPUTime = std::nullopt; + return; + } + + if (!m_perActivityStateCPUTime) { + m_perActivityStateCPUTime = getCPUTime(); + return; + } + + std::optional<CPUTime> cpuTime = getCPUTime(); + if (!cpuTime) { + m_perActivityStateCPUTime = std::nullopt; + return; + } + +#if !RELEASE_LOG_DISABLED + double cpuUsage = cpuTime.value().percentageCPUUsageSince(*m_perActivityStateCPUTime); + RELEASE_LOG_IF_ALLOWED(PerformanceLogging, "measureCPUUsageInActivityState: Process is using %.1f%% CPU in state: %s", cpuUsage, stringForCPUSamplingActivityState(activityState)); +#endif + m_page.chrome().client().reportProcessCPUTime((cpuTime.value().systemTime + cpuTime.value().userTime) - (m_perActivityStateCPUTime.value().systemTime + m_perActivityStateCPUTime.value().userTime), activityState); + + m_perActivityStateCPUTime = WTFMove(cpuTime); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/PerformanceMonitor.h b/Source/WebCore/page/PerformanceMonitor.h new file mode 100644 index 000000000..b89fa0dc4 --- /dev/null +++ b/Source/WebCore/page/PerformanceMonitor.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#include "ActivityState.h" +#include "CPUTime.h" +#include "Timer.h" +#include <wtf/Optional.h> + +namespace WebCore { + +class Page; + +class PerformanceMonitor { +public: + explicit PerformanceMonitor(Page&); + + void didStartProvisionalLoad(); + void didFinishLoad(); + void activityStateChanged(ActivityState::Flags oldState, ActivityState::Flags newState); + +private: + void measurePostLoadCPUUsage(); + void measurePostBackgroundingCPUUsage(); + void measurePerActivityStateCPUUsage(); + void measureCPUUsageInActivityState(ActivityStateForCPUSampling); + + void measurePostLoadMemoryUsage(); + void measurePostBackgroundingMemoryUsage(); + + Page& m_page; + + Timer m_postPageLoadCPUUsageTimer; + std::optional<CPUTime> m_postLoadCPUTime; + Timer m_postBackgroundingCPUUsageTimer; + std::optional<CPUTime> m_postBackgroundingCPUTime; + Timer m_perActivityStateCPUUsageTimer; + std::optional<CPUTime> m_perActivityStateCPUTime; + + Timer m_postPageLoadMemoryUsageTimer; + Timer m_postBackgroundingMemoryUsageTimer; +}; + +} diff --git a/Source/WebCore/page/PerformanceNavigation.cpp b/Source/WebCore/page/PerformanceNavigation.cpp index 2cb3aefed..6c60e3239 100644 --- a/Source/WebCore/page/PerformanceNavigation.cpp +++ b/Source/WebCore/page/PerformanceNavigation.cpp @@ -55,9 +55,9 @@ unsigned short PerformanceNavigation::type() const WebCore::NavigationType navigationType = documentLoader->triggeringAction().type(); switch (navigationType) { - case NavigationTypeReload: + case NavigationType::Reload: return TYPE_RELOAD; - case NavigationTypeBackForward: + case NavigationType::BackForward: return TYPE_BACK_FORWARD; default: return TYPE_NAVIGATE; @@ -73,11 +73,11 @@ unsigned short PerformanceNavigation::redirectCount() const if (!loader) return 0; - DocumentLoadTiming* timing = loader->timing(); - if (timing->hasCrossOriginRedirect()) + LoadTiming& timing = loader->timing(); + if (timing.hasCrossOriginRedirect()) return 0; - return timing->redirectCount(); + return timing.redirectCount(); } } // namespace WebCore diff --git a/Source/WebCore/page/PerformanceNavigation.h b/Source/WebCore/page/PerformanceNavigation.h index f397923e6..3d572439b 100644 --- a/Source/WebCore/page/PerformanceNavigation.h +++ b/Source/WebCore/page/PerformanceNavigation.h @@ -28,14 +28,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceNavigation_h -#define PerformanceNavigation_h +#pragma once #if ENABLE(WEB_TIMING) #include "DOMWindowProperty.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/Ref.h> namespace WebCore { @@ -43,7 +42,7 @@ class Frame; class PerformanceNavigation : public RefCounted<PerformanceNavigation>, public DOMWindowProperty { public: - static PassRefPtr<PerformanceNavigation> create(Frame* frame) { return adoptRef(new PerformanceNavigation(frame)); } + static Ref<PerformanceNavigation> create(Frame* frame) { return adoptRef(*new PerformanceNavigation(frame)); } enum PerformanceNavigationType { TYPE_NAVIGATE, @@ -59,7 +58,6 @@ private: explicit PerformanceNavigation(Frame*); }; -} +} // namespace WebCore -#endif // !ENABLE(WEB_TIMING) -#endif // !defined(PerformanceNavigation_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceObserver.cpp b/Source/WebCore/page/PerformanceObserver.cpp new file mode 100644 index 000000000..8d4bfa91a --- /dev/null +++ b/Source/WebCore/page/PerformanceObserver.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 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 "PerformanceObserver.h" + +#if ENABLE(WEB_TIMING) + +#include "DOMWindow.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "Performance.h" +#include "PerformanceObserverEntryList.h" +#include "WorkerGlobalScope.h" + +namespace WebCore { + +PerformanceObserver::PerformanceObserver(ScriptExecutionContext& scriptExecutionContext, Ref<PerformanceObserverCallback>&& callback) + : m_callback(WTFMove(callback)) +{ + if (is<Document>(scriptExecutionContext)) { + auto& document = downcast<Document>(scriptExecutionContext); + if (DOMWindow* window = document.domWindow()) + m_performance = window->performance(); + } else if (is<WorkerGlobalScope>(scriptExecutionContext)) { + auto& workerGlobalScope = downcast<WorkerGlobalScope>(scriptExecutionContext); + m_performance = &workerGlobalScope.performance(); + } else + ASSERT_NOT_REACHED(); +} + +ExceptionOr<void> PerformanceObserver::observe(Init&& init) +{ + if (!m_performance) + return Exception { TypeError }; + + if (init.entryTypes.isEmpty()) + return Exception { TypeError, ASCIILiteral("entryTypes cannot be an empty list") }; + + OptionSet<PerformanceEntry::Type> filter; + for (const String& entryType : init.entryTypes) { + if (auto type = PerformanceEntry::parseEntryTypeString(entryType)) + filter |= *type; + } + + if (filter.isEmpty()) + return Exception { TypeError, ASCIILiteral("entryTypes contained only unsupported types") }; + + m_typeFilter = filter; + + if (!m_registered) { + m_performance->registerPerformanceObserver(*this); + m_registered = true; + } + + return { }; +} + +void PerformanceObserver::disconnect() +{ + if (m_performance) + m_performance->unregisterPerformanceObserver(*this); + + m_registered = false; + m_entriesToDeliver.clear(); +} + +void PerformanceObserver::queueEntry(PerformanceEntry& entry) +{ + m_entriesToDeliver.append(&entry); +} + +void PerformanceObserver::deliver() +{ + if (m_entriesToDeliver.isEmpty()) + return; + + Vector<RefPtr<PerformanceEntry>> entries = WTFMove(m_entriesToDeliver); + auto list = PerformanceObserverEntryList::create(WTFMove(entries)); + m_callback->handleEvent(list.ptr(), this); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceObserver.h b/Source/WebCore/page/PerformanceObserver.h new file mode 100644 index 000000000..f17fc73d5 --- /dev/null +++ b/Source/WebCore/page/PerformanceObserver.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#if ENABLE(WEB_TIMING) + +#include "ExceptionOr.h" +#include "PerformanceEntry.h" +#include "PerformanceObserverCallback.h" +#include <wtf/OptionSet.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class Performance; +class ScriptExecutionContext; + +class PerformanceObserver : public RefCounted<PerformanceObserver> { +public: + struct Init { + Vector<String> entryTypes; + }; + + static Ref<PerformanceObserver> create(ScriptExecutionContext& context, Ref<PerformanceObserverCallback>&& callback) + { + return adoptRef(*new PerformanceObserver(context, WTFMove(callback))); + } + + ExceptionOr<void> observe(Init&&); + void disconnect(); + + OptionSet<PerformanceEntry::Type> typeFilter() const { return m_typeFilter; } + + void queueEntry(PerformanceEntry&); + void deliver(); + +private: + PerformanceObserver(ScriptExecutionContext&, Ref<PerformanceObserverCallback>&&); + + RefPtr<Performance> m_performance; + Vector<RefPtr<PerformanceEntry>> m_entriesToDeliver; + Ref<PerformanceObserverCallback> m_callback; + OptionSet<PerformanceEntry::Type> m_typeFilter; + bool m_registered { false }; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceObserver.idl b/Source/WebCore/page/PerformanceObserver.idl new file mode 100644 index 000000000..97178121c --- /dev/null +++ b/Source/WebCore/page/PerformanceObserver.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 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. + */ + +// https://w3c.github.io/performance-timeline/ + +[ + Conditional=WEB_TIMING, + Constructor(PerformanceObserverCallback callback), + ConstructorCallWith=ScriptExecutionContext, + EnabledAtRuntime=PerformanceTimeline, + Exposed=(Window,Worker), + ImplementationLacksVTable, +] interface PerformanceObserver { + [MayThrowException] void observe(PerformanceObserverInit options); + void disconnect(); +}; + +[ + Conditional=WEB_TIMING, +] +dictionary PerformanceObserverInit { + required sequence<DOMString> entryTypes; +}; diff --git a/Source/WebCore/page/PerformanceObserverCallback.h b/Source/WebCore/page/PerformanceObserverCallback.h new file mode 100644 index 000000000..9135c3984 --- /dev/null +++ b/Source/WebCore/page/PerformanceObserverCallback.h @@ -0,0 +1,45 @@ +/* + * 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 + +#if ENABLE(WEB_TIMING) + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class PerformanceObserver; +class PerformanceObserverEntryList; + +class PerformanceObserverCallback : public RefCounted<PerformanceObserverCallback> { +public: + virtual ~PerformanceObserverCallback() { } + virtual bool handleEvent(PerformanceObserverEntryList*, PerformanceObserver*) = 0; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceObserverCallback.idl b/Source/WebCore/page/PerformanceObserverCallback.idl new file mode 100644 index 000000000..cc9a1dcf8 --- /dev/null +++ b/Source/WebCore/page/PerformanceObserverCallback.idl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 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. + */ + +// https://w3c.github.io/performance-timeline/ + +[ + Conditional=WEB_TIMING, +] callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer); diff --git a/Source/WebCore/page/PerformanceObserverEntryList.cpp b/Source/WebCore/page/PerformanceObserverEntryList.cpp new file mode 100644 index 000000000..7d09ef037 --- /dev/null +++ b/Source/WebCore/page/PerformanceObserverEntryList.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 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 "PerformanceObserverEntryList.h" + +#if ENABLE(WEB_TIMING) + +#include "PerformanceEntry.h" + +namespace WebCore { + +Ref<PerformanceObserverEntryList> PerformanceObserverEntryList::create(Vector<RefPtr<PerformanceEntry>>&& entries) +{ + return adoptRef(*new PerformanceObserverEntryList(WTFMove(entries))); +} + +PerformanceObserverEntryList::PerformanceObserverEntryList(Vector<RefPtr<PerformanceEntry>>&& entries) + : m_entries(WTFMove(entries)) +{ + ASSERT(!m_entries.isEmpty()); + + std::stable_sort(m_entries.begin(), m_entries.end(), PerformanceEntry::startTimeCompareLessThan); +} + +Vector<RefPtr<PerformanceEntry>> PerformanceObserverEntryList::getEntriesByType(const String& entryType) const +{ + return getEntriesByName(String(), entryType); +} + +Vector<RefPtr<PerformanceEntry>> PerformanceObserverEntryList::getEntriesByName(const String& name, const String& entryType) const +{ + Vector<RefPtr<PerformanceEntry>> entries; + + // PerformanceObservers can only be registered for valid types. + // So if the incoming entryType is an unknown type, there will be no matches. + std::optional<PerformanceEntry::Type> type; + if (!entryType.isNull()) { + type = PerformanceEntry::parseEntryTypeString(entryType); + if (!type) + return entries; + } + + for (auto& entry : m_entries) { + if (!name.isNull() && entry->name() != name) + continue; + if (type && entry->type() != *type) + continue; + entries.append(entry.copyRef()); + } + + return entries; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceObserverEntryList.h b/Source/WebCore/page/PerformanceObserverEntryList.h new file mode 100644 index 000000000..3af690ca7 --- /dev/null +++ b/Source/WebCore/page/PerformanceObserverEntryList.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 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. + */ + +#pragma once + +#if ENABLE(WEB_TIMING) + +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class PerformanceEntry; + +class PerformanceObserverEntryList : public RefCounted<PerformanceObserverEntryList> { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref<PerformanceObserverEntryList> create(Vector<RefPtr<PerformanceEntry>>&& entries); + + const Vector<RefPtr<PerformanceEntry>>& getEntries() const { return m_entries; } + Vector<RefPtr<PerformanceEntry>> getEntriesByType(const String& entryType) const; + Vector<RefPtr<PerformanceEntry>> getEntriesByName(const String& name, const String& entryType) const; + +private: + PerformanceObserverEntryList(Vector<RefPtr<PerformanceEntry>>&& entries); + + Vector<RefPtr<PerformanceEntry>> m_entries; +}; + +} // namespace WebCore + +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceObserverEntryList.idl b/Source/WebCore/page/PerformanceObserverEntryList.idl new file mode 100644 index 000000000..a28cbb55e --- /dev/null +++ b/Source/WebCore/page/PerformanceObserverEntryList.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 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. + */ + +// https://w3c.github.io/performance-timeline/ + +[ + Conditional=WEB_TIMING, + EnabledAtRuntime=PerformanceTimeline, + Exposed=(Window,Worker), + ImplementationLacksVTable, +] interface PerformanceObserverEntryList { + PerformanceEntryList getEntries(); + PerformanceEntryList getEntriesByType(DOMString type); + PerformanceEntryList getEntriesByName(DOMString name, optional DOMString type); +}; + +typedef sequence<PerformanceEntry> PerformanceEntryList; diff --git a/Source/WebCore/page/PerformanceResourceTiming.cpp b/Source/WebCore/page/PerformanceResourceTiming.cpp index bb6bc6ad9..270a0863c 100644 --- a/Source/WebCore/page/PerformanceResourceTiming.cpp +++ b/Source/WebCore/page/PerformanceResourceTiming.cpp @@ -32,56 +32,39 @@ #include "config.h" #include "PerformanceResourceTiming.h" -#if ENABLE(RESOURCE_TIMING) +#if ENABLE(WEB_TIMING) #include "Document.h" -#include "DocumentLoadTiming.h" #include "DocumentLoader.h" -#include "URL.h" -#include "ResourceRequest.h" +#include "LoadTiming.h" #include "ResourceResponse.h" -#include "SecurityOrigin.h" -#include <wtf/Vector.h> +#include "ResourceTiming.h" +#include "URL.h" namespace WebCore { -static double monotonicTimeToDocumentMilliseconds(Document* document, double seconds) +static double monotonicTimeToDOMHighResTimeStamp(MonotonicTime timeOrigin, MonotonicTime timeStamp) { - ASSERT(seconds >= 0.0); - return document->loader()->timing()->monotonicTimeToZeroBasedDocumentTime(seconds) * 1000.0; + ASSERT(timeStamp.secondsSinceEpoch().seconds() >= 0); + if (!timeStamp || !timeOrigin) + return 0; + + Seconds seconds = timeStamp - timeOrigin; + return Performance::reduceTimeResolution(seconds).milliseconds(); } -static bool passesTimingAllowCheck(const ResourceResponse& response, Document* requestingDocument) +Ref<PerformanceResourceTiming> PerformanceResourceTiming::create(MonotonicTime timeOrigin, ResourceTiming&& resourceTiming) { - RefPtr<SecurityOrigin> resourceOrigin = SecurityOrigin::create(response.url()); - if (resourceOrigin->isSameSchemeHostPort(requestingDocument->securityOrigin())) - return true; - - const String& timingAllowOriginString = response.httpHeaderField("timing-allow-origin"); - if (timingAllowOriginString.isEmpty() || equalIgnoringCase(timingAllowOriginString, "null")) - return false; - - if (timingAllowOriginString == "*") - return true; - - const String& securityOrigin = requestingDocument->securityOrigin()->toString(); - Vector<String> timingAllowOrigins; - timingAllowOriginString.split(" ", timingAllowOrigins); - for (size_t i = 0; i < timingAllowOrigins.size(); ++i) - if (timingAllowOrigins[i] == securityOrigin) - return true; - - return false; + return adoptRef(*new PerformanceResourceTiming(timeOrigin, WTFMove(resourceTiming))); } -PerformanceResourceTiming::PerformanceResourceTiming(const AtomicString& initiatorType, const ResourceRequest& request, const ResourceResponse& response, double initiationTime, double finishTime, Document* requestingDocument) - : PerformanceEntry(request.url().string(), "resource", monotonicTimeToDocumentMilliseconds(requestingDocument, initiationTime), monotonicTimeToDocumentMilliseconds(requestingDocument, finishTime)) - , m_initiatorType(initiatorType) - , m_timing(response.resourceLoadTiming()) - , m_finishTime(finishTime) - , m_didReuseConnection(response.connectionReused()) - , m_shouldReportDetails(passesTimingAllowCheck(response, requestingDocument)) - , m_requestingDocument(requestingDocument) +PerformanceResourceTiming::PerformanceResourceTiming(MonotonicTime timeOrigin, ResourceTiming&& resourceTiming) + : PerformanceEntry(PerformanceEntry::Type::Resource, resourceTiming.url().string(), ASCIILiteral("resource"), monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().startTime()), monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().responseEnd())) + , m_initiatorType(resourceTiming.initiator()) + , m_timeOrigin(timeOrigin) + , m_loadTiming(resourceTiming.loadTiming()) + , m_networkLoadTiming(resourceTiming.networkLoadTiming()) + , m_shouldReportDetails(resourceTiming.allowTimingDetails()) { } @@ -89,30 +72,33 @@ PerformanceResourceTiming::~PerformanceResourceTiming() { } -AtomicString PerformanceResourceTiming::initiatorType() const +double PerformanceResourceTiming::workerStart() const { - return m_initiatorType; + return 0.0; } double PerformanceResourceTiming::redirectStart() const { - // FIXME: Need to track and report redirects for resources. if (!m_shouldReportDetails) return 0.0; - return 0; + + return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.redirectStart()); } double PerformanceResourceTiming::redirectEnd() const { if (!m_shouldReportDetails) return 0.0; - return 0; + + return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.redirectEnd()); } double PerformanceResourceTiming::fetchStart() const { - // FIXME: This should be different depending on redirects. - return (startTime()); + // fetchStart is a required property. + ASSERT(m_loadTiming.fetchStart()); + + return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.fetchStart()); } double PerformanceResourceTiming::domainLookupStart() const @@ -120,10 +106,10 @@ double PerformanceResourceTiming::domainLookupStart() const if (!m_shouldReportDetails) return 0.0; - if (!m_timing || m_timing->dnsStart < 0) + if (m_networkLoadTiming.domainLookupStart <= 0) return fetchStart(); - return resourceTimeToDocumentMilliseconds(m_timing->dnsStart); + return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadTiming.domainLookupStart); } double PerformanceResourceTiming::domainLookupEnd() const @@ -131,10 +117,10 @@ double PerformanceResourceTiming::domainLookupEnd() const if (!m_shouldReportDetails) return 0.0; - if (!m_timing || m_timing->dnsEnd < 0) + if (m_networkLoadTiming.domainLookupEnd <= 0) return domainLookupStart(); - return resourceTimeToDocumentMilliseconds(m_timing->dnsEnd); + return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadTiming.domainLookupEnd); } double PerformanceResourceTiming::connectStart() const @@ -143,15 +129,15 @@ double PerformanceResourceTiming::connectStart() const return 0.0; // connectStart will be -1 when a network request is not made. - if (!m_timing || m_timing->connectStart < 0 || m_didReuseConnection) + if (m_networkLoadTiming.connectStart <= 0) return domainLookupEnd(); // connectStart includes any DNS time, so we may need to trim that off. - int connectStart = m_timing->connectStart; - if (m_timing->dnsEnd >= 0) - connectStart = m_timing->dnsEnd; + double connectStart = m_networkLoadTiming.connectStart; + if (m_networkLoadTiming.domainLookupEnd >= 0) + connectStart = m_networkLoadTiming.domainLookupEnd; - return resourceTimeToDocumentMilliseconds(connectStart); + return networkLoadTimeToDOMHighResTimeStamp(connectStart); } double PerformanceResourceTiming::connectEnd() const @@ -160,10 +146,10 @@ double PerformanceResourceTiming::connectEnd() const return 0.0; // connectStart will be -1 when a network request is not made. - if (!m_timing || m_timing->connectEnd < 0 || m_didReuseConnection) + if (m_networkLoadTiming.connectEnd <= 0) return connectStart(); - return resourceTimeToDocumentMilliseconds(m_timing->connectEnd); + return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadTiming.connectEnd); } double PerformanceResourceTiming::secureConnectionStart() const @@ -171,10 +157,10 @@ double PerformanceResourceTiming::secureConnectionStart() const if (!m_shouldReportDetails) return 0.0; - if (!m_timing || m_timing->sslStart < 0) // Secure connection not negotiated. + if (m_networkLoadTiming.secureConnectionStart < 0) // Secure connection not negotiated. return 0.0; - return resourceTimeToDocumentMilliseconds(m_timing->sslStart); + return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadTiming.secureConnectionStart); } double PerformanceResourceTiming::requestStart() const @@ -182,10 +168,11 @@ double PerformanceResourceTiming::requestStart() const if (!m_shouldReportDetails) return 0.0; - if (!m_timing) + // requestStart is 0 when a network request is not made. + if (m_networkLoadTiming.requestStart <= 0) return connectEnd(); - return resourceTimeToDocumentMilliseconds(m_timing->sendStart); + return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadTiming.requestStart); } double PerformanceResourceTiming::responseStart() const @@ -193,27 +180,29 @@ double PerformanceResourceTiming::responseStart() const if (!m_shouldReportDetails) return 0.0; - if (!m_timing) + // responseStart is 0 when a network request is not made. + if (m_networkLoadTiming.responseStart <= 0) return requestStart(); - // FIXME: This number isn't exactly correct. See the notes in PerformanceTiming::responseStart(). - return resourceTimeToDocumentMilliseconds(m_timing->receiveHeadersEnd); + + return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadTiming.responseStart); } double PerformanceResourceTiming::responseEnd() const { - if (!m_finishTime) - return responseStart(); + // responseEnd is a required property. + ASSERT(m_loadTiming.responseEnd()); - return monotonicTimeToDocumentMilliseconds(m_requestingDocument.get(), m_finishTime); + return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.responseEnd()); } -double PerformanceResourceTiming::resourceTimeToDocumentMilliseconds(int deltaMilliseconds) const +double PerformanceResourceTiming::networkLoadTimeToDOMHighResTimeStamp(double deltaMilliseconds) const { - if (!deltaMilliseconds) - return 0.0; - return monotonicTimeToDocumentMilliseconds(m_requestingDocument.get(), m_timing->requestTime) + deltaMilliseconds; + ASSERT(deltaMilliseconds); + MonotonicTime combined = m_loadTiming.fetchStart() + Seconds::fromMilliseconds(deltaMilliseconds); + Seconds delta = combined - m_timeOrigin; + return Performance::reduceTimeResolution(delta).milliseconds(); } } // namespace WebCore -#endif // ENABLE(RESOURCE_TIMING) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceResourceTiming.h b/Source/WebCore/page/PerformanceResourceTiming.h index 6276944ca..0eebf1e9d 100644 --- a/Source/WebCore/page/PerformanceResourceTiming.h +++ b/Source/WebCore/page/PerformanceResourceTiming.h @@ -29,33 +29,27 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceResourceTiming_h -#define PerformanceResourceTiming_h +#pragma once -#if ENABLE(RESOURCE_TIMING) +#if ENABLE(WEB_TIMING) +#include "LoadTiming.h" +#include "NetworkLoadTiming.h" #include "PerformanceEntry.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> namespace WebCore { -class Document; -class URL; -class ResourceLoadTiming; -class ResourceRequest; -class ResourceResponse; +class ResourceTiming; -class PerformanceResourceTiming : public PerformanceEntry { +class PerformanceResourceTiming final : public PerformanceEntry { public: - static PassRefPtr<PerformanceResourceTiming> create(const AtomicString& initiatorType, const ResourceRequest& request, const ResourceResponse& response, double initiationTime, double finishTime, Document* requestingDocument) - { - return adoptRef(new PerformanceResourceTiming(initiatorType, request, response, initiationTime, finishTime, requestingDocument)); - } + static Ref<PerformanceResourceTiming> create(MonotonicTime timeOrigin, ResourceTiming&&); - AtomicString initiatorType() const; + AtomicString initiatorType() const { return m_initiatorType; } + double workerStart() const; double redirectStart() const; double redirectEnd() const; double fetchStart() const; @@ -68,24 +62,25 @@ public: double responseStart() const; double responseEnd() const; - virtual bool isResource() { return true; } + bool isResource() const override { return true; } private: - PerformanceResourceTiming(const AtomicString& initatorType, const ResourceRequest&, const ResourceResponse&, double initiationTime, double finishTime, Document*); + PerformanceResourceTiming(MonotonicTime timeOrigin, ResourceTiming&&); ~PerformanceResourceTiming(); - double resourceTimeToDocumentMilliseconds(int deltaMilliseconds) const; + double networkLoadTimeToDOMHighResTimeStamp(double deltaMilliseconds) const; AtomicString m_initiatorType; - RefPtr<ResourceLoadTiming> m_timing; - double m_finishTime; - bool m_didReuseConnection; + MonotonicTime m_timeOrigin; + LoadTiming m_loadTiming; + NetworkLoadTiming m_networkLoadTiming; bool m_shouldReportDetails; - RefPtr<Document> m_requestingDocument; }; -} +} // namespace WebCore -#endif // ENABLE(RESOURCE_TIMING) +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::PerformanceResourceTiming) + static bool isType(const WebCore::PerformanceEntry& entry) { return entry.isResource(); } +SPECIALIZE_TYPE_TRAITS_END() -#endif // !defined(PerformanceResourceTiming_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceResourceTiming.idl b/Source/WebCore/page/PerformanceResourceTiming.idl index a0e48633b..ebf1e2035 100644 --- a/Source/WebCore/page/PerformanceResourceTiming.idl +++ b/Source/WebCore/page/PerformanceResourceTiming.idl @@ -28,21 +28,29 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// See: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ResourceTiming/Overview.html +// https://w3c.github.io/resource-timing/ + +typedef double DOMHighResTimeStamp; + [ - Conditional=RESOURCE_TIMING, + Conditional=WEB_TIMING, + EnabledAtRuntime=ResourceTiming, + Exposed=(Window,Worker), ] interface PerformanceResourceTiming : PerformanceEntry { readonly attribute DOMString initiatorType; - readonly attribute double redirectStart; - readonly attribute double redirectEnd; - readonly attribute double fetchStart; - readonly attribute double domainLookupStart; - readonly attribute double domainLookupEnd; - readonly attribute double connectStart; - readonly attribute double connectEnd; - readonly attribute double secureConnectionStart; - readonly attribute double requestStart; - readonly attribute double responseStart; - readonly attribute double responseEnd; + readonly attribute DOMHighResTimeStamp workerStart; + readonly attribute DOMHighResTimeStamp redirectStart; + readonly attribute DOMHighResTimeStamp redirectEnd; + readonly attribute DOMHighResTimeStamp fetchStart; + readonly attribute DOMHighResTimeStamp domainLookupStart; + readonly attribute DOMHighResTimeStamp domainLookupEnd; + readonly attribute DOMHighResTimeStamp connectStart; + readonly attribute DOMHighResTimeStamp connectEnd; + readonly attribute DOMHighResTimeStamp secureConnectionStart; + readonly attribute DOMHighResTimeStamp requestStart; + readonly attribute DOMHighResTimeStamp responseStart; + readonly attribute DOMHighResTimeStamp responseEnd; + + serializer = { inherit, attribute }; }; diff --git a/Source/WebCore/page/PerformanceTiming.cpp b/Source/WebCore/page/PerformanceTiming.cpp index c14080ffb..2fb5ead71 100644 --- a/Source/WebCore/page/PerformanceTiming.cpp +++ b/Source/WebCore/page/PerformanceTiming.cpp @@ -34,23 +34,18 @@ #if ENABLE(WEB_TIMING) #include "Document.h" -#include "DocumentLoadTiming.h" #include "DocumentLoader.h" #include "DocumentTiming.h" #include "Frame.h" #include "FrameLoader.h" -#include "ResourceLoadTiming.h" +#include "LoadTiming.h" +#include "NetworkLoadTiming.h" +#include "Performance.h" #include "ResourceResponse.h" #include <wtf/CurrentTime.h> namespace WebCore { -static unsigned long long toIntegerMilliseconds(double seconds) -{ - ASSERT(seconds >= 0); - return static_cast<unsigned long long>(seconds * 1000.0); -} - PerformanceTiming::PerformanceTiming(Frame* frame) : DOMWindowProperty(frame) { @@ -58,16 +53,16 @@ PerformanceTiming::PerformanceTiming(Frame* frame) unsigned long long PerformanceTiming::navigationStart() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; - return monotonicTimeToIntegerMilliseconds(timing->navigationStart()); + return monotonicTimeToIntegerMilliseconds(timing->startTime()); } unsigned long long PerformanceTiming::unloadEventStart() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -79,7 +74,7 @@ unsigned long long PerformanceTiming::unloadEventStart() const unsigned long long PerformanceTiming::unloadEventEnd() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -91,7 +86,7 @@ unsigned long long PerformanceTiming::unloadEventEnd() const unsigned long long PerformanceTiming::redirectStart() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -103,7 +98,7 @@ unsigned long long PerformanceTiming::redirectStart() const unsigned long long PerformanceTiming::redirectEnd() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -115,7 +110,7 @@ unsigned long long PerformanceTiming::redirectEnd() const unsigned long long PerformanceTiming::fetchStart() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -124,32 +119,34 @@ unsigned long long PerformanceTiming::fetchStart() const unsigned long long PerformanceTiming::domainLookupStart() const { - ResourceLoadTiming* timing = resourceLoadTiming(); - if (!timing) + DocumentLoader* loader = documentLoader(); + if (!loader) return fetchStart(); - + + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + // This will be -1 when a DNS request is not performed. // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart. - int dnsStart = timing->dnsStart; - if (dnsStart < 0) + if (timing.domainLookupStart < 0) return fetchStart(); - return resourceLoadTimeRelativeToAbsolute(dnsStart); + return resourceLoadTimeRelativeToFetchStart(timing.domainLookupStart); } unsigned long long PerformanceTiming::domainLookupEnd() const { - ResourceLoadTiming* timing = resourceLoadTiming(); - if (!timing) + DocumentLoader* loader = documentLoader(); + if (!loader) return domainLookupStart(); - + + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + // This will be -1 when a DNS request is not performed. // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart. - int dnsEnd = timing->dnsEnd; - if (dnsEnd < 0) + if (timing.domainLookupEnd < 0) return domainLookupStart(); - return resourceLoadTimeRelativeToAbsolute(dnsEnd); + return resourceLoadTimeRelativeToFetchStart(timing.domainLookupEnd); } unsigned long long PerformanceTiming::connectStart() const @@ -158,22 +155,20 @@ unsigned long long PerformanceTiming::connectStart() const if (!loader) return domainLookupEnd(); - ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); - if (!timing) - return domainLookupEnd(); - + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + // connectStart will be -1 when a network request is not made. // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd. - int connectStart = timing->connectStart; - if (connectStart < 0 || loader->response().connectionReused()) + double connectStart = timing.connectStart; + if (connectStart < 0) return domainLookupEnd(); - // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's + // NetworkLoadTiming's connect phase includes DNS, however Navigation Timing's // connect phase should not. So if there is DNS time, trim it from the start. - if (timing->dnsEnd >= 0 && timing->dnsEnd > connectStart) - connectStart = timing->dnsEnd; + if (timing.domainLookupEnd >= 0 && timing.domainLookupEnd > connectStart) + connectStart = timing.domainLookupEnd; - return resourceLoadTimeRelativeToAbsolute(connectStart); + return resourceLoadTimeRelativeToFetchStart(connectStart); } unsigned long long PerformanceTiming::connectEnd() const @@ -182,17 +177,14 @@ unsigned long long PerformanceTiming::connectEnd() const if (!loader) return connectStart(); - ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); - if (!timing) - return connectStart(); - + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + // connectEnd will be -1 when a network request is not made. // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart. - int connectEnd = timing->connectEnd; - if (connectEnd < 0 || loader->response().connectionReused()) + if (timing.connectEnd < 0) return connectStart(); - return resourceLoadTimeRelativeToAbsolute(connectEnd); + return resourceLoadTimeRelativeToFetchStart(timing.connectEnd); } unsigned long long PerformanceTiming::secureConnectionStart() const @@ -201,46 +193,41 @@ unsigned long long PerformanceTiming::secureConnectionStart() const if (!loader) return 0; - ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); - if (!timing) + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + + if (timing.secureConnectionStart < 0) return 0; - int sslStart = timing->sslStart; - if (sslStart < 0) - return 0; - - return resourceLoadTimeRelativeToAbsolute(sslStart); + return resourceLoadTimeRelativeToFetchStart(timing.secureConnectionStart); } unsigned long long PerformanceTiming::requestStart() const { - ResourceLoadTiming* timing = resourceLoadTiming(); - if (!timing) + DocumentLoader* loader = documentLoader(); + if (!loader) return connectEnd(); - - ASSERT(timing->sendStart >= 0); - return resourceLoadTimeRelativeToAbsolute(timing->sendStart); + + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + + ASSERT(timing.requestStart >= 0); + return resourceLoadTimeRelativeToFetchStart(timing.requestStart); } unsigned long long PerformanceTiming::responseStart() const { - ResourceLoadTiming* timing = resourceLoadTiming(); - if (!timing) + DocumentLoader* loader = documentLoader(); + if (!loader) return requestStart(); - // FIXME: Response start needs to be the time of the first received byte. - // However, the ResourceLoadTiming API currently only supports the time - // the last header byte was received. For many responses with reasonable - // sized cookies, the HTTP headers fit into a single packet so this time - // is basically equivalent. But for some responses, particularly those with - // headers larger than a single packet, this time will be too late. - ASSERT(timing->receiveHeadersEnd >= 0); - return resourceLoadTimeRelativeToAbsolute(timing->receiveHeadersEnd); + const NetworkLoadTiming& timing = loader->response().networkLoadTiming(); + + ASSERT(timing.responseStart >= 0); + return resourceLoadTimeRelativeToFetchStart(timing.responseStart); } unsigned long long PerformanceTiming::responseEnd() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -294,7 +281,7 @@ unsigned long long PerformanceTiming::domComplete() const unsigned long long PerformanceTiming::loadEventStart() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -303,7 +290,7 @@ unsigned long long PerformanceTiming::loadEventStart() const unsigned long long PerformanceTiming::loadEventEnd() const { - DocumentLoadTiming* timing = documentLoadTiming(); + LoadTiming* timing = loadTiming(); if (!timing) return 0; @@ -313,7 +300,7 @@ unsigned long long PerformanceTiming::loadEventEnd() const DocumentLoader* PerformanceTiming::documentLoader() const { if (!m_frame) - return 0; + return nullptr; return m_frame->loader().documentLoader(); } @@ -321,47 +308,49 @@ DocumentLoader* PerformanceTiming::documentLoader() const const DocumentTiming* PerformanceTiming::documentTiming() const { if (!m_frame) - return 0; + return nullptr; Document* document = m_frame->document(); if (!document) - return 0; + return nullptr; - return document->timing(); + return &document->timing(); } -DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const +LoadTiming* PerformanceTiming::loadTiming() const { DocumentLoader* loader = documentLoader(); if (!loader) - return 0; + return nullptr; - return loader->timing(); + return &loader->timing(); } -ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const +unsigned long long PerformanceTiming::resourceLoadTimeRelativeToFetchStart(double relativeMilliseconds) const { - DocumentLoader* loader = documentLoader(); - if (!loader) + ASSERT(relativeMilliseconds >= 0); + + LoadTiming* timing = loadTiming(); + if (!timing) return 0; - return loader->response().resourceLoadTiming(); + WallTime fetchStart = timing->monotonicTimeToPseudoWallTime(timing->fetchStart()); + WallTime combined = fetchStart + Seconds::fromMilliseconds(relativeMilliseconds); + Seconds reduced = Performance::reduceTimeResolution(combined.secondsSinceEpoch()); + return static_cast<unsigned long long>(reduced.milliseconds()); } -unsigned long long PerformanceTiming::resourceLoadTimeRelativeToAbsolute(int relativeMilliseconds) const +unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(MonotonicTime timeStamp) const { - ASSERT(relativeMilliseconds >= 0); - ResourceLoadTiming* resourceTiming = resourceLoadTiming(); - ASSERT(resourceTiming); - return monotonicTimeToIntegerMilliseconds(resourceTiming->convertResourceLoadTimeToMonotonicTime(relativeMilliseconds)); -} + ASSERT(timeStamp.secondsSinceEpoch().seconds() >= 0); -unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(double monotonicSeconds) const -{ - ASSERT(monotonicSeconds >= 0); - const DocumentLoadTiming* timing = documentLoadTiming(); - ASSERT(timing); - return toIntegerMilliseconds(timing->monotonicTimeToPseudoWallTime(monotonicSeconds)); + LoadTiming* timing = loadTiming(); + if (!timing) + return 0; + + WallTime wallTime = timing->monotonicTimeToPseudoWallTime(timeStamp); + Seconds reduced = Performance::reduceTimeResolution(wallTime.secondsSinceEpoch()); + return static_cast<unsigned long long>(reduced.milliseconds()); } } // namespace WebCore diff --git a/Source/WebCore/page/PerformanceTiming.h b/Source/WebCore/page/PerformanceTiming.h index 30b80b967..0499f7d64 100644 --- a/Source/WebCore/page/PerformanceTiming.h +++ b/Source/WebCore/page/PerformanceTiming.h @@ -28,26 +28,25 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceTiming_h -#define PerformanceTiming_h +#pragma once #if ENABLE(WEB_TIMING) #include "DOMWindowProperty.h" -#include <wtf/PassRefPtr.h> +#include <wtf/MonotonicTime.h> +#include <wtf/Ref.h> #include <wtf/RefCounted.h> namespace WebCore { -class DocumentLoadTiming; class DocumentLoader; -struct DocumentTiming; class Frame; -class ResourceLoadTiming; +class LoadTiming; +struct DocumentTiming; class PerformanceTiming : public RefCounted<PerformanceTiming>, public DOMWindowProperty { public: - static PassRefPtr<PerformanceTiming> create(Frame* frame) { return adoptRef(new PerformanceTiming(frame)); } + static Ref<PerformanceTiming> create(Frame* frame) { return adoptRef(*new PerformanceTiming(frame)); } unsigned long long navigationStart() const; unsigned long long unloadEventStart() const; @@ -76,13 +75,11 @@ private: const DocumentTiming* documentTiming() const; DocumentLoader* documentLoader() const; - DocumentLoadTiming* documentLoadTiming() const; - ResourceLoadTiming* resourceLoadTiming() const; - unsigned long long resourceLoadTimeRelativeToAbsolute(int) const; - unsigned long long monotonicTimeToIntegerMilliseconds(double) const; + LoadTiming* loadTiming() const; + unsigned long long resourceLoadTimeRelativeToFetchStart(double) const; + unsigned long long monotonicTimeToIntegerMilliseconds(MonotonicTime) const; }; -} +} // namespace WebCore -#endif // !ENABLE(WEB_TIMING) -#endif // !defined(PerformanceTiming_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceTiming.idl b/Source/WebCore/page/PerformanceTiming.idl index 9f4f60c40..91ece5d82 100644 --- a/Source/WebCore/page/PerformanceTiming.idl +++ b/Source/WebCore/page/PerformanceTiming.idl @@ -28,7 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// See: http://dev.w3.org/2006/webapi/WebTiming/ +// See: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html [ Conditional=WEB_TIMING, ] interface PerformanceTiming { @@ -53,5 +53,6 @@ readonly attribute unsigned long long domComplete; readonly attribute unsigned long long loadEventStart; readonly attribute unsigned long long loadEventEnd; -}; + serializer = { attribute }; +}; diff --git a/Source/WebCore/page/PerformanceUserTiming.cpp b/Source/WebCore/page/PerformanceUserTiming.cpp index e2af43ed8..6c933d82b 100644 --- a/Source/WebCore/page/PerformanceUserTiming.cpp +++ b/Source/WebCore/page/PerformanceUserTiming.cpp @@ -26,69 +26,69 @@ #include "config.h" #include "PerformanceUserTiming.h" -#if ENABLE(USER_TIMING) +#if ENABLE(WEB_TIMING) +#include "Document.h" +#include "ExceptionCode.h" #include "Performance.h" -#include "PerformanceMark.h" -#include "PerformanceMeasure.h" +#include "PerformanceTiming.h" +#include <array> +#include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/dtoa/utils.h> -#include <wtf/text/WTFString.h> namespace WebCore { namespace { -typedef HashMap<String, NavigationTimingFunction> RestrictedKeyMap; -static RestrictedKeyMap restrictedKeyMap() +typedef unsigned long long (PerformanceTiming::*NavigationTimingFunction)() const; + +static NavigationTimingFunction restrictedMarkFunction(const String& markName) { - DEFINE_STATIC_LOCAL(RestrictedKeyMap, map, ()); - if (map.isEmpty()) { - map.add("navigationStart", &PerformanceTiming::navigationStart); - map.add("unloadEventStart", &PerformanceTiming::unloadEventStart); - map.add("unloadEventEnd", &PerformanceTiming::unloadEventEnd); - map.add("redirectStart", &PerformanceTiming::redirectStart); - map.add("redirectEnd", &PerformanceTiming::redirectEnd); - map.add("fetchStart", &PerformanceTiming::fetchStart); - map.add("domainLookupStart", &PerformanceTiming::domainLookupStart); - map.add("domainLookupEnd", &PerformanceTiming::domainLookupEnd); - map.add("connectStart", &PerformanceTiming::connectStart); - map.add("connectEnd", &PerformanceTiming::connectEnd); - map.add("secureConnectionStart", &PerformanceTiming::secureConnectionStart); - map.add("requestStart", &PerformanceTiming::requestStart); - map.add("responseStart", &PerformanceTiming::responseStart); - map.add("responseEnd", &PerformanceTiming::responseEnd); - map.add("domLoading", &PerformanceTiming::domLoading); - map.add("domInteractive", &PerformanceTiming::domInteractive); - map.add("domContentLoadedEventStart", &PerformanceTiming::domContentLoadedEventStart); - map.add("domContentLoadedEventEnd", &PerformanceTiming::domContentLoadedEventEnd); - map.add("domComplete", &PerformanceTiming::domComplete); - map.add("loadEventStart", &PerformanceTiming::loadEventStart); - map.add("loadEventEnd", &PerformanceTiming::loadEventEnd); + ASSERT(isMainThread()); + + using MapPair = std::pair<ASCIILiteral, NavigationTimingFunction>; + static const std::array<MapPair, 21> pairs = { { + MapPair { ASCIILiteral("navigationStart"), &PerformanceTiming::navigationStart }, + MapPair { ASCIILiteral("unloadEventStart"), &PerformanceTiming::unloadEventStart }, + MapPair { ASCIILiteral("unloadEventEnd"), &PerformanceTiming::unloadEventEnd }, + MapPair { ASCIILiteral("redirectStart"), &PerformanceTiming::redirectStart }, + MapPair { ASCIILiteral("redirectEnd"), &PerformanceTiming::redirectEnd }, + MapPair { ASCIILiteral("fetchStart"), &PerformanceTiming::fetchStart }, + MapPair { ASCIILiteral("domainLookupStart"), &PerformanceTiming::domainLookupStart }, + MapPair { ASCIILiteral("domainLookupEnd"), &PerformanceTiming::domainLookupEnd }, + MapPair { ASCIILiteral("connectStart"), &PerformanceTiming::connectStart }, + MapPair { ASCIILiteral("connectEnd"), &PerformanceTiming::connectEnd }, + MapPair { ASCIILiteral("secureConnectionStart"), &PerformanceTiming::secureConnectionStart }, + MapPair { ASCIILiteral("requestStart"), &PerformanceTiming::requestStart }, + MapPair { ASCIILiteral("responseStart"), &PerformanceTiming::responseStart }, + MapPair { ASCIILiteral("responseEnd"), &PerformanceTiming::responseEnd }, + MapPair { ASCIILiteral("domLoading"), &PerformanceTiming::domLoading }, + MapPair { ASCIILiteral("domInteractive"), &PerformanceTiming::domInteractive }, + MapPair { ASCIILiteral("domContentLoadedEventStart"), &PerformanceTiming::domContentLoadedEventStart }, + MapPair { ASCIILiteral("domContentLoadedEventEnd"), &PerformanceTiming::domContentLoadedEventEnd }, + MapPair { ASCIILiteral("domComplete"), &PerformanceTiming::domComplete }, + MapPair { ASCIILiteral("loadEventStart"), &PerformanceTiming::loadEventStart }, + MapPair { ASCIILiteral("loadEventEnd"), &PerformanceTiming::loadEventEnd }, + } }; + + static NeverDestroyed<HashMap<String, NavigationTimingFunction>> map; + if (map.get().isEmpty()) { + for (auto& pair : pairs) + map.get().add(pair.first, pair.second); } - return map; + + return map.get().get(markName); } } // namespace anonymous -UserTiming::UserTiming(Performance* performance) +UserTiming::UserTiming(Performance& performance) : m_performance(performance) { } -static void insertPerformanceEntry(PerformanceEntryMap& performanceEntryMap, PassRefPtr<PerformanceEntry> performanceEntry) -{ - RefPtr<PerformanceEntry> entry = performanceEntry; - PerformanceEntryMap::iterator it = performanceEntryMap.find(entry->name()); - if (it != performanceEntryMap.end()) - it->value.append(entry); - else { - Vector<RefPtr<PerformanceEntry> > v(1); - v[0] = entry; - performanceEntryMap.set(entry->name(), v); - } -} - -static void clearPeformanceEntries(PerformanceEntryMap& performanceEntryMap, const String& name) +static void clearPerformanceEntries(PerformanceEntryMap& performanceEntryMap, const String& name) { if (name.isNull()) { performanceEntryMap.clear(); @@ -98,113 +98,111 @@ static void clearPeformanceEntries(PerformanceEntryMap& performanceEntryMap, con performanceEntryMap.remove(name); } -void UserTiming::mark(const String& markName, ExceptionCode& ec) +ExceptionOr<Ref<PerformanceMark>> UserTiming::mark(const String& markName) { - ec = 0; - if (restrictedKeyMap().contains(markName)) { - ec = SYNTAX_ERR; - return; + if (is<Document>(m_performance.scriptExecutionContext())) { + if (restrictedMarkFunction(markName)) + return Exception { SYNTAX_ERR }; } - double startTime = m_performance->now(); - insertPerformanceEntry(m_marksMap, PerformanceMark::create(markName, startTime)); + auto& performanceEntryList = m_marksMap.ensure(markName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value; + auto entry = PerformanceMark::create(markName, m_performance.now()); + performanceEntryList.append(entry.copyRef()); + return WTFMove(entry); } void UserTiming::clearMarks(const String& markName) { - clearPeformanceEntries(m_marksMap, markName); + clearPerformanceEntries(m_marksMap, markName); } -double UserTiming::findExistingMarkStartTime(const String& markName, ExceptionCode& ec) +ExceptionOr<double> UserTiming::findExistingMarkStartTime(const String& markName) { - ec = 0; - if (m_marksMap.contains(markName)) return m_marksMap.get(markName).last()->startTime(); - if (restrictedKeyMap().contains(markName)) { - double value = static_cast<double>((m_performance->timing()->*(restrictedKeyMap().get(markName)))()); - if (!value) { - ec = INVALID_ACCESS_ERR; - return 0.0; - } - return value - m_performance->timing()->navigationStart(); + PerformanceTiming* timing = m_performance.timing(); + if (!timing) + return 0.0; + + if (auto function = restrictedMarkFunction(markName)) { + double value = static_cast<double>(((*timing).*(function))()); + if (!value) + return Exception { INVALID_ACCESS_ERR }; + return value - timing->navigationStart(); } - ec = SYNTAX_ERR; - return 0.0; + return Exception { SYNTAX_ERR }; } -void UserTiming::measure(const String& measureName, const String& startMark, const String& endMark, ExceptionCode& ec) +ExceptionOr<Ref<PerformanceMeasure>> UserTiming::measure(const String& measureName, const String& startMark, const String& endMark) { double startTime = 0.0; double endTime = 0.0; if (startMark.isNull()) - endTime = m_performance->now(); + endTime = m_performance.now(); else if (endMark.isNull()) { - endTime = m_performance->now(); - startTime = findExistingMarkStartTime(startMark, ec); - if (ec) - return; + endTime = m_performance.now(); + auto startMarkResult = findExistingMarkStartTime(startMark); + if (startMarkResult.hasException()) + return startMarkResult.releaseException(); + startTime = startMarkResult.releaseReturnValue(); } else { - endTime = findExistingMarkStartTime(endMark, ec); - if (ec) - return; - startTime = findExistingMarkStartTime(startMark, ec); - if (ec) - return; + auto endMarkResult = findExistingMarkStartTime(endMark); + if (endMarkResult.hasException()) + return endMarkResult.releaseException(); + auto startMarkResult = findExistingMarkStartTime(startMark); + if (startMarkResult.hasException()) + return startMarkResult.releaseException(); + startTime = startMarkResult.releaseReturnValue(); + endTime = endMarkResult.releaseReturnValue(); } - insertPerformanceEntry(m_measuresMap, PerformanceMeasure::create(measureName, startTime, endTime)); + auto& performanceEntryList = m_measuresMap.ensure(measureName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value; + auto entry = PerformanceMeasure::create(measureName, startTime, endTime); + performanceEntryList.append(entry.copyRef()); + return WTFMove(entry); } void UserTiming::clearMeasures(const String& measureName) { - clearPeformanceEntries(m_measuresMap, measureName); + clearPerformanceEntries(m_measuresMap, measureName); } -static Vector<RefPtr<PerformanceEntry> > convertToEntrySequence(const PerformanceEntryMap& performanceEntryMap) +static Vector<RefPtr<PerformanceEntry>> convertToEntrySequence(const PerformanceEntryMap& performanceEntryMap) { - Vector<RefPtr<PerformanceEntry> > entries; - - for (PerformanceEntryMap::const_iterator it = performanceEntryMap.begin(); it != performanceEntryMap.end(); ++it) - entries.appendVector(it->value); - + Vector<RefPtr<PerformanceEntry>> entries; + for (auto& entry : performanceEntryMap.values()) + entries.appendVector(entry); return entries; } -static Vector<RefPtr<PerformanceEntry> > getEntrySequenceByName(const PerformanceEntryMap& performanceEntryMap, const String& name) +static Vector<RefPtr<PerformanceEntry>> getEntrySequenceByName(const PerformanceEntryMap& performanceEntryMap, const String& name) { - Vector<RefPtr<PerformanceEntry> > entries; - - PerformanceEntryMap::const_iterator it = performanceEntryMap.find(name); - if (it != performanceEntryMap.end()) - entries.appendVector(it->value); - - return entries; + return performanceEntryMap.get(name); } -Vector<RefPtr<PerformanceEntry> > UserTiming::getMarks() const +Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks() const { return convertToEntrySequence(m_marksMap); } -Vector<RefPtr<PerformanceEntry> > UserTiming::getMarks(const String& name) const +Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks(const String& name) const { return getEntrySequenceByName(m_marksMap, name); } -Vector<RefPtr<PerformanceEntry> > UserTiming::getMeasures() const +Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures() const { return convertToEntrySequence(m_measuresMap); } -Vector<RefPtr<PerformanceEntry> > UserTiming::getMeasures(const String& name) const +Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures(const String& name) const { return getEntrySequenceByName(m_measuresMap, name); } } // namespace WebCore -#endif // ENABLE(USER_TIMING) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PerformanceUserTiming.h b/Source/WebCore/page/PerformanceUserTiming.h index 14c8c3f9b..783137378 100644 --- a/Source/WebCore/page/PerformanceUserTiming.h +++ b/Source/WebCore/page/PerformanceUserTiming.h @@ -23,56 +23,46 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PerformanceUserTiming_h -#define PerformanceUserTiming_h +#pragma once -#if ENABLE(USER_TIMING) +#if ENABLE(WEB_TIMING) -#include "EventException.h" -#include "ExceptionCode.h" -#include "Performance.h" -#include "PerformanceTiming.h" +#include "ExceptionOr.h" +#include "PerformanceMark.h" +#include "PerformanceMeasure.h" #include <wtf/HashMap.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> #include <wtf/text/StringHash.h> -#include <wtf/text/WTFString.h> namespace WebCore { class Performance; -class PerformanceEntry; -typedef unsigned long long (PerformanceTiming::*NavigationTimingFunction)() const; -typedef HashMap<String, Vector<RefPtr<PerformanceEntry> > > PerformanceEntryMap; +using PerformanceEntryMap = HashMap<String, Vector<RefPtr<PerformanceEntry>>>; -class UserTiming : public RefCounted<UserTiming> { +class UserTiming { public: - static PassRefPtr<UserTiming> create(Performance* performance) { return adoptRef(new UserTiming(performance)); } + explicit UserTiming(Performance&); - void mark(const String& markName, ExceptionCode&); + ExceptionOr<Ref<PerformanceMark>> mark(const String& markName); void clearMarks(const String& markName); - void measure(const String& measureName, const String& startMark, const String& endMark, ExceptionCode&); + ExceptionOr<Ref<PerformanceMeasure>> measure(const String& measureName, const String& startMark, const String& endMark); void clearMeasures(const String& measureName); - Vector<RefPtr<PerformanceEntry> > getMarks() const; - Vector<RefPtr<PerformanceEntry> > getMeasures() const; + Vector<RefPtr<PerformanceEntry>> getMarks() const; + Vector<RefPtr<PerformanceEntry>> getMeasures() const; - Vector<RefPtr<PerformanceEntry> > getMarks(const String& name) const; - Vector<RefPtr<PerformanceEntry> > getMeasures(const String& name) const; + Vector<RefPtr<PerformanceEntry>> getMarks(const String& name) const; + Vector<RefPtr<PerformanceEntry>> getMeasures(const String& name) const; private: - explicit UserTiming(Performance*); + ExceptionOr<double> findExistingMarkStartTime(const String& markName); - double findExistingMarkStartTime(const String& markName, ExceptionCode&); - Performance* m_performance; + Performance& m_performance; PerformanceEntryMap m_marksMap; PerformanceEntryMap m_measuresMap; }; } -#endif // ENABLE(USER_TIMING) - -#endif // !defined(PerformanceUserTiming_h) +#endif // ENABLE(WEB_TIMING) diff --git a/Source/WebCore/page/PlugInClient.h b/Source/WebCore/page/PlugInClient.h index 6582c4efe..25a6962cb 100644 --- a/Source/WebCore/page/PlugInClient.h +++ b/Source/WebCore/page/PlugInClient.h @@ -23,9 +23,9 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PlugInClient_h -#define PlugInClient_h +#pragma once +#include "SessionID.h" #include <wtf/Forward.h> namespace WebCore { @@ -34,11 +34,10 @@ class PlugInClient { public: virtual void pageDestroyed() = 0; virtual bool shouldAutoStartFromOrigin(const String& pageOrigin, const String& pluginOrigin, const String& mimeType) = 0; - virtual void didStartFromOrigin(const String& pageOrigin, const String& pluginOrigin, const String& mimeType) = 0; + virtual void didStartFromOrigin(const String& pageOrigin, const String& pluginOrigin, const String& mimeType, SessionID) = 0; protected: virtual ~PlugInClient() { } }; -} -#endif // PlugInClient_h +} // namespace WebCore diff --git a/Source/WebCore/page/PointerLockController.cpp b/Source/WebCore/page/PointerLockController.cpp index 5d6f5a5ea..ded552ebd 100644 --- a/Source/WebCore/page/PointerLockController.cpp +++ b/Source/WebCore/page/PointerLockController.cpp @@ -25,81 +25,109 @@ #include "config.h" #include "PointerLockController.h" +#if ENABLE(POINTER_LOCK) + #include "Chrome.h" #include "ChromeClient.h" #include "Element.h" #include "Event.h" +#include "EventNames.h" #include "Page.h" #include "PlatformMouseEvent.h" +#include "RuntimeEnabledFeatures.h" +#include "ScriptController.h" #include "VoidCallback.h" -#if ENABLE(POINTER_LOCK) namespace WebCore { -PointerLockController::PointerLockController(Page* page) +PointerLockController::PointerLockController(Page& page) : m_page(page) { } -PassOwnPtr<PointerLockController> PointerLockController::create(Page* page) -{ - return adoptPtr(new PointerLockController(page)); -} - void PointerLockController::requestPointerLock(Element* target) { - if (!target || !target->inDocument() || m_documentOfRemovedElementWhileWaitingForUnlock) { - enqueueEvent(eventNames().webkitpointerlockerrorEvent, target); + if (!target || !target->isConnected() || m_documentOfRemovedElementWhileWaitingForUnlock) { + enqueueEvent(eventNames().pointerlockerrorEvent, target); + return; + } + + if (m_documentAllowedToRelockWithoutUserGesture != &target->document() && !ScriptController::processingUserGesture()) { + enqueueEvent(eventNames().pointerlockerrorEvent, target); return; } if (target->document().isSandboxed(SandboxPointerLock)) { // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists. - target->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked pointer lock on an element because the element's frame is sandboxed and the 'allow-pointer-lock' permission is not set."); - enqueueEvent(eventNames().webkitpointerlockerrorEvent, target); + target->document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, ASCIILiteral("Blocked pointer lock on an element because the element's frame is sandboxed and the 'allow-pointer-lock' permission is not set.")); + enqueueEvent(eventNames().pointerlockerrorEvent, target); return; } if (m_element) { if (&m_element->document() != &target->document()) { - enqueueEvent(eventNames().webkitpointerlockerrorEvent, target); + enqueueEvent(eventNames().pointerlockerrorEvent, target); return; } - enqueueEvent(eventNames().webkitpointerlockchangeEvent, target); m_element = target; - } else if (m_page->chrome().client().requestPointerLock()) { + enqueueEvent(eventNames().pointerlockchangeEvent, target); + } else { m_lockPending = true; m_element = target; - } else { - enqueueEvent(eventNames().webkitpointerlockerrorEvent, target); + if (!m_page.chrome().client().requestPointerLock()) { + clearElement(); + enqueueEvent(eventNames().pointerlockerrorEvent, target); + } } } void PointerLockController::requestPointerUnlock() { - return m_page->chrome().client().requestPointerUnlock(); + if (!m_element) + return; + + m_unlockPending = true; + m_page.chrome().client().requestPointerUnlock(); +} + +void PointerLockController::requestPointerUnlockAndForceCursorVisible() +{ + m_documentAllowedToRelockWithoutUserGesture = nullptr; + + if (!m_element) + return; + + m_unlockPending = true; + m_page.chrome().client().requestPointerUnlock(); + m_forceCursorVisibleUponUnlock = true; } -void PointerLockController::elementRemoved(Element* element) +void PointerLockController::elementRemoved(Element& element) { - if (m_element == element) { + if (m_element == &element) { m_documentOfRemovedElementWhileWaitingForUnlock = &m_element->document(); // Set element null immediately to block any future interaction with it // including mouse events received before the unlock completes. - clearElement(); requestPointerUnlock(); + clearElement(); } } -void PointerLockController::documentDetached(Document* document) +void PointerLockController::documentDetached(Document& document) { - if (m_element && &m_element->document() == document) { - clearElement(); + if (m_element && &m_element->document() == &document) { + m_documentOfRemovedElementWhileWaitingForUnlock = &m_element->document(); requestPointerUnlock(); + clearElement(); } } +bool PointerLockController::isLocked() const +{ + return m_element && !m_lockPending; +} + bool PointerLockController::lockPending() const { return m_lockPending; @@ -112,21 +140,32 @@ Element* PointerLockController::element() const void PointerLockController::didAcquirePointerLock() { - enqueueEvent(eventNames().webkitpointerlockchangeEvent, m_element.get()); + enqueueEvent(eventNames().pointerlockchangeEvent, m_element.get()); m_lockPending = false; + m_forceCursorVisibleUponUnlock = false; + m_documentAllowedToRelockWithoutUserGesture = &m_element->document(); } void PointerLockController::didNotAcquirePointerLock() { - enqueueEvent(eventNames().webkitpointerlockerrorEvent, m_element.get()); + enqueueEvent(eventNames().pointerlockerrorEvent, m_element.get()); clearElement(); + m_unlockPending = false; } void PointerLockController::didLosePointerLock() { - enqueueEvent(eventNames().webkitpointerlockchangeEvent, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get()); + if (!m_unlockPending) + m_documentAllowedToRelockWithoutUserGesture = nullptr; + + enqueueEvent(eventNames().pointerlockchangeEvent, m_element ? &m_element->document() : m_documentOfRemovedElementWhileWaitingForUnlock.get()); clearElement(); - m_documentOfRemovedElementWhileWaitingForUnlock = 0; + m_unlockPending = false; + m_documentOfRemovedElementWhileWaitingForUnlock = nullptr; + if (m_forceCursorVisibleUponUnlock) { + m_forceCursorVisibleUponUnlock = false; + m_page.chrome().client().setCursorHiddenUntilMouseMoves(false); + } } void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& event, const AtomicString& eventType) @@ -141,10 +180,18 @@ void PointerLockController::dispatchLockedMouseEvent(const PlatformMouseEvent& e m_element->dispatchMouseEvent(event, eventNames().clickEvent, event.clickCount()); } +void PointerLockController::dispatchLockedWheelEvent(const PlatformWheelEvent& event) +{ + if (!m_element || !m_element->document().frame()) + return; + + m_element->dispatchWheelEvent(event); +} + void PointerLockController::clearElement() { m_lockPending = false; - m_element = 0; + m_element = nullptr; } void PointerLockController::enqueueEvent(const AtomicString& type, Element* element) diff --git a/Source/WebCore/page/PointerLockController.h b/Source/WebCore/page/PointerLockController.h index c820754ae..5021ae974 100644 --- a/Source/WebCore/page/PointerLockController.h +++ b/Source/WebCore/page/PointerLockController.h @@ -22,8 +22,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PointerLockController_h -#define PointerLockController_h +#pragma once #if ENABLE(POINTER_LOCK) @@ -36,39 +35,43 @@ class Element; class Document; class Page; class PlatformMouseEvent; +class PlatformWheelEvent; class VoidCallback; class PointerLockController { WTF_MAKE_NONCOPYABLE(PointerLockController); WTF_MAKE_FAST_ALLOCATED; public: - static PassOwnPtr<PointerLockController> create(Page*); - + explicit PointerLockController(Page&); void requestPointerLock(Element* target); + void requestPointerUnlock(); - void elementRemoved(Element*); - void documentDetached(Document*); - bool lockPending() const; - Element* element() const; + void requestPointerUnlockAndForceCursorVisible(); + void elementRemoved(Element&); + void documentDetached(Document&); + bool isLocked() const; + WEBCORE_EXPORT bool lockPending() const; + WEBCORE_EXPORT Element* element() const; - void didAcquirePointerLock(); - void didNotAcquirePointerLock(); - void didLosePointerLock(); + WEBCORE_EXPORT void didAcquirePointerLock(); + WEBCORE_EXPORT void didNotAcquirePointerLock(); + WEBCORE_EXPORT void didLosePointerLock(); void dispatchLockedMouseEvent(const PlatformMouseEvent&, const AtomicString& eventType); + void dispatchLockedWheelEvent(const PlatformWheelEvent&); private: - explicit PointerLockController(Page*); void clearElement(); void enqueueEvent(const AtomicString& type, Element*); void enqueueEvent(const AtomicString& type, Document*); - Page* m_page; - bool m_lockPending; + Page& m_page; + bool m_lockPending { false }; + bool m_unlockPending { false }; + bool m_forceCursorVisibleUponUnlock { false }; RefPtr<Element> m_element; RefPtr<Document> m_documentOfRemovedElementWhileWaitingForUnlock; + RefPtr<Document> m_documentAllowedToRelockWithoutUserGesture; }; } // namespace WebCore #endif // ENABLE(POINTER_LOCK) - -#endif // PointerLockController_h diff --git a/Source/WebCore/page/PopupOpeningObserver.h b/Source/WebCore/page/PopupOpeningObserver.h index cef6a4b49..257465ec5 100644 --- a/Source/WebCore/page/PopupOpeningObserver.h +++ b/Source/WebCore/page/PopupOpeningObserver.h @@ -23,8 +23,7 @@ * SUCH DAMAGE. */ -#ifndef PopupOpeningObserver_h -#define PopupOpeningObserver_h +#pragma once namespace WebCore { @@ -36,5 +35,4 @@ protected: virtual ~PopupOpeningObserver() { } }; -} -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/PrintContext.cpp b/Source/WebCore/page/PrintContext.cpp index e3fddb0e3..812358804 100644 --- a/Source/WebCore/page/PrintContext.cpp +++ b/Source/WebCore/page/PrintContext.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Alp Toker <alp@atoker.com> - * Copyright (C) 2007 Apple Inc. + * Copyright (C) 2007, 2016 Apple Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,32 +21,20 @@ #include "config.h" #include "PrintContext.h" +#include "ElementTraversal.h" #include "GraphicsContext.h" #include "Frame.h" #include "FrameView.h" #include "RenderView.h" #include "StyleInheritedData.h" #include "StyleResolver.h" +#include "StyleScope.h" #include <wtf/text/WTFString.h> namespace WebCore { -// By imaging to a width a little wider than the available pixels, -// thin pages will be scaled down a little, matching the way they -// print in IE and Camino. This lets them use fewer sheets than they -// would otherwise, which is presumably why other browsers do this. -// Wide pages will be scaled down more than this. -const float printingMinimumShrinkFactor = 1.25f; - -// This number determines how small we are willing to reduce the page content -// in order to accommodate the widest line. If the page would have to be -// reduced smaller to make the widest line fit, we just clip instead (this -// behavior matches MacIE and Mozilla, at least) -const float printingMaximumShrinkFactor = 2; - PrintContext::PrintContext(Frame* frame) : m_frame(frame) - , m_isPrinting(false) { } @@ -167,10 +155,10 @@ void PrintContext::begin(float width, float height) m_isPrinting = true; FloatSize originalPageSize = FloatSize(width, height); - FloatSize minLayoutSize = m_frame->resizePageRectsKeepingRatio(originalPageSize, FloatSize(width * printingMinimumShrinkFactor, height * printingMinimumShrinkFactor)); + FloatSize minLayoutSize = m_frame->resizePageRectsKeepingRatio(originalPageSize, FloatSize(width * minimumShrinkFactor(), height * minimumShrinkFactor())); // This changes layout, so callers need to make sure that they don't paint to screen while in printing mode. - m_frame->setPrinting(true, minLayoutSize, originalPageSize, printingMaximumShrinkFactor / printingMinimumShrinkFactor, AdjustViewSize); + m_frame->setPrinting(true, minLayoutSize, originalPageSize, maximumShrinkFactor() / minimumShrinkFactor(), AdjustViewSize); } float PrintContext::computeAutomaticScaleFactor(const FloatSize& availablePaperSize) @@ -186,7 +174,7 @@ float PrintContext::computeAutomaticScaleFactor(const FloatSize& availablePaperS if (viewLogicalWidth < 1) return 1; - float maxShrinkToFitScaleFactor = 1 / printingMaximumShrinkFactor; + float maxShrinkToFitScaleFactor = 1 / maximumShrinkFactor(); float shrinkToFitScaleFactor = (useViewWidth ? availablePaperSize.width() : availablePaperSize.height()) / viewLogicalWidth; return std::max(maxShrinkToFitScaleFactor, shrinkToFitScaleFactor); } @@ -198,10 +186,11 @@ void PrintContext::spoolPage(GraphicsContext& ctx, int pageNumber, float width) float scale = width / pageRect.width(); ctx.save(); - ctx.scale(FloatSize(scale, scale)); + ctx.scale(scale); ctx.translate(-pageRect.x(), -pageRect.y()); ctx.clip(pageRect); - m_frame->view()->paintContents(&ctx, pageRect); + m_frame->view()->paintContents(ctx, pageRect); + outputLinkedDestinations(ctx, *m_frame->document(), pageRect); ctx.restore(); } @@ -211,7 +200,8 @@ void PrintContext::spoolRect(GraphicsContext& ctx, const IntRect& rect) ctx.save(); ctx.translate(-rect.x(), -rect.y()); ctx.clip(rect); - m_frame->view()->paintContents(&ctx, rect); + m_frame->view()->paintContents(ctx, rect); + outputLinkedDestinations(ctx, *m_frame->document(), rect); ctx.restore(); } @@ -220,16 +210,14 @@ void PrintContext::end() ASSERT(m_isPrinting); m_isPrinting = false; m_frame->setPrinting(false, FloatSize(), FloatSize(), 0, AdjustViewSize); + m_linkedDestinations = nullptr; } -static RenderBoxModelObject* enclosingBoxModelObject(RenderObject* object) +static inline RenderBoxModelObject* enclosingBoxModelObject(RenderElement* renderer) { - - while (object && !object->isBoxModelObject()) - object = object->parent(); - if (!object) - return 0; - return toRenderBoxModelObject(object); + while (renderer && !is<RenderBoxModelObject>(*renderer)) + renderer = renderer->parent(); + return downcast<RenderBoxModelObject>(renderer); } int PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSizeInPixels) @@ -238,7 +226,7 @@ int PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSi RefPtr<Element> elementRef(element); element->document().updateLayout(); - RenderBoxModelObject* box = enclosingBoxModelObject(element->renderer()); + auto* box = enclosingBoxModelObject(element->renderer()); if (!box) return -1; @@ -250,8 +238,8 @@ int PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSi scaledPageSize.scale(frame->view()->contentsSize().width() / pageRect.width()); printContext.computePageRectsWithPageSize(scaledPageSize, false); - int top = box->pixelSnappedOffsetTop(); - int left = box->pixelSnappedOffsetLeft(); + int top = roundToInt(box->offsetTop()); + int left = roundToInt(box->offsetLeft()); size_t pageNumber = 0; for (; pageNumber < printContext.pageCount(); pageNumber++) { const IntRect& page = printContext.pageRect(pageNumber); @@ -261,18 +249,55 @@ int PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSi return -1; } +void PrintContext::collectLinkedDestinations(Document& document) +{ + for (Element* child = document.documentElement(); child; child = ElementTraversal::next(*child)) { + String outAnchorName; + if (Element* element = child->findAnchorElementForLink(outAnchorName)) + m_linkedDestinations->add(outAnchorName, *element); + } +} + +void PrintContext::outputLinkedDestinations(GraphicsContext& graphicsContext, Document& document, const IntRect& pageRect) +{ + if (!graphicsContext.supportsInternalLinks()) + return; + + if (!m_linkedDestinations) { + m_linkedDestinations = std::make_unique<HashMap<String, Ref<Element>>>(); + collectLinkedDestinations(document); + } + + for (const auto& it : *m_linkedDestinations) { + RenderElement* renderer = it.value->renderer(); + if (!renderer) + continue; + + FloatPoint point = renderer->absoluteAnchorRect().minXMinYCorner(); + point = point.expandedTo(FloatPoint()); + + if (!pageRect.contains(roundedIntPoint(point))) + continue; + + graphicsContext.addDestinationAtPoint(it.key, point); + } +} + String PrintContext::pageProperty(Frame* frame, const char* propertyName, int pageNumber) { - Document* document = frame->document(); + ASSERT(frame); + ASSERT(frame->document()); + + auto& document = *frame->document(); PrintContext printContext(frame); printContext.begin(800); // Any width is OK here. - document->updateLayout(); - RefPtr<RenderStyle> style = document->ensureStyleResolver().styleForPage(pageNumber); + document.updateLayout(); + auto style = document.styleScope().resolver().styleForPage(pageNumber); // Implement formatters for properties we care about. if (!strcmp(propertyName, "margin-left")) { if (style->marginLeft().isAuto()) - return String("auto"); + return ASCIILiteral { "auto" }; return String::number(style->marginLeft().value()); } if (!strcmp(propertyName, "line-height")) @@ -282,9 +307,9 @@ String PrintContext::pageProperty(Frame* frame, const char* propertyName, int pa if (!strcmp(propertyName, "font-family")) return style->fontDescription().firstFamily(); if (!strcmp(propertyName, "size")) - return String::number(style->pageSize().width().value()) + ' ' + String::number(style->pageSize().height().value()); + return String::number(style->pageSize().width.value()) + ' ' + String::number(style->pageSize().height.value()); - return String("pageProperty() unimplemented for: ") + propertyName; + return makeString("pageProperty() unimplemented for: ", propertyName); } bool PrintContext::isPageBoxVisible(Frame* frame, int pageNumber) @@ -301,54 +326,60 @@ String PrintContext::pageSizeAndMarginsInPixels(Frame* frame, int pageNumber, in String::number(marginTop) + ' ' + String::number(marginRight) + ' ' + String::number(marginBottom) + ' ' + String::number(marginLeft); } -int PrintContext::numberOfPages(Frame* frame, const FloatSize& pageSizeInPixels) +bool PrintContext::beginAndComputePageRectsWithPageSize(Frame& frame, const FloatSize& pageSizeInPixels) { - frame->document()->updateLayout(); + if (!frame.document() || !frame.view() || !frame.document()->renderView()) + return false; - FloatRect pageRect(FloatPoint(0, 0), pageSizeInPixels); - PrintContext printContext(frame); - printContext.begin(pageRect.width(), pageRect.height()); + frame.document()->updateLayout(); + + begin(pageSizeInPixels.width(), pageSizeInPixels.height()); // Account for shrink-to-fit. FloatSize scaledPageSize = pageSizeInPixels; - scaledPageSize.scale(frame->view()->contentsSize().width() / pageRect.width()); - printContext.computePageRectsWithPageSize(scaledPageSize, false); - return printContext.pageCount(); + scaledPageSize.scale(frame.view()->contentsSize().width() / pageSizeInPixels.width()); + computePageRectsWithPageSize(scaledPageSize, false); + + return true; } -void PrintContext::spoolAllPagesWithBoundaries(Frame* frame, GraphicsContext& graphicsContext, const FloatSize& pageSizeInPixels) +int PrintContext::numberOfPages(Frame& frame, const FloatSize& pageSizeInPixels) { - if (!frame->document() || !frame->view() || !frame->document()->renderView()) - return; - - frame->document()->updateLayout(); + PrintContext printContext(&frame); + if (!printContext.beginAndComputePageRectsWithPageSize(frame, pageSizeInPixels)) + return -1; - PrintContext printContext(frame); - printContext.begin(pageSizeInPixels.width(), pageSizeInPixels.height()); + return printContext.pageCount(); +} - float pageHeight; - printContext.computePageRects(FloatRect(FloatPoint(0, 0), pageSizeInPixels), 0, 0, 1, pageHeight); +void PrintContext::spoolAllPagesWithBoundaries(Frame& frame, GraphicsContext& graphicsContext, const FloatSize& pageSizeInPixels) +{ + PrintContext printContext(&frame); + if (!printContext.beginAndComputePageRectsWithPageSize(frame, pageSizeInPixels)) + return; const float pageWidth = pageSizeInPixels.width(); const Vector<IntRect>& pageRects = printContext.pageRects(); int totalHeight = pageRects.size() * (pageSizeInPixels.height() + 1) - 1; // Fill the whole background by white. - graphicsContext.setFillColor(Color(255, 255, 255), ColorSpaceDeviceRGB); + graphicsContext.setFillColor(Color(255, 255, 255)); graphicsContext.fillRect(FloatRect(0, 0, pageWidth, totalHeight)); graphicsContext.save(); - graphicsContext.translate(0, totalHeight); - graphicsContext.scale(FloatSize(1, -1)); int currentHeight = 0; for (size_t pageIndex = 0; pageIndex < pageRects.size(); pageIndex++) { // Draw a line for a page boundary if this isn't the first page. if (pageIndex > 0) { +#if PLATFORM(COCOA) + int boundaryLineY = currentHeight; +#else + int boundaryLineY = currentHeight - 1; +#endif graphicsContext.save(); - graphicsContext.setStrokeColor(Color(0, 0, 255), ColorSpaceDeviceRGB); - graphicsContext.setFillColor(Color(0, 0, 255), ColorSpaceDeviceRGB); - graphicsContext.drawLine(IntPoint(0, currentHeight), - IntPoint(pageWidth, currentHeight)); + graphicsContext.setStrokeColor(Color(0, 0, 255)); + graphicsContext.setFillColor(Color(0, 0, 255)); + graphicsContext.drawLine(IntPoint(0, boundaryLineY), IntPoint(pageWidth, boundaryLineY)); graphicsContext.restore(); } diff --git a/Source/WebCore/page/PrintContext.h b/Source/WebCore/page/PrintContext.h index 2efd05d3f..ceaa23c5d 100644 --- a/Source/WebCore/page/PrintContext.h +++ b/Source/WebCore/page/PrintContext.h @@ -18,25 +18,28 @@ * Boston, MA 02110-1301, USA. */ -#ifndef PrintContext_h -#define PrintContext_h +#pragma once #include <wtf/Forward.h> +#include <wtf/HashMap.h> #include <wtf/Vector.h> +#include <wtf/text/WTFString.h> namespace WebCore { +class Document; class Element; class Frame; class FloatRect; class FloatSize; class GraphicsContext; class IntRect; +class Node; class PrintContext { public: - explicit PrintContext(Frame*); - ~PrintContext(); + WEBCORE_EXPORT explicit PrintContext(Frame*); + WEBCORE_EXPORT ~PrintContext(); Frame* frame() const { return m_frame; } @@ -44,40 +47,53 @@ public: // FIXME: This means that CSS page breaks won't be on page boundary if the size is different than what was passed to begin(). That's probably not always desirable. // FIXME: Header and footer height should be applied before layout, not after. // FIXME: The printRect argument is only used to determine page aspect ratio, it would be better to pass a FloatSize with page dimensions instead. - void computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight, bool allowHorizontalTiling = false); + WEBCORE_EXPORT void computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight, bool allowHorizontalTiling = false); // Deprecated. Page size computation is already in this class, clients shouldn't be copying it. - void computePageRectsWithPageSize(const FloatSize& pageSizeInPixels, bool allowHorizontalTiling); + WEBCORE_EXPORT void computePageRectsWithPageSize(const FloatSize& pageSizeInPixels, bool allowHorizontalTiling); // These are only valid after page rects are computed. size_t pageCount() const { return m_pageRects.size(); } const IntRect& pageRect(size_t pageNumber) const { return m_pageRects[pageNumber]; } const Vector<IntRect>& pageRects() const { return m_pageRects; } - float computeAutomaticScaleFactor(const FloatSize& availablePaperSize); + WEBCORE_EXPORT float computeAutomaticScaleFactor(const FloatSize& availablePaperSize); // Enter print mode, updating layout for new page size. // This function can be called multiple times to apply new print options without going back to screen mode. - void begin(float width, float height = 0); + WEBCORE_EXPORT void begin(float width, float height = 0); // FIXME: eliminate width argument. - void spoolPage(GraphicsContext& ctx, int pageNumber, float width); + WEBCORE_EXPORT void spoolPage(GraphicsContext& ctx, int pageNumber, float width); - void spoolRect(GraphicsContext& ctx, const IntRect&); + WEBCORE_EXPORT void spoolRect(GraphicsContext& ctx, const IntRect&); // Return to screen mode. - void end(); + WEBCORE_EXPORT void end(); // Used by layout tests. - static int pageNumberForElement(Element*, const FloatSize& pageSizeInPixels); // Returns -1 if page isn't found. - static String pageProperty(Frame* frame, const char* propertyName, int pageNumber); - static bool isPageBoxVisible(Frame* frame, int pageNumber); - static String pageSizeAndMarginsInPixels(Frame* frame, int pageNumber, int width, int height, int marginTop, int marginRight, int marginBottom, int marginLeft); - static int numberOfPages(Frame*, const FloatSize& pageSizeInPixels); + WEBCORE_EXPORT static int pageNumberForElement(Element*, const FloatSize& pageSizeInPixels); // Returns -1 if page isn't found. + WEBCORE_EXPORT static String pageProperty(Frame*, const char* propertyName, int pageNumber); + WEBCORE_EXPORT static bool isPageBoxVisible(Frame*, int pageNumber); + WEBCORE_EXPORT static String pageSizeAndMarginsInPixels(Frame*, int pageNumber, int width, int height, int marginTop, int marginRight, int marginBottom, int marginLeft); + WEBCORE_EXPORT static int numberOfPages(Frame&, const FloatSize& pageSizeInPixels); // Draw all pages into a graphics context with lines which mean page boundaries. // The height of the graphics context should be // (pageSizeInPixels.height() + 1) * number-of-pages - 1 - static void spoolAllPagesWithBoundaries(Frame*, GraphicsContext&, const FloatSize& pageSizeInPixels); + WEBCORE_EXPORT static void spoolAllPagesWithBoundaries(Frame&, GraphicsContext&, const FloatSize& pageSizeInPixels); + + // By imaging to a width a little wider than the available pixels, + // thin pages will be scaled down a little, matching the way they + // print in IE and Camino. This lets them use fewer sheets than they + // would otherwise, which is presumably why other browsers do this. + // Wide pages will be scaled down more than this. + static constexpr float minimumShrinkFactor() { return 1.25; } + + // This number determines how small we are willing to reduce the page content + // in order to accommodate the widest line. If the page would have to be + // reduced smaller to make the widest line fit, we just clip instead (this + // behavior matches MacIE and Mozilla, at least) + static constexpr float maximumShrinkFactor() { return 2; } protected: Frame* m_frame; @@ -85,11 +101,14 @@ protected: private: void computePageRectsWithPageSizeInternal(const FloatSize& pageSizeInPixels, bool allowHorizontalTiling); + bool beginAndComputePageRectsWithPageSize(Frame&, const FloatSize& pageSizeInPixels); + void collectLinkedDestinations(Document&); + void outputLinkedDestinations(GraphicsContext&, Document&, const IntRect& pageRect); // Used to prevent misuses of begin() and end() (e.g., call end without begin). - bool m_isPrinting; -}; + bool m_isPrinting { false }; -} + std::unique_ptr<HashMap<String, Ref<Element>>> m_linkedDestinations; +}; -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/ResourceUsageData.cpp b/Source/WebCore/page/ResourceUsageData.cpp new file mode 100644 index 000000000..5912b05d9 --- /dev/null +++ b/Source/WebCore/page/ResourceUsageData.cpp @@ -0,0 +1,60 @@ +/* + * 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 "ResourceUsageData.h" + +#if ENABLE(RESOURCE_USAGE) + +namespace WebCore { + +ResourceUsageData::ResourceUsageData() +{ + // VM tag categories. + categories[MemoryCategory::JSJIT] = MemoryCategoryInfo(MemoryCategory::JSJIT); + categories[MemoryCategory::Images] = MemoryCategoryInfo(MemoryCategory::Images); + categories[MemoryCategory::Layers] = MemoryCategoryInfo(MemoryCategory::Layers); + categories[MemoryCategory::LibcMalloc] = MemoryCategoryInfo(MemoryCategory::LibcMalloc); + categories[MemoryCategory::bmalloc] = MemoryCategoryInfo(MemoryCategory::bmalloc); + categories[MemoryCategory::Other] = MemoryCategoryInfo(MemoryCategory::Other); + + // Sub categories (e.g breakdown of bmalloc tag.) + categories[MemoryCategory::GCHeap] = MemoryCategoryInfo(MemoryCategory::GCHeap, true); + categories[MemoryCategory::GCOwned] = MemoryCategoryInfo(MemoryCategory::GCOwned, true); +} + +ResourceUsageData::ResourceUsageData(const ResourceUsageData& other) + : cpu(other.cpu) + , totalDirtySize(other.totalDirtySize) + , totalExternalSize(other.totalExternalSize) + , timeOfNextEdenCollection(other.timeOfNextEdenCollection) + , timeOfNextFullCollection(other.timeOfNextFullCollection) +{ + std::copy(other.categories.begin(), other.categories.end(), categories.begin()); +} + +} + +#endif // ENABLE(RESOURCE_USAGE) diff --git a/Source/WebCore/page/ResourceUsageData.h b/Source/WebCore/page/ResourceUsageData.h new file mode 100644 index 000000000..723696f15 --- /dev/null +++ b/Source/WebCore/page/ResourceUsageData.h @@ -0,0 +1,77 @@ +/* + * 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 + +#if ENABLE(RESOURCE_USAGE) + +#include <array> + +namespace WebCore { + +namespace MemoryCategory { +static const unsigned bmalloc = 0; +static const unsigned LibcMalloc = 1; +static const unsigned JSJIT = 2; +static const unsigned Images = 3; +static const unsigned GCHeap = 4; +static const unsigned GCOwned = 5; +static const unsigned Other = 6; +static const unsigned Layers = 7; +static const unsigned NumberOfCategories = 8; +} + +struct MemoryCategoryInfo { + MemoryCategoryInfo() { } // Needed for std::array. + MemoryCategoryInfo(unsigned category, bool subcategory = false) + : isSubcategory(subcategory) + , type(category) + { + } + + size_t totalSize() const { return dirtySize + externalSize; } + + size_t dirtySize { 0 }; + size_t reclaimableSize { 0 }; + size_t externalSize { 0 }; + bool isSubcategory { false }; + unsigned type { MemoryCategory::NumberOfCategories }; +}; + +struct ResourceUsageData { + ResourceUsageData(); + ResourceUsageData(const ResourceUsageData& data); + + float cpu { 0 }; + size_t totalDirtySize { 0 }; + size_t totalExternalSize { 0 }; + std::array<MemoryCategoryInfo, MemoryCategory::NumberOfCategories> categories; + double timeOfNextEdenCollection { 0 }; + double timeOfNextFullCollection { 0 }; +}; + +} // namespace WebCore + +#endif // ResourceUsageData_h diff --git a/Source/WebCore/page/ResourceUsageOverlay.cpp b/Source/WebCore/page/ResourceUsageOverlay.cpp new file mode 100644 index 000000000..0e47a8fcb --- /dev/null +++ b/Source/WebCore/page/ResourceUsageOverlay.cpp @@ -0,0 +1,130 @@ +/* + * 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" + +#if ENABLE(RESOURCE_USAGE) + +#include "ResourceUsageOverlay.h" + +#include "FrameView.h" +#include "Page.h" +#include "PageOverlayController.h" +#include "PlatformMouseEvent.h" + +namespace WebCore { + +ResourceUsageOverlay::ResourceUsageOverlay(Page& page) + : m_page(page) + , m_overlay(PageOverlay::create(*this, PageOverlay::OverlayType::View)) +{ + // Let the event loop cycle before continuing with initialization. + // This way we'll have access to the FrameView's dimensions. + callOnMainThread([this] { + initialize(); + }); +} + +ResourceUsageOverlay::~ResourceUsageOverlay() +{ + platformDestroy(); + + // FIXME: This is a hack so we don't try to uninstall the PageOverlay during Page destruction. + if (m_page.mainFrame().page()) + m_page.mainFrame().pageOverlayController().uninstallPageOverlay(*m_overlay, PageOverlay::FadeMode::DoNotFade); +} + +void ResourceUsageOverlay::initialize() +{ + if (!m_page.mainFrame().view()) + return; + + FrameView& frameView = *m_page.mainFrame().view(); + + IntRect initialRect(frameView.width() / 2 - normalWidth / 2, frameView.height() - normalHeight - 20, normalWidth, normalHeight); + +#if PLATFORM(IOS) + // FIXME: The overlay should be stuck to the viewport instead of moving along with the page. + initialRect.setY(20); +#endif + + m_overlay->setFrame(initialRect); + m_page.mainFrame().pageOverlayController().installPageOverlay(*m_overlay, PageOverlay::FadeMode::DoNotFade); + platformInitialize(); +} + +bool ResourceUsageOverlay::mouseEvent(PageOverlay&, const PlatformMouseEvent& event) +{ + if (event.button() != LeftButton) + return false; + + switch (event.type()) { + case PlatformEvent::MousePressed: { + m_overlay->setShouldIgnoreMouseEventsOutsideBounds(false); + m_dragging = true; + IntPoint location = m_overlay->frame().location(); + m_dragPoint = event.position() + IntPoint(-location.x(), -location.y()); + return true; + } + case PlatformEvent::MouseReleased: + if (m_dragging) { + m_overlay->setShouldIgnoreMouseEventsOutsideBounds(true); + m_dragging = false; + return true; + } + break; + case PlatformEvent::MouseMoved: + if (m_dragging) { + IntRect newFrame = m_overlay->frame(); + + // Move the new frame relative to the point where the drag was initiated. + newFrame.setLocation(event.position()); + newFrame.moveBy(IntPoint(-m_dragPoint.x(), -m_dragPoint.y())); + + // Force the frame to stay inside the viewport entirely. + if (newFrame.x() < 0) + newFrame.setX(0); + if (newFrame.y() < m_page.topContentInset()) + newFrame.setY(m_page.topContentInset()); + FrameView& frameView = *m_page.mainFrame().view(); + if (newFrame.maxX() > frameView.width()) + newFrame.setX(frameView.width() - newFrame.width()); + if (newFrame.maxY() > frameView.height()) + newFrame.setY(frameView.height() - newFrame.height()); + + m_overlay->setFrame(newFrame); + m_overlay->setNeedsDisplay(); + return true; + } + break; + default: + break; + } + return false; +} + +} + +#endif diff --git a/Source/WebCore/page/ResourceUsageOverlay.h b/Source/WebCore/page/ResourceUsageOverlay.h new file mode 100644 index 000000000..b9d053861 --- /dev/null +++ b/Source/WebCore/page/ResourceUsageOverlay.h @@ -0,0 +1,95 @@ +/* + * 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 + +#if ENABLE(RESOURCE_USAGE) + +#include "FloatRect.h" +#include "GraphicsLayer.h" +#include "IntRect.h" +#include "MainFrame.h" +#include "PageOverlay.h" +#include <wtf/Noncopyable.h> +#include <wtf/RetainPtr.h> + +#if PLATFORM(COCOA) +#include "PlatformCALayer.h" +#endif + +namespace WebCore { + +class FloatRect; +class IntPoint; +class IntRect; + +class ResourceUsageOverlay final : public PageOverlay::Client { + WTF_MAKE_FAST_ALLOCATED; + WTF_MAKE_NONCOPYABLE(ResourceUsageOverlay); +public: + explicit ResourceUsageOverlay(Page&); + ~ResourceUsageOverlay(); + + PageOverlay& overlay() { return *m_overlay; } + +#if PLATFORM(COCOA) + void platformDraw(CGContextRef); +#endif + + static const int normalWidth = 570; + static const int normalHeight = 160; + +private: + void willMoveToPage(PageOverlay&, Page*) override { } + void didMoveToPage(PageOverlay&, Page*) override { } + void drawRect(PageOverlay&, GraphicsContext&, const IntRect&) override { } + bool mouseEvent(PageOverlay&, const PlatformMouseEvent&) override; + void didScrollFrame(PageOverlay&, Frame&) override { } + + void initialize(); + + void platformInitialize(); + void platformDestroy(); + + Page& m_page; + RefPtr<PageOverlay> m_overlay; + bool m_dragging { false }; + IntPoint m_dragPoint; + +#if PLATFORM(COCOA) + ThreadIdentifier m_threadID { 0 }; + RetainPtr<CALayer> m_layer; + RetainPtr<CALayer> m_containerLayer; +#endif + +#if OS(LINUX) + std::unique_ptr<GraphicsLayer> m_paintLayer; + std::unique_ptr<GraphicsLayerClient> m_overlayPainter; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(RESOURCE_USAGE) diff --git a/Source/WebCore/page/ResourceUsageThread.cpp b/Source/WebCore/page/ResourceUsageThread.cpp new file mode 100644 index 000000000..91200c345 --- /dev/null +++ b/Source/WebCore/page/ResourceUsageThread.cpp @@ -0,0 +1,131 @@ +/* + * 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 "ResourceUsageThread.h" + +#if ENABLE(RESOURCE_USAGE) + +#include "CommonVM.h" +#include "JSDOMWindow.h" +#include <thread> +#include <wtf/MainThread.h> +#include <wtf/Vector.h> + +namespace WebCore { + +ResourceUsageThread::ResourceUsageThread() +{ +} + +ResourceUsageThread& ResourceUsageThread::singleton() +{ + static NeverDestroyed<ResourceUsageThread> resourceUsageThread; + return resourceUsageThread; +} + +void ResourceUsageThread::addObserver(void* key, std::function<void (const ResourceUsageData&)> function) +{ + auto& resourceUsageThread = ResourceUsageThread::singleton(); + resourceUsageThread.createThreadIfNeeded(); + + { + LockHolder locker(resourceUsageThread.m_lock); + bool wasEmpty = resourceUsageThread.m_observers.isEmpty(); + resourceUsageThread.m_observers.set(key, function); + + if (wasEmpty) + resourceUsageThread.m_condition.notifyAll(); + } +} + +void ResourceUsageThread::removeObserver(void* key) +{ + auto& resourceUsageThread = ResourceUsageThread::singleton(); + + { + LockHolder locker(resourceUsageThread.m_lock); + resourceUsageThread.m_observers.remove(key); + } +} + +void ResourceUsageThread::waitUntilObservers() +{ + LockHolder locker(m_lock); + while (m_observers.isEmpty()) + m_condition.wait(m_lock); +} + +void ResourceUsageThread::notifyObservers(ResourceUsageData&& data) +{ + callOnMainThread([data = WTFMove(data)]() mutable { + Vector<std::function<void (const ResourceUsageData&)>> functions; + + { + auto& resourceUsageThread = ResourceUsageThread::singleton(); + LockHolder locker(resourceUsageThread.m_lock); + copyValuesToVector(resourceUsageThread.m_observers, functions); + } + + for (auto& function : functions) + function(data); + }); +} + +void ResourceUsageThread::createThreadIfNeeded() +{ + if (m_threadIdentifier) + return; + + m_vm = &commonVM(); + m_threadIdentifier = createThread(threadCallback, this, "WebCore: ResourceUsage"); +} + +void ResourceUsageThread::threadCallback(void* resourceUsageThread) +{ + static_cast<ResourceUsageThread*>(resourceUsageThread)->threadBody(); +} + +NO_RETURN void ResourceUsageThread::threadBody() +{ + while (true) { + // Only do work if we have observers. + waitUntilObservers(); + + auto start = std::chrono::system_clock::now(); + + ResourceUsageData data; + platformThreadBody(m_vm, data); + notifyObservers(WTFMove(data)); + + auto duration = std::chrono::system_clock::now() - start; + auto difference = 500ms - duration; + std::this_thread::sleep_for(difference); + } +} + +} // namespace WebCore + +#endif // ENABLE(RESOURCE_USAGE) diff --git a/Source/WebCore/page/ResourceUsageThread.h b/Source/WebCore/page/ResourceUsageThread.h new file mode 100644 index 000000000..18a1e6754 --- /dev/null +++ b/Source/WebCore/page/ResourceUsageThread.h @@ -0,0 +1,90 @@ +/* + * 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 + +#if ENABLE(RESOURCE_USAGE) + +#include "ResourceUsageData.h" +#include <array> +#include <functional> +#include <wtf/Condition.h> +#include <wtf/HashMap.h> +#include <wtf/Lock.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/Noncopyable.h> + +namespace JSC { +class VM; +} + +namespace WebCore { + +class ResourceUsageThread { + WTF_MAKE_NONCOPYABLE(ResourceUsageThread); + +public: + static void addObserver(void* key, std::function<void (const ResourceUsageData&)>); + static void removeObserver(void* key); + +private: + friend NeverDestroyed<ResourceUsageThread>; + ResourceUsageThread(); + static ResourceUsageThread& singleton(); + + void waitUntilObservers(); + void notifyObservers(ResourceUsageData&&); + + void createThreadIfNeeded(); + static void threadCallback(void* scrollingThread); + void threadBody(); + void platformThreadBody(JSC::VM*, ResourceUsageData&); + + ThreadIdentifier m_threadIdentifier { 0 }; + Lock m_lock; + Condition m_condition; + HashMap<void*, std::function<void (const ResourceUsageData&)>> m_observers; + + // Platforms may need to access some data from the common VM. + // They should ensure their use of the VM is thread safe. + JSC::VM* m_vm { nullptr }; +}; + +#if PLATFORM(COCOA) +struct TagInfo { + TagInfo() { } + size_t dirty { 0 }; + size_t reclaimable { 0 }; +}; + +const char* displayNameForVMTag(unsigned); +size_t vmPageSize(); +std::array<TagInfo, 256> pagesPerVMTag(); +void logFootprintComparison(const std::array<TagInfo, 256>&, const std::array<TagInfo, 256>&); +#endif + +} // namespace WebCore + +#endif // ENABLE(RESOURCE_USAGE) diff --git a/Source/WebCore/page/PerformanceEntryList.cpp b/Source/WebCore/page/RuntimeEnabledFeatures.cpp index d9fa05577..890c07bac 100644 --- a/Source/WebCore/page/PerformanceEntryList.cpp +++ b/Source/WebCore/page/RuntimeEnabledFeatures.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2012 Google Inc. All rights reserved. - * Copyright (C) 2012 Intel Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2013-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 @@ -30,49 +30,74 @@ */ #include "config.h" -#include "PerformanceEntryList.h" +#include "RuntimeEnabledFeatures.h" -#if ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) - -#include "PerformanceEntry.h" +#include "MediaPlayer.h" +#include "WebSocket.h" namespace WebCore { -PerformanceEntryList::PerformanceEntryList() +RuntimeEnabledFeatures::RuntimeEnabledFeatures() { +#if ENABLE(MEDIA_STREAM) && PLATFORM(COCOA) + m_isMediaStreamEnabled = false; +#endif } -PerformanceEntryList::~PerformanceEntryList() +RuntimeEnabledFeatures& RuntimeEnabledFeatures::sharedFeatures() { + static NeverDestroyed<RuntimeEnabledFeatures> runtimeEnabledFeatures; + + return runtimeEnabledFeatures; } -unsigned PerformanceEntryList::length() const +#if ENABLE(VIDEO) +bool RuntimeEnabledFeatures::audioEnabled() const { - return m_entries.size(); + return MediaPlayer::isAvailable(); } -PerformanceEntry* PerformanceEntryList::item(unsigned index) +bool RuntimeEnabledFeatures::htmlMediaElementEnabled() const { - if (index < m_entries.size()) - return m_entries[index].get(); - return 0; + return MediaPlayer::isAvailable(); } -void PerformanceEntryList::append(PassRefPtr<PerformanceEntry> entry) +bool RuntimeEnabledFeatures::htmlAudioElementEnabled() const { - m_entries.append(entry); + return MediaPlayer::isAvailable(); } -void PerformanceEntryList::appendAll(const Vector<RefPtr<PerformanceEntry>>& entries) +bool RuntimeEnabledFeatures::htmlVideoElementEnabled() const { - m_entries.appendVector(entries); + return MediaPlayer::isAvailable(); } -void PerformanceEntryList::sort() +bool RuntimeEnabledFeatures::htmlSourceElementEnabled() const { - std::sort(m_entries.begin(), m_entries.end(), PerformanceEntry::startTimeCompareLessThan); + return MediaPlayer::isAvailable(); } -} // namespace WebCore +bool RuntimeEnabledFeatures::mediaControllerEnabled() const +{ + return MediaPlayer::isAvailable(); +} + +bool RuntimeEnabledFeatures::mediaErrorEnabled() const +{ + return MediaPlayer::isAvailable(); +} -#endif // ENABLE(WEB_TIMING) && ENABLE(PERFORMANCE_TIMELINE) +bool RuntimeEnabledFeatures::timeRangesEnabled() const +{ + return MediaPlayer::isAvailable(); +} +#endif + +#if ENABLE(WEB_SOCKETS) +bool RuntimeEnabledFeatures::webSocketEnabled() const +{ + return WebSocket::isAvailable(); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/page/RuntimeEnabledFeatures.h b/Source/WebCore/page/RuntimeEnabledFeatures.h new file mode 100644 index 000000000..6c695c495 --- /dev/null +++ b/Source/WebCore/page/RuntimeEnabledFeatures.h @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2013-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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "PlatformExportMacros.h" +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +// A class that stores static enablers for all experimental features. Note that +// the method names must line up with the JavaScript method they enable for code +// generation to work properly. + +class RuntimeEnabledFeatures { + WTF_MAKE_NONCOPYABLE(RuntimeEnabledFeatures); +public: + void setDOMIteratorEnabled(bool isEnabled) { m_isDOMIteratorEnabled = isEnabled; } + bool domIteratorEnabled() const { return m_isDOMIteratorEnabled; } + + void setGeolocationEnabled(bool isEnabled) { m_isGeolocationEnabled = isEnabled; } + bool geolocationEnabled() const { return m_isGeolocationEnabled; } + + void setLinkPreloadEnabled(bool isEnabled) { m_isLinkPreloadEnabled = isEnabled; } + bool linkPreloadEnabled() const { return m_isLinkPreloadEnabled; } + + void setResourceTimingEnabled(bool isEnabled) { m_isResourceTimingEnabled = isEnabled; } + bool resourceTimingEnabled() const { return m_isResourceTimingEnabled; } + + void setUserTimingEnabled(bool isEnabled) { m_isUserTimingEnabled = isEnabled; } + bool userTimingEnabled() const { return m_isUserTimingEnabled; } + + bool performanceTimelineEnabled() const { return resourceTimingEnabled() || userTimingEnabled(); } + + void setShadowDOMEnabled(bool isEnabled) { m_isShadowDOMEnabled = isEnabled; } + bool shadowDOMEnabled() const { return m_isShadowDOMEnabled; } + + void setInputEventsEnabled(bool isEnabled) { m_inputEventsEnabled = isEnabled; } + bool inputEventsEnabled() const { return m_inputEventsEnabled; } + + void setInteractiveFormValidationEnabled(bool isEnabled) { m_isInteractiveFormValidationEnabled = isEnabled; } + bool interactiveFormValidationEnabled() const { return m_isInteractiveFormValidationEnabled; } + + void setCustomElementsEnabled(bool areEnabled) { m_areCustomElementsEnabled = areEnabled; } + bool customElementsEnabled() const { return m_areCustomElementsEnabled; } + + void setModernMediaControlsEnabled(bool areEnabled) { m_areModernMediaControlsEnabled = areEnabled; } + bool modernMediaControlsEnabled() const { return m_areModernMediaControlsEnabled; } + +#if ENABLE(INDEXED_DATABASE_IN_WORKERS) + void setIndexedDBWorkersEnabled(bool isEnabled) { m_isIndexedDBWorkersEnabled = isEnabled; } + bool indexedDBWorkersEnabled() const { return m_isIndexedDBWorkersEnabled; } +#endif + +#if ENABLE(FONT_LOAD_EVENTS) + void setFontLoadEventsEnabled(bool isEnabled) { m_isFontLoadEventsEnabled = isEnabled; } + bool fontLoadEventsEnabled() const { return m_isFontLoadEventsEnabled; } +#endif + +#if ENABLE(MEDIA_STREAM) + bool mediaStreamEnabled() const { return m_isMediaStreamEnabled; } + void setMediaStreamEnabled(bool isEnabled) { m_isMediaStreamEnabled = isEnabled; } +#endif + +#if ENABLE(WEB_RTC) + bool peerConnectionEnabled() const { return m_isMediaStreamEnabled && m_isPeerConnectionEnabled; } + void setPeerConnectionEnabled(bool isEnabled) { m_isPeerConnectionEnabled = isEnabled; } +#endif + +#if ENABLE(LEGACY_CSS_VENDOR_PREFIXES) + void setLegacyCSSVendorPrefixesEnabled(bool isEnabled) { m_isLegacyCSSVendorPrefixesEnabled = isEnabled; } + bool legacyCSSVendorPrefixesEnabled() const { return m_isLegacyCSSVendorPrefixesEnabled; } +#endif + +#if ENABLE(INPUT_TYPE_DATE) + bool inputTypeDateEnabled() const { return m_isInputTypeDateEnabled; } + void setInputTypeDateEnabled(bool isEnabled) { m_isInputTypeDateEnabled = isEnabled; } +#endif + +#if ENABLE(INPUT_TYPE_DATETIME_INCOMPLETE) + bool inputTypeDateTimeEnabled() const { return m_isInputTypeDateTimeEnabled; } + void setInputTypeDateTimeEnabled(bool isEnabled) { m_isInputTypeDateTimeEnabled = isEnabled; } +#endif + +#if ENABLE(INPUT_TYPE_DATETIMELOCAL) + bool inputTypeDateTimeLocalEnabled() const { return m_isInputTypeDateTimeLocalEnabled; } + void setInputTypeDateTimeLocalEnabled(bool isEnabled) { m_isInputTypeDateTimeLocalEnabled = isEnabled; } +#endif + +#if ENABLE(INPUT_TYPE_MONTH) + bool inputTypeMonthEnabled() const { return m_isInputTypeMonthEnabled; } + void setInputTypeMonthEnabled(bool isEnabled) { m_isInputTypeMonthEnabled = isEnabled; } +#endif + +#if ENABLE(INPUT_TYPE_TIME) + bool inputTypeTimeEnabled() const { return m_isInputTypeTimeEnabled; } + void setInputTypeTimeEnabled(bool isEnabled) { m_isInputTypeTimeEnabled = isEnabled; } +#endif + +#if ENABLE(INPUT_TYPE_WEEK) + bool inputTypeWeekEnabled() const { return m_isInputTypeWeekEnabled; } + void setInputTypeWeekEnabled(bool isEnabled) { m_isInputTypeWeekEnabled = isEnabled; } +#endif + +#if ENABLE(GAMEPAD) + void setGamepadsEnabled(bool areEnabled) { m_areGamepadsEnabled = areEnabled; } + bool gamepadsEnabled() const { return m_areGamepadsEnabled; } +#endif + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + // FIXME: This is not used. + void setAnimationTriggersEnabled(bool areEnabled) { m_areAnimationTriggersEnabled = areEnabled; } + bool animationTriggersEnabled() const { return m_areAnimationTriggersEnabled; } +#endif + +#if ENABLE(WEB_ANIMATIONS) + void setWebAnimationsEnabled(bool areEnabled) { m_areWebAnimationsEnabled = areEnabled; } + bool webAnimationsEnabled() const { return m_areWebAnimationsEnabled; } +#endif + +#if ENABLE(WEBGL2) + void setWebGL2Enabled(bool isEnabled) { m_isWebGL2Enabled = isEnabled; } + bool webGL2Enabled() const { return m_isWebGL2Enabled; } +#endif + +#if ENABLE(FETCH_API) + void setFetchAPIEnabled(bool isEnabled) { m_isFetchAPIEnabled = isEnabled; } + bool fetchAPIEnabled() const { return m_isFetchAPIEnabled; } +#endif + +#if ENABLE(DOWNLOAD_ATTRIBUTE) + void setDownloadAttributeEnabled(bool isEnabled) { m_isDownloadAttributeEnabled = isEnabled; } + bool downloadAttributeEnabled() const { return m_isDownloadAttributeEnabled; } +#endif + + void setCSSGridLayoutEnabled(bool isEnabled) { m_cssGridLayoutEnabled = isEnabled; } + bool isCSSGridLayoutEnabled() const { return m_cssGridLayoutEnabled; } + +#if ENABLE(INTERSECTION_OBSERVER) + void setIntersectionObserverEnabled(bool isEnabled) { m_intersectionObserverEnabled = isEnabled; } + bool intersectionObserverEnabled() const { return m_intersectionObserverEnabled; } +#endif + +#if ENABLE(ENCRYPTED_MEDIA) + void setEncryptedMediaAPIEnabled(bool isEnabled) { m_encryptedMediaAPIEnabled = isEnabled; } + bool encryptedMediaAPIEnabled() const { return m_encryptedMediaAPIEnabled; } +#endif + +#if ENABLE(SUBTLE_CRYPTO) + void setSubtleCryptoEnabled(bool isEnabled) { m_isSubtleCryptoEnabled = isEnabled; } + bool subtleCryptoEnabled() const { return m_isSubtleCryptoEnabled; } +#endif + +#if ENABLE(VIDEO) + bool audioEnabled() const; + bool htmlMediaElementEnabled() const; + bool htmlAudioElementEnabled() const; + bool htmlVideoElementEnabled() const; + bool htmlSourceElementEnabled() const; + bool mediaControllerEnabled() const; + bool mediaErrorEnabled() const; + bool timeRangesEnabled() const; +#endif + +#if ENABLE(WEB_SOCKETS) + bool webSocketEnabled() const; +#endif + + WEBCORE_EXPORT static RuntimeEnabledFeatures& sharedFeatures(); + +private: + // Never instantiate. + RuntimeEnabledFeatures(); + + bool m_areModernMediaControlsEnabled { false }; + bool m_isLinkPreloadEnabled { false }; + bool m_isResourceTimingEnabled { false }; + bool m_isUserTimingEnabled { false }; + bool m_isInteractiveFormValidationEnabled { false }; + + bool m_isDOMIteratorEnabled { true }; + bool m_isGeolocationEnabled { true }; + bool m_isShadowDOMEnabled { true }; + bool m_areCustomElementsEnabled { true }; + bool m_inputEventsEnabled { true }; + +#if ENABLE(INDEXED_DATABASE_IN_WORKERS) + bool m_isIndexedDBWorkersEnabled { true }; +#endif + +#if ENABLE(MEDIA_STREAM) + bool m_isMediaStreamEnabled { true }; +#endif + +#if ENABLE(WEB_RTC) + bool m_isPeerConnectionEnabled { true }; +#endif + +#if ENABLE(LEGACY_CSS_VENDOR_PREFIXES) + bool m_isLegacyCSSVendorPrefixesEnabled { false }; +#endif + +#if ENABLE(INPUT_TYPE_DATE) + bool m_isInputTypeDateEnabled { true }; +#endif + +#if ENABLE(INPUT_TYPE_DATETIME_INCOMPLETE) + bool m_isInputTypeDateTimeEnabled { false }; +#endif + +#if ENABLE(INPUT_TYPE_DATETIMELOCAL) + bool m_isInputTypeDateTimeLocalEnabled { true }; +#endif + +#if ENABLE(INPUT_TYPE_MONTH) + bool m_isInputTypeMonthEnabled { true }; +#endif + +#if ENABLE(INPUT_TYPE_TIME) + bool m_isInputTypeTimeEnabled { true }; +#endif + +#if ENABLE(INPUT_TYPE_WEEK) + bool m_isInputTypeWeekEnabled { true }; +#endif + +#if ENABLE(FONT_LOAD_EVENTS) + bool m_isFontLoadEventsEnabled { true }; +#endif + +#if ENABLE(GAMEPAD) + bool m_areGamepadsEnabled { false }; +#endif + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool m_areAnimationTriggersEnabled { false }; +#endif + +#if ENABLE(WEB_ANIMATIONS) + bool m_areWebAnimationsEnabled { false }; +#endif + +#if ENABLE(WEBGL2) + bool m_isWebGL2Enabled { false }; +#endif + +#if ENABLE(FETCH_API) + bool m_isFetchAPIEnabled { true }; +#endif + +#if ENABLE(DOWNLOAD_ATTRIBUTE) + bool m_isDownloadAttributeEnabled { false }; +#endif + + bool m_cssGridLayoutEnabled { true }; + +#if ENABLE(ENCRYPTED_MEDIA) + bool m_encryptedMediaAPIEnabled { false }; +#endif + +#if ENABLE(INTERSECTION_OBSERVER) + bool m_intersectionObserverEnabled { false }; +#endif + +#if ENABLE(SUBTLE_CRYPTO) + bool m_isSubtleCryptoEnabled { true }; +#endif + + friend class WTF::NeverDestroyed<RuntimeEnabledFeatures>; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/Screen.cpp b/Source/WebCore/page/Screen.cpp index 824a20032..ff487d7ca 100644 --- a/Source/WebCore/page/Screen.cpp +++ b/Source/WebCore/page/Screen.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. * @@ -26,17 +26,13 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "config.h" #include "Screen.h" #include "FloatRect.h" #include "Frame.h" #include "FrameView.h" -#include "InspectorInstrumentation.h" #include "PlatformScreen.h" -#include "Settings.h" -#include "Widget.h" namespace WebCore { diff --git a/Source/WebCore/page/Screen.h b/Source/WebCore/page/Screen.h index c78850c1e..c56ff6804 100644 --- a/Source/WebCore/page/Screen.h +++ b/Source/WebCore/page/Screen.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. * @@ -27,35 +27,32 @@ */ -#ifndef Screen_h -#define Screen_h +#pragma once #include "DOMWindowProperty.h" #include "ScriptWrappable.h" -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/Ref.h> namespace WebCore { - class Frame; +class Frame; - class Screen : public ScriptWrappable, public RefCounted<Screen>, public DOMWindowProperty { - public: - static PassRefPtr<Screen> create(Frame *frame) { return adoptRef(new Screen(frame)); } +class Screen final : public ScriptWrappable, public RefCounted<Screen>, public DOMWindowProperty { +public: + static Ref<Screen> create(Frame* frame) { return adoptRef(*new Screen(frame)); } - unsigned height() const; - unsigned width() const; - unsigned colorDepth() const; - unsigned pixelDepth() const; - int availLeft() const; - int availTop() const; - unsigned availHeight() const; - unsigned availWidth() const; + unsigned height() const; + unsigned width() const; + unsigned colorDepth() const; + unsigned pixelDepth() const; + int availLeft() const; + int availTop() const; + unsigned availHeight() const; + unsigned availWidth() const; - private: - explicit Screen(Frame*); - }; +private: + explicit Screen(Frame*); +}; } // namespace WebCore - -#endif // Screen_h diff --git a/Source/WebCore/page/Screen.idl b/Source/WebCore/page/Screen.idl index 8ff1748b8..00073436a 100644 --- a/Source/WebCore/page/Screen.idl +++ b/Source/WebCore/page/Screen.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/GestureTapHighlighter.h b/Source/WebCore/page/ScrollToOptions.h index e8015fe5c..84a9d9ddb 100644 --- a/Source/WebCore/page/GestureTapHighlighter.h +++ b/Source/WebCore/page/ScrollToOptions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies) + * 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 @@ -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,21 +26,15 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GestureTapHighlighter_h -#define GestureTapHighlighter_h +#pragma once -namespace WebCore { - -class AffineTransform; -class Path; -class Node; - -namespace GestureTapHighlighter { +#include <wtf/Optional.h> -Path pathForNodeHighlight(const Node*); - -} // namespace GestureTapHighlighter +namespace WebCore { -} // namespace WebCore +struct ScrollToOptions { + std::optional<double> left; + std::optional<double> top; +}; -#endif // GestureTapHighlighter__h +} diff --git a/Source/WebCore/page/ScrollToOptions.idl b/Source/WebCore/page/ScrollToOptions.idl new file mode 100644 index 000000000..71c845277 --- /dev/null +++ b/Source/WebCore/page/ScrollToOptions.idl @@ -0,0 +1,33 @@ +/* + * 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. + * 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. + * + * 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. + */ + +// FIXME: Support ScrollBehavior. +dictionary ScrollToOptions { + unrestricted double left; + unrestricted double top; +}; diff --git a/Source/WebCore/page/SecurityOrigin.cpp b/Source/WebCore/page/SecurityOrigin.cpp index 2be30d44f..38fa1e9a4 100644 --- a/Source/WebCore/page/SecurityOrigin.cpp +++ b/Source/WebCore/page/SecurityOrigin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007-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,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. * @@ -36,14 +36,12 @@ #include "SecurityPolicy.h" #include "ThreadableBlobRegistry.h" #include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> #include <wtf/text/StringBuilder.h> namespace WebCore { -const int InvalidPort = 0; -const int MaxAllowedPort = 65535; - static bool schemeRequiresHost(const URL& url) { // We expect URLs with these schemes to have authority components. If the @@ -54,11 +52,9 @@ static bool schemeRequiresHost(const URL& url) bool SecurityOrigin::shouldUseInnerURL(const URL& url) { -#if ENABLE(BLOB) // FIXME: Blob URLs don't have inner URLs. Their form is "blob:<inner-origin>/<UUID>", so treating the part after "blob:" as a URL is incorrect. - if (url.protocolIs("blob")) + if (url.protocolIsBlob()) return true; -#endif UNUSED_PARAM(url); return false; } @@ -68,22 +64,16 @@ bool SecurityOrigin::shouldUseInnerURL(const URL& url) // security origin can be parsed using this algorithm. URL SecurityOrigin::extractInnerURL(const URL& url) { - if (url.innerURL()) - return *url.innerURL(); // FIXME: Update this callsite to use the innerURL member function when // we finish implementing it. return URL(ParsedURLString, decodeURLEscapeSequences(url.path())); } -static PassRefPtr<SecurityOrigin> getCachedOrigin(const URL& url) +static RefPtr<SecurityOrigin> getCachedOrigin(const URL& url) { -#if ENABLE(BLOB) - if (url.protocolIs("blob")) + if (url.protocolIsBlob()) return ThreadableBlobRegistry::getCachedOrigin(url); -#else - UNUSED_PARAM(url); -#endif - return 0; + return nullptr; } static bool shouldTreatAsUniqueOrigin(const URL& url) @@ -102,11 +92,7 @@ static bool shouldTreatAsUniqueOrigin(const URL& url) if (schemeRequiresHost(innerURL) && innerURL.host().isEmpty()) return true; - // SchemeRegistry needs a lower case protocol because it uses HashMaps - // that assume the scheme has already been canonicalized. - String protocol = innerURL.protocol().lower(); - - if (SchemeRegistry::shouldTreatURLSchemeAsNoAccess(protocol)) + if (SchemeRegistry::shouldTreatURLSchemeAsNoAccess(innerURL.protocol().toStringWithoutCopying())) return true; // This is the common case. @@ -114,41 +100,28 @@ static bool shouldTreatAsUniqueOrigin(const URL& url) } SecurityOrigin::SecurityOrigin(const URL& url) - : m_protocol(url.protocol().isNull() ? "" : url.protocol().lower()) - , m_host(url.host().isNull() ? "" : url.host().lower()) + : m_protocol(url.protocol().isNull() ? emptyString() : url.protocol().toString().convertToASCIILowercase()) + , m_host(url.host().isNull() ? emptyString() : url.host().convertToASCIILowercase()) , m_port(url.port()) - , m_isUnique(false) - , m_universalAccess(false) - , m_domainWasSetInDOM(false) - , m_storageBlockingPolicy(AllowAllStorage) - , m_enforceFilePathSeparation(false) - , m_needsDatabaseIdentifierQuirkForFiles(false) { // document.domain starts as m_host, but can be set by the DOM. m_domain = m_host; - if (isDefaultPortForProtocol(m_port, m_protocol)) - m_port = InvalidPort; + if (m_port && isDefaultPortForProtocol(m_port.value(), m_protocol)) + m_port = std::nullopt; // By default, only local SecurityOrigins can load local resources. m_canLoadLocalResources = isLocal(); if (m_canLoadLocalResources) - m_filePath = url.path(); // In case enforceFilePathSeparation() is called. + m_filePath = url.fileSystemPath(); // In case enforceFilePathSeparation() is called. } SecurityOrigin::SecurityOrigin() - : m_protocol("") - , m_host("") - , m_domain("") - , m_port(InvalidPort) + : m_protocol(emptyString()) + , m_host(emptyString()) + , m_domain(emptyString()) , m_isUnique(true) - , m_universalAccess(false) - , m_domainWasSetInDOM(false) - , m_canLoadLocalResources(false) - , m_storageBlockingPolicy(AllowAllStorage) - , m_enforceFilePathSeparation(false) - , m_needsDatabaseIdentifierQuirkForFiles(false) { } @@ -164,76 +137,64 @@ SecurityOrigin::SecurityOrigin(const SecurityOrigin* other) , m_canLoadLocalResources(other->m_canLoadLocalResources) , m_storageBlockingPolicy(other->m_storageBlockingPolicy) , m_enforceFilePathSeparation(other->m_enforceFilePathSeparation) - , m_needsDatabaseIdentifierQuirkForFiles(other->m_needsDatabaseIdentifierQuirkForFiles) + , m_needsStorageAccessFromFileURLsQuirk(other->m_needsStorageAccessFromFileURLsQuirk) { } -PassRefPtr<SecurityOrigin> SecurityOrigin::create(const URL& url) +Ref<SecurityOrigin> SecurityOrigin::create(const URL& url) { - RefPtr<SecurityOrigin> cachedOrigin = getCachedOrigin(url); - if (cachedOrigin.get()) - return cachedOrigin; - - if (shouldTreatAsUniqueOrigin(url)) { - RefPtr<SecurityOrigin> origin = adoptRef(new SecurityOrigin()); - - if (url.protocolIs("file")) { - // Unfortunately, we can't represent all unique origins exactly - // the same way because we need to produce a quirky database - // identifier for file URLs due to persistent storage in some - // embedders of WebKit. - origin->m_needsDatabaseIdentifierQuirkForFiles = true; - } + if (RefPtr<SecurityOrigin> cachedOrigin = getCachedOrigin(url)) + return cachedOrigin.releaseNonNull(); - return origin.release(); - } + if (shouldTreatAsUniqueOrigin(url)) + return adoptRef(*new SecurityOrigin); if (shouldUseInnerURL(url)) - return adoptRef(new SecurityOrigin(extractInnerURL(url))); + return adoptRef(*new SecurityOrigin(extractInnerURL(url))); - return adoptRef(new SecurityOrigin(url)); + return adoptRef(*new SecurityOrigin(url)); } -PassRefPtr<SecurityOrigin> SecurityOrigin::createUnique() +Ref<SecurityOrigin> SecurityOrigin::createUnique() { - RefPtr<SecurityOrigin> origin = adoptRef(new SecurityOrigin()); - ASSERT(origin->isUnique()); - return origin.release(); + Ref<SecurityOrigin> origin(adoptRef(*new SecurityOrigin)); + ASSERT(origin.get().isUnique()); + return origin; } -PassRefPtr<SecurityOrigin> SecurityOrigin::isolatedCopy() const +Ref<SecurityOrigin> SecurityOrigin::isolatedCopy() const { - return adoptRef(new SecurityOrigin(this)); + return adoptRef(*new SecurityOrigin(this)); } void SecurityOrigin::setDomainFromDOM(const String& newDomain) { m_domainWasSetInDOM = true; - m_domain = newDomain.lower(); + m_domain = newDomain.convertToASCIILowercase(); } bool SecurityOrigin::isSecure(const URL& url) { // Invalid URLs are secure, as are URLs which have a secure protocol. - if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol())) + if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol().toStringWithoutCopying())) return true; // URLs that wrap inner URLs are secure if those inner URLs are secure. - if (shouldUseInnerURL(url) && SchemeRegistry::shouldTreatURLSchemeAsSecure(extractInnerURL(url).protocol())) + if (shouldUseInnerURL(url) && SchemeRegistry::shouldTreatURLSchemeAsSecure(extractInnerURL(url).protocol().toStringWithoutCopying())) return true; return false; } -bool SecurityOrigin::canAccess(const SecurityOrigin* other) const +bool SecurityOrigin::canAccess(const SecurityOrigin& other) const { if (m_universalAccess) return true; - if (this == other) + if (this == &other) return true; - if (isUnique() || other->isUnique()) + if (isUnique() || other.isUnique()) return false; // Here are two cases where we should permit access: @@ -257,30 +218,30 @@ bool SecurityOrigin::canAccess(const SecurityOrigin* other) const // this is a security vulnerability. bool canAccess = false; - if (m_protocol == other->m_protocol) { - if (!m_domainWasSetInDOM && !other->m_domainWasSetInDOM) { - if (m_host == other->m_host && m_port == other->m_port) + if (m_protocol == other.m_protocol) { + if (!m_domainWasSetInDOM && !other.m_domainWasSetInDOM) { + if (m_host == other.m_host && m_port == other.m_port) canAccess = true; - } else if (m_domainWasSetInDOM && other->m_domainWasSetInDOM) { - if (m_domain == other->m_domain) + } else if (m_domainWasSetInDOM && other.m_domainWasSetInDOM) { + if (m_domain == other.m_domain) canAccess = true; } } if (canAccess && isLocal()) - canAccess = passesFileCheck(other); + canAccess = passesFileCheck(other); return canAccess; } -bool SecurityOrigin::passesFileCheck(const SecurityOrigin* other) const +bool SecurityOrigin::passesFileCheck(const SecurityOrigin& other) const { - ASSERT(isLocal() && other->isLocal()); + ASSERT(isLocal() && other.isLocal()); - if (!m_enforceFilePathSeparation && !other->m_enforceFilePathSeparation) + if (!m_enforceFilePathSeparation && !other.m_enforceFilePathSeparation) return true; - return (m_filePath == other->m_filePath); + return (m_filePath == other.m_filePath); } bool SecurityOrigin::canRequest(const URL& url) const @@ -294,7 +255,7 @@ bool SecurityOrigin::canRequest(const URL& url) const if (isUnique()) return false; - RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url); + Ref<SecurityOrigin> targetOrigin(SecurityOrigin::create(url)); if (targetOrigin->isUnique()) return false; @@ -304,35 +265,18 @@ bool SecurityOrigin::canRequest(const URL& url) const if (isSameSchemeHostPort(targetOrigin.get())) return true; - if (SecurityPolicy::isAccessWhiteListed(this, targetOrigin.get())) + if (SecurityPolicy::isAccessWhiteListed(this, &targetOrigin.get())) return true; return false; } -bool SecurityOrigin::taintsCanvas(const URL& url) const +bool SecurityOrigin::canReceiveDragData(const SecurityOrigin& dragInitiator) const { - if (canRequest(url)) - return false; - - // This function exists because we treat data URLs as having a unique origin, - // contrary to the current (9/19/2009) draft of the HTML5 specification. - // We still want to let folks paint data URLs onto untainted canvases, so - // we special case data URLs below. If we change to match HTML5 w.r.t. - // data URL security, then we can remove this function in favor of - // !canRequest. - if (url.protocolIsData()) - return false; - - return true; -} - -bool SecurityOrigin::canReceiveDragData(const SecurityOrigin* dragInitiator) const -{ - if (this == dragInitiator) + if (this == &dragInitiator) return true; - return canAccess(dragInitiator); + return canAccess(dragInitiator); } // This is a hack to allow keep navigation to http/https feeds working. To remove this @@ -360,16 +304,21 @@ bool SecurityOrigin::canDisplay(const URL& url) const if (m_universalAccess) return true; - String protocol = url.protocol().lower(); +#if !PLATFORM(IOS) + if (m_protocol == "file" && url.isLocalFile() && !filesHaveSameVolume(m_filePath, url.fileSystemPath())) + return false; +#endif if (isFeedWithNestedProtocolInHTTPFamily(url)) return true; + String protocol = url.protocol().toString(); + if (SchemeRegistry::canDisplayOnlyIfCanRequest(protocol)) return canRequest(url); if (SchemeRegistry::shouldTreatURLSchemeAsDisplayIsolated(protocol)) - return m_protocol == protocol || SecurityPolicy::isAccessToURLWhiteListed(this, url); + return equalIgnoringASCIICase(m_protocol, protocol) || SecurityPolicy::isAccessToURLWhiteListed(this, url); if (SecurityPolicy::restrictAccessToLocal() && SchemeRegistry::shouldTreatURLSchemeAsLocal(protocol)) return canLoadLocalResources() || SecurityPolicy::isAccessToURLWhiteListed(this, url); @@ -382,6 +331,9 @@ bool SecurityOrigin::canAccessStorage(const SecurityOrigin* topOrigin, ShouldAll if (isUnique()) return false; + if (isLocal() && !needsStorageAccessFromFileURLsQuirk() && !m_universalAccess && shouldAllowFromThirdParty != AlwaysAllowFromThirdParty) + return false; + if (m_storageBlockingPolicy == BlockAllStorage) return false; @@ -395,7 +347,10 @@ bool SecurityOrigin::canAccessStorage(const SecurityOrigin* topOrigin, ShouldAll if (shouldAllowFromThirdParty == AlwaysAllowFromThirdParty) return true; - if ((m_storageBlockingPolicy == BlockThirdPartyStorage || topOrigin->m_storageBlockingPolicy == BlockThirdPartyStorage) && topOrigin->isThirdParty(this)) + if (m_universalAccess) + return true; + + if ((m_storageBlockingPolicy == BlockThirdPartyStorage || topOrigin->m_storageBlockingPolicy == BlockThirdPartyStorage) && !topOrigin->isSameOriginAs(*this)) return false; return true; @@ -410,18 +365,15 @@ SecurityOrigin::Policy SecurityOrigin::canShowNotifications() const return Ask; } -bool SecurityOrigin::isThirdParty(const SecurityOrigin* child) const +bool SecurityOrigin::isSameOriginAs(const SecurityOrigin& other) const { - if (child->m_universalAccess) - return false; + if (this == &other) + return true; - if (this == child) + if (isUnique() || other.isUnique()) return false; - if (isUnique() || child->isUnique()) - return true; - - return !isSameSchemeHostPort(child); + return isSameSchemeHostPort(other); } void SecurityOrigin::grantLoadLocalResources() @@ -438,18 +390,24 @@ void SecurityOrigin::grantUniversalAccess() m_universalAccess = true; } -#if ENABLE(CACHE_PARTITIONING) -String SecurityOrigin::cachePartition() const +void SecurityOrigin::grantStorageAccessFromFileURLsQuirk() +{ + m_needsStorageAccessFromFileURLsQuirk = true; +} + +String SecurityOrigin::domainForCachePartition() const { if (m_storageBlockingPolicy != BlockThirdPartyStorage) - return String(); + return emptyString(); + + if (isHTTPFamily()) + return host(); - if (m_protocol != "http" && m_protocol != "https") - return String(); + if (SchemeRegistry::shouldPartitionCacheForURLScheme(m_protocol)) + return host(); - return host(); + return emptyString(); } -#endif void SecurityOrigin::enforceFilePathSeparation() { @@ -465,99 +423,79 @@ bool SecurityOrigin::isLocal() const String SecurityOrigin::toString() const { if (isUnique()) - return "null"; + return ASCIILiteral("null"); if (m_protocol == "file" && m_enforceFilePathSeparation) - return "null"; + return ASCIILiteral("null"); return toRawString(); } String SecurityOrigin::toRawString() const { if (m_protocol == "file") - return "file://"; + return ASCIILiteral("file://"); StringBuilder result; result.reserveCapacity(m_protocol.length() + m_host.length() + 10); result.append(m_protocol); - result.append("://"); + result.appendLiteral("://"); result.append(m_host); if (m_port) { result.append(':'); - result.appendNumber(m_port); + result.appendNumber(m_port.value()); } return result.toString(); } -PassRefPtr<SecurityOrigin> SecurityOrigin::createFromString(const String& originString) +static inline bool areOriginsMatching(const SecurityOrigin& origin1, const SecurityOrigin& origin2) { - return SecurityOrigin::create(URL(URL(), originString)); -} + if (origin1.isUnique() || origin2.isUnique()) + return origin1.isUnique() == origin2.isUnique(); -static const char separatorCharacter = '_'; - -PassRefPtr<SecurityOrigin> SecurityOrigin::createFromDatabaseIdentifier(const String& databaseIdentifier) -{ - // Make sure there's a first separator - size_t separator1 = databaseIdentifier.find(separatorCharacter); - if (separator1 == notFound) - return create(URL()); - - // Make sure there's a second separator - size_t separator2 = databaseIdentifier.reverseFind(separatorCharacter); - if (separator2 == notFound) - return create(URL()); - - // Ensure there were at least 2 separator characters. Some hostnames on intranets have - // underscores in them, so we'll assume that any additional underscores are part of the host. - if (separator1 == separator2) - return create(URL()); - - // Make sure the port section is a valid port number or doesn't exist - bool portOkay; - int port = databaseIdentifier.right(databaseIdentifier.length() - separator2 - 1).toInt(&portOkay); - bool portAbsent = (separator2 == databaseIdentifier.length() - 1); - if (!(portOkay || portAbsent)) - return create(URL()); - - if (port < 0 || port > MaxAllowedPort) - return create(URL()); - - // Split out the 3 sections of data - String protocol = databaseIdentifier.substring(0, separator1); - String host = databaseIdentifier.substring(separator1 + 1, separator2 - separator1 - 1); - - host = decodeURLEscapeSequences(host); - return create(URL(URL(), protocol + "://" + host + ":" + String::number(port) + "/")); + if (origin1.protocol() != origin2.protocol()) + return false; + + if (origin1.protocol() == "file") + return true; + + if (origin1.host() != origin2.host()) + return false; + + return origin1.port() == origin2.port(); } -PassRefPtr<SecurityOrigin> SecurityOrigin::create(const String& protocol, const String& host, int port) +// This function mimics the result of string comparison of serialized origins +bool originsMatch(const SecurityOrigin& origin1, const SecurityOrigin& origin2) { - if (port < 0 || port > MaxAllowedPort) - return createUnique(); - String decodedHost = decodeURLEscapeSequences(host); - return create(URL(URL(), protocol + "://" + host + ":" + String::number(port) + "/")); + if (&origin1 == &origin2) + return true; + + bool result = areOriginsMatching(origin1, origin2); + ASSERT(result == (origin1.toString() == origin2.toString())); + return result; } -String SecurityOrigin::databaseIdentifier() const +bool originsMatch(const SecurityOrigin* origin1, const SecurityOrigin* origin2) { - // Historically, we've used the following (somewhat non-sensical) string - // for the databaseIdentifier of local files. We used to compute this - // string because of a bug in how we handled the scheme for file URLs. - // Now that we've fixed that bug, we still need to produce this string - // to avoid breaking existing persistent state. - if (m_needsDatabaseIdentifierQuirkForFiles) - return "file__0"; + if (!origin1 || !origin2) + return origin1 == origin2; - StringBuilder stringBuilder; - stringBuilder.append(m_protocol); - stringBuilder.append(separatorCharacter); - stringBuilder.append(encodeForFileName(m_host)); - stringBuilder.append(separatorCharacter); - stringBuilder.appendNumber(m_port); + return originsMatch(*origin1, *origin2); +} - return stringBuilder.toString(); +Ref<SecurityOrigin> SecurityOrigin::createFromString(const String& originString) +{ + return SecurityOrigin::create(URL(URL(), originString)); +} + +Ref<SecurityOrigin> SecurityOrigin::create(const String& protocol, const String& host, std::optional<uint16_t> port) +{ + String decodedHost = decodeURLEscapeSequences(host); + auto origin = create(URL(URL(), protocol + "://" + host + "/")); + if (port && !isDefaultPortForProtocol(*port, protocol)) + origin->m_port = port; + return origin; } bool SecurityOrigin::equal(const SecurityOrigin* other) const @@ -565,7 +503,7 @@ bool SecurityOrigin::equal(const SecurityOrigin* other) const if (other == this) return true; - if (!isSameSchemeHostPort(other)) + if (!isSameSchemeHostPort(*other)) return false; if (m_domainWasSetInDOM != other->m_domainWasSetInDOM) @@ -577,15 +515,15 @@ bool SecurityOrigin::equal(const SecurityOrigin* other) const return true; } -bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const +bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin& other) const { - if (m_host != other->m_host) + if (m_host != other.m_host) return false; - if (m_protocol != other->m_protocol) + if (m_protocol != other.m_protocol) return false; - if (m_port != other->m_port) + if (m_port != other.m_port) return false; if (isLocal() && !passesFileCheck(other)) @@ -594,10 +532,10 @@ bool SecurityOrigin::isSameSchemeHostPort(const SecurityOrigin* other) const return true; } -String SecurityOrigin::urlWithUniqueSecurityOrigin() +URL SecurityOrigin::urlWithUniqueSecurityOrigin() { ASSERT(isMainThread()); - DEFINE_STATIC_LOCAL(const String, uniqueSecurityOriginURL, (ASCIILiteral("data:,"))); + static NeverDestroyed<URL> uniqueSecurityOriginURL(ParsedURLString, ASCIILiteral("data:,")); return uniqueSecurityOriginURL; } diff --git a/Source/WebCore/page/SecurityOrigin.h b/Source/WebCore/page/SecurityOrigin.h index d11866de0..e63f4bd22 100644 --- a/Source/WebCore/page/SecurityOrigin.h +++ b/Source/WebCore/page/SecurityOrigin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007-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,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,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SecurityOrigin_h -#define SecurityOrigin_h +#pragma once #include <wtf/ThreadSafeRefCounted.h> #include <wtf/text/WTFString.h> @@ -50,12 +49,11 @@ public: BlockAllStorage }; - static PassRefPtr<SecurityOrigin> create(const URL&); - static PassRefPtr<SecurityOrigin> createUnique(); + WEBCORE_EXPORT static Ref<SecurityOrigin> create(const URL&); + static Ref<SecurityOrigin> createUnique(); - static PassRefPtr<SecurityOrigin> createFromDatabaseIdentifier(const String&); - static PassRefPtr<SecurityOrigin> createFromString(const String&); - static PassRefPtr<SecurityOrigin> create(const String& protocol, const String& host, int port); + WEBCORE_EXPORT static Ref<SecurityOrigin> createFromString(const String&); + WEBCORE_EXPORT static Ref<SecurityOrigin> create(const String& protocol, const String& host, std::optional<uint16_t> port); // Some URL schemes use nested URLs for their security context. For example, // filesystem URLs look like the following: @@ -72,7 +70,7 @@ public: // Create a deep copy of this SecurityOrigin. This method is useful // when marshalling a SecurityOrigin to another thread. - PassRefPtr<SecurityOrigin> isolatedCopy() const; + WEBCORE_EXPORT Ref<SecurityOrigin> isolatedCopy() const; // Set the domain property of this security origin to newDomain. This // function does not check whether newDomain is a suffix of the current @@ -80,10 +78,10 @@ public: void setDomainFromDOM(const String& newDomain); bool domainWasSetInDOM() const { return m_domainWasSetInDOM; } - String protocol() const { return m_protocol; } - String host() const { return m_host; } - String domain() const { return m_domain; } - unsigned short port() const { return m_port; } + const String& protocol() const { return m_protocol; } + const String& host() const { return m_host; } + const String& domain() const { return m_domain; } + std::optional<uint16_t> port() const { return m_port; } // Returns true if a given URL is secure, based either directly on its // own protocol, or, when relevant, on the protocol of its "inner URL" @@ -94,27 +92,22 @@ public: // SecurityOrigin. For example, call this function before allowing // script from one security origin to read or write objects from // another SecurityOrigin. - bool canAccess(const SecurityOrigin*) const; + WEBCORE_EXPORT bool canAccess(const SecurityOrigin&) const; // Returns true if this SecurityOrigin can read content retrieved from // the given URL. For example, call this function before issuing // XMLHttpRequests. bool canRequest(const URL&) const; - // Returns true if drawing an image from this URL taints a canvas from - // this security origin. For example, call this function before - // drawing an image onto an HTML canvas element with the drawImage API. - bool taintsCanvas(const URL&) const; - // Returns true if this SecurityOrigin can receive drag content from the // initiator. For example, call this function before allowing content to be // dropped onto a target. - bool canReceiveDragData(const SecurityOrigin* dragInitiator) const; + bool canReceiveDragData(const SecurityOrigin& dragInitiator) const; // Returns true if |document| can display content from the given URL (e.g., // in an iframe or as an image). For example, web sites generally cannot // display content from the user's files system. - bool canDisplay(const URL&) const; + WEBCORE_EXPORT bool canDisplay(const URL&) const; // Returns true if this SecurityOrigin can load local resources, such // as images, iframes, and style sheets, and can link to local URLs. @@ -138,28 +131,28 @@ public: // // WARNING: This is an extremely powerful ability. Use with caution! void grantUniversalAccess(); + bool hasUniversalAccess() const { return m_universalAccess; } void setStorageBlockingPolicy(StorageBlockingPolicy policy) { m_storageBlockingPolicy = policy; } -#if ENABLE(CACHE_PARTITIONING) - String cachePartition() const; -#endif + void grantStorageAccessFromFileURLsQuirk(); + bool needsStorageAccessFromFileURLsQuirk() const { return m_needsStorageAccessFromFileURLsQuirk; } + + WEBCORE_EXPORT String domainForCachePartition() const; - bool canAccessDatabase(const SecurityOrigin* topOrigin = 0) const { return canAccessStorage(topOrigin); }; - bool canAccessSessionStorage(const SecurityOrigin* topOrigin) const { return canAccessStorage(topOrigin, AlwaysAllowFromThirdParty); } + bool canAccessDatabase(const SecurityOrigin& topOrigin) const { return canAccessStorage(&topOrigin); }; + bool canAccessSessionStorage(const SecurityOrigin& topOrigin) const { return canAccessStorage(&topOrigin, AlwaysAllowFromThirdParty); } bool canAccessLocalStorage(const SecurityOrigin* topOrigin) const { return canAccessStorage(topOrigin); }; - bool canAccessSharedWorkers(const SecurityOrigin* topOrigin) const { return canAccessStorage(topOrigin); } - bool canAccessPluginStorage(const SecurityOrigin* topOrigin) const { return canAccessStorage(topOrigin); } - bool canAccessApplicationCache(const SecurityOrigin* topOrigin) const { return canAccessStorage(topOrigin); } + bool canAccessPluginStorage(const SecurityOrigin& topOrigin) const { return canAccessStorage(&topOrigin); } + bool canAccessApplicationCache(const SecurityOrigin& topOrigin) const { return canAccessStorage(&topOrigin); } bool canAccessCookies() const { return !isUnique(); } - bool canAccessPasswordManager() const { return !isUnique(); } - bool canAccessFileSystem() const { return !isUnique(); } + bool canRequestGeolocation() const { return !isUnique(); } Policy canShowNotifications() const; // The local SecurityOrigin is the most privileged SecurityOrigin. // The local SecurityOrigin can script any document, navigate to local // resources, and can set arbitrary headers on XMLHttpRequests. - bool isLocal() const; + WEBCORE_EXPORT bool isLocal() const; // The origin is a globally unique identifier assigned when the Document is // created. http://www.whatwg.org/specs/web-apps/current-work/#sandboxOrigin @@ -184,28 +177,28 @@ public: // this SecurityOrigin might have come from a sandboxed iframe, the // SecurityOrigin might be empty, or we might have explicitly decided that // we shouldTreatURLSchemeAsNoAccess. - String toString() const; + WEBCORE_EXPORT String toString() const; // Similar to toString(), but does not take into account any factors that // could make the string return "null". - String toRawString() const; - - // Serialize the security origin to a string that could be used as part of - // file names. This format should be used in storage APIs only. - String databaseIdentifier() const; + WEBCORE_EXPORT String toRawString() const; // This method checks for equality between SecurityOrigins, not whether // one origin can access another. It is used for hash table keys. // For access checks, use canAccess(). // FIXME: If this method is really only useful for hash table keys, it // should be refactored into SecurityOriginHash. - bool equal(const SecurityOrigin*) const; + WEBCORE_EXPORT bool equal(const SecurityOrigin*) const; // This method checks for equality, ignoring the value of document.domain // (and whether it was set) but considering the host. It is used for postMessage. - bool isSameSchemeHostPort(const SecurityOrigin*) const; + WEBCORE_EXPORT bool isSameSchemeHostPort(const SecurityOrigin&) const; + + // This method implements the "same origin" algorithm from the HTML Standard: + // https://html.spec.whatwg.org/multipage/browsers.html#same-origin + WEBCORE_EXPORT bool isSameOriginAs(const SecurityOrigin&) const; - static String urlWithUniqueSecurityOrigin(); + static URL urlWithUniqueSecurityOrigin(); private: SecurityOrigin(); @@ -213,26 +206,31 @@ private: explicit SecurityOrigin(const SecurityOrigin*); // FIXME: Rename this function to something more semantic. - bool passesFileCheck(const SecurityOrigin*) const; - bool isThirdParty(const SecurityOrigin*) const; + bool passesFileCheck(const SecurityOrigin&) const; + + // This method checks that the scheme for this origin is an HTTP-family + // scheme, e.g. HTTP and HTTPS. + bool isHTTPFamily() const { return m_protocol == "http" || m_protocol == "https"; } enum ShouldAllowFromThirdParty { AlwaysAllowFromThirdParty, MaybeAllowFromThirdParty }; - bool canAccessStorage(const SecurityOrigin*, ShouldAllowFromThirdParty = MaybeAllowFromThirdParty) const; + WEBCORE_EXPORT bool canAccessStorage(const SecurityOrigin*, ShouldAllowFromThirdParty = MaybeAllowFromThirdParty) const; String m_protocol; String m_host; String m_domain; String m_filePath; - unsigned short m_port; - bool m_isUnique; - bool m_universalAccess; - bool m_domainWasSetInDOM; - bool m_canLoadLocalResources; - StorageBlockingPolicy m_storageBlockingPolicy; - bool m_enforceFilePathSeparation; - bool m_needsDatabaseIdentifierQuirkForFiles; + std::optional<uint16_t> m_port; + bool m_isUnique { false }; + bool m_universalAccess { false }; + bool m_domainWasSetInDOM { false }; + bool m_canLoadLocalResources { false }; + StorageBlockingPolicy m_storageBlockingPolicy { AllowAllStorage }; + bool m_enforceFilePathSeparation { false }; + bool m_needsStorageAccessFromFileURLsQuirk { false }; }; -} // namespace WebCore +// Returns true if the Origin header values serialized from these two origins would be the same. +bool originsMatch(const SecurityOrigin&, const SecurityOrigin&); +bool originsMatch(const SecurityOrigin*, const SecurityOrigin*); -#endif // SecurityOrigin_h +} // namespace WebCore diff --git a/Source/WebCore/page/SecurityOriginData.cpp b/Source/WebCore/page/SecurityOriginData.cpp new file mode 100644 index 000000000..e2e44f062 --- /dev/null +++ b/Source/WebCore/page/SecurityOriginData.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2011, 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 "SecurityOriginData.h" + +#include "Document.h" +#include "Frame.h" +#include "SecurityOrigin.h" +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +using namespace WebCore; + +namespace WebCore { + +SecurityOriginData SecurityOriginData::fromSecurityOrigin(const SecurityOrigin& securityOrigin) +{ + SecurityOriginData securityOriginData; + + securityOriginData.protocol = securityOrigin.protocol(); + securityOriginData.host = securityOrigin.host(); + securityOriginData.port = securityOrigin.port(); + + return securityOriginData; +} + +#if !LOG_DISABLED +String SecurityOriginData::debugString() const +{ + return makeString(protocol, "://", host, ":", String::number(port.value_or(0))); +} +#endif + +SecurityOriginData SecurityOriginData::fromFrame(Frame* frame) +{ + if (!frame) + return SecurityOriginData(); + + Document* document = frame->document(); + if (!document) + return SecurityOriginData(); + + return SecurityOriginData::fromSecurityOrigin(document->securityOrigin()); +} + +Ref<SecurityOrigin> SecurityOriginData::securityOrigin() const +{ + return SecurityOrigin::create(protocol.isolatedCopy(), host.isolatedCopy(), port); +} + +static const char separatorCharacter = '_'; + +String SecurityOriginData::databaseIdentifier() const +{ + // Historically, we've used the following (somewhat non-sensical) string + // for the databaseIdentifier of local files. We used to compute this + // string because of a bug in how we handled the scheme for file URLs. + // Now that we've fixed that bug, we still need to produce this string + // to avoid breaking existing persistent state. + if (equalIgnoringASCIICase(protocol, "file")) + return ASCIILiteral("file__0"); + + StringBuilder stringBuilder; + stringBuilder.append(protocol); + stringBuilder.append(separatorCharacter); + stringBuilder.append(encodeForFileName(host)); + stringBuilder.append(separatorCharacter); + stringBuilder.appendNumber(port.value_or(0)); + + return stringBuilder.toString(); +} + +std::optional<SecurityOriginData> SecurityOriginData::fromDatabaseIdentifier(const String& databaseIdentifier) +{ + // Make sure there's a first separator + size_t separator1 = databaseIdentifier.find(separatorCharacter); + if (separator1 == notFound) + return std::nullopt; + + // Make sure there's a second separator + size_t separator2 = databaseIdentifier.reverseFind(separatorCharacter); + if (separator2 == notFound) + return std::nullopt; + + // Ensure there were at least 2 separator characters. Some hostnames on intranets have + // underscores in them, so we'll assume that any additional underscores are part of the host. + if (separator1 == separator2) + return std::nullopt; + + // Make sure the port section is a valid port number or doesn't exist + bool portOkay; + int port = databaseIdentifier.right(databaseIdentifier.length() - separator2 - 1).toInt(&portOkay); + bool portAbsent = (separator2 == databaseIdentifier.length() - 1); + if (!(portOkay || portAbsent)) + return std::nullopt; + + if (port < 0 || port > std::numeric_limits<uint16_t>::max()) + return std::nullopt; + + return SecurityOriginData {databaseIdentifier.substring(0, separator1), databaseIdentifier.substring(separator1 + 1, separator2 - separator1 - 1), static_cast<uint16_t>(port)}; +} + +SecurityOriginData SecurityOriginData::isolatedCopy() const +{ + SecurityOriginData result; + + result.protocol = protocol.isolatedCopy(); + result.host = host.isolatedCopy(); + result.port = port; + + return result; +} + +bool operator==(const SecurityOriginData& a, const SecurityOriginData& b) +{ + if (&a == &b) + return true; + + return a.protocol == b.protocol + && a.host == b.host + && a.port == b.port; +} + +} // namespace WebCore diff --git a/Source/WebCore/page/SecurityOriginData.h b/Source/WebCore/page/SecurityOriginData.h new file mode 100644 index 000000000..82de09699 --- /dev/null +++ b/Source/WebCore/page/SecurityOriginData.h @@ -0,0 +1,137 @@ + /* + * Copyright (C) 2011, 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 <wtf/text/WTFString.h> + +namespace WebCore { + +class Frame; +class SecurityOrigin; + +struct SecurityOriginData { + SecurityOriginData() = default; + SecurityOriginData(const String& protocol, const String& host, std::optional<uint16_t> port) + : protocol(protocol) + , host(host) + , port(port) + { + } + SecurityOriginData(WTF::HashTableDeletedValueType) + : protocol(WTF::HashTableDeletedValue) + { + } + + WEBCORE_EXPORT static SecurityOriginData fromSecurityOrigin(const SecurityOrigin&); + WEBCORE_EXPORT static SecurityOriginData fromFrame(Frame*); + + WEBCORE_EXPORT Ref<SecurityOrigin> securityOrigin() const; + + // FIXME <rdar://9018386>: We should be sending more state across the wire than just the protocol, + // host, and port. + + String protocol; + String host; + std::optional<uint16_t> port; + + WEBCORE_EXPORT SecurityOriginData isolatedCopy() const; + + // Serialize the security origin to a string that could be used as part of + // file names. This format should be used in storage APIs only. + WEBCORE_EXPORT String databaseIdentifier() const; + WEBCORE_EXPORT static std::optional<SecurityOriginData> fromDatabaseIdentifier(const String&); + + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static bool decode(Decoder&, SecurityOriginData&); + + bool isEmpty() const + { + return protocol.isNull() && host.isNull() && port == std::nullopt; + } + + bool isHashTableDeletedValue() const + { + return protocol.isHashTableDeletedValue(); + } + +#if !LOG_DISABLED + WEBCORE_EXPORT String debugString() const; +#endif +}; + +WEBCORE_EXPORT bool operator==(const SecurityOriginData&, const SecurityOriginData&); + +template<class Encoder> +void SecurityOriginData::encode(Encoder& encoder) const +{ + encoder << protocol; + encoder << host; + encoder << port; +} + +template<class Decoder> +bool SecurityOriginData::decode(Decoder& decoder, SecurityOriginData& securityOriginData) +{ + if (!decoder.decode(securityOriginData.protocol)) + return false; + if (!decoder.decode(securityOriginData.host)) + return false; + if (!decoder.decode(securityOriginData.port)) + return false; + + return true; +} + +struct SecurityOriginDataHashTraits : WTF::SimpleClassHashTraits<SecurityOriginData> { + static const bool hasIsEmptyValueFunction = true; + static const bool emptyValueIsZero = false; + static bool isEmptyValue(const SecurityOriginData& data) { return data.isEmpty(); } +}; + +struct SecurityOriginDataHash { + static unsigned hash(const SecurityOriginData& data) + { + unsigned hashCodes[3] = { + data.protocol.impl() ? data.protocol.impl()->hash() : 0, + data.host.impl() ? data.host.impl()->hash() : 0, + data.port.value_or(0) + }; + return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes); + } + static bool equal(const SecurityOriginData& a, const SecurityOriginData& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = false; +}; + +} // namespace WebCore + +namespace WTF { + +template<> struct HashTraits<WebCore::SecurityOriginData> : WebCore::SecurityOriginDataHashTraits { }; +template<> struct DefaultHash<WebCore::SecurityOriginData> { + typedef WebCore::SecurityOriginDataHash Hash; +}; + +} // namespace WTF diff --git a/Source/WebCore/page/SecurityOriginHash.h b/Source/WebCore/page/SecurityOriginHash.h index 9886c7d04..ee2572c5a 100644 --- a/Source/WebCore/page/SecurityOriginHash.h +++ b/Source/WebCore/page/SecurityOriginHash.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,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SecurityOriginHash_h -#define SecurityOriginHash_h +#pragma once #include "URL.h" #include "SecurityOrigin.h" @@ -41,7 +40,7 @@ struct SecurityOriginHash { unsigned hashCodes[3] = { origin->protocol().impl() ? origin->protocol().impl()->hash() : 0, origin->host().impl() ? origin->host().impl()->hash() : 0, - origin->port() + origin->port().value_or(0) }; return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes); } @@ -54,7 +53,7 @@ struct SecurityOriginHash { { if (!a || !b) return a == b; - return a->isSameSchemeHostPort(b); + return a->isSameSchemeHostPort(*b); } static bool equal(SecurityOrigin* a, const RefPtr<SecurityOrigin>& b) { @@ -82,5 +81,3 @@ namespace WTF { }; } // namespace WTF - -#endif // SecurityOriginHash_h diff --git a/Source/WebCore/page/SecurityPolicy.cpp b/Source/WebCore/page/SecurityPolicy.cpp index 9d18879f6..18f311d46 100644 --- a/Source/WebCore/page/SecurityPolicy.cpp +++ b/Source/WebCore/page/SecurityPolicy.cpp @@ -29,12 +29,13 @@ #include "config.h" #include "SecurityPolicy.h" -#include "URL.h" -#include <wtf/MainThread.h> #include "OriginAccessEntry.h" #include "SecurityOrigin.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include "URL.h" +#include <memory> +#include <wtf/HashMap.h> +#include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> #include <wtf/text/StringHash.h> namespace WebCore { @@ -42,11 +43,11 @@ namespace WebCore { static SecurityPolicy::LocalLoadPolicy localLoadPolicy = SecurityPolicy::AllowLocalLoadsForLocalOnly; typedef Vector<OriginAccessEntry> OriginAccessWhiteList; -typedef HashMap<String, OwnPtr<OriginAccessWhiteList>> OriginAccessMap; +typedef HashMap<String, std::unique_ptr<OriginAccessWhiteList>> OriginAccessMap; static OriginAccessMap& originAccessMap() { - DEFINE_STATIC_LOCAL(OriginAccessMap, originAccessMap, ()); + static NeverDestroyed<OriginAccessMap> originAccessMap; return originAccessMap; } @@ -68,6 +69,8 @@ bool SecurityPolicy::shouldHideReferrer(const URL& url, const String& referrer) String SecurityPolicy::generateReferrerHeader(ReferrerPolicy referrerPolicy, const URL& url, const String& referrer) { + ASSERT(referrer == URL(URL(), referrer).strippedForUseAsReferrer()); + if (referrer.isEmpty()) return String(); @@ -75,11 +78,11 @@ String SecurityPolicy::generateReferrerHeader(ReferrerPolicy referrerPolicy, con return String(); switch (referrerPolicy) { - case ReferrerPolicyNever: + case ReferrerPolicy::Never: return String(); - case ReferrerPolicyAlways: + case ReferrerPolicy::Always: return referrer; - case ReferrerPolicyOrigin: { + case ReferrerPolicy::Origin: { String origin = SecurityOrigin::createFromString(referrer)->toString(); if (origin == "null") return String(); @@ -87,7 +90,7 @@ String SecurityPolicy::generateReferrerHeader(ReferrerPolicy referrerPolicy, con // to turn it into a canonical URL we can use as referrer. return origin + "/"; } - case ReferrerPolicyDefault: + case ReferrerPolicy::Default: break; } @@ -112,18 +115,18 @@ bool SecurityPolicy::allowSubstituteDataAccessToLocal() bool SecurityPolicy::isAccessWhiteListed(const SecurityOrigin* activeOrigin, const SecurityOrigin* targetOrigin) { if (OriginAccessWhiteList* list = originAccessMap().get(activeOrigin->toString())) { - for (size_t i = 0; i < list->size(); ++i) { - if (list->at(i).matchesOrigin(*targetOrigin)) - return true; - } + for (auto& entry : *list) { + if (entry.matchesOrigin(*targetOrigin)) + return true; + } } return false; } bool SecurityPolicy::isAccessToURLWhiteListed(const SecurityOrigin* activeOrigin, const URL& url) { - RefPtr<SecurityOrigin> targetOrigin = SecurityOrigin::create(url); - return isAccessWhiteListed(activeOrigin, targetOrigin.get()); + Ref<SecurityOrigin> targetOrigin(SecurityOrigin::create(url)); + return isAccessWhiteListed(activeOrigin, &targetOrigin.get()); } void SecurityPolicy::addOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomain, bool allowDestinationSubdomains) @@ -136,10 +139,10 @@ void SecurityPolicy::addOriginAccessWhitelistEntry(const SecurityOrigin& sourceO String sourceString = sourceOrigin.toString(); OriginAccessMap::AddResult result = originAccessMap().add(sourceString, nullptr); if (result.isNewEntry) - result.iterator->value = adoptPtr(new OriginAccessWhiteList); + result.iterator->value = std::make_unique<OriginAccessWhiteList>(); OriginAccessWhiteList* list = result.iterator->value.get(); - list->append(OriginAccessEntry(destinationProtocol, destinationDomain, allowDestinationSubdomains ? OriginAccessEntry::AllowSubdomains : OriginAccessEntry::DisallowSubdomains)); + list->append(OriginAccessEntry(destinationProtocol, destinationDomain, allowDestinationSubdomains ? OriginAccessEntry::AllowSubdomains : OriginAccessEntry::DisallowSubdomains, OriginAccessEntry::TreatIPAddressAsIPAddress)); } void SecurityPolicy::removeOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomain, bool allowDestinationSubdomains) @@ -155,14 +158,12 @@ void SecurityPolicy::removeOriginAccessWhitelistEntry(const SecurityOrigin& sour if (it == map.end()) return; - OriginAccessWhiteList* list = it->value.get(); - size_t index = list->find(OriginAccessEntry(destinationProtocol, destinationDomain, allowDestinationSubdomains ? OriginAccessEntry::AllowSubdomains : OriginAccessEntry::DisallowSubdomains)); - if (index == notFound) + OriginAccessWhiteList& list = *it->value; + OriginAccessEntry originAccessEntry(destinationProtocol, destinationDomain, allowDestinationSubdomains ? OriginAccessEntry::AllowSubdomains : OriginAccessEntry::DisallowSubdomains, OriginAccessEntry::TreatIPAddressAsIPAddress); + if (!list.removeFirst(originAccessEntry)) return; - list->remove(index); - - if (list->isEmpty()) + if (list.isEmpty()) map.remove(it); } diff --git a/Source/WebCore/page/SecurityPolicy.h b/Source/WebCore/page/SecurityPolicy.h index b269c1af5..6c818fd62 100644 --- a/Source/WebCore/page/SecurityPolicy.h +++ b/Source/WebCore/page/SecurityPolicy.h @@ -26,28 +26,26 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SecurityPolicy_h -#define SecurityPolicy_h +#pragma once #include "ReferrerPolicy.h" #include <wtf/text/WTFString.h> namespace WebCore { -class URL; class SecurityOrigin; +class URL; class SecurityPolicy { public: - // True if the referrer should be omitted according to the - // ReferrerPolicyDefault. If you intend to send a referrer header, you - // should use generateReferrerHeader instead. - static bool shouldHideReferrer(const URL&, const String& referrer); + // True if the referrer should be omitted according to ReferrerPolicy::Default. + // If you intend to send a referrer header, you should use generateReferrerHeader instead. + WEBCORE_EXPORT static bool shouldHideReferrer(const URL&, const String& referrer); // Returns the referrer modified according to the referrer policy for a // navigation to a given URL. If the referrer returned is empty, the // referrer header should be omitted. - static String generateReferrerHeader(ReferrerPolicy, const URL&, const String& referrer); + WEBCORE_EXPORT static String generateReferrerHeader(ReferrerPolicy, const URL&, const String& referrer); enum LocalLoadPolicy { AllowLocalLoadsForAll, // No restriction on local loads. @@ -55,18 +53,16 @@ public: AllowLocalLoadsForLocalOnly, }; - static void setLocalLoadPolicy(LocalLoadPolicy); + WEBCORE_EXPORT static void setLocalLoadPolicy(LocalLoadPolicy); static bool restrictAccessToLocal(); static bool allowSubstituteDataAccessToLocal(); - static void addOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomain, bool allowDestinationSubdomains); - static void removeOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomain, bool allowDestinationSubdomains); - static void resetOriginAccessWhitelists(); + WEBCORE_EXPORT static void addOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomain, bool allowDestinationSubdomains); + WEBCORE_EXPORT static void removeOriginAccessWhitelistEntry(const SecurityOrigin& sourceOrigin, const String& destinationProtocol, const String& destinationDomain, bool allowDestinationSubdomains); + WEBCORE_EXPORT static void resetOriginAccessWhitelists(); static bool isAccessWhiteListed(const SecurityOrigin* activeOrigin, const SecurityOrigin* targetOrigin); static bool isAccessToURLWhiteListed(const SecurityOrigin* activeOrigin, const URL&); }; } // namespace WebCore - -#endif // SecurityPolicy_h diff --git a/Source/WebCore/page/SessionID.h b/Source/WebCore/page/SessionID.h new file mode 100644 index 000000000..a58531494 --- /dev/null +++ b/Source/WebCore/page/SessionID.h @@ -0,0 +1,74 @@ +/* + * 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 <wtf/HashFunctions.h> +#include <wtf/HashTraits.h> + +namespace WebCore { + +class SessionID { +public: + SessionID() + : SessionID(emptySessionID()) { } + explicit SessionID(uint64_t sessionID) + : m_sessionID(sessionID) { } + bool isValid() const { return *this != emptySessionID(); } + bool isEphemeral() const { return *this != defaultSessionID(); } + uint64_t sessionID() const { return m_sessionID; } + bool operator==(SessionID sessionID) const { return m_sessionID == sessionID.m_sessionID; } + bool operator!=(SessionID sessionID) const { return m_sessionID != sessionID.m_sessionID; } + bool isAlwaysOnLoggingAllowed() const { return !isEphemeral(); } + + static SessionID emptySessionID() { return SessionID(0); } + static SessionID defaultSessionID() { return SessionID(1); } + static SessionID legacyPrivateSessionID() { return SessionID(2); } +private: + uint64_t m_sessionID; +}; + +} // namespace WebCore + +namespace WTF { + +// The empty value is emptySessionID(), the deleted value is (-1) +struct SessionIDHash { + static unsigned hash(const WebCore::SessionID& p) { return intHash(p.sessionID()); } + static bool equal(const WebCore::SessionID& a, const WebCore::SessionID& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; +template<> struct HashTraits<WebCore::SessionID> : GenericHashTraits<WebCore::SessionID> { + static const uint64_t deletedValueIdentifier = std::numeric_limits<uint64_t>::max(); + static WebCore::SessionID emptyValue() { return WebCore::SessionID::emptySessionID(); } + + static void constructDeletedValue(WebCore::SessionID& slot) { slot = WebCore::SessionID(deletedValueIdentifier); } + static bool isDeletedValue(const WebCore::SessionID& slot) { return slot == WebCore::SessionID(deletedValueIdentifier); } +}; +template<> struct DefaultHash<WebCore::SessionID> { + typedef SessionIDHash Hash; +}; + +} // namespace WTF diff --git a/Source/WebCore/page/Settings.cpp b/Source/WebCore/page/Settings.cpp index 681c3a69d..6bb9d03a8 100644 --- a/Source/WebCore/page/Settings.cpp +++ b/Source/WebCore/page/Settings.cpp @@ -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 @@ -33,22 +33,25 @@ #include "DOMTimer.h" #include "Database.h" #include "Document.h" -#include "Font.h" +#include "FontCascade.h" #include "FontGenericFamilies.h" #include "FrameTree.h" #include "FrameView.h" #include "HTMLMediaElement.h" #include "HistoryItem.h" -#include "InspectorInstrumentation.h" #include "MainFrame.h" #include "Page.h" #include "PageCache.h" +#include "RuntimeApplicationChecks.h" #include "StorageMap.h" -#include "TextAutosizer.h" #include <limits> #include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> +#if ENABLE(MEDIA_STREAM) +#include "MockRealtimeMediaSourceCenter.h" +#endif + namespace WebCore { static void setImageLoadingSettings(Page* page) @@ -57,40 +60,41 @@ static void setImageLoadingSettings(Page* page) return; for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) { - frame->document()->cachedResourceLoader()->setImagesEnabled(page->settings().areImagesEnabled()); - frame->document()->cachedResourceLoader()->setAutoLoadImages(page->settings().loadsImagesAutomatically()); + if (!frame->document()) + continue; + frame->document()->cachedResourceLoader().setImagesEnabled(page->settings().areImagesEnabled()); + frame->document()->cachedResourceLoader().setAutoLoadImages(page->settings().loadsImagesAutomatically()); } } static void invalidateAfterGenericFamilyChange(Page* page) { - invalidateFontGlyphsCache(); + invalidateFontCascadeCache(); if (page) page->setNeedsRecalcStyleInAllFrames(); } -double Settings::gDefaultMinDOMTimerInterval = 0.010; // 10 milliseconds -double Settings::gDefaultDOMTimerAlignmentInterval = 0; -double Settings::gHiddenPageDOMTimerAlignmentInterval = 1.0; - -#if USE(SAFARI_THEME) -bool Settings::gShouldPaintNativeControls = true; -#endif - #if USE(AVFOUNDATION) -bool Settings::gAVFoundationEnabled = false; +bool Settings::gAVFoundationEnabled = true; +bool Settings::gAVFoundationNSURLSessionEnabled = true; #endif -#if PLATFORM(MAC) -bool Settings::gQTKitEnabled = true; +#if PLATFORM(COCOA) +bool Settings::gQTKitEnabled = false; #endif -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -bool Settings::gVideoPluginProxyEnabled = true; +#if USE(GSTREAMER) +bool Settings::gGStreamerEnabled = true; #endif bool Settings::gMockScrollbarsEnabled = false; bool Settings::gUsesOverlayScrollbars = false; +bool Settings::gMockScrollAnimatorEnabled = false; + +#if ENABLE(MEDIA_STREAM) +bool Settings::gMockCaptureDevicesEnabled = false; +bool Settings::gMediaCaptureRequiresSecureConnection = true; +#endif #if PLATFORM(WIN) bool Settings::gShouldUseHighResolutionTimers = true; @@ -98,21 +102,27 @@ bool Settings::gShouldUseHighResolutionTimers = true; bool Settings::gShouldRespectPriorityInCSSAttributeSetters = false; bool Settings::gLowPowerVideoAudioBufferSizeEnabled = false; +bool Settings::gResourceLoadStatisticsEnabledEnabled = false; +bool Settings::gAllowsAnySSLCertificate = false; #if PLATFORM(IOS) bool Settings::gNetworkDataUsageTrackingEnabled = false; bool Settings::gAVKitEnabled = false; +bool Settings::gShouldOptOutOfNetworkStateObservation = false; +bool Settings::gManageAudioSession = false; #endif // NOTEs // 1) EditingMacBehavior comprises Tiger, Leopard, SnowLeopard and iOS builds, as well as QtWebKit when built on Mac; -// 2) EditingWindowsBehavior comprises Win32 and WinCE builds, as well as QtWebKit and Chromium when built on Windows; +// 2) EditingWindowsBehavior comprises Win32 build; // 3) EditingUnixBehavior comprises all unix-based systems, but Darwin/MacOS (and then abusing the terminology); // 99) MacEditingBehavior is used as a fallback. static EditingBehaviorType editingBehaviorTypeForPlatform() { return -#if OS(DARWIN) +#if PLATFORM(IOS) + EditingIOSBehavior +#elif OS(DARWIN) EditingMacBehavior #elif OS(WINDOWS) EditingWindowsBehavior @@ -125,18 +135,46 @@ static EditingBehaviorType editingBehaviorTypeForPlatform() ; } +#if PLATFORM(COCOA) +static const bool defaultYouTubeFlashPluginReplacementEnabled = true; +#else +static const bool defaultYouTubeFlashPluginReplacementEnabled = false; +#endif + #if PLATFORM(IOS) static const bool defaultFixedPositionCreatesStackingContext = true; -static const bool defaultMediaPlaybackAllowsInline = false; -static const bool defaultMediaPlaybackRequiresUserGesture = true; +static const bool defaultFixedBackgroundsPaintRelativeToDocument = true; +static const bool defaultAcceleratedCompositingForFixedPositionEnabled = true; +static const bool defaultAllowsInlineMediaPlayback = false; +static const bool defaultInlineMediaPlaybackRequiresPlaysInlineAttribute = true; +static const bool defaultVideoPlaybackRequiresUserGesture = true; +static const bool defaultAudioPlaybackRequiresUserGesture = true; +static const bool defaultMediaDataLoadsAutomatically = false; static const bool defaultShouldRespectImageOrientation = true; +static const bool defaultImageSubsamplingEnabled = true; +static const bool defaultScrollingTreeIncludesFrames = true; +static const bool defaultMediaControlsScaleWithPageZoom = true; +static const bool defaultQuickTimePluginReplacementEnabled = true; #else static const bool defaultFixedPositionCreatesStackingContext = false; -static const bool defaultMediaPlaybackAllowsInline = true; -static const bool defaultMediaPlaybackRequiresUserGesture = false; +static const bool defaultFixedBackgroundsPaintRelativeToDocument = false; +static const bool defaultAcceleratedCompositingForFixedPositionEnabled = false; +static const bool defaultAllowsInlineMediaPlayback = true; +static const bool defaultInlineMediaPlaybackRequiresPlaysInlineAttribute = false; +static const bool defaultVideoPlaybackRequiresUserGesture = false; +static const bool defaultAudioPlaybackRequiresUserGesture = false; +static const bool defaultMediaDataLoadsAutomatically = true; static const bool defaultShouldRespectImageOrientation = false; +static const bool defaultImageSubsamplingEnabled = false; +static const bool defaultScrollingTreeIncludesFrames = false; +static const bool defaultMediaControlsScaleWithPageZoom = true; +static const bool defaultQuickTimePluginReplacementEnabled = false; #endif +static const bool defaultRequiresUserGestureToLoadVideo = true; + +static const bool defaultAllowsPictureInPictureMediaPlayback = true; + static const double defaultIncrementalRenderingSuppressionTimeoutInSeconds = 5; #if USE(UNIFIED_TEXT_CHECKING) static const bool defaultUnifiedTextCheckerEnabled = true; @@ -149,68 +187,40 @@ static const bool defaultSelectTrailingWhitespaceEnabled = false; // This amount of time must have elapsed before we will even consider scheduling a layout without a delay. // FIXME: For faster machines this value can really be lowered to 200. 250 is adequate, but a little high // for dual G5s. :) -static const auto layoutScheduleThreshold = std::chrono::milliseconds(250); +static const Seconds layoutScheduleThreshold = 250_ms; Settings::Settings(Page* page) - : m_page(0) + : m_page(nullptr) , m_mediaTypeOverride("screen") , m_fontGenericFamilies(std::make_unique<FontGenericFamilies>()) , m_storageBlockingPolicy(SecurityOrigin::AllowAllStorage) , m_layoutInterval(layoutScheduleThreshold) -#if PLATFORM(IOS) - , m_maxParseDuration(-1) -#endif -#if ENABLE(TEXT_AUTOSIZING) - , m_textAutosizingFontScaleFactor(1) -#if HACK_FORCE_TEXT_AUTOSIZING_ON_DESKTOP - , m_textAutosizingWindowSizeOverride(320, 480) - , m_textAutosizingEnabled(true) -#else - , m_textAutosizingEnabled(false) -#endif -#endif + , m_minimumDOMTimerInterval(DOMTimer::defaultMinimumInterval()) SETTINGS_INITIALIZER_LIST - , m_screenFontSubstitutionEnabled(shouldEnableScreenFontSubstitutionByDefault()) , m_isJavaEnabled(false) , m_isJavaEnabledForLocalFiles(true) , m_loadsImagesAutomatically(false) - , m_privateBrowsingEnabled(false) , m_areImagesEnabled(true) + , m_preferMIMETypeForImages(false) , m_arePluginsEnabled(false) , m_isScriptEnabled(false) , m_needsAdobeFrameReloadingQuirk(false) , m_usesPageCache(false) , m_fontRenderingMode(0) -#if PLATFORM(IOS) - , m_standalone(false) - , m_telephoneNumberParsingEnabled(false) - , m_mediaDataLoadsAutomatically(false) - , m_shouldTransformsAffectOverflow(true) - , m_shouldDispatchJavaScriptWindowOnErrorEvents(false) - , m_alwaysUseBaselineOfPrimaryFont(false) - , m_alwaysUseAcceleratedOverflowScroll(false) -#endif -#if ENABLE(CSS_STICKY_POSITION) - , m_cssStickyPositionEnabled(true) -#endif , m_showTiledScrollingIndicator(false) - , m_tiledBackingStoreEnabled(false) , m_backgroundShouldExtendBeyondPage(false) , m_dnsPrefetchingEnabled(false) #if ENABLE(TOUCH_EVENTS) , m_touchEventEmulationEnabled(false) #endif , m_scrollingPerformanceLoggingEnabled(false) - , m_aggressiveTileRetentionEnabled(false) , m_timeWithoutMouseMovementBeforeHidingControls(3) - , m_setImageLoadingSettingsTimer(this, &Settings::imageLoadingSettingsTimerFired) -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) + , m_setImageLoadingSettingsTimer(*this, &Settings::imageLoadingSettingsTimerFired) , m_hiddenPageDOMTimerThrottlingEnabled(false) -#endif -#if ENABLE(PAGE_VISIBILITY_API) , m_hiddenPageCSSAnimationSuspensionEnabled(false) -#endif , m_fontFallbackPrefersPictographs(false) + , m_webFontsAlwaysFallBack(false) + , m_forcePendingWebGLPolicy(false) { // A Frame may not have been created yet, so we initialize the AtomicString // hash before trying to use it. @@ -223,31 +233,14 @@ Settings::~Settings() { } -PassRefPtr<Settings> Settings::create(Page* page) +Ref<Settings> Settings::create(Page* page) { - return adoptRef(new Settings(page)); + return adoptRef(*new Settings(page)); } SETTINGS_SETTER_BODIES -void Settings::setHiddenPageDOMTimerAlignmentInterval(double hiddenPageDOMTimerAlignmentinterval) -{ - gHiddenPageDOMTimerAlignmentInterval = hiddenPageDOMTimerAlignmentinterval; -} - -double Settings::hiddenPageDOMTimerAlignmentInterval() -{ - return gHiddenPageDOMTimerAlignmentInterval; -} - -#if !PLATFORM(MAC) -bool Settings::shouldEnableScreenFontSubstitutionByDefault() -{ - return true; -} -#endif - -#if !PLATFORM(MAC) +#if !PLATFORM(COCOA) void Settings::initializeDefaultFontFamilies() { // Other platforms can set up fonts from a client, but on Mac, we want it in WebCore to share code between WebKit1 and WebKit2. @@ -338,41 +331,16 @@ void Settings::setPictographFontFamily(const AtomicString& family, UScriptCode s invalidateAfterGenericFamilyChange(m_page); } -#if ENABLE(TEXT_AUTOSIZING) -void Settings::setTextAutosizingEnabled(bool textAutosizingEnabled) -{ - if (m_textAutosizingEnabled == textAutosizingEnabled) - return; - - m_textAutosizingEnabled = textAutosizingEnabled; - if (m_page) - m_page->setNeedsRecalcStyleInAllFrames(); -} - -void Settings::setTextAutosizingWindowSizeOverride(const IntSize& textAutosizingWindowSizeOverride) +float Settings::defaultMinimumZoomFontSize() { - if (m_textAutosizingWindowSizeOverride == textAutosizingWindowSizeOverride) - return; - - m_textAutosizingWindowSizeOverride = textAutosizingWindowSizeOverride; - if (m_page) - m_page->setNeedsRecalcStyleInAllFrames(); + return 15; } -void Settings::setTextAutosizingFontScaleFactor(float fontScaleFactor) +#if !PLATFORM(IOS) +bool Settings::defaultTextAutosizingEnabled() { - m_textAutosizingFontScaleFactor = fontScaleFactor; - - if (!m_page) - return; - - // FIXME: I wonder if this needs to traverse frames like in WebViewImpl::resize, or whether there is only one document per Settings instance? - for (Frame* frame = m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) - frame->document()->textAutosizer()->recalculateMultipliers(); - - m_page->setNeedsRecalcStyleInAllFrames(); + return false; } - #endif void Settings::setMediaTypeOverride(const String& mediaTypeOverride) @@ -406,17 +374,15 @@ void Settings::setLoadsImagesAutomatically(bool loadsImagesAutomatically) m_setImageLoadingSettingsTimer.startOneShot(0); } -void Settings::imageLoadingSettingsTimerFired(Timer<Settings>*) +void Settings::imageLoadingSettingsTimerFired() { setImageLoadingSettings(m_page); } void Settings::setScriptEnabled(bool isScriptEnabled) { -#if PLATFORM(IOS) if (m_isScriptEnabled == isScriptEnabled) return; -#endif m_isScriptEnabled = isScriptEnabled; @@ -426,7 +392,6 @@ void Settings::setScriptEnabled(bool isScriptEnabled) #if PLATFORM(IOS) m_page->setNeedsRecalcStyleInAllFrames(); #endif - InspectorInstrumentation::scriptsEnabled(m_page, m_isScriptEnabled); } void Settings::setJavaEnabled(bool isJavaEnabled) @@ -447,23 +412,23 @@ void Settings::setImagesEnabled(bool areImagesEnabled) m_setImageLoadingSettingsTimer.startOneShot(0); } -void Settings::setPluginsEnabled(bool arePluginsEnabled) +void Settings::setPreferMIMETypeForImages(bool preferMIMETypeForImages) { - if (m_arePluginsEnabled == arePluginsEnabled) - return; + m_preferMIMETypeForImages = preferMIMETypeForImages; +} - m_arePluginsEnabled = arePluginsEnabled; - Page::refreshPlugins(false); +void Settings::setForcePendingWebGLPolicy(bool forced) +{ + m_forcePendingWebGLPolicy = forced; } -void Settings::setPrivateBrowsingEnabled(bool privateBrowsingEnabled) +void Settings::setPluginsEnabled(bool arePluginsEnabled) { - if (m_privateBrowsingEnabled == privateBrowsingEnabled) + if (m_arePluginsEnabled == arePluginsEnabled) return; - m_privateBrowsingEnabled = privateBrowsingEnabled; - if (m_page) - m_page->privateBrowsingStateChanged(); + m_arePluginsEnabled = arePluginsEnabled; + Page::refreshPlugins(false); } void Settings::setUserStyleSheetLocation(const URL& userStyleSheetLocation) @@ -484,53 +449,21 @@ void Settings::setNeedsAdobeFrameReloadingQuirk(bool shouldNotReloadIFramesForUn m_needsAdobeFrameReloadingQuirk = shouldNotReloadIFramesForUnchangedSRC; } -void Settings::setDefaultMinDOMTimerInterval(double interval) -{ - gDefaultMinDOMTimerInterval = interval; -} - -double Settings::defaultMinDOMTimerInterval() +void Settings::setMinimumDOMTimerInterval(std::chrono::milliseconds interval) { - return gDefaultMinDOMTimerInterval; -} + auto oldTimerInterval = m_minimumDOMTimerInterval; + m_minimumDOMTimerInterval = interval; -void Settings::setMinDOMTimerInterval(double interval) -{ - if (m_page) - m_page->setMinimumTimerInterval(interval); -} - -double Settings::minDOMTimerInterval() -{ if (!m_page) - return 0; - return m_page->minimumTimerInterval(); -} - -void Settings::setDefaultDOMTimerAlignmentInterval(double interval) -{ - gDefaultDOMTimerAlignmentInterval = interval; -} - -double Settings::defaultDOMTimerAlignmentInterval() -{ - return gDefaultDOMTimerAlignmentInterval; -} - -void Settings::setDOMTimerAlignmentInterval(double interval) -{ - if (m_page) - m_page->setTimerAlignmentInterval(interval); -} + return; -double Settings::domTimerAlignmentInterval() const -{ - if (!m_page) - return 0; - return m_page->timerAlignmentInterval(); + for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (frame->document()) + frame->document()->adjustMinimumTimerInterval(oldTimerInterval); + } } -void Settings::setLayoutInterval(std::chrono::milliseconds layoutInterval) +void Settings::setLayoutInterval(Seconds layoutInterval) { // FIXME: It seems weird that this function may disregard the specified layout interval. // We should either expose layoutScheduleThreshold or better communicate this invariant. @@ -547,29 +480,15 @@ void Settings::setUsesPageCache(bool usesPageCache) if (!m_page) return; - if (!m_usesPageCache) { - int first = -m_page->backForward().backCount(); - int last = m_page->backForward().forwardCount(); - for (int i = first; i <= last; i++) - pageCache()->remove(m_page->backForward().itemAtIndex(i)); - } -} - -void Settings::setScreenFontSubstitutionEnabled(bool enabled) -{ - if (m_screenFontSubstitutionEnabled == enabled) - return; - m_screenFontSubstitutionEnabled = enabled; - - if (m_page) - m_page->setNeedsRecalcStyleInAllFrames(); + if (!m_usesPageCache) + PageCache::singleton().pruneToSizeNow(0, PruningReason::None); } void Settings::setFontRenderingMode(FontRenderingMode mode) { if (fontRenderingMode() == mode) return; - m_fontRenderingMode = mode; + m_fontRenderingMode = static_cast<int>(mode); if (m_page) m_page->setNeedsRecalcStyleInAllFrames(); } @@ -579,13 +498,6 @@ FontRenderingMode Settings::fontRenderingMode() const return static_cast<FontRenderingMode>(m_fontRenderingMode); } -#if USE(SAFARI_THEME) -void Settings::setShouldPaintNativeControls(bool shouldPaintNativeControls) -{ - gShouldPaintNativeControls = shouldPaintNativeControls; -} -#endif - void Settings::setDNSPrefetchingEnabled(bool dnsPrefetchingEnabled) { if (m_dnsPrefetchingEnabled == dnsPrefetchingEnabled) @@ -604,6 +516,18 @@ void Settings::setShowTiledScrollingIndicator(bool enabled) m_showTiledScrollingIndicator = enabled; } +#if ENABLE(RESOURCE_USAGE) +void Settings::setResourceUsageOverlayVisible(bool visible) +{ + if (m_resourceUsageOverlayVisible == visible) + return; + + m_resourceUsageOverlayVisible = visible; + if (m_page) + m_page->setResourceUsageOverlayVisible(visible); +} +#endif + #if PLATFORM(WIN) void Settings::setShouldUseHighResolutionTimers(bool shouldUseHighResolutionTimers) { @@ -621,15 +545,6 @@ void Settings::setStorageBlockingPolicy(SecurityOrigin::StorageBlockingPolicy en m_page->storageBlockingStateChanged(); } -void Settings::setTiledBackingStoreEnabled(bool enabled) -{ - m_tiledBackingStoreEnabled = enabled; -#if USE(TILED_BACKING_STORE) - if (m_page) - m_page->mainFrame().setTiledBackingStoreEnabled(enabled); -#endif -} - void Settings::setBackgroundShouldExtendBeyondPage(bool shouldExtend) { if (m_backgroundShouldExtendBeyondPage == shouldExtend) @@ -637,10 +552,8 @@ void Settings::setBackgroundShouldExtendBeyondPage(bool shouldExtend) m_backgroundShouldExtendBeyondPage = shouldExtend; -#if USE(ACCELERATED_COMPOSITING) if (m_page) - m_page->mainFrame().view()->setBackgroundExtendsBeyondPage(shouldExtend); -#endif + m_page->mainFrame().view()->updateExtendBackgroundIfNecessary(); } #if USE(AVFOUNDATION) @@ -652,9 +565,17 @@ void Settings::setAVFoundationEnabled(bool enabled) gAVFoundationEnabled = enabled; HTMLMediaElement::resetMediaEngines(); } + +void Settings::setAVFoundationNSURLSessionEnabled(bool enabled) +{ + if (gAVFoundationNSURLSessionEnabled == enabled) + return; + + gAVFoundationNSURLSessionEnabled = enabled; +} #endif -#if PLATFORM(MAC) +#if PLATFORM(COCOA) void Settings::setQTKitEnabled(bool enabled) { if (gQTKitEnabled == enabled) @@ -665,17 +586,40 @@ void Settings::setQTKitEnabled(bool enabled) } #endif -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) -void Settings::setVideoPluginProxyEnabled(bool enabled) +#if USE(GSTREAMER) +void Settings::setGStreamerEnabled(bool enabled) { - if (gVideoPluginProxyEnabled == enabled) + if (gGStreamerEnabled == enabled) return; - gVideoPluginProxyEnabled = enabled; + gGStreamerEnabled = enabled; HTMLMediaElement::resetMediaEngines(); } #endif +#if ENABLE(MEDIA_STREAM) +bool Settings::mockCaptureDevicesEnabled() +{ + return gMockCaptureDevicesEnabled; +} + +void Settings::setMockCaptureDevicesEnabled(bool enabled) +{ + gMockCaptureDevicesEnabled = enabled; + MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(enabled); +} + +bool Settings::mediaCaptureRequiresSecureConnection() const +{ + return gMediaCaptureRequiresSecureConnection; +} + +void Settings::setMediaCaptureRequiresSecureConnection(bool mediaCaptureRequiresSecureConnection) +{ + gMediaCaptureRequiresSecureConnection = mediaCaptureRequiresSecureConnection; +} +#endif + void Settings::setScrollingPerformanceLoggingEnabled(bool enabled) { m_scrollingPerformanceLoggingEnabled = enabled; @@ -683,12 +627,11 @@ void Settings::setScrollingPerformanceLoggingEnabled(bool enabled) if (m_page && m_page->mainFrame().view()) m_page->mainFrame().view()->setScrollingPerformanceLoggingEnabled(enabled); } - -void Settings::setAggressiveTileRetentionEnabled(bool enabled) -{ - m_aggressiveTileRetentionEnabled = enabled; -} +// It's very important that this setting doesn't change in the middle of a document's lifetime. +// The Mac port uses this flag when registering and deregistering platform-dependent scrollbar +// objects. Therefore, if this changes at an unexpected time, deregistration may not happen +// correctly, which may cause the platform to follow dangling pointers. void Settings::setMockScrollbarsEnabled(bool flag) { gMockScrollbarsEnabled = flag; @@ -711,6 +654,16 @@ bool Settings::usesOverlayScrollbars() return gUsesOverlayScrollbars; } +void Settings::setUsesMockScrollAnimator(bool flag) +{ + gMockScrollAnimatorEnabled = flag; +} + +bool Settings::usesMockScrollAnimator() +{ + return gMockScrollAnimatorEnabled; +} + void Settings::setShouldRespectPriorityInCSSAttributeSetters(bool flag) { gShouldRespectPriorityInCSSAttributeSetters = flag; @@ -721,7 +674,6 @@ bool Settings::shouldRespectPriorityInCSSAttributeSetters() return gShouldRespectPriorityInCSSAttributeSetters; } -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) void Settings::setHiddenPageDOMTimerThrottlingEnabled(bool flag) { if (m_hiddenPageDOMTimerThrottlingEnabled == flag) @@ -730,9 +682,16 @@ void Settings::setHiddenPageDOMTimerThrottlingEnabled(bool flag) if (m_page) m_page->hiddenPageDOMTimerThrottlingStateChanged(); } -#endif -#if ENABLE(PAGE_VISIBILITY_API) +void Settings::setHiddenPageDOMTimerThrottlingAutoIncreases(bool flag) +{ + if (m_hiddenPageDOMTimerThrottlingAutoIncreases == flag) + return; + m_hiddenPageDOMTimerThrottlingAutoIncreases = flag; + if (m_page) + m_page->hiddenPageDOMTimerThrottlingStateChanged(); +} + void Settings::setHiddenPageCSSAnimationSuspensionEnabled(bool flag) { if (m_hiddenPageCSSAnimationSuspensionEnabled == flag) @@ -741,7 +700,6 @@ void Settings::setHiddenPageCSSAnimationSuspensionEnabled(bool flag) if (m_page) m_page->hiddenPageCSSAnimationSuspensionStateChanged(); } -#endif void Settings::setFontFallbackPrefersPictographs(bool preferPictographs) { @@ -753,17 +711,25 @@ void Settings::setFontFallbackPrefersPictographs(bool preferPictographs) m_page->setNeedsRecalcStyleInAllFrames(); } +void Settings::setWebFontsAlwaysFallBack(bool enable) +{ + if (m_webFontsAlwaysFallBack == enable) + return; + + m_webFontsAlwaysFallBack = enable; +} + void Settings::setLowPowerVideoAudioBufferSizeEnabled(bool flag) { gLowPowerVideoAudioBufferSizeEnabled = flag; } -#if PLATFORM(IOS) -void Settings::setStandalone(bool standalone) +void Settings::setResourceLoadStatisticsEnabled(bool flag) { - m_standalone = standalone; + gResourceLoadStatisticsEnabledEnabled = flag; } +#if PLATFORM(IOS) void Settings::setAudioSessionCategoryOverride(unsigned sessionCategory) { AudioSession::sharedSession().setCategoryOverride(static_cast<AudioSession::CategoryType>(sessionCategory)); @@ -801,4 +767,25 @@ const String& Settings::networkInterfaceName() } #endif +bool Settings::globalConstRedeclarationShouldThrow() +{ +#if PLATFORM(MAC) + return !MacApplication::isIBooks(); +#elif PLATFORM(IOS) + return !IOSApplication::isIBooks(); +#else + return true; +#endif +} + +void Settings::setAllowsAnySSLCertificate(bool allowAnySSLCertificate) +{ + gAllowsAnySSLCertificate = allowAnySSLCertificate; +} + +bool Settings::allowsAnySSLCertificate() +{ + return gAllowsAnySSLCertificate; +} + } // namespace WebCore diff --git a/Source/WebCore/page/Settings.h b/Source/WebCore/page/Settings.h index a3e527d52..7fc5708ec 100644 --- a/Source/WebCore/page/Settings.h +++ b/Source/WebCore/page/Settings.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2016 Apple Inc. All rights reserved. * (C) 2006 Graham Dennis (graham.dennis@gmail.com) * * 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,22 +24,28 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef Settings_h -#define Settings_h +#pragma once +#include "ClipboardAccessPolicy.h" #include "EditingBehaviorTypes.h" -#include "FontRenderingMode.h" #include "IntSize.h" -#include "URL.h" #include "SecurityOrigin.h" #include "SettingsMacros.h" +#include "TextFlags.h" #include "Timer.h" +#include "URL.h" +#include "WritingMode.h" #include <chrono> +#include <runtime/RuntimeFlags.h> +#include <unicode/uscript.h> #include <wtf/HashMap.h> #include <wtf/RefCounted.h> #include <wtf/text/AtomicString.h> #include <wtf/text/AtomicStringHash.h> -#include <wtf/unicode/Unicode.h> + +#if ENABLE(DATA_DETECTION) +#include "DataDetection.h" +#endif namespace WebCore { @@ -60,262 +66,270 @@ enum TextDirectionSubmenuInclusionBehavior { TextDirectionSubmenuAlwaysIncluded }; +enum DebugOverlayRegionFlags { + NonFastScrollableRegion = 1 << 0, + WheelEventHandlerRegion = 1 << 1, +}; + +enum class UserInterfaceDirectionPolicy { + Content, + System +}; + +enum PDFImageCachingPolicy { + PDFImageCachingEnabled, + PDFImageCachingBelowMemoryLimit, + PDFImageCachingDisabled, + PDFImageCachingClipBoundsOnly, +#if PLATFORM(IOS) + PDFImageCachingDefault = PDFImageCachingBelowMemoryLimit +#else + PDFImageCachingDefault = PDFImageCachingEnabled +#endif +}; + +typedef unsigned DebugOverlayRegions; + class Settings : public RefCounted<Settings> { WTF_MAKE_NONCOPYABLE(Settings); WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<Settings> create(Page*); + static Ref<Settings> create(Page*); ~Settings(); void pageDestroyed() { m_page = nullptr; } - void setStandardFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& standardFontFamily(UScriptCode = USCRIPT_COMMON) const; - - void setFixedFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& fixedFontFamily(UScriptCode = USCRIPT_COMMON) const; + enum class ForcedAccessibilityValue { System, On, Off }; + static const Settings::ForcedAccessibilityValue defaultForcedColorsAreInvertedAccessibilityValue = ForcedAccessibilityValue::System; + static const Settings::ForcedAccessibilityValue defaultForcedDisplayIsMonochromeAccessibilityValue = ForcedAccessibilityValue::System; + static const Settings::ForcedAccessibilityValue defaultForcedPrefersReducedMotionAccessibilityValue = ForcedAccessibilityValue::System; - void setSerifFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& serifFontFamily(UScriptCode = USCRIPT_COMMON) const; + WEBCORE_EXPORT void setStandardFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& standardFontFamily(UScriptCode = USCRIPT_COMMON) const; - void setSansSerifFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& sansSerifFontFamily(UScriptCode = USCRIPT_COMMON) const; + WEBCORE_EXPORT void setFixedFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& fixedFontFamily(UScriptCode = USCRIPT_COMMON) const; - void setCursiveFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& cursiveFontFamily(UScriptCode = USCRIPT_COMMON) const; + WEBCORE_EXPORT void setSerifFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& serifFontFamily(UScriptCode = USCRIPT_COMMON) const; - void setFantasyFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& fantasyFontFamily(UScriptCode = USCRIPT_COMMON) const; + WEBCORE_EXPORT void setSansSerifFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& sansSerifFontFamily(UScriptCode = USCRIPT_COMMON) const; - void setPictographFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); - const AtomicString& pictographFontFamily(UScriptCode = USCRIPT_COMMON) const; + WEBCORE_EXPORT void setCursiveFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& cursiveFontFamily(UScriptCode = USCRIPT_COMMON) const; -#if ENABLE(TEXT_AUTOSIZING) - void setTextAutosizingEnabled(bool); - bool textAutosizingEnabled() const { return m_textAutosizingEnabled; } + WEBCORE_EXPORT void setFantasyFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& fantasyFontFamily(UScriptCode = USCRIPT_COMMON) const; - void setTextAutosizingFontScaleFactor(float); - float textAutosizingFontScaleFactor() const { return m_textAutosizingFontScaleFactor; } + WEBCORE_EXPORT void setPictographFontFamily(const AtomicString&, UScriptCode = USCRIPT_COMMON); + WEBCORE_EXPORT const AtomicString& pictographFontFamily(UScriptCode = USCRIPT_COMMON) const; - // Only set by Layout Tests, and only used if textAutosizingEnabled() returns true. - void setTextAutosizingWindowSizeOverride(const IntSize&); - const IntSize& textAutosizingWindowSizeOverride() const { return m_textAutosizingWindowSizeOverride; } -#endif + WEBCORE_EXPORT static bool defaultTextAutosizingEnabled(); + WEBCORE_EXPORT static float defaultMinimumZoomFontSize(); // Only set by Layout Tests. - void setMediaTypeOverride(const String&); + WEBCORE_EXPORT void setMediaTypeOverride(const String&); const String& mediaTypeOverride() const { return m_mediaTypeOverride; } // Unlike areImagesEnabled, this only suppresses the network load of // the image URL. A cached image will still be rendered if requested. - void setLoadsImagesAutomatically(bool); + WEBCORE_EXPORT void setLoadsImagesAutomatically(bool); bool loadsImagesAutomatically() const { return m_loadsImagesAutomatically; } // Clients that execute script should call ScriptController::canExecuteScripts() // instead of this function. ScriptController::canExecuteScripts() checks the // HTML sandbox, plug-in sandboxing, and other important details. bool isScriptEnabled() const { return m_isScriptEnabled; } - void setScriptEnabled(bool); + WEBCORE_EXPORT void setScriptEnabled(bool); SETTINGS_GETTERS_AND_SETTERS - void setScreenFontSubstitutionEnabled(bool); - bool screenFontSubstitutionEnabled() const { return m_screenFontSubstitutionEnabled; } - - void setJavaEnabled(bool); + WEBCORE_EXPORT void setJavaEnabled(bool); bool isJavaEnabled() const { return m_isJavaEnabled; } // This settings is only consulted if isJavaEnabled() returns true; - void setJavaEnabledForLocalFiles(bool); + WEBCORE_EXPORT void setJavaEnabledForLocalFiles(bool); bool isJavaEnabledForLocalFiles() const { return m_isJavaEnabledForLocalFiles; } - void setImagesEnabled(bool); + WEBCORE_EXPORT void setImagesEnabled(bool); bool areImagesEnabled() const { return m_areImagesEnabled; } - void setPluginsEnabled(bool); + WEBCORE_EXPORT void setPreferMIMETypeForImages(bool); + bool preferMIMETypeForImages() const { return m_preferMIMETypeForImages; } + + WEBCORE_EXPORT void setPluginsEnabled(bool); bool arePluginsEnabled() const { return m_arePluginsEnabled; } - // When this option is set, WebCore will avoid storing any record of browsing activity - // that may persist on disk or remain displayed when the option is reset. - // This option does not affect the storage of such information in RAM. - // The following functions respect this setting: - // - HTML5/DOM Storage - // - Icon Database - // - Console Messages - // - MemoryCache - // - Application Cache - // - Back/Forward Page History - // - Page Search Results - // - HTTP Cookies - // - Plug-ins (that support NPNVprivateModeBool) - void setPrivateBrowsingEnabled(bool); - bool privateBrowsingEnabled() const { return m_privateBrowsingEnabled; } - - void setDNSPrefetchingEnabled(bool); + WEBCORE_EXPORT void setDNSPrefetchingEnabled(bool); bool dnsPrefetchingEnabled() const { return m_dnsPrefetchingEnabled; } - void setUserStyleSheetLocation(const URL&); + WEBCORE_EXPORT void setUserStyleSheetLocation(const URL&); const URL& userStyleSheetLocation() const { return m_userStyleSheetLocation; } - void setNeedsAdobeFrameReloadingQuirk(bool); + WEBCORE_EXPORT void setNeedsAdobeFrameReloadingQuirk(bool); bool needsAcrobatFrameReloadingQuirk() const { return m_needsAdobeFrameReloadingQuirk; } - static void setDefaultMinDOMTimerInterval(double); // Interval specified in seconds. - static double defaultMinDOMTimerInterval(); - - static void setHiddenPageDOMTimerAlignmentInterval(double); // Interval specified in seconds. - static double hiddenPageDOMTimerAlignmentInterval(); - - void setMinDOMTimerInterval(double); // Per-page; initialized to default value. - double minDOMTimerInterval(); + WEBCORE_EXPORT void setMinimumDOMTimerInterval(std::chrono::milliseconds); // Initialized to DOMTimer::defaultMinimumInterval(). + std::chrono::milliseconds minimumDOMTimerInterval() const { return m_minimumDOMTimerInterval; } - static void setDefaultDOMTimerAlignmentInterval(double); - static double defaultDOMTimerAlignmentInterval(); + WEBCORE_EXPORT void setLayoutInterval(Seconds); + Seconds layoutInterval() const { return m_layoutInterval; } - void setDOMTimerAlignmentInterval(double); - double domTimerAlignmentInterval() const; - - void setLayoutInterval(std::chrono::milliseconds); - std::chrono::milliseconds layoutInterval() const { return m_layoutInterval; } - -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) bool hiddenPageDOMTimerThrottlingEnabled() const { return m_hiddenPageDOMTimerThrottlingEnabled; } - void setHiddenPageDOMTimerThrottlingEnabled(bool); -#endif - -#if PLATFORM(IOS) - // FIXME: This setting isn't specific to iOS. - void setMaxParseDuration(double maxParseDuration) { m_maxParseDuration = maxParseDuration; } - double maxParseDuration() const { return m_maxParseDuration; } - - void setStandalone(bool); - bool standalone() const { return m_standalone; } + WEBCORE_EXPORT void setHiddenPageDOMTimerThrottlingEnabled(bool); + bool hiddenPageDOMTimerThrottlingAutoIncreases() const { return m_hiddenPageDOMTimerThrottlingAutoIncreases; } + WEBCORE_EXPORT void setHiddenPageDOMTimerThrottlingAutoIncreases(bool); - void setTelephoneNumberParsingEnabled(bool flag) { m_telephoneNumberParsingEnabled = flag; } - bool telephoneNumberParsingEnabled() const { return m_telephoneNumberParsingEnabled; } - - void setMediaDataLoadsAutomatically(bool flag) { m_mediaDataLoadsAutomatically = flag; } - bool mediaDataLoadsAutomatically() const { return m_mediaDataLoadsAutomatically; } - - void setShouldTransformsAffectOverflow(bool flag) { m_shouldTransformsAffectOverflow = flag; } - bool shouldTransformsAffectOverflow() const { return m_shouldTransformsAffectOverflow; } - - void setShouldDispatchJavaScriptWindowOnErrorEvents(bool flag) { m_shouldDispatchJavaScriptWindowOnErrorEvents = flag; } - bool shouldDispatchJavaScriptWindowOnErrorEvents() const { return m_shouldDispatchJavaScriptWindowOnErrorEvents; } - - void setAlwaysUseBaselineOfPrimaryFont(bool flag) { m_alwaysUseBaselineOfPrimaryFont = flag; } - bool alwaysUseBaselineOfPrimaryFont() const { return m_alwaysUseBaselineOfPrimaryFont; } - - void setAlwaysUseAcceleratedOverflowScroll(bool flag) { m_alwaysUseAcceleratedOverflowScroll = flag; } - bool alwaysUseAcceleratedOverflowScroll() const { return m_alwaysUseAcceleratedOverflowScroll; } -#endif - - void setUsesPageCache(bool); + WEBCORE_EXPORT void setUsesPageCache(bool); bool usesPageCache() const { return m_usesPageCache; } void setFontRenderingMode(FontRenderingMode mode); FontRenderingMode fontRenderingMode() const; -#if ENABLE(CSS_STICKY_POSITION) - void setCSSStickyPositionEnabled(bool enabled) { m_cssStickyPositionEnabled = enabled; } - bool cssStickyPositionEnabled() const { return m_cssStickyPositionEnabled; } -#else - void setCSSStickyPositionEnabled(bool) { } - bool cssStickyPositionEnabled() const { return false; } -#endif - - void setShowTiledScrollingIndicator(bool); + WEBCORE_EXPORT void setShowTiledScrollingIndicator(bool); bool showTiledScrollingIndicator() const { return m_showTiledScrollingIndicator; } +#if ENABLE(RESOURCE_USAGE) + bool resourceUsageOverlayVisible() const { return m_resourceUsageOverlayVisible; } + WEBCORE_EXPORT void setResourceUsageOverlayVisible(bool); +#endif + #if PLATFORM(WIN) static void setShouldUseHighResolutionTimers(bool); static bool shouldUseHighResolutionTimers() { return gShouldUseHighResolutionTimers; } #endif - void setTiledBackingStoreEnabled(bool); - bool tiledBackingStoreEnabled() const { return m_tiledBackingStoreEnabled; } + static bool isPostLoadCPUUsageMeasurementEnabled(); + static bool isPostBackgroundingCPUUsageMeasurementEnabled(); + static bool isPerActivityStateCPUUsageMeasurementEnabled(); + + static bool isPostLoadMemoryUsageMeasurementEnabled(); + static bool isPostBackgroundingMemoryUsageMeasurementEnabled(); - void setBackgroundShouldExtendBeyondPage(bool); + static bool globalConstRedeclarationShouldThrow(); + + WEBCORE_EXPORT void setBackgroundShouldExtendBeyondPage(bool); bool backgroundShouldExtendBeyondPage() const { return m_backgroundShouldExtendBeyondPage; } #if USE(AVFOUNDATION) - static void setAVFoundationEnabled(bool flag); + WEBCORE_EXPORT static void setAVFoundationEnabled(bool flag); static bool isAVFoundationEnabled() { return gAVFoundationEnabled; } + WEBCORE_EXPORT static void setAVFoundationNSURLSessionEnabled(bool flag); + static bool isAVFoundationNSURLSessionEnabled() { return gAVFoundationNSURLSessionEnabled; } #endif -#if PLATFORM(MAC) - static void setQTKitEnabled(bool flag); +#if PLATFORM(COCOA) + WEBCORE_EXPORT static void setQTKitEnabled(bool flag); static bool isQTKitEnabled() { return gQTKitEnabled; } +#else + static bool isQTKitEnabled() { return false; } #endif - static const unsigned defaultMaximumHTMLParserDOMTreeDepth = 512; - -#if USE(SAFARI_THEME) - // Windows debugging pref (global) for switching between the Aqua look and a native windows look. - static void setShouldPaintNativeControls(bool); - static bool shouldPaintNativeControls() { return gShouldPaintNativeControls; } +#if USE(GSTREAMER) + WEBCORE_EXPORT static void setGStreamerEnabled(bool flag); + static bool isGStreamerEnabled() { return gGStreamerEnabled; } #endif - static void setMockScrollbarsEnabled(bool flag); - static bool mockScrollbarsEnabled(); + static const unsigned defaultMaximumHTMLParserDOMTreeDepth = 512; + static const unsigned defaultMaximumRenderTreeDepth = 512; + + WEBCORE_EXPORT static void setMockScrollbarsEnabled(bool flag); + WEBCORE_EXPORT static bool mockScrollbarsEnabled(); - static void setUsesOverlayScrollbars(bool flag); + WEBCORE_EXPORT static void setUsesOverlayScrollbars(bool flag); static bool usesOverlayScrollbars(); + WEBCORE_EXPORT static void setUsesMockScrollAnimator(bool); + static bool usesMockScrollAnimator(); + #if ENABLE(TOUCH_EVENTS) void setTouchEventEmulationEnabled(bool enabled) { m_touchEventEmulationEnabled = enabled; } bool isTouchEventEmulationEnabled() const { return m_touchEventEmulationEnabled; } #endif - void setStorageBlockingPolicy(SecurityOrigin::StorageBlockingPolicy); + WEBCORE_EXPORT void setStorageBlockingPolicy(SecurityOrigin::StorageBlockingPolicy); SecurityOrigin::StorageBlockingPolicy storageBlockingPolicy() const { return m_storageBlockingPolicy; } - void setScrollingPerformanceLoggingEnabled(bool); + WEBCORE_EXPORT void setScrollingPerformanceLoggingEnabled(bool); bool scrollingPerformanceLoggingEnabled() { return m_scrollingPerformanceLoggingEnabled; } - - void setAggressiveTileRetentionEnabled(bool); - bool aggressiveTileRetentionEnabled() { return m_aggressiveTileRetentionEnabled; } - static void setShouldRespectPriorityInCSSAttributeSetters(bool); + WEBCORE_EXPORT static void setShouldRespectPriorityInCSSAttributeSetters(bool); static bool shouldRespectPriorityInCSSAttributeSetters(); void setTimeWithoutMouseMovementBeforeHidingControls(double time) { m_timeWithoutMouseMovementBeforeHidingControls = time; } double timeWithoutMouseMovementBeforeHidingControls() const { return m_timeWithoutMouseMovementBeforeHidingControls; } -#if ENABLE(PAGE_VISIBILITY_API) bool hiddenPageCSSAnimationSuspensionEnabled() const { return m_hiddenPageCSSAnimationSuspensionEnabled; } - void setHiddenPageCSSAnimationSuspensionEnabled(bool); -#endif + WEBCORE_EXPORT void setHiddenPageCSSAnimationSuspensionEnabled(bool); - void setFontFallbackPrefersPictographs(bool); + WEBCORE_EXPORT void setFontFallbackPrefersPictographs(bool); bool fontFallbackPrefersPictographs() const { return m_fontFallbackPrefersPictographs; } + WEBCORE_EXPORT void setWebFontsAlwaysFallBack(bool); + bool webFontsAlwaysFallBack() const { return m_webFontsAlwaysFallBack; } + static bool lowPowerVideoAudioBufferSizeEnabled() { return gLowPowerVideoAudioBufferSizeEnabled; } - static void setLowPowerVideoAudioBufferSizeEnabled(bool); + WEBCORE_EXPORT static void setLowPowerVideoAudioBufferSizeEnabled(bool); -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - static void setVideoPluginProxyEnabled(bool flag); - static bool isVideoPluginProxyEnabled() { return gVideoPluginProxyEnabled; } -#endif + static bool resourceLoadStatisticsEnabled() { return gResourceLoadStatisticsEnabledEnabled; } + WEBCORE_EXPORT static void setResourceLoadStatisticsEnabled(bool); #if PLATFORM(IOS) - static void setAudioSessionCategoryOverride(unsigned); + WEBCORE_EXPORT static void setAudioSessionCategoryOverride(unsigned); static unsigned audioSessionCategoryOverride(); - static void setNetworkDataUsageTrackingEnabled(bool); + WEBCORE_EXPORT static void setNetworkDataUsageTrackingEnabled(bool); static bool networkDataUsageTrackingEnabled(); - static void setNetworkInterfaceName(const String&); + WEBCORE_EXPORT static void setNetworkInterfaceName(const String&); static const String& networkInterfaceName(); +#if HAVE(AVKIT) static void setAVKitEnabled(bool flag) { gAVKitEnabled = flag; } +#endif static bool avKitEnabled() { return gAVKitEnabled; } + + static void setShouldOptOutOfNetworkStateObservation(bool flag) { gShouldOptOutOfNetworkStateObservation = flag; } + static bool shouldOptOutOfNetworkStateObservation() { return gShouldOptOutOfNetworkStateObservation; } + + static void setShouldManageAudioSessionCategory(bool flag) { gManageAudioSession = flag; } + static bool shouldManageAudioSessionCategory() { return gManageAudioSession; } +#endif + +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + void setMediaKeysStorageDirectory(const String& directory) { m_mediaKeysStorageDirectory = directory; } + const String& mediaKeysStorageDirectory() const { return m_mediaKeysStorageDirectory; } +#endif + +#if ENABLE(MEDIA_STREAM) + void setMediaDeviceIdentifierStorageDirectory(const String& directory) { m_mediaDeviceIdentifierStorageDirectory = directory; } + const String& mediaDeviceIdentifierStorageDirectory() const { return m_mediaDeviceIdentifierStorageDirectory; } + + static bool mockCaptureDevicesEnabled(); + WEBCORE_EXPORT static void setMockCaptureDevicesEnabled(bool); + + bool mediaCaptureRequiresSecureConnection() const; + WEBCORE_EXPORT static void setMediaCaptureRequiresSecureConnection(bool); #endif +#if ENABLE(APPLE_PAY) + bool applePayEnabled() const { return m_applePayEnabled; } + void setApplePayEnabled(bool applePayEnabled) { m_applePayEnabled = applePayEnabled; } + + bool applePayCapabilityDisclosureAllowed() const { return m_applePayCapabilityDisclosureAllowed; } + void setApplePayCapabilityDisclosureAllowed(bool applePayCapabilityDisclosureAllowed) { m_applePayCapabilityDisclosureAllowed = applePayCapabilityDisclosureAllowed; } +#endif + + WEBCORE_EXPORT void setForcePendingWebGLPolicy(bool); + bool isForcePendingWebGLPolicy() const { return m_forcePendingWebGLPolicy; } + + WEBCORE_EXPORT static void setAllowsAnySSLCertificate(bool); + static bool allowsAnySSLCertificate(); + private: explicit Settings(Page*); void initializeDefaultFontFamilies(); - static bool shouldEnableScreenFontSubstitutionByDefault(); Page* m_page; @@ -323,44 +337,22 @@ private: URL m_userStyleSheetLocation; const std::unique_ptr<FontGenericFamilies> m_fontGenericFamilies; SecurityOrigin::StorageBlockingPolicy m_storageBlockingPolicy; - std::chrono::milliseconds m_layoutInterval; -#if PLATFORM(IOS) - double m_maxParseDuration; -#endif -#if ENABLE(TEXT_AUTOSIZING) - float m_textAutosizingFontScaleFactor; - IntSize m_textAutosizingWindowSizeOverride; - bool m_textAutosizingEnabled : 1; -#endif + Seconds m_layoutInterval; + std::chrono::milliseconds m_minimumDOMTimerInterval; SETTINGS_MEMBER_VARIABLES - bool m_screenFontSubstitutionEnabled : 1; bool m_isJavaEnabled : 1; bool m_isJavaEnabledForLocalFiles : 1; bool m_loadsImagesAutomatically : 1; - bool m_privateBrowsingEnabled : 1; bool m_areImagesEnabled : 1; + bool m_preferMIMETypeForImages : 1; bool m_arePluginsEnabled : 1; bool m_isScriptEnabled : 1; bool m_needsAdobeFrameReloadingQuirk : 1; bool m_usesPageCache : 1; unsigned m_fontRenderingMode : 1; -#if PLATFORM(IOS) - bool m_standalone : 1; - bool m_telephoneNumberParsingEnabled : 1; - bool m_mediaDataLoadsAutomatically : 1; - bool m_shouldTransformsAffectOverflow : 1; - bool m_shouldDispatchJavaScriptWindowOnErrorEvents : 1; - bool m_alwaysUseBaselineOfPrimaryFont : 1; - bool m_allowMultiElementImplicitFormSubmission : 1; - bool m_alwaysUseAcceleratedOverflowScroll : 1; -#endif -#if ENABLE(CSS_STICKY_POSITION) - bool m_cssStickyPositionEnabled : 1; -#endif bool m_showTiledScrollingIndicator : 1; - bool m_tiledBackingStoreEnabled : 1; bool m_backgroundShouldExtendBeyondPage : 1; bool m_dnsPrefetchingEnabled : 1; @@ -368,56 +360,116 @@ private: bool m_touchEventEmulationEnabled : 1; #endif bool m_scrollingPerformanceLoggingEnabled : 1; - bool m_aggressiveTileRetentionEnabled : 1; double m_timeWithoutMouseMovementBeforeHidingControls; - Timer<Settings> m_setImageLoadingSettingsTimer; - void imageLoadingSettingsTimerFired(Timer<Settings>*); + Timer m_setImageLoadingSettingsTimer; + void imageLoadingSettingsTimerFired(); -#if ENABLE(HIDDEN_PAGE_DOM_TIMER_THROTTLING) bool m_hiddenPageDOMTimerThrottlingEnabled : 1; -#endif -#if ENABLE(PAGE_VISIBILITY_API) bool m_hiddenPageCSSAnimationSuspensionEnabled : 1; -#endif bool m_fontFallbackPrefersPictographs : 1; + bool m_webFontsAlwaysFallBack : 1; - static double gDefaultMinDOMTimerInterval; - static double gDefaultDOMTimerAlignmentInterval; + bool m_forcePendingWebGLPolicy : 1; + +#if ENABLE(RESOURCE_USAGE) + bool m_resourceUsageOverlayVisible { false }; +#endif + + bool m_hiddenPageDOMTimerThrottlingAutoIncreases { false }; #if USE(AVFOUNDATION) - static bool gAVFoundationEnabled; + WEBCORE_EXPORT static bool gAVFoundationEnabled; + WEBCORE_EXPORT static bool gAVFoundationNSURLSessionEnabled; #endif -#if PLATFORM(MAC) - static bool gQTKitEnabled; +#if PLATFORM(COCOA) + WEBCORE_EXPORT static bool gQTKitEnabled; #endif - + +#if USE(GSTREAMER) + WEBCORE_EXPORT static bool gGStreamerEnabled; +#endif + static bool gMockScrollbarsEnabled; static bool gUsesOverlayScrollbars; + static bool gMockScrollAnimatorEnabled; -#if USE(SAFARI_THEME) - static bool gShouldPaintNativeControls; -#endif #if PLATFORM(WIN) static bool gShouldUseHighResolutionTimers; #endif static bool gShouldRespectPriorityInCSSAttributeSetters; #if PLATFORM(IOS) static bool gNetworkDataUsageTrackingEnabled; - static bool gAVKitEnabled; + WEBCORE_EXPORT static bool gAVKitEnabled; + WEBCORE_EXPORT static bool gShouldOptOutOfNetworkStateObservation; + WEBCORE_EXPORT static bool gManageAudioSession; #endif - static double gHiddenPageDOMTimerAlignmentInterval; +#if ENABLE(LEGACY_ENCRYPTED_MEDIA) + String m_mediaKeysStorageDirectory; +#endif + +#if ENABLE(MEDIA_STREAM) + String m_mediaDeviceIdentifierStorageDirectory; + static bool gMockCaptureDevicesEnabled; + static bool gMediaCaptureRequiresSecureConnection; +#endif + +#if ENABLE(APPLE_PAY) + bool m_applePayEnabled { false }; + bool m_applePayCapabilityDisclosureAllowed { true }; +#endif static bool gLowPowerVideoAudioBufferSizeEnabled; + static bool gResourceLoadStatisticsEnabledEnabled; + static bool gAllowsAnySSLCertificate; +}; -#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) - static bool gVideoPluginProxyEnabled; +inline bool Settings::isPostLoadCPUUsageMeasurementEnabled() +{ +#if PLATFORM(COCOA) + return true; +#else + return false; #endif -}; +} -} // namespace WebCore +inline bool Settings::isPostBackgroundingCPUUsageMeasurementEnabled() +{ +#if PLATFORM(MAC) + return true; +#else + return false; +#endif +} + +inline bool Settings::isPerActivityStateCPUUsageMeasurementEnabled() +{ +#if PLATFORM(MAC) + return true; +#else + return false; +#endif +} + +inline bool Settings::isPostLoadMemoryUsageMeasurementEnabled() +{ +#if PLATFORM(COCOA) + return true; +#else + return false; +#endif +} +inline bool Settings::isPostBackgroundingMemoryUsageMeasurementEnabled() +{ +#if PLATFORM(MAC) + return true; +#else + return false; #endif +} + +} // namespace WebCore diff --git a/Source/WebCore/page/Settings.in b/Source/WebCore/page/Settings.in index ed48be472..8c33a5036 100644 --- a/Source/WebCore/page/Settings.in +++ b/Source/WebCore/page/Settings.in @@ -6,6 +6,7 @@ ftpDirectoryTemplatePath type=String localStorageDatabasePath type=String editableLinkBehavior type=EditableLinkBehavior, initial=EditableLinkDefaultBehavior textDirectionSubmenuInclusionBehavior type=TextDirectionSubmenuInclusionBehavior, initial=TextDirectionSubmenuAutomaticallyIncluded +pdfImageCachingPolicy type=PDFImageCachingPolicy, initial = PDFImageCachingDefault passwordEchoDurationInSeconds type=double, initial=1 # Sets the magnification value for validation message timer. If the @@ -24,8 +25,9 @@ validationMessageTimerMagnification type=int, initial=50 # draw canvas in software. minimumAccelerated2dCanvasSize type=int, initial=257*256 +maximumAccelerated2dCanvasSize type=unsigned, initial=5120*2880 + layoutFallbackWidth type=int, initial=980 -maximumDecodedImageSize type=size_t, initial=std::numeric_limits<size_t>::max() deviceWidth type=int, initial=0 deviceHeight type=int, initial=0 @@ -35,7 +37,7 @@ sessionStorageQuota type=unsigned, initial=StorageMap::noQuota minimumFontSize type=int, initial=0, setNeedsStyleRecalcInAllFrames=1 minimumLogicalFontSize type=int, initial=0, setNeedsStyleRecalcInAllFrames=1 -defaultFontSize type=int, initial=0, setNeedsStyleRecalcInAllFrames=1 +defaultFontSize type=int, initial=16, setNeedsStyleRecalcInAllFrames=1 defaultFixedFontSize type=int, initial=0, setNeedsStyleRecalcInAllFrames=1 editingBehaviorType type=EditingBehaviorType, initial=editingBehaviorTypeForPlatform() @@ -46,13 +48,16 @@ maximumHTMLParserDOMTreeDepth type=unsigned, initial=defaultMaximumHTMLParserDOM loadsSiteIconsIgnoringImageLoadingSetting initial=false caretBrowsingEnabled initial=false +preventKeyboardDOMEventDispatch initial=false localStorageEnabled initial=false allowUniversalAccessFromFileURLs initial=true allowFileAccessFromFileURLs initial=true +needsStorageAccessFromFileURLsQuirk initial=true javaScriptCanOpenWindowsAutomatically initial=false javaScriptCanAccessClipboard initial=false shouldPrintBackgrounds initial=false usesDashboardBackwardCompatibilityMode initial=false, conditional=DASHBOARD_SUPPORT +clipboardAccessPolicy type=ClipboardAccessPolicy, initial=ClipboardAccessPolicy::RequiresUserGesture textAreasAreResizable initial=false, setNeedsStyleRecalcInAllFrames=1 authorAndUserStylesEnabled initial=true, setNeedsStyleRecalcInAllFrames=1 @@ -60,6 +65,7 @@ acceleratedCompositingEnabled initial=true, setNeedsStyleRecalcInAllFrames=1 acceleratedCompositedAnimationsEnabled initial=true, setNeedsStyleRecalcInAllFrames=1 showDebugBorders initial=false, setNeedsStyleRecalcInAllFrames=1 showRepaintCounter initial=false, setNeedsStyleRecalcInAllFrames=1 +visibleDebugOverlayRegions type=DebugOverlayRegions, initial=0 # This is a quirk we are pro-actively applying to old applications. It changes keyboard event dispatching, # making keyIdentifier available on keypress events, making charCode available on keydown/keyup events, @@ -73,9 +79,9 @@ showsURLsInToolTips initial=false showsToolTipOverTruncatedText initial=false forceFTPDirectoryListings initial=false developerExtrasEnabled initial=false -javaScriptExperimentsEnabled initial=false scriptMarkupEnabled initial=true needsSiteSpecificQuirks initial=false +domTimersThrottlingEnabled initial=true webArchiveDebugModeEnabled initial=false, conditional=WEB_ARCHIVE localFileContentSniffingEnabled initial=false offlineWebApplicationCacheEnabled initial=false @@ -84,38 +90,31 @@ usesEncodingDetector initial=false allowScriptsToCloseWindows initial=false canvasUsesAcceleratedDrawing initial=false acceleratedDrawingEnabled initial=false +displayListDrawingEnabled initial=false acceleratedFiltersEnabled initial=false -regionBasedColumnsEnabled initial=false -cssGridLayoutEnabled initial=false useLegacyTextAlignPositionedElementBehavior initial=false +javaScriptRuntimeFlags type=JSC::RuntimeFlags # FIXME: This should really be disabled by default as it makes platforms that don't support the feature download files # they can't use by. Leaving enabled for now to not change existing behavior. downloadableBinaryFontsEnabled initial=true xssAuditorEnabled initial=false -unsafePluginPastingEnabled initial=true -acceleratedCompositingForFixedPositionEnabled initial=false +acceleratedCompositingForFixedPositionEnabled initial=defaultAcceleratedCompositingForFixedPositionEnabled acceleratedCompositingForOverflowScrollEnabled initial=false - -# Works only in conjunction with forceCompositingMode. -compositedScrollingForFramesEnabled initial=false +rubberBandingForSubScrollableRegionsEnabled initial=true, conditional=RUBBER_BANDING experimentalNotificationsEnabled initial=false webGLEnabled initial=false webGLErrorsToConsoleEnabled initial=true -openGLMultisamplingEnabled initial=true -multithreadedWebGLEnabled initial=false -privilegedWebGLExtensionsEnabled initial=false forceSoftwareWebGLRendering initial=false +forceWebGLUsesLowPower initial=false accelerated2dCanvasEnabled initial=false -antialiased2dCanvasEnabled initial=true loadDeferringEnabled initial=true webAudioEnabled initial=false paginateDuringLayoutEnabled initial=false fullScreenEnabled initial=false, conditional=FULLSCREEN_API asynchronousSpellCheckingEnabled initial=false -mediaStreamEnabled initial=false # This feature requires an implementation of ValidationMessageClient. interactiveFormValidationEnabled initial=false @@ -126,10 +125,18 @@ crossOriginCheckInGetMatchedCSSRulesDisabled initial=false forceCompositingMode initial=false shouldInjectUserScriptsInInitialEmptyDocument initial=false fixedElementsLayoutRelativeToFrame initial=false -allowDisplayOfInsecureContent initial=true -allowRunningOfInsecureContent initial=true -mediaPlaybackRequiresUserGesture initial=defaultMediaPlaybackRequiresUserGesture -mediaPlaybackAllowsInline initial=defaultMediaPlaybackAllowsInline +allowDisplayOfInsecureContent initial=false +allowRunningOfInsecureContent initial=false +requiresUserGestureToLoadVideo initial=defaultRequiresUserGestureToLoadVideo +videoPlaybackRequiresUserGesture initial=defaultVideoPlaybackRequiresUserGesture +audioPlaybackRequiresUserGesture initial=defaultAudioPlaybackRequiresUserGesture +mainContentUserGestureOverrideEnabled initial=false +allowsInlineMediaPlayback initial=defaultAllowsInlineMediaPlayback +allowsInlineMediaPlaybackAfterFullscreen initial=true +inlineMediaPlaybackRequiresPlaysInlineAttribute initial=defaultInlineMediaPlaybackRequiresPlaysInlineAttribute +allowsPictureInPictureMediaPlayback initial=defaultAllowsPictureInPictureMediaPlayback +mediaControlsScaleWithPageZoom initial=defaultMediaControlsScaleWithPageZoom +invisibleAutoplayNotPermitted initial=false passwordEchoEnabled initial=false suppressesIncrementalRendering initial=false incrementalRenderingSuppressionTimeoutInSeconds type=double, initial=defaultIncrementalRenderingSuppressionTimeoutInSeconds @@ -138,23 +145,22 @@ shouldDisplaySubtitles initial=false, conditional=VIDEO_TRACK shouldDisplayCaptions initial=false, conditional=VIDEO_TRACK shouldDisplayTextDescriptions initial=false, conditional=VIDEO_TRACK scrollingCoordinatorEnabled initial=false +scrollingTreeIncludesFrames initial=defaultScrollingTreeIncludesFrames scrollAnimatorEnabled initial=true, conditional=SMOOTH_SCROLLING +forceUpdateScrollbarsOnMainThreadForPerformanceTesting initial=false notificationsEnabled initial=true # Some apps needs isLoadingInAPISense to account for active subresource loaders. needsIsLoadingInAPISenseQuirk initial=false shouldRespectImageOrientation initial=defaultShouldRespectImageOrientation +imageSubsamplingEnabled initial=defaultImageSubsamplingEnabled wantsBalancedSetDefersLoadingBehavior initial=false requestAnimationFrameEnabled initial=true -deviceSupportsTouch initial=false -deviceSupportsMouse initial=true fixedPositionCreatesStackingContext initial=defaultFixedPositionCreatesStackingContext -syncXHRInDocumentsEnabled initial=true cookieEnabled initial=true mediaEnabled initial=true -applicationChromeMode initial=false DOMPasteAllowed initial=false # When enabled, window.blur() does not change focus, and @@ -163,7 +169,6 @@ DOMPasteAllowed initial=false windowFocusRestricted initial=true diagnosticLoggingEnabled initial=false -applyDeviceScaleFactorInCompositor initial=true delegatesPageScaling initial=false plugInSnapshottingEnabled initial=false snapshotAllPlugIns initial=false @@ -172,7 +177,6 @@ primaryPlugInSnapshotDetectionEnabled initial=true maximumPlugInSnapshotAttempts type=unsigned, initial=20 frameFlatteningEnabled initial=false -allowCustomScrollbarInMainFrame initial=true webSecurityEnabled initial=true spatialNavigationEnabled initial=false @@ -190,17 +194,96 @@ defaultVideoPosterURL type=String smartInsertDeleteEnabled initial=defaultSmartInsertDeleteEnabled selectTrailingWhitespaceEnabled initial=defaultSelectTrailingWhitespaceEnabled -selectionIncludesAltImageText initial=true useLegacyBackgroundSizeShorthandBehavior initial=false +fixedBackgroundsPaintRelativeToDocument initial=defaultFixedBackgroundsPaintRelativeToDocument -minimumZoomFontSize type=float, initial=15, conditional=IOS_TEXT_AUTOSIZING +textAutosizingEnabled initial=defaultTextAutosizingEnabled(), setNeedsStyleRecalcInAllFrames=1, conditional=TEXT_AUTOSIZING +textAutosizingWindowSizeOverride type=IntSize, setNeedsStyleRecalcInAllFrames=1, conditional=TEXT_AUTOSIZING +minimumZoomFontSize type=float, initial=defaultMinimumZoomFontSize(), conditional=TEXT_AUTOSIZING simpleLineLayoutEnabled initial=true, setNeedsStyleRecalcInAllFrames=1 simpleLineLayoutDebugBordersEnabled initial=false, setNeedsStyleRecalcInAllFrames=1 -mediaSourceEnabled initial=false +subpixelCSSOMElementMetricsEnabled initial=false + +useGiantTiles initial=false + +mediaSourceEnabled initial=true, conditional=MEDIA_SOURCE # FIXME: Rename to allowMultiElementImplicitFormSubmission once we upstream the iOS changes to WebView.mm. allowMultiElementImplicitSubmission initial=false -mediaPlaybackAllowsAirPlay initial=true, conditional=IOS_AIRPLAY +allowsAirPlayForMediaPlayback initial=true, conditional=WIRELESS_PLAYBACK_TARGET + +shouldConvertPositionStyleOnCopy initial=false + +maxParseDuration type=double, initial=-1 +standalone initial=false +telephoneNumberParsingEnabled initial=false +mediaDataLoadsAutomatically initial=defaultMediaDataLoadsAutomatically +shouldTransformsAffectOverflow initial=true +shouldDispatchJavaScriptWindowOnErrorEvents initial=false +alwaysUseAcceleratedOverflowScroll initial=false +imageControlsEnabled initial=false, conditional=SERVICE_CONTROLS + +enableInheritURIQueryComponent initial=false + +aggressiveTileRetentionEnabled initial=false +temporaryTileCohortRetentionEnabled initial=true + +useImageDocumentForSubframePDF initial=false +dataDetectorTypes type=DataDetectorTypes, initial=DataDetectorTypeNone, conditional=DATA_DETECTION + +# Allow SourceBuffers to store up to 304MB each, enough for approximately five minutes +# of 1080p video and stereo audio. +maximumSourceBufferSize type=int, initial=318767104, conditional=MEDIA_SOURCE + +serviceControlsEnabled initial=false, conditional=SERVICE_CONTROLS + +appleMailPaginationQuirkEnabled initial=false + +attachmentElementEnabled initial=true, conditional=ATTACHMENT_ELEMENT + +newBlockInsideInlineModelEnabled initial=false, setNeedsStyleRecalcInAllFrames=1 + +deferredCSSParserEnabled initial=false + +httpEquivEnabled initial=true + +# Some ports (e.g. iOS) might choose to display attachments inline, regardless of whether the response includes the +# HTTP header "Content-Disposition: attachment". This setting enables a sandbox around these attachments. The sandbox +# enforces all frame sandbox flags (see enum SandboxFlag in SecurityContext.h), and also disables <meta http-equiv> +# processing and subframe loading. +contentDispositionAttachmentSandboxEnabled initial=false + +userInterfaceDirectionPolicy type=UserInterfaceDirectionPolicy, initial=UserInterfaceDirectionPolicy::Content +systemLayoutDirection type=TextDirection, initial=LTR + +allowContentSecurityPolicySourceStarToMatchAnyProtocol initial=false + +selectionPaintingWithoutSelectionGapsEnabled initial=false + +shouldConvertInvalidURLsToBlank initial=true + +springTimingFunctionEnabled initial=false + +treatIPAddressAsDomain initial=false + +# Runtime-enabled features +visualViewportEnabled initial=false, setNeedsStyleRecalcInAllFrames=1 + +inputEventsEnabled initial=true + +quickTimePluginReplacementEnabled initial=defaultQuickTimePluginReplacementEnabled +youTubeFlashPluginReplacementEnabled initial=defaultYouTubeFlashPluginReplacementEnabled + +forcedColorsAreInvertedAccessibilityValue type=ForcedAccessibilityValue, initial=defaultForcedColorsAreInvertedAccessibilityValue +forcedDisplayIsMonochromeAccessibilityValue type=ForcedAccessibilityValue, initial=defaultForcedDisplayIsMonochromeAccessibilityValue +forcedPrefersReducedMotionAccessibilityValue type=ForcedAccessibilityValue, initial=defaultForcedPrefersReducedMotionAccessibilityValue + +largeImageAsyncDecodingEnabled initial=true +animatedImageAsyncDecodingEnabled initial=true + +shouldSuppressKeyboardInputDuringProvisionalNavigation initial=false + +langAttributeAwareFormControlUIEnabled initial=false diff --git a/Source/WebCore/page/SocketProvider.cpp b/Source/WebCore/page/SocketProvider.cpp new file mode 100644 index 000000000..836c0e838 --- /dev/null +++ b/Source/WebCore/page/SocketProvider.cpp @@ -0,0 +1,42 @@ +/* + * 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 "SocketProvider.h" + +#if ENABLE(WEB_SOCKETS) + +#include "SocketStreamHandleImpl.h" + +namespace WebCore { + +Ref<SocketStreamHandle> SocketProvider::createSocketStreamHandle(const URL& url, SocketStreamHandleClient& client, SessionID sessionID, const String& credentialPartition) +{ + return SocketStreamHandleImpl::create(url, client, sessionID, credentialPartition); +} + +} + +#endif // ENABLE(WEB_SOCKETS) diff --git a/Source/WebCore/page/SocketProvider.h b/Source/WebCore/page/SocketProvider.h new file mode 100644 index 000000000..b83105cb1 --- /dev/null +++ b/Source/WebCore/page/SocketProvider.h @@ -0,0 +1,48 @@ +/* + * 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 "SessionID.h" +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class ScriptExecutionContext; +class SocketStreamHandle; +class SocketStreamHandleClient; +class URL; + +class WEBCORE_EXPORT SocketProvider : public ThreadSafeRefCounted<SocketProvider> { +public: + static Ref<SocketProvider> create() { return adoptRef(*new SocketProvider); } +#if ENABLE(WEB_SOCKETS) + virtual Ref<SocketStreamHandle> createSocketStreamHandle(const URL&, SocketStreamHandleClient&, SessionID, const String& credentialPartition); +#endif + virtual ~SocketProvider() { }; +}; + +} diff --git a/Source/WebCore/page/SpatialNavigation.cpp b/Source/WebCore/page/SpatialNavigation.cpp index 50cb3f367..b73f46f4a 100644 --- a/Source/WebCore/page/SpatialNavigation.cpp +++ b/Source/WebCore/page/SpatialNavigation.cpp @@ -13,10 +13,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 @@ -34,7 +34,7 @@ #include "HTMLAreaElement.h" #include "HTMLImageElement.h" #include "HTMLMapElement.h" -#include "HTMLNames.h" +#include "HTMLSelectElement.h" #include "IntRect.h" #include "MainFrame.h" #include "Node.h" @@ -55,25 +55,24 @@ static void entryAndExitPointsForDirection(FocusDirection, const LayoutRect& sta static bool isScrollableNode(const Node*); FocusCandidate::FocusCandidate(Node* node, FocusDirection direction) - : visibleNode(0) - , focusableNode(0) - , enclosingScrollableBox(0) + : visibleNode(nullptr) + , focusableNode(nullptr) + , enclosingScrollableBox(nullptr) , distance(maxDistance()) , alignment(None) , isOffscreen(true) , isOffscreenAfterScrolling(true) { - ASSERT(node); - ASSERT(node->isElementNode()); + ASSERT(is<Element>(node)); - if (isHTMLAreaElement(node)) { - HTMLAreaElement* area = toHTMLAreaElement(node); - HTMLImageElement* image = area->imageElement(); + if (is<HTMLAreaElement>(*node)) { + HTMLAreaElement& area = downcast<HTMLAreaElement>(*node); + HTMLImageElement* image = area.imageElement(); if (!image || !image->renderer()) return; visibleNode = image; - rect = virtualRectForAreaElementAndDirection(area, direction); + rect = virtualRectForAreaElementAndDirection(&area, direction); } else { if (!node->renderer()) return; @@ -366,8 +365,8 @@ bool scrollInDirection(Frame* frame, FocusDirection direction) bool scrollInDirection(Node* container, FocusDirection direction) { ASSERT(container); - if (container->isDocumentNode()) - return scrollInDirection(toDocument(container)->frame(), direction); + if (is<Document>(*container)) + return scrollInDirection(downcast<Document>(*container).frame(), direction); if (!container->renderBox()) return false; @@ -419,15 +418,11 @@ static void deflateIfOverlapped(LayoutRect& a, LayoutRect& b) bool isScrollableNode(const Node* node) { - ASSERT(!node->isDocumentNode()); - if (!node) return false; - - if (RenderObject* renderer = node->renderer()) - return renderer->isBox() && toRenderBox(renderer)->canBeScrolledAndHasScrollableArea() && node->hasChildNodes(); - - return false; + ASSERT(!node->isDocumentNode()); + auto* renderer = node->renderer(); + return is<RenderBox>(renderer) && downcast<RenderBox>(*renderer).canBeScrolledAndHasScrollableArea() && node->hasChildNodes(); } Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusDirection direction, Node* node) @@ -435,11 +430,11 @@ Node* scrollableEnclosingBoxOrParentFrameForNodeInDirection(FocusDirection direc ASSERT(node); Node* parent = node; do { - if (parent->isDocumentNode()) - parent = toDocument(parent)->document().frame()->ownerElement(); + if (is<Document>(*parent)) + parent = downcast<Document>(*parent).document().frame()->ownerElement(); else parent = parent->parentNode(); - } while (parent && !canScrollInDirection(parent, direction) && !parent->isDocumentNode()); + } while (parent && !canScrollInDirection(parent, direction) && !is<Document>(*parent)); return parent; } @@ -448,11 +443,11 @@ bool canScrollInDirection(const Node* container, FocusDirection direction) { ASSERT(container); - if (isHTMLSelectElement(container)) + if (is<HTMLSelectElement>(*container)) return false; - if (container->isDocumentNode()) - return canScrollInDirection(toDocument(container)->frame(), direction); + if (is<Document>(*container)) + return canScrollInDirection(downcast<Document>(*container).frame(), direction); if (!isScrollableNode(container)) return false; @@ -484,24 +479,26 @@ bool canScrollInDirection(const Frame* frame, FocusDirection direction) if ((direction == FocusDirectionUp || direction == FocusDirectionDown) && ScrollbarAlwaysOff == verticalMode) return false; LayoutSize size = frame->view()->totalContentsSize(); - LayoutSize offset = frame->view()->scrollOffset(); - LayoutRect rect = frame->view()->visibleContentRectIncludingScrollbars(); + LayoutPoint scrollPosition = frame->view()->scrollPosition(); + LayoutRect rect = frame->view()->unobscuredContentRectIncludingScrollbars(); + // FIXME: wrong in RTL documents. switch (direction) { case FocusDirectionLeft: - return offset.width() > 0; + return scrollPosition.x() > 0; case FocusDirectionUp: - return offset.height() > 0; + return scrollPosition.y() > 0; case FocusDirectionRight: - return rect.width() + offset.width() < size.width(); + return rect.width() + scrollPosition.x() < size.width(); case FocusDirectionDown: - return rect.height() + offset.height() < size.height(); + return rect.height() + scrollPosition.y() < size.height(); default: ASSERT_NOT_REACHED(); return false; } } +// FIXME: This is completely broken. This should be deleted and callers should be calling ScrollView::contentsToWindow() instead. static LayoutRect rectToAbsoluteCoordinates(Frame* initialFrame, const LayoutRect& initialRect) { LayoutRect rect = initialRect; @@ -510,7 +507,7 @@ static LayoutRect rectToAbsoluteCoordinates(Frame* initialFrame, const LayoutRec do { rect.move(element->offsetLeft(), element->offsetTop()); } while ((element = element->offsetParent())); - rect.move((-frame->view()->scrollOffset())); + rect.moveBy((-frame->view()->scrollPosition())); } } return rect; @@ -520,9 +517,12 @@ LayoutRect nodeRectInAbsoluteCoordinates(Node* node, bool ignoreBorder) { ASSERT(node && node->renderer() && !node->document().view()->needsLayout()); - if (node->isDocumentNode()) - return frameRectInAbsoluteCoordinates(toDocument(node)->frame()); - LayoutRect rect = rectToAbsoluteCoordinates(node->document().frame(), node->boundingBox()); + if (is<Document>(*node)) + return frameRectInAbsoluteCoordinates(downcast<Document>(*node).frame()); + + LayoutRect rect; + if (RenderObject* renderer = node->renderer()) + rect = rectToAbsoluteCoordinates(node->document().frame(), renderer->absoluteBoundingBoxRect()); // For authors that use border instead of outline in their CSS, we compensate by ignoring the border when calculating // the rect of the focused element. @@ -607,7 +607,7 @@ bool areElementsOnSameLine(const FocusCandidate& firstCandidate, const FocusCand if (!firstCandidate.rect.intersects(secondCandidate.rect)) return false; - if (isHTMLAreaElement(firstCandidate.focusableNode) || isHTMLAreaElement(secondCandidate.focusableNode)) + if (is<HTMLAreaElement>(*firstCandidate.focusableNode) || is<HTMLAreaElement>(*secondCandidate.focusableNode)) return false; if (!firstCandidate.visibleNode->renderer()->isRenderInline() || !secondCandidate.visibleNode->renderer()->isRenderInline()) @@ -758,7 +758,7 @@ LayoutRect virtualRectForAreaElementAndDirection(HTMLAreaElement* area, FocusDir HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate& candidate) { - return candidate.isFrameOwnerElement() ? toHTMLFrameOwnerElement(candidate.visibleNode) : nullptr; -}; + return candidate.isFrameOwnerElement() ? downcast<HTMLFrameOwnerElement>(candidate.visibleNode) : nullptr; +} } // namespace WebCore diff --git a/Source/WebCore/page/SpatialNavigation.h b/Source/WebCore/page/SpatialNavigation.h index fd6f48f52..6f201f635 100644 --- a/Source/WebCore/page/SpatialNavigation.h +++ b/Source/WebCore/page/SpatialNavigation.h @@ -18,14 +18,12 @@ * Boston, MA 02110-1301, USA. */ -#ifndef SpatialNavigation_h -#define SpatialNavigation_h +#pragma once #include "FocusDirection.h" #include "HTMLFrameOwnerElement.h" #include "LayoutRect.h" #include "Node.h" - #include <limits> namespace WebCore { @@ -99,9 +97,9 @@ enum RectsAlignment { struct FocusCandidate { FocusCandidate() - : visibleNode(0) - , focusableNode(0) - , enclosingScrollableBox(0) + : visibleNode(nullptr) + , focusableNode(nullptr) + , enclosingScrollableBox(nullptr) , distance(maxDistance()) , alignment(None) , isOffscreen(true) @@ -146,5 +144,3 @@ LayoutRect virtualRectForAreaElementAndDirection(HTMLAreaElement*, FocusDirectio HTMLFrameOwnerElement* frameOwnerElement(FocusCandidate&); } // namspace WebCore - -#endif // SpatialNavigation_h diff --git a/Source/WebCore/page/SpeechInput.cpp b/Source/WebCore/page/SpeechInput.cpp deleted file mode 100644 index 228d3fcd8..000000000 --- a/Source/WebCore/page/SpeechInput.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SpeechInput.h" - -#if ENABLE(INPUT_SPEECH) - -#include "SecurityOrigin.h" -#include "SpeechInputClient.h" -#include "SpeechInputListener.h" -#include <wtf/PassOwnPtr.h> - -namespace WebCore { - -SpeechInput::SpeechInput(SpeechInputClient* client) - : m_client(client) - , m_nextListenerId(1) -{ - m_client->setListener(this); -} - -SpeechInput::~SpeechInput() -{ - m_client->setListener(0); -} - -PassOwnPtr<SpeechInput> SpeechInput::create(SpeechInputClient* client) -{ - return adoptPtr(new SpeechInput(client)); -} - -int SpeechInput::registerListener(SpeechInputListener* listener) -{ -#if defined(DEBUG) - // Check if already present. - for (HashMap<int, SpeechInputListener*>::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) - ASSERT(it->value != listener); -#endif - - m_listeners.add(m_nextListenerId, listener); - return m_nextListenerId++; -} - -void SpeechInput::unregisterListener(int listenerId) -{ - if (m_listeners.contains(listenerId)) - m_listeners.remove(listenerId); -} - -void SpeechInput::didCompleteRecording(int listenerId) -{ - // Don't assert if not present as the element might have been removed by the page while - // this event was on the way. - if (m_listeners.contains(listenerId)) - m_listeners.get(listenerId)->didCompleteRecording(listenerId); -} - -void SpeechInput::didCompleteRecognition(int listenerId) -{ - // Don't assert if not present as the element might have been removed by the page while - // this event was on the way. - if (m_listeners.contains(listenerId)) - m_listeners.get(listenerId)->didCompleteRecognition(listenerId); -} - -void SpeechInput::setRecognitionResult(int listenerId, const SpeechInputResultArray& result) -{ - // Don't assert if not present as the element might have been removed by the page while - // this event was on the way. - if (m_listeners.contains(listenerId)) - m_listeners.get(listenerId)->setRecognitionResult(listenerId, result); -} - -bool SpeechInput::startRecognition(int listenerId, const IntRect& elementRect, const AtomicString& language, const String& grammar, SecurityOrigin* origin) -{ - ASSERT(m_listeners.contains(listenerId)); - return m_client->startRecognition(listenerId, elementRect, language, grammar, origin); -} - -void SpeechInput::stopRecording(int listenerId) -{ - ASSERT(m_listeners.contains(listenerId)); - m_client->stopRecording(listenerId); -} - -void SpeechInput::cancelRecognition(int listenerId) -{ - ASSERT(m_listeners.contains(listenerId)); - m_client->cancelRecognition(listenerId); -} - -const char* SpeechInput::supplementName() -{ - return "SpeechInput"; -} - -void provideSpeechInputTo(Page* page, SpeechInputClient* client) -{ - SpeechInput::provideTo(page, SpeechInput::supplementName(), SpeechInput::create(client)); -} - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) diff --git a/Source/WebCore/page/SpeechInput.h b/Source/WebCore/page/SpeechInput.h deleted file mode 100644 index 447f632e6..000000000 --- a/Source/WebCore/page/SpeechInput.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SpeechInput_h -#define SpeechInput_h - -#if ENABLE(INPUT_SPEECH) - -#include "Page.h" -#include "SpeechInputListener.h" -#include <wtf/Forward.h> -#include <wtf/HashMap.h> - -namespace WebCore { - -class IntRect; -class SecurityOrigin; -class SpeechInputClient; -class SpeechInputListener; - -// This class connects the input elements requiring speech input with the platform specific -// speech recognition engine. It provides methods for the input elements to activate speech -// recognition and methods for the speech recognition engine to return back the results. -class SpeechInput : public SpeechInputListener, - public Supplement<Page> { - WTF_MAKE_NONCOPYABLE(SpeechInput); -public: - virtual ~SpeechInput(); - - static PassOwnPtr<SpeechInput> create(SpeechInputClient*); - static const char* supplementName(); - static SpeechInput* from(Page* page) { return static_cast<SpeechInput*>(Supplement<Page>::from(page, supplementName())); } - - // Generates a unique ID for the given listener to be used for speech requests. - // This should be the first call made by listeners before anything else. - int registerListener(SpeechInputListener*); - - // Invoked when the listener is done with recording or getting destroyed. - // Failure to unregister may result in crashes if there were any pending speech events. - void unregisterListener(int); - - // Methods invoked by the input elements. - bool startRecognition(int listenerId, const IntRect& elementRect, const AtomicString& language, const String& grammar, SecurityOrigin*); - void stopRecording(int); - void cancelRecognition(int); - - // SpeechInputListener methods. - virtual void didCompleteRecording(int); - virtual void didCompleteRecognition(int); - virtual void setRecognitionResult(int, const SpeechInputResultArray&); - -private: - explicit SpeechInput(SpeechInputClient*); - - SpeechInputClient* m_client; - HashMap<int, SpeechInputListener*> m_listeners; - int m_nextListenerId; -}; - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) - -#endif // SpeechInput_h diff --git a/Source/WebCore/page/SpeechInputClient.h b/Source/WebCore/page/SpeechInputClient.h deleted file mode 100644 index 6992fba43..000000000 --- a/Source/WebCore/page/SpeechInputClient.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SpeechInputClient_h -#define SpeechInputClient_h - -#include <wtf/Forward.h> - -#if ENABLE(INPUT_SPEECH) - -namespace WebCore { - -class IntRect; -class SecurityOrigin; -class SpeechInputListener; -class Page; - -// Provides an interface for SpeechInput to call into the embedder. -class SpeechInputClient { -public: - // This is the first call made by a listener, registering itself for future callbacks. - // When the listener no longer needs speech input (for e.g. when it gets destroyed), - // it should set a null listener to stop receiving callbacks. - // The client does not take ownership of the pointer. - virtual void setListener(SpeechInputListener*) = 0; - - // Starts speech recognition and audio recording. elementRect is the position - // of the element where the user clicked in the RootView coordinate system. - virtual bool startRecognition(int requestId, const IntRect& elementRect, const AtomicString& language, const String& grammar, SecurityOrigin*) = 0; - - // Stops audio recording and performs recognition with the audio recorded until now - // (does not discard audio). - virtual void stopRecording(int requestId) = 0; - - // Cancels an ongoing recognition and discards any audio recorded so far. No partial - // recognition results are returned to the listener. - virtual void cancelRecognition(int requestId) = 0; - -protected: - virtual ~SpeechInputClient() { } -}; - -void provideSpeechInputTo(Page*, SpeechInputClient*); - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) - -#endif // SpeechInputClient_h diff --git a/Source/WebCore/page/SpeechInputEvent.cpp b/Source/WebCore/page/SpeechInputEvent.cpp deleted file mode 100644 index 9afb7710e..000000000 --- a/Source/WebCore/page/SpeechInputEvent.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(INPUT_SPEECH) - -#include "SpeechInputEvent.h" - -#include "EventNames.h" - -namespace WebCore { - -PassRefPtr<SpeechInputEvent> SpeechInputEvent::create() -{ - return adoptRef(new SpeechInputEvent); -} - -PassRefPtr<SpeechInputEvent> SpeechInputEvent::create(const AtomicString& eventType, const SpeechInputResultArray& results) -{ - return adoptRef(new SpeechInputEvent(eventType, results)); -} - -SpeechInputEvent::SpeechInputEvent() -{ -} - -SpeechInputEvent::SpeechInputEvent(const AtomicString& eventType, const SpeechInputResultArray& results) - : Event(eventType, true, false) // Can bubble, not cancelable - , m_results(SpeechInputResultList::create(results)) -{ -} - -SpeechInputEvent::~SpeechInputEvent() -{ -} - -EventInterface SpeechInputEvent::eventInterface() const -{ - return SpeechInputEventInterfaceType; -} - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) diff --git a/Source/WebCore/page/SpeechInputEvent.h b/Source/WebCore/page/SpeechInputEvent.h deleted file mode 100644 index bb33cebdb..000000000 --- a/Source/WebCore/page/SpeechInputEvent.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SpeechInputEvent_h -#define SpeechInputEvent_h - -#if ENABLE(INPUT_SPEECH) - -#include "Event.h" -#include "SpeechInputResultList.h" - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class SpeechInputEvent : public Event { -public: - static PassRefPtr<SpeechInputEvent> create(); - static PassRefPtr<SpeechInputEvent> create(const AtomicString& eventType, const SpeechInputResultArray& results); - ~SpeechInputEvent(); - - SpeechInputResultList* results() const { return m_results.get(); } - - virtual EventInterface eventInterface() const; - -private: - SpeechInputEvent(); - SpeechInputEvent(const AtomicString& eventType, const SpeechInputResultArray& results); - - RefPtr<SpeechInputResultList> m_results; -}; - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) - -#endif // SpeechInputEvent_h diff --git a/Source/WebCore/page/SpeechInputEvent.idl b/Source/WebCore/page/SpeechInputEvent.idl deleted file mode 100644 index be82107d1..000000000 --- a/Source/WebCore/page/SpeechInputEvent.idl +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -[ - Conditional=INPUT_SPEECH, -] interface SpeechInputEvent : Event { - readonly attribute SpeechInputResultList results; -}; - diff --git a/Source/WebCore/page/SpeechInputListener.h b/Source/WebCore/page/SpeechInputListener.h deleted file mode 100644 index 741530127..000000000 --- a/Source/WebCore/page/SpeechInputListener.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SpeechInputListener_h -#define SpeechInputListener_h - -#if ENABLE(INPUT_SPEECH) - -#include "SpeechInputResult.h" -#include <wtf/Forward.h> - -namespace WebCore { - -// Interface to be implemented by the element which invokes SpeechInput. -class SpeechInputListener { -public: - // Informs that audio recording has completed and recognition is underway. - virtual void didCompleteRecording(int requestId) = 0; - - // Informs that speech recognition has completed. This gets invoked irrespective of whether - // recognition was succesful or not, whether setRecognitionResult() was invoked or not. The - // handler typically frees up any temporary resources allocated and waits for the next speech - // recognition request. - virtual void didCompleteRecognition(int requestId) = 0; - - // Gives results from speech recognition, either partial or the final results. - // This method can potentially get called multiple times if there are partial results - // available as the user keeps speaking. If the speech could not be recognized properly - // or if there was any other errors in the process, this method may never be called. - virtual void setRecognitionResult(int requestId, const SpeechInputResultArray&) = 0; - -protected: - virtual ~SpeechInputListener() { } -}; - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) - -#endif // SpeechInputListener_h diff --git a/Source/WebCore/page/SpeechInputResult.cpp b/Source/WebCore/page/SpeechInputResult.cpp deleted file mode 100644 index 1c042a056..000000000 --- a/Source/WebCore/page/SpeechInputResult.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SpeechInputResult.h" - -#if ENABLE(INPUT_SPEECH) - -namespace WebCore { - -PassRefPtr<SpeechInputResult> SpeechInputResult::create(const String& utterance, double confidence) -{ - return adoptRef(new SpeechInputResult(utterance, confidence)); -} - -PassRefPtr<SpeechInputResult> SpeechInputResult::create(const SpeechInputResult& source) -{ - return adoptRef(new SpeechInputResult(source.m_utterance, source.m_confidence)); -} - -SpeechInputResult::SpeechInputResult(const String& utterance, double confidence) - : m_utterance(utterance) - , m_confidence(confidence) -{ -} - -double SpeechInputResult::confidence() const -{ - return m_confidence; -} - -const String& SpeechInputResult::utterance() const -{ - return m_utterance; -} - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) diff --git a/Source/WebCore/page/SpeechInputResult.h b/Source/WebCore/page/SpeechInputResult.h deleted file mode 100644 index 1070e1257..000000000 --- a/Source/WebCore/page/SpeechInputResult.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SpeechInputResult_h -#define SpeechInputResult_h - -#if ENABLE(INPUT_SPEECH) - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/text/WTFString.h> - -namespace WebCore { - -// This class holds one speech recognition result including the text and other related -// fields, as received from the embedder. -class SpeechInputResult : public RefCounted<SpeechInputResult> { -public: - static PassRefPtr<SpeechInputResult> create(const SpeechInputResult& source); - static PassRefPtr<SpeechInputResult> create(const String& utterance, double confidence); - - double confidence() const; - const String& utterance() const; - -private: - SpeechInputResult(const String& utterance, double confidence); - - String m_utterance; - double m_confidence; -}; - -typedef Vector<RefPtr<SpeechInputResult>> SpeechInputResultArray; - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) - -#endif // SpeechInputResult_h diff --git a/Source/WebCore/page/SpeechInputResult.idl b/Source/WebCore/page/SpeechInputResult.idl deleted file mode 100644 index 41a530ad3..000000000 --- a/Source/WebCore/page/SpeechInputResult.idl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -[ - NoInterfaceObject, - Conditional=INPUT_SPEECH, - ImplementationLacksVTable -] interface SpeechInputResult { - readonly attribute DOMString utterance; - readonly attribute float confidence; -}; - diff --git a/Source/WebCore/page/SpeechInputResultList.cpp b/Source/WebCore/page/SpeechInputResultList.cpp deleted file mode 100644 index 41fd108f5..000000000 --- a/Source/WebCore/page/SpeechInputResultList.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SpeechInputResultList.h" - -#if ENABLE(INPUT_SPEECH) - -namespace WebCore { - -PassRefPtr<SpeechInputResultList> SpeechInputResultList::create(const SpeechInputResultArray& results) -{ - return adoptRef(new SpeechInputResultList(results)); -} - -SpeechInputResult* SpeechInputResultList::item(unsigned index) -{ - return index >= m_results.size() ? 0 : m_results[index].get(); -} - -SpeechInputResultList::SpeechInputResultList(const SpeechInputResultArray& results) - : m_results(results) // Takes a copy of the array of RefPtrs. -{ -} - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) diff --git a/Source/WebCore/page/SpeechInputResultList.h b/Source/WebCore/page/SpeechInputResultList.h deleted file mode 100644 index 8556aecf0..000000000 --- a/Source/WebCore/page/SpeechInputResultList.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SpeechInputResultList_h -#define SpeechInputResultList_h - -#if ENABLE(INPUT_SPEECH) - -#include "SpeechInputResult.h" - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class SpeechInputResultList : public RefCounted<SpeechInputResultList> { -public: - static PassRefPtr<SpeechInputResultList> create(const SpeechInputResultArray& results); - - // Methods from the IDL. - size_t length() { return m_results.size(); } - SpeechInputResult* item(unsigned index); - -private: - explicit SpeechInputResultList(const SpeechInputResultArray& results); - - SpeechInputResultArray m_results; -}; - -} // namespace WebCore - -#endif // ENABLE(INPUT_SPEECH) - -#endif // SpeechInputResultList_h diff --git a/Source/WebCore/page/SpeechInputResultList.idl b/Source/WebCore/page/SpeechInputResultList.idl deleted file mode 100644 index 7a21c136f..000000000 --- a/Source/WebCore/page/SpeechInputResultList.idl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2010 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -[ - NoInterfaceObject, - Conditional=INPUT_SPEECH, - ImplementationLacksVTable, -] interface SpeechInputResultList { - readonly attribute unsigned long length; - getter SpeechInputResult item([IsIndex] unsigned long index); -}; - diff --git a/Source/WebCore/page/SuspendableTimer.cpp b/Source/WebCore/page/SuspendableTimer.cpp index 00df344f5..76583c12b 100644 --- a/Source/WebCore/page/SuspendableTimer.cpp +++ b/Source/WebCore/page/SuspendableTimer.cpp @@ -13,7 +13,7 @@ * 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 @@ -31,8 +31,8 @@ namespace WebCore { -SuspendableTimer::SuspendableTimer(ScriptExecutionContext* context) - : ActiveDOMObject(context) +SuspendableTimer::SuspendableTimer(ScriptExecutionContext& context) + : ActiveDOMObject(&context) , m_suspended(false) , m_savedNextFireInterval(0) , m_savedRepeatInterval(0) @@ -84,7 +84,7 @@ void SuspendableTimer::resume() start(m_savedNextFireInterval, m_savedRepeatInterval); } -bool SuspendableTimer::canSuspend() const +bool SuspendableTimer::canSuspendForDocumentSuspension() const { return true; } diff --git a/Source/WebCore/page/SuspendableTimer.h b/Source/WebCore/page/SuspendableTimer.h index 44cb13856..e15f0a709 100644 --- a/Source/WebCore/page/SuspendableTimer.h +++ b/Source/WebCore/page/SuspendableTimer.h @@ -13,7 +13,7 @@ * 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,8 +24,7 @@ * */ -#ifndef SuspendableTimer_h -#define SuspendableTimer_h +#pragma once #include "ActiveDOMObject.h" #include "Timer.h" @@ -34,7 +33,7 @@ namespace WebCore { class SuspendableTimer : private TimerBase, public ActiveDOMObject { public: - explicit SuspendableTimer(ScriptExecutionContext*); + explicit SuspendableTimer(ScriptExecutionContext&); virtual ~SuspendableTimer(); // A hook for derived classes to perform cleanup. @@ -43,11 +42,19 @@ public: // Part of TimerBase interface used by SuspendableTimer clients, modified to work when suspended. bool isActive() const { return TimerBase::isActive() || (m_suspended && m_savedIsActive); } bool isSuspended() const { return m_suspended; } + void startRepeating(double repeatInterval); void startOneShot(double interval); double repeatInterval() const; void augmentFireInterval(double delta); void augmentRepeatInterval(double delta); + + void startRepeating(std::chrono::milliseconds repeatInterval) { startRepeating(msToSeconds(repeatInterval)); } + void startOneShot(std::chrono::milliseconds interval) { startOneShot(msToSeconds(interval)); } + std::chrono::milliseconds repeatIntervalMS() const { return secondsToMS(repeatInterval()); } + void augmentFireInterval(std::chrono::milliseconds delta) { augmentFireInterval(msToSeconds(delta)); } + void augmentRepeatInterval(std::chrono::milliseconds delta) { augmentRepeatInterval(msToSeconds(delta)); } + using TimerBase::didChangeAlignmentInterval; using TimerBase::operator new; using TimerBase::operator delete; @@ -55,14 +62,14 @@ public: void cancel(); // Equivalent to TimerBase::stop(), whose name conflicts with ActiveDOMObject::stop(). private: - virtual void fired() = 0; + void fired() override = 0; - // ActiveDOMObject - virtual bool hasPendingActivity() const final override; - virtual void stop() final override; - virtual bool canSuspend() const final override; - virtual void suspend(ReasonForSuspension) final override; - virtual void resume() final override; + // ActiveDOMObject API. + bool hasPendingActivity() const final; + void stop() final; + bool canSuspendForDocumentSuspension() const final; + void suspend(ReasonForSuspension) final; + void resume() final; bool m_suspended; @@ -72,6 +79,3 @@ private: }; } // namespace WebCore - -#endif // SuspendableTimer_h - diff --git a/Source/WebCore/page/TextIndicator.cpp b/Source/WebCore/page/TextIndicator.cpp new file mode 100644 index 000000000..ea90bf74b --- /dev/null +++ b/Source/WebCore/page/TextIndicator.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010, 2015-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 "TextIndicator.h" + +#include "Document.h" +#include "Editor.h" +#include "Element.h" +#include "Frame.h" +#include "FrameSelection.h" +#include "FrameSnapshotting.h" +#include "FrameView.h" +#include "GeometryUtilities.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "IntRect.h" +#include "NodeTraversal.h" +#include "Range.h" +#include "RenderObject.h" + +using namespace WebCore; + +namespace WebCore { + +static bool initializeIndicator(TextIndicatorData&, Frame&, const Range&, FloatSize margin, bool indicatesCurrentSelection); + +TextIndicator::TextIndicator(const TextIndicatorData& data) + : m_data(data) +{ +} + +TextIndicator::~TextIndicator() +{ +} + +Ref<TextIndicator> TextIndicator::create(const TextIndicatorData& data) +{ + return adoptRef(*new TextIndicator(data)); +} + +RefPtr<TextIndicator> TextIndicator::createWithRange(const Range& range, TextIndicatorOptions options, TextIndicatorPresentationTransition presentationTransition, FloatSize margin) +{ + Frame* frame = range.startContainer().document().frame(); + + if (!frame) + return nullptr; + + Ref<Frame> protector(*frame); + +#if PLATFORM(IOS) + frame->editor().setIgnoreCompositionSelectionChange(true); + frame->selection().setUpdateAppearanceEnabled(true); +#endif + + VisibleSelection oldSelection = frame->selection().selection(); + frame->selection().setSelection(range); + + TextIndicatorData data; + + data.presentationTransition = presentationTransition; + data.options = options; + + bool indicatesCurrentSelection = areRangesEqual(&range, oldSelection.toNormalizedRange().get()); + + if (!initializeIndicator(data, *frame, range, margin, indicatesCurrentSelection)) + return nullptr; + + RefPtr<TextIndicator> indicator = TextIndicator::create(data); + + frame->selection().setSelection(oldSelection); + +#if PLATFORM(IOS) + frame->editor().setIgnoreCompositionSelectionChange(false, Editor::RevealSelection::No); + frame->selection().setUpdateAppearanceEnabled(false); +#endif + + return indicator; +} + +RefPtr<TextIndicator> TextIndicator::createWithSelectionInFrame(Frame& frame, TextIndicatorOptions options, TextIndicatorPresentationTransition presentationTransition, FloatSize margin) +{ + RefPtr<Range> range = frame.selection().toNormalizedRange(); + if (!range) + return nullptr; + + TextIndicatorData data; + + data.presentationTransition = presentationTransition; + data.options = options; + + if (!initializeIndicator(data, frame, *range, margin, true)) + return nullptr; + + return TextIndicator::create(data); +} + +static bool hasNonInlineOrReplacedElements(const Range& range) +{ + Node* stopNode = range.pastLastNode(); + for (Node* node = range.firstNode(); node != stopNode; node = NodeTraversal::next(*node)) { + if (!node) + continue; + RenderObject* renderer = node->renderer(); + if (!renderer) + continue; + if ((!renderer->isInline() || renderer->isReplaced()) && range.intersectsNode(*node).releaseReturnValue()) + return true; + } + + return false; +} + +static SnapshotOptions snapshotOptionsForTextIndicatorOptions(TextIndicatorOptions options) +{ + SnapshotOptions snapshotOptions = SnapshotOptionsNone; + if (!(options & TextIndicatorOptionRespectTextColor)) + snapshotOptions |= SnapshotOptionsForceBlackText; + + if (!(options & TextIndicatorOptionPaintAllContent)) { + if (options & TextIndicatorOptionPaintBackgrounds) + snapshotOptions |= SnapshotOptionsPaintSelectionAndBackgroundsOnly; + else + snapshotOptions |= SnapshotOptionsPaintSelectionOnly; + } else + snapshotOptions |= SnapshotOptionsExcludeSelectionHighlighting; + + return snapshotOptions; +} + +static RefPtr<Image> takeSnapshot(Frame& frame, IntRect rect, SnapshotOptions options, float& scaleFactor, Vector<FloatRect>& clipRectsInDocumentCoordinates) +{ + std::unique_ptr<ImageBuffer> buffer = snapshotFrameRectWithClip(frame, rect, clipRectsInDocumentCoordinates, options); + if (!buffer) + return nullptr; + scaleFactor = buffer->resolutionScale(); + return ImageBuffer::sinkIntoImage(WTFMove(buffer), Unscaled); +} + +static bool takeSnapshots(TextIndicatorData& data, Frame& frame, IntRect snapshotRect, Vector<FloatRect>& clipRectsInDocumentCoordinates) +{ + SnapshotOptions snapshotOptions = snapshotOptionsForTextIndicatorOptions(data.options); + + data.contentImage = takeSnapshot(frame, snapshotRect, snapshotOptions, data.contentImageScaleFactor, clipRectsInDocumentCoordinates); + if (!data.contentImage) + return false; + + if (data.options & TextIndicatorOptionIncludeSnapshotWithSelectionHighlight) { + float snapshotScaleFactor; + data.contentImageWithHighlight = takeSnapshot(frame, snapshotRect, SnapshotOptionsNone, snapshotScaleFactor, clipRectsInDocumentCoordinates); + ASSERT(!data.contentImageWithHighlight || data.contentImageScaleFactor == snapshotScaleFactor); + } + + return true; +} + +static bool initializeIndicator(TextIndicatorData& data, Frame& frame, const Range& range, FloatSize margin, bool indicatesCurrentSelection) +{ + Vector<FloatRect> textRects; + + // FIXME (138888): Ideally we wouldn't remove the margin in this case, but we need to + // ensure that the indicator and indicator-with-highlight overlap precisely, and + // we can't add a margin to the indicator-with-highlight. + if (indicatesCurrentSelection && !(data.options & TextIndicatorOptionIncludeMarginIfRangeMatchesSelection)) + margin = FloatSize(); + + FrameSelection::TextRectangleHeight textRectHeight = (data.options & TextIndicatorOptionTightlyFitContent) ? FrameSelection::TextRectangleHeight::TextHeight : FrameSelection::TextRectangleHeight::SelectionHeight; + + if ((data.options & TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges) && hasNonInlineOrReplacedElements(range)) + data.options |= TextIndicatorOptionPaintAllContent; + else { + if (data.options & TextIndicatorOptionDoNotClipToVisibleRect) + frame.selection().getTextRectangles(textRects, textRectHeight); + else + frame.selection().getClippedVisibleTextRectangles(textRects, textRectHeight); + } + + if (textRects.isEmpty()) { + RenderView* renderView = frame.contentRenderer(); + if (!renderView) + return false; + FloatRect boundingRect = range.absoluteBoundingRect(); + if (data.options & TextIndicatorOptionDoNotClipToVisibleRect) + textRects.append(boundingRect); + else { + // Clip to the visible rect, just like getClippedVisibleTextRectangles does. + // FIXME: We really want to clip to the unobscured rect in both cases, I think. + // (this seems to work on Mac, but maybe not iOS?) + FloatRect visibleContentRect = frame.view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); + textRects.append(intersection(visibleContentRect, boundingRect)); + } + } + + FloatRect textBoundingRectInRootViewCoordinates; + FloatRect textBoundingRectInDocumentCoordinates; + Vector<FloatRect> textRectsInRootViewCoordinates; + for (const FloatRect& textRect : textRects) { + FloatRect textRectInDocumentCoordinatesIncludingMargin = textRect; + textRectInDocumentCoordinatesIncludingMargin.inflateX(margin.width()); + textRectInDocumentCoordinatesIncludingMargin.inflateY(margin.height()); + textBoundingRectInDocumentCoordinates.unite(textRectInDocumentCoordinatesIncludingMargin); + + FloatRect textRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(textRectInDocumentCoordinatesIncludingMargin)); + textRectsInRootViewCoordinates.append(textRectInRootViewCoordinates); + textBoundingRectInRootViewCoordinates.unite(textRectInRootViewCoordinates); + } + + Vector<FloatRect> textRectsInBoundingRectCoordinates; + for (auto rect : textRectsInRootViewCoordinates) { + rect.moveBy(-textBoundingRectInRootViewCoordinates.location()); + textRectsInBoundingRectCoordinates.append(rect); + } + + // Store the selection rect in window coordinates, to be used subsequently + // to determine if the indicator and selection still precisely overlap. + data.selectionRectInRootViewCoordinates = frame.view()->contentsToRootView(enclosingIntRect(frame.selection().selectionBounds())); + data.textBoundingRectInRootViewCoordinates = textBoundingRectInRootViewCoordinates; + data.textRectsInBoundingRectCoordinates = textRectsInBoundingRectCoordinates; + + return takeSnapshots(data, frame, enclosingIntRect(textBoundingRectInDocumentCoordinates), textRects); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/TextIndicator.h b/Source/WebCore/page/TextIndicator.h new file mode 100644 index 000000000..dfa42c7dc --- /dev/null +++ b/Source/WebCore/page/TextIndicator.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2010 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 "FloatRect.h" +#include "Image.h" +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Frame; +class GraphicsContext; +class Range; + +// FIXME: Move PresentationTransition to TextIndicatorWindow, because it's about presentation. +enum class TextIndicatorPresentationTransition { + None, + + // These animations drive themselves. + Bounce, + BounceAndCrossfade, + + // This animation needs to be driven manually via TextIndicatorWindow::setAnimationProgress. + FadeIn, +}; + +// Make sure to keep these in sync with the ones in Internals.idl. +enum TextIndicatorOption : uint8_t { + TextIndicatorOptionDefault = 0, + + // Use the styled text color instead of forcing black text (the default) + TextIndicatorOptionRespectTextColor = 1 << 0, + + // Paint backgrounds, even if they're not part of the Range + TextIndicatorOptionPaintBackgrounds = 1 << 1, + + // Don't restrict painting to the given Range + TextIndicatorOptionPaintAllContent = 1 << 2, + + // Take two snapshots: + // - one including the selection highlight and ignoring other painting-related options + // - one respecting the other painting-related options + TextIndicatorOptionIncludeSnapshotWithSelectionHighlight = 1 << 3, + + // Tightly fit the content instead of expanding to cover the bounds of the selection highlight + TextIndicatorOptionTightlyFitContent = 1 << 4, + + // If there are any non-inline or replaced elements in the Range, indicate the bounding rect + // of the range instead of the individual subrects, and don't restrict painting to the given Range + TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges = 1 << 5, + + // By default, TextIndicator removes any margin if the given Range matches the + // selection Range. If this option is set, maintain the margin in any case. + TextIndicatorOptionIncludeMarginIfRangeMatchesSelection = 1 << 6, + + // By default, TextIndicator clips the indicated rects to the visible content rect. + // If this option is set, do not clip the indicated rects. + TextIndicatorOptionDoNotClipToVisibleRect = 1 << 7, +}; +typedef uint8_t TextIndicatorOptions; + +struct TextIndicatorData { + FloatRect selectionRectInRootViewCoordinates; + FloatRect textBoundingRectInRootViewCoordinates; + Vector<FloatRect> textRectsInBoundingRectCoordinates; + float contentImageScaleFactor; + RefPtr<Image> contentImageWithHighlight; + RefPtr<Image> contentImage; + TextIndicatorPresentationTransition presentationTransition; + TextIndicatorOptions options; +}; + +class TextIndicator : public RefCounted<TextIndicator> { +public: + // FIXME: These are fairly Mac-specific, and they don't really belong here. + // But they're needed at TextIndicator creation time, so they can't go in TextIndicatorWindow. + // Maybe they can live in some Theme code somewhere? + constexpr static float defaultHorizontalMargin { 2 }; + constexpr static float defaultVerticalMargin { 1 }; + + WEBCORE_EXPORT static Ref<TextIndicator> create(const TextIndicatorData&); + WEBCORE_EXPORT static RefPtr<TextIndicator> createWithSelectionInFrame(Frame&, TextIndicatorOptions, TextIndicatorPresentationTransition, FloatSize margin = FloatSize(defaultHorizontalMargin, defaultVerticalMargin)); + WEBCORE_EXPORT static RefPtr<TextIndicator> createWithRange(const Range&, TextIndicatorOptions, TextIndicatorPresentationTransition, FloatSize margin = FloatSize(defaultHorizontalMargin, defaultVerticalMargin)); + + WEBCORE_EXPORT ~TextIndicator(); + + FloatRect selectionRectInRootViewCoordinates() const { return m_data.selectionRectInRootViewCoordinates; } + FloatRect textBoundingRectInRootViewCoordinates() const { return m_data.textBoundingRectInRootViewCoordinates; } + const Vector<FloatRect>& textRectsInBoundingRectCoordinates() const { return m_data.textRectsInBoundingRectCoordinates; } + float contentImageScaleFactor() const { return m_data.contentImageScaleFactor; } + Image* contentImageWithHighlight() const { return m_data.contentImageWithHighlight.get(); } + Image* contentImage() const { return m_data.contentImage.get(); } + + TextIndicatorPresentationTransition presentationTransition() const { return m_data.presentationTransition; } + void setPresentationTransition(TextIndicatorPresentationTransition transition) { m_data.presentationTransition = transition; } + + TextIndicatorData data() const { return m_data; } + +private: + TextIndicator(const TextIndicatorData&); + + TextIndicatorData m_data; +}; + +} // namespace WebKit diff --git a/Source/WebCore/page/UserContentController.cpp b/Source/WebCore/page/UserContentController.cpp index 95fa5fdfd..bcdac0b2c 100644 --- a/Source/WebCore/page/UserContentController.cpp +++ b/Source/WebCore/page/UserContentController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2014-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 @@ -27,17 +27,21 @@ #include "UserContentController.h" #include "DOMWrapperWorld.h" -#include "Document.h" -#include "MainFrame.h" -#include "Page.h" #include "UserScript.h" #include "UserStyleSheet.h" +#include <heap/HeapInlines.h> +#include <runtime/JSCellInlines.h> +#include <runtime/StructureInlines.h> + +#if ENABLE(CONTENT_EXTENSIONS) +#include "CompiledContentExtension.h" +#endif namespace WebCore { -RefPtr<UserContentController> UserContentController::create() +Ref<UserContentController> UserContentController::create() { - return adoptRef(new UserContentController); + return adoptRef(*new UserContentController); } UserContentController::UserContentController() @@ -48,36 +52,39 @@ UserContentController::~UserContentController() { } -void UserContentController::addPage(Page& page) +void UserContentController::forEachUserScript(const std::function<void(DOMWrapperWorld&, const UserScript&)>& functor) const { - ASSERT(!m_pages.contains(&page)); - m_pages.add(&page); + for (const auto& worldAndUserScriptVector : m_userScripts) { + auto& world = *worldAndUserScriptVector.key.get(); + for (const auto& userScript : *worldAndUserScriptVector.value) + functor(world, *userScript); + } } -void UserContentController::removePage(Page& page) +void UserContentController::forEachUserStyleSheet(const std::function<void(const UserStyleSheet&)>& functor) const { - ASSERT(m_pages.contains(&page)); - m_pages.remove(&page); + for (auto& styleSheetVector : m_userStyleSheets.values()) { + for (const auto& styleSheet : *styleSheetVector) + functor(*styleSheet); + } } -void UserContentController::addUserScript(DOMWrapperWorld& world, std::unique_ptr<UserScript> userScript) +#if ENABLE(USER_MESSAGE_HANDLERS) +void UserContentController::forEachUserMessageHandler(const std::function<void(const UserMessageHandlerDescriptor&)>&) const { - if (!m_userScripts) - m_userScripts = std::make_unique<UserScriptMap>(); +} +#endif - auto& scriptsInWorld = m_userScripts->add(&world, nullptr).iterator->value; - if (!scriptsInWorld) - scriptsInWorld = std::make_unique<UserScriptVector>(); - scriptsInWorld->append(std::move(userScript)); +void UserContentController::addUserScript(DOMWrapperWorld& world, std::unique_ptr<UserScript> userScript) +{ + auto& scriptsInWorld = m_userScripts.ensure(&world, [&] { return std::make_unique<UserScriptVector>(); }).iterator->value; + scriptsInWorld->append(WTFMove(userScript)); } void UserContentController::removeUserScript(DOMWrapperWorld& world, const URL& url) { - if (!m_userScripts) - return; - - auto it = m_userScripts->find(&world); - if (it == m_userScripts->end()) + auto it = m_userScripts.find(&world); + if (it == m_userScripts.end()) return; auto scripts = it->value.get(); @@ -87,38 +94,27 @@ void UserContentController::removeUserScript(DOMWrapperWorld& world, const URL& } if (scripts->isEmpty()) - m_userScripts->remove(it); + m_userScripts.remove(it); } void UserContentController::removeUserScripts(DOMWrapperWorld& world) { - if (!m_userScripts) - return; - - m_userScripts->remove(&world); + m_userScripts.remove(&world); } void UserContentController::addUserStyleSheet(DOMWrapperWorld& world, std::unique_ptr<UserStyleSheet> userStyleSheet, UserStyleInjectionTime injectionTime) { - if (!m_userStyleSheets) - m_userStyleSheets = std::make_unique<UserStyleSheetMap>(); - - auto& styleSheetsInWorld = m_userStyleSheets->add(&world, nullptr).iterator->value; - if (!styleSheetsInWorld) - styleSheetsInWorld = std::make_unique<UserStyleSheetVector>(); - styleSheetsInWorld->append(std::move(userStyleSheet)); + auto& styleSheetsInWorld = m_userStyleSheets.ensure(&world, [&] { return std::make_unique<UserStyleSheetVector>(); }).iterator->value; + styleSheetsInWorld->append(WTFMove(userStyleSheet)); if (injectionTime == InjectInExistingDocuments) - invalidateInjectedStyleSheetCacheInAllFrames(); + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); } void UserContentController::removeUserStyleSheet(DOMWrapperWorld& world, const URL& url) { - if (!m_userStyleSheets) - return; - - auto it = m_userStyleSheets->find(&world); - if (it == m_userStyleSheets->end()) + auto it = m_userStyleSheets.find(&world); + if (it == m_userStyleSheets.end()) return; auto& stylesheets = *it->value; @@ -135,39 +131,26 @@ void UserContentController::removeUserStyleSheet(DOMWrapperWorld& world, const U return; if (stylesheets.isEmpty()) - m_userStyleSheets->remove(it); + m_userStyleSheets.remove(it); - invalidateInjectedStyleSheetCacheInAllFrames(); + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); } void UserContentController::removeUserStyleSheets(DOMWrapperWorld& world) { - if (!m_userStyleSheets) - return; - - if (!m_userStyleSheets->remove(&world)) + if (!m_userStyleSheets.remove(&world)) return; - invalidateInjectedStyleSheetCacheInAllFrames(); + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); } void UserContentController::removeAllUserContent() { - m_userScripts = nullptr; + m_userScripts.clear(); - if (m_userStyleSheets) { - m_userStyleSheets = nullptr; - invalidateInjectedStyleSheetCacheInAllFrames(); - } -} - -void UserContentController::invalidateInjectedStyleSheetCacheInAllFrames() -{ - for (auto& page : m_pages) { - for (Frame* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) { - frame->document()->styleSheetCollection().invalidateInjectedStyleSheetCache(); - frame->document()->styleResolverChanged(DeferRecalcStyle); - } + if (!m_userStyleSheets.isEmpty()) { + m_userStyleSheets.clear(); + invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); } } diff --git a/Source/WebCore/page/UserContentController.h b/Source/WebCore/page/UserContentController.h index 9ddb8d0e1..8d100dcdf 100644 --- a/Source/WebCore/page/UserContentController.h +++ b/Source/WebCore/page/UserContentController.h @@ -23,56 +23,45 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UserContentController_h -#define UserContentController_h +#pragma once -#include "UserScriptTypes.h" -#include "UserStyleSheetTypes.h" -#include <wtf/HashSet.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> +#include "UserContentProvider.h" namespace WebCore { -class DOMWrapperWorld; -class Page; -class URL; -class UserScript; -class UserStyleSheet; - -class UserContentController : public RefCounted<UserContentController> { +class UserContentController final : public UserContentProvider { public: - static RefPtr<UserContentController> create(); - ~UserContentController(); - - void addPage(Page&); - void removePage(Page&); - - const UserScriptMap* userScripts() const { return m_userScripts.get(); } - - void addUserScript(DOMWrapperWorld&, std::unique_ptr<UserScript>); - void removeUserScript(DOMWrapperWorld&, const URL&); - void removeUserScripts(DOMWrapperWorld&); + WEBCORE_EXPORT static Ref<UserContentController> create(); + WEBCORE_EXPORT ~UserContentController(); - const UserStyleSheetMap* userStyleSheets() const { return m_userStyleSheets.get(); } + WEBCORE_EXPORT void addUserScript(DOMWrapperWorld&, std::unique_ptr<UserScript>); + WEBCORE_EXPORT void removeUserScript(DOMWrapperWorld&, const URL&); + WEBCORE_EXPORT void removeUserScripts(DOMWrapperWorld&); - void addUserStyleSheet(DOMWrapperWorld&, std::unique_ptr<UserStyleSheet>, UserStyleInjectionTime); - void removeUserStyleSheet(DOMWrapperWorld&, const URL&); - void removeUserStyleSheets(DOMWrapperWorld&); + WEBCORE_EXPORT void addUserStyleSheet(DOMWrapperWorld&, std::unique_ptr<UserStyleSheet>, UserStyleInjectionTime); + WEBCORE_EXPORT void removeUserStyleSheet(DOMWrapperWorld&, const URL&); + WEBCORE_EXPORT void removeUserStyleSheets(DOMWrapperWorld&); - void removeAllUserContent(); + WEBCORE_EXPORT void removeAllUserContent(); private: UserContentController(); - void invalidateInjectedStyleSheetCacheInAllFrames(); - - HashSet<Page*> m_pages; - - std::unique_ptr<UserScriptMap> m_userScripts; - std::unique_ptr<UserStyleSheetMap> m_userStyleSheets; + // UserContentProvider + void forEachUserScript(const std::function<void(DOMWrapperWorld&, const UserScript&)>&) const override; + void forEachUserStyleSheet(const std::function<void(const UserStyleSheet&)>&) const override; +#if ENABLE(USER_MESSAGE_HANDLERS) + void forEachUserMessageHandler(const std::function<void(const UserMessageHandlerDescriptor&)>&) const override; +#endif +#if ENABLE(CONTENT_EXTENSIONS) + ContentExtensions::ContentExtensionsBackend& userContentExtensionBackend() override { return m_contentExtensionBackend; } +#endif + + UserScriptMap m_userScripts; + UserStyleSheetMap m_userStyleSheets; +#if ENABLE(CONTENT_EXTENSIONS) + ContentExtensions::ContentExtensionsBackend m_contentExtensionBackend; +#endif }; } // namespace WebCore - -#endif // UserContentController_h diff --git a/Source/WebCore/page/UserContentProvider.cpp b/Source/WebCore/page/UserContentProvider.cpp new file mode 100644 index 000000000..2759346a7 --- /dev/null +++ b/Source/WebCore/page/UserContentProvider.cpp @@ -0,0 +1,120 @@ +/* + * 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 "UserContentProvider.h" + +#include "Document.h" +#include "DocumentLoader.h" +#include "MainFrame.h" +#include "Page.h" + +#if ENABLE(CONTENT_EXTENSIONS) +#include "ContentExtensionCompiler.h" +#include "ContentExtensionsBackend.h" +#endif + +namespace WebCore { + +UserContentProvider::UserContentProvider() +{ +} + +UserContentProvider::~UserContentProvider() +{ + ASSERT(m_pages.isEmpty()); +} + +void UserContentProvider::addPage(Page& page) +{ + ASSERT(!m_pages.contains(&page)); + + m_pages.add(&page); +} + +void UserContentProvider::removePage(Page& page) +{ + ASSERT(m_pages.contains(&page)); + + m_pages.remove(&page); +} + +void UserContentProvider::registerForUserMessageHandlerInvalidation(UserContentProviderInvalidationClient& invalidationClient) +{ + ASSERT(!m_userMessageHandlerInvalidationClients.contains(&invalidationClient)); + + m_userMessageHandlerInvalidationClients.add(&invalidationClient); +} + +void UserContentProvider::unregisterForUserMessageHandlerInvalidation(UserContentProviderInvalidationClient& invalidationClient) +{ + ASSERT(m_userMessageHandlerInvalidationClients.contains(&invalidationClient)); + + m_userMessageHandlerInvalidationClients.remove(&invalidationClient); +} + +void UserContentProvider::invalidateAllRegisteredUserMessageHandlerInvalidationClients() +{ + for (auto& client : m_userMessageHandlerInvalidationClients) + client->didInvalidate(*this); +} + +void UserContentProvider::invalidateInjectedStyleSheetCacheInAllFramesInAllPages() +{ + for (auto& page : m_pages) + page->invalidateInjectedStyleSheetCacheInAllFrames(); +} + +#if ENABLE(CONTENT_EXTENSIONS) +static bool contentExtensionsEnabled(const DocumentLoader& documentLoader) +{ + if (auto frame = documentLoader.frame()) { + if (frame->isMainFrame()) + return documentLoader.userContentExtensionsEnabled(); + if (auto mainDocumentLoader = frame->mainFrame().loader().documentLoader()) + return mainDocumentLoader->userContentExtensionsEnabled(); + } + + return true; +} + +ContentExtensions::BlockedStatus UserContentProvider::processContentExtensionRulesForLoad(const URL& url, ResourceType resourceType, DocumentLoader& initiatingDocumentLoader) +{ + if (!contentExtensionsEnabled(initiatingDocumentLoader)) + return { }; + + return userContentExtensionBackend().processContentExtensionRulesForLoad(url, resourceType, initiatingDocumentLoader); +} + +Vector<ContentExtensions::Action> UserContentProvider::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo, DocumentLoader& initiatingDocumentLoader) +{ + if (!contentExtensionsEnabled(initiatingDocumentLoader)) + return { }; + + return userContentExtensionBackend().actionsForResourceLoad(resourceLoadInfo); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/page/UserContentProvider.h b/Source/WebCore/page/UserContentProvider.h new file mode 100644 index 000000000..e158c8b4d --- /dev/null +++ b/Source/WebCore/page/UserContentProvider.h @@ -0,0 +1,107 @@ +/* + * 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 "UserScriptTypes.h" +#include "UserStyleSheetTypes.h" +#include <functional> +#include <wtf/Forward.h> +#include <wtf/HashSet.h> +#include <wtf/RefCounted.h> + +#if ENABLE(USER_MESSAGE_HANDLERS) +#include "UserMessageHandlerDescriptorTypes.h" +#endif + +#if ENABLE(CONTENT_EXTENSIONS) +#include "ContentExtensionActions.h" +#include "ContentExtensionsBackend.h" +#endif + +namespace WebCore { + +class DocumentLoader; +class Page; +class ResourceRequest; +class URL; + +enum class ResourceType : uint16_t; + +struct ResourceLoadInfo; + +namespace ContentExtensions { +class ContentExtensionsBackend; +struct Action; +} + +class UserContentProvider; + +class UserContentProviderInvalidationClient { +public: + virtual ~UserContentProviderInvalidationClient() + { + } + + virtual void didInvalidate(UserContentProvider&) = 0; +}; + +class UserContentProvider : public RefCounted<UserContentProvider> { +public: + WEBCORE_EXPORT UserContentProvider(); + WEBCORE_EXPORT virtual ~UserContentProvider(); + + virtual void forEachUserScript(const std::function<void(DOMWrapperWorld&, const UserScript&)>&) const = 0; + virtual void forEachUserStyleSheet(const std::function<void(const UserStyleSheet&)>&) const = 0; +#if ENABLE(USER_MESSAGE_HANDLERS) + virtual void forEachUserMessageHandler(const std::function<void(const UserMessageHandlerDescriptor&)>&) const = 0; +#endif +#if ENABLE(CONTENT_EXTENSIONS) + virtual ContentExtensions::ContentExtensionsBackend& userContentExtensionBackend() = 0; +#endif + + void registerForUserMessageHandlerInvalidation(UserContentProviderInvalidationClient&); + void unregisterForUserMessageHandlerInvalidation(UserContentProviderInvalidationClient&); + + void addPage(Page&); + void removePage(Page&); + +#if ENABLE(CONTENT_EXTENSIONS) + // FIXME: These don't really belong here. They should probably bundled up in the ContentExtensionsBackend + // which should always exist. + ContentExtensions::BlockedStatus processContentExtensionRulesForLoad(const URL&, ResourceType, DocumentLoader& initiatingDocumentLoader); + Vector<ContentExtensions::Action> actionsForResourceLoad(const ResourceLoadInfo&, DocumentLoader& initiatingDocumentLoader); +#endif + +protected: + WEBCORE_EXPORT void invalidateAllRegisteredUserMessageHandlerInvalidationClients(); + WEBCORE_EXPORT void invalidateInjectedStyleSheetCacheInAllFramesInAllPages(); + +private: + HashSet<Page*> m_pages; + HashSet<UserContentProviderInvalidationClient*> m_userMessageHandlerInvalidationClients; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/UserContentTypes.h b/Source/WebCore/page/UserContentTypes.h index d012b8cbd..754ce53bc 100644 --- a/Source/WebCore/page/UserContentTypes.h +++ b/Source/WebCore/page/UserContentTypes.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,16 +23,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef UserContentTypes_h -#define UserContentTypes_h - -#include <wtf/HashMap.h> -#include <wtf/Vector.h> +#pragma once namespace WebCore { enum UserContentInjectedFrames { InjectInAllFrames, InjectInTopFrameOnly }; } // namespace WebCore - -#endif // UserContentTypes_h diff --git a/Source/WebCore/page/UserContentURLPattern.cpp b/Source/WebCore/page/UserContentURLPattern.cpp index fdaed1e50..d431ee74d 100644 --- a/Source/WebCore/page/UserContentURLPattern.cpp +++ b/Source/WebCore/page/UserContentURLPattern.cpp @@ -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,6 +27,7 @@ #include "UserContentURLPattern.h" #include "URL.h" +#include <wtf/NeverDestroyed.h> #include <wtf/StdLibExtras.h> namespace WebCore { @@ -37,9 +38,8 @@ bool UserContentURLPattern::matchesPatterns(const URL& url, const Vector<String> // If there is no whitelist at all, then all URLs are assumed to be in the whitelist. bool matchesWhitelist = whitelist.isEmpty(); if (!matchesWhitelist) { - size_t whitelistSize = whitelist.size(); - for (size_t i = 0; i < whitelistSize; ++i) { - UserContentURLPattern contentPattern(whitelist[i]); + for (auto& entry : whitelist) { + UserContentURLPattern contentPattern(entry); if (contentPattern.matches(url)) { matchesWhitelist = true; break; @@ -49,9 +49,8 @@ bool UserContentURLPattern::matchesPatterns(const URL& url, const Vector<String> bool matchesBlacklist = false; if (!blacklist.isEmpty()) { - size_t blacklistSize = blacklist.size(); - for (size_t i = 0; i < blacklistSize; ++i) { - UserContentURLPattern contentPattern(blacklist[i]); + for (auto& entry : blacklist) { + UserContentURLPattern contentPattern(entry); if (contentPattern.matches(url)) { matchesBlacklist = true; break; @@ -64,7 +63,7 @@ bool UserContentURLPattern::matchesPatterns(const URL& url, const Vector<String> bool UserContentURLPattern::parse(const String& pattern) { - DEFINE_STATIC_LOCAL(const String, schemeSeparator, (ASCIILiteral("://"))); + static NeverDestroyed<const String> schemeSeparator(ASCIILiteral("://")); size_t schemeEndPos = pattern.find(schemeSeparator); if (schemeEndPos == notFound) @@ -72,16 +71,16 @@ bool UserContentURLPattern::parse(const String& pattern) m_scheme = pattern.left(schemeEndPos); - unsigned hostStartPos = schemeEndPos + schemeSeparator.length(); + unsigned hostStartPos = schemeEndPos + schemeSeparator.get().length(); if (hostStartPos >= pattern.length()) return false; int pathStartPos = 0; - if (equalIgnoringCase(m_scheme, "file")) + if (equalLettersIgnoringASCIICase(m_scheme, "file")) pathStartPos = hostStartPos; else { - size_t hostEndPos = pattern.find("/", hostStartPos); + size_t hostEndPos = pattern.find('/', hostStartPos); if (hostEndPos == notFound) return false; @@ -90,7 +89,7 @@ bool UserContentURLPattern::parse(const String& pattern) if (m_host == "*") { // The pattern can be just '*', which means match all domains. - m_host = ""; + m_host = emptyString(); m_matchSubdomains = true; } else if (m_host.startsWith("*.")) { // The first component can be '*', which means to match all subdomains. @@ -99,7 +98,7 @@ bool UserContentURLPattern::parse(const String& pattern) } // No other '*' can occur in the host. - if (m_host.find("*") != notFound) + if (m_host.find('*') != notFound) return false; pathStartPos = hostEndPos; @@ -115,10 +114,10 @@ bool UserContentURLPattern::matches(const URL& test) const if (m_invalid) return false; - if (!equalIgnoringCase(test.protocol(), m_scheme)) + if (!equalIgnoringASCIICase(test.protocol(), m_scheme)) return false; - if (!equalIgnoringCase(m_scheme, "file") && !matchesHost(test)) + if (!equalLettersIgnoringASCIICase(m_scheme, "file") && !matchesHost(test)) return false; return matchesPath(test); @@ -127,7 +126,7 @@ bool UserContentURLPattern::matches(const URL& test) const bool UserContentURLPattern::matchesHost(const URL& test) const { const String& host = test.host(); - if (equalIgnoringCase(host, m_host)) + if (equalIgnoringASCIICase(host, m_host)) return true; if (!m_matchSubdomains) diff --git a/Source/WebCore/page/UserContentURLPattern.h b/Source/WebCore/page/UserContentURLPattern.h index a86892bce..e9ffe1c00 100644 --- a/Source/WebCore/page/UserContentURLPattern.h +++ b/Source/WebCore/page/UserContentURLPattern.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 UserContentURLPattern_h -#define UserContentURLPattern_h +#pragma once #include <wtf/Vector.h> #include <wtf/text/WTFString.h> @@ -45,7 +44,7 @@ public: bool isValid() const { return !m_invalid; } - bool matches(const URL&) const; + WEBCORE_EXPORT bool matches(const URL&) const; const String& scheme() const { return m_scheme; } const String& host() const { return m_host; } @@ -56,7 +55,7 @@ public: static bool matchesPatterns(const URL&, const Vector<String>& whitelist, const Vector<String>& blacklist); private: - bool parse(const String& pattern); + WEBCORE_EXPORT bool parse(const String& pattern); bool matchesHost(const URL&) const; bool matchesPath(const URL&) const; @@ -70,7 +69,4 @@ private: bool m_matchSubdomains; }; - } // namespace WebCore - -#endif // UserContentURLPattern_h diff --git a/Source/WebCore/page/UserMessageHandler.cpp b/Source/WebCore/page/UserMessageHandler.cpp new file mode 100644 index 000000000..0ff70e266 --- /dev/null +++ b/Source/WebCore/page/UserMessageHandler.cpp @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#include "config.h" +#include "UserMessageHandler.h" + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "ExceptionCode.h" +#include "Frame.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +UserMessageHandler::UserMessageHandler(Frame& frame, UserMessageHandlerDescriptor& descriptor) + : FrameDestructionObserver(&frame) + , m_descriptor(&descriptor) +{ +} + +UserMessageHandler::~UserMessageHandler() +{ +} + +ExceptionOr<void> UserMessageHandler::postMessage(RefPtr<SerializedScriptValue>&& value) +{ + // Check to see if the descriptor has been removed. This can happen if the host application has + // removed the named message handler at the WebKit2 API level. + if (!m_descriptor) + return Exception { INVALID_ACCESS_ERR }; + + m_descriptor->didPostMessage(*this, value.get()); + return { }; +} + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandler.h b/Source/WebCore/page/UserMessageHandler.h new file mode 100644 index 000000000..94affb50c --- /dev/null +++ b/Source/WebCore/page/UserMessageHandler.h @@ -0,0 +1,57 @@ +/* + * 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 + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "ExceptionOr.h" +#include "FrameDestructionObserver.h" +#include "UserMessageHandlerDescriptor.h" + +namespace WebCore { + +class UserMessageHandler : public RefCounted<UserMessageHandler>, public FrameDestructionObserver { +public: + static Ref<UserMessageHandler> create(Frame& frame, UserMessageHandlerDescriptor& descriptor) + { + return adoptRef(*new UserMessageHandler(frame, descriptor)); + } + virtual ~UserMessageHandler(); + + ExceptionOr<void> postMessage(RefPtr<SerializedScriptValue>&&); + + UserMessageHandlerDescriptor* descriptor() { return m_descriptor.get(); } + void invalidateDescriptor() { m_descriptor = nullptr; } + +private: + UserMessageHandler(Frame&, UserMessageHandlerDescriptor&); + + RefPtr<UserMessageHandlerDescriptor> m_descriptor; +}; + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandler.idl b/Source/WebCore/page/UserMessageHandler.idl new file mode 100644 index 000000000..776c1824b --- /dev/null +++ b/Source/WebCore/page/UserMessageHandler.idl @@ -0,0 +1,30 @@ +/* + * 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. + */ + +[ + Conditional=USER_MESSAGE_HANDLERS +] interface UserMessageHandler { + [MayThrowException] void postMessage(SerializedScriptValue message); +}; diff --git a/Source/WebCore/page/UserMessageHandlerDescriptor.cpp b/Source/WebCore/page/UserMessageHandlerDescriptor.cpp new file mode 100644 index 000000000..723782c4b --- /dev/null +++ b/Source/WebCore/page/UserMessageHandlerDescriptor.cpp @@ -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. 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 "UserMessageHandlerDescriptor.h" + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "DOMWrapperWorld.h" + +namespace WebCore { + +UserMessageHandlerDescriptor::UserMessageHandlerDescriptor(const AtomicString& name, DOMWrapperWorld& world) + : m_name(name) + , m_world(world) +{ +} + +UserMessageHandlerDescriptor::~UserMessageHandlerDescriptor() +{ +} + +const AtomicString& UserMessageHandlerDescriptor::name() const +{ + return m_name; +} + +DOMWrapperWorld& UserMessageHandlerDescriptor::world() +{ + return m_world.get(); +} + +const DOMWrapperWorld& UserMessageHandlerDescriptor::world() const +{ + return m_world.get(); +} + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandlerDescriptor.h b/Source/WebCore/page/UserMessageHandlerDescriptor.h new file mode 100644 index 000000000..d297a8294 --- /dev/null +++ b/Source/WebCore/page/UserMessageHandlerDescriptor.h @@ -0,0 +1,58 @@ +/* + * 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 + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class DOMWrapperWorld; +class SerializedScriptValue; +class UserMessageHandler; + +class UserMessageHandlerDescriptor : public RefCounted<UserMessageHandlerDescriptor> { +public: + WEBCORE_EXPORT explicit UserMessageHandlerDescriptor(const AtomicString&, DOMWrapperWorld&); + WEBCORE_EXPORT virtual ~UserMessageHandlerDescriptor(); + + WEBCORE_EXPORT const AtomicString& name() const; + WEBCORE_EXPORT DOMWrapperWorld& world(); + WEBCORE_EXPORT const DOMWrapperWorld& world() const; + + virtual void didPostMessage(UserMessageHandler&, SerializedScriptValue*) = 0; + +private: + AtomicString m_name; + Ref<DOMWrapperWorld> m_world; +}; + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandlerDescriptorTypes.h b/Source/WebCore/page/UserMessageHandlerDescriptorTypes.h new file mode 100644 index 000000000..681605483 --- /dev/null +++ b/Source/WebCore/page/UserMessageHandlerDescriptorTypes.h @@ -0,0 +1,44 @@ +/* + * 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 + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include <wtf/HashMap.h> +#include <wtf/RefPtr.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + +class DOMWrapperWorld; +class UserMessageHandlerDescriptor; + +typedef HashMap<std::pair<AtomicString, RefPtr<DOMWrapperWorld>>, RefPtr<UserMessageHandlerDescriptor>> UserMessageHandlerDescriptorMap; + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandlersNamespace.cpp b/Source/WebCore/page/UserMessageHandlersNamespace.cpp new file mode 100644 index 000000000..7cef10c8d --- /dev/null +++ b/Source/WebCore/page/UserMessageHandlersNamespace.cpp @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#include "config.h" +#include "UserMessageHandlersNamespace.h" + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "DOMWrapperWorld.h" +#include "Frame.h" +#include "Page.h" +#include "UserContentController.h" +#include "UserMessageHandler.h" + +namespace WebCore { + +UserMessageHandlersNamespace::UserMessageHandlersNamespace(Frame& frame, UserContentProvider& userContentProvider) + : FrameDestructionObserver(&frame) + , m_userContentProvider(userContentProvider) +{ + m_userContentProvider->registerForUserMessageHandlerInvalidation(*this); +} + +UserMessageHandlersNamespace::~UserMessageHandlersNamespace() +{ + m_userContentProvider->unregisterForUserMessageHandlerInvalidation(*this); +} + +void UserMessageHandlersNamespace::didInvalidate(UserContentProvider& provider) +{ + auto oldMap = WTFMove(m_messageHandlers); + + provider.forEachUserMessageHandler([&](const UserMessageHandlerDescriptor& descriptor) { + auto userMessageHandler = oldMap.take(std::make_pair(descriptor.name(), const_cast<DOMWrapperWorld*>(&descriptor.world()))); + if (userMessageHandler) { + m_messageHandlers.add(std::make_pair(descriptor.name(), const_cast<DOMWrapperWorld*>(&descriptor.world())), userMessageHandler); + return; + } + }); + + for (auto& userMessageHandler : oldMap.values()) + userMessageHandler->invalidateDescriptor(); +} + +UserMessageHandler* UserMessageHandlersNamespace::handler(const AtomicString& name, DOMWrapperWorld& world) +{ + Frame* frame = this->frame(); + if (!frame) + return nullptr; + + Page* page = frame->page(); + if (!page) + return nullptr; + + UserMessageHandler* handler = m_messageHandlers.get(std::pair<AtomicString, RefPtr<DOMWrapperWorld>>(name, &world)); + if (handler) + return handler; + + page->userContentProvider().forEachUserMessageHandler([&](const UserMessageHandlerDescriptor& descriptor) { + if (descriptor.name() != name || &descriptor.world() != &world) + return; + + ASSERT(!handler); + + auto addResult = m_messageHandlers.add(std::make_pair(descriptor.name(), const_cast<DOMWrapperWorld*>(&descriptor.world())), UserMessageHandler::create(*frame, const_cast<UserMessageHandlerDescriptor&>(descriptor))); + handler = addResult.iterator->value.get(); + }); + + return handler; +} + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandlersNamespace.h b/Source/WebCore/page/UserMessageHandlersNamespace.h new file mode 100644 index 000000000..0c8ed0886 --- /dev/null +++ b/Source/WebCore/page/UserMessageHandlersNamespace.h @@ -0,0 +1,68 @@ +/* + * 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 + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "FrameDestructionObserver.h" +#include "UserContentProvider.h" +#include "UserMessageHandler.h" +#include <wtf/HashMap.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + +class Frame; +class UserMessageHandler; +class DOMWrapperWorld; + +class UserMessageHandlersNamespace : public RefCounted<UserMessageHandlersNamespace>, public FrameDestructionObserver, public UserContentProviderInvalidationClient { +public: + static Ref<UserMessageHandlersNamespace> create(Frame& frame, UserContentProvider& userContentProvider) + { + return adoptRef(*new UserMessageHandlersNamespace(frame, userContentProvider)); + } + + virtual ~UserMessageHandlersNamespace(); + + UserMessageHandler* handler(const AtomicString&, DOMWrapperWorld&); + +private: + explicit UserMessageHandlersNamespace(Frame&, UserContentProvider&); + + // UserContentProviderInvalidationClient + void didInvalidate(UserContentProvider&) override; + + Ref<UserContentProvider> m_userContentProvider; + HashMap<std::pair<AtomicString, RefPtr<DOMWrapperWorld>>, RefPtr<UserMessageHandler>> m_messageHandlers; +}; + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/UserMessageHandlersNamespace.idl b/Source/WebCore/page/UserMessageHandlersNamespace.idl new file mode 100644 index 000000000..7d2050fdf --- /dev/null +++ b/Source/WebCore/page/UserMessageHandlersNamespace.idl @@ -0,0 +1,30 @@ +/* + * 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. + */ + +[ + JSCustomGetOwnPropertySlotAndDescriptor, + Conditional=USER_MESSAGE_HANDLERS +] interface UserMessageHandlersNamespace { +}; diff --git a/Source/WebCore/page/UserScript.h b/Source/WebCore/page/UserScript.h index 8a2b41187..714cad126 100644 --- a/Source/WebCore/page/UserScript.h +++ b/Source/WebCore/page/UserScript.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 UserScript_h -#define UserScript_h +#pragma once #include "URL.h" #include "UserContentTypes.h" @@ -42,11 +41,11 @@ public: { } - UserScript(const String& source, const URL& url, const Vector<String>& whitelist, const Vector<String>& blacklist, UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames) + UserScript(const String& source, const URL& url, Vector<String>&& whitelist, Vector<String>&& blacklist, UserScriptInjectionTime injectionTime, UserContentInjectedFrames injectedFrames) : m_source(source) , m_url(url) - , m_whitelist(whitelist) - , m_blacklist(blacklist) + , m_whitelist(WTFMove(whitelist)) + , m_blacklist(WTFMove(blacklist)) , m_injectionTime(injectionTime) , m_injectedFrames(injectedFrames) { @@ -69,5 +68,3 @@ private: }; } // namespace WebCore - -#endif // UserScript_h diff --git a/Source/WebCore/page/UserScriptTypes.h b/Source/WebCore/page/UserScriptTypes.h index f85977e54..da16ac253 100644 --- a/Source/WebCore/page/UserScriptTypes.h +++ b/Source/WebCore/page/UserScriptTypes.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 UserScriptTypes_h -#define UserScriptTypes_h +#pragma once #include <wtf/HashMap.h> #include <wtf/Vector.h> @@ -40,5 +39,3 @@ typedef Vector<std::unique_ptr<UserScript>> UserScriptVector; typedef HashMap<RefPtr<DOMWrapperWorld>, std::unique_ptr<UserScriptVector>> UserScriptMap; } // namespace WebCore - -#endif // UserScriptTypes_h diff --git a/Source/WebCore/page/UserStyleSheet.h b/Source/WebCore/page/UserStyleSheet.h index 7234462cb..68f10a82a 100644 --- a/Source/WebCore/page/UserStyleSheet.h +++ b/Source/WebCore/page/UserStyleSheet.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 UserStyleSheet_h -#define UserStyleSheet_h +#pragma once #include "URL.h" #include "UserContentTypes.h" @@ -42,11 +41,11 @@ public: { } - UserStyleSheet(const String& source, const URL& url, const Vector<String>& whitelist, const Vector<String>& blacklist, UserContentInjectedFrames injectedFrames, UserStyleLevel level) + UserStyleSheet(const String& source, const URL& url, Vector<String>&& whitelist, Vector<String>&& blacklist, UserContentInjectedFrames injectedFrames, UserStyleLevel level) : m_source(source) , m_url(url) - , m_whitelist(whitelist) - , m_blacklist(blacklist) + , m_whitelist(WTFMove(whitelist)) + , m_blacklist(WTFMove(blacklist)) , m_injectedFrames(injectedFrames) , m_level(level) { @@ -69,5 +68,3 @@ private: }; } // namespace WebCore - -#endif // UserStyleSheet_h diff --git a/Source/WebCore/page/UserStyleSheetTypes.h b/Source/WebCore/page/UserStyleSheetTypes.h index 207286464..e10d17f93 100644 --- a/Source/WebCore/page/UserStyleSheetTypes.h +++ b/Source/WebCore/page/UserStyleSheetTypes.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 UserStyleSheetTypes_h -#define UserStyleSheetTypes_h +#pragma once #include <wtf/HashMap.h> #include <wtf/Vector.h> @@ -41,5 +40,3 @@ typedef Vector<std::unique_ptr<UserStyleSheet>> UserStyleSheetVector; typedef HashMap<RefPtr<DOMWrapperWorld>, std::unique_ptr<UserStyleSheetVector>> UserStyleSheetMap; } // namespace WebCore - -#endif // UserStyleSheetTypes_h diff --git a/Source/WebCore/page/ValidationMessageClient.h b/Source/WebCore/page/ValidationMessageClient.h index 3beaca09f..630cde3fd 100644 --- a/Source/WebCore/page/ValidationMessageClient.h +++ b/Source/WebCore/page/ValidationMessageClient.h @@ -23,13 +23,13 @@ * SUCH DAMAGE. */ -#ifndef ValidationMessageClient_h -#define ValidationMessageClient_h +#pragma once #include <wtf/Forward.h> namespace WebCore { +class Document; class Element; class ValidationMessageClient { @@ -45,11 +45,16 @@ public: // anchor is already visible. virtual void hideValidationMessage(const Element& anchor) = 0; + // Hide any validation message currently displayed. + virtual void hideAnyValidationMessage() = 0; + // Returns true if the validation message for the specified anchor element // is visible. virtual bool isValidationMessageVisible(const Element& anchor) = 0; -}; -} + virtual void updateValidationBubbleStateIfNeeded() = 0; + + virtual void documentDetached(Document&) = 0; +}; -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/ViewportConfiguration.cpp b/Source/WebCore/page/ViewportConfiguration.cpp new file mode 100644 index 000000000..805c6e0cc --- /dev/null +++ b/Source/WebCore/page/ViewportConfiguration.cpp @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2005-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. + */ + +#include "config.h" +#include "ViewportConfiguration.h" + +#include "TextStream.h" +#include <wtf/Assertions.h> +#include <wtf/MathExtras.h> +#include <wtf/text/CString.h> + +#if PLATFORM(IOS) +#include "PlatformScreen.h" +#endif + +namespace WebCore { + +#if !ASSERT_DISABLED +static bool constraintsAreAllRelative(const ViewportConfiguration::Parameters& configuration) +{ + return !configuration.widthIsSet && !configuration.heightIsSet && !configuration.initialScaleIsSet; +} +#endif + +ViewportConfiguration::ViewportConfiguration() + : m_minimumLayoutSize(1024, 768) + , m_canIgnoreScalingConstraints(false) + , m_forceAlwaysUserScalable(false) +{ + // Setup a reasonable default configuration to avoid computing infinite scale/sizes. + // Those are the original iPhone configuration. + m_defaultConfiguration = ViewportConfiguration::webpageParameters(); + updateConfiguration(); +} + +void ViewportConfiguration::setDefaultConfiguration(const ViewportConfiguration::Parameters& defaultConfiguration) +{ + ASSERT(!constraintsAreAllRelative(m_configuration)); + ASSERT(!defaultConfiguration.initialScaleIsSet || defaultConfiguration.initialScale > 0); + ASSERT(defaultConfiguration.minimumScale > 0); + ASSERT(defaultConfiguration.maximumScale >= defaultConfiguration.minimumScale); + + m_defaultConfiguration = defaultConfiguration; + updateConfiguration(); +} + +bool ViewportConfiguration::setContentsSize(const IntSize& contentSize) +{ + if (m_contentSize == contentSize) + return false; + + m_contentSize = contentSize; + updateConfiguration(); + return true; +} + +bool ViewportConfiguration::setMinimumLayoutSize(const FloatSize& minimumLayoutSize) +{ + if (m_minimumLayoutSize == minimumLayoutSize) + return false; + + m_minimumLayoutSize = minimumLayoutSize; + updateConfiguration(); + return true; +} + +bool ViewportConfiguration::setViewportArguments(const ViewportArguments& viewportArguments) +{ + if (m_viewportArguments == viewportArguments) + return false; + + m_viewportArguments = viewportArguments; + updateConfiguration(); + return true; +} + +bool ViewportConfiguration::setCanIgnoreScalingConstraints(bool canIgnoreScalingConstraints) +{ + if (canIgnoreScalingConstraints == m_canIgnoreScalingConstraints) + return false; + + m_canIgnoreScalingConstraints = canIgnoreScalingConstraints; + updateConfiguration(); + return true; +} + +IntSize ViewportConfiguration::layoutSize() const +{ + return IntSize(layoutWidth(), layoutHeight()); +} + +bool ViewportConfiguration::shouldIgnoreHorizontalScalingConstraints() const +{ + if (!m_canIgnoreScalingConstraints) + return false; + + if (!m_configuration.allowsShrinkToFit) + return false; + + bool laidOutWiderThanViewport = m_contentSize.width() > layoutWidth(); + if (m_viewportArguments.width == ViewportArguments::ValueDeviceWidth) + return laidOutWiderThanViewport; + + if (m_configuration.initialScaleIsSet && m_configuration.initialScale == 1) + return laidOutWiderThanViewport; + + return false; +} + +bool ViewportConfiguration::shouldIgnoreVerticalScalingConstraints() const +{ + if (!m_canIgnoreScalingConstraints) + return false; + + if (!m_configuration.allowsShrinkToFit) + return false; + + bool laidOutTallerThanViewport = m_contentSize.height() > layoutHeight(); + if (m_viewportArguments.height == ViewportArguments::ValueDeviceHeight && m_viewportArguments.width == ViewportArguments::ValueAuto) + return laidOutTallerThanViewport; + + return false; +} + +bool ViewportConfiguration::shouldIgnoreScalingConstraints() const +{ + return shouldIgnoreHorizontalScalingConstraints() || shouldIgnoreVerticalScalingConstraints(); +} + +double ViewportConfiguration::initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const +{ + ASSERT(!constraintsAreAllRelative(m_configuration)); + + // If the document has specified its own initial scale, use it regardless. + // This is guaranteed to be sanity checked already, so no need for MIN/MAX. + if (m_configuration.initialScaleIsSet && !shouldIgnoreScalingConstraints) + return m_configuration.initialScale; + + // If not, it is up to us to determine the initial scale. + // We want a scale small enough to fit the document width-wise. + const FloatSize& minimumLayoutSize = m_minimumLayoutSize; + double initialScale = 0; + if (width > 0 && !shouldIgnoreVerticalScalingConstraints()) + initialScale = minimumLayoutSize.width() / width; + + // Prevent the initial scale from shrinking to a height smaller than our view's minimum height. + if (height > 0 && height * initialScale < minimumLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints()) + initialScale = minimumLayoutSize.height() / height; + return std::min(std::max(initialScale, shouldIgnoreScalingConstraints ? m_defaultConfiguration.minimumScale : m_configuration.minimumScale), m_configuration.maximumScale); +} + +double ViewportConfiguration::initialScale() const +{ + return initialScaleFromSize(m_contentSize.width() > 0 ? m_contentSize.width() : layoutWidth(), m_contentSize.height() > 0 ? m_contentSize.height() : layoutHeight(), shouldIgnoreScalingConstraints()); +} + +double ViewportConfiguration::initialScaleIgnoringContentSize() const +{ + return initialScaleFromSize(layoutWidth(), layoutHeight(), false); +} + +double ViewportConfiguration::minimumScale() const +{ + // If we scale to fit, then this is our minimum scale as well. + if (!m_configuration.initialScaleIsSet || shouldIgnoreScalingConstraints()) + return initialScale(); + + // If not, we still need to sanity check our value. + double minimumScale = m_configuration.minimumScale; + + if (m_forceAlwaysUserScalable) + minimumScale = std::min(minimumScale, forceAlwaysUserScalableMinimumScale); + + const FloatSize& minimumLayoutSize = m_minimumLayoutSize; + double contentWidth = m_contentSize.width(); + if (contentWidth > 0 && contentWidth * minimumScale < minimumLayoutSize.width() && !shouldIgnoreVerticalScalingConstraints()) + minimumScale = minimumLayoutSize.width() / contentWidth; + + double contentHeight = m_contentSize.height(); + if (contentHeight > 0 && contentHeight * minimumScale < minimumLayoutSize.height() && !shouldIgnoreHorizontalScalingConstraints()) + minimumScale = minimumLayoutSize.height() / contentHeight; + + minimumScale = std::min(std::max(minimumScale, m_configuration.minimumScale), m_configuration.maximumScale); + + return minimumScale; +} + +bool ViewportConfiguration::allowsUserScaling() const +{ + return m_forceAlwaysUserScalable || allowsUserScalingIgnoringAlwaysScalable(); +} + +bool ViewportConfiguration::allowsUserScalingIgnoringAlwaysScalable() const +{ + return shouldIgnoreScalingConstraints() || m_configuration.allowsUserScaling; +} + +ViewportConfiguration::Parameters ViewportConfiguration::webpageParameters() +{ + Parameters parameters; + parameters.width = 980; + parameters.widthIsSet = true; + parameters.allowsUserScaling = true; + parameters.allowsShrinkToFit = true; + parameters.minimumScale = 0.25; + parameters.maximumScale = 5; + return parameters; +} + +ViewportConfiguration::Parameters ViewportConfiguration::textDocumentParameters() +{ + Parameters parameters; + +#if PLATFORM(IOS) + parameters.width = static_cast<int>(screenSize().width()); +#else + // FIXME: this needs to be unified with ViewportArguments on all ports. + parameters.width = 320; +#endif + + parameters.widthIsSet = true; + parameters.allowsUserScaling = true; + parameters.allowsShrinkToFit = false; + parameters.minimumScale = 0.25; + parameters.maximumScale = 5; + return parameters; +} + +ViewportConfiguration::Parameters ViewportConfiguration::imageDocumentParameters() +{ + Parameters parameters; + parameters.width = 980; + parameters.widthIsSet = true; + parameters.allowsUserScaling = true; + parameters.allowsShrinkToFit = false; + parameters.minimumScale = 0.01; + parameters.maximumScale = 5; + return parameters; +} + +ViewportConfiguration::Parameters ViewportConfiguration::xhtmlMobileParameters() +{ + Parameters parameters = webpageParameters(); + parameters.width = 320; + return parameters; +} + +ViewportConfiguration::Parameters ViewportConfiguration::testingParameters() +{ + Parameters parameters; + parameters.initialScale = 1; + parameters.initialScaleIsSet = true; + parameters.allowsShrinkToFit = true; + parameters.minimumScale = 1; + parameters.maximumScale = 5; + return parameters; +} + +static inline bool viewportArgumentValueIsValid(float value) +{ + return value > 0; +} + +template<typename ValueType, typename ViewportArgumentsType> +static inline void applyViewportArgument(ValueType& value, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum) +{ + if (viewportArgumentValueIsValid(viewportArgumentValue)) + value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue))); +} + +template<typename ValueType, typename ViewportArgumentsType> +static inline void applyViewportArgument(ValueType& value, bool& valueIsSet, ViewportArgumentsType viewportArgumentValue, ValueType minimum, ValueType maximum) +{ + if (viewportArgumentValueIsValid(viewportArgumentValue)) { + value = std::min(maximum, std::max(minimum, static_cast<ValueType>(viewportArgumentValue))); + valueIsSet = true; + } else + valueIsSet = false; +} + +static inline bool booleanViewportArgumentIsSet(float value) +{ + return !value || value == 1; +} + +void ViewportConfiguration::updateConfiguration() +{ + m_configuration = m_defaultConfiguration; + + const double minimumViewportArgumentsScaleFactor = 0.1; + const double maximumViewportArgumentsScaleFactor = 10.0; + + bool viewportArgumentsOverridesInitialScale; + bool viewportArgumentsOverridesWidth; + bool viewportArgumentsOverridesHeight; + + applyViewportArgument(m_configuration.minimumScale, m_viewportArguments.minZoom, minimumViewportArgumentsScaleFactor, maximumViewportArgumentsScaleFactor); + applyViewportArgument(m_configuration.maximumScale, m_viewportArguments.maxZoom, m_configuration.minimumScale, maximumViewportArgumentsScaleFactor); + applyViewportArgument(m_configuration.initialScale, viewportArgumentsOverridesInitialScale, m_viewportArguments.zoom, m_configuration.minimumScale, m_configuration.maximumScale); + + double minimumViewportArgumentsDimension = 10; + double maximumViewportArgumentsDimension = 10000; + applyViewportArgument(m_configuration.width, viewportArgumentsOverridesWidth, viewportArgumentsLength(m_viewportArguments.width), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension); + applyViewportArgument(m_configuration.height, viewportArgumentsOverridesHeight, viewportArgumentsLength(m_viewportArguments.height), minimumViewportArgumentsDimension, maximumViewportArgumentsDimension); + + if (viewportArgumentsOverridesInitialScale || viewportArgumentsOverridesWidth || viewportArgumentsOverridesHeight) { + m_configuration.initialScaleIsSet = viewportArgumentsOverridesInitialScale; + m_configuration.widthIsSet = viewportArgumentsOverridesWidth; + m_configuration.heightIsSet = viewportArgumentsOverridesHeight; + } + + if (booleanViewportArgumentIsSet(m_viewportArguments.userZoom)) + m_configuration.allowsUserScaling = m_viewportArguments.userZoom != 0.; + + if (booleanViewportArgumentIsSet(m_viewportArguments.shrinkToFit)) + m_configuration.allowsShrinkToFit = m_viewportArguments.shrinkToFit != 0.; +} + +double ViewportConfiguration::viewportArgumentsLength(double length) const +{ + if (length == ViewportArguments::ValueDeviceWidth) + return m_minimumLayoutSize.width(); + if (length == ViewportArguments::ValueDeviceHeight) + return m_minimumLayoutSize.height(); + return length; +} + +int ViewportConfiguration::layoutWidth() const +{ + ASSERT(!constraintsAreAllRelative(m_configuration)); + + const FloatSize& minimumLayoutSize = m_minimumLayoutSize; + if (m_configuration.widthIsSet) { + // If we scale to fit, then accept the viewport width with sanity checking. + if (!m_configuration.initialScaleIsSet) { + double maximumScale = this->maximumScale(); + double maximumContentWidthInViewportCoordinate = maximumScale * m_configuration.width; + if (maximumContentWidthInViewportCoordinate < minimumLayoutSize.width()) { + // The content zoomed to maxScale does not fit the the view. Return the minimum width + // satisfying the constraint maximumScale. + return std::round(minimumLayoutSize.width() / maximumScale); + } + return std::round(m_configuration.width); + } + + // If not, make sure the viewport width and initial scale can co-exist. + double initialContentWidthInViewportCoordinate = m_configuration.width * m_configuration.initialScale; + if (initialContentWidthInViewportCoordinate < minimumLayoutSize.width()) { + // The specified width does not fit in viewport. Return the minimum width that satisfy the initialScale constraint. + return std::round(minimumLayoutSize.width() / m_configuration.initialScale); + } + return std::round(m_configuration.width); + } + + // If the page has a real scale, then just return the minimum size over the initial scale. + if (m_configuration.initialScaleIsSet && !m_configuration.heightIsSet) + return std::round(minimumLayoutSize.width() / m_configuration.initialScale); + + if (minimumLayoutSize.height() > 0) + return std::round(minimumLayoutSize.width() * layoutHeight() / minimumLayoutSize.height()); + return minimumLayoutSize.width(); +} + +int ViewportConfiguration::layoutHeight() const +{ + ASSERT(!constraintsAreAllRelative(m_configuration)); + + const FloatSize& minimumLayoutSize = m_minimumLayoutSize; + if (m_configuration.heightIsSet) { + // If we scale to fit, then accept the viewport height with sanity checking. + if (!m_configuration.initialScaleIsSet) { + double maximumScale = this->maximumScale(); + double maximumContentHeightInViewportCoordinate = maximumScale * m_configuration.height; + if (maximumContentHeightInViewportCoordinate < minimumLayoutSize.height()) { + // The content zoomed to maxScale does not fit the the view. Return the minimum height that + // satisfy the constraint maximumScale. + return std::round(minimumLayoutSize.height() / maximumScale); + } + return std::round(m_configuration.height); + } + + // If not, make sure the viewport width and initial scale can co-exist. + double initialContentHeightInViewportCoordinate = m_configuration.height * m_configuration.initialScale; + if (initialContentHeightInViewportCoordinate < minimumLayoutSize.height()) { + // The specified width does not fit in viewport. Return the minimum height that satisfy the initialScale constraint. + return std::round(minimumLayoutSize.height() / m_configuration.initialScale); + } + return std::round(m_configuration.height); + } + + // If the page has a real scale, then just return the minimum size over the initial scale. + if (m_configuration.initialScaleIsSet && !m_configuration.widthIsSet) + return std::round(minimumLayoutSize.height() / m_configuration.initialScale); + + if (minimumLayoutSize.width() > 0) + return std::round(minimumLayoutSize.height() * layoutWidth() / minimumLayoutSize.width()); + return minimumLayoutSize.height(); +} + +#ifndef NDEBUG + +TextStream& operator<<(TextStream& ts, const ViewportConfiguration::Parameters& parameters) +{ + ts.startGroup(); + ts << "width " << parameters.width << ", set: " << (parameters.widthIsSet ? "true" : "false"); + ts.endGroup(); + + ts.startGroup(); + ts << "height " << parameters.height << ", set: " << (parameters.heightIsSet ? "true" : "false"); + ts.endGroup(); + + ts.startGroup(); + ts << "initialScale " << parameters.initialScale << ", set: " << (parameters.initialScaleIsSet ? "true" : "false"); + ts.endGroup(); + + ts.dumpProperty("minimumScale", parameters.minimumScale); + ts.dumpProperty("maximumScale", parameters.maximumScale); + ts.dumpProperty("allowsUserScaling", parameters.allowsUserScaling); + ts.dumpProperty("allowsShrinkToFit", parameters.allowsShrinkToFit); + + return ts; +} + +CString ViewportConfiguration::description() const +{ + TextStream ts; + + ts.startGroup(); + ts << "viewport-configuration " << (void*)this; + { + TextStream::GroupScope scope(ts); + ts << "viewport arguments"; + ts << m_viewportArguments; + } + { + TextStream::GroupScope scope(ts); + ts << "configuration"; + ts << m_configuration; + } + { + TextStream::GroupScope scope(ts); + ts << "default configuration"; + ts << m_defaultConfiguration; + } + + ts.dumpProperty("contentSize", m_contentSize); + ts.dumpProperty("minimumLayoutSize", m_minimumLayoutSize); + ts.dumpProperty("computed initial scale", initialScale()); + ts.dumpProperty("computed minimum scale", minimumScale()); + ts.dumpProperty("computed layout size", layoutSize()); + ts.dumpProperty("ignoring horizontal scaling constraints", shouldIgnoreHorizontalScalingConstraints() ? "true" : "false"); + ts.dumpProperty("ignoring vertical scaling constraints", shouldIgnoreVerticalScalingConstraints() ? "true" : "false"); + + ts.endGroup(); + + return ts.release().utf8(); +} + +void ViewportConfiguration::dump() const +{ + WTFLogAlways("%s", description().data()); +} + +#endif + +} // namespace WebCore diff --git a/Source/WebCore/page/ViewportConfiguration.h b/Source/WebCore/page/ViewportConfiguration.h new file mode 100644 index 000000000..b72c0bfa9 --- /dev/null +++ b/Source/WebCore/page/ViewportConfiguration.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2005-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 "FloatSize.h" +#include "IntSize.h" +#include "ViewportArguments.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +static const double forceAlwaysUserScalableMaximumScale = 5.0; +static const double forceAlwaysUserScalableMinimumScale = 1.0; + +class TextStream; + +class ViewportConfiguration { + WTF_MAKE_NONCOPYABLE(ViewportConfiguration); WTF_MAKE_FAST_ALLOCATED; +public: + // FIXME: unify with ViewportArguments. + struct Parameters { + Parameters() + : width(0) + , height(0) + , initialScale(0) + , minimumScale(0) + , maximumScale(0) + , allowsUserScaling(false) + , allowsShrinkToFit(false) + , widthIsSet(false) + , heightIsSet(false) + , initialScaleIsSet(false) + { + } + + double width; + double height; + double initialScale; + double minimumScale; + double maximumScale; + bool allowsUserScaling; + bool allowsShrinkToFit; + + bool widthIsSet; + bool heightIsSet; + bool initialScaleIsSet; + }; + + WEBCORE_EXPORT ViewportConfiguration(); + + const Parameters& defaultConfiguration() const { return m_defaultConfiguration; } + WEBCORE_EXPORT void setDefaultConfiguration(const Parameters&); + + const IntSize& contentsSize() const { return m_contentSize; } + WEBCORE_EXPORT bool setContentsSize(const IntSize&); + + const FloatSize& minimumLayoutSize() const { return m_minimumLayoutSize; } + WEBCORE_EXPORT bool setMinimumLayoutSize(const FloatSize&); + + const ViewportArguments& viewportArguments() const { return m_viewportArguments; } + WEBCORE_EXPORT bool setViewportArguments(const ViewportArguments&); + + WEBCORE_EXPORT bool setCanIgnoreScalingConstraints(bool); + void setForceAlwaysUserScalable(bool forceAlwaysUserScalable) { m_forceAlwaysUserScalable = forceAlwaysUserScalable; } + + WEBCORE_EXPORT IntSize layoutSize() const; + WEBCORE_EXPORT double initialScale() const; + WEBCORE_EXPORT double initialScaleIgnoringContentSize() const; + WEBCORE_EXPORT double minimumScale() const; + double maximumScale() const { return m_forceAlwaysUserScalable ? forceAlwaysUserScalableMaximumScale : m_configuration.maximumScale; } + double maximumScaleIgnoringAlwaysScalable() const { return m_configuration.maximumScale; } + WEBCORE_EXPORT bool allowsUserScaling() const; + WEBCORE_EXPORT bool allowsUserScalingIgnoringAlwaysScalable() const; + bool allowsShrinkToFit() const; + + WEBCORE_EXPORT static Parameters webpageParameters(); + WEBCORE_EXPORT static Parameters textDocumentParameters(); + WEBCORE_EXPORT static Parameters imageDocumentParameters(); + WEBCORE_EXPORT static Parameters xhtmlMobileParameters(); + WEBCORE_EXPORT static Parameters testingParameters(); + +#ifndef NDEBUG + WTF::CString description() const; + WEBCORE_EXPORT void dump() const; +#endif + +private: + void updateConfiguration(); + double viewportArgumentsLength(double length) const; + double initialScaleFromSize(double width, double height, bool shouldIgnoreScalingConstraints) const; + int layoutWidth() const; + int layoutHeight() const; + + bool shouldIgnoreScalingConstraints() const; + bool shouldIgnoreVerticalScalingConstraints() const; + bool shouldIgnoreHorizontalScalingConstraints() const; + + Parameters m_configuration; + Parameters m_defaultConfiguration; + IntSize m_contentSize; + FloatSize m_minimumLayoutSize; + ViewportArguments m_viewportArguments; + + bool m_canIgnoreScalingConstraints; + bool m_forceAlwaysUserScalable; +}; + +TextStream& operator<<(TextStream&, const ViewportConfiguration::Parameters&); + +} // namespace WebCore diff --git a/Source/WebCore/page/VisitedLinkStore.cpp b/Source/WebCore/page/VisitedLinkStore.cpp new file mode 100644 index 000000000..973b78ee6 --- /dev/null +++ b/Source/WebCore/page/VisitedLinkStore.cpp @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include "config.h" +#include "VisitedLinkStore.h" + +#include "Page.h" + +namespace WebCore { + +VisitedLinkStore::VisitedLinkStore() +{ +} + +VisitedLinkStore::~VisitedLinkStore() +{ + ASSERT(m_pages.isEmpty()); +} + +void VisitedLinkStore::addPage(Page& page) +{ + ASSERT(!m_pages.contains(&page)); + + m_pages.add(&page); +} + +void VisitedLinkStore::removePage(Page& page) +{ + ASSERT(m_pages.contains(&page)); + + m_pages.remove(&page); +} + +void VisitedLinkStore::invalidateStylesForAllLinks() +{ + for (auto& page : m_pages) + page->invalidateStylesForAllLinks(); +} + +void VisitedLinkStore::invalidateStylesForLink(LinkHash linkHash) +{ + for (auto& page : m_pages) + page->invalidateStylesForLink(linkHash); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/VisitedLinkStore.h b/Source/WebCore/page/VisitedLinkStore.h new file mode 100644 index 000000000..441306a47 --- /dev/null +++ b/Source/WebCore/page/VisitedLinkStore.h @@ -0,0 +1,57 @@ +/* + * 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 <wtf/Forward.h> +#include <wtf/HashSet.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +typedef uint64_t LinkHash; +class Page; +class URL; + +class VisitedLinkStore : public RefCounted<VisitedLinkStore> { +public: + WEBCORE_EXPORT VisitedLinkStore(); + WEBCORE_EXPORT virtual ~VisitedLinkStore(); + + // FIXME: These two members should only take the link hash. + virtual bool isLinkVisited(Page&, LinkHash, const URL& baseURL, const AtomicString& attributeURL) = 0; + virtual void addVisitedLink(Page&, LinkHash) = 0; + + void addPage(Page&); + void removePage(Page&); + + WEBCORE_EXPORT void invalidateStylesForAllLinks(); + WEBCORE_EXPORT void invalidateStylesForLink(LinkHash); + +private: + HashSet<Page*> m_pages; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/WebCoreKeyboardUIMode.h b/Source/WebCore/page/WebCoreKeyboardUIMode.h index 187cf09df..af521cc64 100644 --- a/Source/WebCore/page/WebCoreKeyboardUIMode.h +++ b/Source/WebCore/page/WebCoreKeyboardUIMode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003 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,18 +23,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebCoreKeyboardUIMode_h -#define WebCoreKeyboardUIMode_h +#pragma once namespace WebCore { - enum KeyboardUIMode { - KeyboardAccessDefault = 0x00000000, - KeyboardAccessFull = 0x00000001, - // this flag may be or'ed with either of the two above - KeyboardAccessTabsToLinks = 0x10000000 - }; +enum KeyboardUIMode { + KeyboardAccessDefault = 0x00000000, + KeyboardAccessFull = 0x00000001, + // this flag may be or'ed with either of the two above + KeyboardAccessTabsToLinks = 0x10000000 +}; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/WebKitNamespace.cpp b/Source/WebCore/page/WebKitNamespace.cpp new file mode 100644 index 000000000..a1c6e141a --- /dev/null +++ b/Source/WebCore/page/WebKitNamespace.cpp @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#include "config.h" +#include "WebKitNamespace.h" + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "DOMWindow.h" +#include "UserMessageHandlersNamespace.h" + +namespace WebCore { + +WebKitNamespace::WebKitNamespace(Frame& frame, UserContentProvider& userContentProvider) + : DOMWindowProperty(&frame) + , m_messageHandlerNamespace(UserMessageHandlersNamespace::create(frame, userContentProvider)) +{ +} + +WebKitNamespace::~WebKitNamespace() +{ +} + +UserMessageHandlersNamespace* WebKitNamespace::messageHandlers() +{ + return &m_messageHandlerNamespace.get(); +} + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/WebKitNamespace.h b/Source/WebCore/page/WebKitNamespace.h new file mode 100644 index 000000000..ce818b375 --- /dev/null +++ b/Source/WebCore/page/WebKitNamespace.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 + +#if ENABLE(USER_MESSAGE_HANDLERS) + +#include "DOMWindowProperty.h" +#include <wtf/Ref.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class Frame; +class UserContentProvider; +class UserMessageHandlersNamespace; + +class WebKitNamespace : public DOMWindowProperty, public RefCounted<WebKitNamespace> { +public: + static Ref<WebKitNamespace> create(Frame& frame, UserContentProvider& userContentProvider) + { + return adoptRef(*new WebKitNamespace(frame, userContentProvider)); + } + + virtual ~WebKitNamespace(); + + UserMessageHandlersNamespace* messageHandlers(); + +private: + explicit WebKitNamespace(Frame&, UserContentProvider&); + + Ref<UserMessageHandlersNamespace> m_messageHandlerNamespace; +}; + +} // namespace WebCore + +#endif // ENABLE(USER_MESSAGE_HANDLERS) diff --git a/Source/WebCore/page/VisitedLinkProvider.cpp b/Source/WebCore/page/WebKitNamespace.idl index 3f9b2a128..f5444806f 100644 --- a/Source/WebCore/page/VisitedLinkProvider.cpp +++ b/Source/WebCore/page/WebKitNamespace.idl @@ -23,17 +23,9 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "VisitedLinkProvider.h" -namespace WebCore { - -VisitedLinkProvider::VisitedLinkProvider() -{ -} - -VisitedLinkProvider::~VisitedLinkProvider() -{ -} - -} +[ + Conditional=USER_MESSAGE_HANDLERS +] interface WebKitNamespace { + readonly attribute UserMessageHandlersNamespace messageHandlers; +}; diff --git a/Source/WebCore/page/WebKitPoint.h b/Source/WebCore/page/WebKitPoint.h index ff5d442ae..e6669145c 100644 --- a/Source/WebCore/page/WebKitPoint.h +++ b/Source/WebCore/page/WebKitPoint.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,42 +23,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WebKitPoint_h -#define WebKitPoint_h +#pragma once -#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> namespace WebCore { - class WebKitPoint : public RefCounted<WebKitPoint> { - public: +class WebKitPoint : public RefCounted<WebKitPoint> { +public: + static Ref<WebKitPoint> create() + { + return adoptRef(*new WebKitPoint); + } + static Ref<WebKitPoint> create(float x, float y) + { + return adoptRef(*new WebKitPoint(x, y)); + } - static PassRefPtr<WebKitPoint> create() - { - return adoptRef(new WebKitPoint()); - } - static PassRefPtr<WebKitPoint> create(float x, float y) - { - return adoptRef(new WebKitPoint(x, y)); - } + float x() const { return m_x; } + float y() const { return m_y; } + + void setX(float x) { m_x = x; } + void setY(float y) { m_y = y; } - float x() const { return m_x; } - float y() const { return m_y; } - - void setX(float x) { m_x = x; } - void setY(float y) { m_y = y; } +private: + WebKitPoint(float x, float y) + : m_x(std::isnan(x) ? 0 : x) + , m_y(std::isnan(y) ? 0 : y) + { + } - private: - WebKitPoint(float x=0, float y=0) - : m_x(x) - , m_y(y) - { - } + WebKitPoint() + { + } - float m_x, m_y; - }; + float m_x { 0 }; + float m_y { 0 }; +}; } // namespace WebCore - -#endif // WebKitPoint_h diff --git a/Source/WebCore/page/WebKitPoint.idl b/Source/WebCore/page/WebKitPoint.idl index 44e3b10fc..d1ec5edd3 100644 --- a/Source/WebCore/page/WebKitPoint.idl +++ b/Source/WebCore/page/WebKitPoint.idl @@ -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,11 +24,9 @@ */ [ - CustomConstructor, - CustomConstructor(float x, float y), + Constructor(optional unrestricted float x = 0, optional unrestricted float y = 0), ImplementationLacksVTable ] interface WebKitPoint { - attribute float x; - attribute float y; + attribute unrestricted float x; + attribute unrestricted float y; }; - diff --git a/Source/WebCore/page/WheelEventDeltaFilter.cpp b/Source/WebCore/page/WheelEventDeltaFilter.cpp new file mode 100644 index 000000000..e037fe9b8 --- /dev/null +++ b/Source/WebCore/page/WheelEventDeltaFilter.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015-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 "WheelEventDeltaFilter.h" + +#if HAVE(NSSCROLLING_FILTERS) +#include "WheelEventDeltaFilterMac.h" +#endif + +#include "FloatSize.h" +#include "Logging.h" +#include "TextStream.h" + +namespace WebCore { + +WheelEventDeltaFilter::WheelEventDeltaFilter() +{ +} + +WheelEventDeltaFilter::~WheelEventDeltaFilter() +{ +} + +std::unique_ptr<WheelEventDeltaFilter> WheelEventDeltaFilter::create() +{ +#if HAVE(NSSCROLLING_FILTERS) + return std::make_unique<WheelEventDeltaFilterMac>(); +#else + return std::make_unique<BasicWheelEventDeltaFilter>(); +#endif +} + +bool WheelEventDeltaFilter::isFilteringDeltas() const +{ + return m_isFilteringDeltas; +} + +FloatSize WheelEventDeltaFilter::filteredDelta() const +{ + return m_currentFilteredDelta; +} + +FloatSize WheelEventDeltaFilter::filteredVelocity() const +{ + return m_currentFilteredVelocity; +} + +BasicWheelEventDeltaFilter::BasicWheelEventDeltaFilter() + : WheelEventDeltaFilter() +{ +} + +const size_t basicWheelEventDeltaFilterWindowSize = 3; + +void BasicWheelEventDeltaFilter::updateFromDelta(const FloatSize& delta) +{ + m_currentFilteredDelta = delta; + if (!m_isFilteringDeltas) + return; + + m_recentWheelEventDeltas.append(delta); + if (m_recentWheelEventDeltas.size() > basicWheelEventDeltaFilterWindowSize) + m_recentWheelEventDeltas.removeFirst(); + + DominantScrollGestureDirection scrollDirection = dominantScrollGestureDirection(); + if (scrollDirection == DominantScrollGestureDirection::Vertical) + m_currentFilteredDelta.setWidth(0); + else if (scrollDirection == DominantScrollGestureDirection::Horizontal) + m_currentFilteredDelta.setHeight(0); +} + +void BasicWheelEventDeltaFilter::beginFilteringDeltas() +{ + m_recentWheelEventDeltas.clear(); + m_isFilteringDeltas = true; +} + +void BasicWheelEventDeltaFilter::endFilteringDeltas() +{ + m_currentFilteredDelta = FloatSize(0, 0); + m_isFilteringDeltas = false; +} + +static inline bool deltaIsPredominantlyVertical(const FloatSize& delta) +{ + return fabs(delta.height()) > fabs(delta.width()); +} + +DominantScrollGestureDirection BasicWheelEventDeltaFilter::dominantScrollGestureDirection() const +{ + bool allVertical = m_recentWheelEventDeltas.size(); + bool allHorizontal = m_recentWheelEventDeltas.size(); + + for (const auto& delta : m_recentWheelEventDeltas) { + bool isVertical = deltaIsPredominantlyVertical(delta); + allVertical &= isVertical; + allHorizontal &= !isVertical; + } + + if (allVertical) + return DominantScrollGestureDirection::Vertical; + + if (allHorizontal) + return DominantScrollGestureDirection::Horizontal; + + return DominantScrollGestureDirection::None; +} + +}; diff --git a/Source/WebCore/page/WheelEventDeltaFilter.h b/Source/WebCore/page/WheelEventDeltaFilter.h new file mode 100644 index 000000000..db0e44b40 --- /dev/null +++ b/Source/WebCore/page/WheelEventDeltaFilter.h @@ -0,0 +1,71 @@ +/* + * 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 "FloatSize.h" +#include <wtf/Deque.h> + +namespace WebCore { + +class WheelEventDeltaFilter { +public: + WheelEventDeltaFilter(); + virtual ~WheelEventDeltaFilter(); + + WEBCORE_EXPORT static std::unique_ptr<WheelEventDeltaFilter> create(); + WEBCORE_EXPORT virtual void updateFromDelta(const FloatSize&) = 0; + WEBCORE_EXPORT virtual void beginFilteringDeltas() = 0; + WEBCORE_EXPORT virtual void endFilteringDeltas() = 0; + WEBCORE_EXPORT FloatSize filteredVelocity() const; + WEBCORE_EXPORT bool isFilteringDeltas() const; + WEBCORE_EXPORT FloatSize filteredDelta() const; + +protected: + FloatSize m_currentFilteredDelta; + FloatSize m_currentFilteredVelocity; + bool m_isFilteringDeltas { false }; +}; + +enum class DominantScrollGestureDirection { + None, + Vertical, + Horizontal +}; + +class BasicWheelEventDeltaFilter final : public WheelEventDeltaFilter { +public: + BasicWheelEventDeltaFilter(); + void updateFromDelta(const FloatSize&) override; + void beginFilteringDeltas() override; + void endFilteringDeltas() override; + +private: + DominantScrollGestureDirection dominantScrollGestureDirection() const; + + Deque<FloatSize> m_recentWheelEventDeltas; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/WheelEventTestTrigger.cpp b/Source/WebCore/page/WheelEventTestTrigger.cpp new file mode 100644 index 000000000..39887f135 --- /dev/null +++ b/Source/WebCore/page/WheelEventTestTrigger.cpp @@ -0,0 +1,133 @@ +/* + * 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. + * 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. + * + * 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 "WheelEventTestTrigger.h" + +#include "Logging.h" + +#if !LOG_DISABLED +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> +#endif + +namespace WebCore { + +WheelEventTestTrigger::WheelEventTestTrigger() + : m_testTriggerTimer(RunLoop::current(), this, &WheelEventTestTrigger::triggerTestTimerFired) +{ +} + +void WheelEventTestTrigger::clearAllTestDeferrals() +{ + std::lock_guard<Lock> lock(m_testTriggerMutex); + m_deferTestTriggerReasons.clear(); + m_testNotificationCallback = std::function<void()>(); + m_testTriggerTimer.stop(); + LOG(WheelEventTestTriggers, " (=) WheelEventTestTrigger::clearAllTestDeferrals: cleared all test state."); +} + +void WheelEventTestTrigger::setTestCallbackAndStartNotificationTimer(std::function<void()> functionCallback) +{ + { + std::lock_guard<Lock> lock(m_testTriggerMutex); + m_testNotificationCallback = WTFMove(functionCallback); + } + + if (!m_testTriggerTimer.isActive()) + m_testTriggerTimer.startRepeating(1.0 / 60.0); +} + +void WheelEventTestTrigger::deferTestsForReason(ScrollableAreaIdentifier identifier, DeferTestTriggerReason reason) +{ + std::lock_guard<Lock> lock(m_testTriggerMutex); + auto it = m_deferTestTriggerReasons.find(identifier); + if (it == m_deferTestTriggerReasons.end()) + it = m_deferTestTriggerReasons.add(identifier, std::set<DeferTestTriggerReason>()).iterator; + + LOG(WheelEventTestTriggers, " (=) WheelEventTestTrigger::deferTestsForReason: id=%p, reason=%d", identifier, reason); + it->value.insert(reason); +} + +void WheelEventTestTrigger::removeTestDeferralForReason(ScrollableAreaIdentifier identifier, DeferTestTriggerReason reason) +{ + std::lock_guard<Lock> lock(m_testTriggerMutex); + auto it = m_deferTestTriggerReasons.find(identifier); + if (it == m_deferTestTriggerReasons.end()) + return; + + LOG(WheelEventTestTriggers, " (=) WheelEventTestTrigger::removeTestDeferralForReason: id=%p, reason=%d", identifier, reason); + it->value.erase(reason); + + if (it->value.empty()) + m_deferTestTriggerReasons.remove(it); +} + +#if !LOG_DISABLED +static void dumpState(WTF::HashMap<WheelEventTestTrigger::ScrollableAreaIdentifier, std::set<WheelEventTestTrigger::DeferTestTriggerReason>> reasons) +{ + LOG(WheelEventTestTriggers, " WheelEventTestTrigger::dumpState:"); + for (const auto& scrollRegion : reasons) { + LOG(WheelEventTestTriggers, " For scroll region %p", scrollRegion.key); + StringBuilder reasons; + for (const auto& reason : scrollRegion.value) { + if (!reasons.isEmpty()) + reasons.append(", "); + reasons.append(String::number(reason)); + } + LOG(WheelEventTestTriggers, " Reasons: %s", reasons.toString().utf8().data()); + } +} +#endif + +void WheelEventTestTrigger::triggerTestTimerFired() +{ + std::function<void()> functionCallback; + + { + std::lock_guard<Lock> lock(m_testTriggerMutex); + if (!m_deferTestTriggerReasons.isEmpty()) { +#if !LOG_DISABLED + if (isLogChannelEnabled("WheelEventTestTriggers")) + dumpState(m_deferTestTriggerReasons); +#endif + return; + } + + functionCallback = WTFMove(m_testNotificationCallback); + m_testNotificationCallback = std::function<void()>(); + } + + m_testTriggerTimer.stop(); + + LOG(WheelEventTestTriggers, " WheelEventTestTrigger::triggerTestTimerFired: FIRING TEST"); + if (functionCallback) + functionCallback(); +} + +} diff --git a/Source/WebCore/page/WheelEventTestTrigger.h b/Source/WebCore/page/WheelEventTestTrigger.h new file mode 100644 index 000000000..c2b36ddcf --- /dev/null +++ b/Source/WebCore/page/WheelEventTestTrigger.h @@ -0,0 +1,65 @@ +/* + * 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. + * 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. + * + * 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. + */ + +#pragma once + +#include <set> +#include <wtf/HashMap.h> +#include <wtf/Lock.h> +#include <wtf/RunLoop.h> +#include <wtf/ThreadSafeRefCounted.h> + +namespace WebCore { + +class WheelEventTestTrigger : public ThreadSafeRefCounted<WheelEventTestTrigger> { + WTF_MAKE_NONCOPYABLE(WheelEventTestTrigger); WTF_MAKE_FAST_ALLOCATED; +public: + WheelEventTestTrigger(); + + WEBCORE_EXPORT void setTestCallbackAndStartNotificationTimer(std::function<void()>); + WEBCORE_EXPORT void clearAllTestDeferrals(); + + enum DeferTestTriggerReason { + RubberbandInProgress, + ScrollSnapInProgress, + ScrollingThreadSyncNeeded, + ContentScrollInProgress + }; + typedef const void* ScrollableAreaIdentifier; + void WEBCORE_EXPORT deferTestsForReason(ScrollableAreaIdentifier, DeferTestTriggerReason); + void WEBCORE_EXPORT removeTestDeferralForReason(ScrollableAreaIdentifier, DeferTestTriggerReason); + void triggerTestTimerFired(); + +private: + std::function<void()> m_testNotificationCallback; + RunLoop::Timer<WheelEventTestTrigger> m_testTriggerTimer; + mutable Lock m_testTriggerMutex; + WTF::HashMap<ScrollableAreaIdentifier, std::set<DeferTestTriggerReason>> m_deferTestTriggerReasons; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/WindowEventHandlers.idl b/Source/WebCore/page/WindowEventHandlers.idl new file mode 100644 index 000000000..0ff8dd76f --- /dev/null +++ b/Source/WebCore/page/WindowEventHandlers.idl @@ -0,0 +1,52 @@ +/* + * 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. ``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. + * + */ + +// https://html.spec.whatwg.org/multipage/webappapis.html#windoweventhandlers +[ + NoInterfaceObject, +] interface WindowEventHandlers { + // Commented out event handlers are defined by the HTML5 specification but + // are not yet implemented. + + // [WindowEventHandler] attribute EventHandler onafterprint;. + // [WindowEventHandler] attribute EventHandler onbeforeprint;. + [WindowEventHandler] attribute EventHandler onbeforeunload; + [WindowEventHandler] attribute EventHandler onhashchange; + [WindowEventHandler] attribute EventHandler onlanguagechange; + [WindowEventHandler] attribute EventHandler onmessage; + [WindowEventHandler] attribute EventHandler onoffline; + [WindowEventHandler] attribute EventHandler ononline; + [WindowEventHandler] attribute EventHandler onpagehide; + [WindowEventHandler] attribute EventHandler onpageshow; + [WindowEventHandler] attribute EventHandler onpopstate; + [WindowEventHandler] attribute EventHandler onstorage; + [WindowEventHandler] attribute EventHandler onunload; + + + // Additions that are not yet part of the standard. + + [NotEnumerable, WindowEventHandler, Conditional=ORIENTATION_EVENTS] attribute EventHandler onorientationchange; +}; diff --git a/Source/WebCore/page/WindowFeatures.cpp b/Source/WebCore/page/WindowFeatures.cpp index d93420161..b8adcb449 100644 --- a/Source/WebCore/page/WindowFeatures.cpp +++ b/Source/WebCore/page/WindowFeatures.cpp @@ -25,154 +25,123 @@ #include "FloatRect.h" #include <wtf/Assertions.h> +#include <wtf/HashMap.h> #include <wtf/MathExtras.h> #include <wtf/text/StringHash.h> namespace WebCore { -// Though isspace() considers \t and \v to be whitespace, Win IE doesn't when parsing window features. -static bool isWindowFeaturesSeparator(UChar c) +typedef HashMap<String, String, ASCIICaseInsensitiveHash> DialogFeaturesMap; + +static void setWindowFeature(WindowFeatures&, StringView key, StringView value); + +static DialogFeaturesMap parseDialogFeaturesMap(const String&); +static std::optional<bool> boolFeature(const DialogFeaturesMap&, const char* key); +static std::optional<float> floatFeature(const DialogFeaturesMap&, const char* key, float min, float max); + +static bool isSeparator(UChar character) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '=' || c == ',' || c == '\0'; + return character == ' ' || character == '\t' || character == '\n' || character == '\r' || character == '=' || character == ','; } -WindowFeatures::WindowFeatures(const String& features) - : xSet(false) - , ySet(false) - , widthSet(false) - , heightSet(false) - , fullscreen(false) - , dialog(false) +WindowFeatures parseWindowFeatures(StringView featuresString) { - /* - The IE rule is: all features except for channelmode and fullscreen default to YES, but - if the user specifies a feature string, all features default to NO. (There is no public - standard that applies to this method.) - - <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp> - We always allow a window to be resized, which is consistent with Firefox. - */ - - if (features.length() == 0) { - menuBarVisible = true; - statusBarVisible = true; - toolBarVisible = true; - locationBarVisible = true; - scrollbarsVisible = true; - resizable = true; - return; - } + // The IE rule is: all features except for channelmode and fullscreen default to YES, but + // if the user specifies a feature string, all features default to NO. (There is no public + // standard that applies to this method.) + // + // <http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/open_0.asp> + // We always allow a window to be resized, which is consistent with Firefox. - menuBarVisible = false; - statusBarVisible = false; - toolBarVisible = false; - locationBarVisible = false; - scrollbarsVisible = false; - resizable = true; - - // Tread lightly in this code -- it was specifically designed to mimic Win IE's parsing behavior. - int keyBegin, keyEnd; - int valueBegin, valueEnd; - - int i = 0; - int length = features.length(); - String buffer = features.lower(); - while (i < length) { - // skip to first non-separator, but don't skip past the end of the string - while (isWindowFeaturesSeparator(buffer[i])) { - if (i >= length) - break; - i++; - } - keyBegin = i; + WindowFeatures features; - // skip to first separator - while (!isWindowFeaturesSeparator(buffer[i])) - i++; - keyEnd = i; + if (featuresString.isEmpty()) + return features; - // skip to first '=', but don't skip past a ',' or the end of the string - while (buffer[i] != '=') { - if (buffer[i] == ',' || i >= length) - break; - i++; - } + features.menuBarVisible = false; + features.statusBarVisible = false; + features.toolBarVisible = false; + features.locationBarVisible = false; + features.scrollbarsVisible = false; - // skip to first non-separator, but don't skip past a ',' or the end of the string - while (isWindowFeaturesSeparator(buffer[i])) { - if (buffer[i] == ',' || i >= length) - break; - i++; - } - valueBegin = i; + processFeaturesString(featuresString, [&features](StringView key, StringView value) { + setWindowFeature(features, key, value); + }); + + return features; +} + +void processFeaturesString(StringView features, std::function<void(StringView type, StringView value)> callback) +{ + unsigned length = features.length(); + for (unsigned i = 0; i < length; ) { + // skip to first non-separator + while (i < length && isSeparator(features[i])) + ++i; + unsigned keyBegin = i; // skip to first separator - while (!isWindowFeaturesSeparator(buffer[i])) + while (i < length && !isSeparator(features[i])) i++; - valueEnd = i; + unsigned keyEnd = i; - ASSERT_WITH_SECURITY_IMPLICATION(i <= length); + // skip to first '=', but don't skip past a ',' + while (i < length && features[i] != '=' && features[i] != ',') + ++i; - String keyString(buffer.substring(keyBegin, keyEnd - keyBegin)); - String valueString(buffer.substring(valueBegin, valueEnd - valueBegin)); - setWindowFeature(keyString, valueString); + // skip to first non-separator, but don't skip past a ',' + while (i < length && isSeparator(features[i]) && features[i] != ',') + ++i; + unsigned valueBegin = i; + + // skip to first separator + while (i < length && !isSeparator(features[i])) + ++i; + unsigned valueEnd = i; + + callback(features.substring(keyBegin, keyEnd - keyBegin), features.substring(valueBegin, valueEnd - valueBegin)); } } -void WindowFeatures::setWindowFeature(const String& keyString, const String& valueString) +static void setWindowFeature(WindowFeatures& features, StringView key, StringView value) { - int value; - // Listing a key with no value is shorthand for key=yes - if (valueString.isEmpty() || valueString == "yes") - value = 1; + int numericValue; + if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "yes")) + numericValue = 1; else - value = valueString.toInt(); + numericValue = value.toInt(); - // We treat keyString of "resizable" here as an additional feature rather than setting resizeable to true. + // We treat key of "resizable" here as an additional feature rather than setting resizeable to true. // This is consistent with Firefox, but could also be handled at another level. - if (keyString == "left" || keyString == "screenx") { - xSet = true; - x = value; - } else if (keyString == "top" || keyString == "screeny") { - ySet = true; - y = value; - } else if (keyString == "width" || keyString == "innerwidth") { - widthSet = true; - width = value; - } else if (keyString == "height" || keyString == "innerheight") { - heightSet = true; - height = value; - } else if (keyString == "menubar") - menuBarVisible = value; - else if (keyString == "toolbar") - toolBarVisible = value; - else if (keyString == "location") - locationBarVisible = value; - else if (keyString == "status") - statusBarVisible = value; - else if (keyString == "fullscreen") - fullscreen = value; - else if (keyString == "scrollbars") - scrollbarsVisible = value; - else if (value == 1) - additionalFeatures.append(keyString); + if (equalLettersIgnoringASCIICase(key, "left") || equalLettersIgnoringASCIICase(key, "screenx")) + features.x = numericValue; + else if (equalLettersIgnoringASCIICase(key, "top") || equalLettersIgnoringASCIICase(key, "screeny")) + features.y = numericValue; + else if (equalLettersIgnoringASCIICase(key, "width") || equalLettersIgnoringASCIICase(key, "innerwidth")) + features.width = numericValue; + else if (equalLettersIgnoringASCIICase(key, "height") || equalLettersIgnoringASCIICase(key, "innerheight")) + features.height = numericValue; + else if (equalLettersIgnoringASCIICase(key, "menubar")) + features.menuBarVisible = numericValue; + else if (equalLettersIgnoringASCIICase(key, "toolbar")) + features.toolBarVisible = numericValue; + else if (equalLettersIgnoringASCIICase(key, "location")) + features.locationBarVisible = numericValue; + else if (equalLettersIgnoringASCIICase(key, "status")) + features.statusBarVisible = numericValue; + else if (equalLettersIgnoringASCIICase(key, "fullscreen")) + features.fullscreen = numericValue; + else if (equalLettersIgnoringASCIICase(key, "scrollbars")) + features.scrollbarsVisible = numericValue; + else if (numericValue == 1) + features.additionalFeatures.append(key.toString()); } -WindowFeatures::WindowFeatures(const String& dialogFeaturesString, const FloatRect& screenAvailableRect) - : widthSet(true) - , heightSet(true) - , menuBarVisible(false) - , toolBarVisible(false) - , locationBarVisible(false) - , fullscreen(false) - , dialog(true) +WindowFeatures parseDialogFeatures(const String& dialogFeaturesString, const FloatRect& screenAvailableRect) { - DialogFeaturesMap features; - parseDialogFeatures(dialogFeaturesString, features); - - const bool trusted = false; + auto featuresMap = parseDialogFeaturesMap(dialogFeaturesString); // The following features from Microsoft's documentation are not implemented: // - default font settings @@ -182,66 +151,81 @@ WindowFeatures::WindowFeatures(const String& dialogFeaturesString, const FloatRe // - help: boolFeature(features, "help", true), makes help icon appear in dialog (what does it do on Windows?) // - unadorned: trusted && boolFeature(features, "unadorned"); - width = floatFeature(features, "dialogwidth", 100, screenAvailableRect.width(), 620); // default here came from frame size of dialog in MacIE - height = floatFeature(features, "dialogheight", 100, screenAvailableRect.height(), 450); // default here came from frame size of dialog in MacIE + WindowFeatures features; - x = floatFeature(features, "dialogleft", screenAvailableRect.x(), screenAvailableRect.maxX() - width, -1); - xSet = x > 0; - y = floatFeature(features, "dialogtop", screenAvailableRect.y(), screenAvailableRect.maxY() - height, -1); - ySet = y > 0; + features.menuBarVisible = false; + features.toolBarVisible = false; + features.locationBarVisible = false; + features.dialog = true; - if (boolFeature(features, "center", true)) { - if (!xSet) { - x = screenAvailableRect.x() + (screenAvailableRect.width() - width) / 2; - xSet = true; - } - if (!ySet) { - y = screenAvailableRect.y() + (screenAvailableRect.height() - height) / 2; - ySet = true; - } + float width = floatFeature(featuresMap, "dialogwidth", 100, screenAvailableRect.width()).value_or(620); // default here came from frame size of dialog in MacIE + float height = floatFeature(featuresMap, "dialogheight", 100, screenAvailableRect.height()).value_or(450); // default here came from frame size of dialog in MacIE + + features.width = width; + features.height = height; + + features.x = floatFeature(featuresMap, "dialogleft", screenAvailableRect.x(), screenAvailableRect.maxX() - width); + features.y = floatFeature(featuresMap, "dialogtop", screenAvailableRect.y(), screenAvailableRect.maxY() - height); + + if (boolFeature(featuresMap, "center").value_or(true)) { + if (!features.x) + features.x = screenAvailableRect.x() + (screenAvailableRect.width() - width) / 2; + if (!features.y) + features.y = screenAvailableRect.y() + (screenAvailableRect.height() - height) / 2; } - resizable = boolFeature(features, "resizable"); - scrollbarsVisible = boolFeature(features, "scroll", true); - statusBarVisible = boolFeature(features, "status", !trusted); + features.resizable = boolFeature(featuresMap, "resizable").value_or(false); + features.scrollbarsVisible = boolFeature(featuresMap, "scroll").value_or(true); + features.statusBarVisible = boolFeature(featuresMap, "status").value_or(false); + + return features; } -bool WindowFeatures::boolFeature(const DialogFeaturesMap& features, const char* key, bool defaultValue) +static std::optional<bool> boolFeature(const DialogFeaturesMap& features, const char* key) { - DialogFeaturesMap::const_iterator it = features.find(key); + auto it = features.find(key); if (it == features.end()) - return defaultValue; - const String& value = it->value; - return value.isNull() || value == "1" || value == "yes" || value == "on"; + return std::nullopt; + + auto& value = it->value; + return value.isNull() + || value == "1" + || equalLettersIgnoringASCIICase(value, "yes") + || equalLettersIgnoringASCIICase(value, "on"); } -float WindowFeatures::floatFeature(const DialogFeaturesMap& features, const char* key, float min, float max, float defaultValue) +static std::optional<float> floatFeature(const DialogFeaturesMap& features, const char* key, float min, float max) { - DialogFeaturesMap::const_iterator it = features.find(key); + auto it = features.find(key); if (it == features.end()) - return defaultValue; + return std::nullopt; + // FIXME: The toDouble function does not offer a way to tell "0q" from string with no digits in it: Both // return the number 0 and false for ok. But "0q" should yield the minimum rather than the default. bool ok; double parsedNumber = it->value.toDouble(&ok); if ((!parsedNumber && !ok) || std::isnan(parsedNumber)) - return defaultValue; + return std::nullopt; if (parsedNumber < min || max <= min) return min; if (parsedNumber > max) return max; + // FIXME: Seems strange to cast a double to int and then convert back to a float. Why is this a good idea? return static_cast<int>(parsedNumber); } -void WindowFeatures::parseDialogFeatures(const String& string, DialogFeaturesMap& map) +static DialogFeaturesMap parseDialogFeaturesMap(const String& string) { + // FIXME: Not clear why we take such a different approach to parsing dialog features + // as opposed to window features (using a map, different parsing quirks). + + DialogFeaturesMap features; + Vector<String> vector; string.split(';', vector); - size_t size = vector.size(); - for (size_t i = 0; i < size; ++i) { - const String& featureString = vector[i]; + for (auto& featureString : vector) { size_t separatorPosition = featureString.find('='); size_t colonPosition = featureString.find(':'); if (separatorPosition != notFound && colonPosition != notFound) @@ -249,17 +233,19 @@ void WindowFeatures::parseDialogFeatures(const String& string, DialogFeaturesMap if (separatorPosition == notFound) separatorPosition = colonPosition; - String key = featureString.left(separatorPosition).stripWhiteSpace().lower(); + String key = featureString.left(separatorPosition).stripWhiteSpace(); // Null string for value indicates key without value. String value; if (separatorPosition != notFound) { - value = featureString.substring(separatorPosition + 1).stripWhiteSpace().lower(); + value = featureString.substring(separatorPosition + 1).stripWhiteSpace(); value = value.left(value.find(' ')); } - map.set(key, value); + features.set(key, value); } + + return features; } } // namespace WebCore diff --git a/Source/WebCore/page/WindowFeatures.h b/Source/WebCore/page/WindowFeatures.h index 43a0b1ba2..d46a75a02 100644 --- a/Source/WebCore/page/WindowFeatures.h +++ b/Source/WebCore/page/WindowFeatures.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2010, 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,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,10 +26,11 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WindowFeatures_h -#define WindowFeatures_h +#pragma once -#include <wtf/HashMap.h> +#include <functional> +#include <wtf/Optional.h> +#include <wtf/Vector.h> #include <wtf/text/WTFString.h> namespace WebCore { @@ -37,53 +38,27 @@ namespace WebCore { class FloatRect; struct WindowFeatures { - WindowFeatures() - : xSet(false) - , ySet(false) - , widthSet(false) - , heightSet(false) - , menuBarVisible(true) - , statusBarVisible(true) - , toolBarVisible(true) - , locationBarVisible(true) - , scrollbarsVisible(true) - , resizable(true) - , fullscreen(false) - , dialog(false) - { - } - explicit WindowFeatures(const String& windowFeaturesString); - WindowFeatures(const String& dialogFeaturesString, const FloatRect& screenAvailableRect); + std::optional<float> x; + std::optional<float> y; + std::optional<float> width; + std::optional<float> height; - float x; - bool xSet; - float y; - bool ySet; - float width; - bool widthSet; - float height; - bool heightSet; + bool menuBarVisible { true }; + bool statusBarVisible { true }; + bool toolBarVisible { true }; + bool locationBarVisible { true }; + bool scrollbarsVisible { true }; + bool resizable { true }; - bool menuBarVisible; - bool statusBarVisible; - bool toolBarVisible; - bool locationBarVisible; - bool scrollbarsVisible; - bool resizable; - - bool fullscreen; - bool dialog; + bool fullscreen { false }; + bool dialog { false }; Vector<String> additionalFeatures; - -private: - typedef HashMap<String, String> DialogFeaturesMap; - static void parseDialogFeatures(const String&, HashMap<String, String>&); - static bool boolFeature(const DialogFeaturesMap&, const char* key, bool defaultValue = false); - static float floatFeature(const DialogFeaturesMap&, const char* key, float min, float max, float defaultValue); - void setWindowFeature(const String& keyString, const String& valueString); }; -} // namespace WebCore +WindowFeatures parseWindowFeatures(StringView windowFeaturesString); +WindowFeatures parseDialogFeatures(const String& dialogFeaturesString, const FloatRect& screenAvailableRect); -#endif // WindowFeatures_h +void processFeaturesString(StringView features, std::function<void(StringView type, StringView value)> callback); + +} // namespace WebCore diff --git a/Source/WebCore/page/WindowFocusAllowedIndicator.cpp b/Source/WebCore/page/WindowFocusAllowedIndicator.cpp index f44de6a07..ffa3aae47 100644 --- a/Source/WebCore/page/WindowFocusAllowedIndicator.cpp +++ b/Source/WebCore/page/WindowFocusAllowedIndicator.cpp @@ -13,7 +13,7 @@ * 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 + * 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 diff --git a/Source/WebCore/page/WindowFocusAllowedIndicator.h b/Source/WebCore/page/WindowFocusAllowedIndicator.h index 86598d7d6..9987561a2 100644 --- a/Source/WebCore/page/WindowFocusAllowedIndicator.h +++ b/Source/WebCore/page/WindowFocusAllowedIndicator.h @@ -13,7 +13,7 @@ * 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 + * 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,11 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WindowFocusAllowedIndicator_h -#define WindowFocusAllowedIndicator_h +#pragma once #include <wtf/Noncopyable.h> -#include <wtf/RefPtr.h> namespace WebCore { @@ -44,5 +42,3 @@ private: }; } // namespace WebCore - -#endif // WindowFocusAllowedIndicator_h diff --git a/Source/WebCore/page/WindowTimers.idl b/Source/WebCore/page/WindowOrWorkerGlobalScope.idl index 8465b880d..da294a6e7 100644 --- a/Source/WebCore/page/WindowTimers.idl +++ b/Source/WebCore/page/WindowOrWorkerGlobalScope.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2009, 2013, 1016 Apple Inc. All rights reserved. * Copyright (C) 2011 Google Inc. All rights reserved. * Copyright (C) 2013 Samsung Electronics. All rights reserved. * @@ -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 @@ -27,10 +27,14 @@ [ NoInterfaceObject, -] interface WindowTimers { - [Custom] long setTimeout(any handler, [Default=Undefined] optional long timeout); - void clearTimeout([Default=Undefined] optional long handle); - [Custom] long setInterval(any handler, [Default=Undefined] optional long timeout); - void clearInterval([Default=Undefined] optional long handle); -}; +] interface WindowOrWorkerGlobalScope { + // Timers. + [Custom] long setTimeout(any handler, optional long timeout = 0); + void clearTimeout(optional long handle = 0); + [Custom] long setInterval(any handler, optional long timeout = 0); + void clearInterval(optional long handle = 0); + // Base64 utility methods. + [MayThrowException] DOMString atob(DOMString string); + [MayThrowException] DOMString btoa(DOMString string); +}; diff --git a/Source/WebCore/page/WorkerNavigator.cpp b/Source/WebCore/page/WorkerNavigator.cpp index eac5ddf3e..a2e42bb15 100644 --- a/Source/WebCore/page/WorkerNavigator.cpp +++ b/Source/WebCore/page/WorkerNavigator.cpp @@ -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 @@ -34,10 +34,6 @@ WorkerNavigator::WorkerNavigator(const String& userAgent) { } -WorkerNavigator::~WorkerNavigator() -{ -} - String WorkerNavigator::userAgent() const { return m_userAgent; diff --git a/Source/WebCore/page/WorkerNavigator.h b/Source/WebCore/page/WorkerNavigator.h index f25d8cd48..92fb4b014 100644 --- a/Source/WebCore/page/WorkerNavigator.h +++ b/Source/WebCore/page/WorkerNavigator.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,24 +23,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WorkerNavigator_h -#define WorkerNavigator_h +#pragma once #include "NavigatorBase.h" #include "Supplementable.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> #include <wtf/text/WTFString.h> namespace WebCore { -class WorkerNavigator : public NavigatorBase, public RefCounted<WorkerNavigator>, public Supplementable<WorkerNavigator> { +class WorkerNavigator final : public NavigatorBase, public Supplementable<WorkerNavigator> { public: - static PassRefPtr<WorkerNavigator> create(const String& userAgent) { return adoptRef(new WorkerNavigator(userAgent)); } - virtual ~WorkerNavigator(); + static Ref<WorkerNavigator> create(const String& userAgent) { return adoptRef(*new WorkerNavigator(userAgent)); } - virtual String userAgent() const; + String userAgent() const final; private: explicit WorkerNavigator(const String&); @@ -49,5 +44,3 @@ private: }; } // namespace WebCore - -#endif // WorkerNavigator_h diff --git a/Source/WebCore/page/WorkerNavigator.idl b/Source/WebCore/page/WorkerNavigator.idl index e2a282de7..f7e7c2a69 100644 --- a/Source/WebCore/page/WorkerNavigator.idl +++ b/Source/WebCore/page/WorkerNavigator.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. * @@ -27,15 +27,12 @@ */ [ - NoInterfaceObject, + Exposed=Worker, GenerateIsReachable=Impl, - JSNoStaticTables, ] interface WorkerNavigator { - readonly attribute DOMString appName; - readonly attribute DOMString appVersion; - readonly attribute DOMString platform; - readonly attribute DOMString userAgent; - - readonly attribute boolean onLine; }; +WorkerNavigator implements NavigatorConcurrentHardware; +WorkerNavigator implements NavigatorID; +WorkerNavigator implements NavigatorLanguage; +WorkerNavigator implements NavigatorOnLine; diff --git a/Source/WebCore/page/animation/AnimationBase.cpp b/Source/WebCore/page/animation/AnimationBase.cpp index dd3315ebb..44bfe82a3 100644 --- a/Source/WebCore/page/animation/AnimationBase.cpp +++ b/Source/WebCore/page/animation/AnimationBase.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. * @@ -29,16 +29,18 @@ #include "config.h" #include "AnimationBase.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPrimitiveValue.h" #include "CSSPropertyAnimation.h" #include "CompositeAnimation.h" #include "Document.h" -#include "EventNames.h" #include "FloatConversion.h" +#include "GeometryUtilities.h" #include "Logging.h" #include "RenderBox.h" #include "RenderStyle.h" +#include "RenderView.h" +#include "SpringSolver.h" #include "UnitBezier.h" #include <algorithm> #include <wtf/CurrentTime.h> @@ -68,21 +70,16 @@ static inline double solveStepsFunction(int numSteps, bool stepAtStart, double t return floor(numSteps * t) / numSteps; } -AnimationBase::AnimationBase(const Animation& transition, RenderElement* renderer, CompositeAnimation* compAnim) - : m_animState(AnimationStateNew) - , m_isAccelerated(false) - , m_transformFunctionListValid(false) -#if ENABLE(CSS_FILTERS) - , m_filterFunctionListsMatch(false) -#endif - , m_startTime(0) - , m_pauseTime(-1) - , m_requestedStartTime(0) - , m_totalDuration(-1) - , m_nextIterationDuration(-1) - , m_object(renderer) - , m_animation(const_cast<Animation*>(&transition)) - , m_compAnim(compAnim) +static inline double solveSpringFunction(double mass, double stiffness, double damping, double initialVelocity, double t, double duration) +{ + SpringSolver solver(mass, stiffness, damping, initialVelocity); + return solver.solve(t * duration); +} + +AnimationBase::AnimationBase(const Animation& animation, RenderElement* renderer, CompositeAnimation* compositeAnimation) + : m_object(renderer) + , m_compositeAnimation(compositeAnimation) + , m_animation(const_cast<Animation&>(animation)) { // Compute the total duration if (m_animation->iterationCount() > 0) @@ -91,9 +88,11 @@ AnimationBase::AnimationBase(const Animation& transition, RenderElement* rendere void AnimationBase::setNeedsStyleRecalc(Element* element) { - ASSERT(!element || !element->document().inPageCache()); - if (element) - element->setNeedsStyleRecalc(SyntheticStyleChange); + if (!element || element->document().renderTreeBeingDestroyed()) + return; + + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); } double AnimationBase::duration() const @@ -106,137 +105,164 @@ bool AnimationBase::playStatePlaying() const return m_animation->playState() == AnimPlayStatePlaying; } -bool AnimationBase::animationsMatch(const Animation* anim) const +bool AnimationBase::animationsMatch(const Animation& animation) const { - return m_animation->animationsMatch(anim); + return m_animation->animationsMatch(animation); } #if !LOG_DISABLED -static const char* nameForState(AnimationBase::AnimState state) +static const char* nameForState(AnimationBase::AnimationState state) { switch (state) { - case AnimationBase::AnimationStateNew: return "New"; - case AnimationBase::AnimationStateStartWaitTimer: return "StartWaitTimer"; - case AnimationBase::AnimationStateStartWaitStyleAvailable: return "StartWaitStyleAvailable"; - case AnimationBase::AnimationStateStartWaitResponse: return "StartWaitResponse"; - case AnimationBase::AnimationStateLooping: return "Looping"; - case AnimationBase::AnimationStateEnding: return "Ending"; - case AnimationBase::AnimationStatePausedNew: return "PausedNew"; - case AnimationBase::AnimationStatePausedWaitTimer: return "PausedWaitTimer"; - case AnimationBase::AnimationStatePausedWaitStyleAvailable: return "PausedWaitStyleAvailable"; - case AnimationBase::AnimationStatePausedWaitResponse: return "PausedWaitResponse"; - case AnimationBase::AnimationStatePausedRun: return "PausedRun"; - case AnimationBase::AnimationStateDone: return "Done"; - case AnimationBase::AnimationStateFillingForwards: return "FillingForwards"; + case AnimationBase::AnimationState::New: return "New"; + case AnimationBase::AnimationState::StartWaitTimer: return "StartWaitTimer"; + case AnimationBase::AnimationState::StartWaitStyleAvailable: return "StartWaitStyleAvailable"; + case AnimationBase::AnimationState::StartWaitResponse: return "StartWaitResponse"; + case AnimationBase::AnimationState::Looping: return "Looping"; + case AnimationBase::AnimationState::Ending: return "Ending"; + case AnimationBase::AnimationState::PausedNew: return "PausedNew"; + case AnimationBase::AnimationState::PausedWaitTimer: return "PausedWaitTimer"; + case AnimationBase::AnimationState::PausedWaitStyleAvailable: return "PausedWaitStyleAvailable"; + case AnimationBase::AnimationState::PausedWaitResponse: return "PausedWaitResponse"; + case AnimationBase::AnimationState::PausedRun: return "PausedRun"; + case AnimationBase::AnimationState::Done: return "Done"; + case AnimationBase::AnimationState::FillingForwards: return "FillingForwards"; + } + return ""; +} + +static const char* nameForStateInput(AnimationBase::AnimationStateInput input) +{ + switch (input) { + case AnimationBase::AnimationStateInput::MakeNew: return "MakeNew"; + case AnimationBase::AnimationStateInput::StartAnimation: return "StartAnimation"; + case AnimationBase::AnimationStateInput::RestartAnimation: return "RestartAnimation"; + case AnimationBase::AnimationStateInput::StartTimerFired: return "StartTimerFired"; + case AnimationBase::AnimationStateInput::StyleAvailable: return "StyleAvailable"; + case AnimationBase::AnimationStateInput::StartTimeSet: return "StartTimeSet"; + case AnimationBase::AnimationStateInput::LoopTimerFired: return "LoopTimerFired"; + case AnimationBase::AnimationStateInput::EndTimerFired: return "EndTimerFired"; + case AnimationBase::AnimationStateInput::PauseOverride: return "PauseOverride"; + case AnimationBase::AnimationStateInput::ResumeOverride: return "ResumeOverride"; + case AnimationBase::AnimationStateInput::PlayStateRunning: return "PlayStateRunning"; + case AnimationBase::AnimationStateInput::PlayStatePaused: return "PlayStatePaused"; + case AnimationBase::AnimationStateInput::EndAnimation: return "EndAnimation"; } return ""; } #endif -void AnimationBase::updateStateMachine(AnimStateInput input, double param) +void AnimationBase::updateStateMachine(AnimationStateInput input, double param) { - if (!m_compAnim) + if (!m_compositeAnimation) return; - // If we get AnimationStateInputRestartAnimation then we force a new animation, regardless of state. - if (input == AnimationStateInputMakeNew) { - if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); - LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); - m_animState = AnimationStateNew; - m_startTime = 0; - m_pauseTime = -1; + // If we get AnimationStateInput::RestartAnimation then we force a new animation, regardless of state. + if (input == AnimationStateInput::MakeNew) { + if (m_animationState == AnimationState::StartWaitStyleAvailable) + m_compositeAnimation->animationController().removeFromAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + m_startTime = std::nullopt; + m_pauseTime = std::nullopt; m_requestedStartTime = 0; - m_nextIterationDuration = -1; + m_nextIterationDuration = std::nullopt; endAnimation(); return; } - if (input == AnimationStateInputRestartAnimation) { - if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); - LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); - m_animState = AnimationStateNew; - m_startTime = 0; - m_pauseTime = -1; + if (input == AnimationStateInput::RestartAnimation) { + if (m_animationState == AnimationState::StartWaitStyleAvailable) + m_compositeAnimation->animationController().removeFromAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + m_startTime = std::nullopt; + m_pauseTime = std::nullopt; m_requestedStartTime = 0; - m_nextIterationDuration = -1; + m_nextIterationDuration = std::nullopt; endAnimation(); if (!paused()) - updateStateMachine(AnimationStateInputStartAnimation, -1); + updateStateMachine(AnimationStateInput::StartAnimation, -1); return; } - if (input == AnimationStateInputEndAnimation) { - if (m_animState == AnimationStateStartWaitStyleAvailable) - m_compAnim->animationController()->removeFromAnimationsWaitingForStyle(this); - LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState)); - m_animState = AnimationStateDone; + if (input == AnimationStateInput::EndAnimation) { + if (m_animationState == AnimationState::StartWaitStyleAvailable) + m_compositeAnimation->animationController().removeFromAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animationState)); + m_animationState = AnimationState::Done; endAnimation(); return; } - if (input == AnimationStateInputPauseOverride) { - if (m_animState == AnimationStateStartWaitResponse) { - // If we are in AnimationStateStartWaitResponse, the animation will get canceled before + if (input == AnimationStateInput::PauseOverride) { + if (m_animationState == AnimationState::StartWaitResponse) { + // If we are in AnimationState::StartWaitResponse, the animation will get canceled before // we get a response, so move to the next state. endAnimation(); - updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime()); } return; } - if (input == AnimationStateInputResumeOverride) { - if (m_animState == AnimationStateLooping || m_animState == AnimationStateEnding) { + if (input == AnimationStateInput::ResumeOverride) { + if (m_animationState == AnimationState::Looping || m_animationState == AnimationState::Ending) { // Start the animation - startAnimation(beginAnimationUpdateTime() - m_startTime); + startAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); } return; } // Execute state machine - switch (m_animState) { - case AnimationStateNew: - ASSERT(input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning || input == AnimationStateInputPlayStatePaused); - if (input == AnimationStateInputStartAnimation || input == AnimationStateInputPlayStateRunning) { + switch (m_animationState) { + case AnimationState::New: + ASSERT(input == AnimationStateInput::StartAnimation || input == AnimationStateInput::PlayStateRunning || input == AnimationStateInput::PlayStatePaused); + + if (input == AnimationStateInput::StartAnimation || input == AnimationStateInput::PlayStateRunning) { m_requestedStartTime = beginAnimationUpdateTime(); - LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitTimer; + LOG(Animations, "%p AnimationState %s -> StartWaitTimer", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitTimer; } else { // We are pausing before we even started. - LOG(Animations, "%p AnimationState %s -> AnimationStatePausedNew", this, nameForState(m_animState)); - m_animState = AnimationStatePausedNew; + LOG(Animations, "%p AnimationState %s -> AnimationState::PausedNew", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedNew; + m_pauseTime = std::nullopt; } + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) + m_compositeAnimation->animationController().addToAnimationsDependentOnScroll(this); +#endif break; - case AnimationStateStartWaitTimer: - ASSERT(input == AnimationStateInputStartTimerFired || input == AnimationStateInputPlayStatePaused); + case AnimationState::StartWaitTimer: + ASSERT(input == AnimationStateInput::StartTimerFired || input == AnimationStateInput::PlayStatePaused); - if (input == AnimationStateInputStartTimerFired) { + if (input == AnimationStateInput::StartTimerFired) { ASSERT(param >= 0); // Start timer has fired, tell the animation to start and wait for it to respond with start time - LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitStyleAvailable; - m_compAnim->animationController()->addToAnimationsWaitingForStyle(this); + LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::StartWaitStyleAvailable; + m_compositeAnimation->animationController().addToAnimationsWaitingForStyle(this); // Trigger a render so we can start the animation if (m_object && m_object->element()) - m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element()); + m_compositeAnimation->animationController().addElementChangeToDispatch(*m_object->element()); } else { ASSERT(!paused()); // We're waiting for the start timer to fire and we got a pause. Cancel the timer, pause and wait m_pauseTime = beginAnimationUpdateTime(); - LOG(Animations, "%p AnimationState %s -> PausedWaitTimer", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitTimer; + LOG(Animations, "%p AnimationState %s -> PausedWaitTimer", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitTimer; } break; - case AnimationStateStartWaitStyleAvailable: - ASSERT(input == AnimationStateInputStyleAvailable || input == AnimationStateInputPlayStatePaused); + case AnimationState::StartWaitStyleAvailable: + ASSERT(input == AnimationStateInput::StyleAvailable || input == AnimationStateInput::PlayStatePaused); - if (input == AnimationStateInputStyleAvailable) { + if (input == AnimationStateInput::StyleAvailable) { // Start timer has fired, tell the animation to start and wait for it to respond with start time - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::StartWaitResponse; overrideAnimations(); @@ -244,10 +270,10 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) if (overridden()) { // We won't try to start accelerated animations if we are overridden and // just move on to the next state. - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitResponse; m_isAccelerated = false; - updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime()); } else { double timeOffset = 0; // If the value for 'animation-delay' is negative then the animation appears to have started in the past. @@ -255,27 +281,29 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) timeOffset = -m_animation->delay(); bool started = startAnimation(timeOffset); - m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); + m_compositeAnimation->animationController().addToAnimationsWaitingForStartTimeResponse(this, started); m_isAccelerated = started; } } else { // We're waiting for the style to be available and we got a pause. Pause and wait m_pauseTime = beginAnimationUpdateTime(); - LOG(Animations, "%p AnimationState %s -> PausedWaitStyleAvailable", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitStyleAvailable; + LOG(Animations, "%p AnimationState %s -> PausedWaitStyleAvailable", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitStyleAvailable; } break; - case AnimationStateStartWaitResponse: - ASSERT(input == AnimationStateInputStartTimeSet || input == AnimationStateInputPlayStatePaused); + case AnimationState::StartWaitResponse: + ASSERT(input == AnimationStateInput::StartTimeSet || input == AnimationStateInput::PlayStatePaused); + + if (input == AnimationStateInput::StartTimeSet) { + ASSERT(param > -0.001); // Sometimes Core Animation gives us a beginTime slightly into the future. + LOG(Animations, "%p AnimationState %s -> StartTimeSet (time is %f)", this, nameForState(m_animationState), param); - if (input == AnimationStateInputStartTimeSet) { - ASSERT(param >= 0); // We have a start time, set it, unless the startTime is already set - if (m_startTime <= 0) { + if (!m_startTime) { m_startTime = param; // If the value for 'animation-delay' is negative then the animation appears to have started in the past. if (m_animation->delay() < 0) - m_startTime += m_animation->delay(); + m_startTime = m_startTime.value() + m_animation->delay(); } // Now that we know the start time, fire the start event. @@ -286,21 +314,23 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) // Dispatch updateStyleIfNeeded so we can start the animation if (m_object && m_object->element()) - m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element()); + m_compositeAnimation->animationController().addElementChangeToDispatch(*m_object->element()); } else { // We are pausing while waiting for a start response. Cancel the animation and wait. When // we unpause, we will act as though the start timer just fired m_pauseTime = beginAnimationUpdateTime(); - pauseAnimation(beginAnimationUpdateTime() - m_startTime); - LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitResponse; + pauseAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitResponse; } break; - case AnimationStateLooping: - ASSERT(input == AnimationStateInputLoopTimerFired || input == AnimationStateInputPlayStatePaused); + case AnimationState::Looping: + ASSERT(input == AnimationStateInput::LoopTimerFired || input == AnimationStateInput::PlayStatePaused); - if (input == AnimationStateInputLoopTimerFired) { + if (input == AnimationStateInput::LoopTimerFired) { ASSERT(param >= 0); + LOG(Animations, "%p AnimationState %s -> LoopTimerFired (time is %f)", this, nameForState(m_animationState), param); + // Loop timer fired, loop again or end. onAnimationIteration(param); @@ -309,178 +339,198 @@ void AnimationBase::updateStateMachine(AnimStateInput input, double param) } else { // We are pausing while running. Cancel the animation and wait m_pauseTime = beginAnimationUpdateTime(); - pauseAnimation(beginAnimationUpdateTime() - m_startTime); - LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); - m_animState = AnimationStatePausedRun; + pauseAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedRun; } break; - case AnimationStateEnding: + case AnimationState::Ending: #if !LOG_DISABLED - if (input != AnimationStateInputEndTimerFired && input != AnimationStateInputPlayStatePaused) - LOG_ERROR("State is AnimationStateEnding, but input is not AnimationStateInputEndTimerFired or AnimationStateInputPlayStatePaused. It is %d.", input); + if (input != AnimationStateInput::EndTimerFired && input != AnimationStateInput::PlayStatePaused) + LOG_ERROR("State is AnimationState::Ending, but input is not AnimationStateInput::EndTimerFired or AnimationStateInput::PlayStatePaused. It is %s.", nameForStateInput(input)); #endif - if (input == AnimationStateInputEndTimerFired) { - + if (input == AnimationStateInput::EndTimerFired) { ASSERT(param >= 0); // End timer fired, finish up onAnimationEnd(param); - LOG(Animations, "%p AnimationState %s -> Done", this, nameForState(m_animState)); - m_animState = AnimationStateDone; + LOG(Animations, "%p AnimationState %s -> Done (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::Done; if (m_object) { if (m_animation->fillsForwards()) { - LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animState)); - m_animState = AnimationStateFillingForwards; + LOG(Animations, "%p AnimationState %s -> FillingForwards", this, nameForState(m_animationState)); + m_animationState = AnimationState::FillingForwards; } else resumeOverriddenAnimations(); // Fire off another style change so we can set the final value if (m_object->element()) - m_compAnim->animationController()->addElementChangeToDispatch(*m_object->element()); + m_compositeAnimation->animationController().addElementChangeToDispatch(*m_object->element()); } } else { // We are pausing while running. Cancel the animation and wait m_pauseTime = beginAnimationUpdateTime(); - pauseAnimation(beginAnimationUpdateTime() - m_startTime); - LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); - m_animState = AnimationStatePausedRun; + pauseAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedRun; } // |this| may be deleted here break; - case AnimationStatePausedWaitTimer: - ASSERT(input == AnimationStateInputPlayStateRunning); + case AnimationState::PausedWaitTimer: + ASSERT(input == AnimationStateInput::PlayStateRunning); ASSERT(paused()); // Update the times - m_startTime += beginAnimationUpdateTime() - m_pauseTime; - m_pauseTime = -1; + m_startTime = m_startTime.value() + beginAnimationUpdateTime() - m_pauseTime.value_or(0); + m_pauseTime = std::nullopt; // we were waiting for the start timer to fire, go back and wait again - LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animState)); - m_animState = AnimationStateNew; - updateStateMachine(AnimationStateInputStartAnimation, 0); + LOG(Animations, "%p AnimationState %s -> New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + updateStateMachine(AnimationStateInput::StartAnimation, 0); break; - case AnimationStatePausedNew: - case AnimationStatePausedWaitResponse: - case AnimationStatePausedWaitStyleAvailable: - case AnimationStatePausedRun: + case AnimationState::PausedNew: + case AnimationState::PausedWaitResponse: + case AnimationState::PausedWaitStyleAvailable: + case AnimationState::PausedRun: // We treat these two cases the same. The only difference is that, when we are in - // AnimationStatePausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. - // When the AnimationStateInputStartTimeSet comes in and we were in AnimationStatePausedRun, we will notice + // AnimationState::PausedWaitResponse, we don't yet have a valid startTime, so we send 0 to startAnimation. + // When the AnimationStateInput::StartTimeSet comes in and we were in AnimationState::PausedRun, we will notice // that we have already set the startTime and will ignore it. - ASSERT(input == AnimationStateInputPlayStateRunning || input == AnimationStateInputStartTimeSet || input == AnimationStateInputStyleAvailable || input == AnimationStateInputStartAnimation); + ASSERT(input == AnimationStateInput::PlayStatePaused || input == AnimationStateInput::PlayStateRunning || input == AnimationStateInput::StartTimeSet || input == AnimationStateInput::StyleAvailable || input == AnimationStateInput::StartAnimation); ASSERT(paused()); - if (input == AnimationStateInputPlayStateRunning) { - if (m_animState == AnimationStatePausedNew) { + if (input == AnimationStateInput::PlayStateRunning) { + if (m_animationState == AnimationState::PausedNew) { // We were paused before we even started, and now we're supposed // to start, so jump back to the New state and reset. - LOG(Animations, "%p AnimationState %s -> AnimationStateNew", this, nameForState(m_animState)); - m_animState = AnimationStateNew; + LOG(Animations, "%p AnimationState %s -> AnimationState::New", this, nameForState(m_animationState)); + m_animationState = AnimationState::New; + m_pauseTime = std::nullopt; updateStateMachine(input, param); break; } // Update the times - if (m_animState == AnimationStatePausedRun) - m_startTime += beginAnimationUpdateTime() - m_pauseTime; + if (m_animationState == AnimationState::PausedRun) + m_startTime = m_startTime.value() + beginAnimationUpdateTime() - m_pauseTime.value_or(0); else m_startTime = 0; - m_pauseTime = -1; - if (m_animState == AnimationStatePausedWaitStyleAvailable) { - LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitStyleAvailable; + m_pauseTime = std::nullopt; + + if (m_animationState == AnimationState::PausedWaitStyleAvailable) { + LOG(Animations, "%p AnimationState %s -> StartWaitStyleAvailable", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitStyleAvailable; } else { // We were either running or waiting for a begin time response from the animation. // Either way we need to restart the animation (possibly with an offset if we // had already been running) and wait for it to start. - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitResponse; // Start the animation if (overridden()) { // We won't try to start accelerated animations if we are overridden and // just move on to the next state. - updateStateMachine(AnimationStateInputStartTimeSet, beginAnimationUpdateTime()); + updateStateMachine(AnimationStateInput::StartTimeSet, beginAnimationUpdateTime()); m_isAccelerated = true; } else { - bool started = startAnimation(beginAnimationUpdateTime() - m_startTime); - m_compAnim->animationController()->addToAnimationsWaitingForStartTimeResponse(this, started); + bool started = startAnimation(beginAnimationUpdateTime() - m_startTime.value_or(0)); + m_compositeAnimation->animationController().addToAnimationsWaitingForStartTimeResponse(this, started); m_isAccelerated = started; } } break; } - if (input == AnimationStateInputStartTimeSet) { - ASSERT(m_animState == AnimationStatePausedWaitResponse); + if (input == AnimationStateInput::StartTimeSet) { + ASSERT(m_animationState == AnimationState::PausedWaitResponse); // We are paused but we got the callback that notifies us that an accelerated animation started. // We ignore the start time and just move into the paused-run state. - LOG(Animations, "%p AnimationState %s -> PausedRun", this, nameForState(m_animState)); - m_animState = AnimationStatePausedRun; - ASSERT(m_startTime == 0); + LOG(Animations, "%p AnimationState %s -> PausedRun (time is %f)", this, nameForState(m_animationState), param); + m_animationState = AnimationState::PausedRun; + ASSERT(!m_startTime); m_startTime = param; - m_pauseTime += m_startTime; + m_pauseTime = m_pauseTime.value_or(0) + param; break; } - ASSERT(m_animState == AnimationStatePausedWaitStyleAvailable); + ASSERT(m_animationState == AnimationState::PausedNew || m_animationState == AnimationState::PausedWaitStyleAvailable); // We are paused but we got the callback that notifies us that style has been updated. - // We move to the AnimationStatePausedWaitResponse state - LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStatePausedWaitResponse; + // We move to the AnimationState::PausedWaitResponse state + LOG(Animations, "%p AnimationState %s -> PausedWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::PausedWaitResponse; overrideAnimations(); break; - case AnimationStateFillingForwards: - case AnimationStateDone: + case AnimationState::FillingForwards: + case AnimationState::Done: // We're done. Stay in this state until we are deleted break; } } - + void AnimationBase::fireAnimationEventsIfNeeded() { - if (!m_compAnim) + if (!m_compositeAnimation) return; // If we are waiting for the delay time to expire and it has, go to the next state - if (m_animState != AnimationStateStartWaitTimer && m_animState != AnimationStateLooping && m_animState != AnimationStateEnding) + if (m_animationState != AnimationState::StartWaitTimer && m_animationState != AnimationState::Looping && m_animationState != AnimationState::Ending) return; // We have to make sure to keep a ref to the this pointer, because it could get destroyed // during an animation callback that might get called. Since the owner is a CompositeAnimation // and it ref counts this object, we will keep a ref to that instead. That way the AnimationBase // can still access the resources of its CompositeAnimation as needed. - Ref<AnimationBase> protect(*this); - Ref<CompositeAnimation> protectCompositeAnimation(*m_compAnim); + Ref<AnimationBase> protectedThis(*this); + Ref<CompositeAnimation> protectCompositeAnimation(*m_compositeAnimation); // Check for start timeout - if (m_animState == AnimationStateStartWaitTimer) { + if (m_animationState == AnimationState::StartWaitTimer) { +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) { + if (m_object) { + float offset = m_compositeAnimation->animationController().scrollPosition(); + auto& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger()); + if (offset > scrollTrigger.startValue().value()) + updateStateMachine(AnimationStateInput::StartTimerFired, 0); + } + + return; + } +#endif if (beginAnimationUpdateTime() - m_requestedStartTime >= m_animation->delay()) - updateStateMachine(AnimationStateInputStartTimerFired, 0); + updateStateMachine(AnimationStateInput::StartTimerFired, 0); return; } - - double elapsedDuration = beginAnimationUpdateTime() - m_startTime; + + double elapsedDuration = beginAnimationUpdateTime() - m_startTime.value_or(0); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + // If we are a triggered animation that depends on scroll, our elapsed + // time is determined by the scroll position. + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) + elapsedDuration = getElapsedTime(); +#endif + // FIXME: we need to ensure that elapsedDuration is never < 0. If it is, this suggests that // we had a recalcStyle() outside of beginAnimationUpdate()/endAnimationUpdate(). // Also check in getTimeToNextEvent(). elapsedDuration = std::max(elapsedDuration, 0.0); // Check for end timeout - if (m_totalDuration >= 0 && elapsedDuration >= m_totalDuration) { - // We may still be in AnimationStateLooping if we've managed to skip a + if (m_totalDuration && elapsedDuration >= m_totalDuration.value()) { + // We may still be in AnimationState::Looping if we've managed to skip a // whole iteration, in which case we should jump to the end state. - LOG(Animations, "%p AnimationState %s -> Ending", this, nameForState(m_animState)); - m_animState = AnimationStateEnding; + LOG(Animations, "%p AnimationState %s -> Ending", this, nameForState(m_animationState)); + m_animationState = AnimationState::Ending; // Fire an end event - updateStateMachine(AnimationStateInputEndTimerFired, m_totalDuration); + updateStateMachine(AnimationStateInput::EndTimerFired, m_totalDuration.value()); } else { // Check for iteration timeout - if (m_nextIterationDuration < 0) { + if (!m_nextIterationDuration) { // Hasn't been set yet, set it double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); m_nextIterationDuration = elapsedDuration + durationLeft; @@ -488,40 +538,51 @@ void AnimationBase::fireAnimationEventsIfNeeded() if (elapsedDuration >= m_nextIterationDuration) { // Set to the next iteration - double previous = m_nextIterationDuration; + double previous = m_nextIterationDuration.value(); double durationLeft = m_animation->duration() - fmod(elapsedDuration, m_animation->duration()); m_nextIterationDuration = elapsedDuration + durationLeft; // Send the event - updateStateMachine(AnimationStateInputLoopTimerFired, previous); + updateStateMachine(AnimationStateInput::LoopTimerFired, previous); } } } void AnimationBase::updatePlayState(EAnimPlayState playState) { - if (!m_compAnim) + if (!m_compositeAnimation) return; // When we get here, we can have one of 4 desired states: running, paused, suspended, paused & suspended. // The state machine can be in one of two states: running, paused. // Set the state machine to the desired state. - bool pause = playState == AnimPlayStatePaused || m_compAnim->isSuspended(); + bool pause = playState == AnimPlayStatePaused || m_compositeAnimation->isSuspended(); if (pause == paused() && !isNew()) return; - updateStateMachine(pause ? AnimationStateInputPlayStatePaused : AnimationStateInputPlayStateRunning, -1); + updateStateMachine(pause ? AnimationStateInput::PlayStatePaused : AnimationStateInput::PlayStateRunning, -1); } double AnimationBase::timeToNextService() { // Returns the time at which next service is required. -1 means no service is required. 0 means // service is required now, and > 0 means service is required that many seconds in the future. - if (paused() || isNew() || m_animState == AnimationStateFillingForwards) + if (paused() || isNew() || postActive() || fillingForwards()) return -1; - if (m_animState == AnimationStateStartWaitTimer) { + if (m_animationState == AnimationState::StartWaitTimer) { +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger()->isScrollAnimationTrigger()) { + if (m_object) { + float currentScrollPosition = m_object->view().frameView().scrollPositionForFixedPosition().y().toFloat(); + auto& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger()); + if (currentScrollPosition >= scrollTrigger.startValue().value() && (!scrollTrigger.hasEndValue() || currentScrollPosition <= scrollTrigger.endValue().value())) + return 0; + } + return -1; + } +#endif double timeFromNow = m_animation->delay() - (beginAnimationUpdateTime() - m_requestedStartTime); return std::max(timeFromNow, 0.0); } @@ -566,39 +627,45 @@ double AnimationBase::fractionalTime(double scale, double elapsedTime, double of return fractionalTime; } -double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const +double AnimationBase::progress(double scale, double offset, const TimingFunction* timingFunction) const { if (preActive()) return 0; + if (postActive()) + return 1; + double elapsedTime = getElapsedTime(); - double dur = m_animation->duration(); + double duration = m_animation->duration(); if (m_animation->iterationCount() > 0) - dur *= m_animation->iterationCount(); + duration *= m_animation->iterationCount(); - if (postActive() || !m_animation->duration()) - return 1.0; + if (fillingForwards()) + elapsedTime = duration; - if (m_animation->iterationCount() > 0 && elapsedTime >= dur) { - const int integralIterationCount = static_cast<int>(m_animation->iterationCount()); - const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount; - return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0; - } + double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); - const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); + if (m_animation->iterationCount() > 0 && elapsedTime >= duration) { + if (WTF::isIntegral(fractionalTime)) + return fractionalTime; + } - if (!tf) - tf = m_animation->timingFunction().get(); + if (!timingFunction) + timingFunction = m_animation->timingFunction(); - switch (tf->type()) { + switch (timingFunction->type()) { case TimingFunction::CubicBezierFunction: { - const CubicBezierTimingFunction* function = static_cast<const CubicBezierTimingFunction*>(tf); - return solveCubicBezierFunction(function->x1(), function->y1(), function->x2(), function->y2(), fractionalTime, m_animation->duration()); + auto& function = *static_cast<const CubicBezierTimingFunction*>(timingFunction); + return solveCubicBezierFunction(function.x1(), function.y1(), function.x2(), function.y2(), fractionalTime, m_animation->duration()); } case TimingFunction::StepsFunction: { - const StepsTimingFunction* stepsTimingFunction = static_cast<const StepsTimingFunction*>(tf); - return solveStepsFunction(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart(), fractionalTime); + auto& function = *static_cast<const StepsTimingFunction*>(timingFunction); + return solveStepsFunction(function.numberOfSteps(), function.stepAtStart(), fractionalTime); + } + case TimingFunction::SpringFunction: { + auto& function = *static_cast<const SpringTimingFunction*>(timingFunction); + return solveSpringFunction(function.mass(), function.stiffness(), function.damping(), function.initialVelocity(), fractionalTime, m_animation->duration()); } case TimingFunction::LinearFunction: return fractionalTime; @@ -611,16 +678,16 @@ double AnimationBase::progress(double scale, double offset, const TimingFunction void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const { // Decide when the end or loop event needs to fire - const double elapsedDuration = std::max(beginAnimationUpdateTime() - m_startTime, 0.0); + const double elapsedDuration = std::max(beginAnimationUpdateTime() - m_startTime.value_or(0), 0.0); double durationLeft = 0; - double nextIterationTime = m_totalDuration; + double nextIterationTime = m_totalDuration.value_or(0); - if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) { + if (!m_totalDuration || elapsedDuration < m_totalDuration.value()) { durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0; nextIterationTime = elapsedDuration + durationLeft; } - if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) { + if (!m_totalDuration || nextIterationTime < m_totalDuration.value()) { // We are not at the end yet ASSERT(nextIterationTime > 0); isLooping = true; @@ -637,52 +704,70 @@ void AnimationBase::goIntoEndingOrLoopingState() double t; bool isLooping; getTimeToNextEvent(t, isLooping); - LOG(Animations, "%p AnimationState %s -> %s", this, nameForState(m_animState), isLooping ? "Looping" : "Ending"); - m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding; + LOG(Animations, "%p AnimationState %s -> %s", this, nameForState(m_animationState), isLooping ? "Looping" : "Ending"); + m_animationState = isLooping ? AnimationState::Looping : AnimationState::Ending; } void AnimationBase::freezeAtTime(double t) { - if (!m_compAnim) + if (!m_compositeAnimation) return; if (!m_startTime) { // If we haven't started yet, make it as if we started. - LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animState)); - m_animState = AnimationStateStartWaitResponse; + LOG(Animations, "%p AnimationState %s -> StartWaitResponse", this, nameForState(m_animationState)); + m_animationState = AnimationState::StartWaitResponse; onAnimationStartResponse(monotonicallyIncreasingTime()); } - ASSERT(m_startTime); // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time. + ASSERT(m_startTime); // If m_startTime is zero, we haven't started yet, so we'll get a bad pause time. if (t <= m_animation->delay()) - m_pauseTime = m_startTime; + m_pauseTime = m_startTime.value_or(0); else - m_pauseTime = m_startTime + t - m_animation->delay(); + m_pauseTime = m_startTime.value_or(0) + t - m_animation->delay(); -#if USE(ACCELERATED_COMPOSITING) if (m_object && m_object->isComposited()) - toRenderBoxModelObject(m_object)->suspendAnimations(m_pauseTime); -#endif + downcast<RenderBoxModelObject>(*m_object).suspendAnimations(m_pauseTime.value()); } double AnimationBase::beginAnimationUpdateTime() const { - if (!m_compAnim) + if (!m_compositeAnimation) return 0; - return m_compAnim->animationController()->beginAnimationUpdateTime(); + return m_compositeAnimation->animationController().beginAnimationUpdateTime(); } double AnimationBase::getElapsedTime() const { - if (paused()) - return m_pauseTime - m_startTime; - if (m_startTime <= 0) +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) { + auto& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger()); + if (scrollTrigger.hasEndValue() && m_object) { + float offset = m_compositeAnimation->animationController().scrollPosition(); + float startValue = scrollTrigger.startValue().value(); + if (offset < startValue) + return 0; + float endValue = scrollTrigger.endValue().value(); + if (offset > endValue) + return m_animation->duration(); + return m_animation->duration() * (offset - startValue) / (endValue - startValue); + } + } +#endif + + if (paused()) { + double delayOffset = (!m_startTime && m_animation->delay() < 0) ? m_animation->delay() : 0; + return m_pauseTime.value_or(0) - m_startTime.value_or(0) - delayOffset; + } + + if (!m_startTime) return 0; - if (postActive()) - return 1; - return beginAnimationUpdateTime() - m_startTime; + if (postActive() || fillingForwards()) + return m_totalDuration.value_or(0); + + return beginAnimationUpdateTime() - m_startTime.value_or(0); } void AnimationBase::setElapsedTime(double time) @@ -701,4 +786,77 @@ void AnimationBase::pause() // FIXME: implement this method } +static bool containsRotation(const Vector<RefPtr<TransformOperation>>& operations) +{ + for (const auto& operation : operations) { + if (operation->type() == TransformOperation::ROTATE) + return true; + } + return false; +} + +bool AnimationBase::computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const +{ + FloatRect floatBounds = bounds; + FloatPoint transformOrigin; + + bool applyTransformOrigin = containsRotation(style.transform().operations()) || style.transform().affectedByTransformOrigin(); + if (applyTransformOrigin) { + float offsetX = style.transformOriginX().isPercent() ? rendererBox.x() : 0; + float offsetY = style.transformOriginY().isPercent() ? rendererBox.y() : 0; + + transformOrigin.setX(floatValueForLength(style.transformOriginX(), rendererBox.width()) + offsetX); + transformOrigin.setY(floatValueForLength(style.transformOriginY(), rendererBox.height()) + offsetY); + // Ignore transformOriginZ because we'll bail if we encounter any 3D transforms. + + floatBounds.moveBy(-transformOrigin); + } + + for (const auto& operation : style.transform().operations()) { + if (operation->type() == TransformOperation::ROTATE) { + // For now, just treat this as a full rotation. This could take angle into account to reduce inflation. + floatBounds = boundsOfRotatingRect(floatBounds); + } else { + TransformationMatrix transform; + operation->apply(transform, rendererBox.size()); + if (!transform.isAffine()) + return false; + + if (operation->type() == TransformOperation::MATRIX || operation->type() == TransformOperation::MATRIX_3D) { + TransformationMatrix::Decomposed2Type toDecomp; + transform.decompose2(toDecomp); + // Any rotation prevents us from using a simple start/end rect union. + if (toDecomp.angle) + return false; + } + + floatBounds = transform.mapRect(floatBounds); + } + } + + if (applyTransformOrigin) + floatBounds.moveBy(transformOrigin); + + bounds = LayoutRect(floatBounds); + return true; +} + +bool AnimationBase::computeTransformedExtentViaMatrix(const FloatRect& rendererBox, const RenderStyle& style, LayoutRect& bounds) const +{ + TransformationMatrix transform; + style.applyTransform(transform, rendererBox, RenderStyle::IncludeTransformOrigin); + if (!transform.isAffine()) + return false; + + TransformationMatrix::Decomposed2Type fromDecomp; + transform.decompose2(fromDecomp); + // Any rotation prevents us from using a simple start/end rect union. + if (fromDecomp.angle) + return false; + + bounds = LayoutRect(transform.mapRect(bounds)); + return true; + +} + } // namespace WebCore diff --git a/Source/WebCore/page/animation/AnimationBase.h b/Source/WebCore/page/animation/AnimationBase.h index 51673374d..f3387d2ef 100644 --- a/Source/WebCore/page/animation/AnimationBase.h +++ b/Source/WebCore/page/animation/AnimationBase.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,23 +26,19 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AnimationBase_h -#define AnimationBase_h +#pragma once #include "Animation.h" #include "CSSPropertyNames.h" #include "RenderStyleConstants.h" -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include <wtf/RefCounted.h> #include <wtf/text/AtomicString.h> namespace WebCore { -class AnimationBase; -class AnimationController; class CompositeAnimation; class Element; +class FloatRect; +class LayoutRect; class RenderElement; class RenderStyle; class TimingFunction; @@ -50,7 +46,7 @@ class TimingFunction; class AnimationBase : public RefCounted<AnimationBase> { friend class CompositeAnimation; friend class CSSPropertyAnimation; - + WTF_MAKE_FAST_ALLOCATED; public: AnimationBase(const Animation& transition, RenderElement*, CompositeAnimation*); virtual ~AnimationBase() { } @@ -58,9 +54,9 @@ public: RenderElement* renderer() const { return m_object; } void clear() { - endAnimation(); - m_object = 0; - m_compAnim = 0; + endAnimation(); + m_object = nullptr; + m_compositeAnimation = nullptr; } double duration() const; @@ -68,106 +64,129 @@ public: // Animations and Transitions go through the states below. When entering the STARTED state // the animation is started. This may or may not require deferred response from the animator. // If so, we stay in this state until that response is received (and it returns the start time). - // Otherwise, we use the current time as the start time and go immediately to AnimationStateLooping - // or AnimationStateEnding. - enum AnimState { - AnimationStateNew, // animation just created, animation not running yet - AnimationStateStartWaitTimer, // start timer running, waiting for fire - AnimationStateStartWaitStyleAvailable, // waiting for style setup so we can start animations - AnimationStateStartWaitResponse, // animation started, waiting for response - AnimationStateLooping, // response received, animation running, loop timer running, waiting for fire - AnimationStateEnding, // received, animation running, end timer running, waiting for fire - AnimationStatePausedNew, // in pause mode when animation was created - AnimationStatePausedWaitTimer, // in pause mode when animation started - AnimationStatePausedWaitStyleAvailable, // in pause mode when waiting for style setup - AnimationStatePausedWaitResponse, // animation paused when in STARTING state - AnimationStatePausedRun, // animation paused when in LOOPING or ENDING state - AnimationStateDone, // end timer fired, animation finished and removed - AnimationStateFillingForwards // animation has ended and is retaining its final value + // Otherwise, we use the current time as the start time and go immediately to AnimationState::Looping + // or AnimationState::Ending. + enum class AnimationState { + New, // animation just created, animation not running yet + StartWaitTimer, // start timer running, waiting for fire + StartWaitStyleAvailable, // waiting for style setup so we can start animations + StartWaitResponse, // animation started, waiting for response + Looping, // response received, animation running, loop timer running, waiting for fire + Ending, // received, animation running, end timer running, waiting for fire + PausedNew, // in pause mode when animation was created + PausedWaitTimer, // in pause mode when animation started + PausedWaitStyleAvailable, // in pause mode when waiting for style setup + PausedWaitResponse, // animation paused when in STARTING state + PausedRun, // animation paused when in LOOPING or ENDING state + Done, // end timer fired, animation finished and removed + FillingForwards // animation has ended and is retaining its final value }; - enum AnimStateInput { - AnimationStateInputMakeNew, // reset back to new from any state - AnimationStateInputStartAnimation, // animation requests a start - AnimationStateInputRestartAnimation, // force a restart from any state - AnimationStateInputStartTimerFired, // start timer fired - AnimationStateInputStyleAvailable, // style is setup, ready to start animating - AnimationStateInputStartTimeSet, // m_startTime was set - AnimationStateInputLoopTimerFired, // loop timer fired - AnimationStateInputEndTimerFired, // end timer fired - AnimationStateInputPauseOverride, // pause an animation due to override - AnimationStateInputResumeOverride, // resume an overridden animation - AnimationStateInputPlayStateRunning, // play state paused -> running - AnimationStateInputPlayStatePaused, // play state running -> paused - AnimationStateInputEndAnimation // force an end from any state + enum class AnimationStateInput { + MakeNew, // reset back to new from any state + StartAnimation, // animation requests a start + RestartAnimation, // force a restart from any state + StartTimerFired, // start timer fired + StyleAvailable, // style is setup, ready to start animating + StartTimeSet, // m_startTime was set + LoopTimerFired, // loop timer fired + EndTimerFired, // end timer fired + PauseOverride, // pause an animation due to override + ResumeOverride, // resume an overridden animation + PlayStateRunning, // play state paused -> running + PlayStatePaused, // play state running -> paused + EndAnimation // force an end from any state }; - // Called when animation is in AnimationStateNew to start animation - void updateStateMachine(AnimStateInput, double param); + // Called when animation is in AnimationState::New to start animation + void updateStateMachine(AnimationStateInput, double param); // Animation has actually started, at passed time void onAnimationStartResponse(double startTime) { - updateStateMachine(AnimationBase::AnimationStateInputStartTimeSet, startTime); + updateStateMachine(AnimationStateInput::StartTimeSet, startTime); } // Called to change to or from paused state void updatePlayState(EAnimPlayState); bool playStatePlaying() const; - bool waitingToStart() const { return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStatePausedNew; } + bool waitingToStart() const { return m_animationState == AnimationState::New || m_animationState == AnimationState::StartWaitTimer || m_animationState == AnimationState::PausedNew; } bool preActive() const { - return m_animState == AnimationStateNew || m_animState == AnimationStateStartWaitTimer || m_animState == AnimationStateStartWaitStyleAvailable || m_animState == AnimationStateStartWaitResponse; + return m_animationState == AnimationState::New || m_animationState == AnimationState::StartWaitTimer || m_animationState == AnimationState::StartWaitStyleAvailable || m_animationState == AnimationState::StartWaitResponse; } - bool postActive() const { return m_animState == AnimationStateDone; } + bool postActive() const { return m_animationState == AnimationState::Done; } + bool fillingForwards() const { return m_animationState == AnimationState::FillingForwards; } bool active() const { return !postActive() && !preActive(); } bool running() const { return !isNew() && !postActive(); } - bool paused() const { return m_pauseTime >= 0 || m_animState == AnimationStatePausedNew; } - bool isNew() const { return m_animState == AnimationStateNew || m_animState == AnimationStatePausedNew; } - bool waitingForStartTime() const { return m_animState == AnimationStateStartWaitResponse; } - bool waitingForStyleAvailable() const { return m_animState == AnimationStateStartWaitStyleAvailable; } + bool paused() const { return m_pauseTime || m_animationState == AnimationState::PausedNew; } + bool inPausedState() const { return m_animationState >= AnimationState::PausedNew && m_animationState <= AnimationState::PausedRun; } + bool isNew() const { return m_animationState == AnimationState::New || m_animationState == AnimationState::PausedNew; } + bool waitingForStartTime() const { return m_animationState == AnimationState::StartWaitResponse; } + bool waitingForStyleAvailable() const { return m_animationState == AnimationState::StartWaitStyleAvailable; } + + bool isAccelerated() const { return m_isAccelerated; } virtual double timeToNextService(); - double progress(double scale, double offset, const TimingFunction*) const; + double progress(double scale = 1, double offset = 0, const TimingFunction* = nullptr) const; - virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* /*currentStyle*/, RenderStyle* /*targetStyle*/, RefPtr<RenderStyle>& /*animatedStyle*/) = 0; - virtual void getAnimatedStyle(RefPtr<RenderStyle>& /*animatedStyle*/) = 0; + // Returns true if the animation state changed. + virtual bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* /*currentStyle*/, const RenderStyle* /*targetStyle*/, std::unique_ptr<RenderStyle>& /*animatedStyle*/, bool& didBlendStyle) = 0; + virtual void getAnimatedStyle(std::unique_ptr<RenderStyle>& /*animatedStyle*/) = 0; + + virtual bool computeExtentOfTransformAnimation(LayoutRect&) const = 0; virtual bool shouldFireEvents() const { return false; } void fireAnimationEventsIfNeeded(); - bool animationsMatch(const Animation*) const; + bool animationsMatch(const Animation&) const; - void setAnimation(const Animation& animation) { m_animation = const_cast<Animation*>(&animation); } + const Animation& animation() const { return m_animation; } + void setAnimation(const Animation& animation) { m_animation = const_cast<Animation&>(animation); } // Return true if this animation is overridden. This will only be the case for // ImplicitAnimations and is used to determine whether or not we should force // set the start time. If an animation is overridden, it will probably not get - // back the AnimationStateInputStartTimeSet input. + // back the AnimationStateInput::StartTimeSet input. virtual bool overridden() const { return false; } // Does this animation/transition involve the given property? virtual bool affectsProperty(CSSPropertyID /*property*/) const { return false; } - bool isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, bool isRunningNow) const + enum RunningStates { + Delaying = 1 << 0, + Paused = 1 << 1, + Running = 1 << 2, + }; + typedef unsigned RunningState; + bool isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, RunningState runningState) const { if (acceleratedOnly && !m_isAccelerated) return false; - - if (isRunningNow) - return (!waitingToStart() && !postActive()) && affectsProperty(property); - return !postActive() && affectsProperty(property); + if (!affectsProperty(property)) + return false; + + if ((runningState & Delaying) && preActive()) + return true; + + if ((runningState & Paused) && inPausedState()) + return true; + + if ((runningState & Running) && !inPausedState() && (m_animationState >= AnimationState::StartWaitStyleAvailable && m_animationState < AnimationState::Done)) + return true; + + return false; } - // FIXME: rename this using the "lists match" terminology. - bool isTransformFunctionListValid() const { return m_transformFunctionListValid; } -#if ENABLE(CSS_FILTERS) + bool transformFunctionListsMatch() const { return m_transformFunctionListsMatch; } bool filterFunctionListsMatch() const { return m_filterFunctionListsMatch; } +#if ENABLE(FILTERS_LEVEL_2) + bool backdropFilterFunctionListsMatch() const { return m_backdropFilterFunctionListsMatch; } #endif // Freeze the animation; used by DumpRenderTree. @@ -186,16 +205,14 @@ public: void styleAvailable() { ASSERT(waitingForStyleAvailable()); - updateStateMachine(AnimationBase::AnimationStateInputStyleAvailable, -1); + updateStateMachine(AnimationStateInput::StyleAvailable, -1); } - const Animation& animation() const { return *m_animation; } - protected: virtual void overrideAnimations() { } virtual void resumeOverriddenAnimations() { } - CompositeAnimation* compositeAnimation() { return m_compAnim; } + CompositeAnimation* compositeAnimation() { return m_compositeAnimation; } // These are called when the corresponding timer fires so subclasses can do any extra work virtual void onAnimationStart(double /*elapsedTime*/) { } @@ -211,7 +228,7 @@ protected: void goIntoEndingOrLoopingState(); - bool isAccelerated() const { return m_isAccelerated; } + AnimationState state() const { return m_animationState; } static void setNeedsStyleRecalc(Element*); @@ -219,26 +236,27 @@ protected: double fractionalTime(double scale, double elapsedTime, double offset) const; - AnimState m_animState; - - bool m_isAccelerated; - bool m_transformFunctionListValid; -#if ENABLE(CSS_FILTERS) - bool m_filterFunctionListsMatch; -#endif - double m_startTime; - double m_pauseTime; - double m_requestedStartTime; - - double m_totalDuration; - double m_nextIterationDuration; + // These return true if we can easily compute a bounding box by applying the style's transform to the bounds rect. + bool computeTransformedExtentViaTransformList(const FloatRect& rendererBox, const RenderStyle&, LayoutRect& bounds) const; + bool computeTransformedExtentViaMatrix(const FloatRect& rendererBox, const RenderStyle&, LayoutRect& bounds) const; RenderElement* m_object; - - RefPtr<Animation> m_animation; - CompositeAnimation* m_compAnim; + CompositeAnimation* m_compositeAnimation; // Ideally this would be a reference, but it has to be cleared if an animation is destroyed inside an event callback. + Ref<Animation> m_animation; + + std::optional<double> m_startTime; + std::optional<double> m_pauseTime; + double m_requestedStartTime { 0 }; + std::optional<double> m_totalDuration; + std::optional<double> m_nextIterationDuration; + + AnimationState m_animationState { AnimationState::New }; + bool m_isAccelerated { false }; + bool m_transformFunctionListsMatch { false }; + bool m_filterFunctionListsMatch { false }; +#if ENABLE(FILTERS_LEVEL_2) + bool m_backdropFilterFunctionListsMatch { false }; +#endif }; } // namespace WebCore - -#endif // AnimationBase_h diff --git a/Source/WebCore/page/animation/AnimationController.cpp b/Source/WebCore/page/animation/AnimationController.cpp deleted file mode 100644 index 5b8c90b8a..000000000 --- a/Source/WebCore/page/animation/AnimationController.cpp +++ /dev/null @@ -1,645 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2009 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 "AnimationController.h" - -#include "AnimationBase.h" -#include "AnimationControllerPrivate.h" -#include "CSSParser.h" -#include "CSSPropertyAnimation.h" -#include "CompositeAnimation.h" -#include "EventNames.h" -#include "Frame.h" -#include "FrameView.h" -#include "Logging.h" -#include "PseudoElement.h" -#include "RenderView.h" -#include "TransitionEvent.h" -#include "WebKitAnimationEvent.h" -#include "WebKitTransitionEvent.h" -#include <wtf/CurrentTime.h> - -namespace WebCore { - -static const double cAnimationTimerDelay = 0.025; -static const double cBeginAnimationUpdateTimeNotSet = -1; - -AnimationControllerPrivate::AnimationControllerPrivate(Frame& frame) - : m_animationTimer(this, &AnimationControllerPrivate::animationTimerFired) - , m_updateStyleIfNeededDispatcher(this, &AnimationControllerPrivate::updateStyleIfNeededDispatcherFired) - , m_frame(frame) - , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) - , m_animationsWaitingForStyle() - , m_animationsWaitingForStartTimeResponse() - , m_waitingForAsyncStartNotification(false) - , m_isSuspended(false) - , m_allowsNewAnimationsWhileSuspended(false) -{ -} - -AnimationControllerPrivate::~AnimationControllerPrivate() -{ -} - -CompositeAnimation& AnimationControllerPrivate::ensureCompositeAnimation(RenderElement* renderer) -{ - auto result = m_compositeAnimations.add(renderer, nullptr); - if (result.isNewEntry) - result.iterator->value = CompositeAnimation::create(this); - return *result.iterator->value; -} - -bool AnimationControllerPrivate::clear(RenderElement* renderer) -{ - // Return false if we didn't do anything OR we are suspended (so we don't try to - // do a setNeedsStyleRecalc() when suspended). - RefPtr<CompositeAnimation> animation = m_compositeAnimations.take(renderer); - if (!animation) - return false; - animation->clearRenderer(); - return animation->isSuspended(); -} - -double AnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/) -{ - double timeToNextService = -1; - bool calledSetChanged = false; - - auto end = m_compositeAnimations.end(); - for (auto it = m_compositeAnimations.begin(); it != end; ++it) { - CompositeAnimation& animation = *it->value; - if (!animation.isSuspended() && animation.hasAnimations()) { - double t = animation.timeToNextService(); - if (t != -1 && (t < timeToNextService || timeToNextService == -1)) - timeToNextService = t; - if (!timeToNextService) { - if (callSetChanged != CallSetChanged) - break; - Element* element = it->key->element(); - ASSERT(element); - ASSERT(!element->document().inPageCache()); - element->setNeedsStyleRecalc(SyntheticStyleChange); - calledSetChanged = true; - } - } - } - - if (calledSetChanged) - m_frame.document()->updateStyleIfNeeded(); - - return timeToNextService; -} - -void AnimationControllerPrivate::updateAnimationTimerForRenderer(RenderElement* renderer) -{ - double timeToNextService = 0; - - const CompositeAnimation* compositeAnimation = m_compositeAnimations.get(renderer); - if (!compositeAnimation->isSuspended() && compositeAnimation->hasAnimations()) - timeToNextService = compositeAnimation->timeToNextService(); - - if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService)) - return; - - m_animationTimer.startOneShot(timeToNextService); -} - -void AnimationControllerPrivate::updateAnimationTimer(SetChanged callSetChanged/* = DoNotCallSetChanged*/) -{ - double timeToNextService = updateAnimations(callSetChanged); - - LOG(Animations, "updateAnimationTimer: timeToNextService is %.2f", timeToNextService); - - // If we want service immediately, we start a repeating timer to reduce the overhead of starting - if (!timeToNextService) { - if (!m_animationTimer.isActive() || m_animationTimer.repeatInterval() == 0) - m_animationTimer.startRepeating(cAnimationTimerDelay); - return; - } - - // If we don't need service, we want to make sure the timer is no longer running - if (timeToNextService < 0) { - if (m_animationTimer.isActive()) - m_animationTimer.stop(); - return; - } - - // Otherwise, we want to start a one-shot timer so we get here again - m_animationTimer.startOneShot(timeToNextService); -} - -void AnimationControllerPrivate::updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>&) -{ - fireEventsAndUpdateStyle(); -} - -void AnimationControllerPrivate::fireEventsAndUpdateStyle() -{ - // Protect the frame from getting destroyed in the event handler - Ref<Frame> protector(m_frame); - - bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_elementChangesToDispatch.isEmpty(); - - // fire all the events - Vector<EventToDispatch> eventsToDispatch = std::move(m_eventsToDispatch); - Vector<EventToDispatch>::const_iterator eventsToDispatchEnd = eventsToDispatch.end(); - for (Vector<EventToDispatch>::const_iterator it = eventsToDispatch.begin(); it != eventsToDispatchEnd; ++it) { - Element* element = it->element.get(); - if (it->eventType == eventNames().transitionendEvent) - element->dispatchEvent(TransitionEvent::create(it->eventType, it->name, it->elapsedTime, PseudoElement::pseudoElementNameForEvents(element->pseudoId()))); - else - element->dispatchEvent(WebKitAnimationEvent::create(it->eventType, it->name, it->elapsedTime)); - } - - for (unsigned i = 0, size = m_elementChangesToDispatch.size(); i < size; ++i) - m_elementChangesToDispatch[i]->setNeedsStyleRecalc(SyntheticStyleChange); - - m_elementChangesToDispatch.clear(); - - if (updateStyle) - m_frame.document()->updateStyleIfNeeded(); -} - -void AnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() -{ - if (!m_updateStyleIfNeededDispatcher.isActive()) - m_updateStyleIfNeededDispatcher.startOneShot(0); -} - -void AnimationControllerPrivate::addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime) -{ - m_eventsToDispatch.grow(m_eventsToDispatch.size()+1); - EventToDispatch& event = m_eventsToDispatch[m_eventsToDispatch.size()-1]; - event.element = element; - event.eventType = eventType; - event.name = name; - event.elapsedTime = elapsedTime; - - startUpdateStyleIfNeededDispatcher(); -} - -void AnimationControllerPrivate::addElementChangeToDispatch(PassRef<Element> element) -{ - m_elementChangesToDispatch.append(std::move(element)); - ASSERT(!m_elementChangesToDispatch.last()->document().inPageCache()); - startUpdateStyleIfNeededDispatcher(); -} - -#if ENABLE(REQUEST_ANIMATION_FRAME) -void AnimationControllerPrivate::animationFrameCallbackFired() -{ - double timeToNextService = updateAnimations(CallSetChanged); - - if (timeToNextService >= 0) - m_frame.document()->view()->scheduleAnimation(); -} -#endif - -void AnimationControllerPrivate::animationTimerFired(Timer<AnimationControllerPrivate>&) -{ - // Make sure animationUpdateTime is updated, so that it is current even if no - // styleChange has happened (e.g. accelerated animations) - setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - - // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate - // updateStyleIfNeeded. It will then call back to us with new information. - updateAnimationTimer(CallSetChanged); - - // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before - // the 'end' event fires. - fireEventsAndUpdateStyle(); -} - -bool AnimationControllerPrivate::isRunningAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - const CompositeAnimation* animation = m_compositeAnimations.get(renderer); - return animation && animation->isAnimatingProperty(property, false, isRunningNow); -} - -bool AnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - const CompositeAnimation* animation = m_compositeAnimations.get(renderer); - return animation && animation->isAnimatingProperty(property, true, isRunningNow); -} - -void AnimationControllerPrivate::suspendAnimations() -{ - if (isSuspended()) - return; - - suspendAnimationsForDocument(m_frame.document()); - - // Traverse subframes - for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) - child->animation().suspendAnimations(); - - m_isSuspended = true; -} - -void AnimationControllerPrivate::resumeAnimations() -{ - if (!isSuspended()) - return; - - resumeAnimationsForDocument(m_frame.document()); - - // Traverse subframes - for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) - child->animation().resumeAnimations(); - - m_isSuspended = false; -} - -void AnimationControllerPrivate::suspendAnimationsForDocument(Document* document) -{ - setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - - for (auto it = m_compositeAnimations.begin(), end = m_compositeAnimations.end(); it != end; ++it) { - if (&it->key->document() == document) - it->value->suspendAnimations(); - } - - updateAnimationTimer(); -} - -void AnimationControllerPrivate::resumeAnimationsForDocument(Document* document) -{ - setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - - for (auto it = m_compositeAnimations.begin(), end = m_compositeAnimations.end(); it != end; ++it) { - if (&it->key->document() == document) - it->value->resumeAnimations(); - } - - updateAnimationTimer(); -} - -void AnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document) -{ - if (!isSuspended() || allowsNewAnimationsWhileSuspended()) - resumeAnimationsForDocument(document); -} - -void AnimationControllerPrivate::setAllowsNewAnimationsWhileSuspended(bool allowed) -{ - m_allowsNewAnimationsWhileSuspended = allowed; -} - -bool AnimationControllerPrivate::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) -{ - if (!renderer) - return false; - - CompositeAnimation& compositeAnimation = ensureCompositeAnimation(renderer); - if (compositeAnimation.pauseAnimationAtTime(name, t)) { - renderer->element()->setNeedsStyleRecalc(SyntheticStyleChange); - startUpdateStyleIfNeededDispatcher(); - return true; - } - - return false; -} - -bool AnimationControllerPrivate::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) -{ - if (!renderer) - return false; - - CompositeAnimation& compositeAnimation = ensureCompositeAnimation(renderer); - if (compositeAnimation.pauseTransitionAtTime(cssPropertyID(property), t)) { - renderer->element()->setNeedsStyleRecalc(SyntheticStyleChange); - startUpdateStyleIfNeededDispatcher(); - return true; - } - - return false; -} - -double AnimationControllerPrivate::beginAnimationUpdateTime() -{ - if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) - m_beginAnimationUpdateTime = monotonicallyIncreasingTime(); - return m_beginAnimationUpdateTime; -} - -void AnimationControllerPrivate::endAnimationUpdate() -{ - styleAvailable(); - if (!m_waitingForAsyncStartNotification) - startTimeResponse(beginAnimationUpdateTime()); -} - -void AnimationControllerPrivate::receivedStartTimeResponse(double time) -{ - m_waitingForAsyncStartNotification = false; - startTimeResponse(time); -} - -PassRefPtr<RenderStyle> AnimationControllerPrivate::getAnimatedStyleForRenderer(RenderElement* renderer) -{ - if (!renderer) - return 0; - - const CompositeAnimation* rendererAnimations = m_compositeAnimations.get(renderer); - if (!rendererAnimations) - return &renderer->style(); - - RefPtr<RenderStyle> animatingStyle = rendererAnimations->getAnimatedStyle(); - if (!animatingStyle) - animatingStyle = &renderer->style(); - - return animatingStyle.release(); -} - -unsigned AnimationControllerPrivate::numberOfActiveAnimations(Document* document) const -{ - unsigned count = 0; - - for (auto it = m_compositeAnimations.begin(), end = m_compositeAnimations.end(); it != end; ++it) { - if (&it->key->document() == document) - count += it->value->numberOfActiveAnimations(); - } - - return count; -} - -void AnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) -{ - // Make sure this animation is not in the start time waiters - m_animationsWaitingForStartTimeResponse.remove(animation); - - m_animationsWaitingForStyle.add(animation); -} - -void AnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) -{ - m_animationsWaitingForStyle.remove(animationToRemove); -} - -void AnimationControllerPrivate::styleAvailable() -{ - // Go through list of waiters and send them on their way - for (const auto& waitingAnimation : m_animationsWaitingForStyle) - waitingAnimation->styleAvailable(); - - m_animationsWaitingForStyle.clear(); -} - -void AnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) -{ - // If willGetResponse is true, it means this animation is actually waiting for a response - // (which will come in as a call to notifyAnimationStarted()). - // In that case we don't need to add it to this list. We just set a waitingForAResponse flag - // which says we are waiting for the response. If willGetResponse is false, this animation - // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for - // another animation to which it will sync. - // - // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is - // true. If so, we just return and will do our work when the first notifyXXXStarted() call - // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will - // do our work right away. In both cases we call the onAnimationStartResponse() method - // on each animation. In the first case we send in the time we got from notifyXXXStarted(). - // In the second case, we just pass in the beginAnimationUpdateTime(). - // - // This will synchronize all software and accelerated animations started in the same - // updateStyleIfNeeded cycle. - // - - if (willGetResponse) - m_waitingForAsyncStartNotification = true; - - m_animationsWaitingForStartTimeResponse.add(animation); -} - -void AnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) -{ - m_animationsWaitingForStartTimeResponse.remove(animationToRemove); - - if (m_animationsWaitingForStartTimeResponse.isEmpty()) - m_waitingForAsyncStartNotification = false; -} - -void AnimationControllerPrivate::startTimeResponse(double time) -{ - // Go through list of waiters and send them on their way - - for (const auto& animation : m_animationsWaitingForStartTimeResponse) - animation->onAnimationStartResponse(time); - - m_animationsWaitingForStartTimeResponse.clear(); - m_waitingForAsyncStartNotification = false; -} - -void AnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) -{ - removeFromAnimationsWaitingForStyle(animation); - removeFromAnimationsWaitingForStartTimeResponse(animation); -} - -AnimationController::AnimationController(Frame& frame) - : m_data(std::make_unique<AnimationControllerPrivate>(frame)) - , m_beginAnimationUpdateCount(0) -{ -} - -AnimationController::~AnimationController() -{ -} - -void AnimationController::cancelAnimations(RenderElement* renderer) -{ - if (!m_data->hasAnimations()) - return; - - if (m_data->clear(renderer)) { - Element* element = renderer->element(); - ASSERT(!element || !element->document().inPageCache()); - if (element) - element->setNeedsStyleRecalc(SyntheticStyleChange); - } -} - -PassRef<RenderStyle> AnimationController::updateAnimations(RenderElement& renderer, PassRef<RenderStyle> newStyle) -{ - // Don't do anything if we're in the cache - if (renderer.document().inPageCache()) - return newStyle; - - RenderStyle* oldStyle = renderer.hasInitializedStyle() ? &renderer.style() : nullptr; - - if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.get().animations() && !newStyle.get().transitions())) - return newStyle; - - // Don't run transitions when printing. - if (renderer.view().printing()) - return newStyle; - - // Fetch our current set of implicit animations from a hashtable. We then compare them - // against the animations in the style and make sure we're in sync. If destination values - // have changed, we reset the animation. We then do a blend to get new values and we return - // a new style. - - // We don't support anonymous pseudo elements like :first-line or :first-letter. - ASSERT(renderer.element()); - - Ref<RenderStyle> newStyleBeforeAnimation(std::move(newStyle)); - - CompositeAnimation& rendererAnimations = m_data->ensureCompositeAnimation(&renderer); - auto blendedStyle = rendererAnimations.animate(renderer, oldStyle, newStyleBeforeAnimation.get()); - - if (renderer.parent() || newStyleBeforeAnimation->animations() || (oldStyle && oldStyle->animations())) { - m_data->updateAnimationTimerForRenderer(&renderer); -#if ENABLE(REQUEST_ANIMATION_FRAME) - renderer.view().frameView().scheduleAnimation(); -#endif - } - - if (&blendedStyle.get() != &newStyleBeforeAnimation.get()) { - // If the animations/transitions change opacity or transform, we need to update - // the style to impose the stacking rules. Note that this is also - // done in StyleResolver::adjustRenderStyle(). - if (blendedStyle.get().hasAutoZIndex() && (blendedStyle.get().opacity() < 1.0f || blendedStyle.get().hasTransform())) - blendedStyle.get().setZIndex(0); - } - return blendedStyle; -} - -PassRefPtr<RenderStyle> AnimationController::getAnimatedStyleForRenderer(RenderElement* renderer) -{ - return m_data->getAnimatedStyleForRenderer(renderer); -} - -void AnimationController::notifyAnimationStarted(RenderElement*, double startTime) -{ - m_data->receivedStartTimeResponse(startTime); -} - -bool AnimationController::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) -{ - return m_data->pauseAnimationAtTime(renderer, name, t); -} - -unsigned AnimationController::numberOfActiveAnimations(Document* document) const -{ - return m_data->numberOfActiveAnimations(document); -} - -bool AnimationController::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) -{ - return m_data->pauseTransitionAtTime(renderer, property, t); -} - -bool AnimationController::isRunningAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - return m_data->isRunningAnimationOnRenderer(renderer, property, isRunningNow); -} - -bool AnimationController::isRunningAcceleratedAnimationOnRenderer(RenderElement* renderer, CSSPropertyID property, bool isRunningNow) const -{ - return m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, isRunningNow); -} - -bool AnimationController::isSuspended() const -{ - return m_data->isSuspended(); -} - -void AnimationController::suspendAnimations() -{ - LOG(Animations, "controller is suspending animations"); - m_data->suspendAnimations(); -} - -void AnimationController::resumeAnimations() -{ - LOG(Animations, "controller is resuming animations"); - m_data->resumeAnimations(); -} - -bool AnimationController::allowsNewAnimationsWhileSuspended() const -{ - return m_data->allowsNewAnimationsWhileSuspended(); -} - -void AnimationController::setAllowsNewAnimationsWhileSuspended(bool allowed) -{ - m_data->setAllowsNewAnimationsWhileSuspended(allowed); -} - -#if ENABLE(REQUEST_ANIMATION_FRAME) -void AnimationController::serviceAnimations() -{ - m_data->animationFrameCallbackFired(); -} -#endif - -void AnimationController::suspendAnimationsForDocument(Document* document) -{ - LOG(Animations, "suspending animations for document %p", document); - m_data->suspendAnimationsForDocument(document); -} - -void AnimationController::resumeAnimationsForDocument(Document* document) -{ - LOG(Animations, "resuming animations for document %p", document); - m_data->resumeAnimationsForDocument(document); -} - -void AnimationController::startAnimationsIfNotSuspended(Document* document) -{ - LOG(Animations, "animations may start for document %p", document); - m_data->startAnimationsIfNotSuspended(document); -} - -void AnimationController::beginAnimationUpdate() -{ - if (!m_beginAnimationUpdateCount) - m_data->setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); - ++m_beginAnimationUpdateCount; -} - -void AnimationController::endAnimationUpdate() -{ - ASSERT(m_beginAnimationUpdateCount > 0); - --m_beginAnimationUpdateCount; - if (!m_beginAnimationUpdateCount) - m_data->endAnimationUpdate(); -} - -bool AnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) -{ -#if USE(ACCELERATED_COMPOSITING) - return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); -#else - UNUSED_PARAM(property); - return false; -#endif -} - -} // namespace WebCore diff --git a/Source/WebCore/page/animation/CSSAnimationController.cpp b/Source/WebCore/page/animation/CSSAnimationController.cpp new file mode 100644 index 000000000..b71ed2e43 --- /dev/null +++ b/Source/WebCore/page/animation/CSSAnimationController.cpp @@ -0,0 +1,805 @@ +/* + * Copyright (C) 2007, 2008, 2009 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 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 "CSSAnimationController.h" + +#include "AnimationBase.h" +#include "AnimationEvent.h" +#include "CSSAnimationControllerPrivate.h" +#include "CSSPropertyAnimation.h" +#include "CSSPropertyParser.h" +#include "CompositeAnimation.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "Logging.h" +#include "PseudoElement.h" +#include "RenderView.h" +#include "TransitionEvent.h" +#include "WebKitAnimationEvent.h" +#include "WebKitTransitionEvent.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +// Allow a little more than 60fps to make sure we can at least hit that frame rate. +static const double cAnimationTimerDelay = 0.015; +static const double cBeginAnimationUpdateTimeNotSet = -1; + +class AnimationPrivateUpdateBlock { +public: + AnimationPrivateUpdateBlock(CSSAnimationControllerPrivate& animationController) + : m_animationController(animationController) + { + m_animationController.beginAnimationUpdate(); + } + + ~AnimationPrivateUpdateBlock() + { + m_animationController.endAnimationUpdate(); + } + + CSSAnimationControllerPrivate& m_animationController; +}; + +CSSAnimationControllerPrivate::CSSAnimationControllerPrivate(Frame& frame) + : m_animationTimer(*this, &CSSAnimationControllerPrivate::animationTimerFired) + , m_updateStyleIfNeededDispatcher(*this, &CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired) + , m_frame(frame) + , m_beginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet) + , m_beginAnimationUpdateCount(0) + , m_waitingForAsyncStartNotification(false) + , m_isSuspended(false) + , m_allowsNewAnimationsWhileSuspended(false) +{ +} + +CSSAnimationControllerPrivate::~CSSAnimationControllerPrivate() +{ +} + +CompositeAnimation& CSSAnimationControllerPrivate::ensureCompositeAnimation(RenderElement& renderer) +{ + auto result = m_compositeAnimations.add(&renderer, nullptr); + if (result.isNewEntry) { + result.iterator->value = CompositeAnimation::create(*this); + renderer.setIsCSSAnimating(true); + } + + if (animationsAreSuspendedForDocument(&renderer.document())) + result.iterator->value->suspendAnimations(); + + return *result.iterator->value; +} + +bool CSSAnimationControllerPrivate::clear(RenderElement& renderer) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p clear: %p", this, &renderer); + + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + + Element* element = renderer.element(); + + m_eventsToDispatch.removeAllMatching([element] (const EventToDispatch& info) { + return info.element.ptr() == element; + }); + + m_elementChangesToDispatch.removeAllMatching([element](auto& currentElement) { + return currentElement.ptr() == element; + }); + + // Return false if we didn't do anything OR we are suspended (so we don't try to + // do a invalidateStyleForSubtree() when suspended). + RefPtr<CompositeAnimation> animation = m_compositeAnimations.take(&renderer); + ASSERT(animation); + renderer.setIsCSSAnimating(false); + animation->clearRenderer(); + return animation->isSuspended(); +} + +double CSSAnimationControllerPrivate::updateAnimations(SetChanged callSetChanged/* = DoNotCallSetChanged*/) +{ + AnimationPrivateUpdateBlock updateBlock(*this); + double timeToNextService = -1; + bool calledSetChanged = false; + + for (auto& compositeAnimation : m_compositeAnimations) { + CompositeAnimation& animation = *compositeAnimation.value; + if (!animation.isSuspended() && animation.hasAnimations()) { + double t = animation.timeToNextService(); + if (t != -1 && (t < timeToNextService || timeToNextService == -1)) + timeToNextService = t; + if (!timeToNextService) { + if (callSetChanged != CallSetChanged) + break; + Element* element = compositeAnimation.key->element(); + ASSERT(element); + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); + calledSetChanged = true; + } + } + } + + if (calledSetChanged) + m_frame.document()->updateStyleIfNeeded(); + + return timeToNextService; +} + +void CSSAnimationControllerPrivate::updateAnimationTimerForRenderer(RenderElement& renderer) +{ + double timeToNextService = 0; + + const CompositeAnimation* compositeAnimation = m_compositeAnimations.get(&renderer); + if (!compositeAnimation->isSuspended() && compositeAnimation->hasAnimations()) + timeToNextService = compositeAnimation->timeToNextService(); + + if (m_animationTimer.isActive() && (m_animationTimer.repeatInterval() || m_animationTimer.nextFireInterval() <= timeToNextService)) + return; + + m_animationTimer.startOneShot(timeToNextService); +} + +void CSSAnimationControllerPrivate::updateAnimationTimer(SetChanged callSetChanged/* = DoNotCallSetChanged*/) +{ + double timeToNextService = updateAnimations(callSetChanged); + + LOG(Animations, "updateAnimationTimer: timeToNextService is %.2f", timeToNextService); + + // If we want service immediately, we start a repeating timer to reduce the overhead of starting + if (!timeToNextService) { + if (!m_animationTimer.isActive() || !m_animationTimer.repeatInterval()) + m_animationTimer.startRepeating(cAnimationTimerDelay); + return; + } + + // If we don't need service, we want to make sure the timer is no longer running + if (timeToNextService < 0) { + if (m_animationTimer.isActive()) + m_animationTimer.stop(); + return; + } + + // Otherwise, we want to start a one-shot timer so we get here again + m_animationTimer.startOneShot(timeToNextService); +} + +void CSSAnimationControllerPrivate::updateStyleIfNeededDispatcherFired() +{ + fireEventsAndUpdateStyle(); +} + +void CSSAnimationControllerPrivate::fireEventsAndUpdateStyle() +{ + // Protect the frame from getting destroyed in the event handler + Ref<Frame> protector(m_frame); + + bool updateStyle = !m_eventsToDispatch.isEmpty() || !m_elementChangesToDispatch.isEmpty(); + + // fire all the events + Vector<EventToDispatch> eventsToDispatch = WTFMove(m_eventsToDispatch); + for (auto& event : eventsToDispatch) { + Element& element = event.element; + if (event.eventType == eventNames().transitionendEvent) + element.dispatchEvent(TransitionEvent::create(event.eventType, event.name, event.elapsedTime, PseudoElement::pseudoElementNameForEvents(element.pseudoId()))); + else + element.dispatchEvent(AnimationEvent::create(event.eventType, event.name, event.elapsedTime)); + } + + for (auto& change : m_elementChangesToDispatch) + change->invalidateStyleAndLayerComposition(); + + m_elementChangesToDispatch.clear(); + + if (updateStyle) + m_frame.document()->updateStyleIfNeeded(); +} + +void CSSAnimationControllerPrivate::startUpdateStyleIfNeededDispatcher() +{ + if (!m_updateStyleIfNeededDispatcher.isActive()) + m_updateStyleIfNeededDispatcher.startOneShot(0); +} + +void CSSAnimationControllerPrivate::addEventToDispatch(Element& element, const AtomicString& eventType, const String& name, double elapsedTime) +{ + m_eventsToDispatch.append({ element, eventType, name, elapsedTime }); + startUpdateStyleIfNeededDispatcher(); +} + +void CSSAnimationControllerPrivate::addElementChangeToDispatch(Element& element) +{ + m_elementChangesToDispatch.append(element); + ASSERT(m_elementChangesToDispatch.last()->document().pageCacheState() == Document::NotInPageCache); + startUpdateStyleIfNeededDispatcher(); +} + +void CSSAnimationControllerPrivate::animationFrameCallbackFired() +{ + double timeToNextService = updateAnimations(CallSetChanged); + + if (timeToNextService >= 0) + m_frame.document()->view()->scheduleAnimation(); +} + +void CSSAnimationControllerPrivate::animationTimerFired() +{ + // We need to keep the frame alive, since it owns us. + Ref<Frame> protector(m_frame); + + // The animation timer might fire before the layout timer, in + // which case we might create some animations with incorrect + // values if we don't layout first. + if (m_requiresLayout) { + if (auto* frameView = m_frame.document()->view()) { + if (frameView->needsLayout()) + frameView->forceLayout(); + } + m_requiresLayout = false; + } + + // Make sure animationUpdateTime is updated, so that it is current even if no + // styleChange has happened (e.g. accelerated animations) + AnimationPrivateUpdateBlock updateBlock(*this); + + // When the timer fires, all we do is call setChanged on all DOM nodes with running animations and then do an immediate + // updateStyleIfNeeded. It will then call back to us with new information. + updateAnimationTimer(CallSetChanged); + + // Fire events right away, to avoid a flash of unanimated style after an animation completes, and before + // the 'end' event fires. + fireEventsAndUpdateStyle(); +} + +bool CSSAnimationControllerPrivate::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& animation = *m_compositeAnimations.get(&renderer); + return animation.isAnimatingProperty(property, false, runningState); +} + +bool CSSAnimationControllerPrivate::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& animation = *m_compositeAnimations.get(&renderer); + return animation.isAnimatingProperty(property, true, runningState); +} + +void CSSAnimationControllerPrivate::suspendAnimations() +{ + if (isSuspended()) + return; + + suspendAnimationsForDocument(m_frame.document()); + + // Traverse subframes + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->animation().suspendAnimations(); + + m_isSuspended = true; +} + +void CSSAnimationControllerPrivate::resumeAnimations() +{ + if (!isSuspended()) + return; + + resumeAnimationsForDocument(m_frame.document()); + + // Traverse subframes + for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) + child->animation().resumeAnimations(); + + m_isSuspended = false; +} + +bool CSSAnimationControllerPrivate::animationsAreSuspendedForDocument(Document* document) +{ + return isSuspended() || m_suspendedDocuments.contains(document); +} + +void CSSAnimationControllerPrivate::detachFromDocument(Document* document) +{ + m_suspendedDocuments.remove(document); +} + +void CSSAnimationControllerPrivate::suspendAnimationsForDocument(Document* document) +{ + if (animationsAreSuspendedForDocument(document)) + return; + + m_suspendedDocuments.add(document); + + AnimationPrivateUpdateBlock updateBlock(*this); + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + animation.value->suspendAnimations(); + } + + updateAnimationTimer(); +} + +void CSSAnimationControllerPrivate::resumeAnimationsForDocument(Document* document) +{ + if (!animationsAreSuspendedForDocument(document)) + return; + + detachFromDocument(document); + + AnimationPrivateUpdateBlock updateBlock(*this); + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + animation.value->resumeAnimations(); + } + + updateAnimationTimer(); +} + +void CSSAnimationControllerPrivate::startAnimationsIfNotSuspended(Document* document) +{ + if (!animationsAreSuspendedForDocument(document) || allowsNewAnimationsWhileSuspended()) + resumeAnimationsForDocument(document); +} + +void CSSAnimationControllerPrivate::setAllowsNewAnimationsWhileSuspended(bool allowed) +{ + m_allowsNewAnimationsWhileSuspended = allowed; +} + +bool CSSAnimationControllerPrivate::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) +{ + if (!renderer) + return false; + + CompositeAnimation& compositeAnimation = ensureCompositeAnimation(*renderer); + if (compositeAnimation.pauseAnimationAtTime(name, t)) { + renderer->element()->invalidateStyleAndLayerComposition(); + startUpdateStyleIfNeededDispatcher(); + return true; + } + + return false; +} + +bool CSSAnimationControllerPrivate::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) +{ + if (!renderer) + return false; + + CompositeAnimation& compositeAnimation = ensureCompositeAnimation(*renderer); + if (compositeAnimation.pauseTransitionAtTime(cssPropertyID(property), t)) { + renderer->element()->invalidateStyleAndLayerComposition(); + startUpdateStyleIfNeededDispatcher(); + return true; + } + + return false; +} + +double CSSAnimationControllerPrivate::beginAnimationUpdateTime() +{ + ASSERT(m_beginAnimationUpdateCount); + if (m_beginAnimationUpdateTime == cBeginAnimationUpdateTimeNotSet) + m_beginAnimationUpdateTime = monotonicallyIncreasingTime(); + + return m_beginAnimationUpdateTime; +} + +void CSSAnimationControllerPrivate::beginAnimationUpdate() +{ + if (!m_beginAnimationUpdateCount) + setBeginAnimationUpdateTime(cBeginAnimationUpdateTimeNotSet); + ++m_beginAnimationUpdateCount; +} + +void CSSAnimationControllerPrivate::endAnimationUpdate() +{ + ASSERT(m_beginAnimationUpdateCount > 0); + if (m_beginAnimationUpdateCount == 1) { + styleAvailable(); + if (!m_waitingForAsyncStartNotification) + startTimeResponse(beginAnimationUpdateTime()); + } + --m_beginAnimationUpdateCount; +} + +void CSSAnimationControllerPrivate::receivedStartTimeResponse(double time) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p receivedStartTimeResponse %f", this, time); + + m_waitingForAsyncStartNotification = false; + startTimeResponse(time); +} + +std::unique_ptr<RenderStyle> CSSAnimationControllerPrivate::getAnimatedStyleForRenderer(RenderElement& renderer) +{ + AnimationPrivateUpdateBlock animationUpdateBlock(*this); + + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + const CompositeAnimation& rendererAnimations = *m_compositeAnimations.get(&renderer); + std::unique_ptr<RenderStyle> animatingStyle = rendererAnimations.getAnimatedStyle(); + if (!animatingStyle) + animatingStyle = RenderStyle::clonePtr(renderer.style()); + + return animatingStyle; +} + +bool CSSAnimationControllerPrivate::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const +{ + ASSERT(renderer.isCSSAnimating()); + ASSERT(m_compositeAnimations.contains(&renderer)); + + const CompositeAnimation& rendererAnimations = *m_compositeAnimations.get(&renderer); + if (!rendererAnimations.isAnimatingProperty(CSSPropertyTransform, false, AnimationBase::Running | AnimationBase::Paused)) + return true; + + return rendererAnimations.computeExtentOfTransformAnimation(bounds); +} + +unsigned CSSAnimationControllerPrivate::numberOfActiveAnimations(Document* document) const +{ + unsigned count = 0; + + for (auto& animation : m_compositeAnimations) { + if (&animation.key->document() == document) + count += animation.value->numberOfActiveAnimations(); + } + + return count; +} + +void CSSAnimationControllerPrivate::addToAnimationsWaitingForStyle(AnimationBase* animation) +{ + // Make sure this animation is not in the start time waiters + m_animationsWaitingForStartTimeResponse.remove(animation); + + m_animationsWaitingForStyle.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsWaitingForStyle(AnimationBase* animationToRemove) +{ + m_animationsWaitingForStyle.remove(animationToRemove); +} + +void CSSAnimationControllerPrivate::styleAvailable() +{ + // Go through list of waiters and send them on their way + for (const auto& waitingAnimation : m_animationsWaitingForStyle) + waitingAnimation->styleAvailable(); + + m_animationsWaitingForStyle.clear(); +} + +void CSSAnimationControllerPrivate::addToAnimationsWaitingForStartTimeResponse(AnimationBase* animation, bool willGetResponse) +{ + // If willGetResponse is true, it means this animation is actually waiting for a response + // (which will come in as a call to notifyAnimationStarted()). + // In that case we don't need to add it to this list. We just set a waitingForAResponse flag + // which says we are waiting for the response. If willGetResponse is false, this animation + // is not waiting for a response for itself, but rather for a notifyXXXStarted() call for + // another animation to which it will sync. + // + // When endAnimationUpdate() is called we check to see if the waitingForAResponse flag is + // true. If so, we just return and will do our work when the first notifyXXXStarted() call + // comes in. If it is false, we will not be getting a notifyXXXStarted() call, so we will + // do our work right away. In both cases we call the onAnimationStartResponse() method + // on each animation. In the first case we send in the time we got from notifyXXXStarted(). + // In the second case, we just pass in the beginAnimationUpdateTime(). + // + // This will synchronize all software and accelerated animations started in the same + // updateStyleIfNeeded cycle. + // + + if (willGetResponse) + m_waitingForAsyncStartNotification = true; + + m_animationsWaitingForStartTimeResponse.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsWaitingForStartTimeResponse(AnimationBase* animationToRemove) +{ + m_animationsWaitingForStartTimeResponse.remove(animationToRemove); + + if (m_animationsWaitingForStartTimeResponse.isEmpty()) + m_waitingForAsyncStartNotification = false; +} + +void CSSAnimationControllerPrivate::startTimeResponse(double time) +{ + // Go through list of waiters and send them on their way + + for (const auto& animation : m_animationsWaitingForStartTimeResponse) + animation->onAnimationStartResponse(time); + + m_animationsWaitingForStartTimeResponse.clear(); + m_waitingForAsyncStartNotification = false; +} + +void CSSAnimationControllerPrivate::animationWillBeRemoved(AnimationBase* animation) +{ + LOG(Animations, "CSSAnimationControllerPrivate %p animationWillBeRemoved: %p", this, animation); + + removeFromAnimationsWaitingForStyle(animation); + removeFromAnimationsWaitingForStartTimeResponse(animation); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + removeFromAnimationsDependentOnScroll(animation); +#endif + + bool anyAnimationsWaitingForAsyncStart = false; + for (auto& animation : m_animationsWaitingForStartTimeResponse) { + if (animation->waitingForStartTime() && animation->isAccelerated()) { + anyAnimationsWaitingForAsyncStart = true; + break; + } + } + + if (!anyAnimationsWaitingForAsyncStart) + m_waitingForAsyncStartNotification = false; +} + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) +void CSSAnimationControllerPrivate::addToAnimationsDependentOnScroll(AnimationBase* animation) +{ + m_animationsDependentOnScroll.add(animation); +} + +void CSSAnimationControllerPrivate::removeFromAnimationsDependentOnScroll(AnimationBase* animation) +{ + m_animationsDependentOnScroll.remove(animation); +} + +void CSSAnimationControllerPrivate::scrollWasUpdated() +{ + auto* view = m_frame.view(); + if (!view || !wantsScrollUpdates()) + return; + + m_scrollPosition = view->scrollPositionForFixedPosition().y().toFloat(); + + // FIXME: This is updating all the animations, rather than just the ones + // that are dependent on scroll. We to go from our AnimationBase to its CompositeAnimation + // so we can execute code similar to updateAnimations. + // https://bugs.webkit.org/show_bug.cgi?id=144170 + updateAnimations(CallSetChanged); +} +#endif + +CSSAnimationController::CSSAnimationController(Frame& frame) + : m_data(std::make_unique<CSSAnimationControllerPrivate>(frame)) +{ +} + +CSSAnimationController::~CSSAnimationController() +{ +} + +void CSSAnimationController::cancelAnimations(RenderElement& renderer) +{ + if (!renderer.isCSSAnimating()) + return; + + if (!m_data->clear(renderer)) + return; + + Element* element = renderer.element(); + if (!element || element->document().renderTreeBeingDestroyed()) + return; + ASSERT(element->document().pageCacheState() == Document::NotInPageCache); + element->invalidateStyleAndLayerComposition(); +} + +bool CSSAnimationController::updateAnimations(RenderElement& renderer, const RenderStyle& newStyle, std::unique_ptr<RenderStyle>& animatedStyle) +{ + auto* oldStyle = renderer.hasInitializedStyle() ? &renderer.style() : nullptr; + if ((!oldStyle || (!oldStyle->animations() && !oldStyle->transitions())) && (!newStyle.animations() && !newStyle.transitions())) + return false; + + if (renderer.document().pageCacheState() != Document::NotInPageCache) + return false; + + // Don't run transitions when printing. + if (renderer.view().printing()) + return false; + + // Fetch our current set of implicit animations from a hashtable. We then compare them + // against the animations in the style and make sure we're in sync. If destination values + // have changed, we reset the animation. We then do a blend to get new values and we return + // a new style. + + // We don't support anonymous pseudo elements like :first-line or :first-letter. + ASSERT(renderer.element()); + + CompositeAnimation& rendererAnimations = m_data->ensureCompositeAnimation(renderer); + bool animationStateChanged = rendererAnimations.animate(renderer, oldStyle, newStyle, animatedStyle); + + if (renderer.parent() || newStyle.animations() || (oldStyle && oldStyle->animations())) { + auto& frameView = renderer.view().frameView(); + if (rendererAnimations.hasAnimationThatDependsOnLayout()) + m_data->setRequiresLayout(); + m_data->updateAnimationTimerForRenderer(renderer); + frameView.scheduleAnimation(); + } + + return animationStateChanged; +} + +std::unique_ptr<RenderStyle> CSSAnimationController::getAnimatedStyleForRenderer(RenderElement& renderer) +{ + if (!renderer.isCSSAnimating()) + return RenderStyle::clonePtr(renderer.style()); + return m_data->getAnimatedStyleForRenderer(renderer); +} + +bool CSSAnimationController::computeExtentOfAnimation(RenderElement& renderer, LayoutRect& bounds) const +{ + if (!renderer.isCSSAnimating()) + return true; + + return m_data->computeExtentOfAnimation(renderer, bounds); +} + +void CSSAnimationController::notifyAnimationStarted(RenderElement& renderer, double startTime) +{ + LOG(Animations, "CSSAnimationController %p notifyAnimationStarted on renderer %p, time=%f", this, &renderer, startTime); + UNUSED_PARAM(renderer); + + AnimationUpdateBlock animationUpdateBlock(this); + m_data->receivedStartTimeResponse(startTime); +} + +bool CSSAnimationController::pauseAnimationAtTime(RenderElement* renderer, const AtomicString& name, double t) +{ + AnimationUpdateBlock animationUpdateBlock(this); + return m_data->pauseAnimationAtTime(renderer, name, t); +} + +unsigned CSSAnimationController::numberOfActiveAnimations(Document* document) const +{ + return m_data->numberOfActiveAnimations(document); +} + +bool CSSAnimationController::pauseTransitionAtTime(RenderElement* renderer, const String& property, double t) +{ + AnimationUpdateBlock animationUpdateBlock(this); + return m_data->pauseTransitionAtTime(renderer, property, t); +} + +bool CSSAnimationController::isRunningAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + return renderer.isCSSAnimating() && m_data->isRunningAnimationOnRenderer(renderer, property, runningState); +} + +bool CSSAnimationController::isRunningAcceleratedAnimationOnRenderer(RenderElement& renderer, CSSPropertyID property, AnimationBase::RunningState runningState) const +{ + return renderer.isCSSAnimating() && m_data->isRunningAcceleratedAnimationOnRenderer(renderer, property, runningState); +} + +bool CSSAnimationController::isSuspended() const +{ + return m_data->isSuspended(); +} + +void CSSAnimationController::suspendAnimations() +{ + LOG(Animations, "controller is suspending animations"); + m_data->suspendAnimations(); +} + +void CSSAnimationController::resumeAnimations() +{ + LOG(Animations, "controller is resuming animations"); + m_data->resumeAnimations(); +} + +bool CSSAnimationController::allowsNewAnimationsWhileSuspended() const +{ + return m_data->allowsNewAnimationsWhileSuspended(); +} + +void CSSAnimationController::setAllowsNewAnimationsWhileSuspended(bool allowed) +{ + m_data->setAllowsNewAnimationsWhileSuspended(allowed); +} + +void CSSAnimationController::serviceAnimations() +{ + m_data->animationFrameCallbackFired(); +} + +bool CSSAnimationController::animationsAreSuspendedForDocument(Document* document) +{ + return m_data->animationsAreSuspendedForDocument(document); +} + +void CSSAnimationController::detachFromDocument(Document* document) +{ + return m_data->detachFromDocument(document); +} + +void CSSAnimationController::suspendAnimationsForDocument(Document* document) +{ + LOG(Animations, "suspending animations for document %p", document); + m_data->suspendAnimationsForDocument(document); +} + +void CSSAnimationController::resumeAnimationsForDocument(Document* document) +{ + LOG(Animations, "resuming animations for document %p", document); + AnimationUpdateBlock animationUpdateBlock(this); + m_data->resumeAnimationsForDocument(document); +} + +void CSSAnimationController::startAnimationsIfNotSuspended(Document* document) +{ + LOG(Animations, "animations may start for document %p", document); + + AnimationUpdateBlock animationUpdateBlock(this); + m_data->startAnimationsIfNotSuspended(document); +} + +void CSSAnimationController::beginAnimationUpdate() +{ + m_data->beginAnimationUpdate(); +} + +void CSSAnimationController::endAnimationUpdate() +{ + m_data->endAnimationUpdate(); +} + +bool CSSAnimationController::supportsAcceleratedAnimationOfProperty(CSSPropertyID property) +{ + return CSSPropertyAnimation::animationOfPropertyIsAccelerated(property); +} + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) +bool CSSAnimationController::wantsScrollUpdates() const +{ + return m_data->wantsScrollUpdates(); +} + +void CSSAnimationController::scrollWasUpdated() +{ + m_data->scrollWasUpdated(); +} +#endif + +bool CSSAnimationController::hasAnimations() const +{ + return m_data->hasAnimations(); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/animation/AnimationController.h b/Source/WebCore/page/animation/CSSAnimationController.h index c29af5d82..d473cd173 100644 --- a/Source/WebCore/page/animation/AnimationController.h +++ b/Source/WebCore/page/animation/CSSAnimationController.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,69 +26,80 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AnimationController_h -#define AnimationController_h +#pragma once +#include "AnimationBase.h" #include "CSSPropertyNames.h" #include <wtf/Forward.h> -#include <wtf/OwnPtr.h> namespace WebCore { -class AnimationBase; -class AnimationControllerPrivate; +class CSSAnimationControllerPrivate; class Document; class Element; class Frame; +class LayoutRect; class RenderElement; class RenderStyle; -class AnimationController { +class CSSAnimationController { + WTF_MAKE_FAST_ALLOCATED; public: - explicit AnimationController(Frame&); - ~AnimationController(); + explicit CSSAnimationController(Frame&); + ~CSSAnimationController(); - void cancelAnimations(RenderElement*); - PassRef<RenderStyle> updateAnimations(RenderElement&, PassRef<RenderStyle> newStyle); - PassRefPtr<RenderStyle> getAnimatedStyleForRenderer(RenderElement*); + void cancelAnimations(RenderElement&); + bool updateAnimations(RenderElement&, const RenderStyle& newStyle, std::unique_ptr<RenderStyle>& animatedStyle); + std::unique_ptr<RenderStyle> getAnimatedStyleForRenderer(RenderElement&); + + // If possible, compute the visual extent of any transform animation on the given renderer + // using the given rect, returning the result in the rect. Return false if there is some + // transform animation but we were unable to cheaply compute its affect on the extent. + bool computeExtentOfAnimation(RenderElement&, LayoutRect&) const; // This is called when an accelerated animation or transition has actually started to animate. - void notifyAnimationStarted(RenderElement*, double startTime); + void notifyAnimationStarted(RenderElement&, double startTime); - bool pauseAnimationAtTime(RenderElement*, const AtomicString& name, double t); // To be used only for testing - bool pauseTransitionAtTime(RenderElement*, const String& property, double t); // To be used only for testing - unsigned numberOfActiveAnimations(Document*) const; // To be used only for testing + WEBCORE_EXPORT bool pauseAnimationAtTime(RenderElement*, const AtomicString& name, double t); // To be used only for testing + WEBCORE_EXPORT bool pauseTransitionAtTime(RenderElement*, const String& property, double t); // To be used only for testing + WEBCORE_EXPORT unsigned numberOfActiveAnimations(Document*) const; // To be used only for testing - bool isRunningAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow = true) const; - bool isRunningAcceleratedAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow = true) const; + bool isRunningAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; + bool isRunningAcceleratedAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; - bool isSuspended() const; - void suspendAnimations(); - void resumeAnimations(); -#if ENABLE(REQUEST_ANIMATION_FRAME) + WEBCORE_EXPORT bool isSuspended() const; + WEBCORE_EXPORT void suspendAnimations(); + WEBCORE_EXPORT void resumeAnimations(); void serviceAnimations(); -#endif - void suspendAnimationsForDocument(Document*); - void resumeAnimationsForDocument(Document*); + WEBCORE_EXPORT void suspendAnimationsForDocument(Document*); + WEBCORE_EXPORT void resumeAnimationsForDocument(Document*); + WEBCORE_EXPORT bool animationsAreSuspendedForDocument(Document*); + void detachFromDocument(Document*); void startAnimationsIfNotSuspended(Document*); void beginAnimationUpdate(); void endAnimationUpdate(); - bool allowsNewAnimationsWhileSuspended() const; - void setAllowsNewAnimationsWhileSuspended(bool); + WEBCORE_EXPORT bool allowsNewAnimationsWhileSuspended() const; + WEBCORE_EXPORT void setAllowsNewAnimationsWhileSuspended(bool); static bool supportsAcceleratedAnimationOfProperty(CSSPropertyID); +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool wantsScrollUpdates() const; + void scrollWasUpdated(); +#endif + + bool hasAnimations() const; + private: - const std::unique_ptr<AnimationControllerPrivate> m_data; - int m_beginAnimationUpdateCount; + const std::unique_ptr<CSSAnimationControllerPrivate> m_data; }; class AnimationUpdateBlock { public: - AnimationUpdateBlock(AnimationController* animationController) + AnimationUpdateBlock(CSSAnimationController* animationController) : m_animationController(animationController) { if (m_animationController) @@ -101,9 +112,7 @@ public: m_animationController->endAnimationUpdate(); } - AnimationController* m_animationController; + CSSAnimationController* m_animationController; }; } // namespace WebCore - -#endif // AnimationController_h diff --git a/Source/WebCore/page/animation/AnimationControllerPrivate.h b/Source/WebCore/page/animation/CSSAnimationControllerPrivate.h index f03092280..c4996c331 100644 --- a/Source/WebCore/page/animation/AnimationControllerPrivate.h +++ b/Source/WebCore/page/animation/CSSAnimationControllerPrivate.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,76 +26,68 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef AnimationControllerPrivate_h -#define AnimationControllerPrivate_h +#pragma once -#include "CSSPropertyNames.h" +#include "AnimationBase.h" #include "Timer.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> #include <wtf/Vector.h> -#include <wtf/text/AtomicString.h> -#include <wtf/text/WTFString.h> namespace WebCore { -class AnimationBase; class CompositeAnimation; class Document; -class Element; class Frame; -class RenderElement; -class RenderStyle; -enum SetChanged { - DoNotCallSetChanged = 0, - CallSetChanged = 1 -}; +enum SetChanged { DoNotCallSetChanged, CallSetChanged }; -class AnimationControllerPrivate { - WTF_MAKE_NONCOPYABLE(AnimationControllerPrivate); WTF_MAKE_FAST_ALLOCATED; +class CSSAnimationControllerPrivate { + WTF_MAKE_FAST_ALLOCATED; public: - explicit AnimationControllerPrivate(Frame&); - ~AnimationControllerPrivate(); + explicit CSSAnimationControllerPrivate(Frame&); + ~CSSAnimationControllerPrivate(); // Returns the time until the next animation needs to be serviced, or -1 if there are none. double updateAnimations(SetChanged callSetChanged = DoNotCallSetChanged); void updateAnimationTimer(SetChanged callSetChanged = DoNotCallSetChanged); - CompositeAnimation& ensureCompositeAnimation(RenderElement*); - bool clear(RenderElement*); + CompositeAnimation& ensureCompositeAnimation(RenderElement&); + bool clear(RenderElement&); - void updateStyleIfNeededDispatcherFired(Timer<AnimationControllerPrivate>&); + void updateStyleIfNeededDispatcherFired(); void startUpdateStyleIfNeededDispatcher(); - void addEventToDispatch(PassRefPtr<Element> element, const AtomicString& eventType, const String& name, double elapsedTime); - void addElementChangeToDispatch(PassRef<Element>); + void addEventToDispatch(Element&, const AtomicString& eventType, const String& name, double elapsedTime); + void addElementChangeToDispatch(Element&); bool hasAnimations() const { return !m_compositeAnimations.isEmpty(); } bool isSuspended() const { return m_isSuspended; } void suspendAnimations(); void resumeAnimations(); -#if ENABLE(REQUEST_ANIMATION_FRAME) void animationFrameCallbackFired(); -#endif void suspendAnimationsForDocument(Document*); void resumeAnimationsForDocument(Document*); + bool animationsAreSuspendedForDocument(Document*); void startAnimationsIfNotSuspended(Document*); + void detachFromDocument(Document*); - bool isRunningAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow) const; - bool isRunningAcceleratedAnimationOnRenderer(RenderElement*, CSSPropertyID, bool isRunningNow) const; + bool isRunningAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; + bool isRunningAcceleratedAnimationOnRenderer(RenderElement&, CSSPropertyID, AnimationBase::RunningState) const; bool pauseAnimationAtTime(RenderElement*, const AtomicString& name, double t); bool pauseTransitionAtTime(RenderElement*, const String& property, double t); unsigned numberOfActiveAnimations(Document*) const; - PassRefPtr<RenderStyle> getAnimatedStyleForRenderer(RenderElement* renderer); + std::unique_ptr<RenderStyle> getAnimatedStyleForRenderer(RenderElement&); + + bool computeExtentOfAnimation(RenderElement&, LayoutRect&) const; double beginAnimationUpdateTime(); void setBeginAnimationUpdateTime(double t) { m_beginAnimationUpdateTime = t; } + + void beginAnimationUpdate(); void endAnimationUpdate(); void receivedStartTimeResponse(double); @@ -107,48 +99,65 @@ public: void animationWillBeRemoved(AnimationBase*); - void updateAnimationTimerForRenderer(RenderElement*); + void updateAnimationTimerForRenderer(RenderElement&); bool allowsNewAnimationsWhileSuspended() const { return m_allowsNewAnimationsWhileSuspended; } void setAllowsNewAnimationsWhileSuspended(bool); + void setRequiresLayout() { m_requiresLayout = true; } + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool wantsScrollUpdates() const { return !m_animationsDependentOnScroll.isEmpty(); } + void addToAnimationsDependentOnScroll(AnimationBase*); + void removeFromAnimationsDependentOnScroll(AnimationBase*); + + void scrollWasUpdated(); + float scrollPosition() const { return m_scrollPosition; } +#endif + private: - void animationTimerFired(Timer<AnimationControllerPrivate>&); + void animationTimerFired(); void styleAvailable(); void fireEventsAndUpdateStyle(); void startTimeResponse(double t); HashMap<RenderElement*, RefPtr<CompositeAnimation>> m_compositeAnimations; - Timer<AnimationControllerPrivate> m_animationTimer; - Timer<AnimationControllerPrivate> m_updateStyleIfNeededDispatcher; + Timer m_animationTimer; + Timer m_updateStyleIfNeededDispatcher; Frame& m_frame; - - class EventToDispatch { - public: - RefPtr<Element> element; + + struct EventToDispatch { + Ref<Element> element; AtomicString eventType; String name; double elapsedTime; }; - Vector<EventToDispatch> m_eventsToDispatch; Vector<Ref<Element>> m_elementChangesToDispatch; - + HashSet<Document*> m_suspendedDocuments; + double m_beginAnimationUpdateTime; - typedef HashSet<RefPtr<AnimationBase>> WaitingAnimationsSet; - WaitingAnimationsSet m_animationsWaitingForStyle; - WaitingAnimationsSet m_animationsWaitingForStartTimeResponse; + using AnimationsSet = HashSet<RefPtr<AnimationBase>>; + AnimationsSet m_animationsWaitingForStyle; + AnimationsSet m_animationsWaitingForStartTimeResponse; + + int m_beginAnimationUpdateCount; + bool m_waitingForAsyncStartNotification; bool m_isSuspended; + bool m_requiresLayout { false }; // Used to flag whether we should revert to previous buggy // behavior of allowing new transitions and animations to // run even when this object is suspended. bool m_allowsNewAnimationsWhileSuspended; + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + AnimationsSet m_animationsDependentOnScroll; + float m_scrollPosition { 0 }; +#endif }; } // namespace WebCore - -#endif // AnimationControllerPrivate_h diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp index 291129e93..98b87c947 100644 --- a/Source/WebCore/page/animation/CSSPropertyAnimation.cpp +++ b/Source/WebCore/page/animation/CSSPropertyAnimation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2013, 2016 Apple Inc. All rights reserved. * Copyright (C) 2012, 2013 Adobe Systems Incorporated. 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. * @@ -41,7 +41,9 @@ #include "CachedImage.h" #include "ClipPathOperation.h" #include "FloatConversion.h" +#include "FontTaggedSettings.h" #include "IdentityTransformOperation.h" +#include "Logging.h" #include "Matrix3DTransformOperation.h" #include "MatrixTransformOperation.h" #include "RenderBox.h" @@ -50,9 +52,13 @@ #include "StyleGeneratedImage.h" #include "StylePropertyShorthand.h" #include "StyleResolver.h" +#include "TextStream.h" #include <algorithm> +#include <memory> #include <wtf/MathExtras.h> +#include <wtf/NeverDestroyed.h> #include <wtf/Noncopyable.h> +#include <wtf/PointerComparison.h> #include <wtf/RefCounted.h> namespace WebCore { @@ -62,11 +68,6 @@ static inline int blendFunc(const AnimationBase*, int from, int to, double progr return blend(from, to, progress); } -static inline unsigned blendFunc(const AnimationBase*, unsigned from, unsigned to, double progress) -{ - return blend(from, to, progress); -} - static inline double blendFunc(const AnimationBase*, double from, double to, double progress) { return blend(from, to, progress); @@ -84,13 +85,12 @@ static inline Color blendFunc(const AnimationBase*, const Color& from, const Col static inline Length blendFunc(const AnimationBase*, const Length& from, const Length& to, double progress) { - return to.blend(from, narrowPrecisionToFloat(progress)); + return blend(from, to, progress); } static inline LengthSize blendFunc(const AnimationBase* anim, const LengthSize& from, const LengthSize& to, double progress) { - return LengthSize(blendFunc(anim, from.width(), to.width(), progress), - blendFunc(anim, from.height(), to.height(), progress)); + return { blendFunc(anim, from.width, to.width, progress), blendFunc(anim, from.height, to.height, progress) }; } static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, ShadowStyle to, double progress) @@ -104,28 +104,28 @@ static inline ShadowStyle blendFunc(const AnimationBase* anim, ShadowStyle from, return result > 0 ? Normal : Inset; } -static inline PassOwnPtr<ShadowData> blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress) +static inline std::unique_ptr<ShadowData> blendFunc(const AnimationBase* anim, const ShadowData* from, const ShadowData* to, double progress) { ASSERT(from && to); if (from->style() != to->style()) - return adoptPtr(new ShadowData(*to)); - - return adoptPtr(new ShadowData(blend(from->location(), to->location(), progress), - blend(from->radius(), to->radius(), progress), - blend(from->spread(), to->spread(), progress), - blendFunc(anim, from->style(), to->style(), progress), - from->isWebkitBoxShadow(), - blend(from->color(), to->color(), progress))); + return std::make_unique<ShadowData>(*to); + + return std::make_unique<ShadowData>(blend(from->location(), to->location(), progress), + blend(from->radius(), to->radius(), progress), + blend(from->spread(), to->spread(), progress), + blendFunc(anim, from->style(), to->style(), progress), + from->isWebkitBoxShadow(), + blend(from->color(), to->color(), progress)); } -static inline TransformOperations blendFunc(const AnimationBase* anim, const TransformOperations& from, const TransformOperations& to, double progress) +static inline TransformOperations blendFunc(const AnimationBase* animation, const TransformOperations& from, const TransformOperations& to, double progress) { - if (anim->isTransformFunctionListValid()) + if (animation->transformFunctionListsMatch()) return to.blendByMatchingOperations(from, progress); - return to.blendByUsingMatrixInterpolation(from, progress, anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize()); + return to.blendByUsingMatrixInterpolation(from, progress, is<RenderBox>(*animation->renderer()) ? downcast<RenderBox>(*animation->renderer()).borderBoxRect().size() : LayoutSize()); } -static inline PassRefPtr<ClipPathOperation> blendFunc(const AnimationBase*, ClipPathOperation* from, ClipPathOperation* to, double progress) +static inline RefPtr<ClipPathOperation> blendFunc(const AnimationBase*, ClipPathOperation* from, ClipPathOperation* to, double progress) { if (!from || !to) return to; @@ -134,44 +134,40 @@ static inline PassRefPtr<ClipPathOperation> blendFunc(const AnimationBase*, Clip if (from->type() != ClipPathOperation::Shape || to->type() != ClipPathOperation::Shape) return to; - const BasicShape* fromShape = static_cast<ShapeClipPathOperation*>(from)->basicShape(); - const BasicShape* toShape = static_cast<ShapeClipPathOperation*>(to)->basicShape(); + const BasicShape& fromShape = downcast<ShapeClipPathOperation>(*from).basicShape(); + const BasicShape& toShape = downcast<ShapeClipPathOperation>(*to).basicShape(); - if (!fromShape->canBlend(toShape)) + if (!fromShape.canBlend(toShape)) return to; - return ShapeClipPathOperation::create(toShape->blend(fromShape, progress)); + return ShapeClipPathOperation::create(toShape.blend(fromShape, progress)); } -#if ENABLE(CSS_SHAPES) -static inline PassRefPtr<ShapeValue> blendFunc(const AnimationBase*, ShapeValue* from, ShapeValue* to, double progress) +static inline RefPtr<ShapeValue> blendFunc(const AnimationBase*, ShapeValue* from, ShapeValue* to, double progress) { if (!from || !to) return to; - // FIXME Bug 102723: Shape-inside should be able to animate a value of 'outside-shape' when shape-outside is set to a BasicShape - if (from->type() != ShapeValue::Shape || to->type() != ShapeValue::Shape) + if (from->type() != ShapeValue::Type::Shape || to->type() != ShapeValue::Type::Shape) return to; - if (from->layoutBox() != to->layoutBox()) + if (from->cssBox() != to->cssBox()) return to; - const BasicShape* fromShape = from->shape(); - const BasicShape* toShape = to->shape(); + const BasicShape& fromShape = *from->shape(); + const BasicShape& toShape = *to->shape(); - if (!fromShape->canBlend(toShape)) + if (!fromShape.canBlend(toShape)) return to; - return ShapeValue::createShapeValue(toShape->blend(fromShape, progress), to->layoutBox()); + return ShapeValue::create(toShape.blend(fromShape, progress), to->cssBox()); } -#endif -#if ENABLE(CSS_FILTERS) -static inline PassRefPtr<FilterOperation> blendFunc(const AnimationBase* anim, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false) +static inline RefPtr<FilterOperation> blendFunc(const AnimationBase* animation, FilterOperation* fromOp, FilterOperation* toOp, double progress, bool blendToPassthrough = false) { ASSERT(toOp); if (toOp->blendingNeedsRendererSize()) { - LayoutSize size = anim->renderer()->isBox() ? toRenderBox(anim->renderer())->borderBoxRect().size() : LayoutSize(); + LayoutSize size = is<RenderBox>(*animation->renderer()) ? downcast<RenderBox>(*animation->renderer()).borderBoxRect().size() : LayoutSize(); return toOp->blend(fromOp, progress, size, blendToPassthrough); } return toOp->blend(fromOp, progress, blendToPassthrough); @@ -200,12 +196,18 @@ static inline FilterOperations blendFilterOperations(const AnimationBase* anim, return result; } -static inline FilterOperations blendFunc(const AnimationBase* anim, const FilterOperations& from, const FilterOperations& to, double progress) +static inline FilterOperations blendFunc(const AnimationBase* anim, const FilterOperations& from, const FilterOperations& to, double progress, bool animatingBackdropFilter = false) { FilterOperations result; // If we have a filter function list, use that to do a per-function animation. +#if ENABLE(FILTERS_LEVEL_2) + if ((!animatingBackdropFilter && anim->filterFunctionListsMatch()) || (animatingBackdropFilter && anim->backdropFilterFunctionListsMatch())) +#else + UNUSED_PARAM(animatingBackdropFilter); if (anim->filterFunctionListsMatch()) +#endif + result = blendFilterOperations(anim, from, to, progress); else { // If the filter function lists don't match, we could try to cross-fade, but don't yet have a way to represent that in CSS. @@ -216,20 +218,18 @@ static inline FilterOperations blendFunc(const AnimationBase* anim, const Filter return result; } -static inline PassRefPtr<StyleImage> blendFilter(const AnimationBase* anim, CachedImage* image, const FilterOperations& from, const FilterOperations& to, double progress) +static inline RefPtr<StyleImage> blendFilter(const AnimationBase* anim, CachedImage* image, const FilterOperations& from, const FilterOperations& to, double progress) { ASSERT(image); FilterOperations filterResult = blendFilterOperations(anim, from, to, progress); - RefPtr<StyleCachedImage> styledImage = StyleCachedImage::create(image); - auto imageValue = CSSImageValue::create(image->url(), styledImage.get()); - auto filterValue = ComputedStyleExtractor::valueForFilter(&anim->renderer()->style(), filterResult, DoNotAdjustPixelValues); + auto imageValue = CSSImageValue::create(*image); + auto filterValue = ComputedStyleExtractor::valueForFilter(anim->renderer()->style(), filterResult, DoNotAdjustPixelValues); - auto result = CSSFilterImageValue::create(std::move(imageValue), std::move(filterValue)); + auto result = CSSFilterImageValue::create(WTFMove(imageValue), WTFMove(filterValue)); result.get().setFilterOperations(filterResult); - return StyleGeneratedImage::create(std::move(result)); + return StyleGeneratedImage::create(WTFMove(result)); } -#endif // ENABLE(CSS_FILTERS) static inline EVisibility blendFunc(const AnimationBase* anim, EVisibility from, EVisibility to, double progress) { @@ -252,12 +252,12 @@ static inline LengthBox blendFunc(const AnimationBase* anim, const LengthBox& fr return result; } -#if ENABLE(SVG) -static inline SVGLength blendFunc(const AnimationBase*, const SVGLength& from, const SVGLength& to, double progress) +static inline SVGLengthValue blendFunc(const AnimationBase*, const SVGLengthValue& from, const SVGLengthValue& to, double progress) { return to.blend(from, narrowPrecisionToFloat(progress)); } -static inline Vector<SVGLength> blendFunc(const AnimationBase*, const Vector<SVGLength>& from, const Vector<SVGLength>& to, double progress) + +static inline Vector<SVGLengthValue> blendFunc(const AnimationBase*, const Vector<SVGLengthValue>& from, const Vector<SVGLengthValue>& to, double progress) { size_t fromLength = from.size(); size_t toLength = to.size(); @@ -272,14 +272,13 @@ static inline Vector<SVGLength> blendFunc(const AnimationBase*, const Vector<SVG else resultLength = fromLength * toLength; } - Vector<SVGLength> result(resultLength); + Vector<SVGLengthValue> result(resultLength); for (size_t i = 0; i < resultLength; ++i) result[i] = to[i % toLength].blend(from[i % fromLength], narrowPrecisionToFloat(progress)); return result; } -#endif -static inline PassRefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleCachedImage* fromStyleImage, StyleCachedImage* toStyleImage, double progress) +static inline RefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleCachedImage* fromStyleImage, StyleCachedImage* toStyleImage, double progress) { // If progress is at one of the extremes, we want getComputedStyle to show the image, // not a completed cross-fade, so we hand back one of the existing images. @@ -287,69 +286,69 @@ static inline PassRefPtr<StyleImage> crossfadeBlend(const AnimationBase*, StyleC return fromStyleImage; if (progress == 1) return toStyleImage; + if (!fromStyleImage->cachedImage() || !toStyleImage->cachedImage()) + return toStyleImage; - auto fromImageValue = CSSImageValue::create(fromStyleImage->cachedImage()->url(), fromStyleImage); - auto toImageValue = CSSImageValue::create(toStyleImage->cachedImage()->url(), toStyleImage); + auto fromImageValue = CSSImageValue::create(*fromStyleImage->cachedImage()); + auto toImageValue = CSSImageValue::create(*toStyleImage->cachedImage()); + auto percentageValue = CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER); - auto crossfadeValue = CSSCrossfadeValue::create(std::move(fromImageValue), std::move(toImageValue)); - crossfadeValue.get().setPercentage(CSSPrimitiveValue::create(progress, CSSPrimitiveValue::CSS_NUMBER)); - return StyleGeneratedImage::create(std::move(crossfadeValue)); + auto crossfadeValue = CSSCrossfadeValue::create(WTFMove(fromImageValue), WTFMove(toImageValue), WTFMove(percentageValue)); + return StyleGeneratedImage::create(WTFMove(crossfadeValue)); } -static inline PassRefPtr<StyleImage> blendFunc(const AnimationBase* anim, StyleImage* from, StyleImage* to, double progress) +static inline RefPtr<StyleImage> blendFunc(const AnimationBase* anim, StyleImage* from, StyleImage* to, double progress) { if (!from || !to) return to; // Animation between two generated images. Cross fade for all other cases. - if (from->isGeneratedImage() && to->isGeneratedImage()) { - CSSImageGeneratorValue& fromGenerated = toStyleGeneratedImage(from)->imageValue(); - CSSImageGeneratorValue& toGenerated = toStyleGeneratedImage(to)->imageValue(); + if (is<StyleGeneratedImage>(*from) && is<StyleGeneratedImage>(*to)) { + CSSImageGeneratorValue& fromGenerated = downcast<StyleGeneratedImage>(*from).imageValue(); + CSSImageGeneratorValue& toGenerated = downcast<StyleGeneratedImage>(*to).imageValue(); -#if ENABLE(CSS_FILTERS) - if (fromGenerated.isFilterImageValue() && toGenerated.isFilterImageValue()) { + if (is<CSSFilterImageValue>(fromGenerated) && is<CSSFilterImageValue>(toGenerated)) { // Animation of generated images just possible if input images are equal. // Otherwise fall back to cross fade animation. - CSSFilterImageValue& fromFilter = toCSSFilterImageValue(fromGenerated); - CSSFilterImageValue& toFilter = toCSSFilterImageValue(toGenerated); + CSSFilterImageValue& fromFilter = downcast<CSSFilterImageValue>(fromGenerated); + CSSFilterImageValue& toFilter = downcast<CSSFilterImageValue>(toGenerated); if (fromFilter.equalInputImages(toFilter) && fromFilter.cachedImage()) return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), toFilter.filterOperations(), progress); } -#endif - if (fromGenerated.isCrossfadeValue() && toGenerated.isCrossfadeValue()) { - CSSCrossfadeValue& fromCrossfade = toCSSCrossfadeValue(fromGenerated); - CSSCrossfadeValue& toCrossfade = toCSSCrossfadeValue(toGenerated); - if (fromCrossfade.equalInputImages(toCrossfade)) - return StyleGeneratedImage::create(*toCrossfade.blend(fromCrossfade, progress)); + if (is<CSSCrossfadeValue>(fromGenerated) && is<CSSCrossfadeValue>(toGenerated)) { + CSSCrossfadeValue& fromCrossfade = downcast<CSSCrossfadeValue>(fromGenerated); + CSSCrossfadeValue& toCrossfade = downcast<CSSCrossfadeValue>(toGenerated); + if (fromCrossfade.equalInputImages(toCrossfade)) { + if (auto crossfadeBlend = toCrossfade.blend(fromCrossfade, progress)) + return StyleGeneratedImage::create(*crossfadeBlend); + } } // FIXME: Add support for animation between two *gradient() functions. // https://bugs.webkit.org/show_bug.cgi?id=119956 -#if ENABLE(CSS_FILTERS) - } else if (from->isGeneratedImage() && to->isCachedImage()) { - CSSImageGeneratorValue& fromGenerated = toStyleGeneratedImage(from)->imageValue(); - if (fromGenerated.isFilterImageValue()) { - CSSFilterImageValue& fromFilter = toCSSFilterImageValue(fromGenerated); - if (fromFilter.cachedImage() && static_cast<StyleCachedImage*>(to)->cachedImage() == fromFilter.cachedImage()) + } else if (is<StyleGeneratedImage>(*from) && is<StyleCachedImage>(*to)) { + CSSImageGeneratorValue& fromGenerated = downcast<StyleGeneratedImage>(*from).imageValue(); + if (is<CSSFilterImageValue>(fromGenerated)) { + CSSFilterImageValue& fromFilter = downcast<CSSFilterImageValue>(fromGenerated); + if (fromFilter.cachedImage() && downcast<StyleCachedImage>(*to).cachedImage() == fromFilter.cachedImage()) return blendFilter(anim, fromFilter.cachedImage(), fromFilter.filterOperations(), FilterOperations(), progress); } // FIXME: Add interpolation between cross-fade and image source. - } else if (from->isCachedImage() && to->isGeneratedImage()) { - CSSImageGeneratorValue& toGenerated = toStyleGeneratedImage(to)->imageValue(); - if (toGenerated.isFilterImageValue()) { - CSSFilterImageValue& toFilter = toCSSFilterImageValue(toGenerated); - if (toFilter.cachedImage() && static_cast<StyleCachedImage*>(from)->cachedImage() == toFilter.cachedImage()) + } else if (is<StyleCachedImage>(*from) && is<StyleGeneratedImage>(*to)) { + CSSImageGeneratorValue& toGenerated = downcast<StyleGeneratedImage>(*to).imageValue(); + if (is<CSSFilterImageValue>(toGenerated)) { + CSSFilterImageValue& toFilter = downcast<CSSFilterImageValue>(toGenerated); + if (toFilter.cachedImage() && downcast<StyleCachedImage>(*from).cachedImage() == toFilter.cachedImage()) return blendFilter(anim, toFilter.cachedImage(), FilterOperations(), toFilter.filterOperations(), progress); } -#endif // FIXME: Add interpolation between image source and cross-fade. } // FIXME: Add support cross fade between cached and generated images. // https://bugs.webkit.org/show_bug.cgi?id=78293 - if (from->isCachedImage() && to->isCachedImage()) - return crossfadeBlend(anim, static_cast<StyleCachedImage*>(from), static_cast<StyleCachedImage*>(to), progress); + if (is<StyleCachedImage>(*from) && is<StyleCachedImage>(*to)) + return crossfadeBlend(anim, downcast<StyleCachedImage>(from), downcast<StyleCachedImage>(to), progress); return to; } @@ -367,11 +366,31 @@ static inline NinePieceImage blendFunc(const AnimationBase* anim, const NinePiec if (from.image()->imageSize(anim->renderer(), 1.0) != to.image()->imageSize(anim->renderer(), 1.0)) return to; - RefPtr<StyleImage> newContentImage = blendFunc(anim, from.image(), to.image(), progress); + return NinePieceImage(blendFunc(anim, from.image(), to.image(), progress), + from.imageSlices(), from.fill(), from.borderSlices(), from.outset(), from.horizontalRule(), from.verticalRule()); +} + +#if ENABLE(VARIATION_FONTS) - return NinePieceImage(newContentImage, from.imageSlices(), from.fill(), from.borderSlices(), from.outset(), from.horizontalRule(), from.verticalRule()); +static inline FontVariationSettings blendFunc(const AnimationBase* anim, const FontVariationSettings& from, const FontVariationSettings& to, double progress) +{ + if (from.size() != to.size()) + return FontVariationSettings(); + FontVariationSettings result; + unsigned size = from.size(); + for (unsigned i = 0; i < size; ++i) { + auto& fromItem = from.at(i); + auto& toItem = to.at(i); + if (fromItem.tag() != toItem.tag()) + return FontVariationSettings(); + float interpolated = blendFunc(anim, fromItem.value(), toItem.value(), progress); + result.insert({ fromItem.tag(), interpolated }); + } + return result; } +#endif + class AnimationPropertyWrapperBase { WTF_MAKE_NONCOPYABLE(AnimationPropertyWrapperBase); WTF_MAKE_FAST_ALLOCATED; @@ -386,12 +405,14 @@ public: virtual bool isShorthandWrapper() const { return false; } virtual bool equals(const RenderStyle* a, const RenderStyle* b) const = 0; virtual void blend(const AnimationBase*, RenderStyle*, const RenderStyle*, const RenderStyle*, double) const = 0; + +#if !LOG_DISABLED + virtual void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double) const = 0; +#endif CSSPropertyID property() const { return m_prop; } -#if USE(ACCELERATED_COMPOSITING) virtual bool animationIsAccelerated() const { return false; } -#endif private: CSSPropertyID m_prop; @@ -399,6 +420,7 @@ private: template <typename T> class PropertyWrapperGetter : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperGetter(CSSPropertyID prop, T (RenderStyle::*getter)() const) : AnimationPropertyWrapperBase(prop) @@ -406,23 +428,34 @@ public: { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. - if ((!a && !b) || a == b) + if (a == b) return true; if (!a || !b) return false; return (a->*m_getter)() == (b->*m_getter)(); } + T value(const RenderStyle* a) const + { + return (a->*m_getter)(); + } + +#if !LOG_DISABLED + void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final + { + LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result)); + } +#endif + protected: T (RenderStyle::*m_getter)() const; }; template <typename T> class PropertyWrapper : public PropertyWrapperGetter<T> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapper(CSSPropertyID prop, T (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) : PropertyWrapperGetter<T>(prop, getter) @@ -430,7 +463,7 @@ public: { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T>::m_getter)(), (b->*PropertyWrapperGetter<T>::m_getter)(), progress)); } @@ -441,69 +474,131 @@ protected: template <typename T> class RefCountedPropertyWrapper : public PropertyWrapperGetter<T*> { + WTF_MAKE_FAST_ALLOCATED; public: - RefCountedPropertyWrapper(CSSPropertyID prop, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<T>)) + RefCountedPropertyWrapper(CSSPropertyID prop, T* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<T>&&)) : PropertyWrapperGetter<T*>(prop, getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<T*>::m_getter)(), (b->*PropertyWrapperGetter<T*>::m_getter)(), progress)); } protected: - void (RenderStyle::*m_setter)(PassRefPtr<T>); + void (RenderStyle::*m_setter)(RefPtr<T>&&); }; template <typename T> class LengthPropertyWrapper : public PropertyWrapperGetter<const T&> { + WTF_MAKE_FAST_ALLOCATED; public: - LengthPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T)) + LengthPropertyWrapper(CSSPropertyID prop, const T& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(T&&)) : PropertyWrapperGetter<const T&>(prop, getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const T&>::m_getter)(), (b->*PropertyWrapperGetter<const T&>::m_getter)(), progress)); } protected: - void (RenderStyle::*m_setter)(T); + void (RenderStyle::*m_setter)(T&&); }; class PropertyWrapperClipPath : public RefCountedPropertyWrapper<ClipPathOperation> { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperClipPath(CSSPropertyID prop, ClipPathOperation* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<ClipPathOperation>)) + PropertyWrapperClipPath(CSSPropertyID prop, ClipPathOperation* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<ClipPathOperation>&&)) : RefCountedPropertyWrapper<ClipPathOperation>(prop, getter, setter) { } + + bool equals(const RenderStyle* a, const RenderStyle* b) const override + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (a == b) + return true; + if (!a || !b) + return false; + + ClipPathOperation* clipPathA = (a->*m_getter)(); + ClipPathOperation* clipPathB = (b->*m_getter)(); + if (clipPathA == clipPathB) + return true; + if (!clipPathA || !clipPathB) + return false; + return *clipPathA == *clipPathB; + } +}; + +#if ENABLE(VARIATION_FONTS) +class PropertyWrapperFontVariationSettings : public PropertyWrapper<FontVariationSettings> { + WTF_MAKE_FAST_ALLOCATED; +public: + PropertyWrapperFontVariationSettings(CSSPropertyID prop, FontVariationSettings (RenderStyle::*getter)() const, void (RenderStyle::*setter)(FontVariationSettings)) + : PropertyWrapper<FontVariationSettings>(prop, getter, setter) + { + } + + bool equals(const RenderStyle* a, const RenderStyle* b) const override + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (a == b) + return true; + if (!a || !b) + return false; + + const FontVariationSettings& variationSettingsA = (a->*m_getter)(); + const FontVariationSettings& variationSettingsB = (b->*m_getter)(); + return variationSettingsA == variationSettingsB; + } }; +#endif -#if ENABLE(CSS_SHAPES) class PropertyWrapperShape : public RefCountedPropertyWrapper<ShapeValue> { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperShape(CSSPropertyID prop, ShapeValue* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<ShapeValue>)) + PropertyWrapperShape(CSSPropertyID prop, ShapeValue* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<ShapeValue>&&)) : RefCountedPropertyWrapper<ShapeValue>(prop, getter, setter) { } + + bool equals(const RenderStyle* a, const RenderStyle* b) const override + { + // If the style pointers are the same, don't bother doing the test. + // If either is null, return false. If both are null, return true. + if (a == b) + return true; + if (!a || !b) + return false; + + ShapeValue* shapeA = (a->*m_getter)(); + ShapeValue* shapeB = (b->*m_getter)(); + if (shapeA == shapeB) + return true; + if (!shapeA || !shapeB) + return false; + return *shapeA == *shapeB; + } }; -#endif class StyleImagePropertyWrapper : public RefCountedPropertyWrapper<StyleImage> { + WTF_MAKE_FAST_ALLOCATED; public: - StyleImagePropertyWrapper(CSSPropertyID prop, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassRefPtr<StyleImage>)) + StyleImagePropertyWrapper(CSSPropertyID prop, StyleImage* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(RefPtr<StyleImage>&&)) : RefCountedPropertyWrapper<StyleImage>(prop, getter, setter) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. if (a == b) return true; if (!a || !b) @@ -511,67 +606,82 @@ public: StyleImage* imageA = (a->*m_getter)(); StyleImage* imageB = (b->*m_getter)(); - return StyleImage::imagesEquivalent(imageA, imageB); + return arePointingToEqualData(imageA, imageB); } }; -class PropertyWrapperColor : public PropertyWrapperGetter<Color> { +class PropertyWrapperColor : public PropertyWrapperGetter<const Color&> { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) - : PropertyWrapperGetter<Color>(prop, getter) + PropertyWrapperColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + : PropertyWrapperGetter<const Color&>(prop, getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<Color>::m_getter)(), (b->*PropertyWrapperGetter<Color>::m_getter)(), progress)); + (dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<const Color&>::m_getter)(), (b->*PropertyWrapperGetter<const Color&>::m_getter)(), progress)); } protected: void (RenderStyle::*m_setter)(const Color&); }; - -#if USE(ACCELERATED_COMPOSITING) class PropertyWrapperAcceleratedOpacity : public PropertyWrapper<float> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperAcceleratedOpacity() : PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity) { } - virtual bool animationIsAccelerated() const { return true; } + bool animationIsAccelerated() const override { return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - float fromOpacity = a->opacity(); - - // This makes sure we put the object being animated into a RenderLayer during the animation - dst->setOpacity(blendFunc(anim, (fromOpacity == 1) ? 0.999999f : fromOpacity, b->opacity(), progress)); + dst->setOpacity(blendFunc(anim, a->opacity(), b->opacity(), progress)); } }; class PropertyWrapperAcceleratedTransform : public PropertyWrapper<const TransformOperations&> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperAcceleratedTransform() - : PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform) + : PropertyWrapper<const TransformOperations&>(CSSPropertyTransform, &RenderStyle::transform, &RenderStyle::setTransform) { } - virtual bool animationIsAccelerated() const { return true; } + bool animationIsAccelerated() const override { return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { dst->setTransform(blendFunc(anim, a->transform(), b->transform(), progress)); } }; -#if ENABLE(CSS_FILTERS) class PropertyWrapperAcceleratedFilter : public PropertyWrapper<const FilterOperations&> { + WTF_MAKE_FAST_ALLOCATED; public: PropertyWrapperAcceleratedFilter() - : PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitFilter, &RenderStyle::filter, &RenderStyle::setFilter) + : PropertyWrapper<const FilterOperations&>(CSSPropertyFilter, &RenderStyle::filter, &RenderStyle::setFilter) + { + } + + bool animationIsAccelerated() const override { return true; } + + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override + { + dst->setFilter(blendFunc(anim, a->filter(), b->filter(), progress)); + } +}; + +#if ENABLE(FILTERS_LEVEL_2) +class PropertyWrapperAcceleratedBackdropFilter : public PropertyWrapper<const FilterOperations&> { + WTF_MAKE_FAST_ALLOCATED; +public: + PropertyWrapperAcceleratedBackdropFilter() + : PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitBackdropFilter, &RenderStyle::backdropFilter, &RenderStyle::setBackdropFilter) { } @@ -579,11 +689,10 @@ public: virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const { - dst->setFilter(blendFunc(anim, a->filter(), b->filter(), progress)); + dst->setBackdropFilter(blendFunc(anim, a->backdropFilter(), b->backdropFilter(), progress, true)); } }; #endif -#endif // USE(ACCELERATED_COMPOSITING) static inline size_t shadowListLength(const ShadowData* shadow) { @@ -595,32 +704,37 @@ static inline size_t shadowListLength(const ShadowData* shadow) static inline const ShadowData* shadowForBlending(const ShadowData* srcShadow, const ShadowData* otherShadow) { - DEFINE_STATIC_LOCAL(ShadowData, defaultShadowData, (IntPoint(), 0, 0, Normal, false, Color::transparent)); - DEFINE_STATIC_LOCAL(ShadowData, defaultInsetShadowData, (IntPoint(), 0, 0, Inset, false, Color::transparent)); - - DEFINE_STATIC_LOCAL(ShadowData, defaultWebKitBoxShadowData, (IntPoint(), 0, 0, Normal, true, Color::transparent)); - DEFINE_STATIC_LOCAL(ShadowData, defaultInsetWebKitBoxShadowData, (IntPoint(), 0, 0, Inset, true, Color::transparent)); + static NeverDestroyed<ShadowData> defaultShadowData(IntPoint(), 0, 0, Normal, false, Color::transparent); + static NeverDestroyed<ShadowData> defaultInsetShadowData(IntPoint(), 0, 0, Inset, false, Color::transparent); + static NeverDestroyed<ShadowData> defaultWebKitBoxShadowData(IntPoint(), 0, 0, Normal, true, Color::transparent); + static NeverDestroyed<ShadowData> defaultInsetWebKitBoxShadowData(IntPoint(), 0, 0, Inset, true, Color::transparent); if (srcShadow) return srcShadow; if (otherShadow->style() == Inset) - return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData : &defaultInsetShadowData; + return otherShadow->isWebkitBoxShadow() ? &defaultInsetWebKitBoxShadowData.get() : &defaultInsetShadowData.get(); - return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData : &defaultShadowData; + return otherShadow->isWebkitBoxShadow() ? &defaultWebKitBoxShadowData.get() : &defaultShadowData.get(); } class PropertyWrapperShadow : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperShadow(CSSPropertyID prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(PassOwnPtr<ShadowData>, bool)) + PropertyWrapperShadow(CSSPropertyID prop, const ShadowData* (RenderStyle::*getter)() const, void (RenderStyle::*setter)(std::unique_ptr<ShadowData>, bool)) : AnimationPropertyWrapperBase(prop) , m_getter(getter) , m_setter(setter) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { + if (a == b) + return true; + if (!a || !b) + return false; + const ShadowData* shadowA = (a->*m_getter)(); const ShadowData* shadowB = (b->*m_getter)(); @@ -643,7 +757,7 @@ public: return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { const ShadowData* shadowA = (a->*m_getter)(); const ShadowData* shadowB = (b->*m_getter)(); @@ -659,23 +773,31 @@ public: (dst->*m_setter)(blendMismatchedShadowLists(anim, progress, shadowA, shadowB, fromLength, toLength), false); } +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending ShadowData at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif + private: - PassOwnPtr<ShadowData> blendSimpleOrMatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB) const + std::unique_ptr<ShadowData> blendSimpleOrMatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB) const { - OwnPtr<ShadowData> newShadowData; + std::unique_ptr<ShadowData> newShadowData; ShadowData* lastShadow = 0; while (shadowA || shadowB) { const ShadowData* srcShadow = shadowForBlending(shadowA, shadowB); const ShadowData* dstShadow = shadowForBlending(shadowB, shadowA); - OwnPtr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); + std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); ShadowData* blendedShadowPtr = blendedShadow.get(); if (!lastShadow) - newShadowData = blendedShadow.release(); + newShadowData = WTFMove(blendedShadow); else - lastShadow->setNext(blendedShadow.release()); + lastShadow->setNext(WTFMove(blendedShadow)); lastShadow = blendedShadowPtr; @@ -683,10 +805,10 @@ private: shadowB = shadowB ? shadowB->next() : 0; } - return newShadowData.release(); + return newShadowData; } - PassOwnPtr<ShadowData> blendMismatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB, int fromLength, int toLength) const + std::unique_ptr<ShadowData> blendMismatchedShadowLists(const AnimationBase* anim, double progress, const ShadowData* shadowA, const ShadowData* shadowB, int fromLength, int toLength) const { // The shadows in ShadowData are stored in reverse order, so when animating mismatched lists, // reverse them and match from the end. @@ -702,7 +824,7 @@ private: shadowB = shadowB->next(); } - OwnPtr<ShadowData> newShadowData; + std::unique_ptr<ShadowData> newShadowData; int maxLength = std::max(fromLength, toLength); for (int i = 0; i < maxLength; ++i) { @@ -712,32 +834,38 @@ private: const ShadowData* srcShadow = shadowForBlending(fromShadow, toShadow); const ShadowData* dstShadow = shadowForBlending(toShadow, fromShadow); - OwnPtr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); + std::unique_ptr<ShadowData> blendedShadow = blendFunc(anim, srcShadow, dstShadow, progress); // Insert at the start of the list to preserve the order. - blendedShadow->setNext(newShadowData.release()); - newShadowData = blendedShadow.release(); + blendedShadow->setNext(WTFMove(newShadowData)); + newShadowData = WTFMove(blendedShadow); } - return newShadowData.release(); + return newShadowData; } const ShadowData* (RenderStyle::*m_getter)() const; - void (RenderStyle::*m_setter)(PassOwnPtr<ShadowData>, bool); + void (RenderStyle::*m_setter)(std::unique_ptr<ShadowData>, bool); }; class PropertyWrapperMaybeInvalidColor : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperMaybeInvalidColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + PropertyWrapperMaybeInvalidColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) : AnimationPropertyWrapperBase(prop) , m_getter(getter) , m_setter(setter) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - Color fromColor = (a->*m_getter)(); - Color toColor = (b->*m_getter)(); + if (a == b) + return true; + if (!a || !b) + return false; + + Color fromColor = value(a); + Color toColor = value(b); if (!fromColor.isValid() && !toColor.isValid()) return true; @@ -750,10 +878,10 @@ public: return fromColor == toColor; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - Color fromColor = (a->*m_getter)(); - Color toColor = (b->*m_getter)(); + Color fromColor = value(a); + Color toColor = value(b); if (!fromColor.isValid() && !toColor.isValid()) return; @@ -765,46 +893,69 @@ public: (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress)); } + Color value(const RenderStyle* a) const + { + return (a->*m_getter)(); + } + +#if !LOG_DISABLED + void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending " << getPropertyName(property()) << " from " << value(a) << " to " << value(b) << " at " << TextStream::FormatNumberRespectingIntegers(progress) << " -> " << value(result)); + } +#endif + private: - Color (RenderStyle::*m_getter)() const; + const Color& (RenderStyle::*m_getter)() const; void (RenderStyle::*m_setter)(const Color&); }; enum MaybeInvalidColorTag { MaybeInvalidColor }; class PropertyWrapperVisitedAffectedColor : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), - Color (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) + PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), + const Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) : AnimationPropertyWrapperBase(prop) - , m_wrapper(adoptPtr(new PropertyWrapperColor(prop, getter, setter))) - , m_visitedWrapper(adoptPtr(new PropertyWrapperColor(prop, visitedGetter, visitedSetter))) + , m_wrapper(std::make_unique<PropertyWrapperColor>(prop, getter, setter)) + , m_visitedWrapper(std::make_unique<PropertyWrapperColor>(prop, visitedGetter, visitedSetter)) { } - PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, MaybeInvalidColorTag, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), - Color (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) + PropertyWrapperVisitedAffectedColor(CSSPropertyID prop, MaybeInvalidColorTag, const Color& (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&), + const Color& (RenderStyle::*visitedGetter)() const, void (RenderStyle::*visitedSetter)(const Color&)) : AnimationPropertyWrapperBase(prop) - , m_wrapper(adoptPtr(new PropertyWrapperMaybeInvalidColor(prop, getter, setter))) - , m_visitedWrapper(adoptPtr(new PropertyWrapperMaybeInvalidColor(prop, visitedGetter, visitedSetter))) + , m_wrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, getter, setter)) + , m_visitedWrapper(std::make_unique<PropertyWrapperMaybeInvalidColor>(prop, visitedGetter, visitedSetter)) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { return m_wrapper->equals(a, b) && m_visitedWrapper->equals(a, b); } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { m_wrapper->blend(anim, dst, a, b, progress); m_visitedWrapper->blend(anim, dst, a, b, progress); } +#if !LOG_DISABLED + void logBlend(const RenderStyle* a, const RenderStyle* b, const RenderStyle* result, double progress) const final + { + m_wrapper->logBlend(a, b, result, progress); + m_visitedWrapper->logBlend(a, b, result, progress); + } +#endif + private: - OwnPtr<AnimationPropertyWrapperBase> m_wrapper; - OwnPtr<AnimationPropertyWrapperBase> m_visitedWrapper; + std::unique_ptr<AnimationPropertyWrapperBase> m_wrapper; + std::unique_ptr<AnimationPropertyWrapperBase> m_visitedWrapper; }; // Wrapper base class for an animatable property in a FillLayer class FillLayerAnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: FillLayerAnimationPropertyWrapperBase() { @@ -818,6 +969,7 @@ public: template <typename T> class FillLayerPropertyWrapperGetter : public FillLayerAnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; WTF_MAKE_NONCOPYABLE(FillLayerPropertyWrapperGetter); public: FillLayerPropertyWrapperGetter(T (FillLayer::*getter)() const) @@ -825,13 +977,11 @@ public: { } - virtual bool equals(const FillLayer* a, const FillLayer* b) const + bool equals(const FillLayer* a, const FillLayer* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. - if ((!a && !b) || a == b) - return true; - if (!a || !b) + if (a == b) + return true; + if (!a || !b) return false; return (a->*m_getter)() == (b->*m_getter)(); } @@ -842,6 +992,7 @@ protected: template <typename T> class FillLayerPropertyWrapper : public FillLayerPropertyWrapperGetter<const T&> { + WTF_MAKE_FAST_ALLOCATED; public: FillLayerPropertyWrapper(const T& (FillLayer::*getter)() const, void (FillLayer::*setter)(T)) : FillLayerPropertyWrapperGetter<const T&>(getter) @@ -849,7 +1000,7 @@ public: { } - virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const + void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<const T&>::m_getter)(), progress)); } @@ -860,33 +1011,33 @@ protected: template <typename T> class FillLayerRefCountedPropertyWrapper : public FillLayerPropertyWrapperGetter<T*> { + WTF_MAKE_FAST_ALLOCATED; public: - FillLayerRefCountedPropertyWrapper(T* (FillLayer::*getter)() const, void (FillLayer::*setter)(PassRefPtr<T>)) + FillLayerRefCountedPropertyWrapper(T* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<T>&&)) : FillLayerPropertyWrapperGetter<T*>(getter) , m_setter(setter) { } - virtual void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const + void blend(const AnimationBase* anim, FillLayer* dst, const FillLayer* a, const FillLayer* b, double progress) const override { (dst->*m_setter)(blendFunc(anim, (a->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), (b->*FillLayerPropertyWrapperGetter<T*>::m_getter)(), progress)); } protected: - void (FillLayer::*m_setter)(PassRefPtr<T>); + void (FillLayer::*m_setter)(RefPtr<T>&&); }; class FillLayerStyleImagePropertyWrapper : public FillLayerRefCountedPropertyWrapper<StyleImage> { + WTF_MAKE_FAST_ALLOCATED; public: - FillLayerStyleImagePropertyWrapper(StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(PassRefPtr<StyleImage>)) + FillLayerStyleImagePropertyWrapper(StyleImage* (FillLayer::*getter)() const, void (FillLayer::*setter)(RefPtr<StyleImage>&&)) : FillLayerRefCountedPropertyWrapper<StyleImage>(getter, setter) { } - virtual bool equals(const FillLayer* a, const FillLayer* b) const + bool equals(const FillLayer* a, const FillLayer* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. if (a == b) return true; if (!a || !b) @@ -894,14 +1045,15 @@ public: StyleImage* imageA = (a->*m_getter)(); StyleImage* imageB = (b->*m_getter)(); - return StyleImage::imagesEquivalent(imageA, imageB); + return arePointingToEqualData(imageA, imageB); } }; class FillLayersPropertyWrapper : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - typedef const FillLayer* (RenderStyle::*LayersGetter)() const; - typedef FillLayer* (RenderStyle::*LayersAccessor)(); + typedef const FillLayer& (RenderStyle::*LayersGetter)() const; + typedef FillLayer& (RenderStyle::*LayersAccessor)(); FillLayersPropertyWrapper(CSSPropertyID prop, LayersGetter getter, LayersAccessor accessor) : AnimationPropertyWrapperBase(prop) @@ -911,29 +1063,34 @@ public: switch (prop) { case CSSPropertyBackgroundPositionX: case CSSPropertyWebkitMaskPositionX: - m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::xPosition, &FillLayer::setXPosition); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<Length>>(&FillLayer::xPosition, &FillLayer::setXPosition); break; case CSSPropertyBackgroundPositionY: case CSSPropertyWebkitMaskPositionY: - m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<Length>(&FillLayer::yPosition, &FillLayer::setYPosition); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<Length>>(&FillLayer::yPosition, &FillLayer::setYPosition); break; case CSSPropertyBackgroundSize: case CSSPropertyWebkitBackgroundSize: case CSSPropertyWebkitMaskSize: - m_fillLayerPropertyWrapper = new FillLayerPropertyWrapper<LengthSize>(&FillLayer::sizeLength, &FillLayer::setSizeLength); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerPropertyWrapper<LengthSize>>(&FillLayer::sizeLength, &FillLayer::setSizeLength); break; case CSSPropertyBackgroundImage: - m_fillLayerPropertyWrapper = new FillLayerStyleImagePropertyWrapper(&FillLayer::image, &FillLayer::setImage); + m_fillLayerPropertyWrapper = std::make_unique<FillLayerStyleImagePropertyWrapper>(&FillLayer::image, &FillLayer::setImage); break; default: break; } } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - const FillLayer* fromLayer = (a->*m_layersGetter)(); - const FillLayer* toLayer = (b->*m_layersGetter)(); + if (a == b) + return true; + if (!a || !b) + return false; + + auto* fromLayer = &(a->*m_layersGetter)(); + auto* toLayer = &(b->*m_layersGetter)(); while (fromLayer && toLayer) { if (!m_fillLayerPropertyWrapper->equals(fromLayer, toLayer)) @@ -946,11 +1103,11 @@ public: return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - const FillLayer* aLayer = (a->*m_layersGetter)(); - const FillLayer* bLayer = (b->*m_layersGetter)(); - FillLayer* dstLayer = (dst->*m_layersAccessor)(); + auto* aLayer = &(a->*m_layersGetter)(); + auto* bLayer = &(b->*m_layersGetter)(); + auto* dstLayer = &(dst->*m_layersAccessor)(); while (aLayer && bLayer && dstLayer) { m_fillLayerPropertyWrapper->blend(anim, dstLayer, aLayer, bLayer, progress); @@ -960,57 +1117,77 @@ public: } } +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending FillLayers at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif + private: - FillLayerAnimationPropertyWrapperBase* m_fillLayerPropertyWrapper; + std::unique_ptr<FillLayerAnimationPropertyWrapperBase> m_fillLayerPropertyWrapper; LayersGetter m_layersGetter; LayersAccessor m_layersAccessor; }; class ShorthandPropertyWrapper : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: ShorthandPropertyWrapper(CSSPropertyID property, Vector<AnimationPropertyWrapperBase*> longhandWrappers) : AnimationPropertyWrapperBase(property) + , m_propertyWrappers(WTFMove(longhandWrappers)) { - m_propertyWrappers.swap(longhandWrappers); } - virtual bool isShorthandWrapper() const { return true; } + bool isShorthandWrapper() const override { return true; } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - Vector<AnimationPropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); - for (Vector<AnimationPropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) { - if (!(*it)->equals(a, b)) + if (a == b) + return true; + if (!a || !b) + return false; + + for (auto& wrapper : m_propertyWrappers) { + if (!wrapper->equals(a, b)) return false; } return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override + { + for (auto& wrapper : m_propertyWrappers) + wrapper->blend(anim, dst, a, b, progress); + } + +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final { - Vector<AnimationPropertyWrapperBase*>::const_iterator end = m_propertyWrappers.end(); - for (Vector<AnimationPropertyWrapperBase*>::const_iterator it = m_propertyWrappers.begin(); it != end; ++it) - (*it)->blend(anim, dst, a, b, progress); + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending shorthand property " << getPropertyName(property()) << " at " << TextStream::FormatNumberRespectingIntegers(progress)); } +#endif - const Vector<AnimationPropertyWrapperBase*> propertyWrappers() const { return m_propertyWrappers; } + const Vector<AnimationPropertyWrapperBase*>& propertyWrappers() const { return m_propertyWrappers; } private: Vector<AnimationPropertyWrapperBase*> m_propertyWrappers; }; class PropertyWrapperFlex : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperFlex() : AnimationPropertyWrapperBase(CSSPropertyWebkitFlex) + PropertyWrapperFlex() + : AnimationPropertyWrapperBase(CSSPropertyFlex) { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { - // If the style pointers are the same, don't bother doing the test. - // If either is null, return false. If both are null, return true. - if ((!a && !b) || a == b) + if (a == b) return true; if (!a || !b) return false; @@ -1018,18 +1195,26 @@ public: return a->flexBasis() == b->flexBasis() && a->flexGrow() == b->flexGrow() && a->flexShrink() == b->flexShrink(); } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { dst->setFlexBasis(blendFunc(anim, a->flexBasis(), b->flexBasis(), progress)); dst->setFlexGrow(blendFunc(anim, a->flexGrow(), b->flexGrow(), progress)); dst->setFlexShrink(blendFunc(anim, a->flexShrink(), b->flexShrink(), progress)); } + +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending flex at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif }; -#if ENABLE(SVG) class PropertyWrapperSVGPaint : public AnimationPropertyWrapperBase { + WTF_MAKE_FAST_ALLOCATED; public: - PropertyWrapperSVGPaint(CSSPropertyID prop, const SVGPaint::SVGPaintType& (RenderStyle::*paintTypeGetter)() const, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) + PropertyWrapperSVGPaint(CSSPropertyID prop, const SVGPaintType& (RenderStyle::*paintTypeGetter)() const, Color (RenderStyle::*getter)() const, void (RenderStyle::*setter)(const Color&)) : AnimationPropertyWrapperBase(prop) , m_paintTypeGetter(paintTypeGetter) , m_getter(getter) @@ -1037,15 +1222,20 @@ public: { } - virtual bool equals(const RenderStyle* a, const RenderStyle* b) const + bool equals(const RenderStyle* a, const RenderStyle* b) const override { + if (a == b) + return true; + if (!a || !b) + return false; + if ((a->*m_paintTypeGetter)() != (b->*m_paintTypeGetter)()) return false; // We only support animations between SVGPaints that are pure Color values. // For everything else we must return true for this method, otherwise // we will try to animate between values forever. - if ((a->*m_paintTypeGetter)() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR) { + if ((a->*m_paintTypeGetter)() == SVG_PAINTTYPE_RGBCOLOR) { Color fromColor = (a->*m_getter)(); Color toColor = (b->*m_getter)(); @@ -1062,10 +1252,10 @@ public: return true; } - virtual void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const + void blend(const AnimationBase* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override { - if ((a->*m_paintTypeGetter)() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR - || (b->*m_paintTypeGetter)() != SVGPaint::SVG_PAINTTYPE_RGBCOLOR) + if ((a->*m_paintTypeGetter)() != SVG_PAINTTYPE_RGBCOLOR + || (b->*m_paintTypeGetter)() != SVG_PAINTTYPE_RGBCOLOR) return; Color fromColor = (a->*m_getter)(); @@ -1081,32 +1271,38 @@ public: (dst->*m_setter)(blendFunc(anim, fromColor, toColor, progress)); } +#if !LOG_DISABLED + void logBlend(const RenderStyle*, const RenderStyle*, const RenderStyle*, double progress) const final + { + // FIXME: better logging. + LOG_WITH_STREAM(Animations, stream << " blending SVGPaint at " << TextStream::FormatNumberRespectingIntegers(progress)); + } +#endif + private: - const SVGPaint::SVGPaintType& (RenderStyle::*m_paintTypeGetter)() const; + const SVGPaintType& (RenderStyle::*m_paintTypeGetter)() const; Color (RenderStyle::*m_getter)() const; void (RenderStyle::*m_setter)(const Color&); }; -#endif class CSSPropertyAnimationWrapperMap { + WTF_MAKE_FAST_ALLOCATED; public: - static CSSPropertyAnimationWrapperMap& instance() + static CSSPropertyAnimationWrapperMap& singleton() { - // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last AnimationController is destroyed? - DEFINE_STATIC_LOCAL(OwnPtr<CSSPropertyAnimationWrapperMap>, map, ()); - if (!map) - map = adoptPtr(new CSSPropertyAnimationWrapperMap); - return *map; + // FIXME: This data is never destroyed. Maybe we should ref count it and toss it when the last CSSAnimationController is destroyed? + static NeverDestroyed<CSSPropertyAnimationWrapperMap> map; + return map; } AnimationPropertyWrapperBase* wrapperForProperty(CSSPropertyID propertyID) { if (propertyID < firstCSSProperty || propertyID > lastCSSProperty) - return 0; + return nullptr; unsigned wrapperIndex = indexFromPropertyID(propertyID); if (wrapperIndex == cInvalidPropertyWrapperIndex) - return 0; + return nullptr; return m_propertyWrappers[wrapperIndex].get(); } @@ -1124,15 +1320,19 @@ public: private: CSSPropertyAnimationWrapperMap(); + ~CSSPropertyAnimationWrapperMap() = delete; + unsigned char& indexFromPropertyID(CSSPropertyID propertyID) { return m_propertyToIdMap[propertyID - firstCSSProperty]; } - Vector<OwnPtr<AnimationPropertyWrapperBase>> m_propertyWrappers; + Vector<std::unique_ptr<AnimationPropertyWrapperBase>> m_propertyWrappers; unsigned char m_propertyToIdMap[numCSSProperties]; static const unsigned char cInvalidPropertyWrapperIndex = UCHAR_MAX; + + friend class WTF::NeverDestroyed<CSSPropertyAnimationWrapperMap>; }; CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() @@ -1154,10 +1354,10 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperFlex(), - new PropertyWrapper<unsigned>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth), - new PropertyWrapper<unsigned>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth), - new PropertyWrapper<unsigned>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth), - new PropertyWrapper<unsigned>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth), + new PropertyWrapper<float>(CSSPropertyBorderLeftWidth, &RenderStyle::borderLeftWidth, &RenderStyle::setBorderLeftWidth), + new PropertyWrapper<float>(CSSPropertyBorderRightWidth, &RenderStyle::borderRightWidth, &RenderStyle::setBorderRightWidth), + new PropertyWrapper<float>(CSSPropertyBorderTopWidth, &RenderStyle::borderTopWidth, &RenderStyle::setBorderTopWidth), + new PropertyWrapper<float>(CSSPropertyBorderBottomWidth, &RenderStyle::borderBottomWidth, &RenderStyle::setBorderBottomWidth), new LengthPropertyWrapper<Length>(CSSPropertyMarginLeft, &RenderStyle::marginLeft, &RenderStyle::setMarginLeft), new LengthPropertyWrapper<Length>(CSSPropertyMarginRight, &RenderStyle::marginRight, &RenderStyle::setMarginRight), new LengthPropertyWrapper<Length>(CSSPropertyMarginTop, &RenderStyle::marginTop, &RenderStyle::setMarginTop), @@ -1170,7 +1370,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperVisitedAffectedColor(CSSPropertyBackgroundColor, &RenderStyle::backgroundColor, &RenderStyle::setBackgroundColor, &RenderStyle::visitedLinkBackgroundColor, &RenderStyle::setVisitedLinkBackgroundColor), - new FillLayersPropertyWrapper(CSSPropertyBackgroundImage, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyBackgroundImage, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), new StyleImagePropertyWrapper(CSSPropertyListStyleImage, &RenderStyle::listStyleImage, &RenderStyle::setListStyleImage), new StyleImagePropertyWrapper(CSSPropertyWebkitMaskImage, &RenderStyle::maskImage, &RenderStyle::setMaskImage), @@ -1182,47 +1382,38 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new StyleImagePropertyWrapper(CSSPropertyWebkitMaskBoxImageSource, &RenderStyle::maskBoxImageSource, &RenderStyle::setMaskBoxImageSource), new PropertyWrapper<const NinePieceImage&>(CSSPropertyWebkitMaskBoxImage, &RenderStyle::maskBoxImage, &RenderStyle::setMaskBoxImage), - new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::accessBackgroundLayers), - - new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers), - new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers), - new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::accessMaskLayers), - - new PropertyWrapper<float>(CSSPropertyFontSize, - // Must pass a specified size to setFontSize if Text Autosizing is enabled, but a computed size - // if text zoom is enabled (if neither is enabled it's irrelevant as they're probably the same). - // FIXME: Find some way to assert that text zoom isn't activated when Text Autosizing is compiled in. -#if ENABLE(TEXT_AUTOSIZING) - &RenderStyle::specifiedFontSize, -#else - &RenderStyle::computedFontSize, -#endif - &RenderStyle::setFontSize), - new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth), - new PropertyWrapper<float>(CSSPropertyWebkitColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap), - new PropertyWrapper<unsigned short>(CSSPropertyWebkitColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount), - new PropertyWrapper<float>(CSSPropertyWebkitColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth), - new PropertyWrapper<short>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing), - new PropertyWrapper<short>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing), + new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionX, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyBackgroundPositionY, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + new FillLayersPropertyWrapper(CSSPropertyWebkitBackgroundSize, &RenderStyle::backgroundLayers, &RenderStyle::ensureBackgroundLayers), + + new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionX, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers), + new FillLayersPropertyWrapper(CSSPropertyWebkitMaskPositionY, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers), + new FillLayersPropertyWrapper(CSSPropertyWebkitMaskSize, &RenderStyle::maskLayers, &RenderStyle::ensureMaskLayers), + + new PropertyWrapper<float>(CSSPropertyFontSize, &RenderStyle::computedFontSize, &RenderStyle::setFontSize), + new PropertyWrapper<unsigned short>(CSSPropertyColumnRuleWidth, &RenderStyle::columnRuleWidth, &RenderStyle::setColumnRuleWidth), + new PropertyWrapper<float>(CSSPropertyColumnGap, &RenderStyle::columnGap, &RenderStyle::setColumnGap), + new PropertyWrapper<unsigned short>(CSSPropertyColumnCount, &RenderStyle::columnCount, &RenderStyle::setColumnCount), + new PropertyWrapper<float>(CSSPropertyColumnWidth, &RenderStyle::columnWidth, &RenderStyle::setColumnWidth), + new PropertyWrapper<float>(CSSPropertyWebkitBorderHorizontalSpacing, &RenderStyle::horizontalBorderSpacing, &RenderStyle::setHorizontalBorderSpacing), + new PropertyWrapper<float>(CSSPropertyWebkitBorderVerticalSpacing, &RenderStyle::verticalBorderSpacing, &RenderStyle::setVerticalBorderSpacing), new PropertyWrapper<int>(CSSPropertyZIndex, &RenderStyle::zIndex, &RenderStyle::setZIndex), new PropertyWrapper<short>(CSSPropertyOrphans, &RenderStyle::orphans, &RenderStyle::setOrphans), new PropertyWrapper<short>(CSSPropertyWidows, &RenderStyle::widows, &RenderStyle::setWidows), new LengthPropertyWrapper<Length>(CSSPropertyLineHeight, &RenderStyle::specifiedLineHeight, &RenderStyle::setLineHeight), - new PropertyWrapper<int>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset), - new PropertyWrapper<unsigned short>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth), + new PropertyWrapper<float>(CSSPropertyOutlineOffset, &RenderStyle::outlineOffset, &RenderStyle::setOutlineOffset), + new PropertyWrapper<float>(CSSPropertyOutlineWidth, &RenderStyle::outlineWidth, &RenderStyle::setOutlineWidth), new PropertyWrapper<float>(CSSPropertyLetterSpacing, &RenderStyle::letterSpacing, &RenderStyle::setLetterSpacing), new LengthPropertyWrapper<Length>(CSSPropertyWordSpacing, &RenderStyle::wordSpacing, &RenderStyle::setWordSpacing), new LengthPropertyWrapper<Length>(CSSPropertyTextIndent, &RenderStyle::textIndent, &RenderStyle::setTextIndent), - new PropertyWrapper<float>(CSSPropertyWebkitPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY), - new PropertyWrapper<float>(CSSPropertyWebkitTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ), + new PropertyWrapper<float>(CSSPropertyPerspective, &RenderStyle::perspective, &RenderStyle::setPerspective), + new LengthPropertyWrapper<Length>(CSSPropertyPerspectiveOriginX, &RenderStyle::perspectiveOriginX, &RenderStyle::setPerspectiveOriginX), + new LengthPropertyWrapper<Length>(CSSPropertyPerspectiveOriginY, &RenderStyle::perspectiveOriginY, &RenderStyle::setPerspectiveOriginY), + new LengthPropertyWrapper<Length>(CSSPropertyTransformOriginX, &RenderStyle::transformOriginX, &RenderStyle::setTransformOriginX), + new LengthPropertyWrapper<Length>(CSSPropertyTransformOriginY, &RenderStyle::transformOriginY, &RenderStyle::setTransformOriginY), + new PropertyWrapper<float>(CSSPropertyTransformOriginZ, &RenderStyle::transformOriginZ, &RenderStyle::setTransformOriginZ), new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopLeftRadius, &RenderStyle::borderTopLeftRadius, &RenderStyle::setBorderTopLeftRadius), new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderTopRightRadius, &RenderStyle::borderTopRightRadius, &RenderStyle::setBorderTopRightRadius), new LengthPropertyWrapper<LengthSize>(CSSPropertyBorderBottomLeftRadius, &RenderStyle::borderBottomLeftRadius, &RenderStyle::setBorderBottomLeftRadius), @@ -1232,30 +1423,19 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new LengthPropertyWrapper<LengthBox>(CSSPropertyClip, &RenderStyle::clip, &RenderStyle::setClip), -#if USE(ACCELERATED_COMPOSITING) new PropertyWrapperAcceleratedOpacity(), new PropertyWrapperAcceleratedTransform(), -#if ENABLE(CSS_FILTERS) new PropertyWrapperAcceleratedFilter(), +#if ENABLE(FILTERS_LEVEL_2) + new PropertyWrapperAcceleratedBackdropFilter(), #endif -#else - new PropertyWrapper<float>(CSSPropertyOpacity, &RenderStyle::opacity, &RenderStyle::setOpacity), - new PropertyWrapper<const TransformOperations&>(CSSPropertyWebkitTransform, &RenderStyle::transform, &RenderStyle::setTransform), -#if ENABLE(CSS_FILTERS) - new PropertyWrapper<const FilterOperations&>(CSSPropertyWebkitFilter, &RenderStyle::filter, &RenderStyle::setFilter), -#endif -#endif - new PropertyWrapperClipPath(CSSPropertyWebkitClipPath, &RenderStyle::clipPath, &RenderStyle::setClipPath), -#if ENABLE(CSS_SHAPES) - new PropertyWrapperShape(CSSPropertyWebkitShapeInside, &RenderStyle::shapeInside, &RenderStyle::setShapeInside), - new PropertyWrapperShape(CSSPropertyWebkitShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside), - new LengthPropertyWrapper<Length>(CSSPropertyWebkitShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin), - new PropertyWrapper<float>(CSSPropertyWebkitShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold), -#endif + new PropertyWrapperShape(CSSPropertyShapeOutside, &RenderStyle::shapeOutside, &RenderStyle::setShapeOutside), + new LengthPropertyWrapper<Length>(CSSPropertyShapeMargin, &RenderStyle::shapeMargin, &RenderStyle::setShapeMargin), + new PropertyWrapper<float>(CSSPropertyShapeImageThreshold, &RenderStyle::shapeImageThreshold, &RenderStyle::setShapeImageThreshold), - new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor), + new PropertyWrapperVisitedAffectedColor(CSSPropertyColumnRuleColor, MaybeInvalidColor, &RenderStyle::columnRuleColor, &RenderStyle::setColumnRuleColor, &RenderStyle::visitedLinkColumnRuleColor, &RenderStyle::setVisitedLinkColumnRuleColor), new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextStrokeColor, MaybeInvalidColor, &RenderStyle::textStrokeColor, &RenderStyle::setTextStrokeColor, &RenderStyle::visitedLinkTextStrokeColor, &RenderStyle::setVisitedLinkTextStrokeColor), new PropertyWrapperVisitedAffectedColor(CSSPropertyWebkitTextFillColor, MaybeInvalidColor, &RenderStyle::textFillColor, &RenderStyle::setTextFillColor, &RenderStyle::visitedLinkTextFillColor, &RenderStyle::setVisitedLinkTextFillColor), new PropertyWrapperVisitedAffectedColor(CSSPropertyBorderLeftColor, MaybeInvalidColor, &RenderStyle::borderLeftColor, &RenderStyle::setBorderLeftColor, &RenderStyle::visitedLinkBorderLeftColor, &RenderStyle::setVisitedLinkBorderLeftColor), @@ -1268,17 +1448,24 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperShadow(CSSPropertyWebkitBoxShadow, &RenderStyle::boxShadow, &RenderStyle::setBoxShadow), new PropertyWrapperShadow(CSSPropertyTextShadow, &RenderStyle::textShadow, &RenderStyle::setTextShadow), -#if ENABLE(SVG) new PropertyWrapperSVGPaint(CSSPropertyFill, &RenderStyle::fillPaintType, &RenderStyle::fillPaintColor, &RenderStyle::setFillPaintColor), new PropertyWrapper<float>(CSSPropertyFillOpacity, &RenderStyle::fillOpacity, &RenderStyle::setFillOpacity), new PropertyWrapperSVGPaint(CSSPropertyStroke, &RenderStyle::strokePaintType, &RenderStyle::strokePaintColor, &RenderStyle::setStrokePaintColor), new PropertyWrapper<float>(CSSPropertyStrokeOpacity, &RenderStyle::strokeOpacity, &RenderStyle::setStrokeOpacity), - new PropertyWrapper<SVGLength>(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth), - new PropertyWrapper< Vector<SVGLength>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray), - new PropertyWrapper<SVGLength>(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset), + new PropertyWrapper<Vector<SVGLengthValue>>(CSSPropertyStrokeDasharray, &RenderStyle::strokeDashArray, &RenderStyle::setStrokeDashArray), new PropertyWrapper<float>(CSSPropertyStrokeMiterlimit, &RenderStyle::strokeMiterLimit, &RenderStyle::setStrokeMiterLimit), + new LengthPropertyWrapper<Length>(CSSPropertyCx, &RenderStyle::cx, &RenderStyle::setCx), + new LengthPropertyWrapper<Length>(CSSPropertyCy, &RenderStyle::cy, &RenderStyle::setCy), + new LengthPropertyWrapper<Length>(CSSPropertyR, &RenderStyle::r, &RenderStyle::setR), + new LengthPropertyWrapper<Length>(CSSPropertyRx, &RenderStyle::rx, &RenderStyle::setRx), + new LengthPropertyWrapper<Length>(CSSPropertyRy, &RenderStyle::ry, &RenderStyle::setRy), + new LengthPropertyWrapper<Length>(CSSPropertyStrokeDashoffset, &RenderStyle::strokeDashOffset, &RenderStyle::setStrokeDashOffset), + new LengthPropertyWrapper<Length>(CSSPropertyStrokeWidth, &RenderStyle::strokeWidth, &RenderStyle::setStrokeWidth), + new LengthPropertyWrapper<Length>(CSSPropertyX, &RenderStyle::x, &RenderStyle::setX), + new LengthPropertyWrapper<Length>(CSSPropertyY, &RenderStyle::y, &RenderStyle::setY), + new PropertyWrapper<float>(CSSPropertyFloodOpacity, &RenderStyle::floodOpacity, &RenderStyle::setFloodOpacity), new PropertyWrapperMaybeInvalidColor(CSSPropertyFloodColor, &RenderStyle::floodColor, &RenderStyle::setFloodColor), @@ -1287,8 +1474,10 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() new PropertyWrapperMaybeInvalidColor(CSSPropertyLightingColor, &RenderStyle::lightingColor, &RenderStyle::setLightingColor), - new PropertyWrapper<SVGLength>(CSSPropertyBaselineShift, &RenderStyle::baselineShiftValue, &RenderStyle::setBaselineShiftValue), - new PropertyWrapper<SVGLength>(CSSPropertyKerning, &RenderStyle::kerning, &RenderStyle::setKerning), + new PropertyWrapper<SVGLengthValue>(CSSPropertyBaselineShift, &RenderStyle::baselineShiftValue, &RenderStyle::setBaselineShiftValue), + new PropertyWrapper<SVGLengthValue>(CSSPropertyKerning, &RenderStyle::kerning, &RenderStyle::setKerning), +#if ENABLE(VARIATION_FONTS) + new PropertyWrapperFontVariationSettings(CSSPropertyFontVariationSettings, &RenderStyle::fontVariationSettings, &RenderStyle::setFontVariationSettings), #endif }; const unsigned animatableLonghandPropertiesCount = WTF_ARRAY_LENGTH(animatableLonghandPropertyWrappers); @@ -1311,9 +1500,9 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() CSSPropertyOutline, CSSPropertyPadding, CSSPropertyWebkitTextStroke, - CSSPropertyWebkitColumnRule, + CSSPropertyColumnRule, CSSPropertyWebkitBorderRadius, - CSSPropertyWebkitTransformOrigin + CSSPropertyTransformOrigin }; const unsigned animatableShorthandPropertiesCount = WTF_ARRAY_LENGTH(animatableShorthandProperties); @@ -1323,7 +1512,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() // // Compound properties that have components that should be animatable: // - // CSSPropertyWebkitColumns + // CSSPropertyColumns // CSSPropertyWebkitBoxReflect // Make sure unused slots have a value @@ -1338,7 +1527,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() for (unsigned i = 0; i < animatableLonghandPropertiesCount; ++i) { AnimationPropertyWrapperBase* wrapper = animatableLonghandPropertyWrappers[i]; - m_propertyWrappers.uncheckedAppend(adoptPtr(wrapper)); + m_propertyWrappers.uncheckedAppend(std::unique_ptr<AnimationPropertyWrapperBase>(wrapper)); indexFromPropertyID(wrapper->property()) = i; } @@ -1359,7 +1548,7 @@ CSSPropertyAnimationWrapperMap::CSSPropertyAnimationWrapperMap() longhandWrappers.uncheckedAppend(m_propertyWrappers[wrapperIndex].get()); } - m_propertyWrappers.uncheckedAppend(adoptPtr(new ShorthandPropertyWrapper(propertyID, longhandWrappers))); + m_propertyWrappers.uncheckedAppend(std::make_unique<ShorthandPropertyWrapper>(propertyID, WTFMove(longhandWrappers))); indexFromPropertyID(propertyID) = animatableLonghandPropertiesCount + i; } } @@ -1370,11 +1559,8 @@ static bool gatherEnclosingShorthandProperties(CSSPropertyID property, Animation return false; ShorthandPropertyWrapper* shorthandWrapper = static_cast<ShorthandPropertyWrapper*>(wrapper); - bool contained = false; - for (size_t i = 0; i < shorthandWrapper->propertyWrappers().size(); ++i) { - AnimationPropertyWrapperBase* currWrapper = shorthandWrapper->propertyWrappers()[i]; - + for (auto& currWrapper : shorthandWrapper->propertyWrappers()) { if (gatherEnclosingShorthandProperties(property, currWrapper, propertySet) || currWrapper->property() == property) contained = true; } @@ -1390,31 +1576,27 @@ bool CSSPropertyAnimation::blendProperties(const AnimationBase* anim, CSSPropert { ASSERT(prop != CSSPropertyInvalid); - AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop); + AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); if (wrapper) { wrapper->blend(anim, dst, a, b, progress); -#if USE(ACCELERATED_COMPOSITING) - return !wrapper->animationIsAccelerated() || !anim->isAccelerated(); -#else - return true; +#if !LOG_DISABLED + wrapper->logBlend(a, b, dst, progress); #endif + return !wrapper->animationIsAccelerated() || !anim->isAccelerated(); } - return false; } -#if USE(ACCELERATED_COMPOSITING) bool CSSPropertyAnimation::animationOfPropertyIsAccelerated(CSSPropertyID prop) { - AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop); + AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); return wrapper ? wrapper->animationIsAccelerated() : false; } -#endif // Note: this is inefficient. It's only called from pauseTransitionAtTime(). HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProperty(CSSPropertyID property) { - CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::instance(); + CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton(); HashSet<CSSPropertyID> foundProperties; for (unsigned i = 0; i < map.size(); ++i) @@ -1425,7 +1607,7 @@ HashSet<CSSPropertyID> CSSPropertyAnimation::animatableShorthandsAffectingProper bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle* a, const RenderStyle* b) { - AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::instance().wrapperForProperty(prop); + AnimationPropertyWrapperBase* wrapper = CSSPropertyAnimationWrapperMap::singleton().wrapperForProperty(prop); if (wrapper) return wrapper->equals(a, b); return true; @@ -1433,7 +1615,7 @@ bool CSSPropertyAnimation::propertiesEqual(CSSPropertyID prop, const RenderStyle CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, bool& isShorthand) { - CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::instance(); + CSSPropertyAnimationWrapperMap& map = CSSPropertyAnimationWrapperMap::singleton(); if (i < 0 || static_cast<unsigned>(i) >= map.size()) return CSSPropertyInvalid; @@ -1445,7 +1627,7 @@ CSSPropertyID CSSPropertyAnimation::getPropertyAtIndex(int i, bool& isShorthand) int CSSPropertyAnimation::getNumProperties() { - return CSSPropertyAnimationWrapperMap::instance().size(); + return CSSPropertyAnimationWrapperMap::singleton().size(); } } diff --git a/Source/WebCore/page/animation/CSSPropertyAnimation.h b/Source/WebCore/page/animation/CSSPropertyAnimation.h index c41c7dd90..98ec9a154 100644 --- a/Source/WebCore/page/animation/CSSPropertyAnimation.h +++ b/Source/WebCore/page/animation/CSSPropertyAnimation.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,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CSSPropertyAnimation_h -#define CSSPropertyAnimation_h +#pragma once #include "CSSPropertyNames.h" #include "RenderStyleConstants.h" @@ -40,9 +39,7 @@ class RenderStyle; class CSSPropertyAnimation { public: -#if USE(ACCELERATED_COMPOSITING) static bool animationOfPropertyIsAccelerated(CSSPropertyID); -#endif static bool propertiesEqual(CSSPropertyID, const RenderStyle* a, const RenderStyle* b); static CSSPropertyID getPropertyAtIndex(int, bool& isShorthand); static int getNumProperties(); @@ -54,5 +51,3 @@ public: }; } // namespace WebCore - -#endif // CSSPropertyAnimation_h diff --git a/Source/WebCore/page/animation/CompositeAnimation.cpp b/Source/WebCore/page/animation/CompositeAnimation.cpp index e18779ea7..f89f1e514 100644 --- a/Source/WebCore/page/animation/CompositeAnimation.cpp +++ b/Source/WebCore/page/animation/CompositeAnimation.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. * @@ -29,7 +29,7 @@ #include "config.h" #include "CompositeAnimation.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPropertyAnimation.h" #include "CSSPropertyNames.h" #include "ImplicitAnimation.h" @@ -37,14 +37,15 @@ #include "Logging.h" #include "RenderElement.h" #include "RenderStyle.h" +#include <wtf/NeverDestroyed.h> #include <wtf/text/CString.h> namespace WebCore { -CompositeAnimation::CompositeAnimation(AnimationControllerPrivate* animationController) +CompositeAnimation::CompositeAnimation(CSSAnimationControllerPrivate& animationController) : m_animationController(animationController) { - m_suspended = animationController->isSuspended() && !animationController->allowsNewAnimationsWhileSuspended(); + m_suspended = m_animationController.isSuspended() && !m_animationController.allowsNewAnimationsWhileSuspended(); } CompositeAnimation::~CompositeAnimation() @@ -62,25 +63,21 @@ void CompositeAnimation::clearRenderer() if (!m_transitions.isEmpty()) { // Clear the renderers from all running animations, in case we are in the middle of // an animation callback (see https://bugs.webkit.org/show_bug.cgi?id=22052) - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* transition = it->value.get(); - animationController()->animationWillBeRemoved(transition); + for (auto& transition : m_transitions.values()) { + animationController().animationWillBeRemoved(transition.get()); transition->clear(); } } if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - animationController()->animationWillBeRemoved(anim); - anim->clear(); + for (auto& animation : m_keyframeAnimations.values()) { + animationController().animationWillBeRemoved(animation.get()); + animation->clear(); } } } -void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +void CompositeAnimation::updateTransitions(RenderElement* renderer, const RenderStyle* currentStyle, const RenderStyle* targetStyle) { // If currentStyle is null or there are no old or new transitions, just skip it if (!currentStyle || (!targetStyle->transitions() && m_transitions.isEmpty())) @@ -88,20 +85,19 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // Mark all existing transitions as no longer active. We will mark the still active ones // in the next loop and then toss the ones that didn't get marked. - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) - it->value->setActive(false); + for (auto& transition : m_transitions.values()) + transition->setActive(false); - RefPtr<RenderStyle> modifiedCurrentStyle; + std::unique_ptr<RenderStyle> modifiedCurrentStyle; // Check to see if we need to update the active transitions if (targetStyle->transitions()) { for (size_t i = 0; i < targetStyle->transitions()->size(); ++i) { - const Animation& animation = targetStyle->transitions()->animation(i); + auto& animation = targetStyle->transitions()->animation(i); bool isActiveTransition = !m_suspended && (animation.duration() || animation.delay() > 0); Animation::AnimationMode mode = animation.animationMode(); - if (mode == Animation::AnimateNone) + if (mode == Animation::AnimateNone || mode == Animation::AnimateUnknownProperty) continue; CSSPropertyID prop = animation.property(); @@ -125,8 +121,8 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // If there is a running animation for this property, the transition is overridden // and we have to use the unanimatedStyle from the animation. We do the test // against the unanimated style here, but we "override" the transition later. - RefPtr<KeyframeAnimation> keyframeAnim = getAnimationForProperty(prop); - RenderStyle* fromStyle = keyframeAnim ? keyframeAnim->unanimatedStyle() : currentStyle; + auto* keyframeAnimation = animationForProperty(prop); + auto* fromStyle = keyframeAnimation ? keyframeAnimation->unanimatedStyle() : currentStyle; // See if there is a current transition for this prop ImplicitAnimation* implAnim = m_transitions.get(prop); @@ -148,18 +144,16 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // list. In this case, the latter one overrides the earlier one, so we // behave as though this is a running animation being replaced. if (!implAnim->isTargetPropertyEqual(prop, targetStyle)) { -#if USE(ACCELERATED_COMPOSITING) // For accelerated animations we need to return a new RenderStyle with the _current_ value // of the property, so that restarted transitions use the correct starting point. if (CSSPropertyAnimation::animationOfPropertyIsAccelerated(prop) && implAnim->isAccelerated()) { if (!modifiedCurrentStyle) - modifiedCurrentStyle = RenderStyle::clone(currentStyle); + modifiedCurrentStyle = RenderStyle::clonePtr(*currentStyle); implAnim->blendPropertyValueInStyle(prop, modifiedCurrentStyle.get()); } -#endif LOG(Animations, "Removing existing ImplicitAnimation %p for property %s", implAnim, getPropertyName(prop)); - animationController()->animationWillBeRemoved(implAnim); + animationController().animationWillBeRemoved(implAnim); m_transitions.remove(prop); equal = false; } @@ -174,11 +168,11 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // <https://bugs.webkit.org/show_bug.cgi?id=24787> if (!equal && isActiveTransition) { // Add the new transition - RefPtr<ImplicitAnimation> implicitAnimation = ImplicitAnimation::create(animation, prop, renderer, this, modifiedCurrentStyle ? modifiedCurrentStyle.get() : fromStyle); - LOG(Animations, "Created ImplicitAnimation %p for property %s duration %.2f delay %.2f", implicitAnimation.get(), getPropertyName(prop), animation.duration(), animation.delay()); - m_transitions.set(prop, implicitAnimation.release()); + auto implicitAnimation = ImplicitAnimation::create(animation, prop, renderer, this, modifiedCurrentStyle ? modifiedCurrentStyle.get() : fromStyle); + LOG(Animations, "Created ImplicitAnimation %p on renderer %p for property %s duration %.2f delay %.2f", implicitAnimation.ptr(), renderer, getPropertyName(prop), animation.duration(), animation.delay()); + m_transitions.set(prop, WTFMove(implicitAnimation)); } - + // We only need one pass for the single prop case if (!all) break; @@ -188,158 +182,184 @@ void CompositeAnimation::updateTransitions(RenderElement* renderer, RenderStyle* // Make a list of transitions to be removed Vector<int> toBeRemoved; - end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (!anim->active()) { - animationController()->animationWillBeRemoved(anim); - toBeRemoved.append(anim->animatingProperty()); - LOG(Animations, "Removing ImplicitAnimation %p for property %s", anim, getPropertyName(anim->animatingProperty())); + for (auto& transition : m_transitions.values()) { + if (!transition->active()) { + animationController().animationWillBeRemoved(transition.get()); + toBeRemoved.append(transition->animatingProperty()); + LOG(Animations, "Removing ImplicitAnimation %p from renderer %p for property %s", transition.get(), renderer, getPropertyName(transition->animatingProperty())); } } // Now remove the transitions from the list - for (size_t j = 0; j < toBeRemoved.size(); ++j) - m_transitions.remove(toBeRemoved[j]); + for (auto propertyToRemove : toBeRemoved) + m_transitions.remove(propertyToRemove); } -void CompositeAnimation::updateKeyframeAnimations(RenderElement* renderer, RenderStyle* currentStyle, RenderStyle* targetStyle) +void CompositeAnimation::updateKeyframeAnimations(RenderElement* renderer, const RenderStyle* currentStyle, const RenderStyle* targetStyle) { // Nothing to do if we don't have any animations, and didn't have any before if (m_keyframeAnimations.isEmpty() && !targetStyle->hasAnimations()) return; m_keyframeAnimations.checkConsistency(); + + if (currentStyle && currentStyle->hasAnimations() && targetStyle->hasAnimations() && *(currentStyle->animations()) == *(targetStyle->animations())) + return; + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + m_hasScrollTriggeredAnimation = false; +#endif + + AnimationNameMap newAnimations; + + // Toss the animation order map. + m_keyframeAnimationOrderMap.clear(); - AnimationNameMap::const_iterator kfend = m_keyframeAnimations.end(); + static NeverDestroyed<const AtomicString> none("none", AtomicString::ConstructFromLiteral); - if (currentStyle && currentStyle->hasAnimations() && targetStyle->hasAnimations() && *(currentStyle->animations()) == *(targetStyle->animations())) { - // The current and target animations are the same so we just need to toss any - // animation which is finished (postActive). - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) { - if (it->value->postActive()) - it->value->setIndex(-1); - } - } else { - // Mark all existing animations as no longer active. - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) - it->value->setIndex(-1); + // Now mark any still active animations as active and add any new animations. + if (targetStyle->animations()) { + int numAnims = targetStyle->animations()->size(); + for (int i = 0; i < numAnims; ++i) { + auto& animation = targetStyle->animations()->animation(i); + AtomicString animationName(animation.name()); + + if (!animation.isValidAnimation()) + continue; - // Toss the animation order map. - m_keyframeAnimationOrderMap.clear(); + // See if there is a current animation for this name. + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl()); + if (keyframeAnim) { + newAnimations.add(keyframeAnim->name().impl(), keyframeAnim); - DEFINE_STATIC_LOCAL(const AtomicString, none, ("none", AtomicString::ConstructFromLiteral)); - - // Now mark any still active animations as active and add any new animations. - if (targetStyle->animations()) { - int numAnims = targetStyle->animations()->size(); - for (int i = 0; i < numAnims; ++i) { - const Animation& animation = targetStyle->animations()->animation(i); - AtomicString animationName(animation.name()); - - if (!animation.isValidAnimation()) + if (keyframeAnim->postActive()) continue; - - // See if there is a current animation for this name. - RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(animationName.impl()); - - if (keyframeAnim) { - // If this animation is postActive, skip it so it gets removed at the end of this function. - if (keyframeAnim->postActive()) - continue; - - // This one is still active. - - // Animations match, but play states may differ. Update if needed. - keyframeAnim->updatePlayState(animation.playState()); - - // Set the saved animation to this new one, just in case the play state has changed. - keyframeAnim->setAnimation(animation); - keyframeAnim->setIndex(i); - } else if ((animation.duration() || animation.delay()) && animation.iterationCount() && animationName != none) { - keyframeAnim = KeyframeAnimation::create(animation, renderer, i, this, targetStyle); - LOG(Animations, "Creating KeyframeAnimation %p with keyframes %s, duration %.2f, delay %.2f, iterations %.2f", keyframeAnim.get(), animation.name().utf8().data(), animation.duration(), animation.delay(), animation.iterationCount()); - if (m_suspended) { - keyframeAnim->updatePlayState(AnimPlayStatePaused); - LOG(Animations, " (created in suspended/paused state)"); - } -#if !LOG_DISABLED - for (auto it = keyframeAnim->keyframes().beginProperties(), end = keyframeAnim->keyframes().endProperties(); it != end; ++it) - LOG(Animations, " property %s", getPropertyName(*it)); + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (animation.trigger()->isScrollAnimationTrigger()) + m_hasScrollTriggeredAnimation = true; #endif - m_keyframeAnimations.set(keyframeAnim->name().impl(), keyframeAnim); + + // Animations match, but play states may differ. Update if needed. + keyframeAnim->updatePlayState(animation.playState()); + + // Set the saved animation to this new one, just in case the play state has changed. + keyframeAnim->setAnimation(animation); + } else if ((animation.duration() || animation.delay()) && animation.iterationCount() && animationName != none) { + keyframeAnim = KeyframeAnimation::create(animation, renderer, this, targetStyle); + LOG(Animations, "Creating KeyframeAnimation %p on renderer %p with keyframes %s, duration %.2f, delay %.2f, iterations %.2f", keyframeAnim.get(), renderer, animation.name().utf8().data(), animation.duration(), animation.delay(), animation.iterationCount()); + + if (m_suspended) { + keyframeAnim->updatePlayState(AnimPlayStatePaused); + LOG(Animations, " (created in suspended/paused state)"); } - - // Add this to the animation order map. - if (keyframeAnim) - m_keyframeAnimationOrderMap.append(keyframeAnim->name().impl()); +#if !LOG_DISABLED + for (auto propertyID : keyframeAnim->keyframes().properties()) + LOG(Animations, " property %s", getPropertyName(propertyID)); +#endif + +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + if (animation.trigger()->isScrollAnimationTrigger()) + m_hasScrollTriggeredAnimation = true; +#endif + + newAnimations.set(keyframeAnim->name().impl(), keyframeAnim); } + + // Add this to the animation order map. + if (keyframeAnim) + m_keyframeAnimationOrderMap.append(keyframeAnim->name().impl()); } } // Make a list of animations to be removed. - Vector<AtomicStringImpl*> animsToBeRemoved; - kfend = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != kfend; ++it) { - KeyframeAnimation* keyframeAnim = it->value.get(); - if (keyframeAnim->index() < 0) { - animsToBeRemoved.append(keyframeAnim->name().impl()); - animationController()->animationWillBeRemoved(keyframeAnim); - keyframeAnim->clear(); - LOG(Animations, "Removing KeyframeAnimation %p", keyframeAnim); + for (auto& animation : m_keyframeAnimations.values()) { + if (!newAnimations.contains(animation->name().impl())) { + animationController().animationWillBeRemoved(animation.get()); + animation->clear(); + LOG(Animations, "Removing KeyframeAnimation %p from renderer %p", animation.get(), renderer); } } - // Now remove the animations from the list. - for (size_t j = 0; j < animsToBeRemoved.size(); ++j) - m_keyframeAnimations.remove(animsToBeRemoved[j]); + std::swap(newAnimations, m_keyframeAnimations); } -PassRef<RenderStyle> CompositeAnimation::animate(RenderElement& renderer, RenderStyle* currentStyle, RenderStyle& targetStyle) +bool CompositeAnimation::animate(RenderElement& renderer, const RenderStyle* currentStyle, const RenderStyle& targetStyle, std::unique_ptr<RenderStyle>& blendedStyle) { - RefPtr<RenderStyle> resultStyle; - // We don't do any transitions if we don't have a currentStyle (on startup). updateTransitions(&renderer, currentStyle, &targetStyle); updateKeyframeAnimations(&renderer, currentStyle, &targetStyle); m_keyframeAnimations.checkConsistency(); + bool animationStateChanged = false; + bool forceStackingContext = false; + if (currentStyle) { // Now that we have transition objects ready, let them know about the new goal state. We want them // to fill in a RenderStyle*& only if needed. - if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - if (ImplicitAnimation* anim = it->value.get()) - anim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle); - } + bool checkForStackingContext = false; + for (auto& transition : m_transitions.values()) { + bool didBlendStyle = false; + if (transition->animate(this, &renderer, currentStyle, &targetStyle, blendedStyle, didBlendStyle)) + animationStateChanged = true; + + if (didBlendStyle) + checkForStackingContext |= WillChangeData::propertyCreatesStackingContext(transition->animatingProperty()); + } + + if (blendedStyle && checkForStackingContext) { + // Note that this is similar to code in StyleResolver::adjustRenderStyle() but only needs to consult + // animatable properties that can trigger stacking context. + if (blendedStyle->opacity() < 1.0f + || blendedStyle->hasTransformRelatedProperty() + || blendedStyle->hasMask() + || blendedStyle->clipPath() + || blendedStyle->boxReflect() + || blendedStyle->hasFilter() +#if ENABLE(FILTERS_LEVEL_2) + || blendedStyle->hasBackdropFilter() +#endif + ) + forceStackingContext = true; } } // Now that we have animation objects ready, let them know about the new goal state. We want them // to fill in a RenderStyle*& only if needed. - for (Vector<AtomicStringImpl*>::const_iterator it = m_keyframeAnimationOrderMap.begin(); it != m_keyframeAnimationOrderMap.end(); ++it) { - RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(*it); - if (keyframeAnim) - keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, resultStyle); + for (auto& name : m_keyframeAnimationOrderMap) { + RefPtr<KeyframeAnimation> keyframeAnim = m_keyframeAnimations.get(name); + if (keyframeAnim) { + bool didBlendStyle = false; + if (keyframeAnim->animate(this, &renderer, currentStyle, &targetStyle, blendedStyle, didBlendStyle)) + animationStateChanged = true; + + forceStackingContext |= didBlendStyle && keyframeAnim->triggersStackingContext(); + m_hasAnimationThatDependsOnLayout |= keyframeAnim->dependsOnLayout(); + } } - return resultStyle ? resultStyle.releaseNonNull() : targetStyle; + // https://drafts.csswg.org/css-animations-1/ + // While an animation is applied but has not finished, or has finished but has an animation-fill-mode of forwards or both, + // the user agent must act as if the will-change property ([css-will-change-1]) on the element additionally + // includes all the properties animated by the animation. + if (forceStackingContext && blendedStyle) { + if (blendedStyle->hasAutoZIndex()) + blendedStyle->setZIndex(0); + } + + return animationStateChanged; } -PassRefPtr<RenderStyle> CompositeAnimation::getAnimatedStyle() const +std::unique_ptr<RenderStyle> CompositeAnimation::getAnimatedStyle() const { - RefPtr<RenderStyle> resultStyle; - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - if (ImplicitAnimation* implicitAnimation = it->value.get()) - implicitAnimation->getAnimatedStyle(resultStyle); - } + std::unique_ptr<RenderStyle> resultStyle; + for (auto& transition : m_transitions.values()) + transition->getAnimatedStyle(resultStyle); m_keyframeAnimations.checkConsistency(); - for (Vector<AtomicStringImpl*>::const_iterator it = m_keyframeAnimationOrderMap.begin(); it != m_keyframeAnimationOrderMap.end(); ++it) { - RefPtr<KeyframeAnimation> keyframeAnimation = m_keyframeAnimations.get(*it); + for (auto& name : m_keyframeAnimationOrderMap) { + RefPtr<KeyframeAnimation> keyframeAnimation = m_keyframeAnimations.get(name); if (keyframeAnimation) keyframeAnimation->getAnimatedStyle(resultStyle); } @@ -354,10 +374,10 @@ double CompositeAnimation::timeToNextService() const double minT = -1; if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* transition = it->value.get(); - double t = transition ? transition->timeToNextService() : -1; + for (auto& transition : m_transitions.values()) { + double t = transition->timeToNextService(); + if (t == -1) + continue; if (t < minT || minT == -1) minT = t; if (minT == 0) @@ -366,10 +386,10 @@ double CompositeAnimation::timeToNextService() const } if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* animation = it->value.get(); - double t = animation ? animation->timeToNextService() : -1; + for (auto& animation : m_keyframeAnimations.values()) { + double t = animation->timeToNextService(); + if (t == -1) + continue; if (t < minT || minT == -1) minT = t; if (minT == 0) @@ -380,23 +400,53 @@ double CompositeAnimation::timeToNextService() const return minT; } -PassRefPtr<KeyframeAnimation> CompositeAnimation::getAnimationForProperty(CSSPropertyID property) const +KeyframeAnimation* CompositeAnimation::animationForProperty(CSSPropertyID property) const { - RefPtr<KeyframeAnimation> retval; - + KeyframeAnimation* result = nullptr; + // We want to send back the last animation with the property if there are multiples. // So we need to iterate through all animations if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - RefPtr<KeyframeAnimation> anim = it->value; - if (anim->hasAnimationForProperty(property)) - retval = anim; + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->hasAnimationForProperty(property)) + result = animation.get(); } } + + return result; +} + +bool CompositeAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const +{ + // If more than one transition and animation affect transform, give up. + bool seenTransformAnimation = false; + + for (auto& animation : m_keyframeAnimations.values()) { + if (!animation->hasAnimationForProperty(CSSPropertyTransform)) + continue; + + if (seenTransformAnimation) + return false; + + seenTransformAnimation = true; + + if (!animation->computeExtentOfTransformAnimation(bounds)) + return false; + } + + for (auto& transition : m_transitions.values()) { + if (transition->animatingProperty() != CSSPropertyTransform || !transition->hasStyle()) + continue; + + if (seenTransformAnimation) + return false; + + if (!transition->computeExtentOfTransformAnimation(bounds)) + return false; + } - return retval; + return true; } void CompositeAnimation::suspendAnimations() @@ -408,18 +458,14 @@ void CompositeAnimation::suspendAnimations() if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - if (KeyframeAnimation* anim = it->value.get()) - anim->updatePlayState(AnimPlayStatePaused); - } + for (auto& animation : m_keyframeAnimations.values()) + animation->updatePlayState(AnimPlayStatePaused); } + if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->hasStyle()) - anim->updatePlayState(AnimPlayStatePaused); + for (auto& transition : m_transitions.values()) { + if (transition->hasStyle()) + transition->updatePlayState(AnimPlayStatePaused); } } } @@ -433,32 +479,26 @@ void CompositeAnimation::resumeAnimations() if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - if (anim && anim->playStatePlaying()) - anim->updatePlayState(AnimPlayStatePlaying); + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->playStatePlaying()) + animation->updatePlayState(AnimPlayStatePlaying); } } if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->hasStyle()) - anim->updatePlayState(AnimPlayStatePlaying); + for (auto& transition : m_transitions.values()) { + if (transition->hasStyle()) + transition->updatePlayState(AnimPlayStatePlaying); } } } void CompositeAnimation::overrideImplicitAnimations(CSSPropertyID property) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); if (!m_transitions.isEmpty()) { - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->animatingProperty() == property) - anim->setOverridden(true); + for (auto& transition : m_transitions.values()) { + if (transition->animatingProperty() == property) + transition->setOverridden(true); } } } @@ -466,32 +506,26 @@ void CompositeAnimation::overrideImplicitAnimations(CSSPropertyID property) void CompositeAnimation::resumeOverriddenImplicitAnimations(CSSPropertyID property) { if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator end = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != end; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->animatingProperty() == property) - anim->setOverridden(false); + for (auto& transition : m_transitions.values()) { + if (transition->animatingProperty() == property) + transition->setOverridden(false); } } } -bool CompositeAnimation::isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, bool isRunningNow) const +bool CompositeAnimation::isAnimatingProperty(CSSPropertyID property, bool acceleratedOnly, AnimationBase::RunningState runningState) const { if (!m_keyframeAnimations.isEmpty()) { m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - if (anim && anim->isAnimatingProperty(property, acceleratedOnly, isRunningNow)) + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->isAnimatingProperty(property, acceleratedOnly, runningState)) return true; } } if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim && anim->isAnimatingProperty(property, acceleratedOnly, isRunningNow)) + for (auto& transition : m_transitions.values()) { + if (transition->isAnimatingProperty(property, acceleratedOnly, runningState)) return true; } } @@ -506,13 +540,8 @@ bool CompositeAnimation::pauseAnimationAtTime(const AtomicString& name, double t if (!keyframeAnim || !keyframeAnim->running()) return false; - double count = keyframeAnim->m_animation->iterationCount(); - if ((t >= 0.0) && ((count == Animation::IterationCountInfinite) || (t <= count * keyframeAnim->duration()))) { - keyframeAnim->freezeAtTime(t); - return true; - } - - return false; + keyframeAnim->freezeAtTime(t); + return true; } bool CompositeAnimation::pauseTransitionAtTime(CSSPropertyID property, double t) @@ -526,9 +555,8 @@ bool CompositeAnimation::pauseTransitionAtTime(CSSPropertyID property, double t) // This code is only used for testing, so performance is not critical here. HashSet<CSSPropertyID> shorthandProperties = CSSPropertyAnimation::animatableShorthandsAffectingProperty(property); bool anyPaused = false; - HashSet<CSSPropertyID>::const_iterator end = shorthandProperties.end(); - for (HashSet<CSSPropertyID>::const_iterator it = shorthandProperties.begin(); it != end; ++it) { - if (pauseTransitionAtTime(*it, t)) + for (auto propertyID : shorthandProperties) { + if (pauseTransitionAtTime(propertyID, t)) anyPaused = true; } return anyPaused; @@ -549,23 +577,15 @@ unsigned CompositeAnimation::numberOfActiveAnimations() const { unsigned count = 0; - if (!m_keyframeAnimations.isEmpty()) { - m_keyframeAnimations.checkConsistency(); - AnimationNameMap::const_iterator animationsEnd = m_keyframeAnimations.end(); - for (AnimationNameMap::const_iterator it = m_keyframeAnimations.begin(); it != animationsEnd; ++it) { - KeyframeAnimation* anim = it->value.get(); - if (anim->running()) - ++count; - } + m_keyframeAnimations.checkConsistency(); + for (auto& animation : m_keyframeAnimations.values()) { + if (animation->running()) + ++count; } - if (!m_transitions.isEmpty()) { - CSSPropertyTransitionsMap::const_iterator transitionsEnd = m_transitions.end(); - for (CSSPropertyTransitionsMap::const_iterator it = m_transitions.begin(); it != transitionsEnd; ++it) { - ImplicitAnimation* anim = it->value.get(); - if (anim->running()) - ++count; - } + for (auto& transition : m_transitions.values()) { + if (transition->running()) + ++count; } return count; diff --git a/Source/WebCore/page/animation/CompositeAnimation.h b/Source/WebCore/page/animation/CompositeAnimation.h index 48c679ef2..715de586d 100644 --- a/Source/WebCore/page/animation/CompositeAnimation.h +++ b/Source/WebCore/page/animation/CompositeAnimation.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,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CompositeAnimation_h -#define CompositeAnimation_h +#pragma once #include "ImplicitAnimation.h" #include "KeyframeAnimation.h" @@ -37,30 +36,32 @@ namespace WebCore { -class AnimationControllerPrivate; -class AnimationController; +class CSSAnimationControllerPrivate; +class CSSAnimationController; class RenderElement; class RenderStyle; // A CompositeAnimation represents a collection of animations that are running // on a single RenderElement, such as a number of properties transitioning at once. class CompositeAnimation : public RefCounted<CompositeAnimation> { + WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<CompositeAnimation> create(AnimationControllerPrivate* animationController) + static Ref<CompositeAnimation> create(CSSAnimationControllerPrivate& animationController) { - return adoptRef(new CompositeAnimation(animationController)); + return adoptRef(*new CompositeAnimation(animationController)); }; ~CompositeAnimation(); void clearRenderer(); - PassRef<RenderStyle> animate(RenderElement&, RenderStyle* currentStyle, RenderStyle& targetStyle); - PassRefPtr<RenderStyle> getAnimatedStyle() const; + bool animate(RenderElement&, const RenderStyle* currentStyle, const RenderStyle& targetStyle, std::unique_ptr<RenderStyle>& blendedStyle); + std::unique_ptr<RenderStyle> getAnimatedStyle() const; + bool computeExtentOfTransformAnimation(LayoutRect&) const; double timeToNextService() const; - AnimationControllerPrivate* animationController() const { return m_animationController; } + CSSAnimationControllerPrivate& animationController() const { return m_animationController; } void suspendAnimations(); void resumeAnimations(); @@ -68,9 +69,9 @@ public: bool hasAnimations() const { return !m_transitions.isEmpty() || !m_keyframeAnimations.isEmpty(); } - bool isAnimatingProperty(CSSPropertyID, bool acceleratedOnly, bool isRunningNow) const; + bool isAnimatingProperty(CSSPropertyID, bool acceleratedOnly, AnimationBase::RunningState) const; - PassRefPtr<KeyframeAnimation> getAnimationForProperty(CSSPropertyID) const; + KeyframeAnimation* animationForProperty(CSSPropertyID) const; void overrideImplicitAnimations(CSSPropertyID); void resumeOverriddenImplicitAnimations(CSSPropertyID); @@ -79,22 +80,30 @@ public: bool pauseTransitionAtTime(CSSPropertyID, double); unsigned numberOfActiveAnimations() const; +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool hasScrollTriggeredAnimation() const { return m_hasScrollTriggeredAnimation; } +#endif + + bool hasAnimationThatDependsOnLayout() const { return m_hasAnimationThatDependsOnLayout; } + private: - CompositeAnimation(AnimationControllerPrivate*); + CompositeAnimation(CSSAnimationControllerPrivate&); - void updateTransitions(RenderElement*, RenderStyle* currentStyle, RenderStyle* targetStyle); - void updateKeyframeAnimations(RenderElement*, RenderStyle* currentStyle, RenderStyle* targetStyle); + void updateTransitions(RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle); + void updateKeyframeAnimations(RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle); typedef HashMap<int, RefPtr<ImplicitAnimation>> CSSPropertyTransitionsMap; - typedef HashMap<AtomicStringImpl*, RefPtr<KeyframeAnimation>> AnimationNameMap; + typedef HashMap<AtomicStringImpl*, RefPtr<KeyframeAnimation>> AnimationNameMap; - AnimationControllerPrivate* m_animationController; + CSSAnimationControllerPrivate& m_animationController; CSSPropertyTransitionsMap m_transitions; AnimationNameMap m_keyframeAnimations; Vector<AtomicStringImpl*> m_keyframeAnimationOrderMap; bool m_suspended; + bool m_hasAnimationThatDependsOnLayout { false }; +#if ENABLE(CSS_ANIMATIONS_LEVEL_2) + bool m_hasScrollTriggeredAnimation { false }; +#endif }; } // namespace WebCore - -#endif // CompositeAnimation_h diff --git a/Source/WebCore/page/animation/ImplicitAnimation.cpp b/Source/WebCore/page/animation/ImplicitAnimation.cpp index af1d78819..924eb4b35 100644 --- a/Source/WebCore/page/animation/ImplicitAnimation.cpp +++ b/Source/WebCore/page/animation/ImplicitAnimation.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. * @@ -27,24 +27,24 @@ */ #include "config.h" +#include "ImplicitAnimation.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPropertyAnimation.h" #include "CompositeAnimation.h" #include "EventNames.h" -#include "ImplicitAnimation.h" +#include "GeometryUtilities.h" #include "KeyframeAnimation.h" -#include "RenderBoxModelObject.h" +#include "RenderBox.h" +#include "StylePendingResources.h" namespace WebCore { -ImplicitAnimation::ImplicitAnimation(const Animation& transition, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compAnim, RenderStyle* fromStyle) +ImplicitAnimation::ImplicitAnimation(const Animation& transition, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compAnim, const RenderStyle* fromStyle) : AnimationBase(transition, renderer, compAnim) + , m_fromStyle(RenderStyle::clonePtr(*fromStyle)) , m_transitionProperty(transition.property()) , m_animatingProperty(animatingProperty) - , m_overridden(false) - , m_active(true) - , m_fromStyle(fromStyle) { ASSERT(animatingProperty != CSSPropertyInvalid); } @@ -61,12 +61,14 @@ bool ImplicitAnimation::shouldSendEventForListener(Document::ListenerType inList return m_object->document().hasListenerType(inListenerType); } -void ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +bool ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const RenderStyle*, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) { // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // So just return. Everything is already all cleaned up. if (postActive()) - return; + return false; + + AnimationState oldState = state(); // Reset to start the transition if we are new if (isNew()) @@ -75,40 +77,64 @@ void ImplicitAnimation::animate(CompositeAnimation*, RenderElement*, const Rende // Run a cycle of animation. // We know we will need a new render style, so make one if needed if (!animatedStyle) - animatedStyle = RenderStyle::clone(targetStyle); + animatedStyle = RenderStyle::clonePtr(*targetStyle); -#if USE(ACCELERATED_COMPOSITING) - bool needsAnim = CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); + CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); // FIXME: we also need to detect cases where we have to software animate for other reasons, // such as a child using inheriting the transform. https://bugs.webkit.org/show_bug.cgi?id=23902 - if (!needsAnim) - // If we are running an accelerated animation, set a flag in the style which causes the style - // to compare as different to any other style. This ensures that changes to the property - // that is animating are correctly detected during the animation (e.g. when a transition - // gets interrupted). - animatedStyle->setIsRunningAcceleratedAnimation(); -#endif // Fire the start timeout if needed fireAnimationEventsIfNeeded(); + + didBlendStyle = true; + return state() != oldState; } -void ImplicitAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) +void ImplicitAnimation::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle) { if (!animatedStyle) - animatedStyle = RenderStyle::clone(m_toStyle.get()); + animatedStyle = RenderStyle::clonePtr(*m_toStyle); + + CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress()); +} + +bool ImplicitAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const +{ + ASSERT(hasStyle()); + + if (!is<RenderBox>(m_object)) + return true; // Non-boxes don't get transformed; - CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); + ASSERT(m_animatingProperty == CSSPropertyTransform); + + RenderBox& box = downcast<RenderBox>(*m_object); + FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor()); + + LayoutRect startBounds = bounds; + LayoutRect endBounds = bounds; + + if (transformFunctionListsMatch()) { + if (!computeTransformedExtentViaTransformList(rendererBox, *m_fromStyle, startBounds)) + return false; + + if (!computeTransformedExtentViaTransformList(rendererBox, *m_toStyle, endBounds)) + return false; + } else { + if (!computeTransformedExtentViaMatrix(rendererBox, *m_fromStyle, startBounds)) + return false; + + if (!computeTransformedExtentViaMatrix(rendererBox, *m_toStyle, endBounds)) + return false; + } + + bounds = unionRect(startBounds, endBounds); + return true; } bool ImplicitAnimation::startAnimation(double timeOffset) { -#if USE(ACCELERATED_COMPOSITING) if (m_object && m_object->isComposited()) - return toRenderBoxModelObject(m_object)->startTransition(timeOffset, m_animatingProperty, m_fromStyle.get(), m_toStyle.get()); -#else - UNUSED_PARAM(timeOffset); -#endif + return downcast<RenderBoxModelObject>(*m_object).startTransition(timeOffset, m_animatingProperty, m_fromStyle.get(), m_toStyle.get()); return false; } @@ -117,12 +143,8 @@ void ImplicitAnimation::pauseAnimation(double timeOffset) if (!m_object) return; -#if USE(ACCELERATED_COMPOSITING) if (m_object->isComposited()) - toRenderBoxModelObject(m_object)->transitionPaused(timeOffset, m_animatingProperty); -#else - UNUSED_PARAM(timeOffset); -#endif + downcast<RenderBoxModelObject>(*m_object).transitionPaused(timeOffset, m_animatingProperty); // Restore the original (unanimated) style if (!paused()) setNeedsStyleRecalc(m_object->element()); @@ -130,10 +152,8 @@ void ImplicitAnimation::pauseAnimation(double timeOffset) void ImplicitAnimation::endAnimation() { -#if USE(ACCELERATED_COMPOSITING) if (m_object && m_object->isComposited()) - toRenderBoxModelObject(m_object)->transitionFinished(m_animatingProperty); -#endif + downcast<RenderBoxModelObject>(*m_object).transitionFinished(m_animatingProperty); } void ImplicitAnimation::onAnimationEnd(double elapsedTime) @@ -143,10 +163,9 @@ void ImplicitAnimation::onAnimationEnd(double elapsedTime) // running. But now that the transition has completed, we need to update this style with its new // destination. If we didn't, the next time through we would think a transition had started // (comparing the old unanimated style with the new final style of the transition). - RefPtr<KeyframeAnimation> keyframeAnim = m_compAnim->getAnimationForProperty(m_animatingProperty); - if (keyframeAnim) - keyframeAnim->setUnanimatedStyle(m_toStyle); - + if (auto* animation = m_compositeAnimation->animationForProperty(m_animatingProperty)) + animation->setUnanimatedStyle(RenderStyle::clonePtr(*m_toStyle)); + sendTransitionEvent(eventNames().transitionendEvent, elapsedTime); endAnimation(); } @@ -162,12 +181,12 @@ bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, doubl // Dispatch the event RefPtr<Element> element = m_object->element(); - ASSERT(!element || !element->document().inPageCache()); + ASSERT(!element || element->document().pageCacheState() == Document::NotInPageCache); if (!element) return false; // Schedule event handling - m_compAnim->animationController()->addEventToDispatch(element, eventType, propertyName, elapsedTime); + m_compositeAnimation->animationController().addEventToDispatch(*element, eventType, propertyName, elapsedTime); // Restore the original (unanimated) style if (eventType == eventNames().transitionendEvent && element->renderer()) @@ -180,21 +199,25 @@ bool ImplicitAnimation::sendTransitionEvent(const AtomicString& eventType, doubl return false; // Didn't dispatch an event } -void ImplicitAnimation::reset(RenderStyle* to) +void ImplicitAnimation::reset(const RenderStyle* to) { ASSERT(to); ASSERT(m_fromStyle); - m_toStyle = to; + m_toStyle = RenderStyle::clonePtr(*to); + + if (m_object && m_object->element()) + Style::loadPendingResources(*m_toStyle, m_object->element()->document(), m_object->element()); // Restart the transition if (m_fromStyle && m_toStyle) - updateStateMachine(AnimationStateInputRestartAnimation, -1); + updateStateMachine(AnimationStateInput::RestartAnimation, -1); // set the transform animation list validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + checkForMatchingBackdropFilterFunctionLists(); #endif } @@ -204,7 +227,7 @@ void ImplicitAnimation::setOverridden(bool b) return; m_overridden = b; - updateStateMachine(m_overridden ? AnimationStateInputPauseOverride : AnimationStateInputResumeOverride, -1); + updateStateMachine(m_overridden ? AnimationStateInput::PauseOverride : AnimationStateInput::ResumeOverride, -1); } bool ImplicitAnimation::affectsProperty(CSSPropertyID property) const @@ -229,12 +252,12 @@ void ImplicitAnimation::blendPropertyValueInStyle(CSSPropertyID prop, RenderStyl if (!m_toStyle) return; - CSSPropertyAnimation::blendProperties(this, prop, currentStyle, m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); + CSSPropertyAnimation::blendProperties(this, prop, currentStyle, m_fromStyle.get(), m_toStyle.get(), progress()); } void ImplicitAnimation::validateTransformFunctionList() { - m_transformFunctionListValid = false; + m_transformFunctionListsMatch = false; if (!m_fromStyle || !m_toStyle) return; @@ -248,44 +271,53 @@ void ImplicitAnimation::validateTransformFunctionList() if (val->operations().isEmpty()) return; - // An emtpy transform list matches anything. + // An empty transform list matches anything. if (val != toVal && !toVal->operations().isEmpty() && !val->operationsMatch(*toVal)) return; // Transform lists match. - m_transformFunctionListValid = true; + m_transformFunctionListsMatch = true; +} + +static bool filterOperationsMatch(const FilterOperations* fromOperations, const FilterOperations& toOperations) +{ + if (fromOperations->operations().isEmpty()) + fromOperations = &toOperations; + + if (fromOperations->operations().isEmpty()) + return false; + + if (fromOperations != &toOperations && !toOperations.operations().isEmpty() && !fromOperations->operationsMatch(toOperations)) + return false; + + return true; } -#if ENABLE(CSS_FILTERS) void ImplicitAnimation::checkForMatchingFilterFunctionLists() { m_filterFunctionListsMatch = false; - + if (!m_fromStyle || !m_toStyle) return; - - const FilterOperations* val = &m_fromStyle->filter(); - const FilterOperations* toVal = &m_toStyle->filter(); - if (val->operations().isEmpty()) - val = toVal; + m_filterFunctionListsMatch = filterOperationsMatch(&m_fromStyle->filter(), m_toStyle->filter()); +} - if (val->operations().isEmpty()) - return; - - // An emtpy filter list matches anything. - if (val != toVal && !toVal->operations().isEmpty() && !val->operationsMatch(*toVal)) +#if ENABLE(FILTERS_LEVEL_2) +void ImplicitAnimation::checkForMatchingBackdropFilterFunctionLists() +{ + m_backdropFilterFunctionListsMatch = false; + + if (!m_fromStyle || !m_toStyle) return; - // Filter lists match. - m_filterFunctionListsMatch = true; + m_backdropFilterFunctionListsMatch = filterOperationsMatch(&m_fromStyle->backdropFilter(), m_toStyle->backdropFilter()); } #endif double ImplicitAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); -#if USE(ACCELERATED_COMPOSITING) if (t != 0 || preActive()) return t; @@ -295,7 +327,6 @@ double ImplicitAnimation::timeToNextService() bool isLooping; getTimeToNextEvent(t, isLooping); } -#endif return t; } diff --git a/Source/WebCore/page/animation/ImplicitAnimation.h b/Source/WebCore/page/animation/ImplicitAnimation.h index df651141e..9447417b0 100644 --- a/Source/WebCore/page/animation/ImplicitAnimation.h +++ b/Source/WebCore/page/animation/ImplicitAnimation.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,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ImplicitAnimation_h -#define ImplicitAnimation_h +#pragma once #include "AnimationBase.h" #include "CSSPropertyNames.h" @@ -41,27 +40,29 @@ class RenderElement; // for a single RenderElement. class ImplicitAnimation : public AnimationBase { public: - static PassRefPtr<ImplicitAnimation> create(const Animation& animation, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compositeAnimation, RenderStyle* fromStyle) + static Ref<ImplicitAnimation> create(const Animation& animation, CSSPropertyID animatingProperty, RenderElement* renderer, CompositeAnimation* compositeAnimation, const RenderStyle* fromStyle) { - return adoptRef(new ImplicitAnimation(animation, animatingProperty, renderer, compositeAnimation, fromStyle)); + return adoptRef(*new ImplicitAnimation(animation, animatingProperty, renderer, compositeAnimation, fromStyle)); }; CSSPropertyID transitionProperty() const { return m_transitionProperty; } CSSPropertyID animatingProperty() const { return m_animatingProperty; } - virtual void onAnimationEnd(double elapsedTime) override; - virtual bool startAnimation(double timeOffset) override; - virtual void pauseAnimation(double timeOffset) override; - virtual void endAnimation() override; + void onAnimationEnd(double elapsedTime) override; + bool startAnimation(double timeOffset) override; + void pauseAnimation(double timeOffset) override; + void endAnimation() override; - virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override; - virtual void getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) override; - virtual void reset(RenderStyle* to); + bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) override; + void getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle) override; + void reset(const RenderStyle* to); + + bool computeExtentOfTransformAnimation(LayoutRect&) const override; void setOverridden(bool); - virtual bool overridden() const override { return m_overridden; } + bool overridden() const override { return m_overridden; } - virtual bool affectsProperty(CSSPropertyID) const override; + bool affectsProperty(CSSPropertyID) const override; bool hasStyle() const { return m_fromStyle && m_toStyle; } @@ -69,7 +70,7 @@ public: void blendPropertyValueInStyle(CSSPropertyID, RenderStyle*); - virtual double timeToNextService() override; + double timeToNextService() override; bool active() const { return m_active; } void setActive(bool b) { m_active = b; } @@ -79,24 +80,24 @@ protected: bool sendTransitionEvent(const AtomicString&, double elapsedTime); void validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) void checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + void checkForMatchingBackdropFilterFunctionLists(); #endif private: - ImplicitAnimation(const Animation&, CSSPropertyID, RenderElement*, CompositeAnimation*, RenderStyle*); + ImplicitAnimation(const Animation&, CSSPropertyID, RenderElement*, CompositeAnimation*, const RenderStyle*); virtual ~ImplicitAnimation(); + // The two styles that we are blending. + std::unique_ptr<RenderStyle> m_fromStyle; + std::unique_ptr<RenderStyle> m_toStyle; + CSSPropertyID m_transitionProperty; // Transition property as specified in the RenderStyle. CSSPropertyID m_animatingProperty; // Specific property for this ImplicitAnimation - bool m_overridden; // true when there is a keyframe animation that overrides the transitioning property - bool m_active; // used for culling the list of transitions - // The two styles that we are blending. - RefPtr<RenderStyle> m_fromStyle; - RefPtr<RenderStyle> m_toStyle; + bool m_active { true }; // Used for culling the list of transitions. + bool m_overridden { false }; // True when there is a keyframe animation that overrides the transitioning property }; } // namespace WebCore - -#endif // ImplicitAnimation_h diff --git a/Source/WebCore/page/animation/KeyframeAnimation.cpp b/Source/WebCore/page/animation/KeyframeAnimation.cpp index 8f661c6c3..5294b0a31 100644 --- a/Source/WebCore/page/animation/KeyframeAnimation.cpp +++ b/Source/WebCore/page/animation/KeyframeAnimation.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. * @@ -29,33 +29,37 @@ #include "config.h" #include "KeyframeAnimation.h" -#include "AnimationControllerPrivate.h" +#include "CSSAnimationControllerPrivate.h" #include "CSSPropertyAnimation.h" #include "CSSPropertyNames.h" #include "CompositeAnimation.h" #include "EventNames.h" -#include "RenderBoxModelObject.h" +#include "GeometryUtilities.h" +#include "RenderBox.h" #include "RenderStyle.h" +#include "StylePendingResources.h" #include "StyleResolver.h" +#include "StyleScope.h" +#include "TranslateTransformOperation.h" +#include "WillChangeData.h" namespace WebCore { -KeyframeAnimation::KeyframeAnimation(const Animation& animation, RenderElement* renderer, int index, CompositeAnimation* compAnim, RenderStyle* unanimatedStyle) - : AnimationBase(animation, renderer, compAnim) +KeyframeAnimation::KeyframeAnimation(const Animation& animation, RenderElement* renderer, CompositeAnimation* compositeAnimation, const RenderStyle* unanimatedStyle) + : AnimationBase(animation, renderer, compositeAnimation) , m_keyframes(animation.name()) - , m_index(index) - , m_startEventDispatched(false) - , m_unanimatedStyle(unanimatedStyle) + , m_unanimatedStyle(RenderStyle::clonePtr(*unanimatedStyle)) { - // Get the keyframe RenderStyles - if (m_object && m_object->element()) - m_object->document().ensureStyleResolver().keyframeStylesForAnimation(m_object->element(), unanimatedStyle, m_keyframes); + resolveKeyframeStyles(); // Update the m_transformFunctionListValid flag based on whether the function lists in the keyframes match. validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + checkForMatchingBackdropFilterFunctionLists(); #endif + computeStackingContextImpact(); + computeLayoutDependency(); } KeyframeAnimation::~KeyframeAnimation() @@ -65,38 +69,60 @@ KeyframeAnimation::~KeyframeAnimation() endAnimation(); } -static const Animation* getAnimationFromStyleByName(const RenderStyle* style, const AtomicString& name) +void KeyframeAnimation::computeStackingContextImpact() { - if (!style->animations()) - return 0; - - for (size_t i = 0; i < style->animations()->size(); i++) { - if (name == style->animations()->animation(i).name()) - return &style->animations()->animation(i); + for (auto propertyID : m_keyframes.properties()) { + if (WillChangeData::propertyCreatesStackingContext(propertyID)) { + m_triggersStackingContext = true; + break; + } } +} + +void KeyframeAnimation::computeLayoutDependency() +{ + if (!m_keyframes.containsProperty(CSSPropertyTransform)) + return; - return 0; + size_t numKeyframes = m_keyframes.size(); + for (size_t i = 0; i < numKeyframes; i++) { + auto* keyframeStyle = m_keyframes[i].style(); + if (!keyframeStyle) { + ASSERT_NOT_REACHED(); + continue; + } + if (keyframeStyle->hasTransform()) { + auto& transformOperations = keyframeStyle->transform(); + for (auto operation : transformOperations.operations()) { + if (operation->isTranslateTransformOperationType()) { + auto translation = downcast<TranslateTransformOperation>(operation.get()); + if (translation->x().isPercent() || translation->y().isPercent()) { + m_dependsOnLayout = true; + return; + } + } + } + } + } } void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& prog) const { + size_t numKeyframes = m_keyframes.size(); + if (!numKeyframes) + return; + // Find the first key double elapsedTime = getElapsedTime(); if (m_animation->duration() && m_animation->iterationCount() != Animation::IterationCountInfinite) elapsedTime = std::min(elapsedTime, m_animation->duration() * m_animation->iterationCount()); const double fractionalTime = this->fractionalTime(1, elapsedTime, 0); - - size_t numKeyframes = m_keyframes.size(); - if (!numKeyframes) - return; - ASSERT(!m_keyframes[0].key()); ASSERT(m_keyframes[m_keyframes.size() - 1].key() == 1); - + int prevIndex = -1; int nextIndex = -1; - // FIXME: with a lot of keys, this linear search will be slow. We could binary search. for (size_t i = 0; i < numKeyframes; ++i) { const KeyframeValue& currKeyFrame = m_keyframes[i]; @@ -108,16 +134,11 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property nextIndex = i; break; } - prevIndex = i; } - double scale = 1; - double offset = 0; - if (prevIndex == -1) prevIndex = 0; - if (nextIndex == -1) nextIndex = m_keyframes.size() - 1; @@ -126,32 +147,32 @@ void KeyframeAnimation::fetchIntervalEndpointsForProperty(CSSPropertyID property fromStyle = prevKeyframe.style(); toStyle = nextKeyframe.style(); - - offset = prevKeyframe.key(); - scale = 1.0 / (nextKeyframe.key() - prevKeyframe.key()); - const TimingFunction* timingFunction = 0; - if (const Animation* matchedAnimation = getAnimationFromStyleByName(fromStyle, name())) - timingFunction = matchedAnimation->timingFunction().get(); + double offset = prevKeyframe.key(); + double scale = 1.0 / (nextIndex == prevIndex ? 1 : (nextKeyframe.key() - prevKeyframe.key())); - prog = progress(scale, offset, timingFunction); + prog = progress(scale, offset, prevKeyframe.timingFunction(name())); } -void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) +bool KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderElement*, const RenderStyle*, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) { // Fire the start timeout if needed fireAnimationEventsIfNeeded(); // If we have not yet started, we will not have a valid start time, so just start the animation if needed. - if (isNew() && m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended()) - updateStateMachine(AnimationStateInputStartAnimation, -1); + if (isNew()) { + if (m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended()) + updateStateMachine(AnimationStateInput::StartAnimation, -1); + else if (m_animation->playState() == AnimPlayStatePaused) + updateStateMachine(AnimationStateInput::PlayStatePaused, -1); + } // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // If so, we need to send back the targetStyle. if (postActive()) { if (!animatedStyle) - animatedStyle = const_cast<RenderStyle*>(targetStyle); - return; + animatedStyle = RenderStyle::clonePtr(*targetStyle); + return false; } // If we are waiting for the start timer, we don't want to change the style yet. @@ -160,65 +181,95 @@ void KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderEl // Special case 2 - if there is a backwards fill mode, then we want to continue // through to the style blend so that we get the fromStyle. if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) - return; + return false; // If we have no keyframes, don't animate. if (!m_keyframes.size()) { - updateStateMachine(AnimationStateInputEndAnimation, -1); - return; + updateStateMachine(AnimationStateInput::EndAnimation, -1); + return false; } + // FIXME: the code below never changes the state, so this function always returns false. + AnimationState oldState = state(); + // Run a cycle of animation. // We know we will need a new render style, so make one if needed. if (!animatedStyle) - animatedStyle = RenderStyle::clone(targetStyle); + animatedStyle = RenderStyle::clonePtr(*targetStyle); // FIXME: we need to be more efficient about determining which keyframes we are animating between. // We should cache the last pair or something. - HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { + for (auto propertyID : m_keyframes.properties()) { // Get the from/to styles and progress between - const RenderStyle* fromStyle = 0; - const RenderStyle* toStyle = 0; - double progress = 0.0; - fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress); - -#if USE(ACCELERATED_COMPOSITING) - bool needsAnim = CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); - if (!needsAnim) - // If we are running an accelerated animation, set a flag in the style - // to indicate it. This can be used to make sure we get an updated - // style for hit testing, etc. - animatedStyle->setIsRunningAcceleratedAnimation(); -#endif + const RenderStyle* fromStyle = nullptr; + const RenderStyle* toStyle = nullptr; + double progress = 0; + fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress); + + CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); } + + didBlendStyle = true; + return state() != oldState; } -void KeyframeAnimation::getAnimatedStyle(RefPtr<RenderStyle>& animatedStyle) +void KeyframeAnimation::getAnimatedStyle(std::unique_ptr<RenderStyle>& animatedStyle) { - // If we're in the delay phase and we're not backwards filling, tell the caller - // to use the current style. - if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) + // If we're done, or in the delay phase and we're not backwards filling, tell the caller to use the current style. + if (postActive() || (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards())) return; if (!m_keyframes.size()) return; if (!animatedStyle) - animatedStyle = RenderStyle::clone(&m_object->style()); + animatedStyle = RenderStyle::clonePtr(m_object->style()); - HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { + for (auto propertyID : m_keyframes.properties()) { // Get the from/to styles and progress between - const RenderStyle* fromStyle = 0; - const RenderStyle* toStyle = 0; - double progress = 0.0; - fetchIntervalEndpointsForProperty(*it, fromStyle, toStyle, progress); + const RenderStyle* fromStyle = nullptr; + const RenderStyle* toStyle = nullptr; + double progress = 0; + fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress); - CSSPropertyAnimation::blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); + CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); } } +bool KeyframeAnimation::computeExtentOfTransformAnimation(LayoutRect& bounds) const +{ + ASSERT(m_keyframes.containsProperty(CSSPropertyTransform)); + + if (!is<RenderBox>(m_object)) + return true; // Non-boxes don't get transformed; + + RenderBox& box = downcast<RenderBox>(*m_object); + FloatRect rendererBox = snapRectToDevicePixels(box.borderBoxRect(), box.document().deviceScaleFactor()); + + FloatRect cumulativeBounds = bounds; + + for (auto& keyframe : m_keyframes.keyframes()) { + if (!keyframe.containsProperty(CSSPropertyTransform)) + continue; + + LayoutRect keyframeBounds = bounds; + + bool canCompute; + if (transformFunctionListsMatch()) + canCompute = computeTransformedExtentViaTransformList(rendererBox, *keyframe.style(), keyframeBounds); + else + canCompute = computeTransformedExtentViaMatrix(rendererBox, *keyframe.style(), keyframeBounds); + + if (!canCompute) + return false; + + cumulativeBounds.unite(keyframeBounds); + } + + bounds = LayoutRect(cumulativeBounds); + return true; +} + bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const { return m_keyframes.containsProperty(property); @@ -226,13 +277,8 @@ bool KeyframeAnimation::hasAnimationForProperty(CSSPropertyID property) const bool KeyframeAnimation::startAnimation(double timeOffset) { -#if USE(ACCELERATED_COMPOSITING) - if (m_object && m_object->isComposited()) { - return toRenderBoxModelObject(m_object)->startAnimation(timeOffset, m_animation.get(), m_keyframes); - } -#else - UNUSED_PARAM(timeOffset); -#endif + if (m_object && m_object->isComposited()) + return downcast<RenderBoxModelObject>(*m_object).startAnimation(timeOffset, m_animation.ptr(), m_keyframes); return false; } @@ -241,12 +287,9 @@ void KeyframeAnimation::pauseAnimation(double timeOffset) if (!m_object) return; -#if USE(ACCELERATED_COMPOSITING) if (m_object->isComposited()) - toRenderBoxModelObject(m_object)->animationPaused(timeOffset, m_keyframes.animationName()); -#else - UNUSED_PARAM(timeOffset); -#endif + downcast<RenderBoxModelObject>(*m_object).animationPaused(timeOffset, m_keyframes.animationName()); + // Restore the original (unanimated) style if (!paused()) setNeedsStyleRecalc(m_object->element()); @@ -257,10 +300,9 @@ void KeyframeAnimation::endAnimation() if (!m_object) return; -#if USE(ACCELERATED_COMPOSITING) if (m_object->isComposited()) - toRenderBoxModelObject(m_object)->animationFinished(m_keyframes.animationName()); -#endif + downcast<RenderBoxModelObject>(*m_object).animationFinished(m_keyframes.animationName()); + // Restore the original (unanimated) style if (!paused()) setNeedsStyleRecalc(m_object->element()); @@ -273,17 +315,17 @@ bool KeyframeAnimation::shouldSendEventForListener(Document::ListenerType listen void KeyframeAnimation::onAnimationStart(double elapsedTime) { - sendAnimationEvent(eventNames().webkitAnimationStartEvent, elapsedTime); + sendAnimationEvent(eventNames().animationstartEvent, elapsedTime); } void KeyframeAnimation::onAnimationIteration(double elapsedTime) { - sendAnimationEvent(eventNames().webkitAnimationIterationEvent, elapsedTime); + sendAnimationEvent(eventNames().animationiterationEvent, elapsedTime); } void KeyframeAnimation::onAnimationEnd(double elapsedTime) { - sendAnimationEvent(eventNames().webkitAnimationEndEvent, elapsedTime); + sendAnimationEvent(eventNames().animationendEvent, elapsedTime); // End the animation if we don't fill forwards. Forward filling // animations are ended properly in the class destructor. if (!m_animation->fillsForwards()) @@ -293,12 +335,12 @@ void KeyframeAnimation::onAnimationEnd(double elapsedTime) bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double elapsedTime) { Document::ListenerType listenerType; - if (eventType == eventNames().webkitAnimationIterationEvent) + if (eventType == eventNames().webkitAnimationIterationEvent || eventType == eventNames().animationiterationEvent) listenerType = Document::ANIMATIONITERATION_LISTENER; - else if (eventType == eventNames().webkitAnimationEndEvent) + else if (eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent) listenerType = Document::ANIMATIONEND_LISTENER; else { - ASSERT(eventType == eventNames().webkitAnimationStartEvent); + ASSERT(eventType == eventNames().webkitAnimationStartEvent || eventType == eventNames().animationstartEvent); if (m_startEventDispatched) return false; m_startEventDispatched = true; @@ -309,15 +351,15 @@ bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double // Dispatch the event RefPtr<Element> element = m_object->element(); - ASSERT(!element || !element->document().inPageCache()); + ASSERT(!element || element->document().pageCacheState() == Document::NotInPageCache); if (!element) return false; // Schedule event handling - m_compAnim->animationController()->addEventToDispatch(element, eventType, m_keyframes.animationName(), elapsedTime); + m_compositeAnimation->animationController().addEventToDispatch(*element, eventType, m_keyframes.animationName(), elapsedTime); // Restore the original (unanimated) style - if (eventType == eventNames().webkitAnimationEndEvent && element->renderer()) + if ((eventType == eventNames().webkitAnimationEndEvent || eventType == eventNames().animationendEvent) && element->renderer()) setNeedsStyleRecalc(element.get()); return true; // Did dispatch an event @@ -329,17 +371,15 @@ bool KeyframeAnimation::sendAnimationEvent(const AtomicString& eventType, double void KeyframeAnimation::overrideAnimations() { // This will override implicit animations that match the properties in the keyframe animation - HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) - compositeAnimation()->overrideImplicitAnimations(*it); + for (auto propertyID : m_keyframes.properties()) + compositeAnimation()->overrideImplicitAnimations(propertyID); } void KeyframeAnimation::resumeOverriddenAnimations() { // This will resume overridden implicit animations - HashSet<CSSPropertyID>::const_iterator end = m_keyframes.endProperties(); - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != end; ++it) - compositeAnimation()->resumeOverriddenImplicitAnimations(*it); + for (auto propertyID : m_keyframes.properties()) + compositeAnimation()->resumeOverriddenImplicitAnimations(propertyID); } bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const @@ -347,11 +387,27 @@ bool KeyframeAnimation::affectsProperty(CSSPropertyID property) const return m_keyframes.containsProperty(property); } +void KeyframeAnimation::resolveKeyframeStyles() +{ + if (!m_object || !m_object->element()) + return; + auto& element = *m_object->element(); + + if (auto* styleScope = Style::Scope::forOrdinal(element, m_animation->nameStyleScopeOrdinal())) + styleScope->resolver().keyframeStylesForAnimation(*m_object->element(), m_unanimatedStyle.get(), m_keyframes); + + // Ensure resource loads for all the frames. + for (auto& keyframe : m_keyframes.keyframes()) { + if (auto* style = const_cast<RenderStyle*>(keyframe.style())) + Style::loadPendingResources(*style, element.document(), &element); + } +} + void KeyframeAnimation::validateTransformFunctionList() { - m_transformFunctionListValid = false; + m_transformFunctionListsMatch = false; - if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitTransform)) + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyTransform)) return; // Empty transforms match anything, so find the first non-empty entry as the reference @@ -384,16 +440,14 @@ void KeyframeAnimation::validateTransformFunctionList() return; } - // Keyframes are valid - m_transformFunctionListValid = true; + m_transformFunctionListsMatch = true; } -#if ENABLE(CSS_FILTERS) void KeyframeAnimation::checkForMatchingFilterFunctionLists() { m_filterFunctionListsMatch = false; - if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitFilter)) + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyFilter)) return; // Empty filters match anything, so find the first non-empty entry as the reference @@ -401,8 +455,7 @@ void KeyframeAnimation::checkForMatchingFilterFunctionLists() size_t firstNonEmptyFilterKeyframeIndex = numKeyframes; for (size_t i = 0; i < numKeyframes; ++i) { - const KeyframeValue& currentKeyframe = m_keyframes[i]; - if (currentKeyframe.style()->filter().operations().size()) { + if (m_keyframes[i].style()->filter().operations().size()) { firstNonEmptyFilterKeyframeIndex = i; break; } @@ -410,39 +463,74 @@ void KeyframeAnimation::checkForMatchingFilterFunctionLists() if (firstNonEmptyFilterKeyframeIndex == numKeyframes) return; - - const FilterOperations* firstVal = &m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter(); + + auto& firstVal = m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->filter(); for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) { - const KeyframeValue& currentKeyframe = m_keyframes[i]; - const FilterOperations* val = ¤tKeyframe.style()->filter(); - + auto& value = m_keyframes[i].style()->filter(); + // An emtpy filter list matches anything. - if (val->operations().isEmpty()) + if (value.operations().isEmpty()) continue; - - if (!firstVal->operationsMatch(*val)) + + if (!firstVal.operationsMatch(value)) return; } - + m_filterFunctionListsMatch = true; } + +#if ENABLE(FILTERS_LEVEL_2) +void KeyframeAnimation::checkForMatchingBackdropFilterFunctionLists() +{ + m_backdropFilterFunctionListsMatch = false; + + if (m_keyframes.size() < 2 || !m_keyframes.containsProperty(CSSPropertyWebkitBackdropFilter)) + return; + + // Empty filters match anything, so find the first non-empty entry as the reference + size_t numKeyframes = m_keyframes.size(); + size_t firstNonEmptyFilterKeyframeIndex = numKeyframes; + + for (size_t i = 0; i < numKeyframes; ++i) { + if (m_keyframes[i].style()->backdropFilter().operations().size()) { + firstNonEmptyFilterKeyframeIndex = i; + break; + } + } + + if (firstNonEmptyFilterKeyframeIndex == numKeyframes) + return; + + auto& firstVal = m_keyframes[firstNonEmptyFilterKeyframeIndex].style()->backdropFilter(); + + for (size_t i = firstNonEmptyFilterKeyframeIndex + 1; i < numKeyframes; ++i) { + auto& value = m_keyframes[i].style()->backdropFilter(); + + // An emtpy filter list matches anything. + if (value.operations().isEmpty()) + continue; + + if (!firstVal.operationsMatch(value)) + return; + } + + m_backdropFilterFunctionListsMatch = true; +} #endif double KeyframeAnimation::timeToNextService() { double t = AnimationBase::timeToNextService(); -#if USE(ACCELERATED_COMPOSITING) if (t != 0 || preActive()) return t; // A return value of 0 means we need service. But if we only have accelerated animations we // only need service at the end of the transition - HashSet<CSSPropertyID>::const_iterator endProperties = m_keyframes.endProperties(); bool acceleratedPropertiesOnly = true; - for (HashSet<CSSPropertyID>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { - if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(*it) || !isAccelerated()) { + for (auto propertyID : m_keyframes.properties()) { + if (!CSSPropertyAnimation::animationOfPropertyIsAccelerated(propertyID) || !isAccelerated()) { acceleratedPropertiesOnly = false; break; } @@ -452,7 +540,7 @@ double KeyframeAnimation::timeToNextService() bool isLooping; getTimeToNextEvent(t, isLooping); } -#endif + return t; } diff --git a/Source/WebCore/page/animation/KeyframeAnimation.h b/Source/WebCore/page/animation/KeyframeAnimation.h index 34ebd9951..8731d343c 100644 --- a/Source/WebCore/page/animation/KeyframeAnimation.h +++ b/Source/WebCore/page/animation/KeyframeAnimation.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,8 +26,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef KeyframeAnimation_h -#define KeyframeAnimation_h +#pragma once #include "AnimationBase.h" #include "Document.h" @@ -40,66 +39,72 @@ class RenderStyle; // A KeyframeAnimation tracks the state of an explicit animation for a single RenderElement. class KeyframeAnimation final : public AnimationBase { public: - static RefPtr<KeyframeAnimation> create(const Animation& animation, RenderElement* renderer, int index, CompositeAnimation* compositeAnimation, RenderStyle* unanimatedStyle) + static Ref<KeyframeAnimation> create(const Animation& animation, RenderElement* renderer, CompositeAnimation* compositeAnimation, const RenderStyle* unanimatedStyle) { - return adoptRef(new KeyframeAnimation(animation, renderer, index, compositeAnimation, unanimatedStyle)); + return adoptRef(*new KeyframeAnimation(animation, renderer, compositeAnimation, unanimatedStyle)); } - virtual void animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) override; - virtual void getAnimatedStyle(RefPtr<RenderStyle>&) override; + bool animate(CompositeAnimation*, RenderElement*, const RenderStyle* currentStyle, const RenderStyle* targetStyle, std::unique_ptr<RenderStyle>& animatedStyle, bool& didBlendStyle) override; + void getAnimatedStyle(std::unique_ptr<RenderStyle>&) override; + + bool computeExtentOfTransformAnimation(LayoutRect&) const override; const KeyframeList& keyframes() const { return m_keyframes; } const AtomicString& name() const { return m_keyframes.animationName(); } - int index() const { return m_index; } - void setIndex(int i) { m_index = i; } bool hasAnimationForProperty(CSSPropertyID) const; - - void setUnanimatedStyle(PassRefPtr<RenderStyle> style) { m_unanimatedStyle = style; } + + bool triggersStackingContext() const { return m_triggersStackingContext; } + bool dependsOnLayout() const { return m_dependsOnLayout; } + + void setUnanimatedStyle(std::unique_ptr<RenderStyle> style) { m_unanimatedStyle = WTFMove(style); } RenderStyle* unanimatedStyle() const { return m_unanimatedStyle.get(); } - virtual double timeToNextService() override; + double timeToNextService() override; protected: - virtual void onAnimationStart(double elapsedTime) override; - virtual void onAnimationIteration(double elapsedTime) override; - virtual void onAnimationEnd(double elapsedTime) override; - virtual bool startAnimation(double timeOffset) override; - virtual void pauseAnimation(double timeOffset) override; - virtual void endAnimation() override; + void onAnimationStart(double elapsedTime) override; + void onAnimationIteration(double elapsedTime) override; + void onAnimationEnd(double elapsedTime) override; + bool startAnimation(double timeOffset) override; + void pauseAnimation(double timeOffset) override; + void endAnimation() override; - virtual void overrideAnimations() override; - virtual void resumeOverriddenAnimations() override; + void overrideAnimations() override; + void resumeOverriddenAnimations() override; bool shouldSendEventForListener(Document::ListenerType inListenerType) const; bool sendAnimationEvent(const AtomicString&, double elapsedTime); - virtual bool affectsProperty(CSSPropertyID) const override; + bool affectsProperty(CSSPropertyID) const override; + bool computeExtentOfAnimationForMatrixAnimation(const FloatRect& rendererBox, LayoutRect&) const; + + bool computeExtentOfAnimationForMatchingTransformLists(const FloatRect& rendererBox, LayoutRect&) const; + + void computeStackingContextImpact(); + void computeLayoutDependency(); + void resolveKeyframeStyles(); void validateTransformFunctionList(); -#if ENABLE(CSS_FILTERS) void checkForMatchingFilterFunctionLists(); +#if ENABLE(FILTERS_LEVEL_2) + void checkForMatchingBackdropFilterFunctionLists(); #endif private: - KeyframeAnimation(const Animation&, RenderElement*, int index, CompositeAnimation*, RenderStyle* unanimatedStyle); + KeyframeAnimation(const Animation&, RenderElement*, CompositeAnimation*, const RenderStyle* unanimatedStyle); virtual ~KeyframeAnimation(); // Get the styles for the given property surrounding the current animation time and the progress between them. void fetchIntervalEndpointsForProperty(CSSPropertyID, const RenderStyle*& fromStyle, const RenderStyle*& toStyle, double& progress) const; - // The keyframes that we are blending. KeyframeList m_keyframes; + std::unique_ptr<RenderStyle> m_unanimatedStyle; // The style just before we started animation - // The order in which this animation appears in the animation-name style. - int m_index; - bool m_startEventDispatched; - - // The style just before we started animation - RefPtr<RenderStyle> m_unanimatedStyle; + bool m_startEventDispatched { false }; + bool m_triggersStackingContext { false }; + bool m_dependsOnLayout { false }; }; } // namespace WebCore - -#endif // KeyframeAnimation_h diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.cpp b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp new file mode 100644 index 000000000..475087f0e --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.cpp @@ -0,0 +1,881 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * Copyright (C) 2013-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 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 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 "ContentSecurityPolicyDirective.h" +#include "ContentSecurityPolicyDirectiveList.h" +#include "ContentSecurityPolicyDirectiveNames.h" +#include "ContentSecurityPolicyHash.h" +#include "ContentSecurityPolicySource.h" +#include "ContentSecurityPolicySourceList.h" +#include "DOMStringList.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "EventNames.h" +#include "FormData.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLParserIdioms.h" +#include "InspectorInstrumentation.h" +#include "JSDOMWindowShell.h" +#include "JSMainThreadExecState.h" +#include "ParsingUtilities.h" +#include "PingLoader.h" +#include "ResourceRequest.h" +#include "RuntimeEnabledFeatures.h" +#include "SchemeRegistry.h" +#include "SecurityOrigin.h" +#include "SecurityPolicyViolationEvent.h" +#include "Settings.h" +#include "TextEncoding.h" +#include <inspector/InspectorValues.h> +#include <inspector/ScriptCallStack.h> +#include <inspector/ScriptCallStackFactory.h> +#include <pal/crypto/CryptoDigest.h> +#include <wtf/SetForScope.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/TextPosition.h> + +using namespace Inspector; + +namespace WebCore { + +static String consoleMessageForViolation(const char* effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const char* prefix, const char* subject = "it") +{ + StringBuilder result; + if (violatedDirective.directiveList().isReportOnly()) + result.appendLiteral("[Report Only] "); + result.append(prefix); + if (!blockedURL.isEmpty()) { + result.append(' '); + result.append(blockedURL.stringCenterEllipsizedToLength()); + } + result.appendLiteral(" because "); + result.append(subject); + if (violatedDirective.isDefaultSrc()) { + result.appendLiteral(" appears in neither the "); + result.append(effectiveViolatedDirective); + result.appendLiteral(" directive nor the default-src directive of the Content Security Policy."); + } else { + result.appendLiteral(" does not appear in the "); + result.append(effectiveViolatedDirective); + result.appendLiteral(" directive of the Content Security Policy."); + } + return result.toString(); +} + +ContentSecurityPolicy::ContentSecurityPolicy(ScriptExecutionContext& scriptExecutionContext) + : m_scriptExecutionContext(&scriptExecutionContext) + , m_sandboxFlags(SandboxNone) +{ + ASSERT(scriptExecutionContext.securityOrigin()); + updateSourceSelf(*scriptExecutionContext.securityOrigin()); +} + +ContentSecurityPolicy::ContentSecurityPolicy(const SecurityOrigin& securityOrigin, const Frame* frame) + : m_frame(frame) + , m_sandboxFlags(SandboxNone) +{ + updateSourceSelf(securityOrigin); +} + +ContentSecurityPolicy::~ContentSecurityPolicy() +{ +} + +void ContentSecurityPolicy::copyStateFrom(const ContentSecurityPolicy* other) +{ + if (m_hasAPIPolicy) + return; + ASSERT(m_policies.isEmpty()); + for (auto& policy : other->m_policies) + didReceiveHeader(policy->header(), policy->headerType(), ContentSecurityPolicy::PolicyFrom::Inherited); +} + +void ContentSecurityPolicy::copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy& other) +{ + m_upgradeInsecureRequests = other.m_upgradeInsecureRequests; + m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end()); +} + +bool ContentSecurityPolicy::allowRunningOrDisplayingInsecureContent(const URL& url) +{ + bool allow = true; + bool isReportOnly = false; + for (auto& policy : m_policies) { + if (!policy->hasBlockAllMixedContentDirective()) + continue; + + isReportOnly = policy->isReportOnly(); + + StringBuilder consoleMessage; + if (isReportOnly) + consoleMessage.appendLiteral("[Report Only] "); + consoleMessage.append("Blocked mixed content "); + consoleMessage.append(url.stringCenterEllipsizedToLength()); + consoleMessage.appendLiteral(" because "); + consoleMessage.append("'block-all-mixed-content' appears in the Content Security Policy."); + reportViolation(ContentSecurityPolicyDirectiveNames::blockAllMixedContent, ContentSecurityPolicyDirectiveNames::blockAllMixedContent, *policy, url, consoleMessage.toString()); + + if (!isReportOnly) + allow = false; + } + return allow; +} + +void ContentSecurityPolicy::didCreateWindowShell(JSDOMWindowShell& windowShell) const +{ + JSDOMWindow* window = windowShell.window(); + ASSERT(window); + ASSERT(window->scriptExecutionContext()); + ASSERT(window->scriptExecutionContext()->contentSecurityPolicy() == this); + if (!windowShell.world().isNormal()) { + window->setEvalEnabled(true); + return; + } + window->setEvalEnabled(m_lastPolicyEvalDisabledErrorMessage.isNull(), m_lastPolicyEvalDisabledErrorMessage); +} + +ContentSecurityPolicyResponseHeaders ContentSecurityPolicy::responseHeaders() const +{ + ContentSecurityPolicyResponseHeaders result; + result.m_headers.reserveInitialCapacity(m_policies.size()); + for (auto& policy : m_policies) + result.m_headers.uncheckedAppend({ policy->header(), policy->headerType() }); + return result; +} + +void ContentSecurityPolicy::didReceiveHeaders(const ContentSecurityPolicyResponseHeaders& headers, ReportParsingErrors reportParsingErrors) +{ + SetForScope<bool> isReportingEnabled(m_isReportingEnabled, reportParsingErrors == ReportParsingErrors::Yes); + for (auto& header : headers.m_headers) + didReceiveHeader(header.first, header.second, ContentSecurityPolicy::PolicyFrom::HTTPHeader); +} + +void ContentSecurityPolicy::didReceiveHeader(const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom policyFrom) +{ + if (m_hasAPIPolicy) + return; + + if (policyFrom == PolicyFrom::API) { + ASSERT(m_policies.isEmpty()); + m_hasAPIPolicy = true; + } + + // 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. + auto characters = StringView(header).upconvertedCharacters(); + const UChar* begin = characters; + const UChar* position = begin; + const UChar* end = begin + header.length(); + while (position < end) { + skipUntil<UChar>(position, end, ','); + + // header1,header2 OR header1 + // ^ ^ + m_policies.append(ContentSecurityPolicyDirectiveList::create(*this, String(begin, position - begin), type, policyFrom)); + + // Skip the comma, and begin the next header from the current position. + ASSERT(position == end || *position == ','); + skipExactly<UChar>(position, end, ','); + begin = position; + } + + if (m_scriptExecutionContext) + applyPolicyToScriptExecutionContext(); +} + +void ContentSecurityPolicy::updateSourceSelf(const SecurityOrigin& securityOrigin) +{ + m_selfSourceProtocol = securityOrigin.protocol(); + m_selfSource = std::make_unique<ContentSecurityPolicySource>(*this, m_selfSourceProtocol, securityOrigin.host(), securityOrigin.port(), emptyString(), false, false); +} + +void ContentSecurityPolicy::applyPolicyToScriptExecutionContext() +{ + ASSERT(m_scriptExecutionContext); + + // Update source self as the security origin may have changed between the time we were created and now. + // For instance, we may have been initially created for an about:blank iframe that later inherited the + // security origin of its owner document. + ASSERT(m_scriptExecutionContext->securityOrigin()); + updateSourceSelf(*m_scriptExecutionContext->securityOrigin()); + + bool enableStrictMixedContentMode = false; + for (auto& policy : m_policies) { + const ContentSecurityPolicyDirective* violatedDirective = policy->violatedDirectiveForUnsafeEval(); + if (violatedDirective && !violatedDirective->directiveList().isReportOnly()) + m_lastPolicyEvalDisabledErrorMessage = policy->evalDisabledErrorMessage(); + if (policy->hasBlockAllMixedContentDirective() && !policy->isReportOnly()) + enableStrictMixedContentMode = true; + } + + if (!m_lastPolicyEvalDisabledErrorMessage.isNull()) + m_scriptExecutionContext->disableEval(m_lastPolicyEvalDisabledErrorMessage); + if (m_sandboxFlags != SandboxNone && is<Document>(m_scriptExecutionContext)) + m_scriptExecutionContext->enforceSandboxFlags(m_sandboxFlags); + if (enableStrictMixedContentMode) + m_scriptExecutionContext->setStrictMixedContentMode(true); +} + +void ContentSecurityPolicy::setOverrideAllowInlineStyle(bool value) +{ + m_overrideInlineStyleAllowed = value; +} + +bool ContentSecurityPolicy::urlMatchesSelf(const URL& url) const +{ + return m_selfSource->matches(url); +} + +bool ContentSecurityPolicy::allowContentSecurityPolicySourceStarToMatchAnyProtocol() const +{ + if (is<Document>(m_scriptExecutionContext)) + return downcast<Document>(*m_scriptExecutionContext).settings().allowContentSecurityPolicySourceStarToMatchAnyProtocol(); + return false; +} + +bool ContentSecurityPolicy::protocolMatchesSelf(const URL& url) const +{ + if (equalLettersIgnoringASCIICase(m_selfSourceProtocol, "http")) + return url.protocolIsInHTTPFamily(); + return equalIgnoringASCIICase(url.protocol(), m_selfSourceProtocol); +} + +template<typename Predicate, typename... Args> +typename std::enable_if<!std::is_convertible<Predicate, ContentSecurityPolicy::ViolatedDirectiveCallback>::value, bool>::type ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, Predicate&& predicate, Args&&... args) const +{ + bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly; + for (auto& policy : m_policies) { + if (policy->isReportOnly() != isReportOnly) + continue; + if ((policy.get()->*predicate)(std::forward<Args>(args)...)) + return false; + } + return true; +} + +template<typename Predicate, typename... Args> +bool ContentSecurityPolicy::allPoliciesWithDispositionAllow(Disposition disposition, ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const +{ + bool isReportOnly = disposition == ContentSecurityPolicy::Disposition::ReportOnly; + bool isAllowed = true; + for (auto& policy : m_policies) { + if (policy->isReportOnly() != isReportOnly) + continue; + if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) { + isAllowed = false; + callback(*violatedDirective); + } + } + return isAllowed; +} + +template<typename Predicate, typename... Args> +bool ContentSecurityPolicy::allPoliciesAllow(ViolatedDirectiveCallback&& callback, Predicate&& predicate, Args&&... args) const +{ + bool isAllowed = true; + for (auto& policy : m_policies) { + if (const ContentSecurityPolicyDirective* violatedDirective = (policy.get()->*predicate)(std::forward<Args>(args)...)) { + if (!violatedDirective->directiveList().isReportOnly()) + isAllowed = false; + callback(*violatedDirective); + } + } + return isAllowed; +} + +static PAL::CryptoDigest::Algorithm toCryptoDigestAlgorithm(ContentSecurityPolicyHashAlgorithm algorithm) +{ + switch (algorithm) { + case ContentSecurityPolicyHashAlgorithm::SHA_256: + return PAL::CryptoDigest::Algorithm::SHA_256; + case ContentSecurityPolicyHashAlgorithm::SHA_384: + return PAL::CryptoDigest::Algorithm::SHA_384; + case ContentSecurityPolicyHashAlgorithm::SHA_512: + return PAL::CryptoDigest::Algorithm::SHA_512; + } + ASSERT_NOT_REACHED(); + return PAL::CryptoDigest::Algorithm::SHA_512; +} + +template<typename Predicate> +ContentSecurityPolicy::HashInEnforcedAndReportOnlyPoliciesPair ContentSecurityPolicy::findHashOfContentInPolicies(Predicate&& predicate, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm> algorithms) const +{ + if (algorithms.isEmpty() || content.isEmpty()) + return { false, false }; + + // FIXME: We should compute the document encoding once and cache it instead of computing it on each invocation. + TextEncoding documentEncoding; + if (is<Document>(m_scriptExecutionContext)) + documentEncoding = downcast<Document>(*m_scriptExecutionContext).textEncoding(); + const TextEncoding& encodingToUse = documentEncoding.isValid() ? documentEncoding : UTF8Encoding(); + + // FIXME: Compute the digest with respect to the raw bytes received from the page. + // See <https://bugs.webkit.org/show_bug.cgi?id=155184>. + CString contentCString = encodingToUse.encode(content, EntitiesForUnencodables); + bool foundHashInEnforcedPolicies = false; + bool foundHashInReportOnlyPolicies = false; + for (auto algorithm : algorithms) { + auto cryptoDigest = PAL::CryptoDigest::create(toCryptoDigestAlgorithm(algorithm)); + cryptoDigest->addBytes(contentCString.data(), contentCString.length()); + ContentSecurityPolicyHash hash = { algorithm, cryptoDigest->computeHash() }; + if (!foundHashInEnforcedPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, std::forward<Predicate>(predicate), hash)) + foundHashInEnforcedPolicies = true; + if (!foundHashInReportOnlyPolicies && allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, std::forward<Predicate>(predicate), hash)) + foundHashInReportOnlyPolicies = true; + if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies) + break; + } + return { foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies }; +} + +bool ContentSecurityPolicy::allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'"); + reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber())); + if (!violatedDirective.directiveList().isReportOnly()) + reportBlockedScriptExecutionToInspector(violatedDirective.text()); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript); +} + +bool ContentSecurityPolicy::allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script for an inline event handler", "'unsafe-inline'"); + reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber())); + if (!violatedDirective.directiveList().isReportOnly()) + reportBlockedScriptExecutionToInspector(violatedDirective.text()); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript); +} + +bool ContentSecurityPolicy::allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce); + if (strippedNonce.isEmpty()) + return false; + // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>. + return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce, strippedNonce); +} + +bool ContentSecurityPolicy::allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + String strippedNonce = stripLeadingAndTrailingHTMLSpaces(nonce); + if (strippedNonce.isEmpty()) + return false; + // FIXME: We need to report violations in report-only policies. See <https://bugs.webkit.org/show_bug.cgi?id=159830>. + return allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce, strippedNonce); +} + +bool ContentSecurityPolicy::allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + bool foundHashInEnforcedPolicies; + bool foundHashInReportOnlyPolicies; + std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash, scriptContent, m_hashAlgorithmsForInlineScripts); + if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies) + return true; + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "its hash, its nonce, or 'unsafe-inline'"); + reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber())); + if (!violatedDirective.directiveList().isReportOnly()) + reportBlockedScriptExecutionToInspector(violatedDirective.text()); + }; + // FIXME: We should not report that the inline script violated a policy when its hash matched a source + // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>. + if (!foundHashInReportOnlyPolicies) + allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript); + return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript); +} + +bool ContentSecurityPolicy::allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + if (m_overrideInlineStyleAllowed) + return true; + bool foundHashInEnforcedPolicies; + bool foundHashInReportOnlyPolicies; + std::tie(foundHashInEnforcedPolicies, foundHashInReportOnlyPolicies) = findHashOfContentInPolicies(&ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash, styleContent, m_hashAlgorithmsForInlineStylesheets); + if (foundHashInEnforcedPolicies && foundHashInReportOnlyPolicies) + return true; + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), "Refused to apply a stylesheet", "its hash, its nonce, or 'unsafe-inline'"); + reportViolation(ContentSecurityPolicyDirectiveNames::styleSrc, violatedDirective, URL(), consoleMessage, contextURL, TextPosition(contextLine, WTF::OrdinalNumber())); + }; + // FIXME: We should not report that the inline stylesheet violated a policy when its hash matched a source + // expression in the policy and the page has more than one policy. See <https://bugs.webkit.org/show_bug.cgi?id=159832>. + if (!foundHashInReportOnlyPolicies) + allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::ReportOnly, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle); + return foundHashInEnforcedPolicies || allPoliciesWithDispositionAllow(ContentSecurityPolicy::Disposition::Enforce, handleViolatedDirective, &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle); +} + +bool ContentSecurityPolicy::allowEval(JSC::ExecState* state, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), "Refused to execute a script", "'unsafe-eval'"); + reportViolation(ContentSecurityPolicyDirectiveNames::scriptSrc, violatedDirective, URL(), consoleMessage, state); + if (!violatedDirective.directiveList().isReportOnly()) + reportBlockedScriptExecutionToInspector(violatedDirective.text()); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval); +} + +bool ContentSecurityPolicy::allowFrameAncestors(const Frame& frame, const URL& url, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + Frame& topFrame = frame.tree().top(); + if (&frame == &topFrame) + return true; + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, "Refused to load"); + reportViolation(ContentSecurityPolicyDirectiveNames::frameAncestors, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor, frame); +} + +bool ContentSecurityPolicy::allowPluginType(const String& type, const String& typeAttribute, const URL& url, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, "Refused to load", "its MIME type"); + reportViolation(ContentSecurityPolicyDirectiveNames::pluginTypes, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType, type, typeAttribute); +} + +bool ContentSecurityPolicy::allowObjectFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying())) + return true; + // As per section object-src of the Content Security Policy Level 3 spec., <http://w3c.github.io/webappsec-csp> (Editor's Draft, 29 February 2016), + // "If plugin content is loaded without an associated URL (perhaps an object element lacks a data attribute, but loads some default plugin based + // on the specified type), it MUST be blocked if object-src's value is 'none', but will otherwise be allowed". + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, "Refused to load"); + reportViolation(ContentSecurityPolicyDirectiveNames::objectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::Yes); +} + +bool ContentSecurityPolicy::allowChildFrameFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying())) + return true; + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + const char* effectiveViolatedDirective = violatedDirective.name() == ContentSecurityPolicyDirectiveNames::frameSrc ? ContentSecurityPolicyDirectiveNames::frameSrc : ContentSecurityPolicyDirectiveNames::childSrc; + String consoleMessage = consoleMessageForViolation(effectiveViolatedDirective, violatedDirective, url, "Refused to load"); + reportViolation(effectiveViolatedDirective, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame, url, redirectResponseReceived == RedirectResponseReceived::Yes); +} + +bool ContentSecurityPolicy::allowResourceFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived, const char* name, ResourcePredicate resourcePredicate) const +{ + if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying())) + return true; + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(name, violatedDirective, url, "Refused to load"); + reportViolation(name, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), resourcePredicate, url, redirectResponseReceived == RedirectResponseReceived::Yes); +} + +bool ContentSecurityPolicy::allowChildContextFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::childSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext); +} + +bool ContentSecurityPolicy::allowScriptFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::scriptSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForScript); +} + +bool ContentSecurityPolicy::allowImageFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::imgSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForImage); +} + +bool ContentSecurityPolicy::allowStyleFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::styleSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle); +} + +bool ContentSecurityPolicy::allowFontFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::fontSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFont); +} + +bool ContentSecurityPolicy::allowMediaFromSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::mediaSrc, &ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia); +} + +bool ContentSecurityPolicy::allowConnectToSource(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying())) + return true; + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, "Refused to connect to"); + reportViolation(ContentSecurityPolicyDirectiveNames::connectSrc, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource, url, redirectResponseReceived == RedirectResponseReceived::Yes); +} + +bool ContentSecurityPolicy::allowFormAction(const URL& url, RedirectResponseReceived redirectResponseReceived) const +{ + return allowResourceFromSource(url, redirectResponseReceived, ContentSecurityPolicyDirectiveNames::formAction, &ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction); +} + +bool ContentSecurityPolicy::allowBaseURI(const URL& url, bool overrideContentSecurityPolicy) const +{ + if (overrideContentSecurityPolicy) + return true; + if (SchemeRegistry::schemeShouldBypassContentSecurityPolicy(url.protocol().toStringWithoutCopying())) + return true; + String sourceURL; + TextPosition sourcePosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber()); + auto handleViolatedDirective = [&] (const ContentSecurityPolicyDirective& violatedDirective) { + String consoleMessage = consoleMessageForViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, "Refused to change the document base URL to"); + reportViolation(ContentSecurityPolicyDirectiveNames::baseURI, violatedDirective, url, consoleMessage, sourceURL, sourcePosition); + }; + return allPoliciesAllow(WTFMove(handleViolatedDirective), &ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI, url); +} + +static String stripURLForUseInReport(Document& document, const URL& url) +{ + if (!url.isValid()) + return String(); + if (!url.isHierarchical() || url.protocolIs("file")) + return url.protocol().toString(); + return document.securityOrigin().canRequest(url) ? url.strippedForUseAsReferrer() : SecurityOrigin::create(url).get().toString(); +} + +void ContentSecurityPolicy::reportViolation(const String& violatedDirective, const ContentSecurityPolicyDirective& effectiveViolatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const +{ + // FIXME: Extract source file and source position from JSC::ExecState. + return reportViolation(violatedDirective, effectiveViolatedDirective.text(), effectiveViolatedDirective.directiveList(), blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state); +} + +void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* state) const +{ + // FIXME: Extract source file and source position from JSC::ExecState. + return reportViolation(effectiveViolatedDirective, violatedDirective, violatedDirectiveList, blockedURL, consoleMessage, String(), TextPosition(WTF::OrdinalNumber::beforeFirst(), WTF::OrdinalNumber::beforeFirst()), state); +} + +void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* state) const +{ + return reportViolation(effectiveViolatedDirective, violatedDirective.text(), violatedDirective.directiveList(), blockedURL, consoleMessage, sourceURL, sourcePosition, state); +} + +void ContentSecurityPolicy::reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* state) const +{ + logToConsole(consoleMessage, sourceURL, sourcePosition.m_line, state); + + if (!m_isReportingEnabled) + return; + + // FIXME: Support sending reports from worker. + if (!is<Document>(m_scriptExecutionContext) && !m_frame) + return; + + ASSERT(!m_frame || effectiveViolatedDirective == ContentSecurityPolicyDirectiveNames::frameAncestors); + + auto& document = is<Document>(m_scriptExecutionContext) ? downcast<Document>(*m_scriptExecutionContext) : *m_frame->document(); + auto* frame = document.frame(); + ASSERT(!m_frame || m_frame == frame); + if (!frame) + return; + + String documentURI; + String blockedURI; + if (is<Document>(m_scriptExecutionContext)) { + documentURI = document.url().strippedForUseAsReferrer(); + blockedURI = stripURLForUseInReport(document, blockedURL); + } else { + // The URL of |document| may not have been initialized (say, when reporting a frame-ancestors violation). + // So, we use the URL of the blocked document for the protected document URL. + documentURI = blockedURL; + blockedURI = blockedURL; + } + String violatedDirectiveText = violatedDirective; + String originalPolicy = violatedDirectiveList.header(); + String referrer = document.referrer(); + ASSERT(document.loader()); + // FIXME: Is it policy to not use the status code for HTTPS, or is that a bug? + unsigned short statusCode = document.url().protocolIs("http") && document.loader() ? document.loader()->response().httpStatusCode() : 0; + + String sourceFile; + int lineNumber = 0; + int columnNumber = 0; + auto stack = createScriptCallStack(JSMainThreadExecState::currentState(), 2); + auto* callFrame = stack->firstNonNativeCallFrame(); + if (callFrame && callFrame->lineNumber()) { + sourceFile = stripURLForUseInReport(document, URL(URL(), callFrame->sourceURL())); + lineNumber = callFrame->lineNumber(); + columnNumber = callFrame->columnNumber(); + } + + // 1. Dispatch violation event. + bool canBubble = false; + bool cancelable = false; + document.enqueueDocumentEvent(SecurityPolicyViolationEvent::create(eventNames().securitypolicyviolationEvent, canBubble, cancelable, documentURI, referrer, blockedURI, violatedDirectiveText, effectiveViolatedDirective, originalPolicy, sourceFile, statusCode, lineNumber, columnNumber)); + + // 2. Send violation report (if applicable). + auto& reportURIs = violatedDirectiveList.reportURIs(); + 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 suppress 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. + + auto cspReport = InspectorObject::create(); + cspReport->setString(ASCIILiteral("document-uri"), documentURI); + cspReport->setString(ASCIILiteral("referrer"), referrer); + cspReport->setString(ASCIILiteral("violated-directive"), violatedDirectiveText); + cspReport->setString(ASCIILiteral("effective-directive"), effectiveViolatedDirective); + cspReport->setString(ASCIILiteral("original-policy"), originalPolicy); + cspReport->setString(ASCIILiteral("blocked-uri"), blockedURI); + cspReport->setInteger(ASCIILiteral("status-code"), statusCode); + if (!sourceFile.isNull()) { + cspReport->setString(ASCIILiteral("source-file"), sourceFile); + cspReport->setInteger(ASCIILiteral("line-number"), lineNumber); + cspReport->setInteger(ASCIILiteral("column-number"), columnNumber); + } + + auto reportObject = InspectorObject::create(); + reportObject->setObject(ASCIILiteral("csp-report"), WTFMove(cspReport)); + + auto report = FormData::create(reportObject->toJSONString().utf8()); + for (const auto& url : reportURIs) + PingLoader::sendViolationReport(*frame, is<Document>(m_scriptExecutionContext) ? document.completeURL(url) : document.completeURL(url, blockedURL), report.copyRef(), ViolationReportType::ContentSecurityPolicy); +} + +void ContentSecurityPolicy::reportUnsupportedDirective(const String& name) const +{ + String message; + if (equalLettersIgnoringASCIICase(name, "allow")) + message = ASCIILiteral("The 'allow' directive has been replaced with 'default-src'. Please use that directive instead, as 'allow' has no effect."); + else if (equalLettersIgnoringASCIICase(name, "options")) + message = 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."); + else if (equalLettersIgnoringASCIICase(name, "policy-uri")) + message = ASCIILiteral("The 'policy-uri' directive has been removed from the specification. Please specify a complete policy via the Content-Security-Policy header."); + else + message = makeString("Unrecognized Content-Security-Policy directive '", name, "'.\n"); // FIXME: Why does this include a newline? + + logToConsole(message); +} + +void ContentSecurityPolicy::reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const +{ + logToConsole("The Content Security Policy directive '" + directiveName + "' contains '" + sourceExpression + "' as a source expression. Did you mean '" + directiveName + " ...; " + sourceExpression + "...' (note the semicolon)?"); +} + +void ContentSecurityPolicy::reportDuplicateDirective(const String& name) const +{ + logToConsole(makeString("Ignoring duplicate Content-Security-Policy directive '", name, "'.\n")); +} + +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::reportInvalidDirectiveInReportOnlyMode(const String& directiveName) const +{ + logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered in a report-only policy."); +} + +void ContentSecurityPolicy::reportInvalidDirectiveInHTTPEquivMeta(const String& directiveName) const +{ + logToConsole("The Content Security Policy directive '" + directiveName + "' is ignored when delivered via an HTML meta element."); +} + +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; + if (invalidChar == '?') + ignoring = "The query component, including the '?', will be ignored."; + else + ignoring = "The fragment identifier, 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::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 (equalLettersIgnoringASCIICase(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 +{ + if (!m_isReportingEnabled) + return; + + // FIXME: <http://webkit.org/b/114317> ContentSecurityPolicy::logToConsole should include a column number + if (m_scriptExecutionContext) + m_scriptExecutionContext->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state); + else if (m_frame && m_frame->document()) + static_cast<ScriptExecutionContext*>(m_frame->document())->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message, contextURL, contextLine.oneBasedInt(), 0, state); +} + +void ContentSecurityPolicy::reportBlockedScriptExecutionToInspector(const String& directiveText) const +{ + if (m_scriptExecutionContext) + InspectorInstrumentation::scriptExecutionBlockedByCSP(m_scriptExecutionContext, directiveText); +} + +void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(ResourceRequest& request, InsecureRequestType requestType) const +{ + URL url = request.url(); + upgradeInsecureRequestIfNeeded(url, requestType); + request.setURL(url); +} + +void ContentSecurityPolicy::upgradeInsecureRequestIfNeeded(URL& url, InsecureRequestType requestType) const +{ + if (!url.protocolIs("http") && !url.protocolIs("ws")) + return; + + bool upgradeRequest = m_insecureNavigationRequestsToUpgrade.contains(SecurityOrigin::create(url)); + if (requestType == InsecureRequestType::Load || requestType == InsecureRequestType::FormSubmission) + upgradeRequest |= m_upgradeInsecureRequests; + + if (!upgradeRequest) + return; + + if (url.protocolIs("http")) + url.setProtocol("https"); + else if (url.protocolIs("ws")) + url.setProtocol("wss"); + else + return; + + if (url.port() && url.port().value() == 80) + url.setPort(443); +} + +void ContentSecurityPolicy::setUpgradeInsecureRequests(bool upgradeInsecureRequests) +{ + m_upgradeInsecureRequests = upgradeInsecureRequests; + if (!m_upgradeInsecureRequests) + return; + + if (!m_scriptExecutionContext) + return; + + // Store the upgrade domain as an 'insecure' protocol so we can quickly identify + // origins we should upgrade. + URL upgradeURL = m_scriptExecutionContext->url(); + if (upgradeURL.protocolIs("https")) + upgradeURL.setProtocol("http"); + else if (upgradeURL.protocolIs("wss")) + upgradeURL.setProtocol("ws"); + + m_insecureNavigationRequestsToUpgrade.add(SecurityOrigin::create(upgradeURL)); +} + +void ContentSecurityPolicy::inheritInsecureNavigationRequestsToUpgradeFromOpener(const ContentSecurityPolicy& other) +{ + m_insecureNavigationRequestsToUpgrade.add(other.m_insecureNavigationRequestsToUpgrade.begin(), other.m_insecureNavigationRequestsToUpgrade.end()); +} + +HashSet<RefPtr<SecurityOrigin>>&& ContentSecurityPolicy::takeNavigationRequestsToUpgrade() +{ + return WTFMove(m_insecureNavigationRequestsToUpgrade); +} + +void ContentSecurityPolicy::setInsecureNavigationRequestsToUpgrade(HashSet<RefPtr<SecurityOrigin>>&& insecureNavigationRequests) +{ + m_insecureNavigationRequestsToUpgrade = WTFMove(insecureNavigationRequests); +} + +} diff --git a/Source/WebCore/page/csp/ContentSecurityPolicy.h b/Source/WebCore/page/csp/ContentSecurityPolicy.h new file mode 100644 index 000000000..5286a86a6 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicy.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 + +#include "ContentSecurityPolicyResponseHeaders.h" +#include "SecurityOrigin.h" +#include "SecurityOriginHash.h" +#include <wtf/HashSet.h> +#include <wtf/OptionSet.h> +#include <wtf/Vector.h> +#include <wtf/text/TextPosition.h> + +namespace JSC { +class ExecState; +} + +namespace WTF { +class OrdinalNumber; +} + +namespace WebCore { + +class ContentSecurityPolicyDirective; +class ContentSecurityPolicyDirectiveList; +class ContentSecurityPolicySource; +class DOMStringList; +class Frame; +class JSDOMWindowShell; +class ResourceRequest; +class ScriptExecutionContext; +class SecurityOrigin; +class TextEncoding; +class URL; + +enum class ContentSecurityPolicyHashAlgorithm; + +typedef Vector<std::unique_ptr<ContentSecurityPolicyDirectiveList>> CSPDirectiveListVector; +typedef int SandboxFlags; + +class ContentSecurityPolicy { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit ContentSecurityPolicy(ScriptExecutionContext&); + explicit ContentSecurityPolicy(const SecurityOrigin&, const Frame* = nullptr); + ~ContentSecurityPolicy(); + + void copyStateFrom(const ContentSecurityPolicy*); + void copyUpgradeInsecureRequestStateFrom(const ContentSecurityPolicy&); + + void didCreateWindowShell(JSDOMWindowShell&) const; + + enum class PolicyFrom { + API, + HTTPEquivMeta, + HTTPHeader, + Inherited, + }; + ContentSecurityPolicyResponseHeaders responseHeaders() const; + enum ReportParsingErrors { No, Yes }; + void didReceiveHeaders(const ContentSecurityPolicyResponseHeaders&, ReportParsingErrors = ReportParsingErrors::Yes); + void didReceiveHeader(const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom); + + bool allowScriptWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const; + bool allowStyleWithNonce(const String& nonce, bool overrideContentSecurityPolicy = false) const; + + bool allowJavaScriptURLs(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false) const; + bool allowInlineEventHandlers(const String& contextURL, const WTF::OrdinalNumber& contextLine, bool overrideContentSecurityPolicy = false) const; + bool allowInlineScript(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& scriptContent, bool overrideContentSecurityPolicy = false) const; + bool allowInlineStyle(const String& contextURL, const WTF::OrdinalNumber& contextLine, const String& styleContent, bool overrideContentSecurityPolicy = false) const; + + bool allowEval(JSC::ExecState*, bool overrideContentSecurityPolicy = false) const; + + bool allowPluginType(const String& type, const String& typeAttribute, const URL&, bool overrideContentSecurityPolicy = false) const; + + bool allowFrameAncestors(const Frame&, const URL&, bool overrideContentSecurityPolicy = false) const; + + enum class RedirectResponseReceived { No, Yes }; + bool allowScriptFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowImageFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowStyleFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowFontFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowMediaFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + + bool allowChildFrameFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowChildContextFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowConnectToSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowFormAction(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + + bool allowObjectFromSource(const URL&, RedirectResponseReceived = RedirectResponseReceived::No) const; + bool allowBaseURI(const URL&, bool overrideContentSecurityPolicy = false) const; + + void setOverrideAllowInlineStyle(bool); + + void gatherReportURIs(DOMStringList&) const; + + bool allowRunningOrDisplayingInsecureContent(const URL&); + + // The following functions are used by internal data structures to call back into this object when parsing, validating, + // and applying a Content Security Policy. + // FIXME: We should make the various directives serve only as state stores for the parsed policy and remove these functions. + // This class should traverse the directives, validating the policy, and applying it to the script execution context. + + // Used by ContentSecurityPolicyMediaListDirective + void reportInvalidPluginTypes(const String&) const; + + // Used by ContentSecurityPolicySourceList + void reportDirectiveAsSourceExpression(const String& directiveName, const String& sourceExpression) const; + void reportInvalidPathCharacter(const String& directiveName, const String& value, const char) const; + void reportInvalidSourceExpression(const String& directiveName, const String& source) const; + bool urlMatchesSelf(const URL&) const; + bool allowContentSecurityPolicySourceStarToMatchAnyProtocol() const; + + // Used by ContentSecurityPolicyDirectiveList + void reportDuplicateDirective(const String&) const; + void reportInvalidDirectiveValueCharacter(const String& directiveName, const String& value) const; + void reportInvalidSandboxFlags(const String&) const; + void reportInvalidDirectiveInReportOnlyMode(const String&) const; + void reportInvalidDirectiveInHTTPEquivMeta(const String&) const; + void reportMissingReportURI(const String&) const; + void reportUnsupportedDirective(const String&) const; + void enforceSandboxFlags(SandboxFlags sandboxFlags) { m_sandboxFlags |= sandboxFlags; } + void addHashAlgorithmsForInlineScripts(OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsForInlineScripts) + { + m_hashAlgorithmsForInlineScripts |= hashAlgorithmsForInlineScripts; + } + void addHashAlgorithmsForInlineStylesheets(OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsForInlineStylesheets) + { + m_hashAlgorithmsForInlineStylesheets |= hashAlgorithmsForInlineStylesheets; + } + + // Used by ContentSecurityPolicySource + bool protocolMatchesSelf(const URL&) const; + + void setUpgradeInsecureRequests(bool); + bool upgradeInsecureRequests() const { return m_upgradeInsecureRequests; } + enum class InsecureRequestType { Load, FormSubmission, Navigation }; + void upgradeInsecureRequestIfNeeded(ResourceRequest&, InsecureRequestType) const; + void upgradeInsecureRequestIfNeeded(URL&, InsecureRequestType) const; + + HashSet<RefPtr<SecurityOrigin>>&& takeNavigationRequestsToUpgrade(); + void inheritInsecureNavigationRequestsToUpgradeFromOpener(const ContentSecurityPolicy&); + void setInsecureNavigationRequestsToUpgrade(HashSet<RefPtr<SecurityOrigin>>&&); + +private: + void logToConsole(const String& message, const String& contextURL = String(), const WTF::OrdinalNumber& contextLine = WTF::OrdinalNumber::beforeFirst(), JSC::ExecState* = nullptr) const; + void updateSourceSelf(const SecurityOrigin&); + void applyPolicyToScriptExecutionContext(); + + const TextEncoding& documentEncoding() const; + + enum class Disposition { + Enforce, + ReportOnly, + }; + + using ViolatedDirectiveCallback = std::function<void (const ContentSecurityPolicyDirective&)>; + + template<typename Predicate, typename... Args> + typename std::enable_if<!std::is_convertible<Predicate, ViolatedDirectiveCallback>::value, bool>::type allPoliciesWithDispositionAllow(Disposition, Predicate&&, Args&&...) const; + + template<typename Predicate, typename... Args> + bool allPoliciesWithDispositionAllow(Disposition, ViolatedDirectiveCallback&&, Predicate&&, Args&&...) const; + + template<typename Predicate, typename... Args> + bool allPoliciesAllow(ViolatedDirectiveCallback&&, Predicate&&, Args&&...) const WARN_UNUSED_RETURN; + + using ResourcePredicate = const ContentSecurityPolicyDirective *(ContentSecurityPolicyDirectiveList::*)(const URL &, bool) const; + bool allowResourceFromSource(const URL&, RedirectResponseReceived, const char*, ResourcePredicate) const; + + using HashInEnforcedAndReportOnlyPoliciesPair = std::pair<bool, bool>; + template<typename Predicate> HashInEnforcedAndReportOnlyPoliciesPair findHashOfContentInPolicies(Predicate&&, const String& content, OptionSet<ContentSecurityPolicyHashAlgorithm>) const WARN_UNUSED_RETURN; + + void reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, JSC::ExecState*) const; + void reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList&, const URL& blockedURL, const String& consoleMessage, JSC::ExecState* = nullptr) const; + void reportViolation(const String& effectiveViolatedDirective, const ContentSecurityPolicyDirective& violatedDirective, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState* = nullptr) const; + void reportViolation(const String& effectiveViolatedDirective, const String& violatedDirective, const ContentSecurityPolicyDirectiveList& violatedDirectiveList, const URL& blockedURL, const String& consoleMessage, const String& sourceURL, const TextPosition& sourcePosition, JSC::ExecState*) const; + void reportBlockedScriptExecutionToInspector(const String& directiveText) const; + + // We can never have both a script execution context and a frame. + ScriptExecutionContext* m_scriptExecutionContext { nullptr }; + const Frame* m_frame { nullptr }; + std::unique_ptr<ContentSecurityPolicySource> m_selfSource; + String m_selfSourceProtocol; + CSPDirectiveListVector m_policies; + String m_lastPolicyEvalDisabledErrorMessage; + SandboxFlags m_sandboxFlags; + bool m_overrideInlineStyleAllowed { false }; + bool m_isReportingEnabled { true }; + bool m_upgradeInsecureRequests { false }; + bool m_hasAPIPolicy { false }; + OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsForInlineScripts; + OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsForInlineStylesheets; + HashSet<RefPtr<SecurityOrigin>> m_insecureNavigationRequestsToUpgrade; +}; + +} diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp new file mode 100644 index 000000000..980c72fd4 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.cpp @@ -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. + */ + +#include "config.h" +#include "ContentSecurityPolicyDirective.h" + +#include "ContentSecurityPolicyDirectiveList.h" + +namespace WebCore { + +bool ContentSecurityPolicyDirective::isDefaultSrc() const +{ + return this == m_directiveList.defaultSrc(); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/FeatureObserver.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.h index f5397abee..a8f1d4f0b 100644 --- a/Source/WebCore/page/FeatureObserver.cpp +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirective.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2012 Google, Inc. All rights reserved. + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 @@ -13,7 +14,7 @@ * 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 + * 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,56 +24,34 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "FeatureObserver.h" +#pragma once -#include "DOMWindow.h" -#include "Document.h" -#include "HistogramSupport.h" -#include "Page.h" +#include <wtf/text/WTFString.h> namespace WebCore { -FeatureObserver::FeatureObserver() -{ -} +class ContentSecurityPolicyDirectiveList; -FeatureObserver::~FeatureObserver() -{ - updateMeasurements(); -} +class ContentSecurityPolicyDirective { +public: + ContentSecurityPolicyDirective(const ContentSecurityPolicyDirectiveList& directiveList, const String& name, const String& value) + : m_name(name) + , m_text(name + ' ' + value) + , m_directiveList(directiveList) + { + } -void FeatureObserver::updateMeasurements() -{ - if (!m_featureBits) - return; + const String& name() const { return m_name; } + const String& text() const { return m_text; } - // 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(); -} + const ContentSecurityPolicyDirectiveList& directiveList() const { return m_directiveList; } -void FeatureObserver::didCommitLoad() -{ - updateMeasurements(); -} + bool isDefaultSrc() const; -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); -} +private: + String m_name; + String m_text; + const ContentSecurityPolicyDirectiveList& m_directiveList; +}; } // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp new file mode 100644 index 000000000..4bdc6f1d2 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.cpp @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 "ContentSecurityPolicyDirectiveList.h" + +#include "ContentSecurityPolicyDirectiveNames.h" +#include "Document.h" +#include "Frame.h" +#include "ParsingUtilities.h" +#include "SecurityContext.h" + +namespace WebCore { + +static bool isDirectiveNameCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '-'; +} + +static bool isDirectiveValueCharacter(UChar c) +{ + return isASCIISpace(c) || (c >= 0x21 && c <= 0x7e); // Whitespace + VCHAR +} + +static bool isNotASCIISpace(UChar c) +{ + return !isASCIISpace(c); +} + +static inline bool checkEval(ContentSecurityPolicySourceListDirective* directive) +{ + return !directive || directive->allowEval(); +} + +static inline bool checkInline(ContentSecurityPolicySourceListDirective* directive) +{ + return !directive || directive->allowInline(); +} + +static inline bool checkSource(ContentSecurityPolicySourceListDirective* directive, const URL& url, bool didReceiveRedirectResponse = false, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty = ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::No) +{ + return !directive || directive->allows(url, didReceiveRedirectResponse, shouldAllowEmptyURLIfSourceListEmpty); +} + +static inline bool checkHash(ContentSecurityPolicySourceListDirective* directive, const ContentSecurityPolicyHash& hash) +{ + return !directive || directive->allows(hash); +} + +static inline bool checkNonce(ContentSecurityPolicySourceListDirective* directive, const String& nonce) +{ + return !directive || directive->allows(nonce); +} + +static inline bool checkFrameAncestors(ContentSecurityPolicySourceListDirective* directive, const Frame& frame) +{ + if (!directive) + return true; + bool didReceiveRedirectResponse = false; + for (Frame* current = frame.tree().parent(); current; current = current->tree().parent()) { + if (!directive->allows(current->document()->url(), didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone::No)) + return false; + } + return true; +} + +static inline bool checkMediaType(ContentSecurityPolicyMediaListDirective* directive, const String& type, const String& typeAttribute) +{ + if (!directive) + return true; + if (typeAttribute.isEmpty() || typeAttribute.stripWhiteSpace() != type) + return false; + return directive->allows(type); +} + +ContentSecurityPolicyDirectiveList::ContentSecurityPolicyDirectiveList(ContentSecurityPolicy& policy, ContentSecurityPolicyHeaderType type) + : m_policy(policy) + , m_headerType(type) +{ + m_reportOnly = (type == ContentSecurityPolicyHeaderType::Report || type == ContentSecurityPolicyHeaderType::PrefixedReport); +} + +std::unique_ptr<ContentSecurityPolicyDirectiveList> ContentSecurityPolicyDirectiveList::create(ContentSecurityPolicy& policy, const String& header, ContentSecurityPolicyHeaderType type, ContentSecurityPolicy::PolicyFrom from) +{ + auto directives = std::make_unique<ContentSecurityPolicyDirectiveList>(policy, type); + directives->parse(header, from); + + if (!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; +} + +ContentSecurityPolicySourceListDirective* ContentSecurityPolicyDirectiveList::operativeDirective(ContentSecurityPolicySourceListDirective* directive) const +{ + return directive ? directive : m_defaultSrc.get(); +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeEval() const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get()); + if (checkEval(operativeDirective)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineScript() const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get()); + if (checkInline(operativeDirective)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForUnsafeInlineStyle() const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get()); + if (checkInline(operativeDirective)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptHash(const ContentSecurityPolicyHash& hash) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get()); + if (checkHash(operativeDirective, hash)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleHash(const ContentSecurityPolicyHash& hash) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get()); + if (checkHash(operativeDirective, hash)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScriptNonce(const String& nonce) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get()); + if (checkNonce(operativeDirective, nonce)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForStyleNonce(const String& nonce) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get()); + if (checkNonce(operativeDirective, nonce)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForBaseURI(const URL& url) const +{ + if (checkSource(m_baseURI.get(), url)) + return nullptr; + return m_baseURI.get(); +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForChildContext(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_childSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForConnectSource(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_connectSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFont(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_fontSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFormAction(const URL& url, bool didReceiveRedirectResponse) const +{ + if (checkSource(m_formAction.get(), url, didReceiveRedirectResponse)) + return nullptr; + return m_formAction.get(); +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFrame(const URL& url, bool didReceiveRedirectResponse) const +{ + if (url.isBlankURL()) + return nullptr; + + // We must enforce the frame-src directive (if specified) before enforcing the child-src directive for a nested browsing + // context by <https://w3c.github.io/webappsec-csp/2/#directive-child-src-nested> (29 August 2015). + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_frameSrc ? m_frameSrc.get() : m_childSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForFrameAncestor(const Frame& frame) const +{ + if (checkFrameAncestors(m_frameAncestors.get(), frame)) + return nullptr; + return m_frameAncestors.get(); +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForImage(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_imgSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForMedia(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_mediaSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForObjectSource(const URL& url, bool didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty) const +{ + if (url.isBlankURL()) + return nullptr; + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_objectSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse, shouldAllowEmptyURLIfSourceListEmpty)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForPluginType(const String& type, const String& typeAttribute) const +{ + if (checkMediaType(m_pluginTypes.get(), type, typeAttribute)) + return nullptr; + return m_pluginTypes.get(); +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForScript(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_scriptSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +const ContentSecurityPolicyDirective* ContentSecurityPolicyDirectiveList::violatedDirectiveForStyle(const URL& url, bool didReceiveRedirectResponse) const +{ + ContentSecurityPolicySourceListDirective* operativeDirective = this->operativeDirective(m_styleSrc.get()); + if (checkSource(operativeDirective, url, didReceiveRedirectResponse)) + return nullptr; + return operativeDirective; +} + +// policy = directive-list +// directive-list = [ directive *( ";" [ directive ] ) ] +// +void ContentSecurityPolicyDirectiveList::parse(const String& policy, ContentSecurityPolicy::PolicyFrom policyFrom) +{ + m_header = policy; + if (policy.isEmpty()) + return; + + auto characters = StringView(policy).upconvertedCharacters(); + const UChar* position = characters; + const UChar* end = position + policy.length(); + + while (position < end) { + const UChar* directiveBegin = position; + skipUntil<UChar>(position, end, ';'); + + String name, value; + if (parseDirective(directiveBegin, position, name, value)) { + ASSERT(!name.isEmpty()); + if (policyFrom == ContentSecurityPolicy::PolicyFrom::Inherited) { + if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::upgradeInsecureRequests)) + continue; + } else if (policyFrom == ContentSecurityPolicy::PolicyFrom::HTTPEquivMeta) { + if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameAncestors)) { + m_policy.reportInvalidDirectiveInHTTPEquivMeta(name); + continue; + } + } + addDirective(name, value); + } + + ASSERT(position == end || *position == ';'); + skipExactly<UChar>(position, end, ';'); + } +} + +// directive = *WSP [ directive-name [ WSP directive-value ] ] +// directive-name = 1*( ALPHA / DIGIT / "-" ) +// directive-value = *( WSP / <VCHAR except ";"> ) +// +bool ContentSecurityPolicyDirectiveList::parseDirective(const UChar* begin, const UChar* end, String& name, String& value) +{ + ASSERT(name.isEmpty()); + ASSERT(value.isEmpty()); + + const UChar* position = begin; + skipWhile<UChar, isASCIISpace>(position, end); + + // Empty directive (e.g. ";;;"). Exit early. + if (position == end) + return false; + + const UChar* nameBegin = position; + skipWhile<UChar, isDirectiveNameCharacter>(position, end); + + // The directive-name must be non-empty. + if (nameBegin == position) { + skipWhile<UChar, isNotASCIISpace>(position, end); + m_policy.reportUnsupportedDirective(String(nameBegin, position - nameBegin)); + return false; + } + + name = String(nameBegin, position - nameBegin); + + if (position == end) + return true; + + if (!skipExactly<UChar, isASCIISpace>(position, end)) { + skipWhile<UChar, isNotASCIISpace>(position, end); + m_policy.reportUnsupportedDirective(String(nameBegin, position - nameBegin)); + return false; + } + + skipWhile<UChar, isASCIISpace>(position, end); + + const UChar* valueBegin = position; + skipWhile<UChar, isDirectiveValueCharacter>(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 ContentSecurityPolicyDirectiveList::parseReportURI(const String& name, const String& value) +{ + if (!m_reportURIs.isEmpty()) { + m_policy.reportDuplicateDirective(name); + return; + } + + auto characters = StringView(value).upconvertedCharacters(); + const UChar* position = characters; + const UChar* end = position + value.length(); + + while (position < end) { + skipWhile<UChar, isASCIISpace>(position, end); + + const UChar* urlBegin = position; + skipWhile<UChar, isNotASCIISpace>(position, end); + + if (urlBegin < position) + m_reportURIs.append(value.substring(urlBegin - characters, position - urlBegin)); + } +} + + +template<class CSPDirectiveType> +void ContentSecurityPolicyDirectiveList::setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>& directive) +{ + if (directive) { + m_policy.reportDuplicateDirective(name); + return; + } + directive = std::make_unique<CSPDirectiveType>(*this, name, value); +} + +void ContentSecurityPolicyDirectiveList::applySandboxPolicy(const String& name, const String& sandboxPolicy) +{ + if (m_reportOnly) { + m_policy.reportInvalidDirectiveInReportOnlyMode(name); + return; + } + 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 ContentSecurityPolicyDirectiveList::setUpgradeInsecureRequests(const String& name) +{ + if (m_reportOnly) { + m_policy.reportInvalidDirectiveInReportOnlyMode(name); + return; + } + if (m_upgradeInsecureRequests) { + m_policy.reportDuplicateDirective(name); + return; + } + m_upgradeInsecureRequests = true; + m_policy.setUpgradeInsecureRequests(true); +} + +void ContentSecurityPolicyDirectiveList::setBlockAllMixedContentEnabled(const String& name) +{ + if (m_hasBlockAllMixedContentDirective) { + m_policy.reportDuplicateDirective(name); + return; + } + m_hasBlockAllMixedContentDirective = true; +} + +void ContentSecurityPolicyDirectiveList::addDirective(const String& name, const String& value) +{ + ASSERT(!name.isEmpty()); + + if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::defaultSrc)) { + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_defaultSrc); + m_policy.addHashAlgorithmsForInlineScripts(m_defaultSrc->hashAlgorithmsUsed()); + m_policy.addHashAlgorithmsForInlineStylesheets(m_defaultSrc->hashAlgorithmsUsed()); + } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::scriptSrc)) { + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_scriptSrc); + m_policy.addHashAlgorithmsForInlineScripts(m_scriptSrc->hashAlgorithmsUsed()); + } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::styleSrc)) { + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_styleSrc); + m_policy.addHashAlgorithmsForInlineStylesheets(m_styleSrc->hashAlgorithmsUsed()); + } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::objectSrc)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_objectSrc); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameSrc)) { + // FIXME: Log to console "The frame-src directive is deprecated. Use the child-src directive instead." + // See <https://bugs.webkit.org/show_bug.cgi?id=155773>. + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_frameSrc); + } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::imgSrc)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_imgSrc); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::fontSrc)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_fontSrc); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::mediaSrc)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_mediaSrc); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::connectSrc)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_connectSrc); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::childSrc)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_childSrc); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::formAction)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_formAction); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::baseURI)) + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_baseURI); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameAncestors)) { + if (m_reportOnly) { + m_policy.reportInvalidDirectiveInReportOnlyMode(name); + return; + } + setCSPDirective<ContentSecurityPolicySourceListDirective>(name, value, m_frameAncestors); + } else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::pluginTypes)) + setCSPDirective<ContentSecurityPolicyMediaListDirective>(name, value, m_pluginTypes); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox)) + applySandboxPolicy(name, value); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI)) + parseReportURI(name, value); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::upgradeInsecureRequests)) + setUpgradeInsecureRequests(name); + else if (equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::blockAllMixedContent)) + setBlockAllMixedContentEnabled(name); + else + m_policy.reportUnsupportedDirective(name); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h new file mode 100644 index 000000000..3adb22fe8 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveList.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 + +#include "ContentSecurityPolicy.h" +#include "ContentSecurityPolicyHash.h" +#include "ContentSecurityPolicyMediaListDirective.h" +#include "ContentSecurityPolicySourceListDirective.h" +#include "URL.h" + +namespace WebCore { + +class Frame; + +class ContentSecurityPolicyDirectiveList { + WTF_MAKE_FAST_ALLOCATED; +public: + static std::unique_ptr<ContentSecurityPolicyDirectiveList> create(ContentSecurityPolicy&, const String&, ContentSecurityPolicyHeaderType, ContentSecurityPolicy::PolicyFrom); + ContentSecurityPolicyDirectiveList(ContentSecurityPolicy&, ContentSecurityPolicyHeaderType); + + const String& header() const { return m_header; } + ContentSecurityPolicyHeaderType headerType() const { return m_headerType; } + + const ContentSecurityPolicyDirective* violatedDirectiveForUnsafeEval() const; + const ContentSecurityPolicyDirective* violatedDirectiveForUnsafeInlineScript() const; + const ContentSecurityPolicyDirective* violatedDirectiveForUnsafeInlineStyle() const; + + const ContentSecurityPolicyDirective* violatedDirectiveForScriptHash(const ContentSecurityPolicyHash&) const; + const ContentSecurityPolicyDirective* violatedDirectiveForStyleHash(const ContentSecurityPolicyHash&) const; + + const ContentSecurityPolicyDirective* violatedDirectiveForScriptNonce(const String&) const; + const ContentSecurityPolicyDirective* violatedDirectiveForStyleNonce(const String&) const; + + const ContentSecurityPolicyDirective* violatedDirectiveForBaseURI(const URL&) const; + const ContentSecurityPolicyDirective* violatedDirectiveForChildContext(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForConnectSource(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForFont(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForFormAction(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForFrame(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForFrameAncestor(const Frame&) const; + const ContentSecurityPolicyDirective* violatedDirectiveForImage(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForMedia(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForObjectSource(const URL&, bool didReceiveRedirectResponse, ContentSecurityPolicySourceListDirective::ShouldAllowEmptyURLIfSourceListIsNotNone) const; + const ContentSecurityPolicyDirective* violatedDirectiveForPluginType(const String& type, const String& typeAttribute) const; + const ContentSecurityPolicyDirective* violatedDirectiveForScript(const URL&, bool didReceiveRedirectResponse) const; + const ContentSecurityPolicyDirective* violatedDirectiveForStyle(const URL&, bool didReceiveRedirectResponse) const; + + const ContentSecurityPolicyDirective* defaultSrc() const { return m_defaultSrc.get(); } + + bool hasBlockAllMixedContentDirective() const { return m_hasBlockAllMixedContentDirective; } + + const String& evalDisabledErrorMessage() const { return m_evalDisabledErrorMessage; } + bool isReportOnly() const { return m_reportOnly; } + const Vector<String>& reportURIs() const { return m_reportURIs; } + + // FIXME: Remove this once we teach ContentSecurityPolicyDirectiveList how to log an arbitrary console message. + const ContentSecurityPolicy& policy() const { return m_policy; } + +private: + void parse(const String&, ContentSecurityPolicy::PolicyFrom); + + bool parseDirective(const UChar* begin, const UChar* end, String& name, String& value); + void parseReportURI(const String& name, const String& value); + void parsePluginTypes(const String& name, const String& value); + void addDirective(const String& name, const String& value); + void applySandboxPolicy(const String& name, const String& sandboxPolicy); + void setUpgradeInsecureRequests(const String& name); + void setBlockAllMixedContentEnabled(const String& name); + + template <class CSPDirectiveType> + void setCSPDirective(const String& name, const String& value, std::unique_ptr<CSPDirectiveType>&); + + ContentSecurityPolicySourceListDirective* operativeDirective(ContentSecurityPolicySourceListDirective*) const; + + void setEvalDisabledErrorMessage(const String& errorMessage) { m_evalDisabledErrorMessage = errorMessage; } + + // FIXME: Make this a const reference once we teach applySandboxPolicy() to store its policy as opposed to applying it directly onto ContentSecurityPolicy. + ContentSecurityPolicy& m_policy; + + String m_header; + ContentSecurityPolicyHeaderType m_headerType; + + bool m_reportOnly { false }; + bool m_haveSandboxPolicy { false }; + bool m_upgradeInsecureRequests { false }; + bool m_hasBlockAllMixedContentDirective { false }; + + std::unique_ptr<ContentSecurityPolicyMediaListDirective> m_pluginTypes; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_baseURI; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_connectSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_childSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_defaultSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_fontSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_formAction; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_frameAncestors; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_frameSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_imgSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_mediaSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_objectSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_scriptSrc; + std::unique_ptr<ContentSecurityPolicySourceListDirective> m_styleSrc; + + Vector<String> m_reportURIs; + + String m_evalDisabledErrorMessage; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp new file mode 100644 index 000000000..261f741f6 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.cpp @@ -0,0 +1,54 @@ +/* + * 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 "ContentSecurityPolicyDirectiveNames.h" + +namespace WebCore { + +namespace ContentSecurityPolicyDirectiveNames { + +const char* const baseURI = "base-uri"; +const char* const childSrc = "child-src"; +const char* const connectSrc = "connect-src"; +const char* const defaultSrc = "default-src"; +const char* const fontSrc = "font-src"; +const char* const formAction = "form-action"; +const char* const frameAncestors = "frame-ancestors"; +const char* const frameSrc = "frame-src"; +const char* const imgSrc = "img-src"; +const char* const mediaSrc = "media-src"; +const char* const objectSrc = "object-src"; +const char* const pluginTypes = "plugin-types"; +const char* const reportURI = "report-uri"; +const char* const sandbox = "sandbox"; +const char* const scriptSrc = "script-src"; +const char* const styleSrc = "style-src"; +const char* const upgradeInsecureRequests = "upgrade-insecure-requests"; +const char* const blockAllMixedContent = "block-all-mixed-content"; + +} // namespace ContentSecurityPolicyDirectiveNames + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h new file mode 100644 index 000000000..f13f04aca --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyDirectiveNames.h @@ -0,0 +1,54 @@ +/* + * 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 + +namespace WebCore { + +namespace ContentSecurityPolicyDirectiveNames { + +extern const char* const baseURI; +extern const char* const childSrc; +extern const char* const connectSrc; +extern const char* const defaultSrc; +extern const char* const fontSrc; +extern const char* const formAction; +extern const char* const frameAncestors; +extern const char* const frameSrc; +extern const char* const imgSrc; +extern const char* const mediaSrc; +extern const char* const objectSrc; +extern const char* const pluginTypes; +extern const char* const reportURI; +extern const char* const sandbox; +extern const char* const scriptSrc; +extern const char* const styleSrc; +extern const char* const upgradeInsecureRequests; +extern const char* const blockAllMixedContent; + +} // namespace ContentSecurityPolicyDirectiveNames + +} // namespace WebCore + diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyHash.h b/Source/WebCore/page/csp/ContentSecurityPolicyHash.h new file mode 100644 index 000000000..83c1ec22e --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyHash.h @@ -0,0 +1,66 @@ +/* + * 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 <wtf/HashTraits.h> +#include <wtf/Hasher.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// Keep this synchronized with the constant maximumContentSecurityPolicyDigestLength below. +enum class ContentSecurityPolicyHashAlgorithm { + SHA_256 = 1 << 0, + SHA_384 = 1 << 1, + SHA_512 = 1 << 2, +}; + +const size_t maximumContentSecurityPolicyDigestLength = 64; // bytes to hold SHA-512 digest + +typedef Vector<uint8_t> ContentSecurityPolicyDigest; +typedef std::pair<ContentSecurityPolicyHashAlgorithm, ContentSecurityPolicyDigest> ContentSecurityPolicyHash; + +} // namespace WebCore + +namespace WTF { + +template<> struct DefaultHash<WebCore::ContentSecurityPolicyHashAlgorithm> { typedef IntHash<WebCore::ContentSecurityPolicyHashAlgorithm> Hash; }; +template<> struct HashTraits<WebCore::ContentSecurityPolicyHashAlgorithm> : StrongEnumHashTraits<WebCore::ContentSecurityPolicyHashAlgorithm> { }; +template<> struct DefaultHash<WebCore::ContentSecurityPolicyDigest> { + struct Hash { + static unsigned hash(const WebCore::ContentSecurityPolicyDigest& digest) + { + return StringHasher::computeHashAndMaskTop8Bits(digest.data(), digest.size()); + } + static bool equal(const WebCore::ContentSecurityPolicyDigest& a, const WebCore::ContentSecurityPolicyDigest& b) + { + return a == b; + } + static const bool safeToCompareToEmptyOrDeleted = true; + }; +}; + +} // namespace WTF diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp new file mode 100644 index 000000000..3d3ed4d0d --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 "ContentSecurityPolicyMediaListDirective.h" + +#include "ContentSecurityPolicy.h" +#include "ContentSecurityPolicyDirectiveList.h" +#include "ParsingUtilities.h" +#include <wtf/text/StringHash.h> + +namespace WebCore { + +static bool isMediaTypeCharacter(UChar c) +{ + return !isASCIISpace(c) && c != '/'; +} + +static bool isNotASCIISpace(UChar c) +{ + return !isASCIISpace(c); +} + +ContentSecurityPolicyMediaListDirective::ContentSecurityPolicyMediaListDirective(const ContentSecurityPolicyDirectiveList& directiveList, const String& name, const String& value) + : ContentSecurityPolicyDirective(directiveList, name, value) +{ + parse(value); +} + +bool ContentSecurityPolicyMediaListDirective::allows(const String& type) const +{ + return m_pluginTypes.contains(type); +} + +void ContentSecurityPolicyMediaListDirective::parse(const String& value) +{ + auto characters = StringView(value).upconvertedCharacters(); + const UChar* begin = characters; + const UChar* position = begin; + const UChar* end = begin + value.length(); + + // 'plugin-types ____;' OR 'plugin-types;' + if (value.isEmpty()) { + directiveList().policy().reportInvalidPluginTypes(value); + return; + } + + while (position < end) { + // _____ OR _____mime1/mime1 + // ^ ^ + skipWhile<UChar, isASCIISpace>(position, end); + if (position == end) + return; + + // mime1/mime1 mime2/mime2 + // ^ + begin = position; + if (!skipExactly<UChar, isMediaTypeCharacter>(position, end)) { + skipWhile<UChar, isNotASCIISpace>(position, end); + directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin)); + continue; + } + skipWhile<UChar, isMediaTypeCharacter>(position, end); + + // mime1/mime1 mime2/mime2 + // ^ + if (!skipExactly<UChar>(position, end, '/')) { + skipWhile<UChar, isNotASCIISpace>(position, end); + directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin)); + continue; + } + + // mime1/mime1 mime2/mime2 + // ^ + if (!skipExactly<UChar, isMediaTypeCharacter>(position, end)) { + skipWhile<UChar, isNotASCIISpace>(position, end); + directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin)); + continue; + } + skipWhile<UChar, isMediaTypeCharacter>(position, end); + + // mime1/mime1 mime2/mime2 OR mime1/mime1 OR mime1/mime1/error + // ^ ^ ^ + if (position < end && isNotASCIISpace(*position)) { + skipWhile<UChar, isNotASCIISpace>(position, end); + directiveList().policy().reportInvalidPluginTypes(String(begin, position - begin)); + continue; + } + m_pluginTypes.add(String(begin, position - begin)); + + ASSERT(position == end || isASCIISpace(*position)); + } +} + +} // namespace WebCore diff --git a/Source/WebCore/page/WindowBase64.idl b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h index 3404399b9..d991481fa 100644 --- a/Source/WebCore/page/WindowBase64.idl +++ b/Source/WebCore/page/csp/ContentSecurityPolicyMediaListDirective.h @@ -1,7 +1,6 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. - * Copyright (C) 2011 Google Inc. All rights reserved. - * Copyright (C) 2013 Samsung Electronics. All rights reserved. + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 @@ -12,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 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 + * 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 @@ -25,9 +24,26 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -[ - NoInterfaceObject, -] interface WindowBase64 { - [RaisesException] DOMString atob(DOMString string); - [RaisesException] DOMString btoa(DOMString string); +#pragma once + +#include "ContentSecurityPolicyDirective.h" +#include <wtf/HashSet.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class ContentSecurityPolicyDirectiveList; + +class ContentSecurityPolicyMediaListDirective : public ContentSecurityPolicyDirective { +public: + ContentSecurityPolicyMediaListDirective(const ContentSecurityPolicyDirectiveList&, const String& name, const String& value); + + bool allows(const String& type) const; + +private: + void parse(const String&); + + HashSet<String> m_pluginTypes; }; + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp new file mode 100644 index 000000000..0b67b91d4 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.cpp @@ -0,0 +1,62 @@ +/* + * 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 "ContentSecurityPolicyResponseHeaders.h" + +#include "HTTPHeaderNames.h" +#include "ResourceResponse.h" + +namespace WebCore { + +ContentSecurityPolicyResponseHeaders::ContentSecurityPolicyResponseHeaders(const ResourceResponse& response) +{ + String policyValue = response.httpHeaderField(HTTPHeaderName::ContentSecurityPolicy); + if (!policyValue.isEmpty()) + m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::Enforce }); + + policyValue = response.httpHeaderField(HTTPHeaderName::ContentSecurityPolicyReportOnly); + if (!policyValue.isEmpty()) + m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::Report }); + + policyValue = response.httpHeaderField(HTTPHeaderName::XWebKitCSP); + if (!policyValue.isEmpty()) + m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::PrefixedEnforce }); + + policyValue = response.httpHeaderField(HTTPHeaderName::XWebKitCSPReportOnly); + if (!policyValue.isEmpty()) + m_headers.append({ policyValue, ContentSecurityPolicyHeaderType::PrefixedReport }); +} + +ContentSecurityPolicyResponseHeaders ContentSecurityPolicyResponseHeaders::isolatedCopy() const +{ + ContentSecurityPolicyResponseHeaders isolatedCopy; + isolatedCopy.m_headers.reserveInitialCapacity(m_headers.size()); + for (auto& header : m_headers) + isolatedCopy.m_headers.uncheckedAppend({ header.first.isolatedCopy(), header.second }); + return isolatedCopy; +} + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h new file mode 100644 index 000000000..b154613c3 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicyResponseHeaders.h @@ -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. + */ + +#pragma once + +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class ContentSecurityPolicy; +class ResourceResponse; + +enum class ContentSecurityPolicyHeaderType { + Report, + Enforce, + PrefixedReport, + PrefixedEnforce, +}; + +class ContentSecurityPolicyResponseHeaders { +public: + explicit ContentSecurityPolicyResponseHeaders(const ResourceResponse&); + + ContentSecurityPolicyResponseHeaders isolatedCopy() const; + +private: + friend class ContentSecurityPolicy; + + ContentSecurityPolicyResponseHeaders() = default; + + Vector<std::pair<String, ContentSecurityPolicyHeaderType>> m_headers; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySource.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySource.cpp new file mode 100644 index 000000000..59604c67a --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicySource.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 "ContentSecurityPolicySource.h" + +#include "ContentSecurityPolicy.h" +#include "URL.h" + +namespace WebCore { + +ContentSecurityPolicySource::ContentSecurityPolicySource(const ContentSecurityPolicy& policy, const String& scheme, const String& host, std::optional<uint16_t> 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 ContentSecurityPolicySource::matches(const URL& url, bool didReceiveRedirectResponse) const +{ + if (!schemeMatches(url)) + return false; + if (isSchemeOnly()) + return true; + return hostMatches(url) && portMatches(url) && (didReceiveRedirectResponse || pathMatches(url)); +} + +bool ContentSecurityPolicySource::schemeMatches(const URL& url) const +{ + if (m_scheme.isEmpty()) + return m_policy.protocolMatchesSelf(url); + if (equalLettersIgnoringASCIICase(m_scheme, "http")) + return url.protocolIsInHTTPFamily(); + return equalIgnoringASCIICase(url.protocol(), m_scheme); +} + +bool ContentSecurityPolicySource::hostMatches(const URL& url) const +{ + const String& host = url.host(); + if (equalIgnoringASCIICase(host, m_host)) + return true; + return m_hostHasWildcard && host.endsWith("." + m_host, false); + +} + +bool ContentSecurityPolicySource::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); + + return path == m_path; +} + +bool ContentSecurityPolicySource::portMatches(const URL& url) const +{ + if (m_portHasWildcard) + return true; + + std::optional<uint16_t> port = url.port(); + + if (port == m_port) + return true; + + if (isDefaultPortForProtocol(m_port.value(), "http") && ((!port && url.protocolIs("https")) || isDefaultPortForProtocol(port.value(), "https"))) + return true; + + if (!port) + return isDefaultPortForProtocol(m_port.value(), url.protocol()); + + if (!m_port) + return isDefaultPortForProtocol(port.value(), url.protocol()); + + return false; +} + +bool ContentSecurityPolicySource::isSchemeOnly() const +{ + return m_host.isEmpty(); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/DOMSecurityPolicy.h b/Source/WebCore/page/csp/ContentSecurityPolicySource.h index 97dbae11a..28a1a6830 100644 --- a/Source/WebCore/page/DOMSecurityPolicy.h +++ b/Source/WebCore/page/csp/ContentSecurityPolicySource.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 @@ -13,7 +14,7 @@ * 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 + * 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,51 +24,37 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DOMSecurityPolicy_h -#define DOMSecurityPolicy_h +#pragma once -#include "ContextDestructionObserver.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> #include <wtf/text/WTFString.h> namespace WebCore { class ContentSecurityPolicy; -class DOMStringList; -class Frame; +class URL; -class DOMSecurityPolicy : public RefCounted<DOMSecurityPolicy>, public ContextDestructionObserver { +class ContentSecurityPolicySource { + WTF_MAKE_FAST_ALLOCATED; public: - static PassRefPtr<DOMSecurityPolicy> create(ScriptExecutionContext* context) - { - return adoptRef(new DOMSecurityPolicy(context)); - } - ~DOMSecurityPolicy(); + ContentSecurityPolicySource(const ContentSecurityPolicy&, const String& scheme, const String& host, std::optional<uint16_t> port, const String& path, bool hostHasWildcard, bool portHasWildcard); - bool isActive() const; - PassRefPtr<DOMStringList> 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; + bool matches(const URL&, bool didReceiveRedirectResponse = false) const; private: - explicit DOMSecurityPolicy(ScriptExecutionContext*); + bool schemeMatches(const URL&) const; + bool hostMatches(const URL&) const; + bool pathMatches(const URL&) const; + bool portMatches(const URL&) const; + bool isSchemeOnly() const; + + const ContentSecurityPolicy& m_policy; + String m_scheme; + String m_host; + std::optional<uint16_t> m_port; + String m_path; + + bool m_hostHasWildcard; + bool m_portHasWildcard; }; -} - -#endif +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp new file mode 100644 index 000000000..b9e8c1141 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.cpp @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 "ContentSecurityPolicySourceList.h" + +#include "ContentSecurityPolicy.h" +#include "ContentSecurityPolicyDirectiveNames.h" +#include "ParsingUtilities.h" +#include "URL.h" +#include <wtf/ASCIICType.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/Base64.h> + +namespace WebCore { + +static bool isCSPDirectiveName(const String& name) +{ + return equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::baseURI) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::connectSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::defaultSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::fontSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::formAction) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::frameSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::imgSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::mediaSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::objectSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::pluginTypes) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::reportURI) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::sandbox) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::scriptSrc) + || equalIgnoringASCIICase(name, ContentSecurityPolicyDirectiveNames::styleSrc); +} + +static bool isSourceCharacter(UChar c) +{ + return !isASCIISpace(c); +} + +static bool isHostCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '-'; +} + +static bool isPathComponentCharacter(UChar c) +{ + return c != '?' && c != '#'; +} + +static bool isSchemeContinuationCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '+' || c == '-' || c == '.'; +} + +static bool isNotColonOrSlash(UChar c) +{ + return c != ':' && c != '/'; +} + +static bool isSourceListNone(const String& value) +{ + auto characters = StringView(value).upconvertedCharacters(); + const UChar* begin = characters; + const UChar* end = characters + value.length(); + skipWhile<UChar, isASCIISpace>(begin, end); + + const UChar* position = begin; + skipWhile<UChar, isSourceCharacter>(position, end); + if (!equalLettersIgnoringASCIICase(begin, position - begin, "'none'")) + return false; + + skipWhile<UChar, isASCIISpace>(position, end); + if (position != end) + return false; + + return true; +} + +ContentSecurityPolicySourceList::ContentSecurityPolicySourceList(const ContentSecurityPolicy& policy, const String& directiveName) + : m_policy(policy) + , m_directiveName(directiveName) +{ +} + +void ContentSecurityPolicySourceList::parse(const String& value) +{ + if (isSourceListNone(value)) { + m_isNone = true; + return; + } + auto characters = StringView(value).upconvertedCharacters(); + parse(characters, characters + value.length()); +} + +bool ContentSecurityPolicySourceList::isProtocolAllowedByStar(const URL& url) const +{ + if (m_policy.allowContentSecurityPolicySourceStarToMatchAnyProtocol()) + return true; + + // Although not allowed by the Content Security Policy Level 3 spec., we allow a data URL to match + // "img-src *" and either a data URL or blob URL to match "media-src *" for web compatibility. + bool isAllowed = url.protocolIsInHTTPFamily() || url.protocolIs("ws") || url.protocolIs("wss") || m_policy.protocolMatchesSelf(url); + if (equalIgnoringASCIICase(m_directiveName, ContentSecurityPolicyDirectiveNames::imgSrc)) + isAllowed |= url.protocolIsData(); + else if (equalIgnoringASCIICase(m_directiveName, ContentSecurityPolicyDirectiveNames::mediaSrc)) + isAllowed |= url.protocolIsData() || url.protocolIsBlob(); + return isAllowed; +} + +bool ContentSecurityPolicySourceList::matches(const URL& url, bool didReceiveRedirectResponse) +{ + if (m_allowStar && isProtocolAllowedByStar(url)) + return true; + + if (m_allowSelf && m_policy.urlMatchesSelf(url)) + return true; + + for (auto& entry : m_list) { + if (entry.matches(url, didReceiveRedirectResponse)) + return true; + } + + return false; +} + +bool ContentSecurityPolicySourceList::matches(const ContentSecurityPolicyHash& hash) const +{ + return m_hashes.contains(hash); +} + +bool ContentSecurityPolicySourceList::matches(const String& nonce) const +{ + return m_nonces.contains(nonce); +} + +// source-list = *WSP [ source *( 1*WSP source ) *WSP ] +// / *WSP "'none'" *WSP +// +void ContentSecurityPolicySourceList::parse(const UChar* begin, const UChar* end) +{ + const UChar* position = begin; + + while (position < end) { + skipWhile<UChar, isASCIISpace>(position, end); + if (position == end) + return; + + const UChar* beginSource = position; + skipWhile<UChar, isSourceCharacter>(position, end); + + String scheme, host, path; + std::optional<uint16_t> port; + bool hostHasWildcard = false; + bool portHasWildcard = false; + + if (parseNonceSource(beginSource, position)) + continue; + + if (parseHashSource(beginSource, position)) + continue; + + 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 (isCSPDirectiveName(host)) + m_policy.reportDirectiveAsSourceExpression(m_directiveName, host); + m_list.append(ContentSecurityPolicySource(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 ContentSecurityPolicySourceList::parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, std::optional<uint16_t>& port, String& path, bool& hostHasWildcard, bool& portHasWildcard) +{ + if (begin == end) + return false; + + if (equalLettersIgnoringASCIICase(begin, end - begin, "'none'")) + return false; + + if (end - begin == 1 && *begin == '*') { + m_allowStar = true; + return true; + } + + if (equalLettersIgnoringASCIICase(begin, end - begin, "'self'")) { + m_allowSelf = true; + return true; + } + + if (equalLettersIgnoringASCIICase(begin, end - begin, "'unsafe-inline'")) { + m_allowInline = true; + return true; + } + + if (equalLettersIgnoringASCIICase(begin, end - begin, "'unsafe-eval'")) { + m_allowEval = true; + return true; + } + + const UChar* position = begin; + const UChar* beginHost = begin; + const UChar* beginPath = end; + const UChar* beginPort = nullptr; + + skipWhile<UChar, isNotColonOrSlash>(position, end); + + if (position == end) { + // host + // ^ + return parseHost(beginHost, position, host, hostHasWildcard); + } + + if (position < end && *position == '/') { + // host/path || host/ || / + // ^ ^ ^ + return parseHost(beginHost, position, host, hostHasWildcard) && parsePath(position, end, path); + } + + 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<UChar>(position, end, ':') + || !skipExactly<UChar>(position, end, '/') + || !skipExactly<UChar>(position, end, '/')) + return false; + if (position == end) + return false; + beginHost = position; + skipWhile<UChar, isNotColonOrSlash>(position, end); + } + + if (position < end && *position == ':') { + // host:port || scheme://host:port + // ^ ^ + beginPort = position; + skipUntil<UChar>(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) + port = std::nullopt; + else { + if (!parsePort(beginPort, beginPath, port, portHasWildcard)) + return false; + } + + if (beginPath != end) { + if (!parsePath(beginPath, end, path)) + return false; + } + + return true; +} + +// ; <scheme> production from RFC 3986 +// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +// +bool ContentSecurityPolicySourceList::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<UChar, isASCIIAlpha>(position, end)) + return false; + + skipWhile<UChar, isSchemeContinuationCharacter>(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 ContentSecurityPolicySourceList::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<UChar>(position, end, '*')) { + hostHasWildcard = true; + + if (position == end) + return true; + + if (!skipExactly<UChar>(position, end, '.')) + return false; + } + + const UChar* hostBegin = position; + + while (position < end) { + if (!skipExactly<UChar, isHostCharacter>(position, end)) + return false; + + skipWhile<UChar, isHostCharacter>(position, end); + + if (position < end && !skipExactly<UChar>(position, end, '.')) + return false; + } + + ASSERT(position == end); + host = String(hostBegin, end - hostBegin); + return true; +} + +bool ContentSecurityPolicySourceList::parsePath(const UChar* begin, const UChar* end, String& path) +{ + ASSERT(begin <= end); + ASSERT(path.isEmpty()); + + const UChar* position = begin; + skipWhile<UChar, isPathComponentCharacter>(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 ContentSecurityPolicySourceList::parsePort(const UChar* begin, const UChar* end, std::optional<uint16_t>& port, bool& portHasWildcard) +{ + ASSERT(begin <= end); + ASSERT(!port); + ASSERT(!portHasWildcard); + + if (!skipExactly<UChar>(begin, end, ':')) + ASSERT_NOT_REACHED(); + + if (begin == end) + return false; + + if (end - begin == 1 && *begin == '*') { + port = std::nullopt; + portHasWildcard = true; + return true; + } + + const UChar* position = begin; + skipWhile<UChar, isASCIIDigit>(position, end); + + if (position != end) + return false; + + bool ok; + int portInt = charactersToIntStrict(begin, end - begin, &ok); + if (portInt < 0 || portInt > std::numeric_limits<uint16_t>::max()) + return false; + port = portInt; + return ok; +} + +static bool isBase64Character(UChar c) +{ + return isASCIIAlphanumeric(c) || c == '+' || c == '/' || c == '-' || c == '_'; +} + +// Match Blink's behavior of allowing an equal sign to appear anywhere in the value of the nonce +// even though this does not match the behavior of Content Security Policy Level 3 spec., +// <https://w3c.github.io/webappsec-csp/> (29 February 2016). +static bool isNonceCharacter(UChar c) +{ + return isBase64Character(c) || c == '='; +} + +// nonce-source = "'nonce-" nonce-value "'" +// nonce-value = base64-value +bool ContentSecurityPolicySourceList::parseNonceSource(const UChar* begin, const UChar* end) +{ + static NeverDestroyed<String> noncePrefix("'nonce-", String::ConstructFromLiteral); + if (!StringView(begin, end - begin).startsWithIgnoringASCIICase(noncePrefix.get())) + return false; + const UChar* position = begin + noncePrefix.get().length(); + const UChar* beginNonceValue = position; + skipWhile<UChar, isNonceCharacter>(position, end); + if (position >= end || position == beginNonceValue || *position != '\'') + return false; + m_nonces.add(String(beginNonceValue, position - beginNonceValue)); + return true; +} + +static bool parseHashAlgorithmAdvancingPosition(const UChar*& position, size_t length, ContentSecurityPolicyHashAlgorithm& algorithm) +{ + static struct { + NeverDestroyed<String> label; + ContentSecurityPolicyHashAlgorithm algorithm; + } labelToHashAlgorithmTable[] { + { ASCIILiteral("sha256"), ContentSecurityPolicyHashAlgorithm::SHA_256 }, + { ASCIILiteral("sha384"), ContentSecurityPolicyHashAlgorithm::SHA_384 }, + { ASCIILiteral("sha512"), ContentSecurityPolicyHashAlgorithm::SHA_512 }, + }; + + StringView stringView(position, length); + for (auto& entry : labelToHashAlgorithmTable) { + String& label = entry.label.get(); + if (!stringView.startsWithIgnoringASCIICase(label)) + continue; + position += label.length(); + algorithm = entry.algorithm; + return true; + } + return false; +} + +// hash-source = "'" hash-algorithm "-" base64-value "'" +// hash-algorithm = "sha256" / "sha384" / "sha512" +// base64-value = 1*( ALPHA / DIGIT / "+" / "/" / "-" / "_" )*2( "=" ) +bool ContentSecurityPolicySourceList::parseHashSource(const UChar* begin, const UChar* end) +{ + if (begin == end) + return false; + + const UChar* position = begin; + if (!skipExactly<UChar>(position, end, '\'')) + return false; + + ContentSecurityPolicyHashAlgorithm algorithm; + if (!parseHashAlgorithmAdvancingPosition(position, end - position, algorithm)) + return false; + + if (!skipExactly<UChar>(position, end, '-')) + return false; + + const UChar* beginHashValue = position; + skipWhile<UChar, isBase64Character>(position, end); + skipExactly<UChar>(position, end, '='); + skipExactly<UChar>(position, end, '='); + if (position >= end || position == beginHashValue || *position != '\'') + return false; + Vector<uint8_t> digest; + StringView hashValue(beginHashValue, position - beginHashValue); // base64url or base64 encoded + // FIXME: Normalize Base64URL to Base64 instead of decoding twice. See <https://bugs.webkit.org/show_bug.cgi?id=155186>. + if (!base64Decode(hashValue.toStringWithoutCopying(), digest, Base64ValidatePadding)) { + if (!base64URLDecode(hashValue.toStringWithoutCopying(), digest)) + return false; + } + if (digest.size() > maximumContentSecurityPolicyDigestLength) + return false; + + m_hashes.add(std::make_pair(algorithm, digest)); + m_hashAlgorithmsUsed |= algorithm; + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h new file mode 100644 index 000000000..04dc29c33 --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceList.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 + +#include "ContentSecurityPolicyHash.h" +#include "ContentSecurityPolicySource.h" +#include <wtf/HashSet.h> +#include <wtf/OptionSet.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class ContentSecurityPolicy; +class URL; + +class ContentSecurityPolicySourceList { +public: + ContentSecurityPolicySourceList(const ContentSecurityPolicy&, const String& directiveName); + + void parse(const String&); + + bool matches(const URL&, bool didReceiveRedirectResponse); + bool matches(const ContentSecurityPolicyHash&) const; + bool matches(const String& nonce) const; + + OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsUsed() const { return m_hashAlgorithmsUsed; } + + bool allowInline() const { return m_allowInline && m_hashes.isEmpty() && m_nonces.isEmpty(); } + bool allowEval() const { return m_allowEval; } + bool allowSelf() const { return m_allowSelf; } + bool isNone() const { return m_isNone; } + +private: + void parse(const UChar* begin, const UChar* end); + + bool parseSource(const UChar* begin, const UChar* end, String& scheme, String& host, std::optional<uint16_t>& 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, std::optional<uint16_t>& port, bool& portHasWildcard); + bool parsePath(const UChar* begin, const UChar* end, String& path); + + bool parseNonceSource(const UChar* begin, const UChar* end); + + bool isProtocolAllowedByStar(const URL&) const; + + bool parseHashSource(const UChar* begin, const UChar* end); + + const ContentSecurityPolicy& m_policy; + Vector<ContentSecurityPolicySource> m_list; + HashSet<String> m_nonces; + HashSet<ContentSecurityPolicyHash> m_hashes; + OptionSet<ContentSecurityPolicyHashAlgorithm> m_hashAlgorithmsUsed; + String m_directiveName; + bool m_allowSelf { false }; + bool m_allowStar { false }; + bool m_allowInline { false }; + bool m_allowEval { false }; + bool m_isNone { false }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp new file mode 100644 index 000000000..c2bf1d45a --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 "ContentSecurityPolicySourceListDirective.h" + +#include "ContentSecurityPolicy.h" +#include "ContentSecurityPolicyDirectiveList.h" +#include "URL.h" + +namespace WebCore { + +ContentSecurityPolicySourceListDirective::ContentSecurityPolicySourceListDirective(const ContentSecurityPolicyDirectiveList& directiveList, const String& name, const String& value) + : ContentSecurityPolicyDirective(directiveList, name, value) + , m_sourceList(directiveList.policy(), name) +{ + m_sourceList.parse(value); +} + +bool ContentSecurityPolicySourceListDirective::allows(const URL& url, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone shouldAllowEmptyURLIfSourceListEmpty) +{ + if (url.isEmpty()) + return shouldAllowEmptyURLIfSourceListEmpty == ShouldAllowEmptyURLIfSourceListIsNotNone::Yes && !m_sourceList.isNone(); + return m_sourceList.matches(url, didReceiveRedirectResponse); +} + +bool ContentSecurityPolicySourceListDirective::allows(const String& nonce) const +{ + return m_sourceList.matches(nonce); +} + +bool ContentSecurityPolicySourceListDirective::allows(const ContentSecurityPolicyHash& hash) const +{ + return m_sourceList.matches(hash); +} + +} // namespace WebCore diff --git a/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h new file mode 100644 index 000000000..afb2ed44f --- /dev/null +++ b/Source/WebCore/page/csp/ContentSecurityPolicySourceListDirective.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 Google, Inc. All rights reserved. + * 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 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 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 + +#include "ContentSecurityPolicyDirective.h" +#include "ContentSecurityPolicySourceList.h" + +namespace WebCore { + +class ContentSecurityPolicyDirectiveList; + +class ContentSecurityPolicySourceListDirective : public ContentSecurityPolicyDirective { +public: + ContentSecurityPolicySourceListDirective(const ContentSecurityPolicyDirectiveList&, const String& name, const String& value); + + enum class ShouldAllowEmptyURLIfSourceListIsNotNone { No, Yes }; + bool allows(const URL&, bool didReceiveRedirectResponse, ShouldAllowEmptyURLIfSourceListIsNotNone); + bool allows(const ContentSecurityPolicyHash&) const; + bool allows(const String& nonce) const; + bool allowInline() const { return m_sourceList.allowInline(); } + bool allowEval() const { return m_sourceList.allowEval(); } + + OptionSet<ContentSecurityPolicyHashAlgorithm> hashAlgorithmsUsed() const { return m_sourceList.hashAlgorithmsUsed(); } + +private: + ContentSecurityPolicySourceList m_sourceList; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/gtk/DragControllerGtk.cpp b/Source/WebCore/page/gtk/DragControllerGtk.cpp index 69606a4ba..0e605d750 100644 --- a/Source/WebCore/page/gtk/DragControllerGtk.cpp +++ b/Source/WebCore/page/gtk/DragControllerGtk.cpp @@ -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,13 +26,14 @@ #include "config.h" #include "DragController.h" -#include "Clipboard.h" +#include "DataTransfer.h" +#include "Document.h" #include "DragData.h" +#include "Editor.h" #include "Element.h" #include "Frame.h" -#include "FrameView.h" -#include "Page.h" #include "Pasteboard.h" +#include "markup.h" namespace WebCore { @@ -45,15 +46,15 @@ const int DragController::DragIconBottomInset = 3; const float DragController::DragImageAlpha = 0.75f; -bool DragController::isCopyKeyDown(DragData&) +bool DragController::isCopyKeyDown(const DragData& dragData) { - return false; + return dragData.flags() & DragApplicationIsCopyKeyDown; } -DragOperation DragController::dragOperation(DragData& dragData) +DragOperation DragController::dragOperation(const DragData& dragData) { // FIXME: This logic is incomplete - if (dragData.containsURL(0)) + if (dragData.containsURL()) return DragOperationCopy; return DragOperationNone; @@ -69,9 +70,17 @@ void DragController::cleanupAfterSystemDrag() { } -void DragController::declareAndWriteDragImage(Clipboard& clipboard, Element& element, const URL& url, const String& label) +void DragController::declareAndWriteDragImage(DataTransfer& dataTransfer, Element& element, const URL& url, const String& label) { - clipboard.pasteboard().writeImage(element, url, label); + Frame* frame = element.document().frame(); + ASSERT(frame); + frame->editor().writeImageToPasteboard(dataTransfer.pasteboard(), element, url, label); } +#if ENABLE(ATTACHMENT_ELEMENT) +void DragController::declareAndWriteAttachment(DataTransfer&, Element&, const URL&) +{ +} +#endif + } diff --git a/Source/WebCore/page/gtk/EventHandlerGtk.cpp b/Source/WebCore/page/gtk/EventHandlerGtk.cpp index e6e45625c..5f09e975c 100644 --- a/Source/WebCore/page/gtk/EventHandlerGtk.cpp +++ b/Source/WebCore/page/gtk/EventHandlerGtk.cpp @@ -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,7 +26,7 @@ #include "config.h" #include "EventHandler.h" -#include "Clipboard.h" +#include "DataTransfer.h" #include "FloatPoint.h" #include "FocusController.h" #include "Frame.h" @@ -46,7 +46,7 @@ namespace WebCore { const double EventHandler::TextDragDelay = 0.0; #endif -bool EventHandler::tabsToAllFormControls(KeyboardEvent* event) const +bool EventHandler::tabsToAllFormControls(KeyboardEvent&) const { // We always allow tabs to all controls return true; @@ -61,10 +61,10 @@ void EventHandler::focusDocumentView() bool EventHandler::passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults& event) { // Figure out which view to send the event to. - RenderObject* target = event.targetNode() ? event.targetNode()->renderer() : 0; - if (!target || !target->isWidget()) + RenderObject* target = event.targetNode() ? event.targetNode()->renderer() : nullptr; + if (!is<RenderWidget>(target)) return false; - return passMouseDownEventToWidget(toRenderWidget(target)->widget()); + return passMouseDownEventToWidget(downcast<RenderWidget>(*target).widget()); } bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget) @@ -72,7 +72,7 @@ bool EventHandler::passWidgetMouseDownEventToWidget(RenderWidget* renderWidget) return passMouseDownEventToWidget(renderWidget->widget()); } -bool EventHandler::passMouseDownEventToWidget(Widget* widget) +bool EventHandler::passMouseDownEventToWidget(Widget*) { notImplemented(); return false; @@ -87,20 +87,21 @@ bool EventHandler::eventActivatedView(const PlatformMouseEvent&) const return false; } -bool EventHandler::passWheelEventToWidget(const PlatformWheelEvent& event, Widget* widget) +bool EventHandler::widgetDidHandleWheelEvent(const PlatformWheelEvent& event, Widget& widget) { - ASSERT(widget); - if (!widget->isFrameView()) + if (!is<FrameView>(widget)) return false; - return toFrameView(widget)->frame().eventHandler().handleWheelEvent(event); + return downcast<FrameView>(widget).frame().eventHandler().handleWheelEvent(event); } #if ENABLE(DRAG_SUPPORT) -PassRefPtr<Clipboard> EventHandler::createDraggingClipboard() const + +Ref<DataTransfer> EventHandler::createDraggingDataTransfer() const { - return Clipboard::createForDragAndDrop(); + return DataTransfer::createForDrag(); } + #endif bool EventHandler::passMousePressEventToSubframe(MouseEventWithHitTestResults& mev, Frame* subframe) @@ -121,18 +122,22 @@ bool EventHandler::passMouseReleaseEventToSubframe(MouseEventWithHitTestResults& return true; } -unsigned EventHandler::accessKeyModifiers() +OptionSet<PlatformEvent::Modifier> EventHandler::accessKeyModifiers() { - return PlatformEvent::AltKey; + return PlatformEvent::Modifier::AltKey; } // GTK+ must scroll horizontally if the mouse pointer is on top of the // horizontal scrollbar while scrolling with the wheel; we need to // add the deltas and ticks here so that this behavior is consistent // for styled scrollbars. -bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult& result, const PlatformWheelEvent&) const +bool EventHandler::shouldTurnVerticalTicksIntoHorizontal(const HitTestResult& result, const PlatformWheelEvent& event) const { - return result.scrollbar() && result.scrollbar()->orientation() == HorizontalScrollbar; + FrameView* view = m_frame.view(); + Scrollbar* scrollbar = view ? view->scrollbarAtPoint(event.position()) : nullptr; + if (!scrollbar) + scrollbar = result.scrollbar(); + return scrollbar && scrollbar->orientation() == HorizontalScrollbar; } } diff --git a/Source/WebCore/page/linux/ResourceUsageOverlayLinux.cpp b/Source/WebCore/page/linux/ResourceUsageOverlayLinux.cpp new file mode 100644 index 000000000..cff63e00e --- /dev/null +++ b/Source/WebCore/page/linux/ResourceUsageOverlayLinux.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * 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 "ResourceUsageOverlay.h" + +#if ENABLE(RESOURCE_USAGE) && OS(LINUX) + +#include "Chrome.h" +#include "ChromeClient.h" +#include "CommonVM.h" +#include "GraphicsContext.h" +#include "Page.h" +#include "RenderTheme.h" +#include "ResourceUsageThread.h" +#include <runtime/VM.h> + +namespace WebCore { + +static ResourceUsageData gData; + +static String cpuUsageString(float cpuUsage) +{ + if (cpuUsage < 0) + return ASCIILiteral("<unknown>"); + return String::format("%.1f%%", cpuUsage); +} + +static String formatByteNumber(size_t number) +{ + if (number >= 1024 * 1048576) + return String::format("%.3f GB", static_cast<double>(number) / (1024 * 1048576)); + if (number >= 1048576) + return String::format("%.2f MB", static_cast<double>(number) / 1048576); + if (number >= 1024) + return String::format("%.1f kB", static_cast<double>(number) / 1024); + return String::format("%lu", number); +} + +static String gcTimerString(double timerFireDate, double now) +{ + if (!timerFireDate) + return ASCIILiteral("[not scheduled]"); + return String::format("%g", timerFireDate - now); +} + +static const float gFontSize = 14; + +class ResourceUsageOverlayPainter final : public GraphicsLayerClient { +public: + ResourceUsageOverlayPainter(ResourceUsageOverlay& overlay) + : m_overlay(overlay) + { + FontCascadeDescription fontDescription; + RenderTheme::defaultTheme()->systemFont(CSSValueMessageBox, fontDescription); + fontDescription.setComputedSize(gFontSize); + m_textFont = FontCascade(fontDescription, 0, 0); + m_textFont.update(nullptr); + } + + ~ResourceUsageOverlayPainter() + { + } + +private: + void paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const FloatRect& clip) override + { + GraphicsContextStateSaver stateSaver(context); + context.fillRect(clip, Color(0.0f, 0.0f, 0.0f, 0.8f)); + context.setFillColor(Color(0.9f, 0.9f, 0.9f, 1.f)); + + FloatPoint position = { 10, 20 }; + String string = "CPU: " + cpuUsageString(gData.cpu); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + + string = "Memory: " + formatByteNumber(gData.totalDirtySize); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + + string = "External: " + formatByteNumber(gData.totalExternalSize); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + + string = "GC Heap: " + formatByteNumber(gData.categories[MemoryCategory::GCHeap].dirtySize); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + + string = "GC owned: " + formatByteNumber(gData.categories[MemoryCategory::GCOwned].dirtySize); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + + double now = WTF::currentTime(); + string = "Eden GC: " + gcTimerString(gData.timeOfNextEdenCollection, now); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + + string = "Full GC: " + gcTimerString(gData.timeOfNextFullCollection, now); + context.drawText(m_textFont, TextRun(string), position); + position.move(0, gFontSize + 2); + } + + void notifyFlushRequired(const GraphicsLayer*) override + { + m_overlay.overlay().page()->chrome().client().scheduleCompositingLayerFlush(); + } + + ResourceUsageOverlay& m_overlay; + FontCascade m_textFont; +}; + +void ResourceUsageOverlay::platformInitialize() +{ + m_overlayPainter = std::make_unique<ResourceUsageOverlayPainter>(*this); + m_paintLayer = GraphicsLayer::create(overlay().page()->chrome().client().graphicsLayerFactory(), *m_overlayPainter); + m_paintLayer->setAnchorPoint(FloatPoint3D()); + m_paintLayer->setSize({ normalWidth, normalHeight }); + m_paintLayer->setBackgroundColor(Color(0.0f, 0.0f, 0.0f, 0.8f)); + m_paintLayer->setDrawsContent(true); + overlay().layer().addChild(m_paintLayer.get()); + + ResourceUsageThread::addObserver(this, [this] (const ResourceUsageData& data) { + gData = data; + m_paintLayer->setNeedsDisplay(); + }); +} + +void ResourceUsageOverlay::platformDestroy() +{ + ResourceUsageThread::removeObserver(this); + if (!m_paintLayer) + return; + + m_paintLayer->removeFromParent(); + m_paintLayer = nullptr; + m_overlayPainter = nullptr; +} + +} // namespace WebCore + +#endif // ENABLE(RESOURCE_USAGE) && OS(LINUX) diff --git a/Source/WebCore/page/linux/ResourceUsageThreadLinux.cpp b/Source/WebCore/page/linux/ResourceUsageThreadLinux.cpp new file mode 100644 index 000000000..1d19497fb --- /dev/null +++ b/Source/WebCore/page/linux/ResourceUsageThreadLinux.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * 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 "ResourceUsageThread.h" + +#if ENABLE(RESOURCE_USAGE) && OS(LINUX) + +#include "CurrentProcessMemoryStatus.h" +#include <errno.h> +#include <fcntl.h> +#include <heap/GCActivityCallback.h> +#include <runtime/VM.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace WebCore { + +static float cpuPeriod() +{ + FILE* file = fopen("/proc/stat", "r"); + if (!file) + return 0; + + static const unsigned statMaxLineLength = 512; + char buffer[statMaxLineLength + 1]; + char* line = fgets(buffer, statMaxLineLength, file); + if (!line) { + fclose(file); + return 0; + } + + unsigned long long userTime, niceTime, systemTime, idleTime; + unsigned long long ioWait, irq, softIrq, steal, guest, guestnice; + ioWait = irq = softIrq = steal = guest = guestnice = 0; + sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", + &userTime, &niceTime, &systemTime, &idleTime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice); + + // Keep parsing if we still don't know cpuCount. + static unsigned cpuCount = 0; + if (!cpuCount) { + while ((line = fgets(buffer, statMaxLineLength, file))) { + if (strlen(line) > 4 && line[0] == 'c' && line[1] == 'p' && line[2] == 'u') + cpuCount++; + else + break; + } + } + fclose(file); + + if (!cpuCount) + return 0; + + static unsigned long long previousTotalTime = 0; + unsigned long long totalTime = userTime + niceTime + systemTime + irq + softIrq + idleTime + ioWait + steal; + unsigned long long period = totalTime > previousTotalTime ? totalTime - previousTotalTime : 0; + previousTotalTime = totalTime; + return static_cast<float>(period) / cpuCount; +} + +static float cpuUsage() +{ + float period = cpuPeriod(); + if (!period) + return -1; + + int fd = open("/proc/self/stat", O_RDONLY); + if (fd < 0) + return -1; + + static const ssize_t maxBufferLength = BUFSIZ - 1; + char buffer[BUFSIZ]; + buffer[0] = '\0'; + + ssize_t totalBytesRead = 0; + while (totalBytesRead < maxBufferLength) { + ssize_t bytesRead = read(fd, buffer + totalBytesRead, maxBufferLength - totalBytesRead); + if (bytesRead < 0) { + if (errno != EINTR) { + close(fd); + return -1; + } + continue; + } + + if (!bytesRead) + break; + + totalBytesRead += bytesRead; + } + close(fd); + buffer[totalBytesRead] = '\0'; + + // Skip pid and process name. + char* position = strrchr(buffer, ')'); + if (!position) + return -1; + + // Move after state. + position += 4; + + // Skip ppid, pgrp, sid, tty_nr, tty_pgrp, flags, min_flt, cmin_flt, maj_flt, cmaj_flt. + unsigned tokensToSkip = 10; + while (tokensToSkip--) { + while (!isASCIISpace(position[0])) + position++; + position++; + } + + static unsigned long long previousUtime = 0; + static unsigned long long previousStime = 0; + unsigned long long utime = strtoull(position, &position, 10); + unsigned long long stime = strtoull(position, &position, 10); + float usage = (utime + stime - (previousUtime + previousStime)) / period * 100.0; + previousUtime = utime; + previousStime = stime; + + return clampTo<float>(usage, 0, 100); +} + +void ResourceUsageThread::platformThreadBody(JSC::VM* vm, ResourceUsageData& data) +{ + data.cpu = cpuUsage(); + + ProcessMemoryStatus memoryStatus; + currentProcessMemoryStatus(memoryStatus); + data.totalDirtySize = memoryStatus.resident - memoryStatus.shared; + + size_t currentGCHeapCapacity = vm->heap.blockBytesAllocated(); + size_t currentGCOwnedExtra = vm->heap.extraMemorySize(); + size_t currentGCOwnedExternal = vm->heap.externalMemorySize(); + RELEASE_ASSERT(currentGCOwnedExternal <= currentGCOwnedExtra); + + data.categories[MemoryCategory::GCHeap].dirtySize = currentGCHeapCapacity; + data.categories[MemoryCategory::GCOwned].dirtySize = currentGCOwnedExtra - currentGCOwnedExternal; + data.categories[MemoryCategory::GCOwned].externalSize = currentGCOwnedExternal; + + data.totalExternalSize = currentGCOwnedExternal; + + data.timeOfNextEdenCollection = vm->heap.edenActivityCallback()->nextFireTime(); + data.timeOfNextFullCollection = vm->heap.fullActivityCallback()->nextFireTime(); +} + +} // namespace WebCore + +#endif // ENABLE(RESOURCE_USAGE) && OS(LINUX) diff --git a/Source/WebCore/page/make_settings.pl b/Source/WebCore/page/make_settings.pl index b01c1f7b3..34c279f81 100755 --- a/Source/WebCore/page/make_settings.pl +++ b/Source/WebCore/page/make_settings.pl @@ -14,7 +14,7 @@ # 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 +# 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,6 +24,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; +use FindBin; +use lib "$FindBin::Bin/../bindings/scripts"; use InFilesCompiler; @@ -239,12 +241,18 @@ sub printGetterAndSetter($$$$) { my ($file, $settingName, $type, $setNeedsStyleRecalcInAllFrames) = @_; my $setterFunctionName = setterFunctionName($settingName); + + my $webcoreExport = ""; + if ($setNeedsStyleRecalcInAllFrames) { + $webcoreExport = "WEBCORE_EXPORT"; # Export is only needed if the definition is not in the header. + } + if (lc(substr($type, 0, 1)) eq substr($type, 0, 1)) { print $file " $type $settingName() const { return m_$settingName; } \\\n"; - print $file " void $setterFunctionName($type $settingName)"; + print $file " $webcoreExport void $setterFunctionName($type $settingName)"; } else { - print $file " const $type& $settingName() { return m_$settingName; } \\\n"; - print $file " void $setterFunctionName(const $type& $settingName)"; + print $file " const $type& $settingName() const { return m_$settingName; } \\\n"; + print $file " $webcoreExport void $setterFunctionName(const $type& $settingName)"; } if ($setNeedsStyleRecalcInAllFrames) { print $file "; \\\n"; @@ -353,6 +361,7 @@ sub generateInternalSettingsIdlFile($) print $file "[\n"; print $file " NoInterfaceObject,\n"; + print $file " ExportMacro=WEBCORE_TESTSUPPORT_EXPORT,\n"; print $file "] interface InternalSettingsGenerated {\n"; sub writeIdlSetter($$$) { @@ -361,7 +370,7 @@ sub generateInternalSettingsIdlFile($) my $type = $parsedItems{$settingName}{"type"}; my $idlType = $webcoreTypeToIdlType{$type}; my $setterFunctionName = setterFunctionName($settingName); - print $file " void $setterFunctionName(in $idlType $settingName);\n"; + print $file " void $setterFunctionName($idlType $settingName);\n"; }; enumerateParsedItems($file, $parsedItemsRef, \&writeIdlSetter); @@ -380,11 +389,9 @@ sub generateInternalSettingsHeaderFile($) print $file $InCompiler->license(); print $file <<EOF; -#ifndef InternalSettingsGenerated_h -#define InternalSettingsGenerated_h +#pragma once -#include "RefCountedSupplement.h" -#include <wtf/PassRefPtr.h> +#include "Supplementable.h" #include <wtf/RefCounted.h> #include <wtf/text/WTFString.h> @@ -432,7 +439,6 @@ EOF print $file "};\n\n"; print $file "} // namespace WebCore\n"; - print $file "#endif // InternalSettingsGenerated_h\n"; close $file; } diff --git a/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp b/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp new file mode 100644 index 000000000..fce95a0a3 --- /dev/null +++ b/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.cpp @@ -0,0 +1,707 @@ +/* + * Copyright (C) 2014-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" + +#if ENABLE(ASYNC_SCROLLING) +#include "AsyncScrollingCoordinator.h" + +#include "Document.h" +#include "EditorClient.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsLayer.h" +#include "Logging.h" +#include "MainFrame.h" +#include "Page.h" +#include "ScrollAnimator.h" +#include "ScrollingConstraints.h" +#include "ScrollingStateFixedNode.h" +#include "ScrollingStateFrameScrollingNode.h" +#include "ScrollingStateOverflowScrollingNode.h" +#include "ScrollingStateStickyNode.h" +#include "ScrollingStateTree.h" +#include "Settings.h" +#include "TextStream.h" +#include "WheelEventTestTrigger.h" + +namespace WebCore { + +AsyncScrollingCoordinator::AsyncScrollingCoordinator(Page* page) + : ScrollingCoordinator(page) + , m_updateNodeScrollPositionTimer(*this, &AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired) + , m_scrollingStateTree(std::make_unique<ScrollingStateTree>(this)) +{ +} + +AsyncScrollingCoordinator::~AsyncScrollingCoordinator() +{ +} + +void AsyncScrollingCoordinator::scrollingStateTreePropertiesChanged() +{ + scheduleTreeStateCommit(); +} + +static inline void setStateScrollingNodeSnapOffsetsAsFloat(ScrollingStateScrollingNode& node, ScrollEventAxis axis, const Vector<LayoutUnit>* snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>* snapOffsetRanges, float deviceScaleFactor) +{ + // FIXME: Incorporate current page scale factor in snapping to device pixel. Perhaps we should just convert to float here and let UI process do the pixel snapping? + Vector<float> snapOffsetsAsFloat; + if (snapOffsets) { + snapOffsetsAsFloat.reserveInitialCapacity(snapOffsets->size()); + for (auto& offset : *snapOffsets) + snapOffsetsAsFloat.uncheckedAppend(roundToDevicePixel(offset, deviceScaleFactor, false)); + } + + Vector<ScrollOffsetRange<float>> snapOffsetRangesAsFloat; + if (snapOffsetRanges) { + snapOffsetRangesAsFloat.reserveInitialCapacity(snapOffsetRanges->size()); + for (auto& range : *snapOffsetRanges) + snapOffsetRangesAsFloat.uncheckedAppend({ roundToDevicePixel(range.start, deviceScaleFactor, false), roundToDevicePixel(range.end, deviceScaleFactor, false) }); + } + if (axis == ScrollEventAxis::Horizontal) { + node.setHorizontalSnapOffsets(snapOffsetsAsFloat); + node.setHorizontalSnapOffsetRanges(snapOffsetRangesAsFloat); + } else { + node.setVerticalSnapOffsets(snapOffsetsAsFloat); + node.setVerticalSnapOffsetRanges(snapOffsetRangesAsFloat); + } +} + +void AsyncScrollingCoordinator::setEventTrackingRegionsDirty() +{ + m_eventTrackingRegionsDirty = true; + // We have to schedule a commit, but the computed non-fast region may not have actually changed. + scheduleTreeStateCommit(); +} + +void AsyncScrollingCoordinator::willCommitTree() +{ + updateEventTrackingRegions(); +} + +void AsyncScrollingCoordinator::updateEventTrackingRegions() +{ + if (!m_eventTrackingRegionsDirty) + return; + + if (!m_scrollingStateTree->rootStateNode()) + return; + + m_scrollingStateTree->rootStateNode()->setEventTrackingRegions(absoluteEventTrackingRegions()); + m_eventTrackingRegionsDirty = false; +} + +void AsyncScrollingCoordinator::frameViewLayoutUpdated(FrameView& frameView) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + // If there isn't a root node yet, don't do anything. We'll be called again after creating one. + if (!m_scrollingStateTree->rootStateNode()) + return; + + // Compute the region of the page that we can't do fast scrolling for. This currently includes + // all scrollable areas, such as subframes, overflow divs and list boxes. We need to do this even if the + // frame view whose layout was updated is not the main frame. + // In the future, we may want to have the ability to set non-fast scrolling regions for more than + // just the root node. But right now, this concept only applies to the root. + m_scrollingStateTree->rootStateNode()->setEventTrackingRegions(absoluteEventTrackingRegions()); + m_eventTrackingRegionsDirty = false; + + if (!coordinatesScrollingForFrameView(frameView)) + return; + + ScrollingStateFrameScrollingNode* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID())); + if (!node) + return; + + Scrollbar* verticalScrollbar = frameView.verticalScrollbar(); + Scrollbar* horizontalScrollbar = frameView.horizontalScrollbar(); + node->setScrollerImpsFromScrollbars(verticalScrollbar, horizontalScrollbar); + + node->setFrameScaleFactor(frameView.frame().frameScaleFactor()); + node->setHeaderHeight(frameView.headerHeight()); + node->setFooterHeight(frameView.footerHeight()); + node->setTopContentInset(frameView.topContentInset()); + + node->setVisualViewportEnabled(visualViewportEnabled()); + node->setLayoutViewport(frameView.layoutViewportRect()); + node->setMinLayoutViewportOrigin(frameView.minStableLayoutViewportOrigin()); + node->setMaxLayoutViewportOrigin(frameView.maxStableLayoutViewportOrigin()); + + node->setScrollOrigin(frameView.scrollOrigin()); + node->setScrollableAreaSize(frameView.visibleContentRect().size()); + node->setTotalContentsSize(frameView.totalContentsSize()); + node->setReachableContentsSize(frameView.totalContentsSize()); + node->setFixedElementsLayoutRelativeToFrame(frameView.fixedElementsLayoutRelativeToFrame()); + node->setScrollBehaviorForFixedElements(frameView.scrollBehaviorForFixedElements()); + +#if ENABLE(CSS_SCROLL_SNAP) + frameView.updateSnapOffsets(); + updateScrollSnapPropertiesWithFrameView(frameView); +#endif + +#if PLATFORM(COCOA) + Page* page = frameView.frame().page(); + if (page && page->expectsWheelEventTriggers()) { + LOG(WheelEventTestTriggers, " AsyncScrollingCoordinator::frameViewLayoutUpdated: Expects wheel event test trigger=%d", page->expectsWheelEventTriggers()); + node->setExpectsWheelEventTestTrigger(page->expectsWheelEventTriggers()); + } +#endif + + ScrollableAreaParameters scrollParameters; + scrollParameters.horizontalScrollElasticity = frameView.horizontalScrollElasticity(); + scrollParameters.verticalScrollElasticity = frameView.verticalScrollElasticity(); + scrollParameters.hasEnabledHorizontalScrollbar = horizontalScrollbar && horizontalScrollbar->enabled(); + scrollParameters.hasEnabledVerticalScrollbar = verticalScrollbar && verticalScrollbar->enabled(); + scrollParameters.horizontalScrollbarMode = frameView.horizontalScrollbarMode(); + scrollParameters.verticalScrollbarMode = frameView.verticalScrollbarMode(); + + node->setScrollableAreaParameters(scrollParameters); +} + +void AsyncScrollingCoordinator::updateExpectsWheelEventTestTriggerWithFrameView(const FrameView& frameView) +{ + auto* page = frameView.frame().page(); + if (!page) + return; + + auto* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID())); + if (!node) + return; + + node->setExpectsWheelEventTestTrigger(page->expectsWheelEventTriggers()); +} + +void AsyncScrollingCoordinator::frameViewEventTrackingRegionsChanged(FrameView&) +{ + if (!m_scrollingStateTree->rootStateNode()) + return; + + setEventTrackingRegionsDirty(); +} + +void AsyncScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (!coordinatesScrollingForFrameView(frameView)) + return; + + // FIXME: In some navigation scenarios, the FrameView has no RenderView or that RenderView has not been composited. + // This needs cleaning up: https://bugs.webkit.org/show_bug.cgi?id=132724 + if (!frameView.scrollLayerID()) + return; + + // If the root layer does not have a ScrollingStateNode, then we should create one. + ensureRootStateNodeForFrameView(frameView); + ASSERT(m_scrollingStateTree->rootStateNode()); + + ScrollingCoordinator::frameViewRootLayerDidChange(frameView); + + ScrollingStateFrameScrollingNode* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID())); + node->setLayer(scrollLayerForFrameView(frameView)); + node->setScrolledContentsLayer(rootContentLayerForFrameView(frameView)); + node->setCounterScrollingLayer(counterScrollingLayerForFrameView(frameView)); + node->setInsetClipLayer(insetClipLayerForFrameView(frameView)); + node->setContentShadowLayer(contentShadowLayerForFrameView(frameView)); + node->setHeaderLayer(headerLayerForFrameView(frameView)); + node->setFooterLayer(footerLayerForFrameView(frameView)); + node->setScrollBehaviorForFixedElements(frameView.scrollBehaviorForFixedElements()); +} + +bool AsyncScrollingCoordinator::requestScrollPositionUpdate(FrameView& frameView, const IntPoint& scrollPosition) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (!coordinatesScrollingForFrameView(frameView)) + return false; + + bool isProgrammaticScroll = frameView.inProgrammaticScroll(); + if (isProgrammaticScroll || frameView.frame().document()->pageCacheState() != Document::NotInPageCache) + updateScrollPositionAfterAsyncScroll(frameView.scrollLayerID(), scrollPosition, std::nullopt, isProgrammaticScroll, ScrollingLayerPositionAction::Set); + + // If this frame view's document is being put into the page cache, we don't want to update our + // main frame scroll position. Just let the FrameView think that we did. + if (frameView.frame().document()->pageCacheState() != Document::NotInPageCache) + return true; + + ScrollingStateScrollingNode* stateNode = downcast<ScrollingStateScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID())); + if (!stateNode) + return false; + + stateNode->setRequestedScrollPosition(scrollPosition, isProgrammaticScroll); + return true; +} + +void AsyncScrollingCoordinator::scheduleUpdateScrollPositionAfterAsyncScroll(ScrollingNodeID nodeID, const FloatPoint& scrollPosition, const std::optional<FloatPoint>& layoutViewportOrigin, bool programmaticScroll, ScrollingLayerPositionAction scrollingLayerPositionAction) +{ + ScheduledScrollUpdate scrollUpdate(nodeID, scrollPosition, layoutViewportOrigin, programmaticScroll, scrollingLayerPositionAction); + + // For programmatic scrolls, requestScrollPositionUpdate() has already called updateScrollPositionAfterAsyncScroll(). + if (programmaticScroll) + return; + + if (m_updateNodeScrollPositionTimer.isActive()) { + if (m_scheduledScrollUpdate.matchesUpdateType(scrollUpdate)) { + m_scheduledScrollUpdate.scrollPosition = scrollPosition; + m_scheduledScrollUpdate.layoutViewportOrigin = layoutViewportOrigin; + return; + } + + // If the parameters don't match what was previously scheduled, dispatch immediately. + m_updateNodeScrollPositionTimer.stop(); + updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.layoutViewportOrigin, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction); + updateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, layoutViewportOrigin, programmaticScroll, scrollingLayerPositionAction); + return; + } + + m_scheduledScrollUpdate = scrollUpdate; + m_updateNodeScrollPositionTimer.startOneShot(0); +} + +void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScrollTimerFired() +{ + updateScrollPositionAfterAsyncScroll(m_scheduledScrollUpdate.nodeID, m_scheduledScrollUpdate.scrollPosition, m_scheduledScrollUpdate.layoutViewportOrigin, m_scheduledScrollUpdate.isProgrammaticScroll, m_scheduledScrollUpdate.updateLayerPositionAction); +} + +FrameView* AsyncScrollingCoordinator::frameViewForScrollingNode(ScrollingNodeID scrollingNodeID) const +{ + if (!m_scrollingStateTree->rootStateNode()) + return nullptr; + + if (scrollingNodeID == m_scrollingStateTree->rootStateNode()->scrollingNodeID()) + return m_page->mainFrame().view(); + + ScrollingStateNode* stateNode = m_scrollingStateTree->stateNodeForID(scrollingNodeID); + if (!stateNode) + return nullptr; + + // Find the enclosing frame scrolling node. + ScrollingStateNode* parentNode = stateNode; + while (parentNode && parentNode->nodeType() != FrameScrollingNode) + parentNode = parentNode->parent(); + + if (!parentNode) + return nullptr; + + // Walk the frame tree to find the matching FrameView. This is not ideal, but avoids back pointers to FrameViews + // from ScrollingTreeStateNodes. + for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) { + if (FrameView* view = frame->view()) { + if (view->scrollLayerID() == parentNode->scrollingNodeID()) + return view; + } + } + + return nullptr; +} + +void AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll(ScrollingNodeID scrollingNodeID, const FloatPoint& scrollPosition, std::optional<FloatPoint> layoutViewportOrigin, bool programmaticScroll, ScrollingLayerPositionAction scrollingLayerPositionAction) +{ + ASSERT(isMainThread()); + + if (!m_page) + return; + + FrameView* frameViewPtr = frameViewForScrollingNode(scrollingNodeID); + if (!frameViewPtr) + return; + + LOG_WITH_STREAM(Scrolling, stream << "AsyncScrollingCoordinator::updateScrollPositionAfterAsyncScroll node " << scrollingNodeID << " scrollPosition " << scrollPosition << " action " << scrollingLayerPositionAction); + + FrameView& frameView = *frameViewPtr; + + if (scrollingNodeID == frameView.scrollLayerID()) { + reconcileScrollingState(frameView, scrollPosition, layoutViewportOrigin, programmaticScroll, true, scrollingLayerPositionAction); + +#if PLATFORM(COCOA) + if (m_page->expectsWheelEventTriggers()) { + frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger()); + if (const auto& trigger = m_page->testTrigger()) + trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID), WheelEventTestTrigger::ScrollingThreadSyncNeeded); + } +#endif + + return; + } + + // Overflow-scroll area. + if (ScrollableArea* scrollableArea = frameView.scrollableAreaForScrollLayerID(scrollingNodeID)) { + scrollableArea->setIsUserScroll(scrollingLayerPositionAction == ScrollingLayerPositionAction::Sync); + scrollableArea->scrollToOffsetWithoutAnimation(scrollPosition); + scrollableArea->setIsUserScroll(false); + if (scrollingLayerPositionAction == ScrollingLayerPositionAction::Set) + m_page->editorClient().overflowScrollPositionChanged(); + +#if PLATFORM(COCOA) + if (m_page->expectsWheelEventTriggers()) { + frameView.scrollAnimator().setWheelEventTestTrigger(m_page->testTrigger()); + if (const auto& trigger = m_page->testTrigger()) + trigger->removeTestDeferralForReason(reinterpret_cast<WheelEventTestTrigger::ScrollableAreaIdentifier>(scrollingNodeID), WheelEventTestTrigger::ScrollingThreadSyncNeeded); + } +#endif + } +} + +void AsyncScrollingCoordinator::reconcileScrollingState(FrameView& frameView, const FloatPoint& scrollPosition, const LayoutViewportOriginOrOverrideRect& layoutViewportOriginOrOverrideRect, bool programmaticScroll, bool inStableState, ScrollingLayerPositionAction scrollingLayerPositionAction) +{ + bool oldProgrammaticScroll = frameView.inProgrammaticScroll(); + frameView.setInProgrammaticScroll(programmaticScroll); + + std::optional<FloatRect> layoutViewportRect; + + WTF::switchOn(layoutViewportOriginOrOverrideRect, + [&frameView](std::optional<FloatPoint> origin) { + if (origin) + frameView.setBaseLayoutViewportOrigin(LayoutPoint(origin.value()), FrameView::TriggerLayoutOrNot::No); + }, [&frameView, &layoutViewportRect, inStableState, visualViewportEnabled = visualViewportEnabled()](std::optional<FloatRect> overrideRect) { + layoutViewportRect = overrideRect; + if (overrideRect && inStableState) { + if (visualViewportEnabled) + frameView.setLayoutViewportOverrideRect(LayoutRect(overrideRect.value())); +#if PLATFORM(IOS) + else + frameView.setCustomFixedPositionLayoutRect(enclosingIntRect(overrideRect.value())); +#endif + } + } + ); + + frameView.setConstrainsScrollingToContentEdge(false); + frameView.notifyScrollPositionChanged(roundedIntPoint(scrollPosition)); + frameView.setConstrainsScrollingToContentEdge(true); + frameView.setInProgrammaticScroll(oldProgrammaticScroll); + + if (!programmaticScroll && scrollingLayerPositionAction != ScrollingLayerPositionAction::Set) { + if (inStableState) + reconcileViewportConstrainedLayerPositions(frameView.rectForFixedPositionLayout(), scrollingLayerPositionAction); + else if (layoutViewportRect) + reconcileViewportConstrainedLayerPositions(LayoutRect(layoutViewportRect.value()), scrollingLayerPositionAction); + } + + GraphicsLayer* scrollLayer = scrollLayerForFrameView(frameView); + if (!scrollLayer) + return; + + GraphicsLayer* counterScrollingLayer = counterScrollingLayerForFrameView(frameView); + GraphicsLayer* insetClipLayer = insetClipLayerForFrameView(frameView); + GraphicsLayer* contentShadowLayer = contentShadowLayerForFrameView(frameView); + GraphicsLayer* scrolledContentsLayer = rootContentLayerForFrameView(frameView); + GraphicsLayer* headerLayer = headerLayerForFrameView(frameView); + GraphicsLayer* footerLayer = footerLayerForFrameView(frameView); + + ASSERT(frameView.scrollPosition() == roundedIntPoint(scrollPosition)); + LayoutPoint scrollPositionForFixed = frameView.scrollPositionForFixedPosition(); + float topContentInset = frameView.topContentInset(); + + FloatPoint positionForInsetClipLayer; + if (insetClipLayer) + positionForInsetClipLayer = FloatPoint(insetClipLayer->position().x(), FrameView::yPositionForInsetClipLayer(scrollPosition, topContentInset)); + FloatPoint positionForContentsLayer = frameView.positionForRootContentLayer(); + + FloatPoint positionForHeaderLayer = FloatPoint(scrollPositionForFixed.x(), FrameView::yPositionForHeaderLayer(scrollPosition, topContentInset)); + FloatPoint positionForFooterLayer = FloatPoint(scrollPositionForFixed.x(), + FrameView::yPositionForFooterLayer(scrollPosition, topContentInset, frameView.totalContentsSize().height(), frameView.footerHeight())); + + if (programmaticScroll || scrollingLayerPositionAction == ScrollingLayerPositionAction::Set) { + scrollLayer->setPosition(-frameView.scrollPosition()); + if (counterScrollingLayer) + counterScrollingLayer->setPosition(scrollPositionForFixed); + if (insetClipLayer) + insetClipLayer->setPosition(positionForInsetClipLayer); + if (contentShadowLayer) + contentShadowLayer->setPosition(positionForContentsLayer); + if (scrolledContentsLayer) + scrolledContentsLayer->setPosition(positionForContentsLayer); + if (headerLayer) + headerLayer->setPosition(positionForHeaderLayer); + if (footerLayer) + footerLayer->setPosition(positionForFooterLayer); + } else { + scrollLayer->syncPosition(-frameView.scrollPosition()); + if (counterScrollingLayer) + counterScrollingLayer->syncPosition(scrollPositionForFixed); + if (insetClipLayer) + insetClipLayer->syncPosition(positionForInsetClipLayer); + if (contentShadowLayer) + contentShadowLayer->syncPosition(positionForContentsLayer); + if (scrolledContentsLayer) + scrolledContentsLayer->syncPosition(positionForContentsLayer); + if (headerLayer) + headerLayer->syncPosition(positionForHeaderLayer); + if (footerLayer) + footerLayer->syncPosition(positionForFooterLayer); + } +} + +void AsyncScrollingCoordinator::scrollableAreaScrollbarLayerDidChange(ScrollableArea& scrollableArea, ScrollbarOrientation orientation) +{ + ASSERT(isMainThread()); + ASSERT(m_page); + + if (&scrollableArea != static_cast<ScrollableArea*>(m_page->mainFrame().view())) + return; + + if (orientation == VerticalScrollbar) + scrollableArea.verticalScrollbarLayerDidChange(); + else + scrollableArea.horizontalScrollbarLayerDidChange(); +} + +ScrollingNodeID AsyncScrollingCoordinator::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID) +{ + return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID); +} + +void AsyncScrollingCoordinator::detachFromStateTree(ScrollingNodeID nodeID) +{ + m_scrollingStateTree->detachNode(nodeID); +} + +void AsyncScrollingCoordinator::clearStateTree() +{ + m_scrollingStateTree->clear(); +} + +void AsyncScrollingCoordinator::reconcileViewportConstrainedLayerPositions(const LayoutRect& viewportRect, ScrollingLayerPositionAction action) +{ + if (!m_scrollingStateTree->rootStateNode()) + return; + + auto children = m_scrollingStateTree->rootStateNode()->children(); + if (!children) + return; + + LOG_WITH_STREAM(Scrolling, stream << "AsyncScrollingCoordinator::reconcileViewportConstrainedLayerPositions for viewport rect " << viewportRect); + + // FIXME: We'll have to traverse deeper into the tree at some point. + for (auto& child : *children) + child->reconcileLayerPositionForViewportRect(viewportRect, action); +} + +void AsyncScrollingCoordinator::ensureRootStateNodeForFrameView(FrameView& frameView) +{ + ASSERT(frameView.scrollLayerID()); + attachToStateTree(FrameScrollingNode, frameView.scrollLayerID(), 0); +} + +void AsyncScrollingCoordinator::updateFrameScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* layer, GraphicsLayer* scrolledContentsLayer, GraphicsLayer* counterScrollingLayer, GraphicsLayer* insetClipLayer, const ScrollingGeometry* scrollingGeometry) +{ + ScrollingStateFrameScrollingNode* node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(nodeID)); + ASSERT(node); + if (!node) + return; + + node->setLayer(layer); + node->setInsetClipLayer(insetClipLayer); + node->setScrolledContentsLayer(scrolledContentsLayer); + node->setCounterScrollingLayer(counterScrollingLayer); + + if (scrollingGeometry) { + node->setScrollOrigin(scrollingGeometry->scrollOrigin); + node->setScrollPosition(scrollingGeometry->scrollPosition); + node->setTotalContentsSize(scrollingGeometry->contentSize); + node->setReachableContentsSize(scrollingGeometry->reachableContentSize); + node->setScrollableAreaSize(scrollingGeometry->scrollableAreaSize); + } +} + +void AsyncScrollingCoordinator::updateOverflowScrollingNode(ScrollingNodeID nodeID, GraphicsLayer* layer, GraphicsLayer* scrolledContentsLayer, const ScrollingGeometry* scrollingGeometry) +{ + ScrollingStateOverflowScrollingNode* node = downcast<ScrollingStateOverflowScrollingNode>(m_scrollingStateTree->stateNodeForID(nodeID)); + ASSERT(node); + if (!node) + return; + + node->setLayer(layer); + node->setScrolledContentsLayer(scrolledContentsLayer); + + if (scrollingGeometry) { + node->setScrollOrigin(scrollingGeometry->scrollOrigin); + node->setScrollPosition(scrollingGeometry->scrollPosition); + node->setTotalContentsSize(scrollingGeometry->contentSize); + node->setReachableContentsSize(scrollingGeometry->reachableContentSize); + node->setScrollableAreaSize(scrollingGeometry->scrollableAreaSize); +#if ENABLE(CSS_SCROLL_SNAP) + setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, &scrollingGeometry->horizontalSnapOffsets, &scrollingGeometry->horizontalSnapOffsetRanges, m_page->deviceScaleFactor()); + setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, &scrollingGeometry->verticalSnapOffsets, &scrollingGeometry->verticalSnapOffsetRanges, m_page->deviceScaleFactor()); + node->setCurrentHorizontalSnapPointIndex(scrollingGeometry->currentHorizontalSnapPointIndex); + node->setCurrentVerticalSnapPointIndex(scrollingGeometry->currentVerticalSnapPointIndex); +#endif + } +} + +void AsyncScrollingCoordinator::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer) +{ + ASSERT(supportsFixedPositionLayers()); + + ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID); + if (!node) + return; + + switch (constraints.constraintType()) { + case ViewportConstraints::FixedPositionConstraint: { + ScrollingStateFixedNode& fixedNode = downcast<ScrollingStateFixedNode>(*node); + fixedNode.setLayer(graphicsLayer); + fixedNode.updateConstraints((const FixedPositionViewportConstraints&)constraints); + break; + } + case ViewportConstraints::StickyPositionConstraint: { + ScrollingStateStickyNode& stickyNode = downcast<ScrollingStateStickyNode>(*node); + stickyNode.setLayer(graphicsLayer); + stickyNode.updateConstraints((const StickyPositionViewportConstraints&)constraints); + break; + } + } +} + +void AsyncScrollingCoordinator::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons) +{ + if (!m_scrollingStateTree->rootStateNode()) + return; + + // The FrameView's GraphicsLayer is likely to be out-of-synch with the PlatformLayer + // at this point. So we'll update it before we switch back to main thread scrolling + // in order to avoid layer positioning bugs. + if (reasons) + updateMainFrameScrollLayerPosition(); + m_scrollingStateTree->rootStateNode()->setSynchronousScrollingReasons(reasons); +} + +void AsyncScrollingCoordinator::updateMainFrameScrollLayerPosition() +{ + ASSERT(isMainThread()); + + if (!m_page) + return; + + FrameView* frameView = m_page->mainFrame().view(); + if (!frameView) + return; + + if (GraphicsLayer* scrollLayer = scrollLayerForFrameView(*frameView)) + scrollLayer->setPosition(-frameView->scrollPosition()); +} + +bool AsyncScrollingCoordinator::isRubberBandInProgress() const +{ + return scrollingTree()->isRubberBandInProgress(); +} + +void AsyncScrollingCoordinator::setScrollPinningBehavior(ScrollPinningBehavior pinning) +{ + scrollingTree()->setScrollPinningBehavior(pinning); +} + +bool AsyncScrollingCoordinator::visualViewportEnabled() const +{ + return m_page->mainFrame().settings().visualViewportEnabled(); +} + +String AsyncScrollingCoordinator::scrollingStateTreeAsText() const +{ + if (m_scrollingStateTree->rootStateNode()) { + if (m_eventTrackingRegionsDirty) + m_scrollingStateTree->rootStateNode()->setEventTrackingRegions(absoluteEventTrackingRegions()); + return m_scrollingStateTree->rootStateNode()->scrollingStateTreeAsText(); + } + + return String(); +} + +#if PLATFORM(COCOA) +void AsyncScrollingCoordinator::setActiveScrollSnapIndices(ScrollingNodeID scrollingNodeID, unsigned horizontalIndex, unsigned verticalIndex) +{ + ASSERT(isMainThread()); + + if (!m_page) + return; + + FrameView* frameView = frameViewForScrollingNode(scrollingNodeID); + if (!frameView) + return; + + if (scrollingNodeID == frameView->scrollLayerID()) { + frameView->setCurrentHorizontalSnapPointIndex(horizontalIndex); + frameView->setCurrentVerticalSnapPointIndex(verticalIndex); + return; + } + + // Overflow-scroll area. + if (ScrollableArea* scrollableArea = frameView->scrollableAreaForScrollLayerID(scrollingNodeID)) { + scrollableArea->setCurrentHorizontalSnapPointIndex(horizontalIndex); + scrollableArea->setCurrentVerticalSnapPointIndex(verticalIndex); + } +} + +void AsyncScrollingCoordinator::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const +{ + ASSERT(isMainThread()); + if (!m_page || !m_page->expectsWheelEventTriggers()) + return; + + if (const auto& trigger = m_page->testTrigger()) { + LOG(WheelEventTestTriggers, " (!) AsyncScrollingCoordinator::deferTestsForReason: Deferring %p for reason %d.", identifier, reason); + trigger->deferTestsForReason(identifier, reason); + } +} + +void AsyncScrollingCoordinator::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) const +{ + ASSERT(isMainThread()); + if (!m_page || !m_page->expectsWheelEventTriggers()) + return; + + if (const auto& trigger = m_page->testTrigger()) { + LOG(WheelEventTestTriggers, " (!) AsyncScrollingCoordinator::removeTestDeferralForReason: Deferring %p for reason %d.", identifier, reason); + trigger->removeTestDeferralForReason(identifier, reason); + } +} +#endif + +#if ENABLE(CSS_SCROLL_SNAP) +bool AsyncScrollingCoordinator::isScrollSnapInProgress() const +{ + return scrollingTree()->isScrollSnapInProgress(); +} + +void AsyncScrollingCoordinator::updateScrollSnapPropertiesWithFrameView(const FrameView& frameView) +{ + if (auto node = downcast<ScrollingStateFrameScrollingNode>(m_scrollingStateTree->stateNodeForID(frameView.scrollLayerID()))) { + setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Horizontal, frameView.horizontalSnapOffsets(), frameView.horizontalSnapOffsetRanges(), m_page->deviceScaleFactor()); + setStateScrollingNodeSnapOffsetsAsFloat(*node, ScrollEventAxis::Vertical, frameView.verticalSnapOffsets(), frameView.verticalSnapOffsetRanges(), m_page->deviceScaleFactor()); + node->setCurrentHorizontalSnapPointIndex(frameView.currentHorizontalSnapPointIndex()); + node->setCurrentVerticalSnapPointIndex(frameView.currentVerticalSnapPointIndex()); + } +} +#endif + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.h b/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.h new file mode 100644 index 000000000..011ae0f23 --- /dev/null +++ b/Source/WebCore/page/scrolling/AsyncScrollingCoordinator.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2014-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 + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingCoordinator.h" +#include "ScrollingTree.h" +#include "Timer.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Page; +class Scrollbar; +class ScrollingStateNode; +class ScrollingStateScrollingNode; +class ScrollingStateTree; + +// ScrollingCoordinator subclass that maintains a ScrollingStateTree and a ScrollingTree, +// allowing asynchronous scrolling (in another thread or process). +class AsyncScrollingCoordinator : public ScrollingCoordinator { +public: + static Ref<AsyncScrollingCoordinator> create(Page*); + WEBCORE_EXPORT virtual ~AsyncScrollingCoordinator(); + + ScrollingTree* scrollingTree() const { return m_scrollingTree.get(); } + + void scrollingStateTreePropertiesChanged(); + + WEBCORE_EXPORT void scheduleUpdateScrollPositionAfterAsyncScroll(ScrollingNodeID, const FloatPoint&, const std::optional<FloatPoint>& layoutViewportOrigin, bool programmaticScroll, ScrollingLayerPositionAction); + +#if PLATFORM(COCOA) + WEBCORE_EXPORT void setActiveScrollSnapIndices(ScrollingNodeID, unsigned horizontalIndex, unsigned verticalIndex); + void deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) const; + void removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) const; +#endif + +#if ENABLE(CSS_SCROLL_SNAP) + WEBCORE_EXPORT void updateScrollSnapPropertiesWithFrameView(const FrameView&) override; +#endif + + WEBCORE_EXPORT void updateExpectsWheelEventTestTriggerWithFrameView(const FrameView&) override; + +protected: + WEBCORE_EXPORT AsyncScrollingCoordinator(Page*); + + void setScrollingTree(Ref<ScrollingTree>&& scrollingTree) { m_scrollingTree = WTFMove(scrollingTree); } + + ScrollingStateTree* scrollingStateTree() { return m_scrollingStateTree.get(); } + + RefPtr<ScrollingTree> releaseScrollingTree() { return WTFMove(m_scrollingTree); } + + void updateScrollPositionAfterAsyncScroll(ScrollingNodeID, const FloatPoint&, std::optional<FloatPoint> layoutViewportOrigin, bool programmaticScroll, ScrollingLayerPositionAction); + + WEBCORE_EXPORT String scrollingStateTreeAsText() const override; + WEBCORE_EXPORT void willCommitTree() override; + + bool eventTrackingRegionsDirty() const { return m_eventTrackingRegionsDirty; } + +private: + bool isAsyncScrollingCoordinator() const override { return true; } + + bool supportsFixedPositionLayers() const override { return true; } + bool hasVisibleSlowRepaintViewportConstrainedObjects(const FrameView&) const override { return false; } + + bool visualViewportEnabled() const; + + WEBCORE_EXPORT void frameViewLayoutUpdated(FrameView&) override; + WEBCORE_EXPORT void frameViewRootLayerDidChange(FrameView&) override; + WEBCORE_EXPORT void frameViewEventTrackingRegionsChanged(FrameView&) override; + + WEBCORE_EXPORT bool requestScrollPositionUpdate(FrameView&, const IntPoint&) override; + + WEBCORE_EXPORT ScrollingNodeID attachToStateTree(ScrollingNodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID) override; + WEBCORE_EXPORT void detachFromStateTree(ScrollingNodeID) override; + WEBCORE_EXPORT void clearStateTree() override; + + WEBCORE_EXPORT void updateViewportConstrainedNode(ScrollingNodeID, const ViewportConstraints&, GraphicsLayer*) override; + + WEBCORE_EXPORT void updateFrameScrollingNode(ScrollingNodeID, GraphicsLayer* scrollLayer, GraphicsLayer* scrolledContentsLayer, GraphicsLayer* counterScrollingLayer, GraphicsLayer* insetClipLayer, const ScrollingGeometry* = nullptr) override; + WEBCORE_EXPORT void updateOverflowScrollingNode(ScrollingNodeID, GraphicsLayer* scrollLayer, GraphicsLayer* scrolledContentsLayer, const ScrollingGeometry* = nullptr) override; + + WEBCORE_EXPORT void reconcileScrollingState(FrameView&, const FloatPoint&, const LayoutViewportOriginOrOverrideRect&, bool programmaticScroll, bool inStableState, ScrollingLayerPositionAction) override; + + bool isRubberBandInProgress() const override; + void setScrollPinningBehavior(ScrollPinningBehavior) override; + +#if ENABLE(CSS_SCROLL_SNAP) + bool isScrollSnapInProgress() const override; +#endif + + WEBCORE_EXPORT void reconcileViewportConstrainedLayerPositions(const LayoutRect& viewportRect, ScrollingLayerPositionAction) override; + WEBCORE_EXPORT void scrollableAreaScrollbarLayerDidChange(ScrollableArea&, ScrollbarOrientation) override; + + WEBCORE_EXPORT void setSynchronousScrollingReasons(SynchronousScrollingReasons) override; + + virtual void scheduleTreeStateCommit() = 0; + + void ensureRootStateNodeForFrameView(FrameView&); + void updateMainFrameScrollLayerPosition(); + + void updateScrollPositionAfterAsyncScrollTimerFired(); + void setEventTrackingRegionsDirty(); + void updateEventTrackingRegions(); + + FrameView* frameViewForScrollingNode(ScrollingNodeID) const; + + Timer m_updateNodeScrollPositionTimer; + + struct ScheduledScrollUpdate { + ScheduledScrollUpdate() = default; + ScheduledScrollUpdate(ScrollingNodeID scrollingNodeID, FloatPoint point, std::optional<FloatPoint> viewportOrigin, bool isProgrammatic, ScrollingLayerPositionAction udpateAction) + : nodeID(scrollingNodeID) + , scrollPosition(point) + , layoutViewportOrigin(viewportOrigin) + , isProgrammaticScroll(isProgrammatic) + , updateLayerPositionAction(udpateAction) + { } + + ScrollingNodeID nodeID { 0 }; + FloatPoint scrollPosition; + std::optional<FloatPoint> layoutViewportOrigin; + bool isProgrammaticScroll { false }; + ScrollingLayerPositionAction updateLayerPositionAction { ScrollingLayerPositionAction::Sync }; + + bool matchesUpdateType(const ScheduledScrollUpdate& other) const + { + return nodeID == other.nodeID + && isProgrammaticScroll == other.isProgrammaticScroll + && updateLayerPositionAction == other.updateLayerPositionAction; + } + }; + + ScheduledScrollUpdate m_scheduledScrollUpdate; + + std::unique_ptr<ScrollingStateTree> m_scrollingStateTree; + RefPtr<ScrollingTree> m_scrollingTree; + + bool m_eventTrackingRegionsDirty { false }; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_COORDINATOR(WebCore::AsyncScrollingCoordinator, isAsyncScrollingCoordinator()); + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp b/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp new file mode 100644 index 000000000..020753847 --- /dev/null +++ b/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2014-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 "AxisScrollSnapOffsets.h" + +#include "ElementChildIterator.h" +#include "HTMLCollection.h" +#include "HTMLElement.h" +#include "Length.h" +#include "Logging.h" +#include "RenderBox.h" +#include "RenderView.h" +#include "ScrollableArea.h" +#include "StyleScrollSnapPoints.h" + +#if ENABLE(CSS_SCROLL_SNAP) + +namespace WebCore { + +enum class InsetOrOutset { + Inset, + Outset +}; + +static LayoutRect computeScrollSnapPortOrAreaRect(const LayoutRect& rect, const LengthBox& insetOrOutsetBox, InsetOrOutset insetOrOutset) +{ + LayoutBoxExtent extents(valueForLength(insetOrOutsetBox.top(), rect.height()), valueForLength(insetOrOutsetBox.right(), rect.width()), valueForLength(insetOrOutsetBox.bottom(), rect.height()), valueForLength(insetOrOutsetBox.left(), rect.width())); + auto snapPortOrArea(rect); + if (insetOrOutset == InsetOrOutset::Inset) + snapPortOrArea.contract(extents); + else + snapPortOrArea.expand(extents); + return snapPortOrArea; +} + +static LayoutUnit computeScrollSnapAlignOffset(const LayoutUnit& leftOrTop, const LayoutUnit& widthOrHeight, ScrollSnapAxisAlignType alignment) +{ + switch (alignment) { + case ScrollSnapAxisAlignType::Start: + return leftOrTop; + case ScrollSnapAxisAlignType::Center: + return leftOrTop + widthOrHeight / 2; + case ScrollSnapAxisAlignType::End: + return leftOrTop + widthOrHeight; + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +#if !LOG_DISABLED + +static String snapOffsetsToString(const Vector<LayoutUnit>& snapOffsets) +{ + StringBuilder s; + s.append("[ "); + for (auto offset : snapOffsets) + s.append(String::format("%.1f ", offset.toFloat())); + + s.append("]"); + return s.toString(); +} + +static String snapOffsetRangesToString(const Vector<ScrollOffsetRange<LayoutUnit>>& ranges) +{ + StringBuilder s; + s.append("[ "); + for (auto range : ranges) + s.append(String::format("(%.1f, %.1f) ", range.start.toFloat(), range.end.toFloat())); + s.append("]"); + return s.toString(); +} + +static String snapPortOrAreaToString(const LayoutRect& rect) +{ + return String::format("{{%.1f, %.1f} {%.1f, %.1f}}", rect.x().toFloat(), rect.y().toFloat(), rect.width().toFloat(), rect.height().toFloat()); +} + +#endif + +template <typename LayoutType> +static void indicesOfNearestSnapOffsetRanges(LayoutType offset, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, unsigned& lowerIndex, unsigned& upperIndex) +{ + if (snapOffsetRanges.isEmpty()) { + lowerIndex = invalidSnapOffsetIndex; + upperIndex = invalidSnapOffsetIndex; + return; + } + + int lowerIndexAsInt = -1; + int upperIndexAsInt = snapOffsetRanges.size(); + do { + int middleIndex = (lowerIndexAsInt + upperIndexAsInt) / 2; + auto& range = snapOffsetRanges[middleIndex]; + if (range.start < offset && offset < range.end) { + lowerIndexAsInt = middleIndex; + upperIndexAsInt = middleIndex; + break; + } + + if (offset > range.end) + lowerIndexAsInt = middleIndex; + else + upperIndexAsInt = middleIndex; + } while (lowerIndexAsInt < upperIndexAsInt - 1); + + if (offset <= snapOffsetRanges.first().start) + lowerIndex = invalidSnapOffsetIndex; + else + lowerIndex = lowerIndexAsInt; + + if (offset >= snapOffsetRanges.last().end) + upperIndex = invalidSnapOffsetIndex; + else + upperIndex = upperIndexAsInt; +} + +template <typename LayoutType> +static void indicesOfNearestSnapOffsets(LayoutType offset, const Vector<LayoutType>& snapOffsets, unsigned& lowerIndex, unsigned& upperIndex) +{ + lowerIndex = 0; + upperIndex = snapOffsets.size() - 1; + while (lowerIndex < upperIndex - 1) { + int middleIndex = (lowerIndex + upperIndex) / 2; + auto middleOffset = snapOffsets[middleIndex]; + if (offset == middleOffset) { + upperIndex = middleIndex; + lowerIndex = middleIndex; + break; + } + + if (offset > middleOffset) + lowerIndex = middleIndex; + else + upperIndex = middleIndex; + } +} + +static void adjustAxisSnapOffsetsForScrollExtent(Vector<LayoutUnit>& snapOffsets, float maxScrollExtent) +{ + if (snapOffsets.isEmpty()) + return; + + std::sort(snapOffsets.begin(), snapOffsets.end()); + if (snapOffsets.last() != maxScrollExtent) + snapOffsets.append(maxScrollExtent); + if (snapOffsets.first()) + snapOffsets.insert(0, 0); +} + +static void computeAxisProximitySnapOffsetRanges(const Vector<LayoutUnit>& snapOffsets, Vector<ScrollOffsetRange<LayoutUnit>>& offsetRanges, LayoutUnit scrollPortAxisLength) +{ + // This is an arbitrary choice for what it means to be "in proximity" of a snap offset. We should play around with + // this and see what feels best. + static const float ratioOfScrollPortAxisLengthToBeConsideredForProximity = 0.3; + if (snapOffsets.size() < 2) + return; + + // The extra rule accounting for scroll offset ranges in between the scroll destination and a potential snap offset + // handles the corner case where the user scrolls with momentum very lightly away from a snap offset, such that the + // predicted scroll destination is still within proximity of the snap offset. In this case, the regular (mandatory + // scroll snapping) behavior would be to snap to the next offset in the direction of momentum scrolling, but + // instead, it is more intuitive to either return to the original snap position (which we arbitrarily choose here) + // or scroll just outside of the snap offset range. This is another minor behavior tweak that we should play around + // with to see what feels best. + LayoutUnit proximityDistance = ratioOfScrollPortAxisLengthToBeConsideredForProximity * scrollPortAxisLength; + for (size_t index = 1; index < snapOffsets.size(); ++index) { + auto startOffset = snapOffsets[index - 1] + proximityDistance; + auto endOffset = snapOffsets[index] - proximityDistance; + if (startOffset < endOffset) + offsetRanges.append({ startOffset, endOffset }); + } +} + +void updateSnapOffsetsForScrollableArea(ScrollableArea& scrollableArea, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle) +{ + auto* scrollContainer = scrollingElement.renderer(); + auto scrollSnapType = scrollingElementStyle.scrollSnapType(); + if (!scrollContainer || scrollSnapType.strictness == ScrollSnapStrictness::None || scrollContainer->view().boxesWithScrollSnapPositions().isEmpty()) { + scrollableArea.clearHorizontalSnapOffsets(); + scrollableArea.clearVerticalSnapOffsets(); + return; + } + + Vector<LayoutUnit> verticalSnapOffsets; + Vector<LayoutUnit> horizontalSnapOffsets; + Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges; + Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges; + HashSet<float> seenVerticalSnapOffsets; + HashSet<float> seenHorizontalSnapOffsets; + bool hasHorizontalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::XAxis || scrollSnapType.axis == ScrollSnapAxis::Inline; + bool hasVerticalSnapOffsets = scrollSnapType.axis == ScrollSnapAxis::Both || scrollSnapType.axis == ScrollSnapAxis::YAxis || scrollSnapType.axis == ScrollSnapAxis::Block; + auto maxScrollLeft = scrollingElementBox.scrollWidth() - scrollingElementBox.contentWidth(); + auto maxScrollTop = scrollingElementBox.scrollHeight() - scrollingElementBox.contentHeight(); + LayoutPoint containerScrollOffset(scrollingElementBox.scrollLeft(), scrollingElementBox.scrollTop()); + + // The bounds of the scrolling container's snap port, where the top left of the scrolling container's border box is the origin. + auto scrollSnapPort = computeScrollSnapPortOrAreaRect(scrollingElementBox.paddingBoxRect(), scrollingElementStyle.scrollPadding(), InsetOrOutset::Inset); +#if !LOG_DISABLED + LOG(Scrolling, "Computing scroll snap offsets in snap port: %s", snapPortOrAreaToString(scrollSnapPort).utf8().data()); +#endif + for (auto* child : scrollContainer->view().boxesWithScrollSnapPositions()) { + if (child->findEnclosingScrollableContainer() != scrollContainer) + continue; + + // The bounds of the child element's snap area, where the top left of the scrolling container's border box is the origin. + // The snap area is the bounding box of the child element's border box, after applying transformations. + auto scrollSnapArea = LayoutRect(child->localToContainerQuad(FloatQuad(child->borderBoundingBox()), scrollingElement.renderBox()).boundingBox()); + scrollSnapArea.moveBy(containerScrollOffset); + scrollSnapArea = computeScrollSnapPortOrAreaRect(scrollSnapArea, child->style().scrollSnapMargin(), InsetOrOutset::Outset); +#if !LOG_DISABLED + LOG(Scrolling, " Considering scroll snap area: %s", snapPortOrAreaToString(scrollSnapArea).utf8().data()); +#endif + auto alignment = child->style().scrollSnapAlign(); + if (hasHorizontalSnapOffsets && alignment.x != ScrollSnapAxisAlignType::None) { + auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.x(), scrollSnapArea.width(), alignment.x) - computeScrollSnapAlignOffset(scrollSnapPort.x(), scrollSnapPort.width(), alignment.x), 0, maxScrollLeft); + if (!seenHorizontalSnapOffsets.contains(absoluteScrollOffset)) { + seenHorizontalSnapOffsets.add(absoluteScrollOffset); + horizontalSnapOffsets.append(absoluteScrollOffset); + } + } + if (hasVerticalSnapOffsets && alignment.y != ScrollSnapAxisAlignType::None) { + auto absoluteScrollOffset = clampTo<LayoutUnit>(computeScrollSnapAlignOffset(scrollSnapArea.y(), scrollSnapArea.height(), alignment.y) - computeScrollSnapAlignOffset(scrollSnapPort.y(), scrollSnapPort.height(), alignment.y), 0, maxScrollTop); + if (!seenVerticalSnapOffsets.contains(absoluteScrollOffset)) { + seenVerticalSnapOffsets.add(absoluteScrollOffset); + verticalSnapOffsets.append(absoluteScrollOffset); + } + } + } + + if (!horizontalSnapOffsets.isEmpty()) { + adjustAxisSnapOffsetsForScrollExtent(horizontalSnapOffsets, maxScrollLeft); +#if !LOG_DISABLED + LOG(Scrolling, " => Computed horizontal scroll snap offsets: %s", snapOffsetsToString(horizontalSnapOffsets).utf8().data()); + LOG(Scrolling, " => Computed horizontal scroll snap offset ranges: %s", snapOffsetRangesToString(horizontalSnapOffsetRanges).utf8().data()); +#endif + if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity) + computeAxisProximitySnapOffsetRanges(horizontalSnapOffsets, horizontalSnapOffsetRanges, scrollSnapPort.width()); + + scrollableArea.setHorizontalSnapOffsets(horizontalSnapOffsets); + scrollableArea.setHorizontalSnapOffsetRanges(horizontalSnapOffsetRanges); + } else + scrollableArea.clearHorizontalSnapOffsets(); + + if (!verticalSnapOffsets.isEmpty()) { + adjustAxisSnapOffsetsForScrollExtent(verticalSnapOffsets, maxScrollTop); +#if !LOG_DISABLED + LOG(Scrolling, " => Computed vertical scroll snap offsets: %s", snapOffsetsToString(verticalSnapOffsets).utf8().data()); + LOG(Scrolling, " => Computed vertical scroll snap offset ranges: %s", snapOffsetRangesToString(verticalSnapOffsetRanges).utf8().data()); +#endif + if (scrollSnapType.strictness == ScrollSnapStrictness::Proximity) + computeAxisProximitySnapOffsetRanges(verticalSnapOffsets, verticalSnapOffsetRanges, scrollSnapPort.height()); + + scrollableArea.setVerticalSnapOffsets(verticalSnapOffsets); + scrollableArea.setVerticalSnapOffsetRanges(verticalSnapOffsetRanges); + } else + scrollableArea.clearVerticalSnapOffsets(); +} + +template <typename LayoutType> +LayoutType closestSnapOffset(const Vector<LayoutType>& snapOffsets, const Vector<ScrollOffsetRange<LayoutType>>& snapOffsetRanges, LayoutType scrollDestination, float velocity, unsigned& activeSnapIndex) +{ + ASSERT(snapOffsets.size()); + activeSnapIndex = 0; + + unsigned lowerSnapOffsetRangeIndex; + unsigned upperSnapOffsetRangeIndex; + indicesOfNearestSnapOffsetRanges<LayoutType>(scrollDestination, snapOffsetRanges, lowerSnapOffsetRangeIndex, upperSnapOffsetRangeIndex); + if (lowerSnapOffsetRangeIndex == upperSnapOffsetRangeIndex && upperSnapOffsetRangeIndex != invalidSnapOffsetIndex) { + activeSnapIndex = invalidSnapOffsetIndex; + return scrollDestination; + } + + if (scrollDestination <= snapOffsets.first()) + return snapOffsets.first(); + + activeSnapIndex = snapOffsets.size() - 1; + if (scrollDestination >= snapOffsets.last()) + return snapOffsets.last(); + + unsigned lowerIndex; + unsigned upperIndex; + indicesOfNearestSnapOffsets<LayoutType>(scrollDestination, snapOffsets, lowerIndex, upperIndex); + LayoutType lowerSnapPosition = snapOffsets[lowerIndex]; + LayoutType upperSnapPosition = snapOffsets[upperIndex]; + if (!std::abs(velocity)) { + bool isCloserToLowerSnapPosition = scrollDestination - lowerSnapPosition <= upperSnapPosition - scrollDestination; + activeSnapIndex = isCloserToLowerSnapPosition ? lowerIndex : upperIndex; + return isCloserToLowerSnapPosition ? lowerSnapPosition : upperSnapPosition; + } + + // Non-zero velocity indicates a flick gesture. Even if another snap point is closer, we should choose the one in the direction of the flick gesture + // as long as a scroll snap offset range does not lie between the scroll destination and the targeted snap offset. + if (velocity < 0) { + if (lowerSnapOffsetRangeIndex != invalidSnapOffsetIndex && lowerSnapPosition < snapOffsetRanges[lowerSnapOffsetRangeIndex].end) { + activeSnapIndex = upperIndex; + return upperSnapPosition; + } + activeSnapIndex = lowerIndex; + return lowerSnapPosition; + } + + if (upperSnapOffsetRangeIndex != invalidSnapOffsetIndex && snapOffsetRanges[upperSnapOffsetRangeIndex].start < upperSnapPosition) { + activeSnapIndex = lowerIndex; + return lowerSnapPosition; + } + activeSnapIndex = upperIndex; + return upperSnapPosition; +} + +LayoutUnit closestSnapOffset(const Vector<LayoutUnit>& snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRanges, LayoutUnit scrollDestination, float velocity, unsigned& activeSnapIndex) +{ + return closestSnapOffset<LayoutUnit>(snapOffsets, snapOffsetRanges, scrollDestination, velocity, activeSnapIndex); +} + +float closestSnapOffset(const Vector<float>& snapOffsets, const Vector<ScrollOffsetRange<float>>& snapOffsetRanges, float scrollDestination, float velocity, unsigned& activeSnapIndex) +{ + return closestSnapOffset<float>(snapOffsets, snapOffsetRanges, scrollDestination, velocity, activeSnapIndex); +} + +} // namespace WebCore + +#endif // CSS_SCROLL_SNAP diff --git a/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h b/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h new file mode 100644 index 000000000..6b6b2bc85 --- /dev/null +++ b/Source/WebCore/page/scrolling/AxisScrollSnapOffsets.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014-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 + +#if ENABLE(CSS_SCROLL_SNAP) + +#include "LayoutUnit.h" +#include "ScrollSnapOffsetsInfo.h" +#include "ScrollTypes.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class HTMLElement; +class RenderBox; +class RenderStyle; +class ScrollableArea; + +void updateSnapOffsetsForScrollableArea(ScrollableArea&, HTMLElement& scrollingElement, const RenderBox& scrollingElementBox, const RenderStyle& scrollingElementStyle); + +const unsigned invalidSnapOffsetIndex = UINT_MAX; +WEBCORE_EXPORT LayoutUnit closestSnapOffset(const Vector<LayoutUnit>& snapOffsets, const Vector<ScrollOffsetRange<LayoutUnit>>& snapOffsetRanges, LayoutUnit scrollDestination, float velocity, unsigned& activeSnapIndex); +WEBCORE_EXPORT float closestSnapOffset(const Vector<float>& snapOffsets, const Vector<ScrollOffsetRange<float>>& snapOffsetRanges, float scrollDestination, float velocity, unsigned& activeSnapIndex); + +} // namespace WebCore + +#endif // ENABLE(CSS_SCROLL_SNAP) diff --git a/Source/WebCore/page/scrolling/ScrollLatchingState.cpp b/Source/WebCore/page/scrolling/ScrollLatchingState.cpp new file mode 100644 index 000000000..01e6529b0 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollLatchingState.cpp @@ -0,0 +1,70 @@ +/* + * 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 "ScrollLatchingState.h" + +#include "Element.h" + +namespace WebCore { + +ScrollLatchingState::ScrollLatchingState() +{ +} + +ScrollLatchingState::~ScrollLatchingState() +{ +} + +void ScrollLatchingState::clear() +{ + m_wheelEventElement = nullptr; + m_frame = nullptr; + m_scrollableContainer = nullptr; + m_widgetIsLatched = false; + m_previousWheelScrolledElement = nullptr; +} + +void ScrollLatchingState::setWheelEventElement(Element* element) +{ + m_wheelEventElement = element; +} + +void ScrollLatchingState::setWidgetIsLatched(bool isOverWidget) +{ + m_widgetIsLatched = isOverWidget; +} + +void ScrollLatchingState::setPreviousWheelScrolledElement(Element* element) +{ + m_previousWheelScrolledElement = element; +} + +void ScrollLatchingState::setScrollableContainer(ContainerNode* container) +{ + m_scrollableContainer = container; +} + +} diff --git a/Source/WebCore/page/scrolling/ScrollLatchingState.h b/Source/WebCore/page/scrolling/ScrollLatchingState.h new file mode 100644 index 000000000..11dcbd0db --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollLatchingState.h @@ -0,0 +1,70 @@ +/* + * 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 + +#include <wtf/RefPtr.h> + +namespace WebCore { + +class ContainerNode; +class Element; +class Frame; + +class ScrollLatchingState final { +public: + ScrollLatchingState(); + ~ScrollLatchingState(); + + void clear(); + + Element* wheelEventElement() { return m_wheelEventElement.get(); } + void setWheelEventElement(Element*); + Frame* frame() { return m_frame; } + void setFrame(Frame* frame) { m_frame = frame; } + + bool widgetIsLatched() const { return m_widgetIsLatched; } + void setWidgetIsLatched(bool isOverWidget); + + Element* previousWheelScrolledElement() { return m_previousWheelScrolledElement.get(); } + void setPreviousWheelScrolledElement(Element*); + + ContainerNode* scrollableContainer() { return m_scrollableContainer.get(); } + void setScrollableContainer(ContainerNode*); + bool startedGestureAtScrollLimit() const { return m_startedGestureAtScrollLimit; } + void setStartedGestureAtScrollLimit(bool startedAtLimit) { m_startedGestureAtScrollLimit = startedAtLimit; } + +private: + RefPtr<Element> m_wheelEventElement; + RefPtr<Element> m_previousWheelScrolledElement; + RefPtr<ContainerNode> m_scrollableContainer; + + Frame* m_frame { nullptr }; + + bool m_widgetIsLatched { false }; + bool m_startedGestureAtScrollLimit { false }; +}; + +} // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h b/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h new file mode 100644 index 000000000..63bd9638b --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollSnapOffsetsInfo.h @@ -0,0 +1,51 @@ +/* + * 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 <wtf/Vector.h> + +namespace WebCore { + +template <typename T> +struct ScrollOffsetRange { + T start; + T end; +}; + +template <typename T> +struct ScrollSnapOffsetsInfo { + Vector<T> horizontalSnapOffsets; + Vector<T> verticalSnapOffsets; + + // Snap offset ranges represent non-empty ranges of scroll offsets in which scrolling may rest after scroll snapping. + // These are used in two cases: (1) for proximity scroll snapping, where portions of areas between adjacent snap offsets + // may emit snap offset ranges, and (2) in the case where the snap area is larger than the snap port, in which case areas + // where the snap port fits within the snap area are considered to be valid snap positions. + Vector<ScrollOffsetRange<T>> horizontalSnapOffsetRanges; + Vector<ScrollOffsetRange<T>> verticalSnapOffsetRanges; +}; + +}; // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingConstraints.cpp b/Source/WebCore/page/scrolling/ScrollingConstraints.cpp index 4b3f4d68c..00f101a34 100644 --- a/Source/WebCore/page/scrolling/ScrollingConstraints.cpp +++ b/Source/WebCore/page/scrolling/ScrollingConstraints.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "ScrollingConstraints.h" +#include "TextStream.h" + namespace WebCore { FloatPoint FixedPositionViewportConstraints::layerPositionForViewportRect(const FloatRect& viewportRect) const @@ -98,4 +100,20 @@ FloatPoint StickyPositionViewportConstraints::layerPositionForConstrainingRect(c return m_layerPositionAtLastLayout + offset - m_stickyOffsetAtLastLayout; } +TextStream& operator<<(TextStream& ts, const FixedPositionViewportConstraints& constraints) +{ + ts.dumpProperty("viewport-rect-at-last-layout", constraints.viewportRectAtLastLayout()); + ts.dumpProperty("layer-position-at-last-layout", constraints.layerPositionAtLastLayout()); + + return ts; +} + +TextStream& operator<<(TextStream& ts, const StickyPositionViewportConstraints& constraints) +{ + ts.dumpProperty("sticky-position-at-last-layout", constraints.stickyOffsetAtLastLayout()); + ts.dumpProperty("layer-position-at-last-layout", constraints.layerPositionAtLastLayout()); + + return ts; +} + } // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingConstraints.h b/Source/WebCore/page/scrolling/ScrollingConstraints.h index d4c1dd461..9d3860062 100644 --- a/Source/WebCore/page/scrolling/ScrollingConstraints.h +++ b/Source/WebCore/page/scrolling/ScrollingConstraints.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScrollingConstraints_h -#define ScrollingConstraints_h +#pragma once #include "FloatRect.h" @@ -33,6 +32,7 @@ namespace WebCore { // ViewportConstraints classes encapsulate data and logic required to reposition elements whose layout // depends on the viewport rect (positions fixed and sticky), when scrolling and zooming. class ViewportConstraints { + WTF_MAKE_FAST_ALLOCATED; public: enum ConstraintType { FixedPositionConstraint, @@ -85,7 +85,7 @@ public: , m_layerPositionAtLastLayout(other.m_layerPositionAtLastLayout) { } - FloatPoint layerPositionForViewportRect(const FloatRect& viewportRect) const; + WEBCORE_EXPORT FloatPoint layerPositionForViewportRect(const FloatRect& viewportRect) const; const FloatRect& viewportRectAtLastLayout() const { return m_viewportRectAtLastLayout; } void setViewportRectAtLastLayout(const FloatRect& rect) { m_viewportRectAtLastLayout = rect; } @@ -104,7 +104,7 @@ public: bool operator!=(const FixedPositionViewportConstraints& other) const { return !(*this == other); } private: - virtual ConstraintType constraintType() const override { return FixedPositionConstraint; }; + ConstraintType constraintType() const override { return FixedPositionConstraint; }; FloatRect m_viewportRectAtLastLayout; FloatPoint m_layerPositionAtLastLayout; @@ -137,7 +137,7 @@ public: const FloatSize stickyOffsetAtLastLayout() const { return m_stickyOffsetAtLastLayout; } void setStickyOffsetAtLastLayout(const FloatSize& offset) { m_stickyOffsetAtLastLayout = offset; } - FloatPoint layerPositionForConstrainingRect(const FloatRect& constrainingRect) const; + WEBCORE_EXPORT FloatPoint layerPositionForConstrainingRect(const FloatRect& constrainingRect) const; const FloatPoint& layerPositionAtLastLayout() const { return m_layerPositionAtLastLayout; } void setLayerPositionAtLastLayout(const FloatPoint& point) { m_layerPositionAtLastLayout = point; } @@ -167,7 +167,9 @@ public: bool operator==(const StickyPositionViewportConstraints& other) const { - return m_leftOffset == other.m_leftOffset + return m_alignmentOffset == other.m_alignmentOffset + && m_anchorEdges == other.m_anchorEdges + && m_leftOffset == other.m_leftOffset && m_rightOffset == other.m_rightOffset && m_topOffset == other.m_topOffset && m_bottomOffset == other.m_bottomOffset @@ -180,7 +182,7 @@ public: bool operator!=(const StickyPositionViewportConstraints& other) const { return !(*this == other); } private: - virtual ConstraintType constraintType() const override { return StickyPositionConstraint; }; + ConstraintType constraintType() const override { return StickyPositionConstraint; }; float m_leftOffset; float m_rightOffset; @@ -193,6 +195,7 @@ private: FloatPoint m_layerPositionAtLastLayout; }; -} // namespace WebCore +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const FixedPositionViewportConstraints&); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, const StickyPositionViewportConstraints&); -#endif // ScrollingConstraints_h +} // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp b/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp index 93dfb0cec..ff34266a7 100644 --- a/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp +++ b/Source/WebCore/page/scrolling/ScrollingCoordinator.cpp @@ -28,6 +28,7 @@ #include "ScrollingCoordinator.h" #include "Document.h" +#include "EventNames.h" #include "FrameView.h" #include "GraphicsLayer.h" #include "IntRect.h" @@ -36,35 +37,38 @@ #include "PlatformWheelEvent.h" #include "PluginViewBase.h" #include "Region.h" +#include "RenderLayerCompositor.h" #include "RenderView.h" #include "ScrollAnimator.h" +#include "Settings.h" +#include "TextStream.h" #include <wtf/MainThread.h> #include <wtf/text/StringBuilder.h> -#if USE(ACCELERATED_COMPOSITING) -#include "RenderLayerCompositor.h" -#endif - #if USE(COORDINATED_GRAPHICS) #include "ScrollingCoordinatorCoordinatedGraphics.h" #endif +#if ENABLE(WEB_REPLAY) +#include "ReplayController.h" +#include <replay/InputCursor.h> +#endif + namespace WebCore { -#if !PLATFORM(MAC) -PassRefPtr<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) +#if !PLATFORM(COCOA) +Ref<ScrollingCoordinator> ScrollingCoordinator::create(Page* page) { #if USE(COORDINATED_GRAPHICS) - return adoptRef(new ScrollingCoordinatorCoordinatedGraphics(page)); + return adoptRef(*new ScrollingCoordinatorCoordinatedGraphics(page)); #endif - return adoptRef(new ScrollingCoordinator(page)); + return adoptRef(*new ScrollingCoordinator(page)); } #endif ScrollingCoordinator::ScrollingCoordinator(Page* page) : m_page(page) - , m_forceSynchronousScrollLayerPositionUpdates(false) { } @@ -76,88 +80,112 @@ ScrollingCoordinator::~ScrollingCoordinator() void ScrollingCoordinator::pageDestroyed() { ASSERT(m_page); - m_page = 0; + m_page = nullptr; } -bool ScrollingCoordinator::coordinatesScrollingForFrameView(FrameView* frameView) const +bool ScrollingCoordinator::coordinatesScrollingForFrameView(const FrameView& frameView) const { ASSERT(isMainThread()); ASSERT(m_page); - // We currently only handle the main frame. - if (!frameView->frame().isMainFrame()) + if (!frameView.frame().isMainFrame() && !m_page->settings().scrollingTreeIncludesFrames()) return false; - // We currently only support composited mode. -#if USE(ACCELERATED_COMPOSITING) RenderView* renderView = m_page->mainFrame().contentRenderer(); if (!renderView) return false; return renderView->usesCompositing(); -#else - return false; -#endif } -Region ScrollingCoordinator::computeNonFastScrollableRegion(const Frame* frame, const IntPoint& frameLocation) const +EventTrackingRegions ScrollingCoordinator::absoluteEventTrackingRegionsForFrame(const Frame& frame) const { - Region nonFastScrollableRegion; - FrameView* frameView = frame->view(); + auto* renderView = frame.contentRenderer(); + if (!renderView || renderView->renderTreeBeingDestroyed()) + return EventTrackingRegions(); + +#if ENABLE(IOS_TOUCH_EVENTS) + // On iOS, we use nonFastScrollableRegion to represent the region covered by elements with touch event handlers. + ASSERT(frame.isMainFrame()); + auto* document = frame.document(); + if (!document) + return EventTrackingRegions(); + return document->eventTrackingRegions(); +#else + auto* frameView = frame.view(); if (!frameView) - return nonFastScrollableRegion; + return EventTrackingRegions(); - IntPoint offset = frameLocation; - offset.moveBy(frameView->frameRect().location()); + Region nonFastScrollableRegion; - if (const FrameView::ScrollableAreaSet* scrollableAreas = frameView->scrollableAreas()) { - for (FrameView::ScrollableAreaSet::const_iterator it = scrollableAreas->begin(), end = scrollableAreas->end(); it != end; ++it) { - ScrollableArea* scrollableArea = *it; -#if USE(ACCELERATED_COMPOSITING) + // FIXME: should ASSERT(!frameView->needsLayout()) here, but need to fix DebugPageOverlays + // to not ask for regions at bad times. + + if (auto* scrollableAreas = frameView->scrollableAreas()) { + for (auto& scrollableArea : *scrollableAreas) { // Composited scrollable areas can be scrolled off the main thread. - if (scrollableArea->usesCompositedScrolling()) + if (scrollableArea->usesAsyncScrolling()) continue; -#endif - IntRect box = scrollableArea->scrollableAreaBoundingBox(); - box.moveBy(offset); + + bool isInsideFixed; + IntRect box = scrollableArea->scrollableAreaBoundingBox(&isInsideFixed); + if (isInsideFixed) + box = IntRect(frameView->fixedScrollableAreaBoundsInflatedForScrolling(LayoutRect(box))); + nonFastScrollableRegion.unite(box); } } - for (auto it = frameView->children().begin(), end = frameView->children().end(); it != end; ++it) { - if (!(*it)->isPluginViewBase()) + for (auto& widget : frameView->widgetsInRenderTree()) { + if (!is<PluginViewBase>(*widget)) + continue; + if (!downcast<PluginViewBase>(*widget).wantsWheelEvents()) + continue; + auto* renderWidget = RenderWidget::find(*widget); + if (!renderWidget) continue; - PluginViewBase* pluginViewBase = toPluginViewBase((*it).get()); - if (pluginViewBase->wantsWheelEvents()) - nonFastScrollableRegion.unite(pluginViewBase->frameRect()); + nonFastScrollableRegion.unite(renderWidget->absoluteBoundingBoxRect()); } + + EventTrackingRegions eventTrackingRegions; - for (Frame* subframe = frame->tree().firstChild(); subframe; subframe = subframe->tree().nextSibling()) - nonFastScrollableRegion.unite(computeNonFastScrollableRegion(subframe, offset)); + // FIXME: if we've already accounted for this subframe as a scrollable area, we can avoid recursing into it here. + for (Frame* subframe = frame.tree().firstChild(); subframe; subframe = subframe->tree().nextSibling()) { + auto* subframeView = subframe->view(); + if (!subframeView) + continue; - return nonFastScrollableRegion; -} + EventTrackingRegions subframeRegion = absoluteEventTrackingRegionsForFrame(*subframe); + // Map from the frame document to our document. + IntPoint offset = subframeView->contentsToContainingViewContents(IntPoint()); -unsigned ScrollingCoordinator::computeCurrentWheelEventHandlerCount() -{ - unsigned wheelEventHandlerCount = 0; + // FIXME: this translation ignores non-trival transforms on the frame. + subframeRegion.translate(toIntSize(offset)); + eventTrackingRegions.unite(subframeRegion); + } - for (Frame* frame = &m_page->mainFrame(); frame; frame = frame->tree().traverseNext()) { - if (frame->document()) - wheelEventHandlerCount += frame->document()->wheelEventHandlerCount(); + auto wheelHandlerRegion = frame.document()->absoluteRegionForEventTargets(frame.document()->wheelEventTargets()); + bool wheelHandlerInFixedContent = wheelHandlerRegion.second; + if (wheelHandlerInFixedContent) { + // FIXME: need to handle position:sticky here too. + LayoutRect inflatedWheelHandlerBounds = frameView->fixedScrollableAreaBoundsInflatedForScrolling(LayoutRect(wheelHandlerRegion.first.bounds())); + wheelHandlerRegion.first.unite(enclosingIntRect(inflatedWheelHandlerBounds)); } + + nonFastScrollableRegion.unite(wheelHandlerRegion.first); + + // FIXME: If this is not the main frame, we could clip the region to the frame's bounds. + eventTrackingRegions.uniteSynchronousRegion(eventNames().wheelEvent, nonFastScrollableRegion); - return wheelEventHandlerCount; + return eventTrackingRegions; +#endif } -void ScrollingCoordinator::frameViewWheelEventHandlerCountChanged(FrameView* frameView) +EventTrackingRegions ScrollingCoordinator::absoluteEventTrackingRegions() const { - ASSERT(isMainThread()); - ASSERT(m_page); - - recomputeWheelEventHandlerCountForFrameView(frameView); + return absoluteEventTrackingRegionsForFrame(m_page->mainFrame()); } -void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* frameView) +void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView& frameView) { ASSERT(isMainThread()); ASSERT(m_page); @@ -165,10 +193,10 @@ void ScrollingCoordinator::frameViewHasSlowRepaintObjectsDidChange(FrameView* fr if (!coordinatesScrollingForFrameView(frameView)) return; - updateSynchronousScrollingReasons(); + updateSynchronousScrollingReasons(frameView); } -void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView) +void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView& frameView) { ASSERT(isMainThread()); ASSERT(m_page); @@ -176,75 +204,80 @@ void ScrollingCoordinator::frameViewFixedObjectsDidChange(FrameView* frameView) if (!coordinatesScrollingForFrameView(frameView)) return; - updateSynchronousScrollingReasons(); + updateSynchronousScrollingReasons(frameView); } -#if USE(ACCELERATED_COMPOSITING) -GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea* scrollableArea) +GraphicsLayer* ScrollingCoordinator::scrollLayerForScrollableArea(ScrollableArea& scrollableArea) { - return scrollableArea->layerForScrolling(); + return scrollableArea.layerForScrolling(); } -GraphicsLayer* ScrollingCoordinator::horizontalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea) +GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView& frameView) { - return scrollableArea->layerForHorizontalScrollbar(); -} - -GraphicsLayer* ScrollingCoordinator::verticalScrollbarLayerForScrollableArea(ScrollableArea* scrollableArea) -{ - return scrollableArea->layerForVerticalScrollbar(); -} -#endif - -GraphicsLayer* ScrollingCoordinator::scrollLayerForFrameView(FrameView* frameView) -{ -#if USE(ACCELERATED_COMPOSITING) - if (RenderView* renderView = frameView->frame().contentRenderer()) + if (RenderView* renderView = frameView.frame().contentRenderer()) return renderView->compositor().scrollLayer(); - return 0; -#else - UNUSED_PARAM(frameView); - return 0; -#endif + return nullptr; } -GraphicsLayer* ScrollingCoordinator::headerLayerForFrameView(FrameView* frameView) +GraphicsLayer* ScrollingCoordinator::headerLayerForFrameView(FrameView& frameView) { -#if USE(ACCELERATED_COMPOSITING) && ENABLE(RUBBER_BANDING) - if (RenderView* renderView = frameView->frame().contentRenderer()) - renderView->compositor().headerLayer(); - return 0; +#if ENABLE(RUBBER_BANDING) + if (RenderView* renderView = frameView.frame().contentRenderer()) + return renderView->compositor().headerLayer(); + return nullptr; #else UNUSED_PARAM(frameView); - return 0; + return nullptr; #endif } -GraphicsLayer* ScrollingCoordinator::footerLayerForFrameView(FrameView* frameView) +GraphicsLayer* ScrollingCoordinator::footerLayerForFrameView(FrameView& frameView) { -#if USE(ACCELERATED_COMPOSITING) && ENABLE(RUBBER_BANDING) - if (RenderView* renderView = frameView->frame().contentRenderer()) +#if ENABLE(RUBBER_BANDING) + if (RenderView* renderView = frameView.frame().contentRenderer()) return renderView->compositor().footerLayer(); - return 0; + return nullptr; #else UNUSED_PARAM(frameView); - return 0; + return nullptr; #endif } -GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView* frameView) +GraphicsLayer* ScrollingCoordinator::counterScrollingLayerForFrameView(FrameView& frameView) { -#if USE(ACCELERATED_COMPOSITING) - if (RenderView* renderView = frameView->frame().contentRenderer()) + if (RenderView* renderView = frameView.frame().contentRenderer()) return renderView->compositor().fixedRootBackgroundLayer(); - return 0; + return nullptr; +} + +GraphicsLayer* ScrollingCoordinator::insetClipLayerForFrameView(FrameView& frameView) +{ + if (RenderView* renderView = frameView.frame().contentRenderer()) + return renderView->compositor().clipLayer(); + return nullptr; +} + +GraphicsLayer* ScrollingCoordinator::contentShadowLayerForFrameView(FrameView& frameView) +{ +#if ENABLE(RUBBER_BANDING) + if (RenderView* renderView = frameView.frame().contentRenderer()) + return renderView->compositor().layerForContentShadow(); + + return nullptr; #else UNUSED_PARAM(frameView); - return 0; + return nullptr; #endif } -void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView) +GraphicsLayer* ScrollingCoordinator::rootContentLayerForFrameView(FrameView& frameView) +{ + if (RenderView* renderView = frameView.frame().contentRenderer()) + return renderView->compositor().rootContentLayer(); + return nullptr; +} + +void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView& frameView) { ASSERT(isMainThread()); ASSERT(m_page); @@ -253,11 +286,10 @@ void ScrollingCoordinator::frameViewRootLayerDidChange(FrameView* frameView) return; frameViewLayoutUpdated(frameView); - recomputeWheelEventHandlerCountForFrameView(frameView); - updateSynchronousScrollingReasons(); + updateSynchronousScrollingReasons(frameView); } -#if PLATFORM(MAC) +#if PLATFORM(COCOA) void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase) { ASSERT(isMainThread()); @@ -269,57 +301,58 @@ void ScrollingCoordinator::handleWheelEventPhase(PlatformWheelEventPhase phase) if (!frameView) return; - frameView->scrollAnimator()->handleWheelEventPhase(phase); + frameView->scrollAnimator().handleWheelEventPhase(phase); } #endif -bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(FrameView* frameView) const +bool ScrollingCoordinator::hasVisibleSlowRepaintViewportConstrainedObjects(const FrameView& frameView) const { - const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView->viewportConstrainedObjects(); + const FrameView::ViewportConstrainedObjectSet* viewportConstrainedObjects = frameView.viewportConstrainedObjects(); if (!viewportConstrainedObjects) return false; -#if USE(ACCELERATED_COMPOSITING) - for (FrameView::ViewportConstrainedObjectSet::const_iterator it = viewportConstrainedObjects->begin(), end = viewportConstrainedObjects->end(); it != end; ++it) { - RenderObject* viewportConstrainedObject = *it; - if (!viewportConstrainedObject->isBoxModelObject() || !viewportConstrainedObject->hasLayer()) + for (auto& viewportConstrainedObject : *viewportConstrainedObjects) { + if (!is<RenderBoxModelObject>(*viewportConstrainedObject) || !viewportConstrainedObject->hasLayer()) return true; - RenderLayer* layer = toRenderBoxModelObject(viewportConstrainedObject)->layer(); + RenderLayer& layer = *downcast<RenderBoxModelObject>(*viewportConstrainedObject).layer(); // Any explicit reason that a fixed position element is not composited shouldn't cause slow scrolling. - if (!layer->isComposited() && layer->viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason) + if (!layer.isComposited() && layer.viewportConstrainedNotCompositedReason() == RenderLayer::NoNotCompositedReason) return true; } return false; -#else - return viewportConstrainedObjects->size(); -#endif } -SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons() const +SynchronousScrollingReasons ScrollingCoordinator::synchronousScrollingReasons(const FrameView& frameView) const { - FrameView* frameView = m_page->mainFrame().view(); - if (!frameView) - return static_cast<SynchronousScrollingReasons>(0); - SynchronousScrollingReasons synchronousScrollingReasons = (SynchronousScrollingReasons)0; if (m_forceSynchronousScrollLayerPositionUpdates) synchronousScrollingReasons |= ForcedOnMainThread; - if (frameView->hasSlowRepaintObjects()) +#if ENABLE(WEB_REPLAY) + InputCursor& cursor = m_page->replayController().activeInputCursor(); + if (cursor.isCapturing() || cursor.isReplaying()) + synchronousScrollingReasons |= ForcedOnMainThread; +#endif + if (frameView.hasSlowRepaintObjects()) synchronousScrollingReasons |= HasSlowRepaintObjects; - if (!supportsFixedPositionLayers() && frameView->hasViewportConstrainedObjects()) + if (!supportsFixedPositionLayers() && frameView.hasViewportConstrainedObjects()) synchronousScrollingReasons |= HasViewportConstrainedObjectsWithoutSupportingFixedLayers; if (supportsFixedPositionLayers() && hasVisibleSlowRepaintViewportConstrainedObjects(frameView)) synchronousScrollingReasons |= HasNonLayerViewportConstrainedObjects; - if (m_page->mainFrame().document() && m_page->mainFrame().document()->isImageDocument()) + if (frameView.frame().mainFrame().document() && frameView.frame().document()->isImageDocument()) synchronousScrollingReasons |= IsImageDocument; return synchronousScrollingReasons; } -void ScrollingCoordinator::updateSynchronousScrollingReasons() +void ScrollingCoordinator::updateSynchronousScrollingReasons(const FrameView& frameView) { - setSynchronousScrollingReasons(synchronousScrollingReasons()); + // FIXME: Once we support async scrolling of iframes, we'll have to track the synchronous scrolling + // reasons per frame (maybe on scrolling tree nodes). + if (!frameView.frame().isMainFrame()) + return; + + setSynchronousScrollingReasons(synchronousScrollingReasons(frameView)); } void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool forceSynchronousScrollLayerPositionUpdates) @@ -328,9 +361,27 @@ void ScrollingCoordinator::setForceSynchronousScrollLayerPositionUpdates(bool fo return; m_forceSynchronousScrollLayerPositionUpdates = forceSynchronousScrollLayerPositionUpdates; - updateSynchronousScrollingReasons(); + if (FrameView* frameView = m_page->mainFrame().view()) + updateSynchronousScrollingReasons(*frameView); } +bool ScrollingCoordinator::shouldUpdateScrollLayerPositionSynchronously(const FrameView& frameView) const +{ + if (&frameView == m_page->mainFrame().view()) + return synchronousScrollingReasons(frameView); + + return true; +} + +#if ENABLE(WEB_REPLAY) +void ScrollingCoordinator::replaySessionStateDidChange() +{ + // FIXME: Once we support async scrolling of iframes, this should go through all subframes. + if (FrameView* frameView = m_page->mainFrame().view()) + updateSynchronousScrollingReasons(*frameView); +} +#endif + ScrollingNodeID ScrollingCoordinator::uniqueScrollLayerID() { static ScrollingNodeID uniqueScrollLayerID = 1; @@ -347,15 +398,15 @@ String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScroll StringBuilder stringBuilder; if (reasons & ScrollingCoordinator::ForcedOnMainThread) - stringBuilder.append("Forced on main thread, "); + stringBuilder.appendLiteral("Forced on main thread, "); if (reasons & ScrollingCoordinator::HasSlowRepaintObjects) - stringBuilder.append("Has slow repaint objects, "); + stringBuilder.appendLiteral("Has slow repaint objects, "); if (reasons & ScrollingCoordinator::HasViewportConstrainedObjectsWithoutSupportingFixedLayers) - stringBuilder.append("Has viewport constrained objects without supporting fixed layers, "); + stringBuilder.appendLiteral("Has viewport constrained objects without supporting fixed layers, "); if (reasons & ScrollingCoordinator::HasNonLayerViewportConstrainedObjects) - stringBuilder.append("Has non-layer viewport-constrained objects, "); + stringBuilder.appendLiteral("Has non-layer viewport-constrained objects, "); if (reasons & ScrollingCoordinator::IsImageDocument) - stringBuilder.append("Is image document, "); + stringBuilder.appendLiteral("Is image document, "); if (stringBuilder.length()) stringBuilder.resize(stringBuilder.length() - 2); @@ -364,7 +415,45 @@ String ScrollingCoordinator::synchronousScrollingReasonsAsText(SynchronousScroll String ScrollingCoordinator::synchronousScrollingReasonsAsText() const { - return synchronousScrollingReasonsAsText(synchronousScrollingReasons()); + if (FrameView* frameView = m_page->mainFrame().view()) + return synchronousScrollingReasonsAsText(synchronousScrollingReasons(*frameView)); + + return String(); +} + +TextStream& operator<<(TextStream& ts, ScrollingNodeType nodeType) +{ + switch (nodeType) { + case FrameScrollingNode: + ts << "frame-scrolling"; + break; + case OverflowScrollingNode: + ts << "overflow-scrolling"; + break; + case FixedNode: + ts << "fixed"; + break; + case StickyNode: + ts << "sticky"; + break; + } + return ts; +} + +TextStream& operator<<(TextStream& ts, ScrollingLayerPositionAction action) +{ + switch (action) { + case ScrollingLayerPositionAction::Set: + ts << "set"; + break; + case ScrollingLayerPositionAction::SetApproximate: + ts << "set approximate"; + break; + case ScrollingLayerPositionAction::Sync: + ts << "sync"; + break; + } + return ts; } } // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinator.h b/Source/WebCore/page/scrolling/ScrollingCoordinator.h index 0e05d69fd..eb6dabf97 100644 --- a/Source/WebCore/page/scrolling/ScrollingCoordinator.h +++ b/Source/WebCore/page/scrolling/ScrollingCoordinator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 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 @@ -23,15 +23,18 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScrollingCoordinator_h -#define ScrollingCoordinator_h +#pragma once +#include "EventTrackingRegions.h" #include "IntRect.h" #include "LayoutRect.h" #include "PlatformWheelEvent.h" -#include "RenderObject.h" +#include "ScrollSnapOffsetsInfo.h" #include "ScrollTypes.h" #include <wtf/Forward.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/TypeCasts.h> +#include <wtf/Variant.h> #if ENABLE(ASYNC_SCROLLING) #include <wtf/HashMap.h> @@ -39,8 +42,8 @@ #include <wtf/Threading.h> #endif -#if PLATFORM(MAC) -#include <wtf/RetainPtr.h> +#if ENABLE(CSS_SCROLL_SNAP) +#include "AxisScrollSnapOffsets.h" #endif namespace WebCore { @@ -48,7 +51,7 @@ namespace WebCore { typedef unsigned SynchronousScrollingReasons; typedef uint64_t ScrollingNodeID; -enum ScrollingNodeType { ScrollingNode, FixedNode, StickyNode }; +enum ScrollingNodeType { FrameScrollingNode, OverflowScrollingNode, FixedNode, StickyNode }; class Document; class Frame; @@ -57,15 +60,17 @@ class GraphicsLayer; class Page; class Region; class ScrollableArea; +class TextStream; class ViewportConstraints; #if ENABLE(ASYNC_SCROLLING) class ScrollingTree; #endif -enum SetOrSyncScrollingLayerPosition { - SetScrollingLayerPosition, - SyncScrollingLayerPosition +enum class ScrollingLayerPositionAction { + Set, + SetApproximate, + Sync }; struct ScrollableAreaParameters { @@ -101,114 +106,145 @@ struct ScrollableAreaParameters { class ScrollingCoordinator : public ThreadSafeRefCounted<ScrollingCoordinator> { public: - static PassRefPtr<ScrollingCoordinator> create(Page*); + static Ref<ScrollingCoordinator> create(Page*); virtual ~ScrollingCoordinator(); - virtual void pageDestroyed(); + WEBCORE_EXPORT virtual void pageDestroyed(); virtual bool isAsyncScrollingCoordinator() const { return false; } virtual bool isRemoteScrollingCoordinator() const { return false; } // Return whether this scrolling coordinator handles scrolling for the given frame view. - bool coordinatesScrollingForFrameView(FrameView*) const; + virtual bool coordinatesScrollingForFrameView(const FrameView&) const; // Should be called whenever the given frame view has been laid out. - virtual void frameViewLayoutUpdated(FrameView*) { } + virtual void frameViewLayoutUpdated(FrameView&) { } - // Should be called whenever a wheel event handler is added or removed in the - // frame view's underlying document. - void frameViewWheelEventHandlerCountChanged(FrameView*); + using LayoutViewportOriginOrOverrideRect = WTF::Variant<std::optional<FloatPoint>, std::optional<FloatRect>>; + virtual void reconcileScrollingState(FrameView&, const FloatPoint&, const LayoutViewportOriginOrOverrideRect&, bool /* programmaticScroll */, bool /* inStableState*/, ScrollingLayerPositionAction) { } // Should be called whenever the slow repaint objects counter changes between zero and one. - void frameViewHasSlowRepaintObjectsDidChange(FrameView*); + void frameViewHasSlowRepaintObjectsDidChange(FrameView&); // Should be called whenever the set of fixed objects changes. - void frameViewFixedObjectsDidChange(FrameView*); + void frameViewFixedObjectsDidChange(FrameView&); + + // Called whenever the non-fast scrollable region changes for reasons other than layout. + virtual void frameViewEventTrackingRegionsChanged(FrameView&) { } // Should be called whenever the root layer for the given frame view changes. - virtual void frameViewRootLayerDidChange(FrameView*); + virtual void frameViewRootLayerDidChange(FrameView&); // Return whether this scrolling coordinator can keep fixed position layers fixed to their // containers while scrolling. virtual bool supportsFixedPositionLayers() const { return false; } -#if PLATFORM(MAC) +#if PLATFORM(COCOA) // Dispatched by the scrolling tree during handleWheelEvent. This is required as long as scrollbars are painted on the main thread. void handleWheelEventPhase(PlatformWheelEventPhase); #endif +#if ENABLE(WEB_REPLAY) + // Called when the page transitions between executing normally and deterministically. + void replaySessionStateDidChange(); +#endif + // Force all scroll layer position updates to happen on the main thread. - void setForceSynchronousScrollLayerPositionUpdates(bool); + WEBCORE_EXPORT void setForceSynchronousScrollLayerPositionUpdates(bool); // These virtual functions are currently unique to the threaded scrolling architecture. // Their meaningful implementations are in ScrollingCoordinatorMac. virtual void commitTreeStateIfNeeded() { } - virtual bool requestScrollPositionUpdate(FrameView*, const IntPoint&) { return false; } - virtual bool handleWheelEvent(FrameView*, const PlatformWheelEvent&) { return true; } + virtual bool requestScrollPositionUpdate(FrameView&, const IntPoint&) { return false; } + virtual bool handleWheelEvent(FrameView&, const PlatformWheelEvent&) { return true; } virtual ScrollingNodeID attachToStateTree(ScrollingNodeType, ScrollingNodeID newNodeID, ScrollingNodeID /*parentID*/) { return newNodeID; } virtual void detachFromStateTree(ScrollingNodeID) { } virtual void clearStateTree() { } virtual void updateViewportConstrainedNode(ScrollingNodeID, const ViewportConstraints&, GraphicsLayer*) { } - virtual void updateScrollingNode(ScrollingNodeID, GraphicsLayer* /*scrollLayer*/, GraphicsLayer* /*counterScrollingLayer*/) { } - virtual void syncChildPositions(const LayoutRect&) { } + + struct ScrollingGeometry { + FloatSize scrollableAreaSize; + FloatSize contentSize; + FloatSize reachableContentSize; // Smaller than contentSize when overflow is hidden on one axis. + FloatPoint scrollPosition; + IntPoint scrollOrigin; +#if ENABLE(CSS_SCROLL_SNAP) + Vector<LayoutUnit> horizontalSnapOffsets; + Vector<LayoutUnit> verticalSnapOffsets; + Vector<ScrollOffsetRange<LayoutUnit>> horizontalSnapOffsetRanges; + Vector<ScrollOffsetRange<LayoutUnit>> verticalSnapOffsetRanges; + unsigned currentHorizontalSnapPointIndex; + unsigned currentVerticalSnapPointIndex; +#endif + }; + + virtual void updateFrameScrollingNode(ScrollingNodeID, GraphicsLayer* /*scrollLayer*/, GraphicsLayer* /*scrolledContentsLayer*/, GraphicsLayer* /*counterScrollingLayer*/, GraphicsLayer* /*insetClipLayer*/, const ScrollingGeometry* = nullptr) { } + virtual void updateOverflowScrollingNode(ScrollingNodeID, GraphicsLayer* /*scrollLayer*/, GraphicsLayer* /*scrolledContentsLayer*/, const ScrollingGeometry* = nullptr) { } + virtual void reconcileViewportConstrainedLayerPositions(const LayoutRect&, ScrollingLayerPositionAction) { } virtual String scrollingStateTreeAsText() const; virtual bool isRubberBandInProgress() const { return false; } + virtual bool isScrollSnapInProgress() const { return false; } + virtual void updateScrollSnapPropertiesWithFrameView(const FrameView&) { } virtual void setScrollPinningBehavior(ScrollPinningBehavior) { } // Generated a unique id for scroll layers. ScrollingNodeID uniqueScrollLayerID(); enum MainThreadScrollingReasonFlags { - ForcedOnMainThread = 1 << 0, - HasSlowRepaintObjects = 1 << 1, - HasViewportConstrainedObjectsWithoutSupportingFixedLayers = 1 << 2, - HasNonLayerViewportConstrainedObjects = 1 << 3, - IsImageDocument = 1 << 4 + ForcedOnMainThread = 1 << 0, + HasSlowRepaintObjects = 1 << 1, + HasViewportConstrainedObjectsWithoutSupportingFixedLayers = 1 << 2, + HasNonLayerViewportConstrainedObjects = 1 << 3, + IsImageDocument = 1 << 4 }; - SynchronousScrollingReasons synchronousScrollingReasons() const; - bool shouldUpdateScrollLayerPositionSynchronously() const { return synchronousScrollingReasons(); } + SynchronousScrollingReasons synchronousScrollingReasons(const FrameView&) const; + bool shouldUpdateScrollLayerPositionSynchronously(const FrameView&) const; - virtual void willDestroyScrollableArea(ScrollableArea*) { } - virtual void scrollableAreaScrollLayerDidChange(ScrollableArea*) { } - virtual void scrollableAreaScrollbarLayerDidChange(ScrollableArea*, ScrollbarOrientation) { } - virtual void setLayerIsContainerForFixedPositionLayers(GraphicsLayer*, bool) { } + virtual void willDestroyScrollableArea(ScrollableArea&) { } + virtual void scrollableAreaScrollLayerDidChange(ScrollableArea&) { } + virtual void scrollableAreaScrollbarLayerDidChange(ScrollableArea&, ScrollbarOrientation) { } static String synchronousScrollingReasonsAsText(SynchronousScrollingReasons); String synchronousScrollingReasonsAsText() const; - Region computeNonFastScrollableRegion(const Frame*, const IntPoint& frameLocation) const; + EventTrackingRegions absoluteEventTrackingRegions() const; + virtual void updateExpectsWheelEventTestTriggerWithFrameView(const FrameView&) { } protected: explicit ScrollingCoordinator(Page*); -#if USE(ACCELERATED_COMPOSITING) - static GraphicsLayer* scrollLayerForScrollableArea(ScrollableArea*); - static GraphicsLayer* horizontalScrollbarLayerForScrollableArea(ScrollableArea*); - static GraphicsLayer* verticalScrollbarLayerForScrollableArea(ScrollableArea*); -#endif + static GraphicsLayer* scrollLayerForScrollableArea(ScrollableArea&); - unsigned computeCurrentWheelEventHandlerCount(); - GraphicsLayer* scrollLayerForFrameView(FrameView*); - GraphicsLayer* counterScrollingLayerForFrameView(FrameView*); - GraphicsLayer* headerLayerForFrameView(FrameView*); - GraphicsLayer* footerLayerForFrameView(FrameView*); + GraphicsLayer* scrollLayerForFrameView(FrameView&); + GraphicsLayer* counterScrollingLayerForFrameView(FrameView&); + GraphicsLayer* insetClipLayerForFrameView(FrameView&); + GraphicsLayer* rootContentLayerForFrameView(FrameView&); + GraphicsLayer* contentShadowLayerForFrameView(FrameView&); + GraphicsLayer* headerLayerForFrameView(FrameView&); + GraphicsLayer* footerLayerForFrameView(FrameView&); + + virtual void willCommitTree() { } Page* m_page; // FIXME: ideally this would be a reference but it gets nulled on async teardown. private: - virtual void recomputeWheelEventHandlerCountForFrameView(FrameView*) { } virtual void setSynchronousScrollingReasons(SynchronousScrollingReasons) { } - virtual bool hasVisibleSlowRepaintViewportConstrainedObjects(FrameView*) const; - void updateSynchronousScrollingReasons(); + virtual bool hasVisibleSlowRepaintViewportConstrainedObjects(const FrameView&) const; + void updateSynchronousScrollingReasons(const FrameView&); + + EventTrackingRegions absoluteEventTrackingRegionsForFrame(const Frame&) const; - bool m_forceSynchronousScrollLayerPositionUpdates; + bool m_forceSynchronousScrollLayerPositionUpdates { false }; }; -#define SCROLLING_COORDINATOR_TYPE_CASTS(ToValueTypeName, predicate) \ - TYPE_CASTS_BASE(ToValueTypeName, WebCore::ScrollingCoordinator, value, value->predicate, value.predicate) +WEBCORE_EXPORT TextStream& operator<<(TextStream&, ScrollingNodeType); +WEBCORE_EXPORT TextStream& operator<<(TextStream&, ScrollingLayerPositionAction); } // namespace WebCore -#endif // ScrollingCoordinator_h +#define SPECIALIZE_TYPE_TRAITS_SCROLLING_COORDINATOR(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \ + static bool isType(const WebCore::ScrollingCoordinator& value) { return value.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() diff --git a/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp b/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp new file mode 100644 index 000000000..1fa5674a2 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.cpp @@ -0,0 +1,235 @@ +/* + * 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 "ScrollingMomentumCalculator.h" + +#include "FloatPoint.h" +#include "FloatSize.h" + +namespace WebCore { + +static const Seconds scrollSnapAnimationDuration = 1_s; +static inline float projectedInertialScrollDistance(float initialWheelDelta) +{ + // On macOS 10.10 and earlier, we don't have a platform scrolling momentum calculator, so we instead approximate the scroll destination + // by multiplying the initial wheel delta by a constant factor. By running a few experiments (i.e. logging scroll destination and initial + // wheel delta for many scroll gestures) we determined that this is a reasonable way to approximate where scrolling will take us without + // using _NSScrollingMomentumCalculator. + const static double inertialScrollPredictionFactor = 16.7; + return inertialScrollPredictionFactor * initialWheelDelta; +} + +ScrollingMomentumCalculator::ScrollingMomentumCalculator(const FloatSize& viewportSize, const FloatSize& contentSize, const FloatPoint& initialOffset, const FloatSize& initialDelta, const FloatSize& initialVelocity) + : m_initialDelta(initialDelta) + , m_initialVelocity(initialVelocity) + , m_initialScrollOffset(initialOffset.x(), initialOffset.y()) + , m_viewportSize(viewportSize) + , m_contentSize(contentSize) +{ +} + +void ScrollingMomentumCalculator::setRetargetedScrollOffset(const FloatSize& target) +{ + if (m_retargetedScrollOffset && m_retargetedScrollOffset == target) + return; + + m_retargetedScrollOffset = target; + retargetedScrollOffsetDidChange(); +} + +FloatSize ScrollingMomentumCalculator::predictedDestinationOffset() +{ + float initialOffsetX = clampTo<float>(m_initialScrollOffset.width() + projectedInertialScrollDistance(m_initialDelta.width()), 0, m_contentSize.width() - m_viewportSize.width()); + float initialOffsetY = clampTo<float>(m_initialScrollOffset.height() + projectedInertialScrollDistance(m_initialDelta.height()), 0, m_contentSize.height() - m_viewportSize.height()); + return { initialOffsetX, initialOffsetY }; +} + +#if !HAVE(NSSCROLLING_FILTERS) + +std::unique_ptr<ScrollingMomentumCalculator> ScrollingMomentumCalculator::create(const FloatSize& viewportSize, const FloatSize& contentSize, const FloatPoint& initialOffset, const FloatSize& initialDelta, const FloatSize& initialVelocity) +{ + return std::make_unique<BasicScrollingMomentumCalculator>(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity); +} + +void ScrollingMomentumCalculator::setPlatformMomentumScrollingPredictionEnabled(bool) +{ +} + +#endif + +BasicScrollingMomentumCalculator::BasicScrollingMomentumCalculator(const FloatSize& viewportSize, const FloatSize& contentSize, const FloatPoint& initialOffset, const FloatSize& initialDelta, const FloatSize& initialVelocity) + : ScrollingMomentumCalculator(viewportSize, contentSize, initialOffset, initialDelta, initialVelocity) +{ +} + +FloatSize BasicScrollingMomentumCalculator::linearlyInterpolatedOffsetAtProgress(float progress) +{ + return m_initialScrollOffset + progress * (retargetedScrollOffset() - m_initialScrollOffset); +} + +FloatSize BasicScrollingMomentumCalculator::cubicallyInterpolatedOffsetAtProgress(float progress) const +{ + ASSERT(!m_forceLinearAnimationCurve); + FloatSize interpolatedPoint; + for (int i = 0; i < 4; ++i) + interpolatedPoint += std::pow(progress, i) * m_snapAnimationCurveCoefficients[i]; + + return interpolatedPoint; +} + +FloatPoint BasicScrollingMomentumCalculator::scrollOffsetAfterElapsedTime(Seconds elapsedTime) +{ + if (m_momentumCalculatorRequiresInitialization) { + initializeSnapProgressCurve(); + initializeInterpolationCoefficientsIfNecessary(); + m_momentumCalculatorRequiresInitialization = false; + } + + float progress = animationProgressAfterElapsedTime(elapsedTime); + auto offsetAsSize = m_forceLinearAnimationCurve ? linearlyInterpolatedOffsetAtProgress(progress) : cubicallyInterpolatedOffsetAtProgress(progress); + return FloatPoint(offsetAsSize.width(), offsetAsSize.height()); +} + +Seconds BasicScrollingMomentumCalculator::animationDuration() +{ + return scrollSnapAnimationDuration; +} + +/** + * Computes and sets coefficients required for interpolated snapping when scrolling in 2 dimensions, given + * initial conditions (the initial and target vectors, along with the initial wheel delta as a vector). The + * path is a cubic Bezier curve of the form p(s) = INITIAL + (C_1 * s) + (C_2 * s^2) + (C_3 * s^3) where each + * C_i is a 2D vector and INITIAL is the vector representing the initial scroll offset. s is a real in the + * interval [0, 1] indicating the "progress" of the curve (i.e. how much of the curve has been traveled). + * + * The curve has 4 control points, the first and last of which are the initial and target points, respectively. + * The distances between adjacent control points are constrained to be the same, making the convex hull an + * isosceles trapezoid with 3 sides of equal length. Additionally, the vector from the first control point to + * the second points in the same direction as the initial scroll delta. These constraints ensure two properties: + * 1. The direction of the snap animation at s=0 will be equal to the direction of the initial scroll delta. + * 2. Points at regular intervals of s will be evenly spread out. + * + * If the initial scroll direction is orthogonal to or points in the opposite direction as the vector from the + * initial point to the target point, initialization returns early and sets the curve to animate directly to the + * snap point without cubic interpolation. + * + * FIXME: This should be refactored to use UnitBezier. + */ +void BasicScrollingMomentumCalculator::initializeInterpolationCoefficientsIfNecessary() +{ + m_forceLinearAnimationCurve = true; + float initialDeltaMagnitude = m_initialDelta.diagonalLength(); + if (initialDeltaMagnitude < 1) { + // The initial wheel delta is so insignificant that we're better off considering this to have the same effect as finishing a scroll gesture with no momentum. + // Thus, cubic interpolation isn't needed here. + return; + } + + FloatSize startToEndVector = retargetedScrollOffset() - m_initialScrollOffset; + float startToEndDistance = startToEndVector.diagonalLength(); + if (!startToEndDistance) { + // The start and end positions are the same, so we shouldn't try to interpolate a path. + return; + } + + float cosTheta = (m_initialDelta.width() * startToEndVector.width() + m_initialDelta.height() * startToEndVector.height()) / (initialDeltaMagnitude * startToEndDistance); + if (cosTheta <= 0) { + // It's possible that the user is not scrolling towards the target snap offset (for instance, scrolling against a corner when 2D scroll snapping). + // In this case, just let the scroll offset animate to the target without computing a cubic curve. + return; + } + + float sideLength = startToEndDistance / (2.0f * cosTheta + 1.0f); + FloatSize controlVector1 = m_initialScrollOffset + sideLength * m_initialDelta / initialDeltaMagnitude; + FloatSize controlVector2 = controlVector1 + (sideLength * startToEndVector / startToEndDistance); + m_snapAnimationCurveCoefficients[0] = m_initialScrollOffset; + m_snapAnimationCurveCoefficients[1] = 3 * (controlVector1 - m_initialScrollOffset); + m_snapAnimationCurveCoefficients[2] = 3 * (m_initialScrollOffset - 2 * controlVector1 + controlVector2); + m_snapAnimationCurveCoefficients[3] = 3 * (controlVector1 - controlVector2) - m_initialScrollOffset + retargetedScrollOffset(); + m_forceLinearAnimationCurve = false; +} + +static const float framesPerSecond = 60.0f; + +/** + * Computes and sets parameters required for tracking the progress of a snap animation curve, interpolated + * or linear. The progress curve s(t) maps time t to progress s; both variables are in the interval [0, 1]. + * The time input t is 0 when the current time is the start of the animation, t = 0, and 1 when the current + * time is at or after the end of the animation, t = m_scrollSnapAnimationDuration. + * + * In this exponential progress model, s(t) = A - A * b^(-kt), where k = 60T is the number of frames in the + * animation (assuming 60 FPS and an animation duration of T) and A, b are reals greater than or equal to 1. + * Also note that we are given the initial progress, a value indicating the portion of the curve which our + * initial scroll delta takes us. This is important when matching the initial speed of the animation to the + * user's initial momentum scrolling speed. Let this initial progress amount equal v_0. I clamp this initial + * progress amount to a minimum or maximum value. + * + * A is referred to as the curve magnitude, while b is referred to as the decay factor. We solve for A and b, + * keeping the following constraints in mind: + * 1. s(0) = 0 + * 2. s(1) = 1 + * 3. s(1/k) = v_0 + * + * First, observe that s(0) = 0 holds for appropriate values of A, b. Solving for the remaining constraints + * yields a nonlinear system of two equations. In lieu of a purely analytical solution, an alternating + * optimization scheme is used to approximate A and b. This technique converges quickly (within 5 iterations + * or so) for appropriate values of v_0. The optimization terminates early when the decay factor changes by + * less than a threshold between one iteration and the next. + */ +void BasicScrollingMomentumCalculator::initializeSnapProgressCurve() +{ + static const int maxNumScrollSnapParameterEstimationIterations = 10; + static const float scrollSnapDecayFactorConvergenceThreshold = 0.001; + static const float initialScrollSnapCurveMagnitude = 1.1; + static const float minScrollSnapInitialProgress = 0.1; + static const float maxScrollSnapInitialProgress = 0.5; + + FloatSize alignmentVector = m_initialDelta * (retargetedScrollOffset() - m_initialScrollOffset); + float initialProgress; + if (alignmentVector.width() + alignmentVector.height() > 0) + initialProgress = clampTo(m_initialDelta.diagonalLength() / (retargetedScrollOffset() - m_initialScrollOffset).diagonalLength(), minScrollSnapInitialProgress, maxScrollSnapInitialProgress); + else + initialProgress = minScrollSnapInitialProgress; + + float previousDecayFactor = 1.0f; + m_snapAnimationCurveMagnitude = initialScrollSnapCurveMagnitude; + for (int i = 0; i < maxNumScrollSnapParameterEstimationIterations; ++i) { + m_snapAnimationDecayFactor = m_snapAnimationCurveMagnitude / (m_snapAnimationCurveMagnitude - initialProgress); + m_snapAnimationCurveMagnitude = 1.0f / (1.0f - std::pow(m_snapAnimationDecayFactor, -framesPerSecond * scrollSnapAnimationDuration.value())); + if (std::abs(m_snapAnimationDecayFactor - previousDecayFactor) < scrollSnapDecayFactorConvergenceThreshold) + break; + + previousDecayFactor = m_snapAnimationDecayFactor; + } +} + +float BasicScrollingMomentumCalculator::animationProgressAfterElapsedTime(Seconds elapsedTime) const +{ + float timeProgress = clampTo<float>(elapsedTime / scrollSnapAnimationDuration, 0, 1); + return std::min(1.0, m_snapAnimationCurveMagnitude * (1.0 - std::pow(m_snapAnimationDecayFactor, -framesPerSecond * scrollSnapAnimationDuration.value() * timeProgress))); +} + +}; // namespace WebCore diff --git a/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h b/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h new file mode 100644 index 000000000..a82afaa66 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingMomentumCalculator.h @@ -0,0 +1,90 @@ +/* + * 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 + +#if ENABLE(CSS_SCROLL_SNAP) + +#include "AxisScrollSnapOffsets.h" +#include "PlatformWheelEvent.h" +#include "ScrollTypes.h" +#include <wtf/Optional.h> +#include <wtf/Seconds.h> + +namespace WebCore { + +class FloatPoint; +class FloatSize; + +class ScrollingMomentumCalculator { +public: + ScrollingMomentumCalculator(const FloatSize& viewportSize, const FloatSize& contentSize, const FloatPoint& initialOffset, const FloatSize& initialDelta, const FloatSize& initialVelocity); + static std::unique_ptr<ScrollingMomentumCalculator> create(const FloatSize& viewportSize, const FloatSize& contentSize, const FloatPoint& initialOffset, const FloatSize& initialDelta, const FloatSize& initialVelocity); + WEBCORE_EXPORT static void setPlatformMomentumScrollingPredictionEnabled(bool); + virtual ~ScrollingMomentumCalculator() { } + + virtual FloatPoint scrollOffsetAfterElapsedTime(Seconds) = 0; + virtual Seconds animationDuration() = 0; + virtual FloatSize predictedDestinationOffset(); + void setRetargetedScrollOffset(const FloatSize&); + +protected: + const FloatSize& retargetedScrollOffset() const { return m_retargetedScrollOffset.value(); } + virtual void retargetedScrollOffsetDidChange() { } + + FloatSize m_initialDelta; + FloatSize m_initialVelocity; + FloatSize m_initialScrollOffset; + FloatSize m_viewportSize; + FloatSize m_contentSize; + +private: + std::optional<FloatSize> m_retargetedScrollOffset; +}; + +class BasicScrollingMomentumCalculator final : public ScrollingMomentumCalculator { +public: + BasicScrollingMomentumCalculator(const FloatSize& viewportSize, const FloatSize& contentSize, const FloatPoint& initialOffset, const FloatSize& initialDelta, const FloatSize& initialVelocity); + +private: + FloatPoint scrollOffsetAfterElapsedTime(Seconds) final; + Seconds animationDuration() final; + void initializeInterpolationCoefficientsIfNecessary(); + void initializeSnapProgressCurve(); + float animationProgressAfterElapsedTime(Seconds) const; + FloatSize linearlyInterpolatedOffsetAtProgress(float progress); + FloatSize cubicallyInterpolatedOffsetAtProgress(float progress) const; + + float m_snapAnimationCurveMagnitude { 0 }; + float m_snapAnimationDecayFactor { 0 }; + FloatSize m_snapAnimationCurveCoefficients[4] { }; + bool m_forceLinearAnimationCurve { false }; + bool m_momentumCalculatorRequiresInitialization { true }; + std::optional<FloatSize> m_predictedDestinationOffset; +}; + +} // namespace WebCore + +#endif // ENABLE(CSS_SCROLL_SNAP) diff --git a/Source/WebCore/page/scrolling/ScrollingStateFixedNode.cpp b/Source/WebCore/page/scrolling/ScrollingStateFixedNode.cpp index cce42eb7e..17d016597 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateFixedNode.cpp +++ b/Source/WebCore/page/scrolling/ScrollingStateFixedNode.cpp @@ -27,17 +27,17 @@ #include "ScrollingStateFixedNode.h" #include "GraphicsLayer.h" +#include "Logging.h" #include "ScrollingStateTree.h" #include "TextStream.h" -#include <wtf/OwnPtr.h> #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) namespace WebCore { -PassOwnPtr<ScrollingStateFixedNode> ScrollingStateFixedNode::create(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) +Ref<ScrollingStateFixedNode> ScrollingStateFixedNode::create(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) { - return adoptPtr(new ScrollingStateFixedNode(stateTree, nodeID)); + return adoptRef(*new ScrollingStateFixedNode(stateTree, nodeID)); } ScrollingStateFixedNode::ScrollingStateFixedNode(ScrollingStateTree& tree, ScrollingNodeID nodeID) @@ -55,9 +55,9 @@ ScrollingStateFixedNode::~ScrollingStateFixedNode() { } -PassOwnPtr<ScrollingStateNode> ScrollingStateFixedNode::clone(ScrollingStateTree& adoptiveTree) +Ref<ScrollingStateNode> ScrollingStateFixedNode::clone(ScrollingStateTree& adoptiveTree) { - return adoptPtr(new ScrollingStateFixedNode(*this, adoptiveTree)); + return adoptRef(*new ScrollingStateFixedNode(*this, adoptiveTree)); } void ScrollingStateFixedNode::updateConstraints(const FixedPositionViewportConstraints& constraints) @@ -69,14 +69,31 @@ void ScrollingStateFixedNode::updateConstraints(const FixedPositionViewportConst setPropertyChanged(ViewportConstraints); } -void ScrollingStateFixedNode::syncLayerPositionForViewportRect(const LayoutRect& viewportRect) +void ScrollingStateFixedNode::reconcileLayerPositionForViewportRect(const LayoutRect& viewportRect, ScrollingLayerPositionAction action) { FloatPoint position = m_constraints.layerPositionForViewportRect(viewportRect); - if (layer().representsGraphicsLayer()) - static_cast<GraphicsLayer*>(layer())->syncPosition(position); + if (layer().representsGraphicsLayer()) { + GraphicsLayer* graphicsLayer = static_cast<GraphicsLayer*>(layer()); + + LOG_WITH_STREAM(Compositing, stream << "ScrollingStateFixedNode::reconcileLayerPositionForViewportRect setting position of layer " << graphicsLayer->primaryLayerID() << " to " << position); + + switch (action) { + case ScrollingLayerPositionAction::Set: + graphicsLayer->setPosition(position); + break; + + case ScrollingLayerPositionAction::SetApproximate: + graphicsLayer->setApproximatePosition(position); + break; + + case ScrollingLayerPositionAction::Sync: + graphicsLayer->syncPosition(position); + break; + } + } } -void ScrollingStateFixedNode::dumpProperties(TextStream& ts, int indent) const +void ScrollingStateFixedNode::dumpProperties(TextStream& ts, int indent, ScrollingStateTreeAsTextBehavior) const { ts << "(" << "Fixed node" << "\n"; diff --git a/Source/WebCore/page/scrolling/ScrollingStateFixedNode.h b/Source/WebCore/page/scrolling/ScrollingStateFixedNode.h index a52d7b6d0..e761b6a73 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateFixedNode.h +++ b/Source/WebCore/page/scrolling/ScrollingStateFixedNode.h @@ -23,8 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScrollingStateFixedNode_h -#define ScrollingStateFixedNode_h +#pragma once #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) @@ -39,9 +38,9 @@ class FixedPositionViewportConstraints; class ScrollingStateFixedNode final : public ScrollingStateNode { public: - static PassOwnPtr<ScrollingStateFixedNode> create(ScrollingStateTree&, ScrollingNodeID); + static Ref<ScrollingStateFixedNode> create(ScrollingStateTree&, ScrollingNodeID); - virtual PassOwnPtr<ScrollingStateNode> clone(ScrollingStateTree&); + Ref<ScrollingStateNode> clone(ScrollingStateTree&) override; virtual ~ScrollingStateFixedNode(); @@ -49,24 +48,22 @@ public: ViewportConstraints = NumStateNodeBits }; - void updateConstraints(const FixedPositionViewportConstraints&); + WEBCORE_EXPORT void updateConstraints(const FixedPositionViewportConstraints&); const FixedPositionViewportConstraints& viewportConstraints() const { return m_constraints; } private: ScrollingStateFixedNode(ScrollingStateTree&, ScrollingNodeID); ScrollingStateFixedNode(const ScrollingStateFixedNode&, ScrollingStateTree&); - virtual void syncLayerPositionForViewportRect(const LayoutRect& viewportRect) override; + void reconcileLayerPositionForViewportRect(const LayoutRect& viewportRect, ScrollingLayerPositionAction) override; - virtual void dumpProperties(TextStream&, int indent) const override; + void dumpProperties(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const override; FixedPositionViewportConstraints m_constraints; }; -SCROLLING_STATE_NODE_TYPE_CASTS(ScrollingStateFixedNode, nodeType() == FixedNode); - } // namespace WebCore -#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) +SPECIALIZE_TYPE_TRAITS_SCROLLING_STATE_NODE(ScrollingStateFixedNode, isFixedNode()) -#endif // ScrollingStateFixedNode_h +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.cpp b/Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.cpp new file mode 100644 index 000000000..6313c1385 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.cpp @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2014, 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 "ScrollingStateFrameScrollingNode.h" + +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) + +#include "ScrollingStateTree.h" +#include "TextStream.h" + +namespace WebCore { + +Ref<ScrollingStateFrameScrollingNode> ScrollingStateFrameScrollingNode::create(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) +{ + return adoptRef(*new ScrollingStateFrameScrollingNode(stateTree, nodeID)); +} + +ScrollingStateFrameScrollingNode::ScrollingStateFrameScrollingNode(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) + : ScrollingStateScrollingNode(stateTree, FrameScrollingNode, nodeID) +{ +} + +ScrollingStateFrameScrollingNode::ScrollingStateFrameScrollingNode(const ScrollingStateFrameScrollingNode& stateNode, ScrollingStateTree& adoptiveTree) + : ScrollingStateScrollingNode(stateNode, adoptiveTree) +#if PLATFORM(MAC) + , m_verticalScrollerImp(stateNode.verticalScrollerImp()) + , m_horizontalScrollerImp(stateNode.horizontalScrollerImp()) +#endif + , m_eventTrackingRegions(stateNode.eventTrackingRegions()) + , m_requestedScrollPosition(stateNode.requestedScrollPosition()) + , m_layoutViewport(stateNode.layoutViewport()) + , m_minLayoutViewportOrigin(stateNode.minLayoutViewportOrigin()) + , m_maxLayoutViewportOrigin(stateNode.maxLayoutViewportOrigin()) + , m_frameScaleFactor(stateNode.frameScaleFactor()) + , m_topContentInset(stateNode.topContentInset()) + , m_headerHeight(stateNode.headerHeight()) + , m_footerHeight(stateNode.footerHeight()) + , m_synchronousScrollingReasons(stateNode.synchronousScrollingReasons()) + , m_behaviorForFixed(stateNode.scrollBehaviorForFixedElements()) + , m_requestedScrollPositionRepresentsProgrammaticScroll(stateNode.requestedScrollPositionRepresentsProgrammaticScroll()) + , m_fixedElementsLayoutRelativeToFrame(stateNode.fixedElementsLayoutRelativeToFrame()) + , m_visualViewportEnabled(stateNode.visualViewportEnabled()) +{ + if (hasChangedProperty(ScrolledContentsLayer)) + setScrolledContentsLayer(stateNode.scrolledContentsLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); + + if (hasChangedProperty(CounterScrollingLayer)) + setCounterScrollingLayer(stateNode.counterScrollingLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); + + if (hasChangedProperty(InsetClipLayer)) + setInsetClipLayer(stateNode.insetClipLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); + + if (hasChangedProperty(ContentShadowLayer)) + setContentShadowLayer(stateNode.contentShadowLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); + + if (hasChangedProperty(HeaderLayer)) + setHeaderLayer(stateNode.headerLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); + + if (hasChangedProperty(FooterLayer)) + setFooterLayer(stateNode.footerLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); +} + +ScrollingStateFrameScrollingNode::~ScrollingStateFrameScrollingNode() +{ +} + +Ref<ScrollingStateNode> ScrollingStateFrameScrollingNode::clone(ScrollingStateTree& adoptiveTree) +{ + return adoptRef(*new ScrollingStateFrameScrollingNode(*this, adoptiveTree)); +} + +void ScrollingStateFrameScrollingNode::setFrameScaleFactor(float scaleFactor) +{ + if (m_frameScaleFactor == scaleFactor) + return; + + m_frameScaleFactor = scaleFactor; + + setPropertyChanged(FrameScaleFactor); +} + +void ScrollingStateFrameScrollingNode::setEventTrackingRegions(const EventTrackingRegions& eventTrackingRegions) +{ + if (m_eventTrackingRegions == eventTrackingRegions) + return; + + m_eventTrackingRegions = eventTrackingRegions; + setPropertyChanged(EventTrackingRegion); +} + +void ScrollingStateFrameScrollingNode::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons) +{ + if (m_synchronousScrollingReasons == reasons) + return; + + m_synchronousScrollingReasons = reasons; + setPropertyChanged(ReasonsForSynchronousScrolling); +} + +void ScrollingStateFrameScrollingNode::setScrollBehaviorForFixedElements(ScrollBehaviorForFixedElements behaviorForFixed) +{ + if (m_behaviorForFixed == behaviorForFixed) + return; + + m_behaviorForFixed = behaviorForFixed; + setPropertyChanged(BehaviorForFixedElements); +} + +void ScrollingStateFrameScrollingNode::setLayoutViewport(const FloatRect& r) +{ + if (m_layoutViewport == r) + return; + + m_layoutViewport = r; + setPropertyChanged(LayoutViewport); +} + +void ScrollingStateFrameScrollingNode::setMinLayoutViewportOrigin(const FloatPoint& p) +{ + if (m_minLayoutViewportOrigin == p) + return; + + m_minLayoutViewportOrigin = p; + setPropertyChanged(MinLayoutViewportOrigin); +} + +void ScrollingStateFrameScrollingNode::setMaxLayoutViewportOrigin(const FloatPoint& p) +{ + if (m_maxLayoutViewportOrigin == p) + return; + + m_maxLayoutViewportOrigin = p; + setPropertyChanged(MaxLayoutViewportOrigin); +} + +void ScrollingStateFrameScrollingNode::setHeaderHeight(int headerHeight) +{ + if (m_headerHeight == headerHeight) + return; + + m_headerHeight = headerHeight; + setPropertyChanged(HeaderHeight); +} + +void ScrollingStateFrameScrollingNode::setFooterHeight(int footerHeight) +{ + if (m_footerHeight == footerHeight) + return; + + m_footerHeight = footerHeight; + setPropertyChanged(FooterHeight); +} + +void ScrollingStateFrameScrollingNode::setTopContentInset(float topContentInset) +{ + if (m_topContentInset == topContentInset) + return; + + m_topContentInset = topContentInset; + setPropertyChanged(TopContentInset); +} + +void ScrollingStateFrameScrollingNode::setScrolledContentsLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_scrolledContentsLayer) + return; + + m_scrolledContentsLayer = layerRepresentation; + setPropertyChanged(ScrolledContentsLayer); +} + +void ScrollingStateFrameScrollingNode::setCounterScrollingLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_counterScrollingLayer) + return; + + m_counterScrollingLayer = layerRepresentation; + setPropertyChanged(CounterScrollingLayer); +} + +void ScrollingStateFrameScrollingNode::setInsetClipLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_insetClipLayer) + return; + + m_insetClipLayer = layerRepresentation; + setPropertyChanged(InsetClipLayer); +} + +void ScrollingStateFrameScrollingNode::setContentShadowLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_contentShadowLayer) + return; + + m_contentShadowLayer = layerRepresentation; + setPropertyChanged(ContentShadowLayer); +} + +void ScrollingStateFrameScrollingNode::setHeaderLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_headerLayer) + return; + + m_headerLayer = layerRepresentation; + setPropertyChanged(HeaderLayer); +} + +void ScrollingStateFrameScrollingNode::setFooterLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_footerLayer) + return; + + m_footerLayer = layerRepresentation; + setPropertyChanged(FooterLayer); +} + +void ScrollingStateFrameScrollingNode::setFixedElementsLayoutRelativeToFrame(bool fixedElementsLayoutRelativeToFrame) +{ + if (fixedElementsLayoutRelativeToFrame == m_fixedElementsLayoutRelativeToFrame) + return; + + m_fixedElementsLayoutRelativeToFrame = fixedElementsLayoutRelativeToFrame; + setPropertyChanged(FixedElementsLayoutRelativeToFrame); +} + +// Only needed while visual viewports are runtime-switchable. +void ScrollingStateFrameScrollingNode::setVisualViewportEnabled(bool visualViewportEnabled) +{ + if (visualViewportEnabled == m_visualViewportEnabled) + return; + + m_visualViewportEnabled = visualViewportEnabled; + setPropertyChanged(VisualViewportEnabled); +} + +#if !PLATFORM(MAC) +void ScrollingStateFrameScrollingNode::setScrollerImpsFromScrollbars(Scrollbar*, Scrollbar*) +{ +} +#endif + +void ScrollingStateFrameScrollingNode::dumpProperties(TextStream& ts, int indent, ScrollingStateTreeAsTextBehavior behavior) const +{ + ts << "(Frame scrolling node" << "\n"; + + ScrollingStateScrollingNode::dumpProperties(ts, indent, behavior); + + if (m_frameScaleFactor != 1) { + writeIndent(ts, indent + 1); + ts << "(frame scale factor " << m_frameScaleFactor << ")\n"; + } + + if (m_visualViewportEnabled) { + writeIndent(ts, indent + 1); + ts << "(layout viewport " << m_layoutViewport << ")\n"; + writeIndent(ts, indent + 1); + ts << "(min layout viewport origin " << m_minLayoutViewportOrigin << ")\n"; + writeIndent(ts, indent + 1); + ts << "(max layout viewport origin " << m_maxLayoutViewportOrigin << ")\n"; + } + + if (m_behaviorForFixed == StickToViewportBounds) { + writeIndent(ts, indent + 1); + ts << "(fixed behavior: stick to viewport)\n"; + } + + if (!m_eventTrackingRegions.asynchronousDispatchRegion.isEmpty()) { + ++indent; + writeIndent(ts, indent); + ts << "(asynchronous event dispatch region"; + ++indent; + for (auto rect : m_eventTrackingRegions.asynchronousDispatchRegion.rects()) { + ts << "\n"; + writeIndent(ts, indent); + ts << rect; + } + ts << ")\n"; + indent -= 2; + } + if (!m_eventTrackingRegions.eventSpecificSynchronousDispatchRegions.isEmpty()) { + for (const auto& synchronousEventRegion : m_eventTrackingRegions.eventSpecificSynchronousDispatchRegions) { + ++indent; + writeIndent(ts, indent); + ts << "(synchronous event dispatch region for event " << synchronousEventRegion.key; + ++indent; + for (auto rect : synchronousEventRegion.value.rects()) { + ts << "\n"; + writeIndent(ts, indent); + ts << rect; + } + ts << ")\n"; + indent -= 2; + } + } + + if (m_synchronousScrollingReasons) { + writeIndent(ts, indent + 1); + ts << "(Scrolling on main thread because: " << ScrollingCoordinator::synchronousScrollingReasonsAsText(m_synchronousScrollingReasons) << ")\n"; + } + + // FIXME: dump more properties. +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.h b/Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.h new file mode 100644 index 000000000..eeaa71fe3 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingStateFrameScrollingNode.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2014, 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 + +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) + +#include "EventTrackingRegions.h" +#include "ScrollTypes.h" +#include "ScrollbarThemeComposite.h" +#include "ScrollingCoordinator.h" +#include "ScrollingStateScrollingNode.h" + +namespace WebCore { + +class Scrollbar; + +class ScrollingStateFrameScrollingNode final : public ScrollingStateScrollingNode { +public: + static Ref<ScrollingStateFrameScrollingNode> create(ScrollingStateTree&, ScrollingNodeID); + + Ref<ScrollingStateNode> clone(ScrollingStateTree&) override; + + virtual ~ScrollingStateFrameScrollingNode(); + + enum ChangedProperty { + FrameScaleFactor = NumScrollingStateNodeBits, + EventTrackingRegion, + ReasonsForSynchronousScrolling, + ScrolledContentsLayer, + CounterScrollingLayer, + InsetClipLayer, + ContentShadowLayer, + HeaderHeight, + FooterHeight, + HeaderLayer, + FooterLayer, + PainterForScrollbar, + BehaviorForFixedElements, + TopContentInset, + FixedElementsLayoutRelativeToFrame, + VisualViewportEnabled, + LayoutViewport, + MinLayoutViewportOrigin, + MaxLayoutViewportOrigin, + }; + + float frameScaleFactor() const { return m_frameScaleFactor; } + WEBCORE_EXPORT void setFrameScaleFactor(float); + + const EventTrackingRegions& eventTrackingRegions() const { return m_eventTrackingRegions; } + WEBCORE_EXPORT void setEventTrackingRegions(const EventTrackingRegions&); + + SynchronousScrollingReasons synchronousScrollingReasons() const { return m_synchronousScrollingReasons; } + WEBCORE_EXPORT void setSynchronousScrollingReasons(SynchronousScrollingReasons); + + ScrollBehaviorForFixedElements scrollBehaviorForFixedElements() const { return m_behaviorForFixed; } + WEBCORE_EXPORT void setScrollBehaviorForFixedElements(ScrollBehaviorForFixedElements); + + FloatRect layoutViewport() const { return m_layoutViewport; }; + WEBCORE_EXPORT void setLayoutViewport(const FloatRect&); + + FloatPoint minLayoutViewportOrigin() const { return m_minLayoutViewportOrigin; } + WEBCORE_EXPORT void setMinLayoutViewportOrigin(const FloatPoint&); + + FloatPoint maxLayoutViewportOrigin() const { return m_maxLayoutViewportOrigin; } + WEBCORE_EXPORT void setMaxLayoutViewportOrigin(const FloatPoint&); + + int headerHeight() const { return m_headerHeight; } + WEBCORE_EXPORT void setHeaderHeight(int); + + int footerHeight() const { return m_footerHeight; } + WEBCORE_EXPORT void setFooterHeight(int); + + float topContentInset() const { return m_topContentInset; } + WEBCORE_EXPORT void setTopContentInset(float); + + const LayerRepresentation& scrolledContentsLayer() const { return m_scrolledContentsLayer; } + WEBCORE_EXPORT void setScrolledContentsLayer(const LayerRepresentation&); + + // This is a layer moved in the opposite direction to scrolling, for example for background-attachment:fixed + const LayerRepresentation& counterScrollingLayer() const { return m_counterScrollingLayer; } + WEBCORE_EXPORT void setCounterScrollingLayer(const LayerRepresentation&); + + // This is a clipping layer that will scroll with the page for all y-delta scroll values between 0 + // and topContentInset(). Once the y-deltas get beyond the content inset point, this layer no longer + // needs to move. If the topContentInset() is 0, this layer does not need to move at all. This is + // only used on the Mac. + const LayerRepresentation& insetClipLayer() const { return m_insetClipLayer; } + WEBCORE_EXPORT void setInsetClipLayer(const LayerRepresentation&); + + const LayerRepresentation& contentShadowLayer() const { return m_contentShadowLayer; } + WEBCORE_EXPORT void setContentShadowLayer(const LayerRepresentation&); + + // The header and footer layers scroll vertically with the page, they should remain fixed when scrolling horizontally. + const LayerRepresentation& headerLayer() const { return m_headerLayer; } + WEBCORE_EXPORT void setHeaderLayer(const LayerRepresentation&); + + // The header and footer layers scroll vertically with the page, they should remain fixed when scrolling horizontally. + const LayerRepresentation& footerLayer() const { return m_footerLayer; } + WEBCORE_EXPORT void setFooterLayer(const LayerRepresentation&); + + bool fixedElementsLayoutRelativeToFrame() const { return m_fixedElementsLayoutRelativeToFrame; } + WEBCORE_EXPORT void setFixedElementsLayoutRelativeToFrame(bool); + + bool visualViewportEnabled() const { return m_visualViewportEnabled; }; + WEBCORE_EXPORT void setVisualViewportEnabled(bool); + +#if PLATFORM(MAC) + NSScrollerImp *verticalScrollerImp() const { return m_verticalScrollerImp.get(); } + NSScrollerImp *horizontalScrollerImp() const { return m_horizontalScrollerImp.get(); } +#endif + void setScrollerImpsFromScrollbars(Scrollbar* verticalScrollbar, Scrollbar* horizontalScrollbar); + + void dumpProperties(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const override; + +private: + ScrollingStateFrameScrollingNode(ScrollingStateTree&, ScrollingNodeID); + ScrollingStateFrameScrollingNode(const ScrollingStateFrameScrollingNode&, ScrollingStateTree&); + + LayerRepresentation m_counterScrollingLayer; + LayerRepresentation m_insetClipLayer; + LayerRepresentation m_scrolledContentsLayer; + LayerRepresentation m_contentShadowLayer; + LayerRepresentation m_headerLayer; + LayerRepresentation m_footerLayer; + +#if PLATFORM(MAC) + RetainPtr<NSScrollerImp> m_verticalScrollerImp; + RetainPtr<NSScrollerImp> m_horizontalScrollerImp; +#endif + + EventTrackingRegions m_eventTrackingRegions; + FloatPoint m_requestedScrollPosition; + + FloatRect m_layoutViewport; + FloatPoint m_minLayoutViewportOrigin; + FloatPoint m_maxLayoutViewportOrigin; + + float m_frameScaleFactor { 1 }; + float m_topContentInset { 0 }; + int m_headerHeight { 0 }; + int m_footerHeight { 0 }; + SynchronousScrollingReasons m_synchronousScrollingReasons { 0 }; + ScrollBehaviorForFixedElements m_behaviorForFixed { StickToDocumentBounds }; + bool m_requestedScrollPositionRepresentsProgrammaticScroll { false }; + bool m_fixedElementsLayoutRelativeToFrame { false }; + bool m_visualViewportEnabled { false }; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_STATE_NODE(ScrollingStateFrameScrollingNode, isFrameScrollingNode()) + +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateNode.cpp b/Source/WebCore/page/scrolling/ScrollingStateNode.cpp index 0b60c5acb..61da58a9d 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateNode.cpp +++ b/Source/WebCore/page/scrolling/ScrollingStateNode.cpp @@ -41,7 +41,7 @@ ScrollingStateNode::ScrollingStateNode(ScrollingNodeType nodeType, ScrollingStat , m_nodeID(nodeID) , m_changedProperties(0) , m_scrollingStateTree(scrollingStateTree) - , m_parent(0) + , m_parent(nullptr) { } @@ -52,7 +52,7 @@ ScrollingStateNode::ScrollingStateNode(const ScrollingStateNode& stateNode, Scro , m_nodeID(stateNode.scrollingNodeID()) , m_changedProperties(stateNode.changedProperties()) , m_scrollingStateTree(adoptiveTree) - , m_parent(0) + , m_parent(nullptr) { if (hasChangedProperty(ScrollLayer)) setLayer(stateNode.layer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); @@ -72,15 +72,16 @@ void ScrollingStateNode::setPropertyChanged(unsigned propertyBit) m_scrollingStateTree.setHasChangedProperties(); } -PassOwnPtr<ScrollingStateNode> ScrollingStateNode::cloneAndReset(ScrollingStateTree& adoptiveTree) +Ref<ScrollingStateNode> ScrollingStateNode::cloneAndReset(ScrollingStateTree& adoptiveTree) { - OwnPtr<ScrollingStateNode> clone = this->clone(adoptiveTree); + auto clone = this->clone(adoptiveTree); // Now that this node is cloned, reset our change properties. resetChangedProperties(); - cloneAndResetChildren(*clone, adoptiveTree); - return clone.release(); + cloneAndResetChildren(clone.get(), adoptiveTree); + + return clone; } void ScrollingStateNode::cloneAndResetChildren(ScrollingStateNode& clone, ScrollingStateTree& adoptiveTree) @@ -88,51 +89,17 @@ void ScrollingStateNode::cloneAndResetChildren(ScrollingStateNode& clone, Scroll if (!m_children) return; - size_t size = m_children->size(); - for (size_t i = 0; i < size; ++i) - clone.appendChild(m_children->at(i)->cloneAndReset(adoptiveTree)); + for (auto& child : *m_children) + clone.appendChild(child->cloneAndReset(adoptiveTree)); } -void ScrollingStateNode::appendChild(PassOwnPtr<ScrollingStateNode> childNode) +void ScrollingStateNode::appendChild(Ref<ScrollingStateNode>&& childNode) { childNode->setParent(this); if (!m_children) - m_children = adoptPtr(new Vector<OwnPtr<ScrollingStateNode>>); - - m_children->append(childNode); -} - -void ScrollingStateNode::removeChild(ScrollingStateNode* node) -{ - if (!m_children) - return; - - size_t index = m_children->find(node); - - // The index will be notFound if the node to remove is a deeper-than-1-level descendant or - // if node is the root state node. - if (index != notFound) { - node->willBeRemovedFromStateTree(); - m_children->remove(index); - return; - } - - size_t size = m_children->size(); - for (size_t i = 0; i < size; ++i) - m_children->at(i)->removeChild(node); -} - -void ScrollingStateNode::willBeRemovedFromStateTree() -{ - scrollingStateTree().didRemoveNode(scrollingNodeID()); - - if (!m_children) - return; - - size_t size = m_children->size(); - for (size_t i = 0; i < size; ++i) - m_children->at(i)->willBeRemovedFromStateTree(); + m_children = std::make_unique<Vector<RefPtr<ScrollingStateNode>>>(); + m_children->append(WTFMove(childNode)); } void ScrollingStateNode::setLayer(const LayerRepresentation& layerRepresentation) @@ -145,18 +112,17 @@ void ScrollingStateNode::setLayer(const LayerRepresentation& layerRepresentation setPropertyChanged(ScrollLayer); } -void ScrollingStateNode::dump(TextStream& ts, int indent) const +void ScrollingStateNode::dump(TextStream& ts, int indent, ScrollingStateTreeAsTextBehavior behavior) const { writeIndent(ts, indent); - dumpProperties(ts, indent); + dumpProperties(ts, indent, behavior); if (m_children) { writeIndent(ts, indent + 1); - size_t size = children()->size(); - ts << "(children " << size << "\n"; + ts << "(children " << children()->size() << "\n"; - for (size_t i = 0; i < size; i++) - m_children->at(i)->dump(ts, indent + 2); + for (auto& child : *m_children) + child->dump(ts, indent + 2, behavior); writeIndent(ts, indent + 1); ts << ")\n"; } @@ -167,9 +133,9 @@ void ScrollingStateNode::dump(TextStream& ts, int indent) const String ScrollingStateNode::scrollingStateTreeAsText() const { - TextStream ts; + TextStream ts(TextStream::LineMode::MultipleLine, TextStream::Formatting::SVGStyleRect); - dump(ts, 0); + dump(ts, 0, ScrollingStateTreeAsTextBehaviorNormal); return ts.release(); } diff --git a/Source/WebCore/page/scrolling/ScrollingStateNode.h b/Source/WebCore/page/scrolling/ScrollingStateNode.h index ca705f3e2..087d61829 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateNode.h +++ b/Source/WebCore/page/scrolling/ScrollingStateNode.h @@ -23,27 +23,31 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScrollingStateNode_h -#define ScrollingStateNode_h +#pragma once #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) #include "GraphicsLayer.h" #include "ScrollingCoordinator.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> #include <wtf/Vector.h> -#if PLATFORM(MAC) -#include <wtf/RetainPtr.h> -#endif - namespace WebCore { class GraphicsLayer; class ScrollingStateTree; class TextStream; +enum ScrollingStateTreeAsTextBehaviorFlags { + ScrollingStateTreeAsTextBehaviorNormal = 0, + ScrollingStateTreeAsTextBehaviorIncludeLayerIDs = 1 << 0, + ScrollingStateTreeAsTextBehaviorIncludeNodeIDs = 1 << 1, + ScrollingStateTreeAsTextBehaviorIncludeLayerPositions = 1 << 2, + ScrollingStateTreeAsTextBehaviorDebug = ScrollingStateTreeAsTextBehaviorIncludeLayerIDs | ScrollingStateTreeAsTextBehaviorIncludeNodeIDs | ScrollingStateTreeAsTextBehaviorIncludeLayerPositions +}; +typedef unsigned ScrollingStateTreeAsTextBehavior; + // Used to allow ScrollingStateNodes to refer to layers in various contexts: // a) Async scrolling, main thread: ScrollingStateNode holds onto a GraphicsLayer, and uses m_layerID // to detect whether that GraphicsLayer's underlying PlatformLayer changed. @@ -75,14 +79,32 @@ public: : m_platformLayer(platformLayer) , m_layerID(0) , m_representation(PlatformLayerRepresentation) - { } + { + retainPlatformLayer(platformLayer); + } LayerRepresentation(GraphicsLayer::PlatformLayerID layerID) : m_graphicsLayer(nullptr) , m_layerID(layerID) , m_representation(PlatformLayerIDRepresentation) - { } - + { + } + + LayerRepresentation(const LayerRepresentation& other) + : m_platformLayer(other.m_platformLayer) + , m_layerID(other.m_layerID) + , m_representation(other.m_representation) + { + if (m_representation == PlatformLayerRepresentation) + retainPlatformLayer(m_platformLayer); + } + + ~LayerRepresentation() + { + if (m_representation == PlatformLayerRepresentation) + releasePlatformLayer(m_platformLayer); + } + operator GraphicsLayer*() const { ASSERT(m_representation == GraphicsLayerRepresentation); @@ -94,14 +116,31 @@ public: ASSERT(m_representation == PlatformLayerRepresentation); return m_platformLayer; } + + GraphicsLayer::PlatformLayerID layerID() const + { + return m_layerID; + } operator GraphicsLayer::PlatformLayerID() const { ASSERT(m_representation != PlatformLayerRepresentation); return m_layerID; } - - bool operator ==(const LayerRepresentation& other) const + + LayerRepresentation& operator=(const LayerRepresentation& other) + { + m_platformLayer = other.m_platformLayer; + m_layerID = other.m_layerID; + m_representation = other.m_representation; + + if (m_representation == PlatformLayerRepresentation) + retainPlatformLayer(m_platformLayer); + + return *this; + } + + bool operator==(const LayerRepresentation& other) const { if (m_representation != other.m_representation) return false; @@ -140,6 +179,9 @@ public: bool representsPlatformLayerID() const { return m_representation == PlatformLayerIDRepresentation; } private: + WEBCORE_EXPORT void retainPlatformLayer(PlatformLayer*); + WEBCORE_EXPORT void releasePlatformLayer(PlatformLayer*); + union { GraphicsLayer* m_graphicsLayer; PlatformLayer *m_platformLayer; @@ -149,15 +191,22 @@ private: Type m_representation; }; -class ScrollingStateNode { +class ScrollingStateNode : public RefCounted<ScrollingStateNode> { + WTF_MAKE_FAST_ALLOCATED; public: ScrollingStateNode(ScrollingNodeType, ScrollingStateTree&, ScrollingNodeID); virtual ~ScrollingStateNode(); ScrollingNodeType nodeType() const { return m_nodeType; } - virtual PassOwnPtr<ScrollingStateNode> clone(ScrollingStateTree& adoptiveTree) = 0; - PassOwnPtr<ScrollingStateNode> cloneAndReset(ScrollingStateTree& adoptiveTree); + bool isFixedNode() const { return m_nodeType == FixedNode; } + bool isStickyNode() const { return m_nodeType == StickyNode; } + bool isScrollingNode() const { return m_nodeType == FrameScrollingNode || m_nodeType == OverflowScrollingNode; } + bool isFrameScrollingNode() const { return m_nodeType == FrameScrollingNode; } + bool isOverflowScrollingNode() const { return m_nodeType == OverflowScrollingNode; } + + virtual Ref<ScrollingStateNode> clone(ScrollingStateTree& adoptiveTree) = 0; + Ref<ScrollingStateNode> cloneAndReset(ScrollingStateTree& adoptiveTree); void cloneAndResetChildren(ScrollingStateNode&, ScrollingStateTree& adoptiveTree); enum { @@ -174,10 +223,10 @@ public: ChangedProperties changedProperties() const { return m_changedProperties; } void setChangedProperties(ChangedProperties changedProperties) { m_changedProperties = changedProperties; } - virtual void syncLayerPositionForViewportRect(const LayoutRect& /*viewportRect*/) { } + virtual void reconcileLayerPositionForViewportRect(const LayoutRect& /*viewportRect*/, ScrollingLayerPositionAction) { } const LayerRepresentation& layer() const { return m_layer; } - void setLayer(const LayerRepresentation&); + WEBCORE_EXPORT void setLayer(const LayerRepresentation&); ScrollingStateTree& scrollingStateTree() const { return m_scrollingStateTree; } @@ -187,10 +236,9 @@ public: void setParent(ScrollingStateNode* parent) { m_parent = parent; } ScrollingNodeID parentNodeID() const { return m_parent ? m_parent->scrollingNodeID() : 0; } - Vector<OwnPtr<ScrollingStateNode>>* children() const { return m_children.get(); } + Vector<RefPtr<ScrollingStateNode>>* children() const { return m_children.get(); } - void appendChild(PassOwnPtr<ScrollingStateNode>); - void removeChild(ScrollingStateNode*); + void appendChild(Ref<ScrollingStateNode>&&); String scrollingStateTreeAsText() const; @@ -198,10 +246,9 @@ protected: ScrollingStateNode(const ScrollingStateNode&, ScrollingStateTree&); private: - void dump(TextStream&, int indent) const; + void dump(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const; - virtual void dumpProperties(TextStream&, int indent) const = 0; - void willBeRemovedFromStateTree(); + virtual void dumpProperties(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const = 0; const ScrollingNodeType m_nodeType; ScrollingNodeID m_nodeID; @@ -210,16 +257,16 @@ private: ScrollingStateTree& m_scrollingStateTree; ScrollingStateNode* m_parent; - OwnPtr<Vector<OwnPtr<ScrollingStateNode>>> m_children; + std::unique_ptr<Vector<RefPtr<ScrollingStateNode>>> m_children; LayerRepresentation m_layer; }; -#define SCROLLING_STATE_NODE_TYPE_CASTS(ToValueTypeName, predicate) \ - TYPE_CASTS_BASE(ToValueTypeName, ScrollingStateNode, value, value->predicate, value.predicate) - } // namespace WebCore -#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) +#define SPECIALIZE_TYPE_TRAITS_SCROLLING_STATE_NODE(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \ + static bool isType(const WebCore::ScrollingStateNode& node) { return node.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() -#endif // ScrollingStateNode_h +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.cpp b/Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.cpp new file mode 100644 index 000000000..346459a71 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.cpp @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#include "config.h" +#include "ScrollingStateOverflowScrollingNode.h" + +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) + +#include "ScrollingStateTree.h" +#include "TextStream.h" + +namespace WebCore { + +Ref<ScrollingStateOverflowScrollingNode> ScrollingStateOverflowScrollingNode::create(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) +{ + return adoptRef(*new ScrollingStateOverflowScrollingNode(stateTree, nodeID)); +} + +ScrollingStateOverflowScrollingNode::ScrollingStateOverflowScrollingNode(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) + : ScrollingStateScrollingNode(stateTree, OverflowScrollingNode, nodeID) +{ +} + +ScrollingStateOverflowScrollingNode::ScrollingStateOverflowScrollingNode(const ScrollingStateOverflowScrollingNode& stateNode, ScrollingStateTree& adoptiveTree) + : ScrollingStateScrollingNode(stateNode, adoptiveTree) +{ + if (hasChangedProperty(ScrolledContentsLayer)) + setScrolledContentsLayer(stateNode.scrolledContentsLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); +} + +ScrollingStateOverflowScrollingNode::~ScrollingStateOverflowScrollingNode() +{ +} + +Ref<ScrollingStateNode> ScrollingStateOverflowScrollingNode::clone(ScrollingStateTree& adoptiveTree) +{ + return adoptRef(*new ScrollingStateOverflowScrollingNode(*this, adoptiveTree)); +} + +void ScrollingStateOverflowScrollingNode::setScrolledContentsLayer(const LayerRepresentation& layerRepresentation) +{ + if (layerRepresentation == m_scrolledContentsLayer) + return; + + m_scrolledContentsLayer = layerRepresentation; + setPropertyChanged(ScrolledContentsLayer); +} + +void ScrollingStateOverflowScrollingNode::dumpProperties(TextStream& ts, int indent, ScrollingStateTreeAsTextBehavior behavior) const +{ + ts << "(" << "Overflow scrolling node" << "\n"; + + ScrollingStateScrollingNode::dumpProperties(ts, indent, behavior); + + if ((behavior & ScrollingStateTreeAsTextBehaviorIncludeLayerIDs) && m_scrolledContentsLayer.layerID()) { + writeIndent(ts, indent + 1); + ts << "(scrolled contents layer " << m_scrolledContentsLayer.layerID() << ")\n"; + } +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.h b/Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.h new file mode 100644 index 000000000..042bcf87d --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingStateOverflowScrollingNode.h @@ -0,0 +1,63 @@ +/* + * 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 + +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) + +#include "ScrollingStateScrollingNode.h" + +namespace WebCore { + +class ScrollingStateOverflowScrollingNode : public ScrollingStateScrollingNode { +public: + static Ref<ScrollingStateOverflowScrollingNode> create(ScrollingStateTree&, ScrollingNodeID); + + Ref<ScrollingStateNode> clone(ScrollingStateTree&) override; + + virtual ~ScrollingStateOverflowScrollingNode(); + + enum ChangedProperty { + ScrolledContentsLayer = NumScrollingStateNodeBits + }; + + // This is a layer with the contents that move. + const LayerRepresentation& scrolledContentsLayer() const { return m_scrolledContentsLayer; } + WEBCORE_EXPORT void setScrolledContentsLayer(const LayerRepresentation&); + + void dumpProperties(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const override; + +private: + ScrollingStateOverflowScrollingNode(ScrollingStateTree&, ScrollingNodeID); + ScrollingStateOverflowScrollingNode(const ScrollingStateOverflowScrollingNode&, ScrollingStateTree&); + + LayerRepresentation m_scrolledContentsLayer; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_STATE_NODE(ScrollingStateOverflowScrollingNode, isOverflowScrollingNode()) + +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp b/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp index 8ef8ff6a5..b3de26324 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp +++ b/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * 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 @@ -30,80 +30,45 @@ #include "ScrollingStateTree.h" #include "TextStream.h" -#include <wtf/OwnPtr.h> namespace WebCore { -PassOwnPtr<ScrollingStateScrollingNode> ScrollingStateScrollingNode::create(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) -{ - return adoptPtr(new ScrollingStateScrollingNode(stateTree, nodeID)); -} - -ScrollingStateScrollingNode::ScrollingStateScrollingNode(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) - : ScrollingStateNode(ScrollingNode, stateTree, nodeID) -#if PLATFORM(MAC) && !PLATFORM(IOS) - , m_verticalScrollbarPainter(0) - , m_horizontalScrollbarPainter(0) -#endif - , m_frameScaleFactor(1) - , m_wheelEventHandlerCount(0) - , m_synchronousScrollingReasons(0) - , m_behaviorForFixed(StickToDocumentBounds) - , m_headerHeight(0) - , m_footerHeight(0) - , m_requestedScrollPositionRepresentsProgrammaticScroll(false) +ScrollingStateScrollingNode::ScrollingStateScrollingNode(ScrollingStateTree& stateTree, ScrollingNodeType nodeType, ScrollingNodeID nodeID) + : ScrollingStateNode(nodeType, stateTree, nodeID) { } ScrollingStateScrollingNode::ScrollingStateScrollingNode(const ScrollingStateScrollingNode& stateNode, ScrollingStateTree& adoptiveTree) : ScrollingStateNode(stateNode, adoptiveTree) -#if PLATFORM(MAC) && !PLATFORM(IOS) - , m_verticalScrollbarPainter(stateNode.verticalScrollbarPainter()) - , m_horizontalScrollbarPainter(stateNode.horizontalScrollbarPainter()) -#endif - , m_viewportRect(stateNode.viewportRect()) + , m_scrollableAreaSize(stateNode.scrollableAreaSize()) , m_totalContentsSize(stateNode.totalContentsSize()) + , m_reachableContentsSize(stateNode.reachableContentsSize()) + , m_scrollPosition(stateNode.scrollPosition()) + , m_requestedScrollPosition(stateNode.requestedScrollPosition()) , m_scrollOrigin(stateNode.scrollOrigin()) +#if ENABLE(CSS_SCROLL_SNAP) + , m_snapOffsetsInfo(stateNode.m_snapOffsetsInfo) +#endif , m_scrollableAreaParameters(stateNode.scrollableAreaParameters()) - , m_nonFastScrollableRegion(stateNode.nonFastScrollableRegion()) - , m_frameScaleFactor(stateNode.frameScaleFactor()) - , m_wheelEventHandlerCount(stateNode.wheelEventHandlerCount()) - , m_synchronousScrollingReasons(stateNode.synchronousScrollingReasons()) - , m_behaviorForFixed(stateNode.scrollBehaviorForFixedElements()) - , m_headerHeight(stateNode.headerHeight()) - , m_footerHeight(stateNode.footerHeight()) - , m_requestedScrollPosition(stateNode.requestedScrollPosition()) , m_requestedScrollPositionRepresentsProgrammaticScroll(stateNode.requestedScrollPositionRepresentsProgrammaticScroll()) + , m_expectsWheelEventTestTrigger(stateNode.expectsWheelEventTestTrigger()) { - if (hasChangedProperty(CounterScrollingLayer)) - setCounterScrollingLayer(stateNode.counterScrollingLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); - - if (hasChangedProperty(HeaderLayer)) - setHeaderLayer(stateNode.headerLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); - - if (hasChangedProperty(FooterLayer)) - setFooterLayer(stateNode.footerLayer().toRepresentation(adoptiveTree.preferredLayerRepresentation())); } ScrollingStateScrollingNode::~ScrollingStateScrollingNode() { } -PassOwnPtr<ScrollingStateNode> ScrollingStateScrollingNode::clone(ScrollingStateTree& adoptiveTree) +void ScrollingStateScrollingNode::setScrollableAreaSize(const FloatSize& size) { - return adoptPtr(new ScrollingStateScrollingNode(*this, adoptiveTree)); -} - -void ScrollingStateScrollingNode::setViewportRect(const IntRect& viewportRect) -{ - if (m_viewportRect == viewportRect) + if (m_scrollableAreaSize == size) return; - m_viewportRect = viewportRect; - setPropertyChanged(ViewportRect); + m_scrollableAreaSize = size; + setPropertyChanged(ScrollableAreaSize); } -void ScrollingStateScrollingNode::setTotalContentsSize(const IntSize& totalContentsSize) +void ScrollingStateScrollingNode::setTotalContentsSize(const FloatSize& totalContentsSize) { if (m_totalContentsSize == totalContentsSize) return; @@ -112,159 +77,142 @@ void ScrollingStateScrollingNode::setTotalContentsSize(const IntSize& totalConte setPropertyChanged(TotalContentsSize); } -void ScrollingStateScrollingNode::setScrollOrigin(const IntPoint& scrollOrigin) +void ScrollingStateScrollingNode::setReachableContentsSize(const FloatSize& reachableContentsSize) { - if (m_scrollOrigin == scrollOrigin) + if (m_reachableContentsSize == reachableContentsSize) return; - m_scrollOrigin = scrollOrigin; - setPropertyChanged(ScrollOrigin); + m_reachableContentsSize = reachableContentsSize; + setPropertyChanged(ReachableContentsSize); } -void ScrollingStateScrollingNode::setScrollableAreaParameters(const ScrollableAreaParameters& parameters) +void ScrollingStateScrollingNode::setScrollPosition(const FloatPoint& scrollPosition) { - if (m_scrollableAreaParameters == parameters) + if (m_scrollPosition == scrollPosition) return; - m_scrollableAreaParameters = parameters; - setPropertyChanged(ScrollableAreaParams); + m_scrollPosition = scrollPosition; + setPropertyChanged(ScrollPosition); } -void ScrollingStateScrollingNode::setFrameScaleFactor(float scaleFactor) +void ScrollingStateScrollingNode::setScrollOrigin(const IntPoint& scrollOrigin) { - if (m_frameScaleFactor == scaleFactor) + if (m_scrollOrigin == scrollOrigin) return; - m_frameScaleFactor = scaleFactor; - - setPropertyChanged(FrameScaleFactor); + m_scrollOrigin = scrollOrigin; + setPropertyChanged(ScrollOrigin); } -void ScrollingStateScrollingNode::setNonFastScrollableRegion(const Region& nonFastScrollableRegion) +#if ENABLE(CSS_SCROLL_SNAP) +void ScrollingStateScrollingNode::setHorizontalSnapOffsets(const Vector<float>& snapOffsets) { - if (m_nonFastScrollableRegion == nonFastScrollableRegion) + if (m_snapOffsetsInfo.horizontalSnapOffsets == snapOffsets) return; - m_nonFastScrollableRegion = nonFastScrollableRegion; - setPropertyChanged(NonFastScrollableRegion); + m_snapOffsetsInfo.horizontalSnapOffsets = snapOffsets; + setPropertyChanged(HorizontalSnapOffsets); } -void ScrollingStateScrollingNode::setWheelEventHandlerCount(unsigned wheelEventHandlerCount) +void ScrollingStateScrollingNode::setVerticalSnapOffsets(const Vector<float>& snapOffsets) { - if (m_wheelEventHandlerCount == wheelEventHandlerCount) + if (m_snapOffsetsInfo.verticalSnapOffsets == snapOffsets) return; - m_wheelEventHandlerCount = wheelEventHandlerCount; - setPropertyChanged(WheelEventHandlerCount); + m_snapOffsetsInfo.verticalSnapOffsets = snapOffsets; + setPropertyChanged(VerticalSnapOffsets); } -void ScrollingStateScrollingNode::setSynchronousScrollingReasons(SynchronousScrollingReasons reasons) +void ScrollingStateScrollingNode::setHorizontalSnapOffsetRanges(const Vector<ScrollOffsetRange<float>>& scrollOffsetRanges) { - if (m_synchronousScrollingReasons == reasons) + if (m_snapOffsetsInfo.horizontalSnapOffsetRanges == scrollOffsetRanges) return; - m_synchronousScrollingReasons = reasons; - setPropertyChanged(ReasonsForSynchronousScrolling); + m_snapOffsetsInfo.horizontalSnapOffsetRanges = scrollOffsetRanges; + setPropertyChanged(HorizontalSnapOffsetRanges); } -void ScrollingStateScrollingNode::setScrollBehaviorForFixedElements(ScrollBehaviorForFixedElements behaviorForFixed) +void ScrollingStateScrollingNode::setVerticalSnapOffsetRanges(const Vector<ScrollOffsetRange<float>>& scrollOffsetRanges) { - if (m_behaviorForFixed == behaviorForFixed) + if (m_snapOffsetsInfo.verticalSnapOffsetRanges == scrollOffsetRanges) return; - m_behaviorForFixed = behaviorForFixed; - setPropertyChanged(BehaviorForFixedElements); -} - -void ScrollingStateScrollingNode::setRequestedScrollPosition(const IntPoint& requestedScrollPosition, bool representsProgrammaticScroll) -{ - m_requestedScrollPosition = requestedScrollPosition; - m_requestedScrollPositionRepresentsProgrammaticScroll = representsProgrammaticScroll; - setPropertyChanged(RequestedScrollPosition); + m_snapOffsetsInfo.verticalSnapOffsetRanges = scrollOffsetRanges; + setPropertyChanged(VerticalSnapOffsetRanges); } -void ScrollingStateScrollingNode::setHeaderHeight(int headerHeight) +void ScrollingStateScrollingNode::setCurrentHorizontalSnapPointIndex(unsigned index) { - if (m_headerHeight == headerHeight) + if (m_currentHorizontalSnapPointIndex == index) return; - - m_headerHeight = headerHeight; - setPropertyChanged(HeaderHeight); + + m_currentHorizontalSnapPointIndex = index; + setPropertyChanged(CurrentHorizontalSnapOffsetIndex); } -void ScrollingStateScrollingNode::setFooterHeight(int footerHeight) +void ScrollingStateScrollingNode::setCurrentVerticalSnapPointIndex(unsigned index) { - if (m_footerHeight == footerHeight) + if (m_currentVerticalSnapPointIndex == index) return; - - m_footerHeight = footerHeight; - setPropertyChanged(FooterHeight); + + m_currentVerticalSnapPointIndex = index; + setPropertyChanged(CurrentVerticalSnapOffsetIndex); } +#endif -void ScrollingStateScrollingNode::setCounterScrollingLayer(const LayerRepresentation& layerRepresentation) +void ScrollingStateScrollingNode::setScrollableAreaParameters(const ScrollableAreaParameters& parameters) { - if (layerRepresentation == m_counterScrollingLayer) + if (m_scrollableAreaParameters == parameters) return; - - m_counterScrollingLayer = layerRepresentation; - setPropertyChanged(CounterScrollingLayer); + m_scrollableAreaParameters = parameters; + setPropertyChanged(ScrollableAreaParams); } -void ScrollingStateScrollingNode::setHeaderLayer(const LayerRepresentation& layerRepresentation) +void ScrollingStateScrollingNode::setRequestedScrollPosition(const FloatPoint& requestedScrollPosition, bool representsProgrammaticScroll) { - if (layerRepresentation == m_headerLayer) - return; - - m_headerLayer = layerRepresentation; - - setPropertyChanged(HeaderLayer); + m_requestedScrollPosition = requestedScrollPosition; + m_requestedScrollPositionRepresentsProgrammaticScroll = representsProgrammaticScroll; + setPropertyChanged(RequestedScrollPosition); } - -void ScrollingStateScrollingNode::setFooterLayer(const LayerRepresentation& layerRepresentation) +void ScrollingStateScrollingNode::setExpectsWheelEventTestTrigger(bool expectsTestTrigger) { - if (layerRepresentation == m_footerLayer) + if (expectsTestTrigger == m_expectsWheelEventTestTrigger) return; - - m_footerLayer = layerRepresentation; - setPropertyChanged(FooterLayer); + m_expectsWheelEventTestTrigger = expectsTestTrigger; + setPropertyChanged(ExpectsWheelEventTestTrigger); } -#if !(PLATFORM(MAC) && !PLATFORM(IOS)) -void ScrollingStateScrollingNode::setScrollbarPaintersFromScrollbars(Scrollbar*, Scrollbar*) +void ScrollingStateScrollingNode::dumpProperties(TextStream& ts, int indent, ScrollingStateTreeAsTextBehavior) const { -} -#endif - -void ScrollingStateScrollingNode::dumpProperties(TextStream& ts, int indent) const -{ - ts << "(" << "Scrolling node" << "\n"; - - if (!m_viewportRect.isEmpty()) { + if (m_scrollPosition != FloatPoint()) { writeIndent(ts, indent + 1); - ts << "(viewport rect " << m_viewportRect.x() << " " << m_viewportRect.y() << " " << m_viewportRect.width() << " " << m_viewportRect.height() << ")\n"; + ts << "(scroll position " + << TextStream::FormatNumberRespectingIntegers(m_scrollPosition.x()) << " " + << TextStream::FormatNumberRespectingIntegers(m_scrollPosition.y()) << ")\n"; } - if (!m_totalContentsSize.isEmpty()) { + if (!m_scrollableAreaSize.isEmpty()) { writeIndent(ts, indent + 1); - ts << "(contents size " << m_totalContentsSize.width() << " " << m_totalContentsSize.height() << ")\n"; + ts << "(scrollable area size " + << TextStream::FormatNumberRespectingIntegers(m_scrollableAreaSize.width()) << " " + << TextStream::FormatNumberRespectingIntegers(m_scrollableAreaSize.height()) << ")\n"; } - if (m_frameScaleFactor != 1) { - writeIndent(ts, indent + 1); - ts << "(frame scale factor " << m_frameScaleFactor << ")\n"; - } - - if (m_synchronousScrollingReasons) { + if (!m_totalContentsSize.isEmpty()) { writeIndent(ts, indent + 1); - ts << "(Scrolling on main thread because: " << ScrollingCoordinator::synchronousScrollingReasonsAsText(m_synchronousScrollingReasons) << ")\n"; + ts << "(contents size " + << TextStream::FormatNumberRespectingIntegers(m_totalContentsSize.width()) << " " + << TextStream::FormatNumberRespectingIntegers(m_totalContentsSize.height()) << ")\n"; } if (m_requestedScrollPosition != IntPoint()) { writeIndent(ts, indent + 1); - ts << "(requested scroll position " << m_requestedScrollPosition.x() << " " << m_requestedScrollPosition.y() << ")\n"; + ts << "(requested scroll position " + << TextStream::FormatNumberRespectingIntegers(m_requestedScrollPosition.x()) << " " + << TextStream::FormatNumberRespectingIntegers(m_requestedScrollPosition.y()) << ")\n"; } if (m_scrollOrigin != IntPoint()) { diff --git a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h b/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h index 14be04b97..628c34f7f 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h +++ b/Source/WebCore/page/scrolling/ScrollingStateScrollingNode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2014-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 @@ -23,142 +23,111 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScrollingStateScrollingNode_h -#define ScrollingStateScrollingNode_h +#pragma once #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) -#include "GraphicsLayer.h" -#include "IntRect.h" -#include "Region.h" +#include "ScrollSnapOffsetsInfo.h" #include "ScrollTypes.h" -#include "ScrollbarThemeComposite.h" #include "ScrollingCoordinator.h" #include "ScrollingStateNode.h" -#include <wtf/PassOwnPtr.h> namespace WebCore { -class Scrollbar; - -class ScrollingStateScrollingNode final : public ScrollingStateNode { +class ScrollingStateScrollingNode : public ScrollingStateNode { public: - static PassOwnPtr<ScrollingStateScrollingNode> create(ScrollingStateTree&, ScrollingNodeID); - - virtual PassOwnPtr<ScrollingStateNode> clone(ScrollingStateTree&); - virtual ~ScrollingStateScrollingNode(); enum ChangedProperty { - ViewportRect = NumStateNodeBits, + ScrollableAreaSize = NumStateNodeBits, TotalContentsSize, + ReachableContentsSize, + ScrollPosition, ScrollOrigin, ScrollableAreaParams, - FrameScaleFactor, - NonFastScrollableRegion, - WheelEventHandlerCount, - ReasonsForSynchronousScrolling, RequestedScrollPosition, - CounterScrollingLayer, - HeaderHeight, - FooterHeight, - HeaderLayer, - FooterLayer, - PainterForScrollbar, - BehaviorForFixedElements + NumScrollingStateNodeBits, +#if ENABLE(CSS_SCROLL_SNAP) + HorizontalSnapOffsets, + VerticalSnapOffsets, + HorizontalSnapOffsetRanges, + VerticalSnapOffsetRanges, + CurrentHorizontalSnapOffsetIndex, + CurrentVerticalSnapOffsetIndex, +#endif + ExpectsWheelEventTestTrigger, }; - const IntRect& viewportRect() const { return m_viewportRect; } - void setViewportRect(const IntRect&); - - const IntSize& totalContentsSize() const { return m_totalContentsSize; } - void setTotalContentsSize(const IntSize&); - - const IntPoint& scrollOrigin() const { return m_scrollOrigin; } - void setScrollOrigin(const IntPoint&); + const FloatSize& scrollableAreaSize() const { return m_scrollableAreaSize; } + WEBCORE_EXPORT void setScrollableAreaSize(const FloatSize&); - float frameScaleFactor() const { return m_frameScaleFactor; } - void setFrameScaleFactor(float); + const FloatSize& totalContentsSize() const { return m_totalContentsSize; } + WEBCORE_EXPORT void setTotalContentsSize(const FloatSize&); - const Region& nonFastScrollableRegion() const { return m_nonFastScrollableRegion; } - void setNonFastScrollableRegion(const Region&); + const FloatSize& reachableContentsSize() const { return m_reachableContentsSize; } + WEBCORE_EXPORT void setReachableContentsSize(const FloatSize&); - unsigned wheelEventHandlerCount() const { return m_wheelEventHandlerCount; } - void setWheelEventHandlerCount(unsigned); + const FloatPoint& scrollPosition() const { return m_scrollPosition; } + WEBCORE_EXPORT void setScrollPosition(const FloatPoint&); - SynchronousScrollingReasons synchronousScrollingReasons() const { return m_synchronousScrollingReasons; } - void setSynchronousScrollingReasons(SynchronousScrollingReasons); - - const ScrollableAreaParameters& scrollableAreaParameters() const { return m_scrollableAreaParameters; } - void setScrollableAreaParameters(const ScrollableAreaParameters& params); - - ScrollBehaviorForFixedElements scrollBehaviorForFixedElements() const { return m_behaviorForFixed; } - void setScrollBehaviorForFixedElements(ScrollBehaviorForFixedElements); - - const IntPoint& requestedScrollPosition() const { return m_requestedScrollPosition; } - void setRequestedScrollPosition(const IntPoint&, bool representsProgrammaticScroll); + const IntPoint& scrollOrigin() const { return m_scrollOrigin; } + WEBCORE_EXPORT void setScrollOrigin(const IntPoint&); - int headerHeight() const { return m_headerHeight; } - void setHeaderHeight(int); +#if ENABLE(CSS_SCROLL_SNAP) + const Vector<float>& horizontalSnapOffsets() const { return m_snapOffsetsInfo.horizontalSnapOffsets; } + WEBCORE_EXPORT void setHorizontalSnapOffsets(const Vector<float>&); - int footerHeight() const { return m_footerHeight; } - void setFooterHeight(int); + const Vector<float>& verticalSnapOffsets() const { return m_snapOffsetsInfo.verticalSnapOffsets; } + WEBCORE_EXPORT void setVerticalSnapOffsets(const Vector<float>&); - // This is a layer moved in the opposite direction to scrolling, for example for background-attachment:fixed - const LayerRepresentation& counterScrollingLayer() const { return m_counterScrollingLayer; } - void setCounterScrollingLayer(const LayerRepresentation&); + const Vector<ScrollOffsetRange<float>>& horizontalSnapOffsetRanges() const { return m_snapOffsetsInfo.horizontalSnapOffsetRanges; } + WEBCORE_EXPORT void setHorizontalSnapOffsetRanges(const Vector<ScrollOffsetRange<float>>&); - // The header and footer layers scroll vertically with the page, they should remain fixed when scrolling horizontally. - const LayerRepresentation& headerLayer() const { return m_headerLayer; } - void setHeaderLayer(const LayerRepresentation&); + const Vector<ScrollOffsetRange<float>>& verticalSnapOffsetRanges() const { return m_snapOffsetsInfo.verticalSnapOffsetRanges; } + WEBCORE_EXPORT void setVerticalSnapOffsetRanges(const Vector<ScrollOffsetRange<float>>&); - // The header and footer layers scroll vertically with the page, they should remain fixed when scrolling horizontally. - const LayerRepresentation& footerLayer() const { return m_footerLayer; } - void setFooterLayer(const LayerRepresentation&); + unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; } + WEBCORE_EXPORT void setCurrentHorizontalSnapPointIndex(unsigned); -#if PLATFORM(MAC) && !PLATFORM(IOS) - ScrollbarPainter verticalScrollbarPainter() const { return m_verticalScrollbarPainter.get(); } - ScrollbarPainter horizontalScrollbarPainter() const { return m_horizontalScrollbarPainter.get(); } + unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; } + WEBCORE_EXPORT void setCurrentVerticalSnapPointIndex(unsigned); #endif - void setScrollbarPaintersFromScrollbars(Scrollbar* verticalScrollbar, Scrollbar* horizontalScrollbar); + const ScrollableAreaParameters& scrollableAreaParameters() const { return m_scrollableAreaParameters; } + WEBCORE_EXPORT void setScrollableAreaParameters(const ScrollableAreaParameters& params); + + const FloatPoint& requestedScrollPosition() const { return m_requestedScrollPosition; } bool requestedScrollPositionRepresentsProgrammaticScroll() const { return m_requestedScrollPositionRepresentsProgrammaticScroll; } + WEBCORE_EXPORT void setRequestedScrollPosition(const FloatPoint&, bool representsProgrammaticScroll); - virtual void dumpProperties(TextStream&, int indent) const override; + bool expectsWheelEventTestTrigger() const { return m_expectsWheelEventTestTrigger; } + WEBCORE_EXPORT void setExpectsWheelEventTestTrigger(bool); -private: - ScrollingStateScrollingNode(ScrollingStateTree&, ScrollingNodeID); +protected: + ScrollingStateScrollingNode(ScrollingStateTree&, ScrollingNodeType, ScrollingNodeID); ScrollingStateScrollingNode(const ScrollingStateScrollingNode&, ScrollingStateTree&); - LayerRepresentation m_counterScrollingLayer; - LayerRepresentation m_headerLayer; - LayerRepresentation m_footerLayer; - -#if PLATFORM(MAC) && !PLATFORM(IOS) - RetainPtr<ScrollbarPainter> m_verticalScrollbarPainter; - RetainPtr<ScrollbarPainter> m_horizontalScrollbarPainter; -#endif - - IntRect m_viewportRect; - IntSize m_totalContentsSize; - IntPoint m_scrollOrigin; + void dumpProperties(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const override; +private: + FloatSize m_scrollableAreaSize; + FloatSize m_totalContentsSize; + FloatSize m_reachableContentsSize; + FloatPoint m_scrollPosition; + FloatPoint m_requestedScrollPosition; + IntPoint m_scrollOrigin; +#if ENABLE(CSS_SCROLL_SNAP) + ScrollSnapOffsetsInfo<float> m_snapOffsetsInfo; + unsigned m_currentHorizontalSnapPointIndex { 0 }; + unsigned m_currentVerticalSnapPointIndex { 0 }; +#endif ScrollableAreaParameters m_scrollableAreaParameters; - Region m_nonFastScrollableRegion; - float m_frameScaleFactor; - unsigned m_wheelEventHandlerCount; - SynchronousScrollingReasons m_synchronousScrollingReasons; - ScrollBehaviorForFixedElements m_behaviorForFixed; - int m_headerHeight; - int m_footerHeight; - IntPoint m_requestedScrollPosition; - bool m_requestedScrollPositionRepresentsProgrammaticScroll; + bool m_requestedScrollPositionRepresentsProgrammaticScroll { false }; + bool m_expectsWheelEventTestTrigger { false }; }; -SCROLLING_STATE_NODE_TYPE_CASTS(ScrollingStateScrollingNode, nodeType() == ScrollingNode); - } // namespace WebCore -#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) +SPECIALIZE_TYPE_TRAITS_SCROLLING_STATE_NODE(ScrollingStateScrollingNode, isScrollingNode()) -#endif // ScrollingStateScrollingNode_h +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateStickyNode.cpp b/Source/WebCore/page/scrolling/ScrollingStateStickyNode.cpp new file mode 100644 index 000000000..912f5b9d7 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingStateStickyNode.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012 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 "ScrollingStateStickyNode.h" + +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) + +#include "GraphicsLayer.h" +#include "Logging.h" +#include "ScrollingStateTree.h" +#include "TextStream.h" + +namespace WebCore { + +Ref<ScrollingStateStickyNode> ScrollingStateStickyNode::create(ScrollingStateTree& stateTree, ScrollingNodeID nodeID) +{ + return adoptRef(*new ScrollingStateStickyNode(stateTree, nodeID)); +} + +ScrollingStateStickyNode::ScrollingStateStickyNode(ScrollingStateTree& tree, ScrollingNodeID nodeID) + : ScrollingStateNode(StickyNode, tree, nodeID) +{ +} + +ScrollingStateStickyNode::ScrollingStateStickyNode(const ScrollingStateStickyNode& node, ScrollingStateTree& adoptiveTree) + : ScrollingStateNode(node, adoptiveTree) + , m_constraints(StickyPositionViewportConstraints(node.viewportConstraints())) +{ +} + +ScrollingStateStickyNode::~ScrollingStateStickyNode() +{ +} + +Ref<ScrollingStateNode> ScrollingStateStickyNode::clone(ScrollingStateTree& adoptiveTree) +{ + return adoptRef(*new ScrollingStateStickyNode(*this, adoptiveTree)); +} + +void ScrollingStateStickyNode::updateConstraints(const StickyPositionViewportConstraints& constraints) +{ + if (m_constraints == constraints) + return; + + m_constraints = constraints; + setPropertyChanged(ViewportConstraints); +} + +void ScrollingStateStickyNode::reconcileLayerPositionForViewportRect(const LayoutRect& viewportRect, ScrollingLayerPositionAction action) +{ + FloatPoint position = m_constraints.layerPositionForConstrainingRect(viewportRect); + if (layer().representsGraphicsLayer()) { + GraphicsLayer* graphicsLayer = static_cast<GraphicsLayer*>(layer()); + + LOG_WITH_STREAM(Compositing, stream << "ScrollingStateStickyNode::reconcileLayerPositionForViewportRect setting position of layer " << graphicsLayer->primaryLayerID() << " to " << position); + + switch (action) { + case ScrollingLayerPositionAction::Set: + graphicsLayer->setPosition(position); + break; + + case ScrollingLayerPositionAction::SetApproximate: + graphicsLayer->setApproximatePosition(position); + break; + + case ScrollingLayerPositionAction::Sync: + graphicsLayer->syncPosition(position); + break; + } + } +} + +void ScrollingStateStickyNode::dumpProperties(TextStream& ts, int indent, ScrollingStateTreeAsTextBehavior) const +{ + ts << "(" << "Sticky node" << "\n"; + + if (m_constraints.anchorEdges()) { + writeIndent(ts, indent + 1); + ts << "(anchor edges: "; + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeLeft)) + ts << "AnchorEdgeLeft "; + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeRight)) + ts << "AnchorEdgeRight "; + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeTop)) + ts << "AnchorEdgeTop "; + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeBottom)) + ts << "AnchorEdgeBottom"; + ts << ")\n"; + } + + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeLeft)) { + writeIndent(ts, indent + 1); + ts << "(left offset " << m_constraints.leftOffset() << ")\n"; + } + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeRight)) { + writeIndent(ts, indent + 1); + ts << "(right offset " << m_constraints.rightOffset() << ")\n"; + } + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeTop)) { + writeIndent(ts, indent + 1); + ts << "(top offset " << m_constraints.topOffset() << ")\n"; + } + if (m_constraints.hasAnchorEdge(ViewportConstraints::AnchorEdgeBottom)) { + writeIndent(ts, indent + 1); + ts << "(bottom offset " << m_constraints.bottomOffset() << ")\n"; + } + + writeIndent(ts, indent + 1); + FloatRect r = m_constraints.containingBlockRect(); + ts << "(containing block rect " << r.x() << ", " << r.y() << " " << r.width() << " x " << r.height() << ")\n"; + + writeIndent(ts, indent + 1); + r = m_constraints.stickyBoxRect(); + ts << "(sticky box rect " << r.x() << " " << r.y() << " " << r.width() << " " << r.height() << ")\n"; + + writeIndent(ts, indent + 1); + r = m_constraints.constrainingRectAtLastLayout(); + ts << "(constraining rect " << r.x() << " " << r.y() << " " << r.width() << " " << r.height() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(sticky offset at last layout " << m_constraints.stickyOffsetAtLastLayout().width() << " " << m_constraints.stickyOffsetAtLastLayout().height() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(layer position at last layout " << m_constraints.layerPositionAtLastLayout().x() << " " << m_constraints.layerPositionAtLastLayout().y() << ")\n"; +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/PageThrottler.h b/Source/WebCore/page/scrolling/ScrollingStateStickyNode.h index dc57f1212..63f908e76 100644 --- a/Source/WebCore/page/PageThrottler.h +++ b/Source/WebCore/page/scrolling/ScrollingStateStickyNode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012 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,59 +23,47 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef PageThrottler_h -#define PageThrottler_h +#pragma once -#include "Timer.h" +#if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) -#include "UserActivity.h" -#include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include "ScrollingConstraints.h" +#include "ScrollingStateNode.h" + +#include <wtf/Forward.h> namespace WebCore { -class Page; -class PageActivityAssertionToken; +class StickyPositionViewportConstraints; -class PageThrottler { +class ScrollingStateStickyNode final : public ScrollingStateNode { public: - PageThrottler(Page&); - ~PageThrottler(); + static Ref<ScrollingStateStickyNode> create(ScrollingStateTree&, ScrollingNodeID); - std::unique_ptr<PageActivityAssertionToken> createActivityToken(); + Ref<ScrollingStateNode> clone(ScrollingStateTree&) override; - bool shouldThrottleAnimations() const { return m_throttleState != PageNotThrottledState; } - bool shouldThrottleTimers() const { return m_throttleState != PageNotThrottledState; } + virtual ~ScrollingStateStickyNode(); - void setIsVisuallyIdle(bool); + enum { + ViewportConstraints = NumStateNodeBits + }; - void reportInterestingEvent(); + WEBCORE_EXPORT void updateConstraints(const StickyPositionViewportConstraints&); + const StickyPositionViewportConstraints& viewportConstraints() const { return m_constraints; } private: - enum PageThrottleState { - PageNotThrottledState, - PageWaitingToThrottleState, - PageThrottledState - }; + ScrollingStateStickyNode(ScrollingStateTree&, ScrollingNodeID); + ScrollingStateStickyNode(const ScrollingStateStickyNode&, ScrollingStateTree&); - friend class PageActivityAssertionToken; - void addActivityToken(PageActivityAssertionToken&); - void removeActivityToken(PageActivityAssertionToken&); + void reconcileLayerPositionForViewportRect(const LayoutRect& viewportRect, ScrollingLayerPositionAction) override; - void startThrottleHysteresisTimer(); - void stopThrottleHysteresisTimer(); - void throttleHysteresisTimerFired(Timer<PageThrottler>&); + void dumpProperties(TextStream&, int indent, ScrollingStateTreeAsTextBehavior) const override; - void throttlePage(); - void unthrottlePage(); - - Page& m_page; - PageThrottleState m_throttleState; - Timer<PageThrottler> m_throttleHysteresisTimer; - HashSet<PageActivityAssertionToken*> m_activityTokens; - UserActivity m_visuallyNonIdle; + StickyPositionViewportConstraints m_constraints; }; -} -#endif +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_STATE_NODE(ScrollingStateStickyNode, isStickyNode()) + +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateTree.cpp b/Source/WebCore/page/scrolling/ScrollingStateTree.cpp index e06f22168..0bbc1b733 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateTree.cpp +++ b/Source/WebCore/page/scrolling/ScrollingStateTree.cpp @@ -30,15 +30,16 @@ #include "AsyncScrollingCoordinator.h" #include "ScrollingStateFixedNode.h" -#include "ScrollingStateScrollingNode.h" +#include "ScrollingStateFrameScrollingNode.h" +#include "ScrollingStateOverflowScrollingNode.h" #include "ScrollingStateStickyNode.h" +#include <wtf/text/CString.h> -namespace WebCore { +#ifndef NDEBUG +#include <stdio.h> +#endif -PassOwnPtr<ScrollingStateTree> ScrollingStateTree::create(AsyncScrollingCoordinator* scrollingCoordinator) -{ - return adoptPtr(new ScrollingStateTree(scrollingCoordinator)); -} +namespace WebCore { ScrollingStateTree::ScrollingStateTree(AsyncScrollingCoordinator* scrollingCoordinator) : m_scrollingCoordinator(scrollingCoordinator) @@ -66,27 +67,58 @@ void ScrollingStateTree::setHasChangedProperties(bool changedProperties) #endif } +Ref<ScrollingStateNode> ScrollingStateTree::createNode(ScrollingNodeType nodeType, ScrollingNodeID nodeID) +{ + switch (nodeType) { + case FixedNode: + return ScrollingStateFixedNode::create(*this, nodeID); + case StickyNode: + return ScrollingStateStickyNode::create(*this, nodeID); + case FrameScrollingNode: + return ScrollingStateFrameScrollingNode::create(*this, nodeID); + case OverflowScrollingNode: + return ScrollingStateOverflowScrollingNode::create(*this, nodeID); + } + ASSERT_NOT_REACHED(); + return ScrollingStateFixedNode::create(*this, nodeID); +} + +bool ScrollingStateTree::nodeTypeAndParentMatch(ScrollingStateNode& node, ScrollingNodeType nodeType, ScrollingNodeID parentID) const +{ + if (node.nodeType() != nodeType) + return false; + + ScrollingStateNode* parent = stateNodeForID(parentID); + if (!parent) + return true; + + return node.parent() == parent; +} + ScrollingNodeID ScrollingStateTree::attachNode(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID) { ASSERT(newNodeID); if (ScrollingStateNode* node = stateNodeForID(newNodeID)) { - ScrollingStateNode* parent = stateNodeForID(parentID); - if (!parent) - return newNodeID; - if (node->parent() == parent) + if (nodeTypeAndParentMatch(*node, nodeType, parentID)) return newNodeID; - // The node is being re-parented. To do that, we'll remove it, and then re-create a new node. - removeNode(node); +#if ENABLE(ASYNC_SCROLLING) + // If the type has changed, we need to destroy and recreate the node with a new ID. + if (nodeType != node->nodeType()) + newNodeID = m_scrollingCoordinator->uniqueScrollLayerID(); +#endif + + // The node is being re-parented. To do that, we'll remove it, and then create a new node. + removeNodeAndAllDescendants(node, SubframeNodeRemoval::Orphan); } - ScrollingStateNode* newNode = 0; + ScrollingStateNode* newNode = nullptr; if (!parentID) { // If we're resetting the root node, we should clear the HashMap and destroy the current children. clear(); - setRootStateNode(ScrollingStateScrollingNode::create(*this, newNodeID)); + setRootStateNode(ScrollingStateFrameScrollingNode::create(*this, newNodeID)); newNode = rootStateNode(); m_hasNewRootStateNode = true; } else { @@ -94,31 +126,22 @@ ScrollingNodeID ScrollingStateTree::attachNode(ScrollingNodeType nodeType, Scrol if (!parent) return 0; - switch (nodeType) { - case FixedNode: { - OwnPtr<ScrollingStateFixedNode> fixedNode = ScrollingStateFixedNode::create(*this, newNodeID); - newNode = fixedNode.get(); - parent->appendChild(fixedNode.release()); - break; - } - case StickyNode: { - OwnPtr<ScrollingStateStickyNode> stickyNode = ScrollingStateStickyNode::create(*this, newNodeID); - newNode = stickyNode.get(); - parent->appendChild(stickyNode.release()); - break; - } - case ScrollingNode: { - // FIXME: We currently only support child nodes that are fixed. - ASSERT_NOT_REACHED(); - OwnPtr<ScrollingStateScrollingNode> scrollingNode = ScrollingStateScrollingNode::create(*this, newNodeID); - newNode = scrollingNode.get(); - parent->appendChild(scrollingNode.release()); - break; + if (nodeType == FrameScrollingNode && parentID) { + if (auto orphanedNode = m_orphanedSubframeNodes.take(newNodeID)) { + newNode = orphanedNode.get(); + parent->appendChild(orphanedNode.releaseNonNull()); + } } + + if (!newNode) { + auto stateNode = createNode(nodeType, newNodeID); + newNode = stateNode.ptr(); + parent->appendChild(WTFMove(stateNode)); } } m_stateNodeMap.set(newNodeID, newNode); + m_nodesRemovedSinceLastCommit.remove(newNodeID); return newNodeID; } @@ -132,35 +155,46 @@ void ScrollingStateTree::detachNode(ScrollingNodeID nodeID) if (!node) return; - removeNode(node); + removeNodeAndAllDescendants(node, SubframeNodeRemoval::Orphan); } void ScrollingStateTree::clear() { - removeNode(rootStateNode()); + if (rootStateNode()) + removeNodeAndAllDescendants(rootStateNode()); + m_stateNodeMap.clear(); + m_orphanedSubframeNodes.clear(); } -PassOwnPtr<ScrollingStateTree> ScrollingStateTree::commit(LayerRepresentation::Type preferredLayerRepresentation) +std::unique_ptr<ScrollingStateTree> ScrollingStateTree::commit(LayerRepresentation::Type preferredLayerRepresentation) { + if (!m_orphanedSubframeNodes.isEmpty()) { + // If we still have orphaned subtrees, remove them from m_stateNodeMap since they will be deleted + // when clearing m_orphanedSubframeNodes. + for (auto& orphanNode : m_orphanedSubframeNodes.values()) + recursiveNodeWillBeRemoved(orphanNode.get(), SubframeNodeRemoval::Delete); + m_orphanedSubframeNodes.clear(); + } + // This function clones and resets the current state tree, but leaves the tree structure intact. - OwnPtr<ScrollingStateTree> treeStateClone = ScrollingStateTree::create(); + std::unique_ptr<ScrollingStateTree> treeStateClone = std::make_unique<ScrollingStateTree>(); treeStateClone->setPreferredLayerRepresentation(preferredLayerRepresentation); if (m_rootStateNode) - treeStateClone->setRootStateNode(static_pointer_cast<ScrollingStateScrollingNode>(m_rootStateNode->cloneAndReset(*treeStateClone))); + treeStateClone->setRootStateNode(static_reference_cast<ScrollingStateFrameScrollingNode>(m_rootStateNode->cloneAndReset(*treeStateClone))); // Copy the IDs of the nodes that have been removed since the last commit into the clone. treeStateClone->m_nodesRemovedSinceLastCommit.swap(m_nodesRemovedSinceLastCommit); // Now the clone tree has changed properties, and the original tree does not. - treeStateClone->m_hasChangedProperties = true; + treeStateClone->m_hasChangedProperties = m_hasChangedProperties; m_hasChangedProperties = false; treeStateClone->m_hasNewRootStateNode = m_hasNewRootStateNode; m_hasNewRootStateNode = false; - return treeStateClone.release(); + return treeStateClone; } void ScrollingStateTree::addNode(ScrollingStateNode* node) @@ -168,39 +202,54 @@ void ScrollingStateTree::addNode(ScrollingStateNode* node) m_stateNodeMap.add(node->scrollingNodeID(), node); } -void ScrollingStateTree::removeNode(ScrollingStateNode* node) +void ScrollingStateTree::removeNodeAndAllDescendants(ScrollingStateNode* node, SubframeNodeRemoval subframeNodeRemoval) { - if (!node) - return; + ScrollingStateNode* parent = node->parent(); - if (node == m_rootStateNode) { - didRemoveNode(node->scrollingNodeID()); + recursiveNodeWillBeRemoved(node, subframeNodeRemoval); + + if (node == m_rootStateNode) m_rootStateNode = nullptr; + else if (parent) { + ASSERT(parent->children()); + ASSERT(parent->children()->find(node) != notFound); + if (auto children = parent->children()) { + size_t index = children->find(node); + if (index != notFound) + children->remove(index); + } + } +} + +void ScrollingStateTree::recursiveNodeWillBeRemoved(ScrollingStateNode* currNode, SubframeNodeRemoval subframeNodeRemoval) +{ + currNode->setParent(nullptr); + if (subframeNodeRemoval == SubframeNodeRemoval::Orphan && currNode != m_rootStateNode && currNode->isFrameScrollingNode()) { + m_orphanedSubframeNodes.add(currNode->scrollingNodeID(), currNode); return; } - ASSERT(m_rootStateNode); - m_rootStateNode->removeChild(node); + willRemoveNode(currNode); - // ScrollingStateTree::removeNode() will destroy children, so we have to make sure we remove those children - // from the HashMap. - size_t size = m_nodesRemovedSinceLastCommit.size(); - for (size_t i = 0; i < size; ++i) - m_stateNodeMap.remove(m_nodesRemovedSinceLastCommit[i]); + if (auto children = currNode->children()) { + for (auto& child : *children) + recursiveNodeWillBeRemoved(child.get(), subframeNodeRemoval); + } } -void ScrollingStateTree::didRemoveNode(ScrollingNodeID nodeID) +void ScrollingStateTree::willRemoveNode(ScrollingStateNode* node) { - m_nodesRemovedSinceLastCommit.append(nodeID); + m_nodesRemovedSinceLastCommit.add(node->scrollingNodeID()); + m_stateNodeMap.remove(node->scrollingNodeID()); setHasChangedProperties(); } -void ScrollingStateTree::setRemovedNodes(Vector<ScrollingNodeID> nodes) +void ScrollingStateTree::setRemovedNodes(HashSet<ScrollingNodeID> nodes) { - m_nodesRemovedSinceLastCommit = std::move(nodes); + m_nodesRemovedSinceLastCommit = WTFMove(nodes); } -ScrollingStateNode* ScrollingStateTree::stateNodeForID(ScrollingNodeID scrollLayerID) +ScrollingStateNode* ScrollingStateTree::stateNodeForID(ScrollingNodeID scrollLayerID) const { if (!scrollLayerID) return 0; @@ -215,4 +264,30 @@ ScrollingStateNode* ScrollingStateTree::stateNodeForID(ScrollingNodeID scrollLay } // namespace WebCore +#ifndef NDEBUG +void showScrollingStateTree(const WebCore::ScrollingStateTree* tree) +{ + if (!tree) + return; + + auto rootNode = tree->rootStateNode(); + if (!rootNode) { + fprintf(stderr, "Scrolling state tree %p with no root node\n", tree); + return; + } + + String output = rootNode->scrollingStateTreeAsText(); + fprintf(stderr, "%s\n", output.utf8().data()); +} + +void showScrollingStateTree(const WebCore::ScrollingStateNode* node) +{ + if (!node) + return; + + showScrollingStateTree(&node->scrollingStateTree()); +} + +#endif + #endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingStateTree.h b/Source/WebCore/page/scrolling/ScrollingStateTree.h index 59659e534..3b256151a 100644 --- a/Source/WebCore/page/scrolling/ScrollingStateTree.h +++ b/Source/WebCore/page/scrolling/ScrollingStateTree.h @@ -23,14 +23,11 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ScrollingStateTree_h -#define ScrollingStateTree_h +#pragma once #if ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) -#include "ScrollingStateScrollingNode.h" -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> +#include "ScrollingStateFrameScrollingNode.h" #include <wtf/RefPtr.h> namespace WebCore { @@ -43,29 +40,30 @@ class AsyncScrollingCoordinator; // the scrolling thread, avoiding locking. class ScrollingStateTree { + WTF_MAKE_FAST_ALLOCATED; friend class ScrollingStateNode; public: - - static PassOwnPtr<ScrollingStateTree> create(AsyncScrollingCoordinator* = 0); - ~ScrollingStateTree(); + WEBCORE_EXPORT ScrollingStateTree(AsyncScrollingCoordinator* = nullptr); + WEBCORE_EXPORT ~ScrollingStateTree(); - ScrollingStateScrollingNode* rootStateNode() const { return m_rootStateNode.get(); } - ScrollingStateNode* stateNodeForID(ScrollingNodeID); + ScrollingStateFrameScrollingNode* rootStateNode() const { return m_rootStateNode.get(); } + WEBCORE_EXPORT ScrollingStateNode* stateNodeForID(ScrollingNodeID) const; - ScrollingNodeID attachNode(ScrollingNodeType, ScrollingNodeID, ScrollingNodeID parentID); + WEBCORE_EXPORT ScrollingNodeID attachNode(ScrollingNodeType, ScrollingNodeID, ScrollingNodeID parentID); void detachNode(ScrollingNodeID); void clear(); - const Vector<ScrollingNodeID>& removedNodes() const { return m_nodesRemovedSinceLastCommit; } - void setRemovedNodes(Vector<ScrollingNodeID>); + const HashSet<ScrollingNodeID>& removedNodes() const { return m_nodesRemovedSinceLastCommit; } + WEBCORE_EXPORT void setRemovedNodes(HashSet<ScrollingNodeID>); // Copies the current tree state and clears the changed properties mask in the original. - PassOwnPtr<ScrollingStateTree> commit(LayerRepresentation::Type preferredLayerRepresentation); + WEBCORE_EXPORT std::unique_ptr<ScrollingStateTree> commit(LayerRepresentation::Type preferredLayerRepresentation); - void setHasChangedProperties(bool = true); + WEBCORE_EXPORT void setHasChangedProperties(bool = true); bool hasChangedProperties() const { return m_hasChangedProperties; } bool hasNewRootStateNode() const { return m_hasNewRootStateNode; } + void setHasNewRootStateNode(bool hasNewRoot) { m_hasNewRootStateNode = hasNewRoot; } int nodeCount() const { return m_stateNodeMap.size(); } @@ -76,17 +74,24 @@ public: void setPreferredLayerRepresentation(LayerRepresentation::Type representation) { m_preferredLayerRepresentation = representation; } private: - ScrollingStateTree(AsyncScrollingCoordinator*); - - void setRootStateNode(PassOwnPtr<ScrollingStateScrollingNode> rootStateNode) { m_rootStateNode = rootStateNode; } + void setRootStateNode(Ref<ScrollingStateFrameScrollingNode>&& rootStateNode) { m_rootStateNode = WTFMove(rootStateNode); } void addNode(ScrollingStateNode*); - void removeNode(ScrollingStateNode*); - void didRemoveNode(ScrollingNodeID); + + Ref<ScrollingStateNode> createNode(ScrollingNodeType, ScrollingNodeID); + + bool nodeTypeAndParentMatch(ScrollingStateNode&, ScrollingNodeType, ScrollingNodeID parentID) const; + + enum class SubframeNodeRemoval { Delete, Orphan }; + void removeNodeAndAllDescendants(ScrollingStateNode*, SubframeNodeRemoval = SubframeNodeRemoval::Delete); + + void recursiveNodeWillBeRemoved(ScrollingStateNode* currNode, SubframeNodeRemoval); + void willRemoveNode(ScrollingStateNode*); AsyncScrollingCoordinator* m_scrollingCoordinator; StateNodeMap m_stateNodeMap; - OwnPtr<ScrollingStateScrollingNode> m_rootStateNode; - Vector<ScrollingNodeID> m_nodesRemovedSinceLastCommit; + RefPtr<ScrollingStateFrameScrollingNode> m_rootStateNode; + HashSet<ScrollingNodeID> m_nodesRemovedSinceLastCommit; + HashMap<ScrollingNodeID, RefPtr<ScrollingStateNode>> m_orphanedSubframeNodes; bool m_hasChangedProperties; bool m_hasNewRootStateNode; LayerRepresentation::Type m_preferredLayerRepresentation; @@ -94,6 +99,9 @@ private: } // namespace WebCore -#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) +#ifndef NDEBUG +void showScrollingStateTree(const WebCore::ScrollingStateTree*); +void showScrollingStateTree(const WebCore::ScrollingStateNode*); +#endif -#endif // ScrollingStateTree_h +#endif // ENABLE(ASYNC_SCROLLING) || USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/ScrollingThread.cpp b/Source/WebCore/page/scrolling/ScrollingThread.cpp new file mode 100644 index 000000000..730c0a6bd --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingThread.cpp @@ -0,0 +1,120 @@ +/* + * 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. 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 "ScrollingThread.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include <mutex> +#include <wtf/MainThread.h> +#include <wtf/NeverDestroyed.h> + +namespace WebCore { + +ScrollingThread::ScrollingThread() + : m_threadIdentifier(0) +{ +} + +bool ScrollingThread::isCurrentThread() +{ + auto threadIdentifier = ScrollingThread::singleton().m_threadIdentifier; + return threadIdentifier && currentThread() == threadIdentifier; +} + +void ScrollingThread::dispatch(Function<void ()>&& function) +{ + auto& scrollingThread = ScrollingThread::singleton(); + scrollingThread.createThreadIfNeeded(); + + { + std::lock_guard<Lock> lock(scrollingThread.m_functionsMutex); + scrollingThread.m_functions.append(WTFMove(function)); + } + + scrollingThread.wakeUpRunLoop(); +} + +void ScrollingThread::dispatchBarrier(Function<void ()>&& function) +{ + dispatch([function = WTFMove(function)]() mutable { + callOnMainThread(WTFMove(function)); + }); +} + +ScrollingThread& ScrollingThread::singleton() +{ + static NeverDestroyed<ScrollingThread> scrollingThread; + + return scrollingThread; +} + +void ScrollingThread::createThreadIfNeeded() +{ + if (m_threadIdentifier) + return; + + // Wait for the thread to initialize the run loop. + { + std::unique_lock<Lock> lock(m_initializeRunLoopMutex); + + m_threadIdentifier = createThread(threadCallback, this, "WebCore: Scrolling"); + +#if PLATFORM(COCOA) + m_initializeRunLoopConditionVariable.wait(lock, [this]{ return m_threadRunLoop; }); +#endif + } +} + +void ScrollingThread::threadCallback(void* scrollingThread) +{ + WTF::setCurrentThreadIsUserInteractive(); + static_cast<ScrollingThread*>(scrollingThread)->threadBody(); +} + +void ScrollingThread::threadBody() +{ + initializeRunLoop(); +} + +void ScrollingThread::dispatchFunctionsFromScrollingThread() +{ + ASSERT(isCurrentThread()); + + Vector<Function<void ()>> functions; + + { + std::lock_guard<Lock> lock(m_functionsMutex); + functions = WTFMove(m_functions); + } + + for (auto& function : functions) + function(); +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingThread.h b/Source/WebCore/page/scrolling/ScrollingThread.h new file mode 100644 index 000000000..de062d77a --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingThread.h @@ -0,0 +1,93 @@ +/* + * 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. 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 + +#if ENABLE(ASYNC_SCROLLING) + +#include <functional> +#include <wtf/Condition.h> +#include <wtf/Forward.h> +#include <wtf/Function.h> +#include <wtf/Lock.h> +#include <wtf/Noncopyable.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +#if PLATFORM(COCOA) +#include <wtf/RetainPtr.h> +#endif + +namespace WebCore { + +class ScrollingThread { + WTF_MAKE_NONCOPYABLE(ScrollingThread); + +public: + static bool isCurrentThread(); + WEBCORE_EXPORT static void dispatch(Function<void ()>&&); + + // Will dispatch the given function on the main thread once all pending functions + // on the scrolling thread have finished executing. Used for synchronization purposes. + WEBCORE_EXPORT static void dispatchBarrier(Function<void ()>&&); + +private: + friend NeverDestroyed<ScrollingThread>; + + ScrollingThread(); + + static ScrollingThread& singleton(); + + void createThreadIfNeeded(); + static void threadCallback(void* scrollingThread); + void threadBody(); + void dispatchFunctionsFromScrollingThread(); + + void initializeRunLoop(); + void wakeUpRunLoop(); + +#if PLATFORM(COCOA) + static void threadRunLoopSourceCallback(void* scrollingThread); + void threadRunLoopSourceCallback(); +#endif + + ThreadIdentifier m_threadIdentifier; + + Condition m_initializeRunLoopConditionVariable; + Lock m_initializeRunLoopMutex; + + Lock m_functionsMutex; + Vector<Function<void ()>> m_functions; + +#if PLATFORM(COCOA) + // FIXME: We should use WebCore::RunLoop here. + RetainPtr<CFRunLoopRef> m_threadRunLoop; + RetainPtr<CFRunLoopSourceRef> m_threadRunLoopSource; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTree.cpp b/Source/WebCore/page/scrolling/ScrollingTree.cpp new file mode 100644 index 000000000..40bcc63ff --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTree.cpp @@ -0,0 +1,410 @@ +/* + * 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. 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 "ScrollingTree.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include "EventNames.h" +#include "Logging.h" +#include "PlatformWheelEvent.h" +#include "ScrollingStateTree.h" +#include "ScrollingTreeFrameScrollingNode.h" +#include "ScrollingTreeNode.h" +#include "ScrollingTreeOverflowScrollingNode.h" +#include "ScrollingTreeScrollingNode.h" +#include "TextStream.h" +#include <wtf/SetForScope.h> + +namespace WebCore { + +ScrollingTree::ScrollingTree() +{ +} + +ScrollingTree::~ScrollingTree() +{ +} + +bool ScrollingTree::shouldHandleWheelEventSynchronously(const PlatformWheelEvent& wheelEvent) +{ + // This method is invoked by the event handling thread + LockHolder lock(m_mutex); + + bool shouldSetLatch = wheelEvent.shouldConsiderLatching(); + + if (hasLatchedNode() && !shouldSetLatch) + return false; + + if (shouldSetLatch) + m_latchedNode = 0; + + if (!m_eventTrackingRegions.isEmpty() && m_rootNode) { + ScrollingTreeFrameScrollingNode& frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(*m_rootNode); + FloatPoint position = wheelEvent.position(); + position.move(frameScrollingNode.viewToContentsOffset(m_mainFrameScrollPosition)); + + const EventNames& names = eventNames(); + IntPoint roundedPosition = roundedIntPoint(position); + bool isSynchronousDispatchRegion = m_eventTrackingRegions.trackingTypeForPoint(names.wheelEvent, roundedPosition) == TrackingType::Synchronous + || m_eventTrackingRegions.trackingTypeForPoint(names.mousewheelEvent, roundedPosition) == TrackingType::Synchronous; + LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree::shouldHandleWheelEventSynchronously: wheelEvent at " << wheelEvent.position() << " mapped to content point " << position << ", in non-fast region " << isSynchronousDispatchRegion); + + if (isSynchronousDispatchRegion) + return true; + } + return false; +} + +void ScrollingTree::setOrClearLatchedNode(const PlatformWheelEvent& wheelEvent, ScrollingNodeID nodeID) +{ + if (wheelEvent.shouldConsiderLatching()) + setLatchedNode(nodeID); + else if (wheelEvent.shouldResetLatching()) + clearLatchedNode(); +} + +void ScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent) +{ + if (m_rootNode) + downcast<ScrollingTreeScrollingNode>(*m_rootNode).handleWheelEvent(wheelEvent); +} + +void ScrollingTree::viewportChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const FloatRect& fixedPositionRect, double scale) +{ + ScrollingTreeNode* node = nodeForID(nodeID); + if (!is<ScrollingTreeScrollingNode>(node)) + return; + + downcast<ScrollingTreeScrollingNode>(*node).updateLayersAfterViewportChange(fixedPositionRect, scale); +} + +void ScrollingTree::scrollPositionChangedViaDelegatedScrolling(ScrollingNodeID nodeID, const WebCore::FloatPoint& scrollPosition, bool inUserInteration) +{ + ScrollingTreeNode* node = nodeForID(nodeID); + if (!is<ScrollingTreeOverflowScrollingNode>(node)) + return; + + // Update descendant nodes + downcast<ScrollingTreeOverflowScrollingNode>(*node).updateLayersAfterDelegatedScroll(scrollPosition); + + // Update GraphicsLayers and scroll state. + scrollingTreeNodeDidScroll(nodeID, scrollPosition, std::nullopt, inUserInteration ? ScrollingLayerPositionAction::Sync : ScrollingLayerPositionAction::Set); +} + +void ScrollingTree::commitTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree) +{ + bool rootStateNodeChanged = scrollingStateTree->hasNewRootStateNode(); + + ScrollingStateScrollingNode* rootNode = scrollingStateTree->rootStateNode(); + if (rootNode + && (rootStateNodeChanged + || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::EventTrackingRegion) + || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer) + || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::VisualViewportEnabled))) { + LockHolder lock(m_mutex); + + if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateNode::ScrollLayer)) + m_mainFrameScrollPosition = FloatPoint(); + + if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::EventTrackingRegion)) + m_eventTrackingRegions = scrollingStateTree->rootStateNode()->eventTrackingRegions(); + + if (rootStateNodeChanged || rootNode->hasChangedProperty(ScrollingStateFrameScrollingNode::VisualViewportEnabled)) + m_visualViewportEnabled = scrollingStateTree->rootStateNode()->visualViewportEnabled(); + } + + bool scrollRequestIsProgammatic = rootNode ? rootNode->requestedScrollPositionRepresentsProgrammaticScroll() : false; + SetForScope<bool> changeHandlingProgrammaticScroll(m_isHandlingProgrammaticScroll, scrollRequestIsProgammatic); + + removeDestroyedNodes(*scrollingStateTree); + + OrphanScrollingNodeMap orphanNodes; + updateTreeFromStateNode(rootNode, orphanNodes); +} + +void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes) +{ + if (!stateNode) { + m_nodeMap.clear(); + m_rootNode = nullptr; + return; + } + + ScrollingNodeID nodeID = stateNode->scrollingNodeID(); + ScrollingNodeID parentNodeID = stateNode->parentNodeID(); + + auto it = m_nodeMap.find(nodeID); + + RefPtr<ScrollingTreeNode> node; + if (it != m_nodeMap.end()) + node = it->value; + else { + node = createScrollingTreeNode(stateNode->nodeType(), nodeID); + if (!parentNodeID) { + // This is the root node. Clear the node map. + ASSERT(stateNode->nodeType() == FrameScrollingNode); + m_rootNode = node; + m_nodeMap.clear(); + } + m_nodeMap.set(nodeID, node.get()); + } + + if (parentNodeID) { + auto parentIt = m_nodeMap.find(parentNodeID); + ASSERT_WITH_SECURITY_IMPLICATION(parentIt != m_nodeMap.end()); + if (parentIt != m_nodeMap.end()) { + ScrollingTreeNode* parent = parentIt->value; + node->setParent(parent); + parent->appendChild(*node); + } + } + + node->commitStateBeforeChildren(*stateNode); + + // Move all children into the orphanNodes map. Live ones will get added back as we recurse over children. + if (auto nodeChildren = node->children()) { + for (auto& childScrollingNode : *nodeChildren) { + childScrollingNode->setParent(nullptr); + orphanNodes.add(childScrollingNode->scrollingNodeID(), childScrollingNode.get()); + } + nodeChildren->clear(); + } + + // Now update the children if we have any. + if (auto children = stateNode->children()) { + for (auto& child : *children) + updateTreeFromStateNode(child.get(), orphanNodes); + } + + node->commitStateAfterChildren(*stateNode); +} + +void ScrollingTree::removeDestroyedNodes(const ScrollingStateTree& stateTree) +{ + for (const auto& removedNodeID : stateTree.removedNodes()) { + m_nodeMap.remove(removedNodeID); + if (removedNodeID == m_latchedNode) + clearLatchedNode(); + } +} + +ScrollingTreeNode* ScrollingTree::nodeForID(ScrollingNodeID nodeID) const +{ + if (!nodeID) + return nullptr; + + return m_nodeMap.get(nodeID); +} + +void ScrollingTree::setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom) +{ + LockHolder locker(m_swipeStateMutex); + + m_mainFramePinnedToTheLeft = pinnedToTheLeft; + m_mainFramePinnedToTheRight = pinnedToTheRight; + m_mainFramePinnedToTheTop = pinnedToTheTop; + m_mainFramePinnedToTheBottom = pinnedToTheBottom; +} + +FloatPoint ScrollingTree::mainFrameScrollPosition() +{ + LockHolder lock(m_mutex); + return m_mainFrameScrollPosition; +} + +void ScrollingTree::setMainFrameScrollPosition(FloatPoint position) +{ + LockHolder lock(m_mutex); + m_mainFrameScrollPosition = position; +} + +TrackingType ScrollingTree::eventTrackingTypeForPoint(const AtomicString& eventName, IntPoint p) +{ + LockHolder lock(m_mutex); + + return m_eventTrackingRegions.trackingTypeForPoint(eventName, p); +} + +bool ScrollingTree::isRubberBandInProgress() +{ + LockHolder lock(m_mutex); + + return m_mainFrameIsRubberBanding; +} + +void ScrollingTree::setMainFrameIsRubberBanding(bool isRubberBanding) +{ + LockHolder locker(m_mutex); + + m_mainFrameIsRubberBanding = isRubberBanding; +} + +bool ScrollingTree::isScrollSnapInProgress() +{ + LockHolder lock(m_mutex); + + return m_mainFrameIsScrollSnapping; +} + +void ScrollingTree::setMainFrameIsScrollSnapping(bool isScrollSnapping) +{ + LockHolder locker(m_mutex); + + m_mainFrameIsScrollSnapping = isScrollSnapping; +} + +void ScrollingTree::setCanRubberBandState(bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom) +{ + LockHolder locker(m_swipeStateMutex); + + m_rubberBandsAtLeft = canRubberBandAtLeft; + m_rubberBandsAtRight = canRubberBandAtRight; + m_rubberBandsAtTop = canRubberBandAtTop; + m_rubberBandsAtBottom = canRubberBandAtBottom; +} + +bool ScrollingTree::rubberBandsAtLeft() +{ + LockHolder lock(m_swipeStateMutex); + + return m_rubberBandsAtLeft; +} + +bool ScrollingTree::rubberBandsAtRight() +{ + LockHolder lock(m_swipeStateMutex); + + return m_rubberBandsAtRight; +} + +bool ScrollingTree::rubberBandsAtBottom() +{ + LockHolder lock(m_swipeStateMutex); + + return m_rubberBandsAtBottom; +} + +bool ScrollingTree::rubberBandsAtTop() +{ + LockHolder lock(m_swipeStateMutex); + + return m_rubberBandsAtTop; +} + +bool ScrollingTree::isHandlingProgrammaticScroll() +{ + return m_isHandlingProgrammaticScroll; +} + +void ScrollingTree::setScrollPinningBehavior(ScrollPinningBehavior pinning) +{ + LockHolder locker(m_swipeStateMutex); + + m_scrollPinningBehavior = pinning; +} + +ScrollPinningBehavior ScrollingTree::scrollPinningBehavior() +{ + LockHolder lock(m_swipeStateMutex); + + return m_scrollPinningBehavior; +} + +bool ScrollingTree::willWheelEventStartSwipeGesture(const PlatformWheelEvent& wheelEvent) +{ + if (wheelEvent.phase() != PlatformWheelEventPhaseBegan) + return false; + + LockHolder lock(m_swipeStateMutex); + + if (wheelEvent.deltaX() > 0 && m_mainFramePinnedToTheLeft && !m_rubberBandsAtLeft) + return true; + if (wheelEvent.deltaX() < 0 && m_mainFramePinnedToTheRight && !m_rubberBandsAtRight) + return true; + if (wheelEvent.deltaY() > 0 && m_mainFramePinnedToTheTop && !m_rubberBandsAtTop) + return true; + if (wheelEvent.deltaY() < 0 && m_mainFramePinnedToTheBottom && !m_rubberBandsAtBottom) + return true; + + return false; +} + +void ScrollingTree::setScrollingPerformanceLoggingEnabled(bool flag) +{ + m_scrollingPerformanceLoggingEnabled = flag; +} + +bool ScrollingTree::scrollingPerformanceLoggingEnabled() +{ + return m_scrollingPerformanceLoggingEnabled; +} + +ScrollingNodeID ScrollingTree::latchedNode() +{ + LockHolder locker(m_mutex); + return m_latchedNode; +} + +void ScrollingTree::setLatchedNode(ScrollingNodeID node) +{ + LockHolder locker(m_mutex); + m_latchedNode = node; +} + +void ScrollingTree::clearLatchedNode() +{ + LockHolder locker(m_mutex); + m_latchedNode = 0; +} + +String ScrollingTree::scrollingTreeAsText() +{ + TextStream ts(TextStream::LineMode::MultipleLine); + + TextStream::GroupScope scope(ts); + ts << "scrolling tree"; + + if (m_latchedNode) + ts.dumpProperty("latched node", m_latchedNode); + + if (m_mainFrameScrollPosition != IntPoint()) + ts.dumpProperty("main frame scroll position", m_mainFrameScrollPosition); + + { + LockHolder lock(m_mutex); + if (m_rootNode) { + TextStream::GroupScope scope(ts); + m_rootNode->dump(ts, ScrollingStateTreeAsTextBehaviorIncludeLayerPositions); + } + } + + return ts.release(); +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTree.h b/Source/WebCore/page/scrolling/ScrollingTree.h new file mode 100644 index 000000000..aaf2d478b --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTree.h @@ -0,0 +1,200 @@ +/* + * 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. 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 + +#if ENABLE(ASYNC_SCROLLING) + +#include "PlatformWheelEvent.h" +#include "Region.h" +#include "ScrollingCoordinator.h" +#include "WheelEventTestTrigger.h" +#include <wtf/HashMap.h> +#include <wtf/Lock.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/TypeCasts.h> + +namespace WebCore { + +class IntPoint; +class ScrollingStateTree; +class ScrollingStateNode; +class ScrollingTreeNode; +class ScrollingTreeScrollingNode; + +class ScrollingTree : public ThreadSafeRefCounted<ScrollingTree> { +public: + WEBCORE_EXPORT ScrollingTree(); + WEBCORE_EXPORT virtual ~ScrollingTree(); + + enum EventResult { + DidNotHandleEvent, + DidHandleEvent, + SendToMainThread + }; + + virtual bool isThreadedScrollingTree() const { return false; } + virtual bool isRemoteScrollingTree() const { return false; } + virtual bool isScrollingTreeIOS() const { return false; } + + bool visualViewportEnabled() const { return m_visualViewportEnabled; } + + virtual EventResult tryToHandleWheelEvent(const PlatformWheelEvent&) = 0; + WEBCORE_EXPORT bool shouldHandleWheelEventSynchronously(const PlatformWheelEvent&); + + void setMainFrameIsRubberBanding(bool); + bool isRubberBandInProgress(); + void setMainFrameIsScrollSnapping(bool); + bool isScrollSnapInProgress(); + + virtual void invalidate() { } + WEBCORE_EXPORT virtual void commitTreeState(std::unique_ptr<ScrollingStateTree>); + + void setMainFramePinState(bool pinnedToTheLeft, bool pinnedToTheRight, bool pinnedToTheTop, bool pinnedToTheBottom); + + virtual Ref<ScrollingTreeNode> createScrollingTreeNode(ScrollingNodeType, ScrollingNodeID) = 0; + + // Called after a scrolling tree node has handled a scroll and updated its layers. + // Updates FrameView/RenderLayer scrolling state and GraphicsLayers. + virtual void scrollingTreeNodeDidScroll(ScrollingNodeID, const FloatPoint& scrollPosition, const std::optional<FloatPoint>& layoutViewportOrigin, ScrollingLayerPositionAction = ScrollingLayerPositionAction::Sync) = 0; + + // Called for requested scroll position updates. + virtual void scrollingTreeNodeRequestsScroll(ScrollingNodeID, const FloatPoint& /*scrollPosition*/, bool /*representsProgrammaticScroll*/) { } + + // Delegated scrolling/zooming has caused the viewport to change, so update viewport-constrained layers + // (but don't cause scroll events to be fired). + WEBCORE_EXPORT virtual void viewportChangedViaDelegatedScrolling(ScrollingNodeID, const WebCore::FloatRect& fixedPositionRect, double scale); + + // Delegated scrolling has scrolled a node. Update layer positions on descendant tree nodes, + // and call scrollingTreeNodeDidScroll(). + WEBCORE_EXPORT virtual void scrollPositionChangedViaDelegatedScrolling(ScrollingNodeID, const WebCore::FloatPoint& scrollPosition, bool inUserInteration); + + WEBCORE_EXPORT virtual void currentSnapPointIndicesDidChange(ScrollingNodeID, unsigned horizontal, unsigned vertical) = 0; + + FloatPoint mainFrameScrollPosition(); + +#if PLATFORM(IOS) + virtual FloatRect fixedPositionRect() = 0; + virtual void scrollingTreeNodeWillStartPanGesture() { } + virtual void scrollingTreeNodeWillStartScroll() { } + virtual void scrollingTreeNodeDidEndScroll() { } +#endif + + WEBCORE_EXPORT TrackingType eventTrackingTypeForPoint(const AtomicString& eventName, IntPoint); + +#if PLATFORM(MAC) + virtual void handleWheelEventPhase(PlatformWheelEventPhase) = 0; + virtual void setActiveScrollSnapIndices(ScrollingNodeID, unsigned /*horizontalIndex*/, unsigned /*verticalIndex*/) { } + virtual void deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) { } + virtual void removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) { } +#endif + + // Can be called from any thread. Will update what edges allow rubber-banding. + WEBCORE_EXPORT void setCanRubberBandState(bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom); + + bool rubberBandsAtLeft(); + bool rubberBandsAtRight(); + bool rubberBandsAtTop(); + bool rubberBandsAtBottom(); + bool isHandlingProgrammaticScroll(); + + void setScrollPinningBehavior(ScrollPinningBehavior); + ScrollPinningBehavior scrollPinningBehavior(); + + WEBCORE_EXPORT bool willWheelEventStartSwipeGesture(const PlatformWheelEvent&); + + WEBCORE_EXPORT void setScrollingPerformanceLoggingEnabled(bool flag); + bool scrollingPerformanceLoggingEnabled(); + + ScrollingTreeNode* rootNode() const { return m_rootNode.get(); } + + ScrollingNodeID latchedNode(); + void setLatchedNode(ScrollingNodeID); + void clearLatchedNode(); + + bool hasLatchedNode() const { return m_latchedNode; } + void setOrClearLatchedNode(const PlatformWheelEvent&, ScrollingNodeID); + + bool hasFixedOrSticky() const { return !!m_fixedOrStickyNodeCount; } + void fixedOrStickyNodeAdded() { ++m_fixedOrStickyNodeCount; } + void fixedOrStickyNodeRemoved() + { + ASSERT(m_fixedOrStickyNodeCount); + --m_fixedOrStickyNodeCount; + } + + WEBCORE_EXPORT String scrollingTreeAsText(); + +protected: + void setMainFrameScrollPosition(FloatPoint); + void setVisualViewportEnabled(bool b) { m_visualViewportEnabled = b; } + + WEBCORE_EXPORT virtual void handleWheelEvent(const PlatformWheelEvent&); + +private: + void removeDestroyedNodes(const ScrollingStateTree&); + + typedef HashMap<ScrollingNodeID, RefPtr<ScrollingTreeNode>> OrphanScrollingNodeMap; + void updateTreeFromStateNode(const ScrollingStateNode*, OrphanScrollingNodeMap&); + + ScrollingTreeNode* nodeForID(ScrollingNodeID) const; + + RefPtr<ScrollingTreeNode> m_rootNode; + + typedef HashMap<ScrollingNodeID, ScrollingTreeNode*> ScrollingTreeNodeMap; + ScrollingTreeNodeMap m_nodeMap; + + Lock m_mutex; + EventTrackingRegions m_eventTrackingRegions; + FloatPoint m_mainFrameScrollPosition; + + Lock m_swipeStateMutex; + ScrollPinningBehavior m_scrollPinningBehavior { DoNotPin }; + ScrollingNodeID m_latchedNode { 0 }; + + unsigned m_fixedOrStickyNodeCount { 0 }; + + bool m_rubberBandsAtLeft { true }; + bool m_rubberBandsAtRight { true }; + bool m_rubberBandsAtTop { true }; + bool m_rubberBandsAtBottom { true }; + bool m_mainFramePinnedToTheLeft { true }; + bool m_mainFramePinnedToTheRight { true }; + bool m_mainFramePinnedToTheTop { true }; + bool m_mainFramePinnedToTheBottom { true }; + bool m_mainFrameIsRubberBanding { false }; + bool m_mainFrameIsScrollSnapping { false }; + bool m_scrollingPerformanceLoggingEnabled { false }; + bool m_isHandlingProgrammaticScroll { false }; + bool m_visualViewportEnabled { false }; +}; + +} // namespace WebCore + +#define SPECIALIZE_TYPE_TRAITS_SCROLLING_TREE(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \ + static bool isType(const WebCore::ScrollingTree& tree) { return tree.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp b/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp new file mode 100644 index 000000000..17ac7a431 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.cpp @@ -0,0 +1,157 @@ +/* + * 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. + */ + +#include "config.h" +#include "ScrollingTreeFrameScrollingNode.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include "FrameView.h" +#include "Logging.h" +#include "ScrollingStateTree.h" +#include "ScrollingTree.h" +#include "TextStream.h" + +namespace WebCore { + +ScrollingTreeFrameScrollingNode::ScrollingTreeFrameScrollingNode(ScrollingTree& scrollingTree, ScrollingNodeID nodeID) + : ScrollingTreeScrollingNode(scrollingTree, FrameScrollingNode, nodeID) +{ +} + +ScrollingTreeFrameScrollingNode::~ScrollingTreeFrameScrollingNode() +{ +} + +void ScrollingTreeFrameScrollingNode::commitStateBeforeChildren(const ScrollingStateNode& stateNode) +{ + ScrollingTreeScrollingNode::commitStateBeforeChildren(stateNode); + + const ScrollingStateFrameScrollingNode& state = downcast<ScrollingStateFrameScrollingNode>(stateNode); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::FrameScaleFactor)) + m_frameScaleFactor = state.frameScaleFactor(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::ReasonsForSynchronousScrolling)) + m_synchronousScrollingReasons = state.synchronousScrollingReasons(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::HeaderHeight)) + m_headerHeight = state.headerHeight(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::FooterHeight)) + m_footerHeight = state.footerHeight(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::BehaviorForFixedElements)) + m_behaviorForFixed = state.scrollBehaviorForFixedElements(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::TopContentInset)) + m_topContentInset = state.topContentInset(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::FixedElementsLayoutRelativeToFrame)) + m_fixedElementsLayoutRelativeToFrame = state.fixedElementsLayoutRelativeToFrame(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::LayoutViewport)) + m_layoutViewport = state.layoutViewport(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::MinLayoutViewportOrigin)) + m_minLayoutViewportOrigin = state.minLayoutViewportOrigin(); + + if (state.hasChangedProperty(ScrollingStateFrameScrollingNode::MaxLayoutViewportOrigin)) + m_maxLayoutViewportOrigin = state.maxLayoutViewportOrigin(); +} + +void ScrollingTreeFrameScrollingNode::scrollBy(const FloatSize& delta) +{ + setScrollPosition(scrollPosition() + delta); +} + +void ScrollingTreeFrameScrollingNode::scrollByWithoutContentEdgeConstraints(const FloatSize& offset) +{ + setScrollPositionWithoutContentEdgeConstraints(scrollPosition() + offset); +} + +void ScrollingTreeFrameScrollingNode::setScrollPosition(const FloatPoint& scrollPosition) +{ + FloatPoint newScrollPosition = scrollPosition.constrainedBetween(minimumScrollPosition(), maximumScrollPosition()); + setScrollPositionWithoutContentEdgeConstraints(newScrollPosition); +} + +FloatRect ScrollingTreeFrameScrollingNode::layoutViewportForScrollPosition(const FloatPoint& visibleContentOrigin, float scale) const +{ + ASSERT(scrollingTree().visualViewportEnabled()); + + FloatRect visibleContentRect(visibleContentOrigin, scrollableAreaSize()); + LayoutRect visualViewport(FrameView::visibleDocumentRect(visibleContentRect, headerHeight(), footerHeight(), totalContentsSize(), scale)); + LayoutRect layoutViewport(m_layoutViewport); + + LOG_WITH_STREAM(Scrolling, stream << "\nScrolling thread: " << "(visibleContentOrigin " << visibleContentOrigin << ") fixed behavior " << m_behaviorForFixed); + LOG_WITH_STREAM(Scrolling, stream << " layoutViewport: " << layoutViewport); + LOG_WITH_STREAM(Scrolling, stream << " visualViewport: " << visualViewport); + LOG_WITH_STREAM(Scrolling, stream << " scroll positions: min: " << minLayoutViewportOrigin() << " max: "<< maxLayoutViewportOrigin()); + + LayoutPoint newLocation = FrameView::computeLayoutViewportOrigin(LayoutRect(visualViewport), LayoutPoint(minLayoutViewportOrigin()), LayoutPoint(maxLayoutViewportOrigin()), layoutViewport, m_behaviorForFixed); + + if (layoutViewport.location() != newLocation) { + layoutViewport.setLocation(newLocation); + LOG_WITH_STREAM(Scrolling, stream << " new layoutViewport " << layoutViewport); + } + + return layoutViewport; +} + +FloatSize ScrollingTreeFrameScrollingNode::viewToContentsOffset(const FloatPoint& scrollPosition) const +{ + return toFloatSize(scrollPosition) - FloatSize(0, headerHeight() + topContentInset()); +} + +void ScrollingTreeFrameScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior) const +{ + ts << "frame scrolling node"; + + ts.dumpProperty("layout viewport", m_layoutViewport); + ts.dumpProperty("min layoutViewport origin", m_minLayoutViewportOrigin); + ts.dumpProperty("max layoutViewport origin", m_maxLayoutViewportOrigin); + + if (m_frameScaleFactor != 1) + ts.dumpProperty("frame scale factor", m_frameScaleFactor); + if (m_topContentInset) + ts.dumpProperty("top content inset", m_topContentInset); + + if (m_headerHeight) + ts.dumpProperty("header height", m_headerHeight); + if (m_footerHeight) + ts.dumpProperty("footer height", m_footerHeight); + if (m_synchronousScrollingReasons) + ts.dumpProperty("synchronous scrolling reasons", ScrollingCoordinator::synchronousScrollingReasonsAsText(m_synchronousScrollingReasons)); + + ts.dumpProperty("behavior for fixed", m_behaviorForFixed); + if (m_fixedElementsLayoutRelativeToFrame) + ts.dumpProperty("fixed elements lay out relative to frame", m_fixedElementsLayoutRelativeToFrame); +} + + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.h b/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.h new file mode 100644 index 000000000..43488c4fa --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeFrameScrollingNode.h @@ -0,0 +1,103 @@ +/* + * 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 + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingTreeScrollingNode.h" + +namespace WebCore { + +class PlatformWheelEvent; +class ScrollingTree; +class ScrollingStateScrollingNode; + +class ScrollingTreeFrameScrollingNode : public ScrollingTreeScrollingNode { +public: + virtual ~ScrollingTreeFrameScrollingNode(); + + void commitStateBeforeChildren(const ScrollingStateNode&) override; + + // FIXME: We should implement this when we support ScrollingTreeScrollingNodes as children. + void updateLayersAfterAncestorChange(const ScrollingTreeNode& /*changedNode*/, const FloatRect& /*fixedPositionRect*/, const FloatSize& /*cumulativeDelta*/) override { } + + void handleWheelEvent(const PlatformWheelEvent&) override = 0; + void setScrollPosition(const FloatPoint&) override; + void setScrollPositionWithoutContentEdgeConstraints(const FloatPoint&) override = 0; + + void updateLayersAfterViewportChange(const FloatRect& fixedPositionRect, double scale) override = 0; + void updateLayersAfterDelegatedScroll(const FloatPoint&) override { } + + SynchronousScrollingReasons synchronousScrollingReasons() const { return m_synchronousScrollingReasons; } + bool shouldUpdateScrollLayerPositionSynchronously() const { return m_synchronousScrollingReasons; } + bool fixedElementsLayoutRelativeToFrame() const { return m_fixedElementsLayoutRelativeToFrame; } + + FloatSize viewToContentsOffset(const FloatPoint& scrollPosition) const; + FloatRect layoutViewportForScrollPosition(const FloatPoint& visibleContentOrigin, float scale) const; + +protected: + ScrollingTreeFrameScrollingNode(ScrollingTree&, ScrollingNodeID); + + void scrollBy(const FloatSize&); + void scrollByWithoutContentEdgeConstraints(const FloatSize&); + + float frameScaleFactor() const { return m_frameScaleFactor; } + int headerHeight() const { return m_headerHeight; } + int footerHeight() const { return m_footerHeight; } + float topContentInset() const { return m_topContentInset; } + + FloatRect layoutViewport() const { return m_layoutViewport; }; + void setLayoutViewport(const FloatRect& r) { m_layoutViewport = r; }; + + FloatPoint minLayoutViewportOrigin() const { return m_minLayoutViewportOrigin; } + FloatPoint maxLayoutViewportOrigin() const { return m_maxLayoutViewportOrigin; } + + ScrollBehaviorForFixedElements scrollBehaviorForFixedElements() const { return m_behaviorForFixed; } + +private: + void dumpProperties(TextStream&, ScrollingStateTreeAsTextBehavior) const override; + + FloatRect m_layoutViewport; + FloatPoint m_minLayoutViewportOrigin; + FloatPoint m_maxLayoutViewportOrigin; + + float m_frameScaleFactor { 1 }; + float m_topContentInset { 0 }; + + int m_headerHeight { 0 }; + int m_footerHeight { 0 }; + + SynchronousScrollingReasons m_synchronousScrollingReasons { 0 }; + ScrollBehaviorForFixedElements m_behaviorForFixed { StickToDocumentBounds }; + + bool m_fixedElementsLayoutRelativeToFrame { false }; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_NODE(ScrollingTreeFrameScrollingNode, isFrameScrollingNode()) + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp b/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp new file mode 100644 index 000000000..0f6616d0f --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012 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 "ScrollingTreeNode.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingStateTree.h" +#include "TextStream.h" + +namespace WebCore { + +ScrollingTreeNode::ScrollingTreeNode(ScrollingTree& scrollingTree, ScrollingNodeType nodeType, ScrollingNodeID nodeID) + : m_scrollingTree(scrollingTree) + , m_nodeType(nodeType) + , m_nodeID(nodeID) + , m_parent(nullptr) +{ +} + +ScrollingTreeNode::~ScrollingTreeNode() +{ +} + +void ScrollingTreeNode::appendChild(Ref<ScrollingTreeNode>&& childNode) +{ + childNode->setParent(this); + + if (!m_children) + m_children = std::make_unique<Vector<RefPtr<ScrollingTreeNode>>>(); + m_children->append(WTFMove(childNode)); +} + +void ScrollingTreeNode::removeChild(ScrollingTreeNode& node) +{ + if (!m_children) + return; + + size_t index = m_children->find(&node); + + // The index will be notFound if the node to remove is a deeper-than-1-level descendant or + // if node is the root state node. + if (index != notFound) { + m_children->remove(index); + return; + } + + for (auto& child : *m_children) + child->removeChild(node); +} + +void ScrollingTreeNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const +{ + if (behavior & ScrollingStateTreeAsTextBehaviorIncludeNodeIDs) + ts.dumpProperty("nodeID", scrollingNodeID()); +} + +void ScrollingTreeNode::dump(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const +{ + dumpProperties(ts, behavior); + + if (m_children) { + for (auto& child : *m_children) { + TextStream::GroupScope scope(ts); + child->dump(ts, behavior); + } + } +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeNode.h b/Source/WebCore/page/scrolling/ScrollingTreeNode.h new file mode 100644 index 000000000..2e163246b --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeNode.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 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 + +#if ENABLE(ASYNC_SCROLLING) + +#include "IntRect.h" +#include "ScrollTypes.h" +#include "ScrollingCoordinator.h" +#include "ScrollingStateNode.h" +#include <wtf/RefCounted.h> +#include <wtf/TypeCasts.h> + +namespace WebCore { + +class ScrollingStateFixedNode; +class ScrollingStateScrollingNode; + +class ScrollingTreeNode : public RefCounted<ScrollingTreeNode> { +public: + virtual ~ScrollingTreeNode(); + + ScrollingNodeType nodeType() const { return m_nodeType; } + ScrollingNodeID scrollingNodeID() const { return m_nodeID; } + + bool isFixedNode() const { return nodeType() == FixedNode; } + bool isStickyNode() const { return nodeType() == StickyNode; } + bool isScrollingNode() const { return nodeType() == FrameScrollingNode || nodeType() == OverflowScrollingNode; } + bool isFrameScrollingNode() const { return nodeType() == FrameScrollingNode; } + bool isOverflowScrollingNode() const { return nodeType() == OverflowScrollingNode; } + + virtual void commitStateBeforeChildren(const ScrollingStateNode&) = 0; + virtual void commitStateAfterChildren(const ScrollingStateNode&) { } + + virtual void updateLayersAfterAncestorChange(const ScrollingTreeNode& changedNode, const FloatRect& fixedPositionRect, const FloatSize& cumulativeDelta) = 0; + + ScrollingTreeNode* parent() const { return m_parent; } + void setParent(ScrollingTreeNode* parent) { m_parent = parent; } + + Vector<RefPtr<ScrollingTreeNode>>* children() { return m_children.get(); } + + void appendChild(Ref<ScrollingTreeNode>&&); + void removeChild(ScrollingTreeNode&); + + WEBCORE_EXPORT void dump(TextStream&, ScrollingStateTreeAsTextBehavior) const; + +protected: + ScrollingTreeNode(ScrollingTree&, ScrollingNodeType, ScrollingNodeID); + ScrollingTree& scrollingTree() const { return m_scrollingTree; } + + std::unique_ptr<Vector<RefPtr<ScrollingTreeNode>>> m_children; + + WEBCORE_EXPORT virtual void dumpProperties(TextStream&, ScrollingStateTreeAsTextBehavior) const; + +private: + ScrollingTree& m_scrollingTree; + + const ScrollingNodeType m_nodeType; + const ScrollingNodeID m_nodeID; + + ScrollingTreeNode* m_parent; +}; + +} // namespace WebCore + +#define SPECIALIZE_TYPE_TRAITS_SCROLLING_NODE(ToValueTypeName, predicate) \ +SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \ + static bool isType(const WebCore::ScrollingTreeNode& node) { return node.predicate; } \ +SPECIALIZE_TYPE_TRAITS_END() + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp b/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp new file mode 100644 index 000000000..013e350e6 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#include "config.h" +#include "ScrollingTreeOverflowScrollingNode.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingStateTree.h" +#include "ScrollingTree.h" + +namespace WebCore { + +ScrollingTreeOverflowScrollingNode::ScrollingTreeOverflowScrollingNode(ScrollingTree& scrollingTree, ScrollingNodeID nodeID) + : ScrollingTreeScrollingNode(scrollingTree, OverflowScrollingNode, nodeID) +{ +} + +ScrollingTreeOverflowScrollingNode::~ScrollingTreeOverflowScrollingNode() +{ +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h b/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h new file mode 100644 index 000000000..97fb95cc4 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h @@ -0,0 +1,46 @@ +/* + * 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 + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingTreeScrollingNode.h" + +namespace WebCore { + +class ScrollingTreeOverflowScrollingNode : public ScrollingTreeScrollingNode { +public: + WEBCORE_EXPORT virtual ~ScrollingTreeOverflowScrollingNode(); + +protected: + WEBCORE_EXPORT ScrollingTreeOverflowScrollingNode(ScrollingTree&, ScrollingNodeID); +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_NODE(ScrollingTreeOverflowScrollingNode, isOverflowScrollingNode()) + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp b/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp new file mode 100644 index 000000000..19754f5c2 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2012 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 "ScrollingTreeScrollingNode.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingStateTree.h" +#include "ScrollingTree.h" +#include "TextStream.h" + +namespace WebCore { + +ScrollingTreeScrollingNode::ScrollingTreeScrollingNode(ScrollingTree& scrollingTree, ScrollingNodeType nodeType, ScrollingNodeID nodeID) + : ScrollingTreeNode(scrollingTree, nodeType, nodeID) +{ +} + +ScrollingTreeScrollingNode::~ScrollingTreeScrollingNode() +{ +} + +void ScrollingTreeScrollingNode::commitStateBeforeChildren(const ScrollingStateNode& stateNode) +{ + const ScrollingStateScrollingNode& state = downcast<ScrollingStateScrollingNode>(stateNode); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaSize)) + m_scrollableAreaSize = state.scrollableAreaSize(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::TotalContentsSize)) { + if (scrollingTree().isRubberBandInProgress()) + m_totalContentsSizeForRubberBand = m_totalContentsSize; + else + m_totalContentsSizeForRubberBand = state.totalContentsSize(); + + m_totalContentsSize = state.totalContentsSize(); + } + + if (state.hasChangedProperty(ScrollingStateScrollingNode::ReachableContentsSize)) + m_reachableContentsSize = state.reachableContentsSize(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollPosition)) + m_lastCommittedScrollPosition = state.scrollPosition(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollOrigin)) + m_scrollOrigin = state.scrollOrigin(); + +#if ENABLE(CSS_SCROLL_SNAP) + if (state.hasChangedProperty(ScrollingStateScrollingNode::HorizontalSnapOffsets)) + m_snapOffsetsInfo.horizontalSnapOffsets = state.horizontalSnapOffsets(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::VerticalSnapOffsets)) + m_snapOffsetsInfo.verticalSnapOffsets = state.verticalSnapOffsets(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::HorizontalSnapOffsetRanges)) + m_snapOffsetsInfo.horizontalSnapOffsetRanges = state.horizontalSnapOffsetRanges(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::VerticalSnapOffsetRanges)) + m_snapOffsetsInfo.verticalSnapOffsetRanges = state.verticalSnapOffsetRanges(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::CurrentHorizontalSnapOffsetIndex)) + m_currentHorizontalSnapPointIndex = state.currentHorizontalSnapPointIndex(); + + if (state.hasChangedProperty(ScrollingStateScrollingNode::CurrentVerticalSnapOffsetIndex)) + m_currentVerticalSnapPointIndex = state.currentVerticalSnapPointIndex(); +#endif + + if (state.hasChangedProperty(ScrollingStateScrollingNode::ScrollableAreaParams)) + m_scrollableAreaParameters = state.scrollableAreaParameters(); +} + +void ScrollingTreeScrollingNode::commitStateAfterChildren(const ScrollingStateNode& stateNode) +{ + const ScrollingStateScrollingNode& scrollingStateNode = downcast<ScrollingStateScrollingNode>(stateNode); + if (scrollingStateNode.hasChangedProperty(ScrollingStateScrollingNode::RequestedScrollPosition)) + scrollingTree().scrollingTreeNodeRequestsScroll(scrollingNodeID(), scrollingStateNode.requestedScrollPosition(), scrollingStateNode.requestedScrollPositionRepresentsProgrammaticScroll()); +} + +void ScrollingTreeScrollingNode::updateLayersAfterAncestorChange(const ScrollingTreeNode& changedNode, const FloatRect& fixedPositionRect, const FloatSize& cumulativeDelta) +{ + if (!m_children) + return; + + for (auto& child : *m_children) + child->updateLayersAfterAncestorChange(changedNode, fixedPositionRect, cumulativeDelta); +} + +void ScrollingTreeScrollingNode::setScrollPosition(const FloatPoint& scrollPosition) +{ + FloatPoint newScrollPosition = scrollPosition.constrainedBetween(minimumScrollPosition(), maximumScrollPosition()); + setScrollPositionWithoutContentEdgeConstraints(newScrollPosition); +} + +void ScrollingTreeScrollingNode::setScrollPositionWithoutContentEdgeConstraints(const FloatPoint& scrollPosition) +{ + setScrollLayerPosition(scrollPosition, { }); + scrollingTree().scrollingTreeNodeDidScroll(scrollingNodeID(), scrollPosition, std::nullopt); +} + +FloatPoint ScrollingTreeScrollingNode::minimumScrollPosition() const +{ + return FloatPoint(); +} + +FloatPoint ScrollingTreeScrollingNode::maximumScrollPosition() const +{ + FloatPoint contentSizePoint(totalContentsSize()); + return FloatPoint(contentSizePoint - scrollableAreaSize()).expandedTo(FloatPoint()); +} + +void ScrollingTreeScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const +{ + ScrollingTreeNode::dumpProperties(ts, behavior); + ts.dumpProperty("scrollable area size", m_scrollableAreaSize); + ts.dumpProperty("total content size", m_totalContentsSize); + if (m_totalContentsSizeForRubberBand != m_totalContentsSize) + ts.dumpProperty("total content size for rubber band", m_totalContentsSizeForRubberBand); + if (m_reachableContentsSize != m_totalContentsSize) + ts.dumpProperty("reachable content size", m_reachableContentsSize); + ts.dumpProperty("scrollable area size", m_lastCommittedScrollPosition); + if (m_scrollOrigin != IntPoint()) + ts.dumpProperty("scrollable area size", m_scrollOrigin); + +#if ENABLE(CSS_SCROLL_SNAP) + if (m_snapOffsetsInfo.horizontalSnapOffsets.size()) + ts.dumpProperty("horizontal snap offsets", m_snapOffsetsInfo.horizontalSnapOffsets); + + if (m_snapOffsetsInfo.verticalSnapOffsets.size()) + ts.dumpProperty("horizontal snap offsets", m_snapOffsetsInfo.verticalSnapOffsets); + + if (m_currentHorizontalSnapPointIndex) + ts.dumpProperty("current horizontal snap point index", m_currentHorizontalSnapPointIndex); + + if (m_currentVerticalSnapPointIndex) + ts.dumpProperty("current vertical snap point index", m_currentVerticalSnapPointIndex); + +#endif +} + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h b/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h new file mode 100644 index 000000000..3594b1086 --- /dev/null +++ b/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 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 + +#if ENABLE(ASYNC_SCROLLING) + +#include "IntRect.h" +#include "ScrollSnapOffsetsInfo.h" +#include "ScrollTypes.h" +#include "ScrollingCoordinator.h" +#include "ScrollingTreeNode.h" + +namespace WebCore { + +class ScrollingTree; +class ScrollingStateScrollingNode; + +class ScrollingTreeScrollingNode : public ScrollingTreeNode { +public: + virtual ~ScrollingTreeScrollingNode(); + + WEBCORE_EXPORT void commitStateBeforeChildren(const ScrollingStateNode&) override; + WEBCORE_EXPORT void commitStateAfterChildren(const ScrollingStateNode&) override; + + WEBCORE_EXPORT void updateLayersAfterAncestorChange(const ScrollingTreeNode& changedNode, const FloatRect& fixedPositionRect, const FloatSize& cumulativeDelta) override; + + virtual void handleWheelEvent(const PlatformWheelEvent&) = 0; + WEBCORE_EXPORT virtual void setScrollPosition(const FloatPoint&); + WEBCORE_EXPORT virtual void setScrollPositionWithoutContentEdgeConstraints(const FloatPoint&); + + virtual void updateLayersAfterViewportChange(const FloatRect& fixedPositionRect, double scale) = 0; + virtual void updateLayersAfterDelegatedScroll(const FloatPoint&) { } + + virtual FloatPoint scrollPosition() const = 0; + +#if ENABLE(CSS_SCROLL_SNAP) + const Vector<float>& horizontalSnapOffsets() const { return m_snapOffsetsInfo.horizontalSnapOffsets; } + const Vector<float>& verticalSnapOffsets() const { return m_snapOffsetsInfo.verticalSnapOffsets; } + const Vector<ScrollOffsetRange<float>>& horizontalSnapOffsetRanges() const { return m_snapOffsetsInfo.horizontalSnapOffsetRanges; } + const Vector<ScrollOffsetRange<float>>& verticalSnapOffsetRanges() const { return m_snapOffsetsInfo.verticalSnapOffsetRanges; } + unsigned currentHorizontalSnapPointIndex() const { return m_currentHorizontalSnapPointIndex; } + unsigned currentVerticalSnapPointIndex() const { return m_currentVerticalSnapPointIndex; } + void setCurrentHorizontalSnapPointIndex(unsigned index) { m_currentHorizontalSnapPointIndex = index; } + void setCurrentVerticalSnapPointIndex(unsigned index) { m_currentVerticalSnapPointIndex = index; } +#endif + +protected: + ScrollingTreeScrollingNode(ScrollingTree&, ScrollingNodeType, ScrollingNodeID); + + WEBCORE_EXPORT virtual FloatPoint minimumScrollPosition() const; + WEBCORE_EXPORT virtual FloatPoint maximumScrollPosition() const; + + virtual void setScrollLayerPosition(const FloatPoint&, const FloatRect& layoutViewport) = 0; + + FloatPoint lastCommittedScrollPosition() const { return m_lastCommittedScrollPosition; } + const FloatSize& scrollableAreaSize() const { return m_scrollableAreaSize; } + const FloatSize& totalContentsSize() const { return m_totalContentsSize; } + const FloatSize& reachableContentsSize() const { return m_reachableContentsSize; } + const IntPoint& scrollOrigin() const { return m_scrollOrigin; } + + // If the totalContentsSize changes in the middle of a rubber-band, we still want to use the old totalContentsSize for the sake of + // computing the stretchAmount(). Using the old value will keep the animation smooth. When there is no rubber-band in progress at + // all, m_totalContentsSizeForRubberBand should be equivalent to m_totalContentsSize. + const FloatSize& totalContentsSizeForRubberBand() const { return m_totalContentsSizeForRubberBand; } + void setTotalContentsSizeForRubberBand(const FloatSize& totalContentsSizeForRubberBand) { m_totalContentsSizeForRubberBand = totalContentsSizeForRubberBand; } + + ScrollElasticity horizontalScrollElasticity() const { return m_scrollableAreaParameters.horizontalScrollElasticity; } + ScrollElasticity verticalScrollElasticity() const { return m_scrollableAreaParameters.verticalScrollElasticity; } + + bool hasEnabledHorizontalScrollbar() const { return m_scrollableAreaParameters.hasEnabledHorizontalScrollbar; } + bool hasEnabledVerticalScrollbar() const { return m_scrollableAreaParameters.hasEnabledVerticalScrollbar; } + + bool canHaveScrollbars() const { return m_scrollableAreaParameters.horizontalScrollbarMode != ScrollbarAlwaysOff || m_scrollableAreaParameters.verticalScrollbarMode != ScrollbarAlwaysOff; } + + WEBCORE_EXPORT void dumpProperties(TextStream&, ScrollingStateTreeAsTextBehavior) const override; + +private: + FloatSize m_scrollableAreaSize; + FloatSize m_totalContentsSize; + FloatSize m_totalContentsSizeForRubberBand; + FloatSize m_reachableContentsSize; + FloatPoint m_lastCommittedScrollPosition; + IntPoint m_scrollOrigin; +#if ENABLE(CSS_SCROLL_SNAP) + ScrollSnapOffsetsInfo<float> m_snapOffsetsInfo; + unsigned m_currentHorizontalSnapPointIndex { 0 }; + unsigned m_currentVerticalSnapPointIndex { 0 }; +#endif + ScrollableAreaParameters m_scrollableAreaParameters; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_NODE(ScrollingTreeScrollingNode, isScrollingNode()) + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp b/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp new file mode 100644 index 000000000..f664ed3ac --- /dev/null +++ b/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2014-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 "ThreadedScrollingTree.h" + +#if ENABLE(ASYNC_SCROLLING) + +#include "AsyncScrollingCoordinator.h" +#include "PlatformWheelEvent.h" +#include "ScrollingThread.h" +#include "ScrollingTreeFixedNode.h" +#include "ScrollingTreeNode.h" +#include "ScrollingTreeScrollingNode.h" +#include "ScrollingTreeStickyNode.h" +#include <wtf/RunLoop.h> + +namespace WebCore { + +ThreadedScrollingTree::ThreadedScrollingTree(AsyncScrollingCoordinator& scrollingCoordinator) + : m_scrollingCoordinator(&scrollingCoordinator) +{ +} + +ThreadedScrollingTree::~ThreadedScrollingTree() +{ + // invalidate() should have cleared m_scrollingCoordinator. + ASSERT(!m_scrollingCoordinator); +} + +ScrollingTree::EventResult ThreadedScrollingTree::tryToHandleWheelEvent(const PlatformWheelEvent& wheelEvent) +{ + if (shouldHandleWheelEventSynchronously(wheelEvent)) + return SendToMainThread; + + if (willWheelEventStartSwipeGesture(wheelEvent)) + return DidNotHandleEvent; + + RefPtr<ThreadedScrollingTree> protectedThis(this); + ScrollingThread::dispatch([protectedThis, wheelEvent] { + protectedThis->handleWheelEvent(wheelEvent); + }); + + return DidHandleEvent; +} + +void ThreadedScrollingTree::handleWheelEvent(const PlatformWheelEvent& wheelEvent) +{ + ASSERT(ScrollingThread::isCurrentThread()); + ScrollingTree::handleWheelEvent(wheelEvent); +} + +void ThreadedScrollingTree::invalidate() +{ + // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread + // to break the reference cycle between ScrollingTree and ScrollingCoordinator when the + // ScrollingCoordinator's page is destroyed. + ASSERT(ScrollingThread::isCurrentThread()); + + // Since this can potentially be the last reference to the scrolling coordinator, + // we need to release it on the main thread since it has member variables (such as timers) + // that expect to be destroyed from the main thread. + RunLoop::main().dispatch([scrollingCoordinator = WTFMove(m_scrollingCoordinator)] { + }); +} + +void ThreadedScrollingTree::commitTreeState(std::unique_ptr<ScrollingStateTree> scrollingStateTree) +{ + ASSERT(ScrollingThread::isCurrentThread()); + ScrollingTree::commitTreeState(WTFMove(scrollingStateTree)); +} + +void ThreadedScrollingTree::scrollingTreeNodeDidScroll(ScrollingNodeID nodeID, const FloatPoint& scrollPosition, const std::optional<FloatPoint>& layoutViewportOrigin, ScrollingLayerPositionAction scrollingLayerPositionAction) +{ + if (!m_scrollingCoordinator) + return; + + if (nodeID == rootNode()->scrollingNodeID()) + setMainFrameScrollPosition(scrollPosition); + + RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, nodeID, scrollPosition, layoutViewportOrigin, localIsHandlingProgrammaticScroll = isHandlingProgrammaticScroll(), scrollingLayerPositionAction] { + scrollingCoordinator->scheduleUpdateScrollPositionAfterAsyncScroll(nodeID, scrollPosition, layoutViewportOrigin, localIsHandlingProgrammaticScroll, scrollingLayerPositionAction); + }); +} + +void ThreadedScrollingTree::currentSnapPointIndicesDidChange(ScrollingNodeID nodeID, unsigned horizontal, unsigned vertical) +{ + if (!m_scrollingCoordinator) + return; + + RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, nodeID, horizontal, vertical] { + scrollingCoordinator->setActiveScrollSnapIndices(nodeID, horizontal, vertical); + }); +} + +#if PLATFORM(MAC) +void ThreadedScrollingTree::handleWheelEventPhase(PlatformWheelEventPhase phase) +{ + if (!m_scrollingCoordinator) + return; + + RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, phase] { + scrollingCoordinator->handleWheelEventPhase(phase); + }); +} + +void ThreadedScrollingTree::setActiveScrollSnapIndices(ScrollingNodeID nodeID, unsigned horizontalIndex, unsigned verticalIndex) +{ + if (!m_scrollingCoordinator) + return; + + RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, nodeID, horizontalIndex, verticalIndex] { + scrollingCoordinator->setActiveScrollSnapIndices(nodeID, horizontalIndex, verticalIndex); + }); +} + +void ThreadedScrollingTree::deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) +{ + if (!m_scrollingCoordinator) + return; + + RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, identifier, reason] { + scrollingCoordinator->deferTestsForReason(identifier, reason); + }); +} + +void ThreadedScrollingTree::removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier identifier, WheelEventTestTrigger::DeferTestTriggerReason reason) +{ + if (!m_scrollingCoordinator) + return; + + RunLoop::main().dispatch([scrollingCoordinator = m_scrollingCoordinator, identifier, reason] { + scrollingCoordinator->removeTestDeferralForReason(identifier, reason); + }); +} + +#endif + +} // namespace WebCore + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/ThreadedScrollingTree.h b/Source/WebCore/page/scrolling/ThreadedScrollingTree.h new file mode 100644 index 000000000..6d1d49d67 --- /dev/null +++ b/Source/WebCore/page/scrolling/ThreadedScrollingTree.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#pragma once + +#if ENABLE(ASYNC_SCROLLING) + +#include "ScrollingStateTree.h" +#include "ScrollingTree.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class AsyncScrollingCoordinator; + +// The ThreadedScrollingTree class lives almost exclusively on the scrolling thread and manages the +// hierarchy of scrollable regions on the page. It's also responsible for dispatching events +// to the correct scrolling tree nodes or dispatching events back to the ScrollingCoordinator +// object on the main thread if they can't be handled on the scrolling thread for various reasons. +class ThreadedScrollingTree : public ScrollingTree { +public: + virtual ~ThreadedScrollingTree(); + + void commitTreeState(std::unique_ptr<ScrollingStateTree>) override; + + void handleWheelEvent(const PlatformWheelEvent&) override; + + // Can be called from any thread. Will try to handle the wheel event on the scrolling thread. + // Returns true if the wheel event can be handled on the scrolling thread and false if the + // event must be sent again to the WebCore event handler. + EventResult tryToHandleWheelEvent(const PlatformWheelEvent&) override; + + void invalidate() override; + +protected: + explicit ThreadedScrollingTree(AsyncScrollingCoordinator&); + + void scrollingTreeNodeDidScroll(ScrollingNodeID, const FloatPoint& scrollPosition, const std::optional<FloatPoint>& layoutViewportOrigin, ScrollingLayerPositionAction = ScrollingLayerPositionAction::Sync) override; + void currentSnapPointIndicesDidChange(ScrollingNodeID, unsigned horizontal, unsigned vertical) override; +#if PLATFORM(MAC) + void handleWheelEventPhase(PlatformWheelEventPhase) override; + void setActiveScrollSnapIndices(ScrollingNodeID, unsigned horizontalIndex, unsigned verticalIndex) override; + void deferTestsForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) override; + void removeTestDeferralForReason(WheelEventTestTrigger::ScrollableAreaIdentifier, WheelEventTestTrigger::DeferTestTriggerReason) override; +#endif + +private: + bool isThreadedScrollingTree() const override { return true; } + + RefPtr<AsyncScrollingCoordinator> m_scrollingCoordinator; +}; + +} // namespace WebCore + +SPECIALIZE_TYPE_TRAITS_SCROLLING_TREE(WebCore::ThreadedScrollingTree, isThreadedScrollingTree()) + +#endif // ENABLE(ASYNC_SCROLLING) diff --git a/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.cpp b/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.cpp new file mode 100644 index 000000000..fa662500e --- /dev/null +++ b/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 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 THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS 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 USE(COORDINATED_GRAPHICS) + +#include "ScrollingCoordinatorCoordinatedGraphics.h" + +#include "CoordinatedGraphicsLayer.h" +#include "FrameView.h" +#include "HostWindow.h" +#include "Page.h" +#include "RenderLayer.h" +#include "RenderLayerBacking.h" +#include "ScrollingConstraints.h" +#include "ScrollingStateFixedNode.h" +#include "ScrollingStateScrollingNode.h" +#include "ScrollingStateStickyNode.h" +#include "ScrollingStateTree.h" + +namespace WebCore { + +ScrollingCoordinatorCoordinatedGraphics::ScrollingCoordinatorCoordinatedGraphics(Page* page) + : ScrollingCoordinator(page) + , m_scrollingStateTree(std::make_unique<ScrollingStateTree>()) +{ +} + +ScrollingCoordinatorCoordinatedGraphics::~ScrollingCoordinatorCoordinatedGraphics() +{ +} + +ScrollingNodeID ScrollingCoordinatorCoordinatedGraphics::attachToStateTree(ScrollingNodeType nodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID) +{ + return m_scrollingStateTree->attachNode(nodeType, newNodeID, parentID); +} + +void ScrollingCoordinatorCoordinatedGraphics::detachFromStateTree(ScrollingNodeID nodeID) +{ + ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID); + if (node && node->nodeType() == FixedNode) + downcast<CoordinatedGraphicsLayer>(*static_cast<GraphicsLayer*>(node->layer())).setFixedToViewport(false); + + m_scrollingStateTree->detachNode(nodeID); +} + +void ScrollingCoordinatorCoordinatedGraphics::clearStateTree() +{ + m_scrollingStateTree->clear(); +} + +void ScrollingCoordinatorCoordinatedGraphics::updateViewportConstrainedNode(ScrollingNodeID nodeID, const ViewportConstraints& constraints, GraphicsLayer* graphicsLayer) +{ + ASSERT(supportsFixedPositionLayers()); + + ScrollingStateNode* node = m_scrollingStateTree->stateNodeForID(nodeID); + if (!node) + return; + + switch (constraints.constraintType()) { + case ViewportConstraints::FixedPositionConstraint: { + downcast<CoordinatedGraphicsLayer>(*graphicsLayer).setFixedToViewport(true); + downcast<ScrollingStateFixedNode>(*node).setLayer(graphicsLayer); + break; + } + case ViewportConstraints::StickyPositionConstraint: + break; // FIXME : Support sticky elements. + default: + ASSERT_NOT_REACHED(); + } +} + +void ScrollingCoordinatorCoordinatedGraphics::scrollableAreaScrollLayerDidChange(ScrollableArea& scrollableArea) +{ + CoordinatedGraphicsLayer* layer = downcast<CoordinatedGraphicsLayer>(scrollLayerForScrollableArea(scrollableArea)); + if (!layer) + return; + + layer->setScrollableArea(&scrollableArea); +} + +void ScrollingCoordinatorCoordinatedGraphics::willDestroyScrollableArea(ScrollableArea& scrollableArea) +{ + CoordinatedGraphicsLayer* layer = downcast<CoordinatedGraphicsLayer>(scrollLayerForScrollableArea(scrollableArea)); + if (!layer) + return; + + layer->setScrollableArea(nullptr); +} + +bool ScrollingCoordinatorCoordinatedGraphics::requestScrollPositionUpdate(FrameView& frameView, const IntPoint& scrollPosition) +{ + if (!frameView.delegatesScrolling()) + return false; + + frameView.hostWindow()->delegatedScrollRequested(scrollPosition); + return true; +} + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.h b/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.h new file mode 100644 index 000000000..075bcfcc8 --- /dev/null +++ b/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingCoordinatorCoordinatedGraphics.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 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 THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS 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 USE(COORDINATED_GRAPHICS) + +#include "ScrollingCoordinator.h" + +namespace WebCore { + +class ScrollingStateTree; + +class ScrollingCoordinatorCoordinatedGraphics : public ScrollingCoordinator { +public: + explicit ScrollingCoordinatorCoordinatedGraphics(Page*); + virtual ~ScrollingCoordinatorCoordinatedGraphics(); + + bool supportsFixedPositionLayers() const override { return true; } + + ScrollingNodeID attachToStateTree(ScrollingNodeType, ScrollingNodeID newNodeID, ScrollingNodeID parentID) override; + void detachFromStateTree(ScrollingNodeID) override; + void clearStateTree() override; + + void updateViewportConstrainedNode(ScrollingNodeID, const ViewportConstraints&, GraphicsLayer*) override; + + void scrollableAreaScrollLayerDidChange(ScrollableArea&) override; + void willDestroyScrollableArea(ScrollableArea&) override; + + bool requestScrollPositionUpdate(FrameView&, const IntPoint&) override; + +private: + std::unique_ptr<ScrollingStateTree> m_scrollingStateTree; +}; + +} // namespace WebCore + +#endif // USE(COORDINATED_GRAPHICS) diff --git a/Source/WebCore/page/PageActivityAssertionToken.cpp b/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingStateNodeCoordinatedGraphics.cpp index 01e874bae..aeb6223c1 100644 --- a/Source/WebCore/page/PageActivityAssertionToken.cpp +++ b/Source/WebCore/page/scrolling/coordinatedgraphics/ScrollingStateNodeCoordinatedGraphics.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013 Intel Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,29 +24,26 @@ */ #include "config.h" -#include "PageActivityAssertionToken.h" +#include "ScrollingStateNode.h" -#include "PageThrottler.h" +#include "GraphicsLayer.h" +#include "NotImplemented.h" +#include "ScrollingStateTree.h" -namespace WebCore { +#if USE(COORDINATED_GRAPHICS) -PageActivityAssertionToken::PageActivityAssertionToken(PageThrottler& throttler) - : m_throttler(&throttler) -{ - m_throttler->addActivityToken(*this); -} +namespace WebCore { -PageActivityAssertionToken::~PageActivityAssertionToken() +void LayerRepresentation::retainPlatformLayer(PlatformLayer*) { - if (m_throttler) - m_throttler->removeActivityToken(*this); + notImplemented(); } -void PageActivityAssertionToken::invalidate() +void LayerRepresentation::releasePlatformLayer(PlatformLayer*) { - m_throttler = 0; + notImplemented(); } } // namespace WebCore - +#endif // USE(COORDINATED_GRAPHICS) |