/* 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;
// https://html.spec.whatwg.org/#dom-navigationhistoryentry-url
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);
}
// https://html.spec.whatwg.org/#dom-navigationhistoryentry-key
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);
}
// https://html.spec.whatwg.org/#dom-navigationhistoryentry-id
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);
}
// https://html.spec.whatwg.org/#dom-navigationhistoryentry-index
int64_t NavigationHistoryEntry::Index()
const {
MOZ_ASSERT(mSHInfo);
if (!HasActiveDocument()) {
return -
1;
}
return mIndex;
}
// https://html.spec.whatwg.org/#dom-navigationhistoryentry-samedocument
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);
}
// https://html.spec.whatwg.org/#dom-navigationhistoryentry-getstate
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