Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 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
/*
* Base class for all our document implementations.
*/
#include "mozilla/dom/Document.h"
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <limits>
#include "Attr.h"
#include "ErrorList.h"
#include "ExpandedPrincipal.h"
#include "MainThreadUtils.h"
#include "MobileViewportManager.h"
#include "NSSErrorsService.h"
#include "NodeUbiReporting.h"
#include "NonCustomCSSPropertyId.h"
#include "PLDHashTable.h"
#include "StorageAccessPermissionRequest.h"
#include "ThirdPartyUtil.h"
#include "domstubs.h"
#include "gfxPlatform.h"
#include "imgIContainer.h"
#include "imgLoader.h"
#include "imgRequestProxy.h"
#include "js/TelemetryTimers.h"
#include "js/Value.h"
#include "jsapi.h"
#include "mozAutoDocUpdate.h"
#include "mozIDOMWindow.h"
#include "mozIThirdPartyUtil.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/ArrayIterator.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/AttributeStyles.h"
#include "mozilla/Base64.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/BounceTrackingProtection.h"
#include "mozilla/CSSEnabledState.h"
#include "mozilla/Components.h"
#include "mozilla/ContentBlockingAllowList.h"
#include "mozilla/ContentBlockingNotifier.h"
#include "mozilla/ContentBlockingUserInteraction.h"
#include "mozilla/ContentPrincipal.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/DocumentStyleRootIterator.h"
#include "mozilla/EditorBase.h"
#include "mozilla/EditorCommands.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventQueue.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/FullscreenChange.h"
#include "mozilla/GlobalStyleSheetCache.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/IdentifierMapEntry.h"
#include "mozilla/InputTaskManager.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Likely.h"
#include "mozilla/Logging.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MappedDeclarationsBuilder.h"
#include "mozilla/Maybe.h"
#include "mozilla/MediaFeatureChange.h"
#include "mozilla/MediaManager.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/NeverDestroyed.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/PendingFullscreenEvent.h"
#include "mozilla/PermissionDelegateHandler.h"
#include "mozilla/PermissionManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/PreloadHashKey.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellForwards.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/PseudoStyleType.h"
#include "mozilla/RelativeTo.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/ReverseIterator.h"
#include "mozilla/SMILAnimationController.h"
#include "mozilla/SMILTimeContainer.h"
#include "mozilla/SVGUtils.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/ScrollTimelineAnimationTracker.h"
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/ServoTypes.h"
#include "mozilla/SizeOfState.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticAnalysisFunctions.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_docshell.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_full_screen_api.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_page_load.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/StaticPresData.h"
#include "mozilla/StorageAccess.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/TelemetryScalarEnums.h"
#include "mozilla/TextControlElement.h"
#include "mozilla/TextEditor.h"
#include "mozilla/URLDecorationStripper.h"
#include "mozilla/URLExtraData.h"
#include "mozilla/css/ImageLoader.h"
#include "mozilla/css/Loader.h"
#include "mozilla/css/Rule.h"
#include "mozilla/css/SheetParsingMode.h"
#include "mozilla/dom/AncestorIterator.h"
#include "mozilla/dom/AnonymousContent.h"
#include "mozilla/dom/BindContext.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CDATASection.h"
#include "mozilla/dom/CSPDictionariesBinding.h"
#include "mozilla/dom/CSSBinding.h"
#include "mozilla/dom/CSSCustomPropertyRegisteredEvent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/CanvasRenderingContextHelper.h"
#include "mozilla/dom/ChromeObserver.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/CloseWatcherManager.h"
#include "mozilla/dom/Comment.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DOMImplementation.h"
#include "mozilla/dom/DOMIntersectionObserver.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/DocumentBinding.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/DocumentL10n.h"
#include "mozilla/dom/DocumentTimeline.h"
#include "mozilla/dom/DocumentType.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventListenerBinding.h"
#include "mozilla/dom/FailedCertSecurityInfoBinding.h"
#include "mozilla/dom/FeaturePolicy.h"
#include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/FragmentDirective.h"
#include "mozilla/dom/FromParser.h"
#include "mozilla/dom/HTMLAllCollection.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLCollectionBinding.h"
#include "mozilla/dom/HTMLDialogElement.h"
#include "mozilla/dom/HTMLEmbedElement.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "mozilla/dom/HTMLIFrameElement.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/HTMLMetaElement.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLSharedElement.h"
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/HighlightRegistry.h"
#include "mozilla/dom/InspectorUtils.h"
#include "mozilla/dom/IntegrityPolicy.h"
#include "mozilla/dom/InteractiveWidget.h"
#include "mozilla/dom/Link.h"
#include "mozilla/dom/MediaQueryList.h"
#include "mozilla/dom/MediaSource.h"
#include "mozilla/dom/MutationObservers.h"
#include "mozilla/dom/NameSpaceConstants.h"
#include "mozilla/dom/NavigationBinding.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/NetErrorInfoBinding.h"
#include "mozilla/dom/NodeInfo.h"
#include "mozilla/dom/NodeIterator.h"
#include "mozilla/dom/PContentChild.h"
#include "mozilla/dom/PWindowGlobalChild.h"
#include "mozilla/dom/PageLoadEventUtils.h"
#include "mozilla/dom/PageTransitionEvent.h"
#include "mozilla/dom/PageTransitionEventBinding.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/PolicyContainer.h"
#include "mozilla/dom/PostMessageEvent.h"
#include "mozilla/dom/ProcessingInstruction.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/RemoteBrowser.h"
#include "mozilla/dom/ResizeObserver.h"
#include "mozilla/dom/RustTypes.h"
#include "mozilla/dom/SVGDocument.h"
#include "mozilla/dom/SVGElement.h"
#include "mozilla/dom/SVGSVGElement.h"
#include "mozilla/dom/SVGUseElement.h"
#include "mozilla/dom/Sanitizer.h"
#include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/Selection.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/ShadowIncludingTreeIterator.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
#include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
#include "mozilla/dom/StyleSheetList.h"
#include "mozilla/dom/StyleSheetRemovedEvent.h"
#include "mozilla/dom/StyleSheetRemovedEventBinding.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/dom/ToggleEvent.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/dom/TreeOrderedArrayInlines.h"
#include "mozilla/dom/TreeWalker.h"
#include "mozilla/dom/TrustedHTML.h"
#include "mozilla/dom/TrustedTypeUtils.h"
#include "mozilla/dom/TrustedTypesConstants.h"
#include "mozilla/dom/URL.h"
#include "mozilla/dom/UseCounterMetrics.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/ViewTransition.h"
#include "mozilla/dom/WakeLockJS.h"
#include "mozilla/dom/WakeLockSentinel.h"
#include "mozilla/dom/WebIdentityHandler.h"
#include "mozilla/dom/WindowBinding.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/dom/WorkerDocumentListener.h"
#include "mozilla/dom/XPathEvaluator.h"
#include "mozilla/dom/XPathExpression.h"
#include "mozilla/dom/XULBroadcastManager.h"
#include "mozilla/dom/XULPersist.h"
#include "mozilla/dom/fragmentdirectives_ffi_generated.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/nsCSPUtils.h"
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
#include "mozilla/extensions/WebExtensionPolicy.h"
#include "mozilla/fallible.h"
#include "mozilla/gfx/BaseCoord.h"
#include "mozilla/gfx/BaseSize.h"
#include "mozilla/gfx/Coord.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/gfx/ScaleFactor.h"
#include "mozilla/glean/DomMetrics.h"
#include "mozilla/glean/DomUseCounterMetrics.h"
#include "mozilla/intl/EncodingToLang.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/ipc/IdleSchedulerChild.h"
#include "mozilla/ipc/MessageChannel.h"
#include "mozilla/net/ChannelEventQueue.h"
#include "mozilla/net/Cookie.h"
#include "mozilla/net/CookieCommons.h"
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/net/CookieParser.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "mozilla/net/RequestContextService.h"
#include "nsAboutProtocolUtils.h"
#include "nsAttrValue.h"
#include "nsAttrValueInlines.h"
#include "nsBaseHashtable.h"
#include "nsBidiUtils.h"
#include "nsCRT.h"
#include "nsCSSProps.h"
#include "nsCSSPseudoElements.h"
#include "nsCSSRendering.h"
#include "nsCanvasFrame.h"
#include "nsCaseTreatment.h"
#include "nsCharsetSource.h"
#include "nsCommandManager.h"
#include "nsCommandParams.h"
#include "nsComponentManagerUtils.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentList.h"
#include "nsContentPermissionHelper.h"
#include "nsContentSecurityUtils.h"
#include "nsContentUtils.h"
#include "nsCoord.h"
#include "nsCycleCollectionNoteChild.h"
#include "nsCycleCollectionTraversalCallback.h"
#include "nsDOMAttributeMap.h"
#include "nsDOMCaretPosition.h"
#include "nsDOMNavigationTiming.h"
#include "nsDOMString.h"
#include "nsDeviceContext.h"
#include "nsDocShell.h"
#include "nsDocShellLoadTypes.h"
#include "nsError.h"
#include "nsEscape.h"
#include "nsFocusManager.h"
#include "nsFrameLoader.h"
#include "nsFrameLoaderOwner.h"
#include "nsGenericHTMLElement.h"
#include "nsGlobalWindowInner.h"
#include "nsGlobalWindowOuter.h"
#include "nsHTMLDocument.h"
#include "nsHtml5Module.h"
#include "nsHtml5Parser.h"
#include "nsHtml5TreeOpExecutor.h"
#include "nsIAppWindow.h"
#include "nsIAsyncShutdown.h"
#include "nsIAuthPrompt.h"
#include "nsIAuthPrompt2.h"
#include "nsIBFCacheEntry.h"
#include "nsIBaseWindow.h"
#include "nsIBrowserChild.h"
#include "nsIBrowserUsage.h"
#include "nsICSSLoaderObserver.h"
#include "nsICategoryManager.h"
#include "nsICertOverrideService.h"
#include "nsIClassifiedChannel.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIContentSink.h"
#include "nsICookieJarSettings.h"
#include "nsICookieService.h"
#include "nsIDNSService.h"
#include "nsIDOMXULCommandDispatcher.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocumentActivity.h"
#include "nsIDocumentEncoder.h"
#include "nsIDocumentLoader.h"
#include "nsIDocumentLoaderFactory.h"
#include "nsIDocumentObserver.h"
#include "nsIEditingSession.h"
#include "nsIEditor.h"
#include "nsIEffectiveTLDService.h"
#include "nsIFile.h"
#include "nsIFileChannel.h"
#include "nsIFrame.h"
#include "nsIGlobalObject.h"
#include "nsIHTMLCollection.h"
#include "nsIHTMLContentSink.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIIOService.h"
#include "nsIImageLoadingContent.h"
#include "nsIInlineSpellChecker.h"
#include "nsIInputStreamChannel.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsILayoutHistoryState.h"
#include "nsIMultiPartChannel.h"
#include "nsIMutationObserver.h"
#include "nsINSSErrorsService.h"
#include "nsINamed.h"
#include "nsINodeList.h"
#include "nsIObjectLoadingContent.h"
#include "nsIObserverService.h"
#include "nsIParentalControlsService.h"
#include "nsIPermission.h"
#include "nsIPrompt.h"
#include "nsIPropertyBag2.h"
#include "nsIPublicKeyPinningService.h"
#include "nsIReferrerInfo.h"
#include "nsIRefreshURI.h"
#include "nsIRequest.h"
#include "nsIRequestContext.h"
#include "nsIRunnable.h"
#include "nsISHEntry.h"
#include "nsIScriptElement.h"
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsISecurityConsoleMessage.h"
#include "nsISelectionController.h"
#include "nsISerialEventTarget.h"
#include "nsISimpleEnumerator.h"
#include "nsISiteSecurityService.h"
#include "nsISocketProvider.h"
#include "nsISpeculativeConnect.h"
#include "nsIStructuredCloneContainer.h"
#include "nsIThread.h"
#include "nsITimedChannel.h"
#include "nsITimer.h"
#include "nsITransportSecurityInfo.h"
#include "nsIURIMutator.h"
#include "nsIVariant.h"
#include "nsIWeakReference.h"
#include "nsIWebNavigation.h"
#include "nsIWidget.h"
#include "nsIX509Cert.h"
#include "nsIX509CertValidity.h"
#include "nsIXMLContentSink.h"
#include "nsIXULRuntime.h"
#include "nsImageLoadingContent.h"
#include "nsImportModule.h"
#include "nsLayoutUtils.h"
#include "nsMenuPopupFrame.h"
#include "nsMimeTypes.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsNodeInfoManager.h"
#include "nsObjectLoadingContent.h"
#include "nsPIDOMWindowInlines.h"
#include "nsPIWindowRoot.h"
#include "nsPoint.h"
#include "nsPointerHashKeys.h"
#include "nsPresContext.h"
#include "nsQueryFrame.h"
#include "nsQueryObject.h"
#include "nsRange.h"
#include "nsRect.h"
#include "nsRefreshDriver.h"
#include "nsSandboxFlags.h"
#include "nsSerializationHelper.h"
#include "nsServiceManagerUtils.h"
#include "nsStringFlags.h"
#include "nsStringIterator.h"
#include "nsStyleSheetService.h"
#include "nsStyleStruct.h"
#include "nsStyleUtil.h"
#include "nsSubDocumentFrame.h"
#include "nsTextControlFrame.h"
#include "nsTextNode.h"
#include "nsURLHelper.h"
#include "nsUnicharUtils.h"
#include "nsWrapperCache.h"
#include "nsWrapperCacheInlines.h"
#include "nsXPCOMCID.h"
#include "nsXULAppAPI.h"
#include "nsXULCommandDispatcher.h"
#include "nsXULPopupManager.h"
#include "nsXULPrototypeDocument.h"
#include "prthread.h"
#include "prtime.h"
#include "prtypes.h"
#include "xpcpublic.h"
// clang-format off
#include "mozilla/Encoding.h"
#include "encoding_rs.h"
// clang-format on
#define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
#define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
#define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
#define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
#define NS_MAX_DOCUMENT_WRITE_DEPTH 20
mozilla::LazyLogModule gPageCacheLog("PageCache");
mozilla::LazyLogModule gSHIPBFCacheLog("SHIPBFCache");
mozilla::LazyLogModule gTimeoutDeferralLog("TimeoutDefer");
mozilla::LazyLogModule gUseCountersLog("UseCounters");
namespace mozilla {
using namespace net;
using performance::pageload_event::PageloadEventType;
namespace dom {
class Document::HeaderData {
public:
HeaderData(nsAtom* aField, const nsAString& aData)
: mField(aField), mData(aData) {}
~HeaderData() {
// Delete iteratively to avoid blowing up the stack, though it shouldn't
// happen in practice.
UniquePtr<HeaderData> next = std::move(mNext);
while (next) {
next = std::move(next->mNext);
}
}
RefPtr<nsAtom> mField;
nsString mData;
UniquePtr<HeaderData> mNext;
};
AutoTArray<Document*, 8>* Document::sLoadingForegroundTopLevelContentDocument =
nullptr;
static LinkedList<Document>& AllDocumentsList() {
static NeverDestroyed<LinkedList<Document>> sAllDocuments;
return *sAllDocuments;
}
static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
static LazyLogModule gCspPRLog("CSP");
LazyLogModule gUserInteractionPRLog("UserInteraction");
static nsresult GetHttpChannelHelper(nsIChannel* aChannel,
nsIHttpChannel** aHttpChannel) {
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
if (httpChannel) {
httpChannel.forget(aHttpChannel);
return NS_OK;
}
nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
if (!multipart) {
*aHttpChannel = nullptr;
return NS_OK;
}
nsCOMPtr<nsIChannel> baseChannel;
nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
httpChannel = do_QueryInterface(baseChannel);
httpChannel.forget(aHttpChannel);
return NS_OK;
}
} // namespace dom
#define NAME_NOT_VALID ((nsSimpleContentList*)1)
IdentifierMapEntry::IdentifierMapEntry(
const IdentifierMapEntry::DependentAtomOrString* aKey)
: mKey(aKey ? *aKey : nullptr) {}
void IdentifierMapEntry::Traverse(
nsCycleCollectionTraversalCallback* aCallback) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mIdentifierMap mNameContentList");
aCallback->NoteXPCOMChild(static_cast<nsINodeList*>(mNameContentList));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mIdentifierMap mDocumentNameContentList");
aCallback->NoteXPCOMChild(
static_cast<nsINodeList*>(mDocumentNameContentList));
if (mImageElement) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mIdentifierMap mImageElement element");
nsIContent* imageElement = mImageElement;
aCallback->NoteXPCOMChild(imageElement);
}
}
bool IdentifierMapEntry::IsEmpty() {
return mIdContentList->IsEmpty() && !mNameContentList &&
!mDocumentNameContentList && !mChangeCallbacks && !mImageElement;
}
bool IdentifierMapEntry::HasNameElement() const {
return mNameContentList && mNameContentList->Length() != 0;
}
void IdentifierMapEntry::AddContentChangeCallback(
Document::IDTargetObserver aCallback, void* aData, bool aForImage) {
if (!mChangeCallbacks) {
mChangeCallbacks = MakeUnique<nsTHashtable<ChangeCallbackEntry>>();
}
ChangeCallback cc = {aCallback, aData, aForImage};
mChangeCallbacks->PutEntry(cc);
}
void IdentifierMapEntry::RemoveContentChangeCallback(
Document::IDTargetObserver aCallback, void* aData, bool aForImage) {
if (!mChangeCallbacks) return;
ChangeCallback cc = {aCallback, aData, aForImage};
mChangeCallbacks->RemoveEntry(cc);
if (mChangeCallbacks->Count() == 0) {
mChangeCallbacks = nullptr;
}
}
void IdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
Element* aNewElement,
bool aImageOnly) {
if (!mChangeCallbacks) return;
for (auto iter = mChangeCallbacks->Iter(); !iter.Done(); iter.Next()) {
IdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
// Don't fire image changes for non-image observers, and don't fire element
// changes for image observers when an image override is active.
if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
continue;
}
if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
iter.Remove();
}
}
}
void IdentifierMapEntry::AddIdElement(Element* aElement) {
MOZ_ASSERT(aElement, "Must have element");
MOZ_ASSERT(!mIdContentList->Contains(nullptr), "Why is null in our list?");
size_t index = mIdContentList.Insert(*aElement);
if (index == 0) {
Element* oldElement = mIdContentList->SafeElementAt(1);
FireChangeCallbacks(oldElement, aElement);
}
}
void IdentifierMapEntry::RemoveIdElement(Element* aElement) {
MOZ_ASSERT(aElement, "Missing element");
// This should only be called while the document is in an update.
// Assertions near the call to this method guarantee this.
// This could fire in OOM situations
// Only assert this in HTML documents for now as XUL does all sorts of weird
// crap.
NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
mIdContentList->Contains(aElement),
"Removing id entry that doesn't exist");
// XXXbz should this ever Compact() I guess when all the content is gone
// we'll just get cleaned up in the natural order of things...
Element* currentElement = mIdContentList->SafeElementAt(0);
mIdContentList.RemoveElement(*aElement);
if (currentElement == aElement) {
FireChangeCallbacks(currentElement, mIdContentList->SafeElementAt(0));
}
}
void IdentifierMapEntry::SetImageElement(Element* aElement) {
Element* oldElement = GetImageIdElement();
mImageElement = aElement;
Element* newElement = GetImageIdElement();
if (oldElement != newElement) {
FireChangeCallbacks(oldElement, newElement, true);
}
}
void IdentifierMapEntry::ClearAndNotify() {
Element* currentElement = mIdContentList->SafeElementAt(0);
mIdContentList.Clear();
if (currentElement) {
FireChangeCallbacks(currentElement, nullptr);
}
mNameContentList = nullptr;
mDocumentNameContentList = nullptr;
if (mImageElement) {
SetImageElement(nullptr);
}
mChangeCallbacks = nullptr;
}
namespace dom {
class SimpleHTMLCollection final : public nsSimpleContentList,
public nsIHTMLCollection {
public:
explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
NS_DECL_ISUPPORTS_INHERITED
virtual nsINode* GetParentObject() override {
return nsSimpleContentList::GetParentObject();
}
virtual uint32_t Length() override { return nsSimpleContentList::Length(); }
virtual Element* GetElementAt(uint32_t aIndex) override {
return mElements.SafeElementAt(aIndex)->AsElement();
}
virtual Element* GetFirstNamedElement(const nsAString& aName,
bool& aFound) override {
aFound = false;
RefPtr<nsAtom> name = NS_Atomize(aName);
for (uint32_t i = 0; i < mElements.Length(); i++) {
MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
Element* element = mElements[i]->AsElement();
if (element->GetID() == name ||
(element->HasName() &&
element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
aFound = true;
return element;
}
}
return nullptr;
}
virtual void GetSupportedNames(nsTArray<nsString>& aNames) override {
AutoTArray<nsAtom*, 8> atoms;
for (uint32_t i = 0; i < mElements.Length(); i++) {
MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
Element* element = mElements[i]->AsElement();
nsAtom* id = element->GetID();
MOZ_ASSERT(id != nsGkAtoms::_empty);
if (id && !atoms.Contains(id)) {
atoms.AppendElement(id);
}
if (element->HasName()) {
nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
MOZ_ASSERT(name && name != nsGkAtoms::_empty);
if (name && !atoms.Contains(name)) {
atoms.AppendElement(name);
}
}
}
nsString* names = aNames.AppendElements(atoms.Length());
for (uint32_t i = 0; i < atoms.Length(); i++) {
atoms[i]->ToString(names[i]);
}
}
virtual JSObject* GetWrapperPreserveColorInternal() override {
return nsWrapperCache::GetWrapperPreserveColor();
}
virtual void PreserveWrapperInternal(
nsISupports* aScriptObjectHolder) override {
nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override {
return HTMLCollection_Binding::Wrap(aCx, this, aGivenProto);
}
using nsBaseContentList::Item;
private:
virtual ~SimpleHTMLCollection() = default;
};
NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
nsIHTMLCollection)
} // namespace dom
void IdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) {
if (!mNameContentList) {
mNameContentList = new dom::SimpleHTMLCollection(aNode);
}
mNameContentList->AppendElement(aElement);
}
void IdentifierMapEntry::RemoveNameElement(Element* aElement) {
if (mNameContentList) {
mNameContentList->RemoveElement(aElement);
}
}
void IdentifierMapEntry::AddDocumentNameElement(
Document* aDocument, nsGenericHTMLElement* aElement) {
if (!mDocumentNameContentList) {
mDocumentNameContentList = new dom::SimpleHTMLCollection(aDocument);
}
mDocumentNameContentList->AppendElement(aElement);
}
void IdentifierMapEntry::RemoveDocumentNameElement(
nsGenericHTMLElement* aElement) {
if (mDocumentNameContentList) {
mDocumentNameContentList->RemoveElement(aElement);
}
}
bool IdentifierMapEntry::HasDocumentNameElement() const {
return mDocumentNameContentList && mDocumentNameContentList->Length() != 0;
}
bool IdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() const {
Element* idElement = GetIdElement();
return idElement &&
nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
}
size_t IdentifierMapEntry::SizeOfExcludingThis(
MallocSizeOf aMallocSizeOf) const {
return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
// Helper structs for the content->subdoc map
class SubDocMapEntry : public PLDHashEntryHdr {
public:
// Both of these are strong references
dom::Element* mKey; // must be first, to look like PLDHashEntryStub
dom::Document* mSubDocument;
};
class OnloadBlocker final : public nsIRequest {
public:
OnloadBlocker() = default;
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUEST
private:
~OnloadBlocker() = default;
};
NS_IMPL_ISUPPORTS(OnloadBlocker, nsIRequest)
NS_IMETHODIMP
OnloadBlocker::GetName(nsACString& aResult) {
aResult.AssignLiteral("about:document-onload-blocker");
return NS_OK;
}
NS_IMETHODIMP
OnloadBlocker::IsPending(bool* _retval) {
*_retval = true;
return NS_OK;
}
NS_IMETHODIMP
OnloadBlocker::GetStatus(nsresult* status) {
*status = NS_OK;
return NS_OK;
}
NS_IMETHODIMP OnloadBlocker::SetCanceledReason(const nsACString& aReason) {
return SetCanceledReasonImpl(aReason);
}
NS_IMETHODIMP OnloadBlocker::GetCanceledReason(nsACString& aReason) {
return GetCanceledReasonImpl(aReason);
}
NS_IMETHODIMP OnloadBlocker::CancelWithReason(nsresult aStatus,
const nsACString& aReason) {
return CancelWithReasonImpl(aStatus, aReason);
}
NS_IMETHODIMP
OnloadBlocker::Cancel(nsresult status) { return NS_OK; }
NS_IMETHODIMP
OnloadBlocker::Suspend(void) { return NS_OK; }
NS_IMETHODIMP
OnloadBlocker::Resume(void) { return NS_OK; }
NS_IMETHODIMP
OnloadBlocker::GetLoadGroup(nsILoadGroup** aLoadGroup) {
*aLoadGroup = nullptr;
return NS_OK;
}
NS_IMETHODIMP
OnloadBlocker::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
NS_IMETHODIMP
OnloadBlocker::GetLoadFlags(nsLoadFlags* aLoadFlags) {
*aLoadFlags = nsIRequest::LOAD_NORMAL;
return NS_OK;
}
NS_IMETHODIMP
OnloadBlocker::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
return GetTRRModeImpl(aTRRMode);
}
NS_IMETHODIMP
OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
return SetTRRModeImpl(aTRRMode);
}
NS_IMETHODIMP
OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
// ==================================================================
namespace dom {
ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
Document* ExternalResourceMap::RequestResource(
nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode,
Document* aDisplayDocument, ExternalResourceLoad** aPendingLoad) {
// If we ever start allowing non-same-origin loads here, we might need to do
// something interesting with aRequestingPrincipal even for the hashtable
// gets.
MOZ_ASSERT(aURI, "Must have a URI");
MOZ_ASSERT(aRequestingNode, "Must have a node");
MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
*aPendingLoad = nullptr;
if (mHaveShutDown) {
return nullptr;
}
// First, make sure we strip the ref from aURI.
nsCOMPtr<nsIURI> clone;
nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(clone));
if (NS_FAILED(rv) || !clone) {
return nullptr;
}
ExternalResource* resource;
mMap.Get(clone, &resource);
if (resource) {
return resource->mDocument;
}
bool loadStartSucceeded =
mPendingLoads.WithEntryHandle(clone, [&](auto&& loadEntry) {
if (!loadEntry) {
loadEntry.Insert(MakeRefPtr<PendingLoad>(aDisplayDocument));
if (NS_FAILED(loadEntry.Data()->StartLoad(clone, aReferrerInfo,
aRequestingNode))) {
return false;
}
}
RefPtr<PendingLoad> load(loadEntry.Data());
load.forget(aPendingLoad);
return true;
});
if (!loadStartSucceeded) {
// Make sure we don't thrash things by trying this load again, since
// chances are it failed for good reasons (security check, etc).
// This must be done outside the WithEntryHandle functor, as it accesses
// mPendingLoads.
AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
}
return nullptr;
}
void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback) const {
nsTArray<RefPtr<Document>> docs(mMap.Count());
for (const auto& entry : mMap.Values()) {
if (Document* doc = entry->mDocument) {
docs.AppendElement(doc);
}
}
for (auto& doc : docs) {
if (aCallback(*doc) == CallState::Stop) {
return;
}
}
}
void ExternalResourceMap::CollectDescendantDocuments(
nsTArray<RefPtr<Document>>& aDocs, SubDocTestFunc aCallback) const {
for (const auto& entry : mMap.Values()) {
if (Document* doc = entry->mDocument) {
if (aCallback(doc)) {
aDocs.AppendElement(doc);
}
doc->CollectDescendantDocuments(aDocs, Document::IncludeSubResources::Yes,
aCallback);
}
}
}
void ExternalResourceMap::Traverse(
nsCycleCollectionTraversalCallback* aCallback) const {
// mPendingLoads will get cleared out as the requests complete, so
// no need to worry about those here.
for (const auto& entry : mMap) {
ExternalResourceMap::ExternalResource* resource = entry.GetWeak();
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mExternalResourceMap.mMap entry"
"->mDocument");
aCallback->NoteXPCOMChild(ToSupports(resource->mDocument));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mExternalResourceMap.mMap entry"
"->mViewer");
aCallback->NoteXPCOMChild(resource->mViewer);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
"mExternalResourceMap.mMap entry"
"->mLoadGroup");
aCallback->NoteXPCOMChild(resource->mLoadGroup);
}
}
void ExternalResourceMap::HideViewers() {
for (const auto& entry : mMap) {
nsCOMPtr<nsIDocumentViewer> viewer = entry.GetData()->mViewer;
if (viewer) {
viewer->Hide();
}
}
}
void ExternalResourceMap::ShowViewers() {
for (const auto& entry : mMap) {
nsCOMPtr<nsIDocumentViewer> viewer = entry.GetData()->mViewer;
if (viewer) {
viewer->Show();
}
}
}
void TransferShowingState(Document* aFromDoc, Document* aToDoc) {
MOZ_ASSERT(aFromDoc && aToDoc, "transferring showing state from/to null doc");
if (aFromDoc->IsShowing()) {
aToDoc->OnPageShow(true, nullptr);
}
}
nsresult ExternalResourceMap::AddExternalResource(nsIURI* aURI,
nsIDocumentViewer* aViewer,
nsILoadGroup* aLoadGroup,
Document* aDisplayDocument) {
MOZ_ASSERT(aURI, "Unexpected call");
MOZ_ASSERT((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
"Must have both or neither");
RefPtr<PendingLoad> load;
mPendingLoads.Remove(aURI, getter_AddRefs(load));
nsresult rv = NS_OK;
nsCOMPtr<Document> doc;
if (aViewer) {
doc = aViewer->GetDocument();
NS_ASSERTION(doc, "Must have a document");
doc->SetDisplayDocument(aDisplayDocument);
// Make sure that hiding our viewer will tear down its presentation.
aViewer->SetSticky(false);
rv = aViewer->Init(nullptr, LayoutDeviceIntRect(), nullptr);
if (NS_SUCCEEDED(rv)) {
rv = aViewer->Open(nullptr, nullptr);
}
if (NS_FAILED(rv)) {
doc = nullptr;
aViewer = nullptr;
aLoadGroup = nullptr;
}
}
ExternalResource* newResource =
mMap.InsertOrUpdate(aURI, MakeUnique<ExternalResource>()).get();
newResource->mDocument = doc;
newResource->mViewer = aViewer;
newResource->mLoadGroup = aLoadGroup;
if (doc) {
if (nsPresContext* pc = doc->GetPresContext()) {
pc->RecomputeBrowsingContextDependentData();
}
TransferShowingState(aDisplayDocument, doc);
}
const nsTArray<nsCOMPtr<nsIObserver>>& obs = load->Observers();
for (uint32_t i = 0; i < obs.Length(); ++i) {
obs[i]->Observe(ToSupports(doc), "external-resource-document-created",
nullptr);
}
return rv;
}
NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad, nsIStreamListener,
nsIRequestObserver)
NS_IMETHODIMP
ExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest* aRequest) {
ExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
if (map.HaveShutDown()) {
return NS_BINDING_ABORTED;
}
nsCOMPtr<nsIDocumentViewer> viewer;
nsCOMPtr<nsILoadGroup> loadGroup;
nsresult rv =
SetupViewer(aRequest, getter_AddRefs(viewer), getter_AddRefs(loadGroup));
// Make sure to do this no matter what
nsresult rv2 =
map.AddExternalResource(mURI, viewer, loadGroup, mDisplayDocument);
if (NS_FAILED(rv)) {
return rv;
}
if (NS_FAILED(rv2)) {
mTargetListener = nullptr;
return rv2;
}
return mTargetListener->OnStartRequest(aRequest);
}
nsresult ExternalResourceMap::PendingLoad::SetupViewer(
nsIRequest* aRequest, nsIDocumentViewer** aViewer,
nsILoadGroup** aLoadGroup) {
MOZ_ASSERT(!mTargetListener, "Unexpected call to OnStartRequest");
*aViewer = nullptr;
*aLoadGroup = nullptr;
nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
if (httpChannel) {
bool requestSucceeded;
if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
!requestSucceeded) {
// Bail out on this load, since it looks like we have an HTTP error page
return NS_BINDING_ABORTED;
}
}
nsAutoCString type;
chan->GetContentType(type);
nsCOMPtr<nsILoadGroup> loadGroup;
chan->GetLoadGroup(getter_AddRefs(loadGroup));
// Give this document its own loadgroup
nsCOMPtr<nsILoadGroup> newLoadGroup =
do_CreateInstance(NS_LOADGROUP_CONTRACTID);
NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
newLoadGroup->SetLoadGroup(loadGroup);
nsCOMPtr<nsIInterfaceRequestor> callbacks;
loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
new LoadgroupCallbacks(callbacks);
newLoadGroup->SetNotificationCallbacks(newCallbacks);
nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
nsContentUtils::FindInternalDocumentViewer(type);
NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIDocumentViewer> viewer;
nsCOMPtr<nsIStreamListener> listener;
nsresult rv = docLoaderFactory->CreateInstance(
"external-resource", chan, newLoadGroup, type, nullptr, nullptr,
getter_AddRefs(listener), getter_AddRefs(viewer));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
if (!parser) {
/// We don't want to deal with the various fake documents yet
return NS_ERROR_NOT_IMPLEMENTED;
}
// We can't handle HTML and other weird things here yet.
nsIContentSink* sink = parser->GetContentSink();
nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
if (!xmlSink) {
return NS_ERROR_NOT_IMPLEMENTED;
}
listener.swap(mTargetListener);
viewer.forget(aViewer);
newLoadGroup.forget(aLoadGroup);
return NS_OK;
}
NS_IMETHODIMP
ExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
nsIInputStream* aStream,
uint64_t aOffset,
uint32_t aCount) {
// mTargetListener might be null if SetupViewer or AddExternalResource failed.
NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
return NS_BINDING_ABORTED;
}
return mTargetListener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
}
NS_IMETHODIMP
ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
nsresult aStatus) {
// mTargetListener might be null if SetupViewer or AddExternalResource failed
if (mTargetListener) {
nsCOMPtr<nsIStreamListener> listener;
mTargetListener.swap(listener);
return listener->OnStopRequest(aRequest, aStatus);
}
return NS_OK;
}
nsresult ExternalResourceMap::PendingLoad::StartLoad(
nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode) {
MOZ_ASSERT(aURI, "Must have a URI");
MOZ_ASSERT(aRequestingNode, "Must have a node");
MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
nsCOMPtr<nsILoadGroup> loadGroup =
aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
nsresult rv = NS_OK;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), aURI, aRequestingNode,
nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
nsIContentPolicy::TYPE_INTERNAL_EXTERNAL_RESOURCE,
nullptr, // aPerformanceStorage
loadGroup);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (httpChannel) {
rv = httpChannel->SetReferrerInfo(aReferrerInfo);
(void)NS_WARN_IF(NS_FAILED(rv));
}
mURI = aURI;
return channel->AsyncOpen(this);
}
NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks,
nsIInterfaceRequestor)
#define IMPL_SHIM(_i) \
NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
IMPL_SHIM(nsILoadContext)
IMPL_SHIM(nsIProgressEventSink)
IMPL_SHIM(nsIChannelEventSink)
#undef IMPL_SHIM
#define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
#define TRY_SHIM(_i) \
PR_BEGIN_MACRO \
if (IID_IS(_i)) { \
nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
if (!real) { \
return NS_NOINTERFACE; \
} \
nsCOMPtr<_i> shim = new _i##Shim(this, real); \
shim.forget(aSink); \
return NS_OK; \
} \
PR_END_MACRO
NS_IMETHODIMP
ExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID& aIID,
void** aSink) {
if (mCallbacks && (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) ||
IID_IS(nsIAuthPrompt2) || IID_IS(nsIBrowserChild))) {
return mCallbacks->GetInterface(aIID, aSink);
}
*aSink = nullptr;
TRY_SHIM(nsILoadContext);
TRY_SHIM(nsIProgressEventSink);
TRY_SHIM(nsIChannelEventSink);
return NS_NOINTERFACE;
}
#undef TRY_SHIM
#undef IID_IS
ExternalResourceMap::ExternalResource::~ExternalResource() {
if (mViewer) {
mViewer->Close(nullptr);
mViewer->Destroy();
}
}
// ==================================================================
// =
// ==================================================================
// If we ever have an nsIDocumentObserver notification for stylesheet title
// changes we should update the list from that instead of overriding
// EnsureFresh.
class DOMStyleSheetSetList final : public DOMStringList {
public:
explicit DOMStyleSheetSetList(Document* aDocument);
void Disconnect() { mDocument = nullptr; }
virtual void EnsureFresh() override;
protected:
Document* mDocument; // Our document; weak ref. It'll let us know if it
// dies.
};
DOMStyleSheetSetList::DOMStyleSheetSetList(Document* aDocument)
: mDocument(aDocument) {
NS_ASSERTION(mDocument, "Must have document!");
}
void DOMStyleSheetSetList::EnsureFresh() {
MOZ_ASSERT(NS_IsMainThread());
mNames.Clear();
if (!mDocument) {
return; // Spec says "no exceptions", and we have no style sets if we have
// no document, for sure
}
size_t count = mDocument->SheetCount();
nsAutoString title;
for (size_t index = 0; index < count; index++) {
StyleSheet* sheet = mDocument->SheetAt(index);
NS_ASSERTION(sheet, "Null sheet in sheet list!");
sheet->GetTitle(title);
if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
return;
}
}
}
Document::PendingFrameStaticClone::~PendingFrameStaticClone() = default;
// ==================================================================
// =
// ==================================================================
Document::InternalCommandDataHashtable*
Document::sInternalCommandDataHashtable = nullptr;
// static
void Document::Shutdown() {
if (sInternalCommandDataHashtable) {
sInternalCommandDataHashtable->Clear();
delete sInternalCommandDataHashtable;
sInternalCommandDataHashtable = nullptr;
}
}
Document::Document(const char* aContentType,
mozilla::dom::LoadedAsData aLoadedAsData)
: nsINode(nullptr),
DocumentOrShadowRoot(this),
mCharacterSet(WINDOWS_1252_ENCODING),
mCharacterSetSource(0),
mParentDocument(nullptr),
mCachedRootElement(nullptr),
mNodeInfoManager(nullptr),
#ifdef DEBUG
mStyledLinksCleared(false),
#endif
mInitialStatus(Document::InitialStatus::NeverInitial),
mCachedStateObjectValid(false),
mBlockAllMixedContent(false),
mBlockAllMixedContentPreloads(false),
mUpgradeInsecureRequests(false),
mUpgradeInsecurePreloads(false),
mDevToolsWatchingDOMMutations(false),
mLoadedAsData(aLoadedAsData == LoadedAsData::AsData),
mRenderingSuppressedForViewTransitions(false),
mBidiEnabled(false),
mMayNeedFontPrefsUpdate(true),
mIgnoreDocGroupMismatches(false),
mAddedToMemoryReportingAsDataDocument(false),
mMayStartLayout(true),
mHaveFiredTitleChange(false),
mIsShowing(false),
mVisible(true),
mIsCompletelyLoaded(false),
mRemovedFromDocShell(false),
// mAllowDNSPrefetch starts true, so that we can always reliably && it
// with various values that might disable it. Since we never prefetch
// unless we get a window, and in that case the docshell value will get
// &&-ed in, this is safe.
mAllowDNSPrefetch(true),
mIsStaticDocument(false),
mCreatingStaticClone(false),
mHasPrintCallbacks(false),
mInUnlinkOrDeletion(false),
mHasHadScriptHandlingObject(false),
mIsBeingUsedAsImage(false),
mChromeRulesEnabled(false),
mInChromeDocShell(false),
mIsSyntheticDocument(false),
mHasLinksToUpdateRunnable(false),
mFlushingPendingLinkUpdates(false),
mMayHaveDOMMutationObservers(false),
mMayHaveAnimationObservers(false),
mHasCSPDeliveredThroughHeader(false),
mBFCacheDisallowed(false),
mHasHadDefaultView(false),
mStyleSheetChangeEventsEnabled(false),
mDevToolsAnonymousAndShadowEventsEnabled(false),
mPausedByDevTools(false),
mForceNonNativeTheme(false),
mIsSrcdocDocument(false),
mHasDisplayDocument(false),
mFontFaceSetDirty(true),
mDidFireDOMContentLoaded(true),
mIsTopLevelContentDocument(false),
mIsContentDocument(false),
mDidCallBeginLoad(false),
mEncodingMenuDisabled(false),
mLinksEnabled(true),
mIsSVGGlyphsDocument(false),
mInDestructor(false),
mIsGoingAway(false),
mStyleSetFilled(false),
mQuirkSheetAdded(false),
mMayHaveTitleElement(false),
mDOMLoadingSet(false),
mDOMInteractiveSet(false),
mDOMCompleteSet(false),
mAutoFocusFired(false),
mScrolledToRefAlready(false),
mChangeScrollPosWhenScrollingToRef(false),
mDelayFrameLoaderInitialization(false),
mSynchronousDOMContentLoaded(false),
mMaybeServiceWorkerControlled(false),
mAllowZoom(false),
mValidScaleFloat(false),
mValidMinScale(false),
mValidMaxScale(false),
mWidthStrEmpty(false),
mLockingImages(false),
mAnimatingImages(true),
mParserAborted(false),
mReportedDocumentUseCounters(false),
mHasReportedShadowDOMUsage(false),
mLoadEventFiring(false),
mSkipLoadEventAfterClose(false),
mDisableCookieAccess(false),
mDisableDocWrite(false),
mTooDeepWriteRecursion(false),
mPendingMaybeEditingStateChanged(false),
mHasBeenEditable(false),
mIsRunningExecCommandByContent(false),
mIsRunningExecCommandByChromeOrAddon(false),
mSetCompleteAfterDOMContentLoaded(false),
mDidHitCompleteSheetCache(false),
mUseCountersInitialized(false),
mShouldReportUseCounters(false),
mShouldSendPageUseCounters(false),
mUserHasInteracted(false),
mHasUserInteractionTimerScheduled(false),
mShouldResistFingerprinting(false),
mIsInPrivateBrowsing(false),
mCloningForSVGUse(false),
mAllowDeclarativeShadowRoots(false),
mSuspendDOMNotifications(false),
mForceLoadAtTop(false),
mSuppressNotifyingDevToolsOfNodeRemovals(false),
mHasPolicyWithRequireTrustedTypesForDirective(false),
mClipboardCopyTriggered(false),
mXMLDeclarationBits(0),
mOnloadBlockCount(0),
mWriteLevel(0),
mContentEditableCount(0),
mEditingState(EditingState::eOff),
mCompatMode(eCompatibility_FullStandards),
mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
mAncestorIsLoading(false),
mVisibilityState(dom::VisibilityState::Hidden),
mType(eUnknown),
mDefaultElementType(0),
mAllowXULXBL(eTriUnset),
mSkipDTDSecurityChecks(false),
mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
mSandboxFlags(0),
mPartID(0),
mMarkedCCGeneration(0),
mPresShell(nullptr),
mPreloadPictureDepth(0),
mEventsSuppressed(0),
mIgnoreDestructiveWritesCounter(0),
mStaticCloneCount(0),
mWindow(nullptr),
mBFCacheEntry(nullptr),
mInSyncOperationCount(0),
mBlockDOMContentLoaded(0),
mUpdateNestLevel(0),
mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED),
mViewportType(Unknown),
mViewportFit(ViewportFitType::Auto),
mInteractiveWidgetMode(
InteractiveWidgetUtils::DefaultInteractiveWidgetMode()),
mHeaderData(nullptr),
mLanguageFromCharset(nullptr),
mServoRestyleRootDirtyBits(0),
mThrowOnDynamicMarkupInsertionCounter(0),
mIgnoreOpensDuringUnloadCounter(0),
mSavedResolution(1.0f),
mClassificationFlags({0, 0}),
mGeneration(0),
mCachedTabSizeGeneration(0),
mNextFormNumber(0),
mNextControlNumber(0),
mPreloadService(this),
mShouldNotifyFetchSuccess(false),
mShouldNotifyFormOrPasswordRemoved(false) {
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
SetIsInDocument();
SetIsConnected(true);
// Create these unconditionally, they will be used to warn about the `zoom`
// property, even if use counters are disabled.
mStyleUseCounters.reset(Servo_UseCounters_Create());
SetContentType(nsDependentCString(aContentType));
// Start out mLastStyleSheetSet as null, per spec
SetDOMStringToNull(mLastStyleSheetSet);
// void state used to differentiate an empty source from an unselected source
mPreloadPictureFoundSource.SetIsVoid(true);
RecomputeLanguageFromCharset();
mPreloadReferrerInfo = new dom::ReferrerInfo(nullptr);
mReferrerInfo = new dom::ReferrerInfo(nullptr);
}
#ifndef ANDROID
// unused by GeckoView
static bool IsAboutErrorPage(nsGlobalWindowInner* aWin, const char* aSpec) {
if (NS_WARN_IF(!aWin)) {
return false;
}
nsIURI* uri = aWin->GetDocumentURI();
if (NS_WARN_IF(!uri)) {
return false;
}
// getSpec is an expensive operation, hence we first check the scheme
// to see if the caller is actually an about: page.
if (!uri->SchemeIs("about")) {
return false;
}
nsAutoCString aboutSpec;
nsresult rv = NS_GetAboutModuleName(uri, aboutSpec);
NS_ENSURE_SUCCESS(rv, false);
return aboutSpec.EqualsASCII(aSpec);
}
#endif
bool Document::CallerIsTrustedAboutNetError(JSContext* aCx, JSObject* aObject) {
nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
#ifdef ANDROID
// GeckoView uses data URLs for error pages, so for now just check for any
// error page
return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
#else
return win && IsAboutErrorPage(win, "neterror");
#endif
}
bool Document::CallerIsTrustedAboutHttpsOnlyError(JSContext* aCx,
JSObject* aObject) {
nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
#ifdef ANDROID
// GeckoView uses data URLs for error pages, so for now just check for any
// error page
return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
#else
return win && IsAboutErrorPage(win, "httpsonlyerror");
#endif
}
already_AddRefed<mozilla::dom::Promise> Document::AddCertException(
bool aIsTemporary, ErrorResult& aError) {
RefPtr<Promise> promise = Promise::Create(GetScopeObject(), aError,
Promise::ePropagateUserInteraction);
if (aError.Failed()) {
return nullptr;
}
nsresult rv = NS_OK;
if (NS_WARN_IF(!mFailedChannel)) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsCOMPtr<nsIURI> failedChannelURI;
NS_GetFinalChannelURI(mFailedChannel, getter_AddRefs(failedChannelURI));
if (!failedChannelURI) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(failedChannelURI);
if (!innerURI) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsAutoCString host;
innerURI->GetAsciiHost(host);
int32_t port;
innerURI->GetPort(&port);
nsCOMPtr<nsITransportSecurityInfo> tsi;
rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
if (NS_WARN_IF(!tsi)) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
nsCOMPtr<nsIX509Cert> cert;
rv = tsi->GetServerCert(getter_AddRefs(cert));
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
if (NS_WARN_IF(!cert)) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
return promise.forget();
}
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
OriginAttributes const& attrs = NodePrincipal()->OriginAttributesRef();
cc->SendAddCertException(cert, host, port, attrs, aIsTemporary)
->Then(GetCurrentSerialEventTarget(), __func__,
[promise](const mozilla::MozPromise<
nsresult, mozilla::ipc::ResponseRejectReason,
true>::ResolveOrRejectValue& aValue) {
if (aValue.IsResolve()) {
promise->MaybeResolve(aValue.ResolveValue());
} else {
promise->MaybeRejectWithUndefined();
}
});
return promise.forget();
}
if (XRE_IsParentProcess()) {
nsCOMPtr<nsICertOverrideService> overrideService =
do_GetService(NS_CERTOVERRIDE_CONTRACTID);
if (!overrideService) {
promise->MaybeReject(NS_ERROR_FAILURE);
return promise.forget();
}
OriginAttributes const& attrs = NodePrincipal()->OriginAttributesRef();
rv = overrideService->RememberValidityOverride(host, port, attrs, cert,
aIsTemporary);
if (NS_WARN_IF(NS_FAILED(rv))) {
promise->MaybeReject(rv);
return promise.forget();
}
promise->MaybeResolveWithUndefined();
return promise.forget();
}
promise->MaybeReject(NS_ERROR_FAILURE);
return promise.forget();
}
void Document::ReloadWithHttpsOnlyException() {
if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
wgc->SendReloadWithHttpsOnlyException();
}
}
// Given an nsresult that is assumed to be synthesized by PSM and describes a
// certificate or TLS error, attempts to convert it into a string
// representation of the underlying NSS error.
// `aErrorCodeString` will be an empty string if `aResult` is not an error from
// PSM or it does not represent a valid NSS error.
void GetErrorCodeStringFromNSResult(nsresult aResult,
nsAString& aErrorCodeString) {
aErrorCodeString.Truncate();
if (NS_ERROR_GET_MODULE(aResult) != NS_ERROR_MODULE_SECURITY ||
NS_ERROR_GET_SEVERITY(aResult) != NS_ERROR_SEVERITY_ERROR) {
return;
}
PRErrorCode errorCode = -1 * NS_ERROR_GET_CODE(aResult);
if (!mozilla::psm::IsNSSErrorCode(errorCode)) {
return;
}
const char* errorCodeString = PR_ErrorToName(errorCode);
if (!errorCodeString) {
return;
}
aErrorCodeString.AssignASCII(errorCodeString);
}
void Document::GetNetErrorInfo(NetErrorInfo& aInfo, ErrorResult& aRv) {
nsresult rv = NS_OK;
if (NS_WARN_IF(!mFailedChannel)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mFailedChannel));
// We don't throw even if httpChannel is null, we just keep responseStatus and
// responseStatusText empty
if (httpChannel) {
uint32_t responseStatus;
nsAutoCString responseStatusText;
rv = httpChannel->GetResponseStatus(&responseStatus);
if (NS_SUCCEEDED(rv)) {
aInfo.mResponseStatus = responseStatus;
}
rv = httpChannel->GetResponseStatusText(responseStatusText);
if (NS_FAILED(rv) || responseStatusText.IsEmpty()) {
net_GetDefaultStatusTextForCode(responseStatus, responseStatusText);
}
aInfo.mResponseStatusText.AssignASCII(responseStatusText);
}
nsCOMPtr<nsITransportSecurityInfo> tsi;
rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
nsresult channelStatus;
rv = mFailedChannel->GetStatus(&channelStatus);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
aInfo.mChannelStatus = static_cast<uint32_t>(channelStatus);
// If nsITransportSecurityInfo is not set, simply keep the remaining fields
// empty (to make responseStatus and responseStatusText accessible).
if (!tsi) {
return;
}
// TransportSecurityInfo::GetErrorCodeString always returns NS_OK
(void)tsi->GetErrorCodeString(aInfo.mErrorCodeString);
if (aInfo.mErrorCodeString.IsEmpty()) {
GetErrorCodeStringFromNSResult(channelStatus, aInfo.mErrorCodeString);
}
}
bool Document::CallerIsTrustedAboutCertError(JSContext* aCx,
JSObject* aObject) {
nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
#ifdef ANDROID
// GeckoView uses data URLs for error pages, so for now just check for any
// error page
return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
#else
return win && IsAboutErrorPage(win, "certerror");
#endif
}
bool Document::CallerIsSystemPrincipalOrWebCompatAddon(JSContext* aCx,
JSObject* aObject) {
RefPtr<BasePrincipal> principal =
BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx));
if (!principal) {
return false;
}
// We allow the privileged APIs to be called from system principal.
if (principal->IsSystemPrincipal()) {
return true;
}
// We only allow calling privileged APIs from the webcompat extension.
if (auto* policy = principal->ContentScriptAddonPolicy()) {
nsAutoString addonID;
policy->GetId(addonID);
return addonID.EqualsLiteral("webcompat@mozilla.org");
}
return false;
}
bool Document::IsErrorPage() const {
nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->LoadInfo() : nullptr;
return loadInfo && loadInfo->GetLoadErrorPage();
}
void Document::GetFailedCertSecurityInfo(FailedCertSecurityInfo& aInfo,
ErrorResult& aRv) {
nsresult rv = NS_OK;
if (NS_WARN_IF(!mFailedChannel)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsCOMPtr<nsITransportSecurityInfo> tsi;
rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
if (NS_WARN_IF(!tsi)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsresult channelStatus;
rv = mFailedChannel->GetStatus(&channelStatus);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
aInfo.mChannelStatus = static_cast<uint32_t>(channelStatus);
// TransportSecurityInfo::GetErrorCodeString always returns NS_OK
(void)tsi->GetErrorCodeString(aInfo.mErrorCodeString);
if (aInfo.mErrorCodeString.IsEmpty()) {
GetErrorCodeStringFromNSResult(channelStatus, aInfo.mErrorCodeString);
}
nsITransportSecurityInfo::OverridableErrorCategory errorCategory;
rv = tsi->GetOverridableErrorCategory(&errorCategory);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
switch (errorCategory) {
case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST:
aInfo.mOverridableErrorCategory =
dom::OverridableErrorCategory::Trust_error;
break;
case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN:
aInfo.mOverridableErrorCategory =
dom::OverridableErrorCategory::Domain_mismatch;
break;
case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME:
aInfo.mOverridableErrorCategory =
dom::OverridableErrorCategory::Expired_or_not_yet_valid;
break;
default:
aInfo.mOverridableErrorCategory = dom::OverridableErrorCategory::Unset;
break;
}
nsCOMPtr<nsIX509Cert> cert;
nsCOMPtr<nsIX509CertValidity> validity;
rv = tsi->GetServerCert(getter_AddRefs(cert));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
if (NS_WARN_IF(!cert)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
rv = cert->GetValidity(getter_AddRefs(validity));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
if (NS_WARN_IF(!validity)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
PRTime validityResult;
rv = validity->GetNotBefore(&validityResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
aInfo.mValidNotBefore = DOMTimeStamp(validityResult / PR_USEC_PER_MSEC);
rv = validity->GetNotAfter(&validityResult);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
aInfo.mValidNotAfter = DOMTimeStamp(validityResult / PR_USEC_PER_MSEC);
nsAutoString issuerCommonName;
nsAutoString certChainPEMString;
Sequence<nsString>& certChainStrings = aInfo.mCertChainStrings.Construct();
int64_t maxValidity = std::numeric_limits<int64_t>::max();
int64_t minValidity = 0;
PRTime notBefore, notAfter;
nsTArray<RefPtr<nsIX509Cert>> handshakeCertificates;
rv = tsi->GetHandshakeCertificates(handshakeCertificates);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
if (NS_WARN_IF(handshakeCertificates.IsEmpty())) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
for (const auto& certificate : handshakeCertificates) {
rv = certificate->GetIssuerCommonName(issuerCommonName);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
rv = certificate->GetValidity(getter_AddRefs(validity));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
if (NS_WARN_IF(!validity)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
rv = validity->GetNotBefore(¬Before);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
rv = validity->GetNotAfter(¬After);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
notBefore = std::max(minValidity, notBefore);
notAfter = std::min(maxValidity, notAfter);
nsTArray<uint8_t> certArray;
rv = certificate->GetRawDER(certArray);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
nsAutoString der64;
rv = Base64Encode(reinterpret_cast<const char*>(certArray.Elements()),
certArray.Length(), der64);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
if (!certChainStrings.AppendElement(der64, fallible)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
}
aInfo.mIssuerCommonName.Assign(issuerCommonName);
aInfo.mCertValidityRangeNotAfter = DOMTimeStamp(notAfter / PR_USEC_PER_MSEC);
aInfo.mCertValidityRangeNotBefore =
DOMTimeStamp(notBefore / PR_USEC_PER_MSEC);
int32_t errorCode;
rv = tsi->GetErrorCode(&errorCode);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
aInfo.mErrorIsOverridable = mozilla::psm::ErrorIsOverridable(errorCode);
nsCOMPtr<nsINSSErrorsService> nsserr =
do_GetService("@mozilla.org/nss_errors_service;1");
if (NS_WARN_IF(!nsserr)) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsresult res;
rv = nsserr->GetXPCOMFromNSSError(errorCode, &res);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
rv = nsserr->GetErrorMessage(res, aInfo.mErrorMessage);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
OriginAttributes attrs;
StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs);
nsCOMPtr<nsIURI> aURI;
mFailedChannel->GetURI(getter_AddRefs(aURI));
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);
cc->SendIsSecureURI(aURI, attrs, &aInfo.mHasHSTS);
} else {
nsCOMPtr<nsISiteSecurityService> sss =
do_GetService(NS_SSSERVICE_CONTRACTID);
if (NS_WARN_IF(!sss)) {
return;
}
(void)NS_WARN_IF(NS_FAILED(sss->IsSecureURI(aURI, attrs, &aInfo.mHasHSTS)));
}
nsCOMPtr<nsIPublicKeyPinningService> pkps =
do_GetService(NS_PKPSERVICE_CONTRACTID);
if (NS_WARN_IF(!pkps)) {
return;
}
(void)NS_WARN_IF(NS_FAILED(pkps->HostHasPins(aURI, &aInfo.mHasHPKP)));
}
bool Document::IsAboutPage() const {
return NodePrincipal()->SchemeIs("about");
}
void Document::ConstructUbiNode(void* storage) {
JS::ubi::Concrete<Document>::construct(storage, this);
}
void Document::LoadEventFired() {
// Collect page load timings
AccumulatePageLoadTelemetry();
// Record page load event
RecordPageLoadEventTelemetry();
// Release the JS bytecode cache from its wait on the load event, and
// potentially dispatch the encoding of the bytecode.
if (mScriptLoader) {
mScriptLoader->LoadEventFired();
}
}
static void RecordExecutionTimeForAsmJS(Document* aDoc) {
if (aDoc->GetScopeObject()) {
AutoJSAPI jsapi;
if (!jsapi.Init(aDoc->GetScopeObject())) {
return;
}
if (JSContext* cx = jsapi.cx()) {
// Disable the execution timer.
JS::SetMeasuringExecutionTimeEnabled(cx, false);
// Get the execution time and accumulate it.
JS::JSTimers timers = JS::GetJSTimers(cx);
mozilla::glean::perf::js_exec_asm_js.AccumulateRawDuration(
timers.executionTime);
}
}
}
class ASMJSExecutionTimeRecorder final : public nsITimerCallback,
public nsINamed {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
explicit ASMJSExecutionTimeRecorder(Document* aDocument)
: mDocument(aDocument) {}
private:
~ASMJSExecutionTimeRecorder() = default;
WeakPtr<Document> mDocument;
};
NS_IMPL_ISUPPORTS(ASMJSExecutionTimeRecorder, nsITimerCallback, nsINamed)
NS_IMETHODIMP
ASMJSExecutionTimeRecorder::Notify(nsITimer* aTimer) {
RefPtr<Document> doc(mDocument);
if (!doc) {
return NS_OK;
}
// Record the JS execution time to Glean.
RecordExecutionTimeForAsmJS(doc);
return NS_OK;
}
NS_IMETHODIMP
ASMJSExecutionTimeRecorder::GetName(nsACString& aName) {
aName.AssignLiteral("ASMJSExecutionTimeRecorder");
return NS_OK;
}
// If an asm.js use counter is set, then enable the JS execution
// timer and record it after 5 minutes of activity.
void Document::RecordASMJSExecutionTime() {
// Skip if the document is being destroyed.
if (mIsGoingAway) {
return;
}
// If the timer has already fired, or the use counter is already set,
// then nothing more needs to be done.
if (mASMJSExecutionTimer || HasUseCounter(eUseCounter_custom_JS_use_asm)) {
return;
}
AutoJSContext cx;
if (static_cast<JSContext*>(cx)) {
JS::SetMeasuringExecutionTimeEnabled(cx, true);
}
RefPtr<ASMJSExecutionTimeRecorder> callback =
new ASMJSExecutionTimeRecorder(this);
nsresult rv =
NS_NewTimerWithCallback(getter_AddRefs(mASMJSExecutionTimer), callback,
5 * 60 * 1000, // 5min delay
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
mASMJSExecutionTimer = nullptr;
}
}
void Document::RecordPageLoadEventTelemetry() {
// If the page load time is empty, then the content wasn't something we want
// to report (i.e. not a top level document).
if (!mPageloadEventData.HasLoadTime()) {
return;
}
MOZ_ASSERT(IsTopLevelContentDocument());
nsPIDOMWindowOuter* window = GetWindow();
if (!window) {
return;
}
nsIDocShell* docshell = window->GetDocShell();
if (!docshell) {
return;
}
// Don't send any event telemetry for private browsing.
if (IsInPrivateBrowsing()) {
return;
}
if (!GetChannel()) {
return;
}
auto pageloadEventType = performance::pageload_event::GetPageloadEventType();
// Return if we are not sending an event for this pageload.
if (pageloadEventType == mozilla::PageloadEventType::kNone) {
return;
}
#ifdef ACCESSIBILITY
if (GetAccService() != nullptr) {
mPageloadEventData.SetUserFeature(
performance::pageload_event::UserFeature::USING_A11Y);
}
#endif
if (GetChannel()) {
nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
do_QueryInterface(GetChannel());
if (cacheInfoChannel) {
nsICacheInfoChannel::CacheDisposition disposition =
nsICacheInfoChannel::kCacheUnknown;
nsresult rv = cacheInfoChannel->GetCacheDisposition(&disposition);
if (NS_SUCCEEDED(rv)) {
mPageloadEventData.set_cacheDisposition(disposition);
}
}
}
nsAutoCString loadTypeStr;
switch (docshell->GetLoadType()) {
case LOAD_NORMAL:
case LOAD_NORMAL_REPLACE:
case LOAD_NORMAL_BYPASS_CACHE:
case LOAD_NORMAL_BYPASS_PROXY:
case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
loadTypeStr.Append("NORMAL");
break;
case LOAD_HISTORY:
loadTypeStr.Append("HISTORY");
break;
case LOAD_RELOAD_NORMAL:
case LOAD_RELOAD_BYPASS_CACHE:
case LOAD_RELOAD_BYPASS_PROXY:
case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
case LOAD_REFRESH:
case LOAD_REFRESH_REPLACE:
case LOAD_RELOAD_CHARSET_CHANGE:
case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
loadTypeStr.Append("RELOAD");
break;
case LOAD_LINK:
loadTypeStr.Append("LINK");
break;
case LOAD_STOP_CONTENT:
case LOAD_STOP_CONTENT_AND_REPLACE:
loadTypeStr.Append("STOP");
break;
case LOAD_ERROR_PAGE:
loadTypeStr.Append("ERROR");
break;
default:
loadTypeStr.Append("OTHER");
break;
}
mPageloadEventData.set_loadType(loadTypeStr);
nsCOMPtr<nsIEffectiveTLDService> tldService =
mozilla::components::EffectiveTLD::Service();
nsresult rv = NS_OK;
if (tldService) {
if (mReferrerInfo &&
(docshell->GetLoadType() & nsIDocShell::LOAD_CMD_NORMAL)) {
nsAutoCString currentBaseDomain, referrerBaseDomain;
nsCOMPtr<nsIURI> referrerURI = mReferrerInfo->GetComputedReferrer();
if (referrerURI) {
rv = tldService->GetBaseDomain(referrerURI, 0, referrerBaseDomain);
if (NS_SUCCEEDED(rv)) {
bool sameOrigin = false;
NodePrincipal()->IsSameOrigin(referrerURI, &sameOrigin);
mPageloadEventData.set_sameOriginNav(sameOrigin);
}
}
}
}
if (pageloadEventType == PageloadEventType::kDomain) {
// Do not record anything if we failed to assign the domain.
if (!mPageloadEventData.MaybeSetPublicRegistrableDomain(GetDocumentURI(),
GetChannel())) {
return;
}
}
// Collect any JS timers that were measured during pageload.
if (GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
AutoJSContext cx;
JSObject* globalObject = GetScopeObject()->GetGlobalJSObject();
JSAutoRealm ar(cx, globalObject);
JS::JSTimers timers = JS::GetJSTimers(cx);
if (!timers.executionTime.IsZero()) {
mPageloadEventData.set_jsExecTime(
static_cast<uint32_t>(timers.executionTime.ToMilliseconds()));
}
if (!timers.delazificationTime.IsZero()) {
mPageloadEventData.set_delazifyTime(
static_cast<uint32_t>(timers.delazificationTime.ToMilliseconds()));
}
}
// Sending a glean ping must be done on the parent process.
if (ContentChild* cc = ContentChild::GetSingleton()) {
if (GetNavigationTiming()) {
uint64_t androidAppLinkLoadIdentifier = 0;
#ifdef ANDROID
if (BrowsingContext* bc = GetBrowsingContext()) {
Maybe<uint64_t> contextAppLinkLoadIdentifier =
bc->GetAndroidAppLinkLoadIdentifier();
if (contextAppLinkLoadIdentifier.isSome()) {
androidAppLinkLoadIdentifier = contextAppLinkLoadIdentifier.value();
}
}
#endif
cc->SendRecordPageLoadEvent(
mPageloadEventData,
GetNavigationTiming()->GetNavigationStartTimeStamp(),
androidAppLinkLoadIdentifier);
}
}
}
#ifndef ANDROID
static void AccumulateHttp3FcpGleanPref(const nsCString& http3Key,
const TimeDuration& duration) {
if (http3Key == "http3"_ns) {
glean::performance_pageload::http3_fcp_http3.AccumulateRawDuration(
duration);
} else if (http3Key == "supports_http3"_ns) {
glean::performance_pageload::http3_fcp_supports_http3.AccumulateRawDuration(
duration);
} else {
MOZ_ASSERT_UNREACHABLE("Unknown value for http3Key");
}
}
static void AccumulatePriorityFcpGleanPref(
const nsCString& http3WithPriorityKey, const TimeDuration& duration) {
if (http3WithPriorityKey == "with_priority"_ns) {
glean::performance_pageload::h3p_fcp_with_priority.AccumulateRawDuration(
duration);
} else if (http3WithPriorityKey == "without_priority"_ns) {
glean::performance_pageload::http3_fcp_without_priority
.AccumulateRawDuration(duration);
} else {
MOZ_ASSERT_UNREACHABLE("Unknown value for http3WithPriorityKey");
}
}
#endif
void Document::AccumulatePageLoadTelemetry() {
// Interested only in top level documents for real websites that are in the
// foreground.
if (!ShouldIncludeInTelemetry() || !IsTopLevelContentDocument() ||
!GetNavigationTiming() ||
!GetNavigationTiming()->DocShellHasBeenActiveSinceNavigationStart()) {
return;
}
if (!GetChannel()) {
return;
}
nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(GetChannel()));
if (!timedChannel) {
return;
}
// Default duration is 0, use this to check for bogus negative values.
const TimeDuration zeroDuration;
TimeStamp responseStart;
timedChannel->GetResponseStart(&responseStart);
TimeStamp redirectStart, redirectEnd;
timedChannel->GetRedirectStart(&redirectStart);
timedChannel->GetRedirectEnd(&redirectEnd);
uint8_t redirectCount;
timedChannel->GetRedirectCount(&redirectCount);
if (redirectCount) {
mPageloadEventData.set_redirectCount(static_cast<uint32_t>(redirectCount));
}
if (!redirectStart.IsNull() && !redirectEnd.IsNull()) {
TimeDuration redirectTime = redirectEnd - redirectStart;
if (redirectTime > zeroDuration) {
mPageloadEventData.set_redirectTime(
static_cast<uint32_t>(redirectTime.ToMilliseconds()));
}
}
TimeStamp dnsLookupStart, dnsLookupEnd;
timedChannel->GetDomainLookupStart(&dnsLookupStart);
timedChannel->GetDomainLookupEnd(&dnsLookupEnd);
if (!dnsLookupStart.IsNull() && !dnsLookupEnd.IsNull()) {
TimeDuration dnsLookupTime = dnsLookupEnd - dnsLookupStart;
if (dnsLookupTime > zeroDuration) {
mPageloadEventData.set_dnsLookupTime(
static_cast<uint32_t>(dnsLookupTime.ToMilliseconds()));
}
}
TimeStamp navigationStart =
GetNavigationTiming()->GetNavigationStartTimeStamp();
if (!navigationStart) {
return;
}
if (!responseStart) {
// This happens when getting a response from the cache.
responseStart = navigationStart;
}
nsAutoCString dnsKey("Native");
nsAutoCString http3Key;
nsAutoCString http3WithPriorityKey;
nsAutoCString earlyHintKey;
nsCOMPtr<nsIHttpChannelInternal> httpChannel =
do_QueryInterface(GetChannel());
if (httpChannel) {
bool resolvedByTRR = false;
(void)httpChannel->GetIsResolvedByTRR(&resolvedByTRR);
if (resolvedByTRR) {
if (nsCOMPtr<nsIDNSService> dns =
do_GetService(NS_DNSSERVICE_CONTRACTID)) {
dns->GetTRRDomainKey(dnsKey);
} else {
// Failed to get the DNS service.
dnsKey = "(fail)"_ns;
}
mPageloadEventData.set_trrDomain(dnsKey);
}
uint32_t major;
uint32_t minor;
if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) {
if (major == 3) {
http3Key = "http3"_ns;
nsCOMPtr<nsIHttpChannel> httpChannel2 = do_QueryInterface(GetChannel());
nsCString header;
if (httpChannel2 &&
NS_SUCCEEDED(
httpChannel2->GetResponseHeader("priority"_ns, header)) &&
!header.IsEmpty()) {
http3WithPriorityKey = "with_priority"_ns;
} else {
http3WithPriorityKey = "without_priority"_ns;
}
} else if (major == 2) {
bool supportHttp3 = false;
if (NS_FAILED(httpChannel->GetSupportsHTTP3(&supportHttp3))) {
supportHttp3 = false;
}
if (supportHttp3) {
http3Key = "supports_http3"_ns;
}
}
mPageloadEventData.set_httpVer(major);
}
uint32_t earlyHintType = 0;
(void)httpChannel->GetEarlyHintLinkType(&earlyHintType);
if (earlyHintType & LinkStyle::ePRECONNECT) {
earlyHintKey.Append("preconnect_"_ns);
}
if (earlyHintType & LinkStyle::ePRELOAD) {
earlyHintKey.Append("preload_"_ns);
earlyHintKey.Append(mPreloadService.GetEarlyHintUsed() ? "1"_ns : "0"_ns);
}
}
TimeStamp asyncOpen;
timedChannel->GetAsyncOpen(&asyncOpen);
if (asyncOpen) {
glean::perf::dns_first_byte.Get(dnsKey).AccumulateRawDuration(
responseStart - asyncOpen);
}
// First Contentful Composite
if (TimeStamp firstContentfulComposite =
GetNavigationTiming()->GetFirstContentfulCompositeTimeStamp()) {
glean::performance_pageload::fcp.AccumulateRawDuration(
firstContentfulComposite - navigationStart);
if (!http3Key.IsEmpty()) {
glean::perf::http3_first_contentful_paint.Get(http3Key)
.AccumulateRawDuration(firstContentfulComposite - navigationStart);
#ifndef ANDROID
AccumulateHttp3FcpGleanPref(http3Key,
firstContentfulComposite - navigationStart);
#endif
}
if (!http3WithPriorityKey.IsEmpty()) {
glean::perf::h3p_first_contentful_paint.Get(http3WithPriorityKey)
.AccumulateRawDuration(firstContentfulComposite - navigationStart);
#ifndef ANDROID
AccumulatePriorityFcpGleanPref(
http3WithPriorityKey, firstContentfulComposite - navigationStart);
#endif
}
glean::perf::dns_first_contentful_paint.Get(dnsKey).AccumulateRawDuration(
firstContentfulComposite - navigationStart);
glean::performance_pageload::fcp_responsestart.AccumulateRawDuration(
firstContentfulComposite - responseStart);
TimeDuration fcpTime = firstContentfulComposite - navigationStart;
if (fcpTime > zeroDuration) {
mPageloadEventData.set_fcpTime(
static_cast<uint32_t>(fcpTime.ToMilliseconds()));
}
}
// Report the most up to date LCP time. For our histogram we actually report
// this on page unload.
if (TimeStamp lcpTime =
GetNavigationTiming()->GetLargestContentfulRenderTimeStamp()) {
mPageloadEventData.set_lcpTime(
static_cast<uint32_t>((lcpTime - navigationStart).ToMilliseconds()));
}
// Load event
if (TimeStamp loadEventStart =
GetNavigationTiming()->GetLoadEventStartTimeStamp()) {
glean::performance_pageload::load_time.AccumulateRawDuration(
loadEventStart - navigationStart);
if (!http3Key.IsEmpty()) {
glean::perf::http3_page_load_time.Get(http3Key).AccumulateRawDuration(
loadEventStart - navigationStart);
}
if (!http3WithPriorityKey.IsEmpty()) {
glean::perf::h3p_page_load_time.Get(http3WithPriorityKey)
.AccumulateRawDuration(loadEventStart - navigationStart);
}
glean::performance_pageload::load_time_responsestart.AccumulateRawDuration(
loadEventStart - responseStart);
TimeDuration responseTime = responseStart - navigationStart;
if (responseTime > zeroDuration) {
mPageloadEventData.set_responseTime(
static_cast<uint32_t>(responseTime.ToMilliseconds()));
}
TimeDuration loadTime = loadEventStart - navigationStart;
if (loadTime > zeroDuration) {
mPageloadEventData.set_loadTime(
static_cast<uint32_t>(loadTime.ToMilliseconds()));
}
TimeStamp requestStart;
timedChannel->GetRequestStart(&requestStart);
if (requestStart) {
TimeDuration timeToRequestStart = requestStart - navigationStart;
if (timeToRequestStart > zeroDuration) {
mPageloadEventData.set_timeToRequestStart(
static_cast<uint32_t>(timeToRequestStart.ToMilliseconds()));
} else {
// Speculative and pre-established connections may yield zero or
// slightly negative timeToRequestStart timings. We record these as zero
// to maintain consistent, non-negative timing data, while still
// capturing the impact of early connection establishment.
mPageloadEventData.set_timeToRequestStart(0);
}
}
TimeStamp secureConnectStart;
TimeStamp connectEnd;
timedChannel->GetSecureConnectionStart(&secureConnectStart);
timedChannel->GetConnectEnd(&connectEnd);
if (secureConnectStart && connectEnd) {
TimeDuration tlsHandshakeTime = connectEnd - secureConnectStart;
if (tlsHandshakeTime > zeroDuration) {
mPageloadEventData.set_tlsHandshakeTime(
static_cast<uint32_t>(tlsHandshakeTime.ToMilliseconds()));
}
}
}
}
Document::~Document() {
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
MOZ_ASSERT(!IsTopLevelContentDocument() || !IsResourceDoc(),
"Can't be top-level and a resource doc at the same time");
NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
if (IsTopLevelContentDocument()) {
RemoveToplevelLoadingDocument(this);
}
mInDestructor = true;
mInUnlinkOrDeletion = true;
mozilla::DropJSObjects(this);
// Clear mObservers to keep it in sync with the mutationobserver list
mObservers.Clear();
mIntersectionObservers.Clear();
if (mStyleSheetSetList) {
mStyleSheetSetList->Disconnect();
}
if (mAnimationController) {
mAnimationController->Disconnect();
}
MOZ_ASSERT(mTimelines.isEmpty());
mParentDocument = nullptr;
// Kill the subdocument map, doing this will release its strong
// references, if any.
mSubDocuments = nullptr;
nsAutoScriptBlocker scriptBlocker;
WillRemoveRoot();
// Invalidate cached array of child nodes
InvalidateChildNodes();
// We should not have child nodes when destructor is called,
// since child nodes keep their owner document alive.
MOZ_ASSERT(!HasChildren());
mCachedRootElement = nullptr;
for (auto& sheets : mAdditionalSheets) {
UnlinkStyleSheets(sheets);
}
if (mAttributeStyles) {
mAttributeStyles->SetOwningDocument(nullptr);
}
if (mListenerManager) {
mListenerManager->Disconnect();
UnsetFlags(NODE_HAS_LISTENERMANAGER);
}
if (mScriptLoader) {
mScriptLoader->DropDocumentReference();
}
if (mCSSLoader) {
// Could be null here if Init() failed or if we have been unlinked.
mCSSLoader->DropDocumentReference();
}
if (mStyleImageLoader) {
mStyleImageLoader->DropDocumentReference();
}
if (mXULBroadcastManager) {
mXULBroadcastManager->DropDocumentReference();
}
if (mXULPersist) {
mXULPersist->DropDocumentReference();
}
if (mPermissionDelegateHandler) {
mPermissionDelegateHandler->DropDocumentReference();
}
SetLockingImages(false);
SetImageAnimationState(false);
mHeaderData = nullptr;
mPendingTitleChangeEvent.Revoke();
MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
"must not have media query lists left");
if (mNodeInfoManager) {
mNodeInfoManager->DropDocumentReference();
}
if (mDocGroup) {
MOZ_ASSERT(mDocGroup->GetBrowsingContextGroup());
mDocGroup->GetBrowsingContextGroup()->RemoveDocument(this, mDocGroup);
}
UnlinkOriginalDocumentIfStatic();
UnregisterFromMemoryReportingForDataDocument();
if (isInList()) {
MOZ_ASSERT(AllDocumentsList().contains(this));
remove();
}
}
void Document::DropStyleSet() { mStyleSet = nullptr; }
NS_INTERFACE_TABLE_HEAD(Document)
NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
NS_INTERFACE_TABLE_BEGIN
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(Document, nsISupports, nsINode)
NS_INTERFACE_TABLE_ENTRY(Document, nsINode)
NS_INTERFACE_TABLE_ENTRY(Document, Document)
NS_INTERFACE_TABLE_ENTRY(Document, nsIScriptObjectPrincipal)
NS_INTERFACE_TABLE_ENTRY(Document, EventTarget)
NS_INTERFACE_TABLE_ENTRY(Document, nsISupportsWeakReference)
NS_INTERFACE_TABLE_END
NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Document)
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Document, LastRelease())
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Document)
if (Element::CanSkip(tmp, aRemovingAllowed)) {
EventListenerManager* elm = tmp->GetExistingListenerManager();
if (elm) {
elm->MarkForCC();
}
return true;
}
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Document)
return Element::CanSkipInCC(tmp);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Document)
return Element::CanSkipThis(tmp);
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
char name[512];
nsAutoCString loadedAsData;
if (tmp->IsLoadedAsData()) {
loadedAsData.AssignLiteral("data");
} else {
loadedAsData.AssignLiteral("normal");
}
uint32_t nsid = tmp->GetDefaultNamespaceID();
nsAutoCString uri;
if (tmp->mDocumentURI) uri = tmp->mDocumentURI->GetSpecOrDefault();
static const char* kNSURIs[] = {"([none])", "(xmlns)", "(xml)",
"(xhtml)", "(XLink)", "(XSLT)",
"(MathML)", "(RDF)", "(XUL)"};
if (nsid < std::size(kNSURIs)) {
SprintfLiteral(name, "Document %s %s %s", loadedAsData.get(),
kNSURIs[nsid], uri.get());
} else {
SprintfLiteral(name, "Document %s %s", loadedAsData.get(), uri.get());
}
cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
} else {
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(Document, tmp->mRefCnt.get())
}
if (!nsINode::Traverse(tmp, cb)) {
return NS_SUCCESS_INTERRUPTED_TRAVERSE;
}
tmp->mExternalResourceMap.Traverse(&cb);
// Traverse all Document pointer members.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFragmentDirective)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFullscreenEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomContentContainer)
DocumentOrShadowRoot::Traverse(tmp, cb);
if (tmp->mRadioGroupContainer) {
RadioGroupContainer::Traverse(tmp->mRadioGroupContainer.get(), cb);
}
for (auto& sheets : tmp->mAdditionalSheets) {
tmp->TraverseStyleSheets(sheets, "mAdditionalSheets[<origin>][i]", cb);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadObserver)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementsObservedForLastRememberedSize)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollTimelineAnimationTracker)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissionDelegateHandler)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveViewTransition)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mViewTransitionUpdateCallbacks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager)
// Traverse all our nsCOMArrays.
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
// Traverse animation components
if (tmp->mAnimationController) {
tmp->mAnimationController->Traverse(&cb);
}
if (tmp->mSubDocuments) {
for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<SubDocMapEntry*>(iter.Get());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSubDocuments entry->mKey");
cb.NoteXPCOMChild(entry->mKey);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mSubDocuments entry->mSubDocument");
cb.NoteXPCOMChild(ToSupports(entry->mSubDocument));
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
// We own only the items in mDOMMediaQueryLists that have listeners;
// this reference is managed by their AddListener and RemoveListener
// methods.
for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;
mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
if (mql->HasListeners() &&
NS_SUCCEEDED(mql->CheckCurrentGlobalCorrectness())) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
cb.NoteXPCOMChild(static_cast<EventTarget*>(mql));
}
}
for (const auto& entry : tmp->mL10nProtoElements) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mL10nProtoElements key");
cb.NoteXPCOMChild(entry.GetKey());
CycleCollectionNoteChild(cb, entry.GetWeak(), "mL10nProtoElements value");
}
for (size_t i = 0; i < tmp->mPendingFrameStaticClones.Length(); ++i) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFrameStaticClones[i].mElement);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
mPendingFrameStaticClones[i].mStaticCloneOf);
}
for (auto& tableEntry : tmp->mActiveLocks) {
ImplCycleCollectionTraverse(cb, *tableEntry.GetModifiableData(),
"mActiveLocks entry", 0);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_CLASS(Document)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Document)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedStateObject)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
tmp->mInUnlinkOrDeletion = true;
tmp->SetStateObject(nullptr);
// Clear out our external resources
tmp->mExternalResourceMap.Shutdown();
nsAutoScriptBlocker scriptBlocker;
tmp->RemoveCustomContentContainer();
nsINode::Unlink(tmp);
while (tmp->HasChildren()) {
// Hold a strong ref to the node when we remove it, because we may be
// the last reference to it.
// If this code changes, change the corresponding code in Document's
// unlink impl and ContentUnbinder::UnbindSubtree.
nsCOMPtr<nsIContent> child = tmp->GetLastChild();
tmp->DisconnectChild(child);
child->UnbindFromTree();
}
tmp->UnlinkOriginalDocumentIfStatic();
tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
tmp->SetScriptGlobalObject(nullptr);
for (auto& sheets : tmp->mAdditionalSheets) {
tmp->UnlinkStyleSheets(sheets);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementsObservedForLastRememberedSize);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFragmentDirective)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingFullscreenEvents)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollTimelineAnimationTracker)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContents)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPermissionDelegateHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuppressedEventListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveViewTransition)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mViewTransitionUpdateCallbacks)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
if (tmp->mDocGroup && tmp->mDocGroup->GetBrowsingContextGroup()) {
tmp->mDocGroup->GetBrowsingContextGroup()->RemoveDocument(tmp,
tmp->mDocGroup);
}
tmp->mDocGroup = nullptr;
if (tmp->IsTopLevelContentDocument()) {
RemoveToplevelLoadingDocument(tmp);
}
tmp->mParentDocument = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
if (tmp->mListenerManager) {
tmp->mListenerManager->Disconnect();
tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
tmp->mListenerManager = nullptr;
}
if (tmp->mStyleSheetSetList) {
tmp->mStyleSheetSetList->Disconnect();
tmp->mStyleSheetSetList = nullptr;
}
tmp->mSubDocuments = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager)
DocumentOrShadowRoot::Unlink(tmp);
tmp->mRadioGroupContainer = nullptr;
// Document has a pretty complex destructor, so we're going to
// assume that *most* cycles you actually want to break somewhere
// else, and not unlink an awful lot here.
tmp->mExpandoAndGeneration.OwnerUnlinked();
if (tmp->mAnimationController) {
tmp->mAnimationController->Unlink();
}
tmp->mPendingTitleChangeEvent.Revoke();
if (tmp->mCSSLoader) {
tmp->mCSSLoader->DropDocumentReference();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
}
if (tmp->mScriptLoader) {
tmp->mScriptLoader->DropDocumentReference();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
}
// We own only the items in mDOMMediaQueryLists that have listeners;
// this reference is managed by their AddListener and RemoveListener
// methods.
for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
MediaQueryList* next =
static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext();
mql->Disconnect();
mql = next;
}
tmp->mPendingFrameStaticClones.Clear();
tmp->mActiveLocks.Clear();
if (tmp->isInList()) {
MOZ_ASSERT(AllDocumentsList().contains(tmp));
tmp->remove();
}
tmp->mInUnlinkOrDeletion = false;
tmp->UnregisterFromMemoryReportingForDataDocument();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
nsresult Document::Init(nsIPrincipal* aPrincipal,
nsIPrincipal* aPartitionedPrincipal) {
if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
return NS_ERROR_ALREADY_INITIALIZED;
}
// Force initialization.
mOnloadBlocker = new OnloadBlocker();
mNodeInfoManager = new nsNodeInfoManager(this, aPrincipal);
// mNodeInfo keeps NodeInfoManager alive!
mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
"Bad NodeType in aNodeInfo");
NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
if (!mLoadedAsData) {
CreateCSSAndStyleImageLoaders(false);
}
// If after creation the owner js global is not set for a document
// we use the default compartment for this document, instead of creating
// wrapper in some random compartment when the document is exposed to js
// via some events.
nsCOMPtr<nsIGlobalObject> global =
xpc::NativeGlobal(xpc::PrivilegedJunkScope());
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
mScopeObject = do_GetWeakReference(global);
MOZ_ASSERT(mScopeObject);
if (!mLoadedAsData) {
mScriptLoader = new dom::ScriptLoader(this);
}
// we need to create a policy here so getting the policy within
// ::Policy() can *always* return a non null policy
mFeaturePolicy = new dom::FeaturePolicy(this);
mFeaturePolicy->SetDefaultOrigin(NodePrincipal());
if (aPrincipal) {
SetPrincipals(aPrincipal, aPartitionedPrincipal);
} else {
RecomputeResistFingerprinting();
}
AllDocumentsList().insertBack(this);
return NS_OK;
}
void Document::RemoveAllProperties() { PropertyTable().RemoveAllProperties(); }
void Document::RemoveAllPropertiesFor(nsINode* aNode) {
PropertyTable().RemoveAllPropertiesFor(aNode);
}
void Document::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
nsCOMPtr<nsIURI> uri;
nsCOMPtr<nsIPrincipal> principal;
nsCOMPtr<nsIPrincipal> partitionedPrincipal;
if (aChannel) {
mIsInPrivateBrowsing = NS_UsePrivateBrowsing(aChannel);
// Note: this code is duplicated in PrototypeDocumentContentSink::Init and
// nsScriptSecurityManager::GetChannelResultPrincipals.
// Note: this should match the uri used for the OnNewURI call in
// nsDocShell::CreateDocumentViewer.
NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
nsIScriptSecurityManager* securityManager =
nsContentUtils::GetSecurityManager();
if (securityManager) {
securityManager->GetChannelResultPrincipals(
aChannel, getter_AddRefs(principal),
getter_AddRefs(partitionedPrincipal));
}
}
bool equal = principal->Equals(partitionedPrincipal);
principal = MaybeDowngradePrincipal(principal);
if (equal) {
partitionedPrincipal = principal;
} else {
partitionedPrincipal = MaybeDowngradePrincipal(partitionedPrincipal);
}
ResetToURI(uri, aLoadGroup, principal, partitionedPrincipal);
// Note that, since mTiming does not change during a reset, the
// navigationStart time remains unchanged and therefore any future new
// timeline will have the same global clock time as the old one.
mDocumentTimeline = nullptr;
if (nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel)) {
if (nsCOMPtr<nsIURI> baseURI = do_GetProperty(bag, u"baseURI"_ns)) {
mDocumentBaseURI = baseURI.forget();
mChromeXHRDocBaseURI = nullptr;
}
}
mChannel = aChannel;
RecomputeResistFingerprinting();
MaybeRecomputePartitionKey();
}
void Document::DisconnectNodeTree() {
// Delete references to sub-documents and kill the subdocument map,
// if any. This is not strictly needed, but makes the node tree
// teardown a bit faster.
mSubDocuments = nullptr;
bool oldVal = mInUnlinkOrDeletion;
mInUnlinkOrDeletion = true;
{ // Scope for update
MOZ_AUTO_DOC_UPDATE(this, true);
WillRemoveRoot();
// Invalidate cached array of child nodes
InvalidateChildNodes();
while (nsCOMPtr<nsIContent> content = GetLastChild()) {
nsMutationGuard::DidMutate();
MutationObservers::NotifyContentWillBeRemoved(this, content, {});
DisconnectChild(content);
if (content == mCachedRootElement) {
// Immediately clear mCachedRootElement, now that it's been removed
// from mChildren, so that GetRootElement() will stop returning this
// now-stale value.
mCachedRootElement = nullptr;
}
content->UnbindFromTree();
}
MOZ_ASSERT(!mCachedRootElement,
"After removing all children, there should be no root elem");
}
mInUnlinkOrDeletion = oldVal;
}
void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
nsIPrincipal* aPrincipal,
nsIPrincipal* aPartitionedPrincipal) {
MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
MOZ_ASSERT(!!aPrincipal == !!aPartitionedPrincipal);
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
mSecurityInfo = nullptr;
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
if (!aLoadGroup || group != aLoadGroup) {
mDocumentLoadGroup = nullptr;
}
DisconnectNodeTree();
// Reset our stylesheets
ResetStylesheetsToURI(aURI);
// Release the listener manager
if (mListenerManager) {
mListenerManager->Disconnect();
mListenerManager = nullptr;
}
// Release the stylesheets list.
mDOMStyleSheets = nullptr;
// Release our principal after tearing down the document, rather than before.
// This ensures that, during teardown, the document and the dying window
// (which already nulled out its document pointer and cached the principal)
// have matching principals.
SetPrincipals(nullptr, nullptr);
// Clear the original URI so SetDocumentURI sets it.
mOriginalURI = nullptr;
SetDocumentURI(aURI);
mChromeXHRDocURI = nullptr;
// If mDocumentBaseURI is null, Document::GetBaseURI() returns
// mDocumentURI.
mDocumentBaseURI = nullptr;
mChromeXHRDocBaseURI = nullptr;
if (aLoadGroup) {
nsCOMPtr<nsIInterfaceRequestor> callbacks;
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
if (callbacks) {
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
if (loadContext) {
// This is asserting that if we previously set mIsInPrivateBrowsing
// to true from the channel in Document::Reset, that the loadContext
// also believes it to be true.
MOZ_ASSERT(!mIsInPrivateBrowsing ||
mIsInPrivateBrowsing == loadContext->UsePrivateBrowsing());
mIsInPrivateBrowsing = loadContext->UsePrivateBrowsing();
}
}
mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
// there was an assertion here that aLoadGroup was not null. This
// is no longer valid: nsDocShell::SetDocument does not create a
// load group, and it works just fine
// XXXbz what does "just fine" mean exactly? And given that there
// is no nsDocShell::SetDocument, what is this talking about?
if (IsContentDocument()) {
// Inform the associated request context about this load start so
// any of its internal load progress flags gets reset.
nsCOMPtr<nsIRequestContextService> rcsvc =
net::RequestContextService::GetOrCreate();
if (rcsvc) {
nsCOMPtr<nsIRequestContext> rc;
rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
if (rc) {
rc->BeginLoad();
}
}
}
}
mLastModified.Truncate();
// XXXbz I guess we're assuming that the caller will either pass in
// a channel with a useful type or call SetContentType?
SetContentType(""_ns);
mContentLanguage = nullptr;
mBaseTarget.Truncate();
mXMLDeclarationBits = 0;
// Now get our new principal
if (aPrincipal) {
SetPrincipals(aPrincipal, aPartitionedPrincipal);
} else {
nsIScriptSecurityManager* securityManager =
nsContentUtils::GetSecurityManager();
if (securityManager) {
nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
if (!loadContext && aLoadGroup) {
nsCOMPtr<nsIInterfaceRequestor> cbs;
aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
loadContext = do_GetInterface(cbs);
}
MOZ_ASSERT(loadContext,
"must have a load context or pass in an explicit principal");
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = securityManager->GetLoadContextContentPrincipal(
mDocumentURI, loadContext, getter_AddRefs(principal));
if (NS_SUCCEEDED(rv)) {
SetPrincipals(principal, principal);
}
}
}
if (mFontFaceSet) {
mFontFaceSet->RefreshStandardFontLoadPrincipal();
}
// Refresh the principal on the realm.
if (nsPIDOMWindowInner* win = GetInnerWindow()) {
nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal();
}
}
already_AddRefed<nsIPrincipal> Document::MaybeDowngradePrincipal(
nsIPrincipal* aPrincipal) {
if (!aPrincipal) {
return nullptr;
}
// We can't load a document with an expanded principal. If we're given one,
// automatically downgrade it to the last principal it subsumes (which is the
// extension principal, in the case of extension content scripts).
auto* basePrin = BasePrincipal::Cast(aPrincipal);
if (basePrin->Is<ExpandedPrincipal>()) {
MOZ_DIAGNOSTIC_CRASH(
"Should never try to create a document with "
"an expanded principal");
auto* expanded = basePrin->As<ExpandedPrincipal>();
return do_AddRef(expanded->AllowList().LastElement());
}
if (aPrincipal->IsSystemPrincipal() && mDocumentContainer) {
// We basically want the parent document here, but because this is very
// early in the load, GetInProcessParentDocument() returns null, so we use
// the docshell hierarchy to get this information instead.
if (RefPtr<BrowsingContext> parent =
mDocumentContainer->GetBrowsingContext()->GetParent()) {
auto* parentWin = nsGlobalWindowOuter::Cast(parent->GetDOMWindow());
if (!parentWin || !parentWin->GetPrincipal()->IsSystemPrincipal()) {
nsCOMPtr<nsIPrincipal> nullPrincipal =
NullPrincipal::CreateWithoutOriginAttributes();
return nullPrincipal.forget();
}
}
}
nsCOMPtr<nsIPrincipal> principal(aPrincipal);
return principal.forget();
}
size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet& aSheet) {
ServoStyleSet& styleSet = EnsureStyleSet();
// lowest index first
const size_t newDocIndex = StyleOrderIndexOfSheet(aSheet);
MOZ_ASSERT(newDocIndex != mStyleSheets.NoIndex);
size_t index = styleSet.SheetCount(StyleOrigin::Author);
while (index--) {
auto* sheet = styleSet.SheetAt(StyleOrigin::Author, index);
MOZ_ASSERT(sheet);
if (!sheet->GetAssociatedDocumentOrShadowRoot()) {
// If the sheet is not owned by the document it should be an author sheet
// registered at nsStyleSheetService, or an additional sheet. In that case
// the doc sheet should end up before it.
// FIXME(emilio): Additional stylesheets inconsistently end up with
// associated document, depending on which code-path adds them. Fix this.
MOZ_ASSERT(
nsStyleSheetService::GetInstance()->AuthorStyleSheets()->Contains(
sheet) ||
mAdditionalSheets[eAuthorSheet].Contains(sheet));
continue;
}
size_t sheetDocIndex = StyleOrderIndexOfSheet(*sheet);
if (MOZ_UNLIKELY(sheetDocIndex == mStyleSheets.NoIndex)) {
MOZ_ASSERT_UNREACHABLE("Which stylesheet can hit this?");
continue;
}
MOZ_ASSERT(sheetDocIndex != newDocIndex);
if (sheetDocIndex < newDocIndex) {
// We found a document-owned sheet. All of them go together, so if the
// current sheet goes before ours, we're at the right index already.
return index + 1;
}
// Otherwise keep looking. Unfortunately we can't do something clever like:
//
// return index - sheetDocIndex + newDocIndex;
//
// Or so, because we need to deal with disabled / non-applicable sheets
// which are not in the styleset, even though they're in the document.
}
// We found no sheet that goes before us, so we're index 0.
return 0;
}
void Document::ResetStylesheetsToURI(nsIURI* aURI) {
MOZ_ASSERT(aURI);
ClearAdoptedStyleSheets();
ServoStyleSet& styleSet = EnsureStyleSet();
auto ClearSheetList = [&](nsTArray<RefPtr<StyleSheet>>& aSheetList) {
for (auto& sheet : Reversed(aSheetList)) {
sheet->ClearAssociatedDocumentOrShadowRoot();
if (mStyleSetFilled) {
styleSet.RemoveStyleSheet(*sheet);
}
}
aSheetList.Clear();
};
ClearSheetList(mStyleSheets);
for (auto& sheets : mAdditionalSheets) {
ClearSheetList(sheets);
}
if (mStyleSetFilled) {
if (auto* ss = nsStyleSheetService::GetInstance()) {
for (auto& sheet : Reversed(*ss->AuthorStyleSheets())) {
MOZ_ASSERT(!sheet->GetAssociatedDocumentOrShadowRoot());
if (sheet->IsApplicable()) {
styleSet.RemoveStyleSheet(*sheet);
}
}
}
}
// Now reset our inline style and attribute sheets.
if (mAttributeStyles) {
mAttributeStyles->Reset();
mAttributeStyles->SetOwningDocument(this);
} else {
mAttributeStyles = new AttributeStyles(this);
}
if (mStyleSetFilled) {
FillStyleSetDocumentSheets();
if (styleSet.StyleSheetsHaveChanged()) {
ApplicableStylesChanged();
}
}
}
void Document::FillStyleSetUserAndUASheets() {
// Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
// ordering.
// The document will fill in the document sheets when we create the presshell
auto* cache = GlobalStyleSheetCache::Singleton();
nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
MOZ_ASSERT(sheetService,
"should never be creating a StyleSet after the style sheet "
"service has gone");
ServoStyleSet& styleSet = EnsureStyleSet();
for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
styleSet.AppendStyleSheet(*sheet);
}
StyleSheet* sheet = IsInChromeDocShell() ? cache->GetUserChromeSheet()
: cache->GetUserContentSheet();
if (sheet) {
styleSet.AppendStyleSheet(*sheet);
}
styleSet.AppendStyleSheet(*cache->UASheet());
if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
styleSet.AppendStyleSheet(*cache->MathMLSheet());
}
if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
styleSet.AppendStyleSheet(*cache->SVGSheet());
}
styleSet.AppendStyleSheet(*cache->HTMLSheet());
if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
styleSet.AppendStyleSheet(*cache->NoFramesSheet());
}
styleSet.AppendStyleSheet(*cache->CounterStylesSheet());
// Only load the full XUL sheet if we'll need it.
if (LoadsFullXULStyleSheetUpFront()) {
styleSet.AppendStyleSheet(*cache->XULSheet());
}
styleSet.AppendStyleSheet(*cache->FormsSheet());
styleSet.AppendStyleSheet(*cache->ScrollbarsSheet());
for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
styleSet.AppendStyleSheet(*sheet);
}
MOZ_ASSERT(!mQuirkSheetAdded);
if (NeedsQuirksSheet()) {
styleSet.AppendStyleSheet(*cache->QuirkSheet());
mQuirkSheetAdded = true;
}
}
void Document::FillStyleSet() {
MOZ_ASSERT(!mStyleSetFilled);
FillStyleSetUserAndUASheets();
FillStyleSetDocumentSheets();
mStyleSetFilled = true;
}
void Document::FillStyleSetDocumentSheets() {
ServoStyleSet& styleSet = EnsureStyleSet();
MOZ_ASSERT(styleSet.SheetCount(StyleOrigin::Author) == 0,
"Style set already has document sheets?");
// Sheets are added in reverse order to avoid worst-case time complexity when
// looking up the index of a sheet.
//
// Note that usually appending is faster (rebuilds less stuff in the
// styleset), but in this case it doesn't matter since we're filling the
// styleset from scratch anyway.
for (StyleSheet* sheet : Reversed(mStyleSheets)) {
if (sheet->IsApplicable()) {
styleSet.AddDocStyleSheet(*sheet);
}
}
EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
if (aSheet.IsApplicable()) {
styleSet.AddDocStyleSheet(aSheet);
}
});
nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
styleSet.AppendStyleSheet(*sheet);
}
for (auto& sheets : mAdditionalSheets) {
for (StyleSheet* sheet : sheets) {
styleSet.AppendStyleSheet(*sheet);
}
}
}
void Document::CompatibilityModeChanged() {
MOZ_ASSERT(IsHTMLOrXHTML());
if (mCSSLoader) {
mCSSLoader->SetCompatibilityMode(mCompatMode);
}
if (mStyleSet) {
mStyleSet->CompatibilityModeChanged();
}
if (!mStyleSetFilled) {
MOZ_ASSERT(!mQuirkSheetAdded);
return;
}
MOZ_ASSERT(mStyleSet);
if (PresShell* presShell = GetPresShell()) {
// Selectors may have become case-sensitive / case-insensitive, the stylist
// has already performed the relevant invalidation.
presShell->EnsureStyleFlush();
}
if (mQuirkSheetAdded == NeedsQuirksSheet()) {
return;
}
auto* cache = GlobalStyleSheetCache::Singleton();
StyleSheet* sheet = cache->QuirkSheet();
if (mQuirkSheetAdded) {
mStyleSet->RemoveStyleSheet(*sheet);
} else {
mStyleSet->AppendStyleSheet(*sheet);
}
mQuirkSheetAdded = !mQuirkSheetAdded;
ApplicableStylesChanged();
}
void Document::SetCompatibilityMode(nsCompatibility aMode) {
NS_ASSERTION(IsHTMLDocument() || aMode == eCompatibility_FullStandards,
"Bad compat mode for XHTML document!");
if (mCompatMode == aMode) {
return;
}
mCompatMode = aMode;
CompatibilityModeChanged();
// Trigger recomputation of the nsViewportInfo the next time it's queried.
mViewportType = Unknown;
}
static void WarnIfSandboxIneffective(nsIDocShell* aDocShell,
uint32_t aSandboxFlags,
nsIChannel* aChannel) {
// If the document permits allow-top-navigation and
// allow-top-navigation-by-user-activation this will permit all top
// navigation.
if (aSandboxFlags != SANDBOXED_NONE &&
!(aSandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) &&
!(aSandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION)) {
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, "Iframe Sandbox"_ns,
aDocShell->GetDocument(), nsContentUtils::eSECURITY_PROPERTIES,
"BothAllowTopNavigationAndUserActivationPresent");
}
// If the document is sandboxed (via the HTML5 iframe sandbox
// attribute) and both the allow-scripts and allow-same-origin
// keywords are supplied, the sandboxed document can call into its
// parent document and remove its sandboxing entirely - we print a
// warning to the web console in this case.
if (aSandboxFlags & SANDBOXED_NAVIGATION &&
!(aSandboxFlags & SANDBOXED_SCRIPTS) &&
!(aSandboxFlags & SANDBOXED_ORIGIN)) {
RefPtr<BrowsingContext> bc = aDocShell->GetBrowsingContext();
MOZ_ASSERT(bc->IsInProcess());
RefPtr<BrowsingContext> parentBC = bc->GetParent();
if (!parentBC || !parentBC->IsInProcess()) {
// If parent document is not in process, then by construction it
// cannot be same origin.
return;
}
// Don't warn if our parent is not the top-level document.
if (!parentBC->IsTopContent()) {
return;
}
nsCOMPtr<nsIDocShell> parentDocShell = parentBC->GetDocShell();
MOZ_ASSERT(parentDocShell);
nsCOMPtr<nsIChannel> parentChannel;
parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
if (!parentChannel) {
return;
}
nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<Document> parentDocument = parentDocShell->GetDocument();
nsCOMPtr<nsIURI> iframeUri;
parentChannel->GetURI(getter_AddRefs(iframeUri));
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, "Iframe Sandbox"_ns, parentDocument,
nsContentUtils::eSECURITY_PROPERTIES,
"BothAllowScriptsAndSameOriginPresent", nsTArray<nsString>(),
SourceLocation(iframeUri.get()));
}
}
bool Document::IsSynthesized() {
nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->LoadInfo() : nullptr;
return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized();
}
// static
bool Document::IsCallerChromeOrAddon(JSContext* aCx, JSObject* aObject) {
nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
return principal && (principal->IsSystemPrincipal() ||
principal->GetIsAddonOrExpandedAddonPrincipal());
}
static void CheckIsBadPolicy(nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
BrowsingContext* aContext, nsIChannel* aChannel) {
#if defined(EARLY_BETA_OR_EARLIER)
auto requireCORP =
nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
if (aContext->GetOpenerPolicy() == aPolicy ||
(aContext->GetOpenerPolicy() != requireCORP && aPolicy != requireCORP)) {
return;
}
nsCOMPtr<nsIURI> uri;
bool hasURI = NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(uri)));
bool isViewSource = hasURI && uri->SchemeIs("view-source");
nsCString contentType;
nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
bool isPDFJS = bag &&
NS_SUCCEEDED(bag->GetPropertyAsACString(u"contentType"_ns,
contentType)) &&
contentType.EqualsLiteral(APPLICATION_PDF);
MOZ_DIAGNOSTIC_ASSERT(!isViewSource,
MOZ_DIAGNOSTIC_ASSERT(aPolicy == requireCORP,
"Assert due to clearing REQUIRE_CORP.");
MOZ_DIAGNOSTIC_ASSERT(aContext->GetOpenerPolicy() == requireCORP,
"Assert due to setting REQUIRE_CORP.");
#endif // defined(EARLY_BETA_OR_EARLIER)
}
nsresult Document::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
nsILoadGroup* aLoadGroup,
nsISupports* aContainer,
nsIStreamListener** aDocListener,
bool aReset) {
if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
nsCOMPtr<nsIURI> uri;
aChannel->GetURI(getter_AddRefs(uri));
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
("DOCUMENT %p StartDocumentLoad %s", this,
uri ? uri->GetSpecOrDefault().get() : ""));
}
MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
"Bad readyState");
SetReadyStateInternal(READYSTATE_LOADING);
if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
MOZ_RELEASE_ASSERT(mLoadedAsData);
SetLoadedAsData(true, /* aConsiderForMemoryReporting */ true);
// We need to disable script & style loading in this case.
// We leave them disabled even in EndLoad(), and let anyone
// who puts the document on display to worry about enabling.
} else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
// Allow CSS, but not scripts
// TODO: Enforce this via the constructor and make mScriptLoader null here.
MOZ_ASSERT(mScriptLoader);
mScriptLoader->SetEnabled(false);
}
mMayStartLayout = false;
MOZ_ASSERT(!mReadyForIdle,
"We should never hit DOMContentLoaded before this point");
if (aReset) {
Reset(aChannel, aLoadGroup);
}
nsAutoCString contentType;
nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(u"contentType"_ns,
contentType))) ||
NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
// XXX this is only necessary for viewsource:
nsACString::const_iterator start, end, semicolon;
contentType.BeginReading(start);
contentType.EndReading(end);
semicolon = start;
FindCharInReadable(';', semicolon, end);
SetContentType(Substring(start, semicolon));
}
RetrieveRelevantHeaders(aChannel);
mChannel = aChannel;
RecomputeResistFingerprinting();
nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
if (inStrmChan) {
bool isSrcdocChannel;
inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
if (isSrcdocChannel) {
mIsSrcdocDocument = true;
}
}
if (mChannel) {
nsLoadFlags loadFlags;
mChannel->GetLoadFlags(&loadFlags);
bool isDocument = false;
mChannel->GetIsDocument(&isDocument);
if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument &&
IsSynthesized() && XRE_IsContentProcess()) {
ContentChild::UpdateCookieStatus(mChannel);
}
// Store the security info for future use.
mChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
}
// If this document is being loaded by a docshell, copy its sandbox flags
// to the document, and store the fullscreen enabled flag. These are
// immutable after being set here.
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
// If this is an error page, don't inherit sandbox flags
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
if (docShell && !loadInfo->GetLoadErrorPage()) {
mSandboxFlags = loadInfo->GetSandboxFlags();
WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
}
nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
do_QueryInterface(aChannel);
if (classifiedChannel) {
mClassificationFlags = {
classifiedChannel->GetFirstPartyClassificationFlags(),
classifiedChannel->GetThirdPartyClassificationFlags()};
}
// Set the opener policy for the top level content document.
nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mChannel);
nsILoadInfo::CrossOriginOpenerPolicy policy =
nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
if (IsTopLevelContentDocument() && httpChan &&
NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy)) && docShell &&
docShell->GetBrowsingContext()) {
CheckIsBadPolicy(policy, docShell->GetBrowsingContext(), aChannel);
// Setting the opener policy on a discarded context has no effect.
(void)docShell->GetBrowsingContext()->SetOpenerPolicy(policy);
}
// The CSP directives upgrade-insecure-requests as well as
// block-all-mixed-content not only apply to the toplevel document,
// but also to nested documents. The loadInfo of a subdocument
// load already holds the correct flag, so let's just set it here
// on the document. Please note that we set the appropriate preload
// bits just for the sake of completeness here, because the preloader
// does not reach into subdocuments.
mUpgradeInsecureRequests = loadInfo->GetUpgradeInsecureRequests();
mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
mBlockAllMixedContent = loadInfo->GetBlockAllMixedContent();
mBlockAllMixedContentPreloads = mBlockAllMixedContent;
// HTTPS-Only Mode flags
// The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all
// sub-resources and sub-documents.
mHttpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
nsresult rv = InitReferrerInfo(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
rv = InitCOEP(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
// HACK: Calling EnsureIPCPoliciesRead() here will parse the CSP using the
// context's current mSelfURI (which is still the previous mSelfURI),
// bypassing some internal bugs with 'self' and iframe inheritance.
// Not calling it here results in the mSelfURI being the current mSelfURI and
// not the previous which breaks said inheritance.
nsCOMPtr<nsIPolicyContainer> policyContainer =
loadInfo->GetPolicyContainerToInherit();
nsCOMPtr<nsIContentSecurityPolicy> cspToInherit =
PolicyContainer::GetCSP(policyContainer);
if (cspToInherit) {
cspToInherit->EnsureIPCPoliciesRead();
}
rv = InitPolicyContainer(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
rv = InitCSP(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
rv = InitIntegrityPolicy(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
rv = InitDocPolicy(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
// Initialize FeaturePolicy
rv = InitFeaturePolicy(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
rv = loadInfo->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings));
NS_ENSURE_SUCCESS(rv, rv);
MaybeRecomputePartitionKey();
// Generally XFO and CSP frame-ancestors is handled within
// DocumentLoadListener. However, the DocumentLoadListener can not handle
nsContentPolicyType internalContentType =
loadInfo->InternalContentPolicyType();
if (internalContentType == nsIContentPolicy::TYPE_INTERNAL_OBJECT ||
internalContentType == nsIContentPolicy::TYPE_INTERNAL_EMBED) {
nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(aChannel);
nsresult status;
aChannel->GetStatus(&status);
if (status == NS_ERROR_XFO_VIOLATION) {
// stop! ERROR page!
// But before we have to reset the principal of the document
// because the onload() event fires before the error page
// is displayed and we do not want the enclosing document
// to access the contentDocument.
RefPtr<NullPrincipal> nullPrincipal =
NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
// Before calling SetPrincipals() we should ensure that mFontFaceSet
// and also GetInnerWindow() is still null at this point, before
// within Document.cpp
MOZ_ASSERT(!mFontFaceSet && !GetInnerWindow());
SetPrincipals(nullPrincipal, nullPrincipal);
}
}
return NS_OK;
}
void Document::SetLoadedAsData(bool aLoadedAsData,
bool aConsiderForMemoryReporting) {
MOZ_RELEASE_ASSERT(aLoadedAsData == mLoadedAsData);
if (aConsiderForMemoryReporting) {
nsIGlobalObject* global = GetScopeObject();
if (global) {
if (nsPIDOMWindowInner* window = global->GetAsInnerWindow()) {
nsGlobalWindowInner::Cast(window)
->RegisterDataDocumentForMemoryReporting(this);
}
}
}
}
nsIContentSecurityPolicy* Document::GetPreloadCsp() const {
return mPreloadCSP;
}
void Document::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCSP) {
mPreloadCSP = aPreloadCSP;
}
void Document::GetCspJSON(nsString& aJSON) {
aJSON.Truncate();
nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
if (!csp) {
dom::CSPPolicies jsonPolicies;
jsonPolicies.ToJSON(aJSON);
return;
}
csp->ToJSON(aJSON);
}
void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
for (uint32_t i = 0; i < aMessages.Length(); ++i) {
nsAutoString messageTag;
aMessages[i]->GetTag(messageTag);
nsAutoString category;
aMessages[i]->GetCategory(category);
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_ConvertUTF16toUTF8(category), this,
nsContentUtils::eSECURITY_PROPERTIES,
NS_ConvertUTF16toUTF8(messageTag).get());
}
}
void Document::ApplySettingsFromCSP(bool aSpeculative) {
nsresult rv = NS_OK;
if (!aSpeculative) {
nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
// 1) apply settings from regular CSP
if (csp) {
// Set up 'block-all-mixed-content' if not already inherited
// from the parent context or set by any other CSP.
if (!mBlockAllMixedContent) {
bool block = false;
rv = csp->GetBlockAllMixedContent(&block);
NS_ENSURE_SUCCESS_VOID(rv);
mBlockAllMixedContent = block;
}
if (!mBlockAllMixedContentPreloads) {
mBlockAllMixedContentPreloads = mBlockAllMixedContent;
}
// Set up 'upgrade-insecure-requests' if not already inherited
// from the parent context or set by any other CSP.
if (!mUpgradeInsecureRequests) {
bool upgrade = false;
rv = csp->GetUpgradeInsecureRequests(&upgrade);
NS_ENSURE_SUCCESS_VOID(rv);
mUpgradeInsecureRequests = upgrade;
}
if (!mUpgradeInsecurePreloads) {
mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
}
// Update csp settings in the parent process
if (auto* wgc = GetWindowGlobalChild()) {
wgc->SendUpdateDocumentCspSettings(mBlockAllMixedContent,
mUpgradeInsecureRequests);
}
}
return;
}
// 2) apply settings from speculative csp
if (mPreloadCSP) {
if (!mBlockAllMixedContentPreloads) {
bool block = false;
rv = mPreloadCSP->GetBlockAllMixedContent(&block);
NS_ENSURE_SUCCESS_VOID(rv);
mBlockAllMixedContent = block;
}
if (!mUpgradeInsecurePreloads) {
bool upgrade = false;
rv = mPreloadCSP->GetUpgradeInsecureRequests(&upgrade);
NS_ENSURE_SUCCESS_VOID(rv);
mUpgradeInsecurePreloads = upgrade;
}
}
}
nsresult Document::InitPolicyContainer(nsIChannel* aChannel) {
bool shouldInherit = CSP_ShouldResponseInheritCSP(aChannel);
if (shouldInherit) {
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
nsCOMPtr<nsIPolicyContainer> policyContainer =