Files
tubestation/dom/navigation/Navigation.cpp
Andreas Farre 6249dabd29 Bug 1948254 - Part 3: Fix DOMEventTargetHelper creation. r=smaug
Also fix getters protocol usage and add asserts when null returning
getters actually have been checked implicitly.

Also add spec links and shorten multipage to single page links.

Differential Revision: https://phabricator.services.mozilla.com/D239874
2025-03-14 16:12:47 +00:00

279 lines
9.2 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Navigation.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/FeaturePolicy.h"
#include "mozilla/dom/NavigationCurrentEntryChangeEvent.h"
#include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/NavigationCurrentEntryChangeEvent.h"
#include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "nsContentUtils.h"
#include "nsIXULRuntime.h"
#include "nsNetUtil.h"
mozilla::LazyLogModule gNavigationLog("Navigation");
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(Navigation, DOMEventTargetHelper, mEntries);
NS_IMPL_ADDREF_INHERITED(Navigation, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Navigation, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigation)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
Navigation::Navigation(nsPIDOMWindowInner* aWindow)
: DOMEventTargetHelper(aWindow) {
MOZ_ASSERT(aWindow);
}
JSObject* Navigation::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return Navigation_Binding::Wrap(aCx, this, aGivenProto);
}
/* static */
bool Navigation::IsAPIEnabled(JSContext* /* unused */, JSObject* /* unused */) {
return SessionHistoryInParent() &&
StaticPrefs::dom_navigation_webidl_enabled_DoNotUseDirectly();
}
void Navigation::Entries(
nsTArray<RefPtr<NavigationHistoryEntry>>& aResult) const {
aResult = mEntries.Clone();
}
already_AddRefed<NavigationHistoryEntry> Navigation::GetCurrentEntry() const {
if (HasEntriesAndEventsDisabled()) {
return nullptr;
}
if (!mCurrentEntryIndex) {
return nullptr;
}
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("Current Entry: %d; Amount of Entries: %d", int(*mCurrentEntryIndex),
int(mEntries.Length())));
MOZ_ASSERT(*mCurrentEntryIndex < mEntries.Length());
RefPtr entry{mEntries[*mCurrentEntryIndex]};
return entry.forget();
}
// https://html.spec.whatwg.org/#dom-navigation-updatecurrententry
void Navigation::UpdateCurrentEntry(
JSContext* aCx, const NavigationUpdateCurrentEntryOptions& aOptions,
ErrorResult& aRv) {
RefPtr currentEntry(GetCurrentEntry());
if (!currentEntry) {
aRv.ThrowInvalidStateError(
"Can't call updateCurrentEntry without a valid entry.");
return;
}
JS::Rooted<JS::Value> state(aCx, aOptions.mState);
auto serializedState = MakeRefPtr<nsStructuredCloneContainer>();
nsresult rv = serializedState->InitFromJSVal(state, aCx);
if (NS_FAILED(rv)) {
aRv.ThrowDataCloneError(
"Failed to serialize value for updateCurrentEntry.");
return;
}
currentEntry->SetState(serializedState);
NavigationCurrentEntryChangeEventInit init;
init.mFrom = currentEntry;
// Leaving the navigation type unspecified means it will be initialized to
// null.
RefPtr event = NavigationCurrentEntryChangeEvent::Constructor(
this, u"currententrychange"_ns, init);
DispatchEvent(*event);
}
// https://html.spec.whatwg.org/#has-entries-and-events-disabled
bool Navigation::HasEntriesAndEventsDisabled() const {
Document* doc = GetDocumentIfCurrent();
return !doc || !doc->IsCurrentActiveDocument() ||
(NS_IsAboutBlankAllowQueryAndFragment(doc->GetDocumentURI()) &&
doc->IsInitialDocument()) ||
doc->GetPrincipal()->GetIsNullPrincipal();
}
// https://html.spec.whatwg.org/#initialize-the-navigation-api-entries-for-a-new-document
void Navigation::InitializeHistoryEntries(
mozilla::Span<const SessionHistoryInfo> aNewSHInfos,
const SessionHistoryInfo* aInitialSHInfo) {
mEntries.Clear();
mCurrentEntryIndex.reset();
if (HasEntriesAndEventsDisabled()) {
return;
}
for (auto i = 0ul; i < aNewSHInfos.Length(); i++) {
mEntries.AppendElement(MakeRefPtr<NavigationHistoryEntry>(
GetOwnerGlobal(), &aNewSHInfos[i], i));
if (aNewSHInfos[i].NavigationKey() == aInitialSHInfo->NavigationKey()) {
mCurrentEntryIndex = Some(i);
}
}
LogHistory();
nsID key = aInitialSHInfo->NavigationKey();
nsID id = aInitialSHInfo->NavigationId();
MOZ_LOG(
gNavigationLog, LogLevel::Debug,
("aInitialSHInfo: %s %s\n", key.ToString().get(), id.ToString().get()));
}
// https://html.spec.whatwg.org/#update-the-navigation-api-entries-for-a-same-document-navigation
void Navigation::UpdateEntriesForSameDocumentNavigation(
SessionHistoryInfo* aDestinationSHE, NavigationType aNavigationType) {
// Step 1.
if (HasEntriesAndEventsDisabled()) {
return;
}
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("Updating entries for same-document navigation"));
// Steps 2-7.
RefPtr<NavigationHistoryEntry> oldCurrentEntry = GetCurrentEntry();
nsTArray<RefPtr<NavigationHistoryEntry>> disposedEntries;
switch (aNavigationType) {
case NavigationType::Traverse:
MOZ_LOG(gNavigationLog, LogLevel::Debug, ("Traverse navigation"));
mCurrentEntryIndex.reset();
for (auto i = 0ul; i < mEntries.Length(); i++) {
if (mEntries[i]->IsSameEntry(aDestinationSHE)) {
mCurrentEntryIndex = Some(i);
break;
}
}
MOZ_ASSERT(mCurrentEntryIndex);
break;
case NavigationType::Push:
MOZ_LOG(gNavigationLog, LogLevel::Debug, ("Push navigation"));
mCurrentEntryIndex =
Some(mCurrentEntryIndex ? *mCurrentEntryIndex + 1 : 0);
while (*mCurrentEntryIndex < mEntries.Length()) {
disposedEntries.AppendElement(mEntries.PopLastElement());
}
mEntries.AppendElement(MakeRefPtr<NavigationHistoryEntry>(
GetOwnerGlobal(), aDestinationSHE, *mCurrentEntryIndex));
break;
case NavigationType::Replace:
MOZ_LOG(gNavigationLog, LogLevel::Debug, ("Replace navigation"));
disposedEntries.AppendElement(oldCurrentEntry);
aDestinationSHE->NavigationKey() = oldCurrentEntry->Key();
mEntries[*mCurrentEntryIndex] = MakeRefPtr<NavigationHistoryEntry>(
GetOwnerGlobal(), aDestinationSHE, *mCurrentEntryIndex);
break;
case NavigationType::Reload:
break;
}
// TODO: Step 8.
// Steps 9-12.
{
nsAutoMicroTask mt;
AutoEntryScript aes(GetOwnerGlobal(),
"UpdateEntriesForSameDocumentNavigation");
ScheduleEventsFromNavigation(aNavigationType, oldCurrentEntry,
std::move(disposedEntries));
}
}
// https://html.spec.whatwg.org/#update-the-navigation-api-entries-for-reactivation
void Navigation::UpdateForReactivation(SessionHistoryInfo* aReactivatedEntry) {
// NAV-TODO
}
void Navigation::ScheduleEventsFromNavigation(
NavigationType aType, const RefPtr<NavigationHistoryEntry>& aPreviousEntry,
nsTArray<RefPtr<NavigationHistoryEntry>>&& aDisposedEntries) {
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
"mozilla::dom::Navigation::ScheduleEventsFromNavigation",
[self = RefPtr(this), previousEntry = RefPtr(aPreviousEntry),
disposedEntries = std::move(aDisposedEntries), aType]() {
if (previousEntry) {
NavigationCurrentEntryChangeEventInit init;
init.mFrom = previousEntry;
init.mNavigationType.SetValue(aType);
RefPtr event = NavigationCurrentEntryChangeEvent::Constructor(
self, u"currententrychange"_ns, init);
self->DispatchEvent(*event);
}
for (const auto& entry : disposedEntries) {
RefPtr<Event> event = NS_NewDOMEvent(entry, nullptr, nullptr);
event->InitEvent(u"dispose"_ns, false, false);
event->SetTrusted(true);
event->SetTarget(entry);
entry->DispatchEvent(*event);
}
}));
}
namespace {
void LogEntry(NavigationHistoryEntry* aEntry, uint64_t aIndex, uint64_t aTotal,
bool aIsCurrent) {
if (!aEntry) {
MOZ_LOG(gNavigationLog, LogLevel::Debug,
(" +- %d NHEntry null\n", int(aIndex)));
return;
}
nsString key, id;
aEntry->GetKey(key);
aEntry->GetId(id);
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("%s+- %d NHEntry %p %s %s\n", aIsCurrent ? ">" : " ", int(aIndex),
aEntry, NS_ConvertUTF16toUTF8(key).get(),
NS_ConvertUTF16toUTF8(id).get()));
nsAutoString url;
aEntry->GetUrl(url);
MOZ_LOG(gNavigationLog, LogLevel::Debug,
(" URL = %s\n", NS_ConvertUTF16toUTF8(url).get()));
}
} // namespace
void Navigation::LogHistory() const {
if (!MOZ_LOG_TEST(gNavigationLog, LogLevel::Debug)) {
return;
}
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("Navigation %p (current entry index: %d)\n", this,
mCurrentEntryIndex ? int(*mCurrentEntryIndex) : -1));
auto length = mEntries.Length();
for (uint64_t i = 0; i < length; i++) {
LogEntry(mEntries[i], i, length,
mCurrentEntryIndex && i == *mCurrentEntryIndex);
}
}
} // namespace mozilla::dom