Source code

Revision control

Copy as Markdown

Other Tools

/* 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/NavigationHistoryEntry.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/NavigationHistoryEntryBinding.h"
#include "mozilla/dom/NavigationUtils.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "nsDocShell.h"
#include "nsGlobalWindowInner.h"
#define LOG_FMTD(format, ...) \
MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, format, ##__VA_ARGS__);
extern mozilla::LazyLogModule gNavigationAPILog;
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(NavigationHistoryEntry,
DOMEventTargetHelper);
NS_IMPL_ADDREF_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NavigationHistoryEntry)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NavigationHistoryEntry::NavigationHistoryEntry(
nsIGlobalObject* aGlobal, const class SessionHistoryInfo* aSHInfo,
int64_t aIndex)
: DOMEventTargetHelper(aGlobal),
mSHInfo(MakeUnique<class SessionHistoryInfo>(*aSHInfo)),
mIndex(aIndex) {}
NavigationHistoryEntry::~NavigationHistoryEntry() = default;
void NavigationHistoryEntry::GetUrl(nsAString& aResult) const {
if (!HasActiveDocument()) {
return;
}
// HasActiveDocument implies that GetAssociatedDocument returns non-null.
MOZ_DIAGNOSTIC_ASSERT(GetAssociatedDocument());
if (!SameDocument()) {
const auto referrerPolicy =
GetAssociatedDocument()->ReferrerPolicyUsedToFetchThisDocument();
if (referrerPolicy == ReferrerPolicy::No_referrer ||
referrerPolicy == ReferrerPolicy::Origin) {
aResult.SetIsVoid(true);
return;
}
}
MOZ_ASSERT(mSHInfo);
nsCOMPtr<nsIURI> uri = mSHInfo->GetURI();
MOZ_ASSERT(uri);
nsCString uriSpec;
uri->GetSpec(uriSpec);
CopyUTF8toUTF16(uriSpec, aResult);
}
void NavigationHistoryEntry::GetKey(nsAString& aResult) const {
if (!HasActiveDocument()) {
return;
}
nsIDToCString keyString(mSHInfo->NavigationKey());
// Omit the curly braces and NUL.
CopyUTF8toUTF16(Substring(keyString.get() + 1, NSID_LENGTH - 3), aResult);
}
void NavigationHistoryEntry::GetId(nsAString& aResult) const {
if (!HasActiveDocument()) {
return;
}
nsIDToCString idString(mSHInfo->NavigationId());
// Omit the curly braces and NUL.
CopyUTF8toUTF16(Substring(idString.get() + 1, NSID_LENGTH - 3), aResult);
}
int64_t NavigationHistoryEntry::Index() const {
MOZ_ASSERT(mSHInfo);
if (!HasActiveDocument()) {
return -1;
}
return mIndex;
}
bool NavigationHistoryEntry::SameDocument() const {
if (!HasActiveDocument()) {
return false;
}
// HasActiveDocument implies that GetAssociatedDocument returns non-null.
MOZ_DIAGNOSTIC_ASSERT(GetAssociatedDocument());
MOZ_ASSERT(mSHInfo);
auto* docShell = nsDocShell::Cast(GetAssociatedDocument()->GetDocShell());
return docShell && docShell->IsSameDocumentAsActiveEntry(*mSHInfo);
}
void NavigationHistoryEntry::GetState(JSContext* aCx,
JS::MutableHandle<JS::Value> aResult,
ErrorResult& aRv) const {
// Step 1
aResult.setUndefined();
if (!HasActiveDocument()) {
return;
}
// Step 2
RefPtr<nsIStructuredCloneContainer> state = mSHInfo->GetNavigationAPIState();
if (!state) {
return;
}
nsresult rv = state->DeserializeToJsval(aCx, aResult);
if (NS_FAILED(rv)) {
// nsStructuredCloneContainer::DeserializeToJsval suppresses exceptions, so
// the best we can do is just re-throw the NS_ERROR_DOM_DATA_CLONE_ERR. When
// nsStructuredCloneContainer::DeserializeToJsval throws better exceptions
// this should too.
// See also: NavigationDestination::GetState
aRv.Throw(rv);
}
}
void NavigationHistoryEntry::SetNavigationAPIState(
nsIStructuredCloneContainer* aState) {
mSHInfo->SetNavigationAPIState(aState);
}
bool NavigationHistoryEntry::IsSameEntry(
const class SessionHistoryInfo* aSHInfo) const {
return mSHInfo->NavigationId() == aSHInfo->NavigationId();
}
bool NavigationHistoryEntry::SharesDocumentWith(
const class SessionHistoryInfo& aSHInfo) const {
return mSHInfo->SharesDocumentWith(aSHInfo);
}
JSObject* NavigationHistoryEntry::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return NavigationHistoryEntry_Binding::Wrap(aCx, this, aGivenProto);
}
Document* NavigationHistoryEntry::GetAssociatedDocument() const {
nsGlobalWindowInner* window = GetOwnerWindow();
return window ? window->GetDocument() : nullptr;
}
bool NavigationHistoryEntry::HasActiveDocument() const {
if (auto* document = GetAssociatedDocument()) {
return document->IsCurrentActiveDocument();
}
return false;
}
const nsID& NavigationHistoryEntry::Key() const {
return mSHInfo->NavigationKey();
}
nsIStructuredCloneContainer* NavigationHistoryEntry::GetNavigationAPIState()
const {
if (!mSHInfo) {
return nullptr;
}
return mSHInfo->GetNavigationAPIState();
}
// Since we don't compute the index of the entry every time we use it, but
// instead cache it, we need to make sure to invalidate the cache when the
// entry is disposed.
void NavigationHistoryEntry::ResetIndexForDisposal() { mIndex = -1; }
MOZ_CAN_RUN_SCRIPT
void NavigationHistoryEntry::FireDisposeEvent() {
RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
// it doesn't bubble, and it isn't cancelable
event->InitEvent(u"dispose"_ns, false, false);
event->SetTrusted(true);
LOG_FMTD("Fire dispose");
DispatchEvent(*event, IgnoreErrors());
}
} // namespace mozilla::dom
#undef LOG_FMTD