| /* |
| * Copyright (C) 2023 Igalia S.L. 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 "EventTarget.h" |
| #include "JSDOMPromiseDeferred.h" |
| #include "LocalDOMWindowProperty.h" |
| #include "NavigateEvent.h" |
| #include "NavigationHistoryEntry.h" |
| #include "NavigationNavigationType.h" |
| #include "NavigationTransition.h" |
| #include <JavaScriptCore/JSCJSValue.h> |
| #include <wtf/RefCountedAndCanMakeWeakPtr.h> |
| #include <wtf/text/StringHash.h> |
| |
| namespace WebCore { |
| |
| class DocumentLoader; |
| class FormState; |
| class HistoryItem; |
| class SerializedScriptValue; |
| class NavigationActivation; |
| class NavigationDestination; |
| |
| enum class FrameLoadType : uint8_t; |
| |
| enum class NavigationAPIMethodTrackerType { }; |
| using NavigationAPIMethodTrackerIdentifier = ObjectIdentifier<NavigationAPIMethodTrackerType>; |
| |
| // https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigation-api-method-tracker |
| struct NavigationAPIMethodTracker : public RefCounted<NavigationAPIMethodTracker> { |
| WTF_DEPRECATED_MAKE_STRUCT_FAST_ALLOCATED(NavigationAPIMethodTracker); |
| |
| static Ref<NavigationAPIMethodTracker> create(Ref<DeferredPromise>&& committed, Ref<DeferredPromise>&& finished, JSC::JSValue&& info, RefPtr<SerializedScriptValue>&& serializedState) |
| { |
| return adoptRef(*new NavigationAPIMethodTracker(WTFMove(committed), WTFMove(finished), WTFMove(info), WTFMove(serializedState))); |
| } |
| |
| bool operator==(const NavigationAPIMethodTracker& other) const |
| { |
| // key is optional so we manually identify each tracker. |
| return identifier == other.identifier; |
| } |
| |
| bool finishedBeforeCommit { false }; |
| String key; |
| JSValueInWrappedObject info; |
| RefPtr<SerializedScriptValue> serializedState; |
| RefPtr<NavigationHistoryEntry> committedToEntry; |
| Ref<DeferredPromise> committedPromise; |
| Ref<DeferredPromise> finishedPromise; |
| |
| private: |
| explicit NavigationAPIMethodTracker(Ref<DeferredPromise>&& committed, Ref<DeferredPromise>&& finished, JSC::JSValue&& info, RefPtr<SerializedScriptValue>&& serializedState) |
| : info(info) |
| , serializedState(serializedState) |
| , committedPromise(WTFMove(committed)) |
| , finishedPromise(WTFMove(finished)) |
| , identifier(NavigationAPIMethodTrackerIdentifier::generate()) |
| { |
| } |
| |
| NavigationAPIMethodTrackerIdentifier identifier; |
| }; |
| |
| enum class ShouldCopyStateObjectFromCurrentEntry : bool { No, Yes }; |
| |
| class Navigation final : public RefCounted<Navigation>, public EventTarget, public LocalDOMWindowProperty { |
| WTF_MAKE_TZONE_OR_ISO_ALLOCATED(Navigation); |
| public: |
| ~Navigation(); |
| |
| static Ref<Navigation> create(LocalDOMWindow& window) { return adoptRef(*new Navigation(window)); } |
| |
| using RefCounted<Navigation>::ref; |
| using RefCounted<Navigation>::deref; |
| |
| using HistoryBehavior = NavigationHistoryBehavior; |
| |
| struct UpdateCurrentEntryOptions { |
| JSC::JSValue state; |
| }; |
| |
| struct Options { |
| JSC::JSValue info; |
| }; |
| |
| struct NavigateOptions : Options { |
| JSC::JSValue state; |
| HistoryBehavior history; |
| }; |
| |
| struct ReloadOptions : Options { |
| JSC::JSValue state; |
| }; |
| |
| struct Result { |
| RefPtr<DOMPromise> committed; |
| RefPtr<DOMPromise> finished; |
| }; |
| |
| const Vector<Ref<NavigationHistoryEntry>>& entries() const; |
| NavigationHistoryEntry* currentEntry() const; |
| RefPtr<NavigationHistoryEntry> protectedCurrentEntry() const { return currentEntry(); } |
| NavigationTransition* transition() { return m_transition.get(); }; |
| NavigationActivation* activation() { return m_activation.get(); }; |
| |
| bool canGoBack() const; |
| bool canGoForward() const; |
| |
| void initializeForNewWindow(std::optional<NavigationNavigationType>, LocalDOMWindow* previousWindow); |
| |
| Result navigate(const String& url, NavigateOptions&&, Ref<DeferredPromise>&&, Ref<DeferredPromise>&&); |
| |
| Result reload(ReloadOptions&&, Ref<DeferredPromise>&&, Ref<DeferredPromise>&&); |
| |
| Result traverseTo(const String& key, Options&&, Ref<DeferredPromise>&&, Ref<DeferredPromise>&&); |
| Result back(Options&&, Ref<DeferredPromise>&&, Ref<DeferredPromise>&&); |
| Result forward(Options&&, Ref<DeferredPromise>&&, Ref<DeferredPromise>&&); |
| |
| ExceptionOr<void> updateCurrentEntry(UpdateCurrentEntryOptions&&); |
| |
| enum class DispatchResult : uint8_t { Completed, Aborted, Intercepted }; |
| DispatchResult dispatchTraversalNavigateEvent(HistoryItem&); |
| bool dispatchPushReplaceReloadNavigateEvent(const URL&, NavigationNavigationType, bool isSameDocument, FormState*, SerializedScriptValue* classicHistoryAPIState = nullptr, Element* sourceElement = nullptr); |
| bool dispatchDownloadNavigateEvent(const URL&, const String& downloadFilename, Element* sourceElement = nullptr); |
| |
| void updateForNavigation(Ref<HistoryItem>&&, NavigationNavigationType, ShouldCopyStateObjectFromCurrentEntry = ShouldCopyStateObjectFromCurrentEntry::No); |
| void updateForReactivation(Vector<Ref<HistoryItem>>& newHistoryItems, HistoryItem& reactivatedItem); |
| void updateForActivation(HistoryItem* previousItem, std::optional<NavigationNavigationType>); |
| |
| RefPtr<NavigationActivation> createForPageswapEvent(HistoryItem* newItem, DocumentLoader*, bool fromBackForwardCache); |
| |
| void abortOngoingNavigationIfNeeded(); |
| |
| std::optional<Ref<NavigationHistoryEntry>> findEntryByKey(const String& key); |
| bool suppressNormalScrollRestoration() const { return m_suppressNormalScrollRestorationDuringOngoingNavigation; } |
| |
| void setFocusChanged(FocusDidChange changed) { m_focusChangedDuringOngoingNavigation = changed; } |
| |
| // EventTarget. |
| ScriptExecutionContext* scriptExecutionContext() const final; |
| RefPtr<ScriptExecutionContext> protectedScriptExecutionContext() const; |
| |
| void rejectFinishedPromise(NavigationAPIMethodTracker*); |
| NavigationAPIMethodTracker* upcomingTraverseMethodTracker(const String& key) const; |
| |
| void visitAdditionalChildren(JSC::AbstractSlotVisitor&); |
| |
| class AbortHandler : public RefCountedAndCanMakeWeakPtr<AbortHandler> { |
| public: |
| bool wasAborted() const { return m_wasAborted; } |
| |
| private: |
| friend class Navigation; |
| |
| static Ref<AbortHandler> create() { return adoptRef(*new AbortHandler); } |
| void markAsAborted() { m_wasAborted = true; } |
| |
| bool m_wasAborted { false }; |
| }; |
| Ref<AbortHandler> registerAbortHandler(); |
| |
| NavigateEvent* ongoingNavigateEvent() { return m_ongoingNavigateEvent.get(); } // This may get called on the GC thread. |
| RefPtr<NavigateEvent> protectedOngoingNavigateEvent() { return m_ongoingNavigateEvent; } |
| |
| void updateNavigationEntry(Ref<HistoryItem>&&, ShouldCopyStateObjectFromCurrentEntry); |
| |
| private: |
| explicit Navigation(LocalDOMWindow&); |
| |
| // EventTarget. |
| enum EventTargetInterfaceType eventTargetInterface() const final; |
| void refEventTarget() final { ref(); } |
| void derefEventTarget() final { deref(); } |
| |
| bool hasEntriesAndEventsDisabled() const; |
| Result performTraversal(const String& key, Navigation::Options, Ref<DeferredPromise>&& committed, Ref<DeferredPromise>&& finished); |
| ExceptionOr<RefPtr<SerializedScriptValue>> serializeState(JSC::JSValue state); |
| DispatchResult innerDispatchNavigateEvent(NavigationNavigationType, Ref<NavigationDestination>&&, const String& downloadRequestFilename, FormState* = nullptr, SerializedScriptValue* classicHistoryAPIState = nullptr, Element* sourceElement = nullptr); |
| |
| RefPtr<NavigationAPIMethodTracker> maybeSetUpcomingNonTraversalTracker(Ref<DeferredPromise>&& committed, Ref<DeferredPromise>&& finished, JSC::JSValue info, RefPtr<SerializedScriptValue>&&); |
| RefPtr<NavigationAPIMethodTracker> addUpcomingTraverseAPIMethodTracker(Ref<DeferredPromise>&& committed, Ref<DeferredPromise>&& finished, const String& key, JSC::JSValue info); |
| void cleanupAPIMethodTracker(NavigationAPIMethodTracker*) WTF_EXCLUDES_LOCK(m_apiMethodTrackersLock); |
| void resolveFinishedPromise(NavigationAPIMethodTracker*); |
| void rejectFinishedPromise(NavigationAPIMethodTracker*, const Exception&, JSC::JSValue exceptionObject); |
| void abortOngoingNavigation(NavigateEvent&); |
| void promoteUpcomingAPIMethodTracker(const String& destinationKey) WTF_EXCLUDES_LOCK(m_apiMethodTrackersLock); |
| void notifyCommittedToEntry(NavigationAPIMethodTracker*, NavigationHistoryEntry*, NavigationNavigationType); |
| Result apiMethodTrackerDerivedResult(const NavigationAPIMethodTracker&); |
| |
| std::optional<size_t> m_currentEntryIndex; |
| RefPtr<NavigationTransition> m_transition; |
| RefPtr<NavigationActivation> m_activation; |
| Vector<Ref<NavigationHistoryEntry>> m_entries; |
| |
| RefPtr<NavigateEvent> m_ongoingNavigateEvent; |
| FocusDidChange m_focusChangedDuringOngoingNavigation { FocusDidChange::No }; |
| bool m_suppressNormalScrollRestorationDuringOngoingNavigation { false }; |
| mutable Lock m_apiMethodTrackersLock; |
| RefPtr<NavigationAPIMethodTracker> m_ongoingAPIMethodTracker WTF_GUARDED_BY_LOCK(m_apiMethodTrackersLock); |
| RefPtr<NavigationAPIMethodTracker> m_upcomingNonTraverseMethodTracker WTF_GUARDED_BY_LOCK(m_apiMethodTrackersLock); |
| HashMap<String, Ref<NavigationAPIMethodTracker>> m_upcomingTraverseMethodTrackers WTF_GUARDED_BY_LOCK(m_apiMethodTrackersLock); |
| WeakHashSet<AbortHandler> m_abortHandlers; |
| }; |
| |
| } // namespace WebCore |