/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=78: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Base class for all our document implementations. */ #include "nsDocument.h" #include "mozilla/ArrayUtils.h" #include "mozilla/AutoRestore.h" #include "mozilla/BinarySearch.h" #include "mozilla/DebugOnly.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Likely.h" #include #include "prlog.h" #include "plstr.h" #include "prprf.h" #include "mozilla/Telemetry.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsILoadContext.h" #include "nsUnicharUtils.h" #include "nsContentList.h" #include "nsIObserver.h" #include "nsIBaseWindow.h" #include "mozilla/css/Loader.h" #include "mozilla/css/ImageLoader.h" #include "nsDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsCOMArray.h" #include "nsDOMClassInfo.h" #include "mozilla/Services.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/BasicEvents.h" #include "mozilla/EventListenerManager.h" #include "mozilla/EventStateManager.h" #include "nsIDOMNodeFilter.h" #include "nsIDOMStyleSheet.h" #include "mozilla/dom/Attr.h" #include "nsIDOMDOMImplementation.h" #include "nsIDOMDocumentXBL.h" #include "mozilla/dom/Element.h" #include "nsGenericHTMLElement.h" #include "mozilla/dom/CDATASection.h" #include "mozilla/dom/ProcessingInstruction.h" #include "nsDOMString.h" #include "nsNodeUtils.h" #include "nsLayoutUtils.h" // for GetFrameForPoint #include "nsIFrame.h" #include "nsITabChild.h" #include "nsRange.h" #include "nsIDOMText.h" #include "nsIDOMComment.h" #include "mozilla/dom/DocumentType.h" #include "mozilla/dom/NodeIterator.h" #include "mozilla/dom/TreeWalker.h" #include "nsIServiceManager.h" #include "nsIServiceWorkerManager.h" #include "nsContentCID.h" #include "nsError.h" #include "nsPresShell.h" #include "nsPresContext.h" #include "nsIJSON.h" #include "nsThreadUtils.h" #include "nsNodeInfoManager.h" #include "nsIFileChannel.h" #include "nsIMultiPartChannel.h" #include "nsIRefreshURI.h" #include "nsIWebNavigation.h" #include "nsIScriptError.h" #include "nsStyleSheetService.h" #include "nsNetUtil.h" // for NS_MakeAbsoluteURI #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsIDOMWindow.h" #include "nsPIDOMWindow.h" #include "nsIDOMElement.h" #include "nsFocusManager.h" // for radio group stuff #include "nsIDOMHTMLInputElement.h" #include "nsIRadioVisitor.h" #include "nsIFormControl.h" #include "nsBidiUtils.h" #include "nsIParserService.h" #include "nsContentCreatorFunctions.h" #include "nsIScriptContext.h" #include "nsBindingManager.h" #include "nsIDOMHTMLDocument.h" #include "nsHTMLDocument.h" #include "nsIDOMHTMLFormElement.h" #include "nsIRequest.h" #include "nsHostObjectProtocolHandler.h" #include "nsCharsetSource.h" #include "nsIParser.h" #include "nsIContentSink.h" #include "nsDateTimeFormatCID.h" #include "nsIDateTimeFormat.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventStates.h" #include "mozilla/InternalMutationEvent.h" #include "nsDOMCID.h" #include "jsapi.h" #include "nsIXPConnect.h" #include "nsCCUncollectableMarker.h" #include "nsIContentPolicy.h" #include "nsContentPolicyUtils.h" #include "nsICategoryManager.h" #include "nsIDocumentLoaderFactory.h" #include "nsIDocumentLoader.h" #include "nsIContentViewer.h" #include "nsIXMLContentSink.h" #include "nsIXULDocument.h" #include "nsIPrompt.h" #include "nsIPropertyBag2.h" #include "mozilla/dom/PageTransitionEvent.h" #include "mozilla/dom/StyleRuleChangeEvent.h" #include "mozilla/dom/StyleSheetChangeEvent.h" #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h" #include "nsJSUtils.h" #include "nsFrameLoader.h" #include "nsEscape.h" #include "nsObjectLoadingContent.h" #include "nsHtml5TreeOpExecutor.h" #include "mozilla/dom/HTMLLinkElement.h" #include "mozilla/dom/HTMLMediaElement.h" #include "mozAutoDocUpdate.h" #include "nsGlobalWindow.h" #include "mozilla/dom/EncodingUtils.h" #include "nsDOMNavigationTiming.h" #include "nsSMILAnimationController.h" #include "imgIContainer.h" #include "nsSVGUtils.h" #include "SVGElementFactory.h" #include "nsRefreshDriver.h" // FOR CSP (autogenerated by xpidl) #include "nsIContentSecurityPolicy.h" #include "nsCSPService.h" #include "nsHTMLStyleSheet.h" #include "nsHTMLCSSStyleSheet.h" #include "SVGAttrAnimationRuleProcessor.h" #include "mozilla/dom/DOMImplementation.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/Comment.h" #include "nsTextNode.h" #include "mozilla/dom/Link.h" #include "mozilla/dom/HTMLElementBinding.h" #include "mozilla/dom/SVGElementBinding.h" #include "nsXULAppAPI.h" #include "mozilla/dom/Touch.h" #include "mozilla/dom/TouchEvent.h" #include "mozilla/Preferences.h" #include "imgILoader.h" #include "imgRequestProxy.h" #include "nsWrapperCacheInlines.h" #include "nsSandboxFlags.h" #include "nsIAppsService.h" #include "mozilla/dom/AnimationTimeline.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/DocumentFragment.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/HTMLInputElement.h" #include "mozilla/dom/NodeFilterBinding.h" #include "mozilla/dom/OwningNonNull.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/UndoManager.h" #include "mozilla/dom/WebComponentsBinding.h" #include "nsFrame.h" #include "nsDOMCaretPosition.h" #include "nsIDOMHTMLTextAreaElement.h" #include "nsViewportInfo.h" #include "nsIContentPermissionPrompt.h" #include "mozilla/StaticPtr.h" #include "nsITextControlElement.h" #include "nsIDOMNSEditableElement.h" #include "nsIEditor.h" #include "nsIDOMCSSStyleRule.h" #include "mozilla/css/Rule.h" #include "nsIDOMLocation.h" #include "nsIHttpChannelInternal.h" #include "nsISecurityConsoleMessage.h" #include "nsCharSeparatedTokenizer.h" #include "mozilla/dom/XPathEvaluator.h" #include "mozilla/dom/XPathNSResolverBinding.h" #include "mozilla/dom/XPathResult.h" #include "nsIDocumentEncoder.h" #include "nsIDocumentActivity.h" #include "nsIStructuredCloneContainer.h" #include "nsIMutableArray.h" #include "nsContentPermissionHelper.h" #include "mozilla/dom/DOMStringList.h" #include "nsWindowMemoryReporter.h" #include "nsLocation.h" #include "mozilla/dom/FontFaceSet.h" #include "mozilla/dom/BoxObject.h" #ifdef MOZ_MEDIA_NAVIGATOR #include "mozilla/MediaManager.h" #endif // MOZ_MEDIA_NAVIGATOR #ifdef MOZ_WEBRTC #include "IPeerConnection.h" #endif // MOZ_WEBRTC using namespace mozilla; using namespace mozilla::dom; typedef nsTArray LinkArray; #ifdef PR_LOGGING static PRLogModuleInfo* gDocumentLeakPRLog; static PRLogModuleInfo* gCspPRLog; #endif #define NAME_NOT_VALID ((nsSimpleContentList*)1) nsIdentifierMapEntry::~nsIdentifierMapEntry() { } void nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mIdentifierMap mNameContentList"); aCallback->NoteXPCOMChild(static_cast(mNameContentList)); if (mImageElement) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mIdentifierMap mImageElement element"); nsIContent* imageElement = mImageElement; aCallback->NoteXPCOMChild(imageElement); } } bool nsIdentifierMapEntry::IsEmpty() { return mIdContentList.Count() == 0 && !mNameContentList && !mChangeCallbacks && !mImageElement; } Element* nsIdentifierMapEntry::GetIdElement() { return static_cast(mIdContentList.SafeElementAt(0)); } Element* nsIdentifierMapEntry::GetImageIdElement() { return mImageElement ? mImageElement.get() : GetIdElement(); } void nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray* aElements) { for (int32_t i = 0; i < mIdContentList.Count(); ++i) { aElements->AppendObject(static_cast(mIdContentList[i])); } } void nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData, bool aForImage) { if (!mChangeCallbacks) { mChangeCallbacks = new nsTHashtable; if (!mChangeCallbacks) return; } ChangeCallback cc = { aCallback, aData, aForImage }; mChangeCallbacks->PutEntry(cc); } void nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback, void* aData, bool aForImage) { if (!mChangeCallbacks) return; ChangeCallback cc = { aCallback, aData, aForImage }; mChangeCallbacks->RemoveEntry(cc); if (mChangeCallbacks->Count() == 0) { mChangeCallbacks = nullptr; } } struct FireChangeArgs { Element* mFrom; Element* mTo; bool mImageOnly; bool mHaveImageOverride; }; // XXX Workaround for bug 980560 to maintain the existing broken semantics template<> struct nsIStyleRule::COMTypeInfo { static const nsIID kIID; }; const nsIID nsIStyleRule::COMTypeInfo::kIID = NS_ISTYLE_RULE_IID; namespace mozilla { namespace dom { static PLDHashOperator CustomDefinitionsTraverse(CustomElementHashKey* aKey, CustomElementDefinition* aDefinition, void* aArg) { nsCycleCollectionTraversalCallback* cb = static_cast(aArg); nsAutoPtr& callbacks = aDefinition->mCallbacks; if (callbacks->mAttributeChangedCallback.WasPassed()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCustomDefinitions->mCallbacks->mAttributeChangedCallback"); cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value()); } if (callbacks->mCreatedCallback.WasPassed()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCustomDefinitions->mCallbacks->mCreatedCallback"); cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value()); } if (callbacks->mAttachedCallback.WasPassed()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCustomDefinitions->mCallbacks->mAttachedCallback"); cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value()); } if (callbacks->mDetachedCallback.WasPassed()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCustomDefinitions->mCallbacks->mDetachedCallback"); cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value()); } return PL_DHASH_NEXT; } static PLDHashOperator CandidatesTraverse(CustomElementHashKey* aKey, nsTArray>* aData, void* aArg) { nsCycleCollectionTraversalCallback *cb = static_cast(aArg); for (size_t i = 0; i < aData->Length(); ++i) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element"); cb->NoteXPCOMChild(aData->ElementAt(i)); } return PL_DHASH_NEXT; } struct CustomDefinitionTraceArgs { const TraceCallbacks& callbacks; void* closure; }; static PLDHashOperator CustomDefinitionTrace(CustomElementHashKey *aKey, CustomElementDefinition *aData, void *aArg) { CustomDefinitionTraceArgs* traceArgs = static_cast(aArg); MOZ_ASSERT(aData, "Definition must not be null"); traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype", traceArgs->closure); return PL_DHASH_NEXT; } NS_IMPL_CYCLE_COLLECTION_CLASS(Registry) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry) CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure }; tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace, &customDefinitionArgs); NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry) tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb); tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry) tmp->mCustomDefinitions.Clear(); tmp->mCandidatesMap.Clear(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry) NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry) Registry::Registry() { mozilla::HoldJSObjects(this); } Registry::~Registry() { mozilla::DropJSObjects(this); } void CustomElementCallback::Call() { ErrorResult rv; switch (mType) { case nsIDocument::eCreated: // For the duration of this callback invocation, the element is being created // flag must be set to true. mOwnerData->mElementIsBeingCreated = true; mOwnerData->mCreatedCallbackInvoked = true; static_cast(mCallback.get())->Call(mThisObject, rv); mOwnerData->mElementIsBeingCreated = false; break; case nsIDocument::eAttached: static_cast(mCallback.get())->Call(mThisObject, rv); break; case nsIDocument::eDetached: static_cast(mCallback.get())->Call(mThisObject, rv); break; case nsIDocument::eAttributeChanged: static_cast(mCallback.get())->Call(mThisObject, mArgs.name, mArgs.oldValue, mArgs.newValue, rv); break; } } void CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject"); aCb.NoteXPCOMChild(mThisObject); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback"); aCb.NoteXPCOMChild(mCallback); } CustomElementCallback::CustomElementCallback(Element* aThisObject, nsIDocument::ElementCallbackType aCallbackType, mozilla::dom::CallbackFunction* aCallback, CustomElementData* aOwnerData) : mThisObject(aThisObject), mCallback(aCallback), mType(aCallbackType), mOwnerData(aOwnerData) { } CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype, nsIAtom* aType, nsIAtom* aLocalName, LifecycleCallbacks* aCallbacks, uint32_t aNamespaceID, uint32_t aDocOrder) : mPrototype(aPrototype), mType(aType), mLocalName(aLocalName), mCallbacks(aCallbacks), mNamespaceID(aNamespaceID), mDocOrder(aDocOrder) { } CustomElementData::CustomElementData(nsIAtom* aType) : mType(aType), mCurrentCallback(-1), mElementIsBeingCreated(false), mCreatedCallbackInvoked(true), mAssociatedMicroTask(-1) { } void CustomElementData::RunCallbackQueue() { // Note: It's possible to re-enter this method. while (static_cast(++mCurrentCallback) < mCallbackQueue.Length()) { mCallbackQueue[mCurrentCallback]->Call(); } mCallbackQueue.Clear(); mCurrentCallback = -1; } } // namespace dom } // namespace mozilla static PLDHashOperator FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg) { FireChangeArgs* args = static_cast(aArg); // 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 (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) : args->mImageOnly) return PL_DHASH_NEXT; return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData) ? PL_DHASH_NEXT : PL_DHASH_REMOVE; } void nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement, Element* aNewElement, bool aImageOnly) { if (!mChangeCallbacks) return; FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement }; mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args); } namespace { struct PositionComparator { Element* const mElement; explicit PositionComparator(Element* const aElement) : mElement(aElement) {} int operator()(void* aElement) const { Element* curElement = static_cast(aElement); if (mElement == curElement) { return 0; } if (nsContentUtils::PositionIsBefore(mElement, curElement)) { return -1; } return 1; } }; } // namespace bool nsIdentifierMapEntry::AddIdElement(Element* aElement) { NS_PRECONDITION(aElement, "Must have element"); NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0, "Why is null in our list?"); #ifdef DEBUG Element* currentElement = static_cast(mIdContentList.SafeElementAt(0)); #endif // Common case if (mIdContentList.Count() == 0) { if (!mIdContentList.AppendElement(aElement)) return false; NS_ASSERTION(currentElement == nullptr, "How did that happen?"); FireChangeCallbacks(nullptr, aElement); return true; } // We seem to have multiple content nodes for the same id, or XUL is messing // with us. Search for the right place to insert the content. size_t idx; if (BinarySearchIf(mIdContentList, 0, mIdContentList.Count(), PositionComparator(aElement), &idx)) { // Already in the list, so already in the right spot. Get out of here. // XXXbz this only happens because XUL does all sorts of random // UpdateIdTableEntry calls. Hate, hate, hate! return true; } if (!mIdContentList.InsertElementAt(aElement, idx)) return false; if (idx == 0) { Element* oldElement = static_cast(mIdContentList.SafeElementAt(1)); NS_ASSERTION(currentElement == oldElement, "How did that happen?"); FireChangeCallbacks(oldElement, aElement); } return true; } void nsIdentifierMapEntry::RemoveIdElement(Element* aElement) { NS_PRECONDITION(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()->IsHTML() || mIdContentList.IndexOf(aElement) >= 0, "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 = static_cast(mIdContentList.SafeElementAt(0)); mIdContentList.RemoveElement(aElement); if (currentElement == aElement) { FireChangeCallbacks(currentElement, static_cast(mIdContentList.SafeElementAt(0))); } } void nsIdentifierMapEntry::SetImageElement(Element* aElement) { Element* oldElement = GetImageIdElement(); mImageElement = aElement; Element* newElement = GetImageIdElement(); if (oldElement != newElement) { FireChangeCallbacks(oldElement, newElement, true); } } void nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) { if (!mNameContentList) { mNameContentList = new nsSimpleContentList(aNode); } mNameContentList->AppendElement(aElement); } void nsIdentifierMapEntry::RemoveNameElement(Element* aElement) { if (mNameContentList) { mNameContentList->RemoveElement(aElement); } } bool nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() { Element* idElement = GetIdElement(); return idElement && nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement); } size_t nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { return nsStringHashKey::SizeOfExcludingThis(aMallocSizeOf); } // Helper structs for the content->subdoc map class SubDocMapEntry : public PLDHashEntryHdr { public: // Both of these are strong references Element *mKey; // must be first, to look like PLDHashEntryStub nsIDocument *mSubDocument; }; struct FindContentData { explicit FindContentData(nsIDocument* aSubDoc) : mSubDocument(aSubDoc), mResult(nullptr) { } nsISupports *mSubDocument; Element *mResult; }; /** * A struct that holds all the information about a radio group. */ struct nsRadioGroupStruct { nsRadioGroupStruct() : mRequiredRadioCount(0) , mGroupSuffersFromValueMissing(false) {} /** * A strong pointer to the currently selected radio button. */ nsRefPtr mSelectedRadioButton; nsCOMArray mRadioButtons; uint32_t mRequiredRadioCount; bool mGroupSuffersFromValueMissing; }; nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument) { mLength = -1; // Not reference counted to avoid circular references. // The document will tell us when its going away. mDocument = aDocument; mDocument->AddObserver(this); } nsDOMStyleSheetList::~nsDOMStyleSheetList() { if (mDocument) { mDocument->RemoveObserver(this); } } NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList, nsIDocumentObserver, nsIMutationObserver) uint32_t nsDOMStyleSheetList::Length() { if (!mDocument) { return 0; } // XXX Find the number and then cache it. We'll use the // observer notification to figure out if new ones have // been added or removed. if (-1 == mLength) { mLength = mDocument->GetNumberOfStyleSheets(); #ifdef DEBUG int32_t i; for (i = 0; i < mLength; i++) { nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i); nsCOMPtr domss(do_QueryInterface(sheet)); NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet"); } #endif } return mLength; } CSSStyleSheet* nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound) { if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) { aFound = false; return nullptr; } aFound = true; nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex); NS_ASSERTION(sheet, "Must have a sheet"); return static_cast(sheet); } void nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode) { mDocument = nullptr; } void nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, bool aDocumentSheet) { if (aDocumentSheet && -1 != mLength) { nsCOMPtr domss(do_QueryInterface(aStyleSheet)); if (domss) { mLength++; } } } void nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, bool aDocumentSheet) { if (aDocumentSheet && -1 != mLength) { nsCOMPtr domss(do_QueryInterface(aStyleSheet)); if (domss) { mLength--; } } } // nsOnloadBlocker implementation NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest) NS_IMETHODIMP nsOnloadBlocker::GetName(nsACString &aResult) { aResult.AssignLiteral("about:document-onload-blocker"); return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::IsPending(bool *_retval) { *_retval = true; return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::GetStatus(nsresult *status) { *status = NS_OK; return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::Cancel(nsresult status) { return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::Suspend(void) { return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::Resume(void) { return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup) { *aLoadGroup = nullptr; return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup) { return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags) { *aLoadFlags = nsIRequest::LOAD_NORMAL; return NS_OK; } NS_IMETHODIMP nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; } // ================================================================== nsExternalResourceMap::nsExternalResourceMap() : mHaveShutDown(false) { } nsIDocument* nsExternalResourceMap::RequestResource(nsIURI* aURI, nsINode* aRequestingNode, nsDocument* 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. NS_PRECONDITION(aURI, "Must have a URI"); NS_PRECONDITION(aRequestingNode, "Must have a node"); *aPendingLoad = nullptr; if (mHaveShutDown) { return nullptr; } // First, make sure we strip the ref from aURI. nsCOMPtr clone; nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone)); if (NS_FAILED(rv) || !clone) { return nullptr; } ExternalResource* resource; mMap.Get(clone, &resource); if (resource) { return resource->mDocument; } nsRefPtr load; mPendingLoads.Get(clone, getter_AddRefs(load)); if (load) { load.forget(aPendingLoad); return nullptr; } load = new PendingLoad(aDisplayDocument); mPendingLoads.Put(clone, load); if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) { // Make sure we don't thrash things by trying this load again, since // chances are it failed for good reasons (security check, etc). AddExternalResource(clone, nullptr, nullptr, aDisplayDocument); } else { load.forget(aPendingLoad); } return nullptr; } struct nsExternalResourceEnumArgs { nsIDocument::nsSubDocEnumFunc callback; void *data; }; static PLDHashOperator ExternalResourceEnumerator(nsIURI* aKey, nsExternalResourceMap::ExternalResource* aData, void* aClosure) { nsExternalResourceEnumArgs* args = static_cast(aClosure); bool next = aData->mDocument ? args->callback(aData->mDocument, args->data) : true; return next ? PL_DHASH_NEXT : PL_DHASH_STOP; } void nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, void* aData) { nsExternalResourceEnumArgs args = { aCallback, aData }; mMap.EnumerateRead(ExternalResourceEnumerator, &args); } static PLDHashOperator ExternalResourceTraverser(nsIURI* aKey, nsExternalResourceMap::ExternalResource* aData, void* aClosure) { nsCycleCollectionTraversalCallback *cb = static_cast(aClosure); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mExternalResourceMap.mMap entry" "->mDocument"); cb->NoteXPCOMChild(aData->mDocument); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mExternalResourceMap.mMap entry" "->mViewer"); cb->NoteXPCOMChild(aData->mViewer); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mExternalResourceMap.mMap entry" "->mLoadGroup"); cb->NoteXPCOMChild(aData->mLoadGroup); return PL_DHASH_NEXT; } void nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const { // mPendingLoads will get cleared out as the requests complete, so // no need to worry about those here. mMap.EnumerateRead(ExternalResourceTraverser, aCallback); } static PLDHashOperator ExternalResourceHider(nsIURI* aKey, nsExternalResourceMap::ExternalResource* aData, void* aClosure) { if (aData->mViewer) { aData->mViewer->Hide(); } return PL_DHASH_NEXT; } void nsExternalResourceMap::HideViewers() { mMap.EnumerateRead(ExternalResourceHider, nullptr); } static PLDHashOperator ExternalResourceShower(nsIURI* aKey, nsExternalResourceMap::ExternalResource* aData, void* aClosure) { if (aData->mViewer) { aData->mViewer->Show(); } return PL_DHASH_NEXT; } void nsExternalResourceMap::ShowViewers() { mMap.EnumerateRead(ExternalResourceShower, nullptr); } void TransferZoomLevels(nsIDocument* aFromDoc, nsIDocument* aToDoc) { NS_ABORT_IF_FALSE(aFromDoc && aToDoc, "transferring zoom levels from/to null doc"); nsIPresShell* fromShell = aFromDoc->GetShell(); if (!fromShell) return; nsPresContext* fromCtxt = fromShell->GetPresContext(); if (!fromCtxt) return; nsIPresShell* toShell = aToDoc->GetShell(); if (!toShell) return; nsPresContext* toCtxt = toShell->GetPresContext(); if (!toCtxt) return; toCtxt->SetFullZoom(fromCtxt->GetFullZoom()); toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize()); toCtxt->SetTextZoom(fromCtxt->TextZoom()); } void TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc) { NS_ABORT_IF_FALSE(aFromDoc && aToDoc, "transferring showing state from/to null doc"); if (aFromDoc->IsShowing()) { aToDoc->OnPageShow(true, nullptr); } } nsresult nsExternalResourceMap::AddExternalResource(nsIURI* aURI, nsIContentViewer* aViewer, nsILoadGroup* aLoadGroup, nsIDocument* aDisplayDocument) { NS_PRECONDITION(aURI, "Unexpected call"); NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup), "Must have both or neither"); nsRefPtr load; mPendingLoads.Get(aURI, getter_AddRefs(load)); mPendingLoads.Remove(aURI); nsresult rv = NS_OK; nsCOMPtr doc; if (aViewer) { doc = aViewer->GetDocument(); NS_ASSERTION(doc, "Must have a document"); nsCOMPtr xulDoc = do_QueryInterface(doc); if (xulDoc) { // We don't handle XUL stuff here yet. rv = NS_ERROR_NOT_AVAILABLE; } else { doc->SetDisplayDocument(aDisplayDocument); // Make sure that hiding our viewer will tear down its presentation. aViewer->SetSticky(false); rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0)); if (NS_SUCCEEDED(rv)) { rv = aViewer->Open(nullptr, nullptr); } } if (NS_FAILED(rv)) { doc = nullptr; aViewer = nullptr; aLoadGroup = nullptr; } } ExternalResource* newResource = new ExternalResource(); mMap.Put(aURI, newResource); newResource->mDocument = doc; newResource->mViewer = aViewer; newResource->mLoadGroup = aLoadGroup; if (doc) { TransferZoomLevels(aDisplayDocument, doc); TransferShowingState(aDisplayDocument, doc); } const nsTArray< nsCOMPtr > & obs = load->Observers(); for (uint32_t i = 0; i < obs.Length(); ++i) { obs[i]->Observe(doc, "external-resource-document-created", nullptr); } return rv; } NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad, nsIStreamListener, nsIRequestObserver) NS_IMETHODIMP nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) { nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap(); if (map.HaveShutDown()) { return NS_BINDING_ABORTED; } nsCOMPtr viewer; nsCOMPtr 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, aContext); } nsresult nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest, nsIContentViewer** aViewer, nsILoadGroup** aLoadGroup) { NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest"); *aViewer = nullptr; *aLoadGroup = nullptr; nsCOMPtr chan(do_QueryInterface(aRequest)); NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); nsCOMPtr 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 loadGroup; chan->GetLoadGroup(getter_AddRefs(loadGroup)); // Give this document its own loadgroup nsCOMPtr newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY); newLoadGroup->SetLoadGroup(loadGroup); nsCOMPtr callbacks; loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks)); nsCOMPtr newCallbacks = new LoadgroupCallbacks(callbacks); newLoadGroup->SetNotificationCallbacks(newCallbacks); // This is some serious hackery cribbed from docshell nsCOMPtr catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID); NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE); nsXPIDLCString contractId; nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(), getter_Copies(contractId)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr docLoaderFactory = do_GetService(contractId); NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE); nsCOMPtr viewer; nsCOMPtr listener; rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup, type.get(), nullptr, nullptr, getter_AddRefs(listener), getter_AddRefs(viewer)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED); nsCOMPtr 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 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 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream, uint64_t aOffset, uint32_t aCount) { NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!"); if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) { return NS_BINDING_ABORTED; } return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount); } NS_IMETHODIMP nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus) { // mTargetListener might be null if SetupViewer or AddExternalResource failed if (mTargetListener) { nsCOMPtr listener; mTargetListener.swap(listener); return listener->OnStopRequest(aRequest, aContext, aStatus); } return NS_OK; } nsresult nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI, nsINode* aRequestingNode) { NS_PRECONDITION(aURI, "Must have a URI"); NS_PRECONDITION(aRequestingNode, "Must have a node"); // Time to start a load. First, the security checks. nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal(); nsresult rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(requestingPrincipal, aURI, nsIScriptSecurityManager::STANDARD); NS_ENSURE_SUCCESS(rv, rv); // Allow data URIs and other URI's that inherit their principal by passing // true as the 3rd argument of CheckMayLoad, since we want // to allow external resources from data URIs regardless of the difference // in URI scheme. rv = requestingPrincipal->CheckMayLoad(aURI, true, true); NS_ENSURE_SUCCESS(rv, rv); int16_t shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER, aURI, requestingPrincipal, aRequestingNode, EmptyCString(), //mime guess nullptr, //extra &shouldLoad, nsContentUtils::GetContentPolicy(), nsContentUtils::GetSecurityManager()); if (NS_FAILED(rv)) return rv; if (NS_CP_REJECTED(shouldLoad)) { // Disallowed by content policy return NS_ERROR_CONTENT_BLOCKED; } nsIDocument* doc = aRequestingNode->OwnerDoc(); nsCOMPtr req = nsContentUtils::GetSameOriginChecker(); NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY); nsCOMPtr loadGroup = doc->GetDocumentLoadGroup(); nsCOMPtr channel; rv = NS_NewChannel(getter_AddRefs(channel), aURI, aRequestingNode, nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, loadGroup, req); // aCallbacks NS_ENSURE_SUCCESS(rv, rv); mURI = aURI; return channel->AsyncOpen(this, nullptr); } NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks, nsIInterfaceRequestor) #define IMPL_SHIM(_i) \ NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i) IMPL_SHIM(nsILoadContext) IMPL_SHIM(nsIProgressEventSink) IMPL_SHIM(nsIChannelEventSink) IMPL_SHIM(nsISecurityEventSink) IMPL_SHIM(nsIApplicationCacheContainer) #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); \ if (!shim) { \ return NS_ERROR_OUT_OF_MEMORY; \ } \ shim.forget(aSink); \ return NS_OK; \ } \ PR_END_MACRO NS_IMETHODIMP nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID, void **aSink) { if (mCallbacks && (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) || IID_IS(nsITabChild))) { return mCallbacks->GetInterface(aIID, aSink); } *aSink = nullptr; TRY_SHIM(nsILoadContext); TRY_SHIM(nsIProgressEventSink); TRY_SHIM(nsIChannelEventSink); TRY_SHIM(nsISecurityEventSink); TRY_SHIM(nsIApplicationCacheContainer); return NS_NOINTERFACE; } #undef TRY_SHIM #undef IID_IS nsExternalResourceMap::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 nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList { public: explicit nsDOMStyleSheetSetList(nsIDocument* aDocument); void Disconnect() { mDocument = nullptr; } virtual void EnsureFresh() MOZ_OVERRIDE; protected: nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it // dies. }; nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument) : mDocument(aDocument) { NS_ASSERTION(mDocument, "Must have document!"); } void nsDOMStyleSheetSetList::EnsureFresh() { mNames.Clear(); if (!mDocument) { return; // Spec says "no exceptions", and we have no style sets if we have // no document, for sure } int32_t count = mDocument->GetNumberOfStyleSheets(); nsAutoString title; for (int32_t index = 0; index < count; index++) { nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index); NS_ASSERTION(sheet, "Null sheet in sheet list!"); sheet->GetTitle(title); if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) { return; } } } // ================================================================== nsIDocument::SelectorCache::SelectorCache() : nsExpirationTracker(1000) { } // CacheList takes ownership of aSelectorList. void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector, nsCSSSelectorList* aSelectorList) { SelectorCacheKey* key = new SelectorCacheKey(aSelector); mTable.Put(key->mKey, aSelectorList); AddObject(key); } class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable { public: explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete) : mSelector(aToDelete) { MOZ_COUNT_CTOR(SelectorCacheKeyDeleter); } protected: ~SelectorCacheKeyDeleter() { MOZ_COUNT_DTOR(SelectorCacheKeyDeleter); } public: NS_IMETHOD Run() { return NS_OK; } private: nsAutoPtr mSelector; }; void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector) { RemoveObject(aSelector); mTable.Remove(aSelector->mKey); nsCOMPtr runnable = new SelectorCacheKeyDeleter(aSelector); NS_DispatchToCurrentThread(runnable); } struct nsIDocument::FrameRequest { FrameRequest(const FrameRequestCallbackHolder& aCallback, int32_t aHandle) : mCallback(aCallback), mHandle(aHandle) {} // Conversion operator so that we can append these to a // FrameRequestCallbackList operator const FrameRequestCallbackHolder& () const { return mCallback; } // Comparator operators to allow RemoveElementSorted with an // integer argument on arrays of FrameRequest bool operator==(int32_t aHandle) const { return mHandle == aHandle; } bool operator<(int32_t aHandle) const { return mHandle < aHandle; } FrameRequestCallbackHolder mCallback; int32_t mHandle; }; static already_AddRefed nullNodeInfo; // ================================================================== // = // ================================================================== nsIDocument::nsIDocument() : nsINode(nullNodeInfo), mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")), mNodeInfoManager(nullptr), mCompatMode(eCompatibility_FullStandards), mVisibilityState(dom::VisibilityState::Hidden), mIsInitialDocumentInWindow(false), mMayStartLayout(true), mVisible(true), 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), mIsBeingUsedAsImage(false), mHasLinksToUpdate(false), mPartID(0), mDidFireDOMContentLoaded(true) { SetInDocument(); } // NOTE! nsDocument::operator new() zeroes out all members, so don't // bother initializing members to 0. nsDocument::nsDocument(const char* aContentType) : nsIDocument() , mAnimatingImages(true) , mViewportType(Unknown) { SetContentTypeInternal(nsDependentCString(aContentType)); #ifdef PR_LOGGING if (!gDocumentLeakPRLog) gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak"); if (gDocumentLeakPRLog) PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, ("DOCUMENT %p created", this)); if (!gCspPRLog) gCspPRLog = PR_NewLogModule("CSP"); #endif // Start out mLastStyleSheetSet as null, per spec SetDOMStringToNull(mLastStyleSheetSet); if (!sProcessingStack) { sProcessingStack.emplace(); // Add the base queue sentinel to the processing stack. sProcessingStack->AppendElement((CustomElementData*) nullptr); } } static PLDHashOperator ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg) { if (aBoxObject) { aBoxObject->Clear(); } return PL_DHASH_NEXT; } nsIDocument::~nsIDocument() { if (mNodeInfoManager) { mNodeInfoManager->DropDocumentReference(); } } nsDocument::~nsDocument() { #ifdef PR_LOGGING if (gDocumentLeakPRLog) PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG, ("DOCUMENT %p destroyed", this)); #endif NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document"); // Note: This assert is only non-fatal because mochitest-bc triggers // it... as well as the preceding assert about !mIsShowing. NS_ASSERTION(!mObservingAppThemeChanged, "Document leaked to shutdown, then the observer service dropped " "its ref to us so we were able to go away."); if (IsTopLevelContentDocument()) { //don't report for about: pages nsCOMPtr principal = GetPrincipal(); nsCOMPtr uri; principal->GetURI(getter_AddRefs(uri)); bool isAboutScheme = true; if (uri) { uri->SchemeIs("about", &isAboutScheme); } if (!isAboutScheme) { // Record the page load uint32_t pageLoaded = 1; Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded); // Record the mixed content status of the docshell in Telemetry enum { NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content }; bool mixedActiveLoaded = GetHasMixedActiveContentLoaded(); bool mixedActiveBlocked = GetHasMixedActiveContentBlocked(); bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded(); bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked(); bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded); bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded); uint32_t mixedContentLevel = NO_MIXED_CONTENT; if (hasMixedDisplay && hasMixedActive) { mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT; } else if (hasMixedActive){ mixedContentLevel = MIXED_ACTIVE_CONTENT; } else if (hasMixedDisplay) { mixedContentLevel = MIXED_DISPLAY_CONTENT; } Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel); } } mInDestructor = true; mInUnlinkOrDeletion = true; mRegistry = nullptr; mozilla::DropJSObjects(this); // Clear mObservers to keep it in sync with the mutationobserver list mObservers.Clear(); if (mStyleSheetSetList) { mStyleSheetSetList->Disconnect(); } if (mAnimationController) { mAnimationController->Disconnect(); } mParentDocument = nullptr; // Kill the subdocument map, doing this will release its strong // references, if any. if (mSubDocuments) { PL_DHashTableDestroy(mSubDocuments); mSubDocuments = nullptr; } // Destroy link map now so we don't waste time removing // links one by one DestroyElementMaps(); nsAutoScriptBlocker scriptBlocker; int32_t indx; // must be signed uint32_t count = mChildren.ChildCount(); for (indx = int32_t(count) - 1; indx >= 0; --indx) { mChildren.ChildAt(indx)->UnbindFromTree(); mChildren.RemoveChildAt(indx); } mFirstChild = nullptr; mCachedRootElement = nullptr; // Let the stylesheets know we're going away indx = mStyleSheets.Count(); while (--indx >= 0) { mStyleSheets[indx]->SetOwningDocument(nullptr); } if (mAttrStyleSheet) { mAttrStyleSheet->SetOwningDocument(nullptr); } // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them. 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(); } delete mHeaderData; if (mBoxObjectTable) { mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr); delete mBoxObjectTable; } mPendingTitleChangeEvent.Revoke(); for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) { nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]); } // We don't want to leave residual locks on images. Make sure we're in an // unlocked state, and then clear the table. SetImageLockingState(false); mImageTracker.Clear(); mPlugins.Clear(); } NS_INTERFACE_TABLE_HEAD(nsDocument) NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY NS_INTERFACE_TABLE_BEGIN NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget) NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver) NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator) NS_INTERFACE_TABLE_END NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument) NS_IMETHODIMP_(MozExternalRefCountType) nsDocument::Release() { NS_PRECONDITION(0 != mRefCnt, "dup release"); NS_ASSERT_OWNINGTHREAD(nsDocument); nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this); bool shouldDelete = false; nsrefcnt count = mRefCnt.decr(base, &shouldDelete); NS_LOG_RELEASE(this, count, "nsDocument"); if (count == 0) { if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) { mNeedsReleaseAfterStackRefCntRelease = true; NS_ADDREF_THIS(); return mRefCnt.get(); } mRefCnt.incr(base); nsNodeUtils::LastRelease(this); mRefCnt.decr(base); if (shouldDelete) { mRefCnt.stabilizeForDeletion(); DeleteCycleCollectable(); } } return count; } NS_IMETHODIMP_(void) nsDocument::DeleteCycleCollectable() { delete this; } NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument) 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(nsDocument) return Element::CanSkipInCC(tmp); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument) return Element::CanSkipThis(tmp); NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END static PLDHashOperator SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, void *arg) { SubDocMapEntry *entry = static_cast(hdr); nsCycleCollectionTraversalCallback *cb = static_cast(arg); 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(entry->mSubDocument); return PL_DHASH_NEXT; } static PLDHashOperator RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData, void* aClosure) { nsCycleCollectionTraversalCallback *cb = static_cast(aClosure); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRadioGroups entry->mSelectedRadioButton"); cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton)); uint32_t i, count = aData->mRadioButtons.Count(); for (i = 0; i < count; ++i) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRadioGroups entry->mRadioButtons[i]"); cb->NoteXPCOMChild(aData->mRadioButtons[i]); } return PL_DHASH_NEXT; } static PLDHashOperator BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg) { nsCycleCollectionTraversalCallback *cb = static_cast(userArg); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry"); cb->NoteXPCOMChild(boxObject); return PL_DHASH_NEXT; } static PLDHashOperator IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg) { nsCycleCollectionTraversalCallback *cb = static_cast(aArg); aEntry->Traverse(cb); return PL_DHASH_NEXT; } static const char* kNSURIs[] = { "([none])", "(xmlns)", "(xml)", "(xhtml)", "(XLink)", "(XSLT)", "(XBL)", "(MathML)", "(RDF)", "(XUL)" }; NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument) 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) tmp->mDocumentURI->GetSpec(uri); if (nsid < ArrayLength(kNSURIs)) { PR_snprintf(name, sizeof(name), "nsDocument %s %s %s", loadedAsData.get(), kNSURIs[nsid], uri.get()); } else { PR_snprintf(name, sizeof(name), "nsDocument %s %s", loadedAsData.get(), uri.get()); } cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); } else { NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get()) } // Always need to traverse script objects, so do that before we check // if we're uncollectable. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS if (!nsINode::Traverse(tmp, cb)) { return NS_SUCCESS_INTERRUPTED_TRAVERSE; } tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb); tmp->mExternalResourceMap.Traverse(&cb); // Traverse the mChildren nsAttrAndChildArray. for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]"); cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1)); } // Traverse all nsIDocument pointer members. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument) // Traverse all nsDocument nsCOMPtrs. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImportManager) tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb); // The boxobject for an element will only exist as long as it's in the // document, so we'll traverse the table here instead of from the element. if (tmp->mBoxObjectTable) { tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationTimeline) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry) // Traverse all our nsCOMArrays. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSubImportLinks) for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]"); cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports()); } // Traverse animation components if (tmp->mAnimationController) { tmp->mAnimationController->Traverse(&cb); } if (tmp->mSubDocuments && tmp->mSubDocuments->ops) { PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader) for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) { nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb); } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument) if (tmp->PreservingWrapper()) { NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando); } NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) tmp->mInUnlinkOrDeletion = true; // Clear out our external resources tmp->mExternalResourceMap.Shutdown(); nsAutoScriptBlocker scriptBlocker; nsINode::Unlink(tmp); // Unlink the mChildren nsAttrAndChildArray. for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1; indx >= 0; --indx) { tmp->mChildren.ChildAt(indx)->UnbindFromTree(); tmp->mChildren.RemoveChildAt(indx); } tmp->mFirstChild = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator) tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps) NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder) NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner) NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection) NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry) NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks) tmp->mParentDocument = nullptr; NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages) if (tmp->mBoxObjectTable) { tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr); delete tmp->mBoxObjectTable; tmp->mBoxObjectTable = nullptr; } if (tmp->mListenerManager) { tmp->mListenerManager->Disconnect(); tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); tmp->mListenerManager = nullptr; } NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets) if (tmp->mStyleSheetSetList) { tmp->mStyleSheetSetList->Disconnect(); tmp->mStyleSheetSetList = nullptr; } if (tmp->mSubDocuments) { PL_DHashTableDestroy(tmp->mSubDocuments); tmp->mSubDocuments = nullptr; } tmp->mFrameRequestCallbacks.Clear(); tmp->mRadioGroups.Clear(); // nsDocument 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->mIdentifierMap.Clear(); tmp->mExpandoAndGeneration.Unlink(); if (tmp->mAnimationController) { tmp->mAnimationController->Unlink(); } tmp->mPendingTitleChangeEvent.Revoke(); if (tmp->mCSSLoader) { tmp->mCSSLoader->DropDocumentReference(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader) } for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) { nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]); } tmp->mInUnlinkOrDeletion = false; NS_IMPL_CYCLE_COLLECTION_UNLINK_END static bool sPrefsInitialized = false; static uint32_t sOnloadDecodeLimit = 0; nsresult nsDocument::Init() { if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) { return NS_ERROR_ALREADY_INITIALIZED; } if (!sPrefsInitialized) { sPrefsInitialized = true; Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0); } // Force initialization. nsINode::nsSlots* slots = Slots(); // Prepend self as mutation-observer whether we need it or not (some // subclasses currently do, other don't). This is because the code in // nsNodeUtils always notifies the first observer first, expecting the // first observer to be the document. NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast(this)), NS_ERROR_OUT_OF_MEMORY); mOnloadBlocker = new nsOnloadBlocker(); mCSSLoader = new mozilla::css::Loader(this); // Assume we're not quirky, until we know otherwise mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards); mStyleImageLoader = new mozilla::css::ImageLoader(this); mNodeInfoManager = new nsNodeInfoManager(); nsresult rv = mNodeInfoManager->Init(this); NS_ENSURE_SUCCESS(rv, rv); // mNodeInfo keeps NodeInfoManager alive! mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo(); NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY); NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE, "Bad NodeType in aNodeInfo"); NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!"); // 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 global = xpc::NativeGlobal(xpc::PrivilegedJunkScope()); NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); mScopeObject = do_GetWeakReference(global); MOZ_ASSERT(mScopeObject); mScriptLoader = new nsScriptLoader(this); mozilla::HoldJSObjects(this); return NS_OK; } void nsIDocument::DeleteAllProperties() { for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) { PropertyTable(i)->DeleteAllProperties(); } } void nsIDocument::DeleteAllPropertiesFor(nsINode* aNode) { for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) { PropertyTable(i)->DeleteAllPropertiesFor(aNode); } } nsPropertyTable* nsIDocument::GetExtraPropertyTable(uint16_t aCategory) { NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled"); while (aCategory >= mExtraPropertyTables.Length() + 1) { mExtraPropertyTables.AppendElement(new nsPropertyTable()); } return mExtraPropertyTables[aCategory - 1]; } bool nsIDocument::IsVisibleConsideringAncestors() const { const nsIDocument *parent = this; do { if (!parent->IsVisible()) { return false; } } while ((parent = parent->GetParentDocument())); return true; } void nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) { nsCOMPtr uri; nsCOMPtr principal; if (aChannel) { // Note: this code is duplicated in XULDocument::StartDocumentLoad and // nsScriptSecurityManager::GetChannelResultPrincipal. // Note: this should match nsDocShell::OnLoadingSite NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); nsIScriptSecurityManager *securityManager = nsContentUtils::GetSecurityManager(); if (securityManager) { securityManager->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal)); } } ResetToURI(uri, aLoadGroup, principal); nsCOMPtr bag = do_QueryInterface(aChannel); if (bag) { nsCOMPtr baseURI; bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), NS_GET_IID(nsIURI), getter_AddRefs(baseURI)); if (baseURI) { mDocumentBaseURI = baseURI; mChromeXHRDocBaseURI = baseURI; } } mChannel = aChannel; } void nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup, nsIPrincipal* aPrincipal) { NS_PRECONDITION(aURI, "Null URI passed to ResetToURI"); #ifdef PR_LOGGING if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { nsAutoCString spec; aURI->GetSpec(spec); PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get()); } #endif mSecurityInfo = nullptr; mDocumentLoadGroup = nullptr; // Delete references to sub-documents and kill the subdocument map, // if any. It holds strong references if (mSubDocuments) { PL_DHashTableDestroy(mSubDocuments); mSubDocuments = nullptr; } // Destroy link map now so we don't waste time removing // links one by one DestroyElementMaps(); bool oldVal = mInUnlinkOrDeletion; mInUnlinkOrDeletion = true; uint32_t count = mChildren.ChildCount(); { // Scope for update MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true); for (int32_t i = int32_t(count) - 1; i >= 0; i--) { nsCOMPtr content = mChildren.ChildAt(i); nsIContent* previousSibling = content->GetPreviousSibling(); if (nsINode::GetFirstChild() == content) { mFirstChild = content->GetNextSibling(); } mChildren.RemoveChildAt(i); nsNodeUtils::ContentRemoved(this, content, i, previousSibling); content->UnbindFromTree(); } mCachedRootElement = nullptr; } mInUnlinkOrDeletion = oldVal; mRegistry = nullptr; // 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. SetPrincipal(nullptr); // Clear the original URI so SetDocumentURI sets it. mOriginalURI = nullptr; SetDocumentURI(aURI); mChromeXHRDocURI = aURI; // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns // mDocumentURI. mDocumentBaseURI = nullptr; mChromeXHRDocBaseURI = nullptr; if (aLoadGroup) { 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? } mLastModified.Truncate(); // XXXbz I guess we're assuming that the caller will either pass in // a channel with a useful type or call SetContentType? SetContentTypeInternal(EmptyCString()); mContentLanguage.Truncate(); mBaseTarget.Truncate(); mReferrer.Truncate(); mXMLDeclarationBits = 0; // Now get our new principal if (aPrincipal) { SetPrincipal(aPrincipal); } else { nsIScriptSecurityManager *securityManager = nsContentUtils::GetSecurityManager(); if (securityManager) { nsCOMPtr loadContext(mDocumentContainer); if (!loadContext && aLoadGroup) { nsCOMPtr 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 principal; nsresult rv = securityManager-> GetLoadContextCodebasePrincipal(mDocumentURI, loadContext, getter_AddRefs(principal)); if (NS_SUCCEEDED(rv)) { SetPrincipal(principal); } } } // Refresh the principal on the compartment. nsPIDOMWindow* win = GetInnerWindow(); if (win) { win->RefreshCompartmentPrincipal(); } } void nsDocument::RemoveDocStyleSheetsFromStyleSets() { // The stylesheets should forget us int32_t indx = mStyleSheets.Count(); while (--indx >= 0) { nsIStyleSheet* sheet = mStyleSheets[indx]; sheet->SetOwningDocument(nullptr); if (sheet->IsApplicable()) { nsCOMPtr shell = GetShell(); if (shell) { shell->StyleSet()->RemoveDocStyleSheet(sheet); } } // XXX Tell observers? } } void nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray& aSheets, nsStyleSet::sheetType aType) { // The stylesheets should forget us int32_t indx = aSheets.Count(); while (--indx >= 0) { nsIStyleSheet* sheet = aSheets[indx]; sheet->SetOwningDocument(nullptr); if (sheet->IsApplicable()) { nsCOMPtr shell = GetShell(); if (shell) { shell->StyleSet()->RemoveStyleSheet(aType, sheet); } } // XXX Tell observers? } } void nsDocument::ResetStylesheetsToURI(nsIURI* aURI) { MOZ_ASSERT(aURI); mozAutoDocUpdate upd(this, UPDATE_STYLE, true); RemoveDocStyleSheetsFromStyleSets(); RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, nsStyleSet::eAgentSheet); RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet); RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet); RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet); // Release all the sheets mStyleSheets.Clear(); mOnDemandBuiltInUASheets.Clear(); for (uint32_t i = 0; i < SheetTypeCount; ++i) mAdditionalSheets[i].Clear(); // NOTE: We don't release the catalog sheets. It doesn't really matter // now, but it could in the future -- in which case not releasing them // is probably the right thing to do. // Now reset our inline style and attribute sheets. if (mAttrStyleSheet) { mAttrStyleSheet->Reset(); mAttrStyleSheet->SetOwningDocument(this); } else { mAttrStyleSheet = new nsHTMLStyleSheet(this); } if (!mStyleAttrStyleSheet) { mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet(); } if (!mSVGAttrAnimationRuleProcessor) { mSVGAttrAnimationRuleProcessor = new mozilla::SVGAttrAnimationRuleProcessor(); } // Now set up our style sets nsCOMPtr shell = GetShell(); if (shell) { FillStyleSet(shell->StyleSet()); } } static bool AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData) { nsStyleSet *styleSet = static_cast(aData); styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet); return true; } static void AppendSheetsToStyleSet(nsStyleSet* aStyleSet, const nsCOMArray& aSheets, nsStyleSet::sheetType aType) { for (int32_t i = aSheets.Count() - 1; i >= 0; --i) { aStyleSet->AppendStyleSheet(aType, aSheets[i]); } } void nsDocument::FillStyleSet(nsStyleSet* aStyleSet) { NS_PRECONDITION(aStyleSet, "Must have a style set"); NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0, "Style set already has document sheets?"); // We could consider moving this to nsStyleSet::Init, to match its // handling of the eAnimationSheet and eTransitionSheet levels. aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet); aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet); int32_t i; for (i = mStyleSheets.Count() - 1; i >= 0; --i) { nsIStyleSheet* sheet = mStyleSheets[i]; if (sheet->IsApplicable()) { aStyleSet->AddDocStyleSheet(sheet, this); } } nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance(); if (sheetService) { sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet, aStyleSet); } // Iterate backwards to maintain order for (i = mOnDemandBuiltInUASheets.Count() - 1; i >= 0; --i) { nsIStyleSheet* sheet = mOnDemandBuiltInUASheets[i]; if (sheet->IsApplicable()) { aStyleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet); } } AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet); AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet); AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet); } static void WarnIfSandboxIneffective(nsIDocShell* aDocShell, uint32_t aSandboxFlags, nsIChannel* aChannel) { // 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)) { nsCOMPtr parentAsItem; aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem)); nsCOMPtr parentDocShell = do_QueryInterface(parentAsItem); if (!parentDocShell) { return; } // Don't warn if our parent is not the top-level document. nsCOMPtr grandParentAsItem; parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem)); if (grandParentAsItem) { return; } nsCOMPtr parentChannel; parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel)); if (!parentChannel) { return; } nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel); if (NS_FAILED(rv)) { return; } nsCOMPtr parentDocument = do_GetInterface(parentDocShell); nsCOMPtr iframeUri; parentChannel->GetURI(getter_AddRefs(iframeUri)); nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Iframe Sandbox"), parentDocument, nsContentUtils::eSECURITY_PROPERTIES, "BothAllowScriptsAndSameOriginPresent", nullptr, 0, iframeUri); } } nsresult nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, nsIStreamListener **aDocListener, bool aReset, nsIContentSink* aSink) { #ifdef PR_LOGGING if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) { nsCOMPtr uri; aChannel->GetURI(getter_AddRefs(uri)); nsAutoCString spec; if (uri) uri->GetSpec(spec); PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get()); } #endif #ifdef DEBUG { uint32_t appId; nsresult rv = NodePrincipal()->GetAppId(&appId); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID, "Document should never have UNKNOWN_APP_ID"); } #endif MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED, "Bad readyState"); SetReadyStateInternal(READYSTATE_LOADING); if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) { mLoadedAsData = 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. // Do not load/process scripts when loading as data ScriptLoader()->SetEnabled(false); // styles CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data } else if (nsCRT::strcmp("external-resource", aCommand) == 0) { // Allow CSS, but not scripts ScriptLoader()->SetEnabled(false); } mMayStartLayout = false; mHaveInputEncoding = true; if (aReset) { Reset(aChannel, aLoadGroup); } nsAutoCString contentType; nsCOMPtr bag = do_QueryInterface(aChannel); if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString( NS_LITERAL_STRING("contentType"), 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); SetContentTypeInternal(Substring(start, semicolon)); } RetrieveRelevantHeaders(aChannel); mChannel = aChannel; nsCOMPtr inStrmChan = do_QueryInterface(mChannel); if (inStrmChan) { bool isSrcdocChannel; inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel); if (isSrcdocChannel) { mIsSrcdocDocument = true; } } // If this document is being loaded by a docshell, copy its sandbox flags // to the document. These are immutable after being set here. nsCOMPtr docShell = do_QueryInterface(aContainer); if (docShell) { nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags); NS_ENSURE_SUCCESS(rv, rv); WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel()); } // If this is not a data document, set CSP. if (!mLoadedAsData) { nsresult rv = InitCSP(aChannel); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } void CSPErrorQueue::Add(const char* aMessageName) { mErrors.AppendElement(aMessageName); } void CSPErrorQueue::Flush(nsIDocument* aDocument) { for (uint32_t i = 0; i < mErrors.Length(); i++) { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("CSP"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, mErrors[i]); } mErrors.Clear(); } void nsDocument::SendToConsole(nsCOMArray& 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()); } } static nsresult AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue, bool aReportOnly) { // Need to tokenize the header value since multiple headers could be // concatenated into one comma-separated list of policies. // See RFC2616 section 4.2 (last paragraph) nsresult rv = NS_OK; nsCharSeparatedTokenizer tokenizer(aHeaderValue, ','); while (tokenizer.hasMoreTokens()) { const nsSubstring& policy = tokenizer.nextToken(); rv = csp->AppendPolicy(policy, aReportOnly); NS_ENSURE_SUCCESS(rv, rv); #ifdef PR_LOGGING { PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP refined with policy: \"%s\"", NS_ConvertUTF16toUTF8(policy).get())); } #endif } return NS_OK; } bool nsDocument::IsLoopDocument(nsIChannel *aChannel) { nsCOMPtr chanURI; nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(chanURI)); NS_ENSURE_SUCCESS(rv, false); bool isAbout = false; bool isLoop = false; rv = chanURI->SchemeIs("about", &isAbout); NS_ENSURE_SUCCESS(rv, false); if (isAbout) { nsCOMPtr loopURI; rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation"); NS_ENSURE_SUCCESS(rv, false); rv = chanURI->EqualsExceptRef(loopURI, &isLoop); NS_ENSURE_SUCCESS(rv, false); if (!isLoop) { rv = NS_NewURI(getter_AddRefs(loopURI), "about:looppanel"); NS_ENSURE_SUCCESS(rv, false); rv = chanURI->EqualsExceptRef(loopURI, &isLoop); NS_ENSURE_SUCCESS(rv, false); } } return isLoop; } nsresult nsDocument::InitCSP(nsIChannel* aChannel) { nsCOMPtr csp; if (!CSPService::sCSPEnabled) { #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP is disabled, skipping CSP init for document %p", this)); #endif return NS_OK; } nsAutoCString tCspHeaderValue, tCspROHeaderValue; nsCOMPtr httpChannel = do_QueryInterface(aChannel); if (httpChannel) { httpChannel->GetResponseHeader( NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue); httpChannel->GetResponseHeader( NS_LITERAL_CSTRING("content-security-policy-report-only"), tCspROHeaderValue); } NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue); NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue); // Figure out if we need to apply an app default CSP or a CSP from an app manifest nsIPrincipal* principal = NodePrincipal(); uint16_t appStatus = principal->GetAppStatus(); bool applyAppDefaultCSP = false; bool applyAppManifestCSP = false; nsAutoString appManifestCSP; nsAutoString appDefaultCSP; if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) { nsCOMPtr appsService = do_GetService(APPS_SERVICE_CONTRACTID); if (appsService) { uint32_t appId = 0; if (NS_SUCCEEDED(principal->GetAppId(&appId))) { appsService->GetManifestCSPByLocalId(appId, appManifestCSP); if (!appManifestCSP.IsEmpty()) { applyAppManifestCSP = true; } appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP); if (!appDefaultCSP.IsEmpty()) { applyAppDefaultCSP = true; } } } } // Check if this is part of the Loop/Hello service bool applyLoopCSP = IsLoopDocument(aChannel); // If there's no CSP to apply, go ahead and return early if (!applyAppDefaultCSP && !applyAppManifestCSP && !applyLoopCSP && cspHeaderValue.IsEmpty() && cspROHeaderValue.IsEmpty()) { #ifdef PR_LOGGING nsCOMPtr chanURI; aChannel->GetURI(getter_AddRefs(chanURI)); nsAutoCString aspec; chanURI->GetAsciiSpec(aspec); PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("no CSP for document, %s, %s", aspec.get(), applyAppDefaultCSP ? "is app" : "not an app")); #endif return NS_OK; } #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this)); #endif nsresult rv; // If Document is an app check to see if we already set CSP and return early // if that is indeed the case. // // In general (see bug 947831), we should not be setting CSP on a principal // that aliases another document. For non-app code this is not a problem // since we only share the underlying principal with nested browsing // contexts for which a header cannot be set (e.g., about:blank and // about:srcodoc iframes) and thus won't try to set the CSP again. This // check ensures that we do not try to set CSP for an app. if (applyAppDefaultCSP || applyAppManifestCSP) { nsCOMPtr csp; rv = principal->GetCsp(getter_AddRefs(csp)); NS_ENSURE_SUCCESS(rv, rv); if (csp) { #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s", "This document is sharing principal with another document.", "Since the document is an app, CSP was already set.", "Skipping attempt to set CSP.")); #endif return NS_OK; } } csp = do_CreateInstance("@mozilla.org/cspcontext;1", &rv); if (NS_FAILED(rv)) { #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv)); #endif return rv; } // used as a "self" identifier for the CSP. nsCOMPtr selfURI; aChannel->GetURI(getter_AddRefs(selfURI)); // Store the request context for violation reports csp->SetRequestContext(nullptr, nullptr, aChannel); // ----- if the doc is an app and we want a default CSP, apply it. if (applyAppDefaultCSP) { csp->AppendPolicy(appDefaultCSP, false); } // ----- if the doc is an app and specifies a CSP in its manifest, apply it. if (applyAppManifestCSP) { csp->AppendPolicy(appManifestCSP, false); } // ----- if the doc is part of Loop, apply the loop CSP if (applyLoopCSP) { nsAdoptingString loopCSP; loopCSP = Preferences::GetString("loop.CSP"); NS_ASSERTION(loopCSP, "Missing loop.CSP preference"); // If the pref has been removed, we continue without setting a CSP if (loopCSP) { csp->AppendPolicy(loopCSP, false); } } // ----- if there's a full-strength CSP header, apply it. if (!cspHeaderValue.IsEmpty()) { rv = AppendCSPFromHeader(csp, cspHeaderValue, false); NS_ENSURE_SUCCESS(rv, rv); } // ----- if there's a report-only CSP header, apply it. if (!cspROHeaderValue.IsEmpty()) { rv = AppendCSPFromHeader(csp, cspROHeaderValue, true); NS_ENSURE_SUCCESS(rv, rv); } // ----- Enforce frame-ancestor policy on any applied policies nsCOMPtr docShell(mDocumentContainer); if (docShell) { bool safeAncestry = false; // PermitsAncestry sends violation reports when necessary rv = csp->PermitsAncestry(docShell, &safeAncestry); if (NS_FAILED(rv) || !safeAncestry) { #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP doesn't like frame's ancestry, not loading.")); #endif // stop! ERROR page! aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION); } } rv = principal->SetCsp(csp); NS_ENSURE_SUCCESS(rv, rv); #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Inserted CSP into principal %p", principal)); #endif return NS_OK; } void nsDocument::StopDocumentLoad() { if (mParser) { mParserAborted = true; mParser->Terminate(); } } void nsDocument::SetDocumentURI(nsIURI* aURI) { nsCOMPtr oldBase = GetDocBaseURI(); mDocumentURI = NS_TryToMakeImmutable(aURI); nsIURI* newBase = GetDocBaseURI(); bool equalBases = false; // Changing just the ref of a URI does not change how relative URIs would // resolve wrt to it, so we can treat the bases as equal as long as they're // equal ignoring the ref. if (oldBase && newBase) { oldBase->EqualsExceptRef(newBase, &equalBases); } else { equalBases = !oldBase && !newBase; } // If this is the first time we're setting the document's URI, set the // document's original URI. if (!mOriginalURI) mOriginalURI = mDocumentURI; // If changing the document's URI changed the base URI of the document, we // need to refresh the hrefs of all the links on the page. if (!equalBases) { RefreshLinkHrefs(); } } void nsDocument::SetChromeXHRDocURI(nsIURI* aURI) { mChromeXHRDocURI = aURI; } void nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI) { mChromeXHRDocBaseURI = aURI; } NS_IMETHODIMP nsDocument::GetLastModified(nsAString& aLastModified) { nsIDocument::GetLastModified(aLastModified); return NS_OK; } void nsIDocument::GetLastModified(nsAString& aLastModified) const { if (!mLastModified.IsEmpty()) { aLastModified.Assign(mLastModified); } else { // If we for whatever reason failed to find the last modified time // (or even the current time), fall back to what NS4.x returned. aLastModified.AssignLiteral(MOZ_UTF16("01/01/1970 00:00:00")); } } void nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName) { MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement), "Only put elements that need to be exposed as document['name'] in " "the named table."); nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(nsDependentAtomString(aName)); // Null for out-of-memory if (entry) { if (!entry->HasNameElement() && !entry->HasIdElementExposedAsHTMLDocumentProperty()) { ++mExpandoAndGeneration.generation; } entry->AddNameElement(this, aElement); } } void nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName) { // Speed up document teardown if (mIdentifierMap.Count() == 0) return; nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(nsDependentAtomString(aName)); if (!entry) // Could be false if the element was anonymous, hence never added return; entry->RemoveNameElement(aElement); if (!entry->HasNameElement() && !entry->HasIdElementExposedAsHTMLDocumentProperty()) { ++mExpandoAndGeneration.generation; } } void nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId) { nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(nsDependentAtomString(aId)); if (entry) { /* True except on OOM */ if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && !entry->HasNameElement() && !entry->HasIdElementExposedAsHTMLDocumentProperty()) { ++mExpandoAndGeneration.generation; } entry->AddIdElement(aElement); } } void nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId) { NS_ASSERTION(aId, "huhwhatnow?"); // Speed up document teardown if (mIdentifierMap.Count() == 0) { return; } nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(nsDependentAtomString(aId)); if (!entry) // Can be null for XML elements with changing ids. return; entry->RemoveIdElement(aElement); if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) && !entry->HasNameElement() && !entry->HasIdElementExposedAsHTMLDocumentProperty()) { ++mExpandoAndGeneration.generation; } if (entry->IsEmpty()) { mIdentifierMap.RawRemoveEntry(entry); } } nsIPrincipal* nsDocument::GetPrincipal() { return NodePrincipal(); } extern bool sDisablePrefetchHTTPSPref; void nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal) { if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) { nsCOMPtr uri; aNewPrincipal->GetURI(getter_AddRefs(uri)); bool isHTTPS; if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) || isHTTPS) { mAllowDNSPrefetch = false; } } mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal); } NS_IMETHODIMP nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache) { NS_IF_ADDREF(*aApplicationCache = mApplicationCache); return NS_OK; } NS_IMETHODIMP nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache) { mApplicationCache = aApplicationCache; return NS_OK; } NS_IMETHODIMP nsDocument::GetContentType(nsAString& aContentType) { CopyUTF8toUTF16(GetContentTypeInternal(), aContentType); return NS_OK; } void nsDocument::SetContentType(const nsAString& aContentType) { NS_ASSERTION(GetContentTypeInternal().IsEmpty() || GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)), "Do you really want to change the content-type?"); SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType)); } nsresult nsDocument::GetAllowPlugins(bool * aAllowPlugins) { // First, we ask our docshell if it allows plugins. nsCOMPtr docShell(mDocumentContainer); if (docShell) { docShell->GetAllowPlugins(aAllowPlugins); // If the docshell allows plugins, we check whether // we are sandboxed and plugins should not be allowed. if (*aAllowPlugins) *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS); } return NS_OK; } already_AddRefed nsDocument::GetUndoManager() { Element* rootElement = GetRootElement(); if (!rootElement) { return nullptr; } if (!mUndoManager) { mUndoManager = new UndoManager(rootElement); } nsRefPtr undoManager = mUndoManager; return undoManager.forget(); } AnimationTimeline* nsDocument::Timeline() { if (!mAnimationTimeline) { mAnimationTimeline = new AnimationTimeline(this); } return mAnimationTimeline; } /* Return true if the document is in the focused top-level window, and is an * ancestor of the focused DOMWindow. */ NS_IMETHODIMP nsDocument::HasFocus(bool* aResult) { ErrorResult rv; *aResult = nsIDocument::HasFocus(rv); return rv.ErrorCode(); } bool nsIDocument::HasFocus(ErrorResult& rv) const { nsIFocusManager* fm = nsFocusManager::GetFocusManager(); if (!fm) { rv.Throw(NS_ERROR_NOT_AVAILABLE); return false; } // Is there a focused DOMWindow? nsCOMPtr focusedWindow; fm->GetFocusedWindow(getter_AddRefs(focusedWindow)); if (!focusedWindow) { return false; } // Are we an ancestor of the focused DOMWindow? nsCOMPtr domDocument; focusedWindow->GetDocument(getter_AddRefs(domDocument)); nsCOMPtr document = do_QueryInterface(domDocument); for (nsIDocument* currentDoc = document; currentDoc; currentDoc = currentDoc->GetParentDocument()) { if (currentDoc == this) { // Yes, we are an ancestor return true; } } return false; } NS_IMETHODIMP nsDocument::GetReferrer(nsAString& aReferrer) { nsIDocument::GetReferrer(aReferrer); return NS_OK; } void nsIDocument::GetReferrer(nsAString& aReferrer) const { if (mIsSrcdocDocument && mParentDocument) mParentDocument->GetReferrer(aReferrer); else CopyUTF8toUTF16(mReferrer, aReferrer); } nsresult nsIDocument::GetSrcdocData(nsAString &aSrcdocData) { if (mIsSrcdocDocument) { nsCOMPtr inStrmChan = do_QueryInterface(mChannel); if (inStrmChan) { return inStrmChan->GetSrcdocData(aSrcdocData); } } aSrcdocData = NullString(); return NS_OK; } NS_IMETHODIMP nsDocument::GetActiveElement(nsIDOMElement **aElement) { nsCOMPtr el(do_QueryInterface(nsIDocument::GetActiveElement())); el.forget(aElement); return NS_OK; } Element* nsIDocument::GetActiveElement() { // Get the focused element. nsCOMPtr window = GetWindow(); if (window) { nsCOMPtr focusedWindow; nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow)); // be safe and make sure the element is from this document if (focusedContent && focusedContent->OwnerDoc() == this) { if (focusedContent->ChromeOnlyAccess()) { focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent(); } if (focusedContent) { return focusedContent->AsElement(); } } } // No focused element anywhere in this document. Try to get the BODY. nsRefPtr htmlDoc = AsHTMLDocument(); if (htmlDoc) { // Because of IE compatibility, return null when html document doesn't have // a body. return htmlDoc->GetBody(); } // If we couldn't get a BODY, return the root element. return GetDocumentElement(); } NS_IMETHODIMP nsDocument::GetCurrentScript(nsIDOMElement **aElement) { nsCOMPtr el(do_QueryInterface(nsIDocument::GetCurrentScript())); el.forget(aElement); return NS_OK; } Element* nsIDocument::GetCurrentScript() { nsCOMPtr el(do_QueryInterface(ScriptLoader()->GetCurrentScript())); return el; } NS_IMETHODIMP nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn) { Element* el = nsIDocument::ElementFromPoint(aX, aY); nsCOMPtr retval = do_QueryInterface(el); retval.forget(aReturn); return NS_OK; } Element* nsIDocument::ElementFromPoint(float aX, float aY) { return ElementFromPointHelper(aX, aY, false, true); } Element* nsDocument::ElementFromPointHelper(float aX, float aY, bool aIgnoreRootScrollFrame, bool aFlushLayout) { // As per the the spec, we return null if either coord is negative if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) { return nullptr; } nscoord x = nsPresContext::CSSPixelsToAppUnits(aX); nscoord y = nsPresContext::CSSPixelsToAppUnits(aY); nsPoint pt(x, y); // Make sure the layout information we get is up-to-date, and // ensure we get a root frame (for everything but XUL) if (aFlushLayout) FlushPendingNotifications(Flush_Layout); nsIPresShell *ps = GetShell(); if (!ps) { return nullptr; } nsIFrame *rootFrame = ps->GetRootFrame(); // XUL docs, unlike HTML, have no frame tree until everything's done loading if (!rootFrame) { return nullptr; // return null to premature XUL callers as a reminder to wait } nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt, nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); if (!ptFrame) { return nullptr; } nsIContent* elem = GetContentInThisDocument(ptFrame); if (elem && !elem->IsElement()) { elem = elem->GetParent(); } return elem ? elem->AsElement() : nullptr; } nsresult nsDocument::NodesFromRectHelper(float aX, float aY, float aTopSize, float aRightSize, float aBottomSize, float aLeftSize, bool aIgnoreRootScrollFrame, bool aFlushLayout, nsIDOMNodeList** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); nsSimpleContentList* elements = new nsSimpleContentList(this); NS_ADDREF(elements); *aReturn = elements; // Following the same behavior of elementFromPoint, // we don't return anything if either coord is negative if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) return NS_OK; nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize); nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize); nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1; nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1; nsRect rect(x, y, w, h); // Make sure the layout information we get is up-to-date, and // ensure we get a root frame (for everything but XUL) if (aFlushLayout) { FlushPendingNotifications(Flush_Layout); } nsIPresShell *ps = GetShell(); NS_ENSURE_STATE(ps); nsIFrame *rootFrame = ps->GetRootFrame(); // XUL docs, unlike HTML, have no frame tree until everything's done loading if (!rootFrame) return NS_OK; // return nothing to premature XUL callers as a reminder to wait nsAutoTArray outFrames; nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames, nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC | (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0)); // Used to filter out repeated elements in sequence. nsIContent* lastAdded = nullptr; for (uint32_t i = 0; i < outFrames.Length(); i++) { nsIContent* node = GetContentInThisDocument(outFrames[i]); if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) { // We have a node that isn't an element or a text node, // use its parent content instead. node = node->GetParent(); } if (node && node != lastAdded) { elements->AppendElement(node); lastAdded = node; } } return NS_OK; } NS_IMETHODIMP nsDocument::GetElementsByClassName(const nsAString& aClasses, nsIDOMNodeList** aReturn) { *aReturn = nsIDocument::GetElementsByClassName(aClasses).take(); return NS_OK; } already_AddRefed nsIDocument::GetElementsByClassName(const nsAString& aClasses) { return nsContentUtils::GetElementsByClassName(this, aClasses); } NS_IMETHODIMP nsDocument::ReleaseCapture() { nsIDocument::ReleaseCapture(); return NS_OK; } void nsIDocument::ReleaseCapture() const { // only release the capture if the caller can access it. This prevents a // page from stopping a scrollbar grab for example. nsCOMPtr node = nsIPresShell::GetCapturingContent(); if (node && nsContentUtils::CanCallerAccess(node)) { nsIPresShell::SetCapturingContent(nullptr, 0); } } already_AddRefed nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const { nsCOMPtr uri; if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) { uri = mChromeXHRDocBaseURI; } else { uri = GetDocBaseURI(); } return uri.forget(); } nsresult nsDocument::SetBaseURI(nsIURI* aURI) { if (!aURI && !mDocumentBaseURI) { return NS_OK; } // Don't do anything if the URI wasn't actually changed. if (aURI && mDocumentBaseURI) { bool equalBases = false; mDocumentBaseURI->Equals(aURI, &equalBases); if (equalBases) { return NS_OK; } } // Check if CSP allows this base-uri nsCOMPtr csp; nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp)); NS_ENSURE_SUCCESS(rv, rv); if (csp) { bool permitsBaseURI = false; rv = csp->PermitsBaseURI(aURI, &permitsBaseURI); NS_ENSURE_SUCCESS(rv, rv); if (!permitsBaseURI) { return NS_OK; } } if (aURI) { mDocumentBaseURI = NS_TryToMakeImmutable(aURI); } else { mDocumentBaseURI = nullptr; } RefreshLinkHrefs(); return NS_OK; } void nsDocument::GetBaseTarget(nsAString &aBaseTarget) { aBaseTarget = mBaseTarget; } void nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID) { // XXX it would be a good idea to assert the sanity of the argument, // but before we figure out what to do about non-Encoding Standard // encodings in the charset menu and in mailnews, assertions are futile. if (!mCharacterSet.Equals(aCharSetID)) { if (mMasterDocument && !aCharSetID.EqualsLiteral("UTF-8")) { // Imports are always UTF-8 return; } mCharacterSet = aCharSetID; int32_t n = mCharSetObservers.Length(); for (int32_t i = 0; i < n; i++) { nsIObserver* observer = mCharSetObservers.ElementAt(i); observer->Observe(static_cast(this), "charset", NS_ConvertASCIItoUTF16(aCharSetID).get()); } } } nsresult nsDocument::AddCharSetObserver(nsIObserver* aObserver) { NS_ENSURE_ARG_POINTER(aObserver); NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE); return NS_OK; } void nsDocument::RemoveCharSetObserver(nsIObserver* aObserver) { mCharSetObservers.RemoveElement(aObserver); } void nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const { aData.Truncate(); const nsDocHeaderData* data = mHeaderData; while (data) { if (data->mField == aHeaderField) { aData = data->mData; break; } data = data->mNext; } } void nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData) { if (!aHeaderField) { NS_ERROR("null headerField"); return; } if (!mHeaderData) { if (!aData.IsEmpty()) { // don't bother storing empty string mHeaderData = new nsDocHeaderData(aHeaderField, aData); } } else { nsDocHeaderData* data = mHeaderData; nsDocHeaderData** lastPtr = &mHeaderData; bool found = false; do { // look for existing and replace if (data->mField == aHeaderField) { if (!aData.IsEmpty()) { data->mData.Assign(aData); } else { // don't store empty string *lastPtr = data->mNext; data->mNext = nullptr; delete data; } found = true; break; } lastPtr = &(data->mNext); data = *lastPtr; } while (data); if (!aData.IsEmpty() && !found) { // didn't find, append *lastPtr = new nsDocHeaderData(aHeaderField, aData); } } if (aHeaderField == nsGkAtoms::headerContentLanguage) { CopyUTF16toUTF8(aData, mContentLanguage); } if (aHeaderField == nsGkAtoms::headerDefaultStyle) { // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per // spec. if (DOMStringIsNull(mLastStyleSheetSet)) { // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet, // per spec. The idea here is that we're changing our preferred set and // that shouldn't change the value of lastStyleSheetSet. Also, we're // using the Internal version so we can update the CSSLoader and not have // to worry about null strings. EnableStyleSheetsForSetInternal(aData, true); } } if (aHeaderField == nsGkAtoms::refresh) { // We get into this code before we have a script global yet, so get to // our container via mDocumentContainer. nsCOMPtr refresher(mDocumentContainer); if (refresher) { // Note: using mDocumentURI instead of mBaseURI here, for consistency // (used to just use the current URI of our webnavigation, but that // should really be the same thing). Note that this code can run // before the current URI of the webnavigation has been updated, so we // can't assert equality here. refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(), NS_ConvertUTF16toUTF8(aData)); } } if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl && mAllowDNSPrefetch) { // Chromium treats any value other than 'on' (case insensitive) as 'off'. mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on"); } if (aHeaderField == nsGkAtoms::viewport || aHeaderField == nsGkAtoms::handheldFriendly || aHeaderField == nsGkAtoms::viewport_minimum_scale || aHeaderField == nsGkAtoms::viewport_maximum_scale || aHeaderField == nsGkAtoms::viewport_initial_scale || aHeaderField == nsGkAtoms::viewport_height || aHeaderField == nsGkAtoms::viewport_width || aHeaderField == nsGkAtoms::viewport_user_scalable) { mViewportType = Unknown; } } void nsDocument::TryChannelCharset(nsIChannel *aChannel, int32_t& aCharsetSource, nsACString& aCharset, nsHtml5TreeOpExecutor* aExecutor) { if (aChannel) { nsAutoCString charsetVal; nsresult rv = aChannel->GetContentCharset(charsetVal); if (NS_SUCCEEDED(rv)) { nsAutoCString preferred; if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) { aCharset = preferred; aCharsetSource = kCharsetFromChannel; return; } else if (aExecutor && !charsetVal.IsEmpty()) { aExecutor->ComplainAboutBogusProtocolCharset(this); } } } } already_AddRefed nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager, nsStyleSet* aStyleSet) { // Don't add anything here. Add it to |doCreateShell| instead. // This exists so that subclasses can pass other values for the 4th // parameter some of the time. return doCreateShell(aContext, aViewManager, aStyleSet, eCompatibility_FullStandards); } already_AddRefed nsDocument::doCreateShell(nsPresContext* aContext, nsViewManager* aViewManager, nsStyleSet* aStyleSet, nsCompatibility aCompatMode) { NS_ASSERTION(!mPresShell, "We have a presshell already!"); NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr); FillStyleSet(aStyleSet); nsRefPtr shell = new PresShell; shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode); // Note: we don't hold a ref to the shell (it holds a ref to us) mPresShell = shell; // Make sure to never paint if we belong to an invisible DocShell. nsCOMPtr docShell(mDocumentContainer); if (docShell && docShell->IsInvisible()) shell->SetNeverPainting(true); mExternalResourceMap.ShowViewers(); MaybeRescheduleAnimationFrameNotifications(); return shell.forget(); } void nsDocument::MaybeRescheduleAnimationFrameNotifications() { if (!mPresShell || !IsEventHandlingEnabled()) { // bail out for now, until one of those conditions changes return; } nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver(); if (!mFrameRequestCallbacks.IsEmpty()) { rd->ScheduleFrameRequestCallbacks(this); } } void nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks) { aCallbacks.AppendElements(mFrameRequestCallbacks); mFrameRequestCallbacks.Clear(); } PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey, uint32_t aData, void* userArg) { aKey->RequestDiscard(); return PL_DHASH_NEXT; } void nsDocument::DeleteShell() { mExternalResourceMap.HideViewers(); if (IsEventHandlingEnabled()) { RevokeAnimationFrameNotifications(); } // When our shell goes away, request that all our images be immediately // discarded, so we don't carry around decoded image data for a document we // no longer intend to paint. mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr); mPresShell = nullptr; } void nsDocument::RevokeAnimationFrameNotifications() { if (!mFrameRequestCallbacks.IsEmpty()) { mPresShell->GetPresContext()->RefreshDriver()-> RevokeFrameRequestCallbacks(this); } } static void SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) { SubDocMapEntry *e = static_cast(entry); NS_RELEASE(e->mKey); if (e->mSubDocument) { e->mSubDocument->SetParentDocument(nullptr); NS_RELEASE(e->mSubDocument); } } static bool SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key) { SubDocMapEntry *e = const_cast (static_cast(entry)); e->mKey = const_cast(static_cast(key)); NS_ADDREF(e->mKey); e->mSubDocument = nullptr; return true; } nsresult nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc) { NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED); if (!aSubDoc) { // aSubDoc is nullptr, remove the mapping if (mSubDocuments) { SubDocMapEntry *entry = static_cast (PL_DHashTableOperate(mSubDocuments, aElement, PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_BUSY(entry)) { PL_DHashTableRawRemove(mSubDocuments, entry); } } } else { if (!mSubDocuments) { // Create a new hashtable static const PLDHashTableOps hash_table_ops = { PL_DHashAllocTable, PL_DHashFreeTable, PL_DHashVoidPtrKeyStub, PL_DHashMatchEntryStub, PL_DHashMoveEntryStub, SubDocClearEntry, PL_DHashFinalizeStub, SubDocInitEntry }; mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr, sizeof(SubDocMapEntry)); if (!mSubDocuments) { return NS_ERROR_OUT_OF_MEMORY; } } // Add a mapping to the hash table SubDocMapEntry *entry = static_cast (PL_DHashTableOperate(mSubDocuments, aElement, PL_DHASH_ADD)); if (!entry) { return NS_ERROR_OUT_OF_MEMORY; } if (entry->mSubDocument) { entry->mSubDocument->SetParentDocument(nullptr); // Release the old sub document NS_RELEASE(entry->mSubDocument); } entry->mSubDocument = aSubDoc; NS_ADDREF(entry->mSubDocument); aSubDoc->SetParentDocument(this); } return NS_OK; } nsIDocument* nsDocument::GetSubDocumentFor(nsIContent *aContent) const { if (mSubDocuments && aContent->IsElement()) { SubDocMapEntry *entry = static_cast (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(), PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_BUSY(entry)) { return entry->mSubDocument; } } return nullptr; } static PLDHashOperator FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, void *arg) { SubDocMapEntry *entry = static_cast(hdr); FindContentData *data = static_cast(arg); if (entry->mSubDocument == data->mSubDocument) { data->mResult = entry->mKey; return PL_DHASH_STOP; } return PL_DHASH_NEXT; } Element* nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const { NS_ENSURE_TRUE(aDocument, nullptr); if (!mSubDocuments) { return nullptr; } FindContentData data(aDocument); PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data); return data.mResult; } bool nsDocument::IsNodeOfType(uint32_t aFlags) const { return !(aFlags & ~eDOCUMENT); } Element* nsIDocument::GetRootElement() const { return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ? mCachedRootElement : GetRootElementInternal(); } Element* nsDocument::GetRootElementInternal() const { // Loop backwards because any non-elements, such as doctypes and PIs // are likely to appear before the root element. uint32_t i; for (i = mChildren.ChildCount(); i > 0; --i) { nsIContent* child = mChildren.ChildAt(i - 1); if (child->IsElement()) { const_cast(this)->mCachedRootElement = child->AsElement(); return child->AsElement(); } } const_cast(this)->mCachedRootElement = nullptr; return nullptr; } nsIContent * nsDocument::GetChildAt(uint32_t aIndex) const { return mChildren.GetSafeChildAt(aIndex); } int32_t nsDocument::IndexOf(const nsINode* aPossibleChild) const { return mChildren.IndexOfChild(aPossibleChild); } uint32_t nsDocument::GetChildCount() const { return mChildren.ChildCount(); } nsIContent * const * nsDocument::GetChildArray(uint32_t* aChildCount) const { return mChildren.GetChildArray(aChildCount); } nsresult nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) { if (aKid->IsElement() && GetRootElement()) { NS_ERROR("Inserting element child when we already have one"); return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; } return doInsertChildAt(aKid, aIndex, aNotify, mChildren); } void nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify) { nsCOMPtr oldKid = GetChildAt(aIndex); if (!oldKid) { return; } if (oldKid->IsElement()) { // Destroy the link map up front before we mess with the child list. DestroyElementMaps(); } doRemoveChildAt(aIndex, aNotify, oldKid, mChildren); mCachedRootElement = nullptr; } void nsDocument::EnsureOnDemandBuiltInUASheet(CSSStyleSheet* aSheet) { // Contains() takes nsISupport*, so annoyingly we have to cast here if (mOnDemandBuiltInUASheets.Contains(static_cast(aSheet))) { return; } BeginUpdate(UPDATE_STYLE); AddOnDemandBuiltInUASheet(aSheet); EndUpdate(UPDATE_STYLE); } void nsDocument::AddOnDemandBuiltInUASheet(CSSStyleSheet* aSheet) { // Contains() takes nsISupport*, so annoyingly we have to cast here MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(static_cast(aSheet))); // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in // the same order that they should end up in the style set. mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet); if (aSheet->IsApplicable()) { // This is like |AddStyleSheetToStyleSets|, but for an agent sheet. nsCOMPtr shell = GetShell(); if (shell) { // Note that prepending here is necessary to make sure that html.css etc. // do not override Firefox OS/Mobile's content.css sheet. Maybe we should // have an insertion point to match the order of // nsDocumentViewer::CreateStyleSet though? shell->StyleSet()->PrependStyleSheet(nsStyleSet::eAgentSheet, aSheet); } } NotifyStyleSheetAdded(aSheet, false); } int32_t nsDocument::GetNumberOfStyleSheets() const { return mStyleSheets.Count(); } nsIStyleSheet* nsDocument::GetStyleSheetAt(int32_t aIndex) const { NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr); return mStyleSheets[aIndex]; } int32_t nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const { return mStyleSheets.IndexOf(aSheet); } void nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet) { nsCOMPtr shell = GetShell(); if (shell) { shell->StyleSet()->AddDocStyleSheet(aSheet, this); } } #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \ do { \ nsRefPtr cssSheet = do_QueryObject(aSheet); \ if (!cssSheet) { \ return; \ } \ \ className##Init init; \ init.mBubbles = true; \ init.mCancelable = true; \ init.mStylesheet = cssSheet; \ init.memberName = argName; \ \ nsRefPtr event = \ className::Constructor(this, NS_LITERAL_STRING(type), init); \ event->SetTrusted(true); \ event->SetTarget(this); \ nsRefPtr asyncDispatcher = \ new AsyncEventDispatcher(this, event); \ asyncDispatcher->mDispatchChromeOnly = true; \ asyncDispatcher->PostDOMEvent(); \ } while (0); void nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet)); if (StyleSheetChangeEventsEnabled()) { DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent, "StyleSheetAdded", mDocumentSheet, aDocumentSheet); } } void nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet)); if (StyleSheetChangeEventsEnabled()) { DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent, "StyleSheetRemoved", mDocumentSheet, aDocumentSheet); } } void nsDocument::AddStyleSheet(nsIStyleSheet* aSheet) { NS_PRECONDITION(aSheet, "null arg"); mStyleSheets.AppendObject(aSheet); aSheet->SetOwningDocument(this); if (aSheet->IsApplicable()) { AddStyleSheetToStyleSets(aSheet); } NotifyStyleSheetAdded(aSheet, true); } void nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet) { nsCOMPtr shell = GetShell(); if (shell) { shell->StyleSet()->RemoveDocStyleSheet(aSheet); } } void nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet) { NS_PRECONDITION(aSheet, "null arg"); nsCOMPtr sheet = aSheet; // hold ref so it won't die too soon if (!mStyleSheets.RemoveObject(aSheet)) { NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found"); return; } if (!mIsGoingAway) { if (aSheet->IsApplicable()) { RemoveStyleSheetFromStyleSets(aSheet); } NotifyStyleSheetRemoved(aSheet, true); } aSheet->SetOwningDocument(nullptr); } void nsDocument::UpdateStyleSheets(nsCOMArray& aOldSheets, nsCOMArray& aNewSheets) { BeginUpdate(UPDATE_STYLE); // XXX Need to set the sheet on the ownernode, if any NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(), "The lists must be the same length!"); int32_t count = aOldSheets.Count(); nsCOMPtr oldSheet; int32_t i; for (i = 0; i < count; ++i) { oldSheet = aOldSheets[i]; // First remove the old sheet. NS_ASSERTION(oldSheet, "None of the old sheets should be null"); int32_t oldIndex = mStyleSheets.IndexOf(oldSheet); RemoveStyleSheet(oldSheet); // This does the right notifications // Now put the new one in its place. If it's null, just ignore it. nsIStyleSheet* newSheet = aNewSheets[i]; if (newSheet) { mStyleSheets.InsertObjectAt(newSheet, oldIndex); newSheet->SetOwningDocument(this); if (newSheet->IsApplicable()) { AddStyleSheetToStyleSets(newSheet); } NotifyStyleSheetAdded(newSheet, true); } } EndUpdate(UPDATE_STYLE); } void nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex) { NS_PRECONDITION(aSheet, "null ptr"); mStyleSheets.InsertObjectAt(aSheet, aIndex); aSheet->SetOwningDocument(this); if (aSheet->IsApplicable()) { AddStyleSheetToStyleSets(aSheet); } NotifyStyleSheetAdded(aSheet, true); } void nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet, bool aApplicable) { NS_PRECONDITION(aSheet, "null arg"); // If we're actually in the document style sheet list if (-1 != mStyleSheets.IndexOf(aSheet)) { if (aApplicable) { AddStyleSheetToStyleSets(aSheet); } else { RemoveStyleSheetFromStyleSets(aSheet); } } // We have to always notify, since this will be called for sheets // that are children of sheets in our style set, as well as some // sheets for nsHTMLEditor. NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, (this, aSheet, aApplicable)); if (StyleSheetChangeEventsEnabled()) { DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent, "StyleSheetApplicableStateChanged", mApplicable, aApplicable); } if (!mSSApplicableStateNotificationPending) { nsRefPtr notification = NS_NewRunnableMethod(this, &nsDocument::NotifyStyleSheetApplicableStateChanged); mSSApplicableStateNotificationPending = NS_SUCCEEDED(NS_DispatchToCurrentThread(notification)); } } void nsDocument::NotifyStyleSheetApplicableStateChanged() { mSSApplicableStateNotificationPending = false; nsCOMPtr observerService = mozilla::services::GetObserverService(); if (observerService) { observerService->NotifyObservers(static_cast(this), "style-sheet-applicable-state-changed", nullptr); } } static nsStyleSet::sheetType ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType) { switch(aType) { case nsIDocument::eAgentSheet: return nsStyleSet::eAgentSheet; case nsIDocument::eUserSheet: return nsStyleSet::eUserSheet; case nsIDocument::eAuthorSheet: return nsStyleSet::eDocSheet; default: NS_ASSERTION(false, "wrong type"); // we must return something although this should never happen return nsStyleSet::eSheetTypeCount; } } static int32_t FindSheet(const nsCOMArray& aSheets, nsIURI* aSheetURI) { for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) { bool bEqual; nsIURI* uri = aSheets[i]->GetSheetURI(); if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual) return i; } return -1; } nsresult nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) { NS_PRECONDITION(aSheetURI, "null arg"); // Checking if we have loaded this one already. if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0) return NS_ERROR_INVALID_ARG; // Loading the sheet sync. nsRefPtr loader = new mozilla::css::Loader(); nsRefPtr sheet; nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet, true, getter_AddRefs(sheet)); NS_ENSURE_SUCCESS(rv, rv); sheet->SetOwningDocument(this); MOZ_ASSERT(sheet->IsApplicable()); return AddAdditionalStyleSheet(aType, sheet); } nsresult nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aSheet) { if (mAdditionalSheets[aType].Contains(aSheet)) return NS_ERROR_INVALID_ARG; if (!aSheet->IsApplicable()) return NS_ERROR_INVALID_ARG; mAdditionalSheets[aType].AppendObject(aSheet); BeginUpdate(UPDATE_STYLE); nsCOMPtr shell = GetShell(); if (shell) { nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); shell->StyleSet()->AppendStyleSheet(type, aSheet); } // Passing false, so documet.styleSheets.length will not be affected by // these additional sheets. NotifyStyleSheetAdded(aSheet, false); EndUpdate(UPDATE_STYLE); return NS_OK; } void nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) { MOZ_ASSERT(aSheetURI); nsCOMArray& sheets = mAdditionalSheets[aType]; int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI); if (i >= 0) { nsCOMPtr sheetRef = sheets[i]; sheets.RemoveObjectAt(i); BeginUpdate(UPDATE_STYLE); if (!mIsGoingAway) { MOZ_ASSERT(sheetRef->IsApplicable()); nsCOMPtr shell = GetShell(); if (shell) { nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); shell->StyleSet()->RemoveStyleSheet(type, sheetRef); } } // Passing false, so documet.styleSheets.length will not be affected by // these additional sheets. NotifyStyleSheetRemoved(sheetRef, false); EndUpdate(UPDATE_STYLE); sheetRef->SetOwningDocument(nullptr); } } nsIStyleSheet* nsDocument::FirstAdditionalAuthorSheet() { return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0); } nsIGlobalObject* nsDocument::GetScopeObject() const { nsCOMPtr scope(do_QueryReferent(mScopeObject)); return scope; } void nsDocument::SetScopeObject(nsIGlobalObject* aGlobal) { mScopeObject = do_GetWeakReference(aGlobal); if (aGlobal) { mHasHadScriptHandlingObject = true; } } #ifdef MOZ_EME static void CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME) { nsCOMPtr domMediaElem(do_QueryInterface(aSupports)); if (domMediaElem) { nsCOMPtr content(do_QueryInterface(domMediaElem)); MOZ_ASSERT(content, "aSupports is not a content"); HTMLMediaElement* mediaElem = static_cast(content.get()); bool* contains = static_cast(aContainsEME); if (mediaElem->GetMediaKeys()) { *contains = true; } } } bool nsDocument::ContainsEMEContent() { bool containsEME = false; EnumerateActivityObservers(CheckIfContainsEMEContent, static_cast(&containsEME)); return containsEME; } #endif // MOZ_EME static void NotifyActivityChanged(nsISupports *aSupports, void *aUnused) { nsCOMPtr domMediaElem(do_QueryInterface(aSupports)); if (domMediaElem) { nsCOMPtr content(do_QueryInterface(domMediaElem)); MOZ_ASSERT(content, "aSupports is not a content"); HTMLMediaElement* mediaElem = static_cast(content.get()); mediaElem->NotifyOwnerDocumentActivityChanged(); } nsCOMPtr objectLoadingContent(do_QueryInterface(aSupports)); if (objectLoadingContent) { nsObjectLoadingContent* olc = static_cast(objectLoadingContent.get()); olc->NotifyOwnerDocumentActivityChanged(); } nsCOMPtr objectDocumentActivity(do_QueryInterface(aSupports)); if (objectDocumentActivity) { objectDocumentActivity->NotifyOwnerDocumentActivityChanged(); } } void nsIDocument::SetContainer(nsDocShell* aContainer) { if (aContainer) { mDocumentContainer = aContainer; } else { mDocumentContainer = WeakPtr(); } EnumerateActivityObservers(NotifyActivityChanged, nullptr); if (!aContainer) { return; } // Get the Docshell if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) { // check if same type root nsCOMPtr sameTypeRoot; aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot)); NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!"); if (sameTypeRoot == aContainer) { static_cast(this)->SetIsTopLevelContentDocument(true); } } } nsISupports* nsIDocument::GetContainer() const { return static_cast(mDocumentContainer); } void nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) { #ifdef DEBUG { nsCOMPtr win(do_QueryInterface(aScriptGlobalObject)); NS_ASSERTION(!win || win->IsInnerWindow(), "Script global object must be an inner window!"); } #endif NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController || mAnimationController->IsPausedByType( nsSMILTimeContainer::PAUSE_PAGEHIDE | nsSMILTimeContainer::PAUSE_BEGIN), "Clearing window pointer while animations are unpaused"); if (mScriptGlobalObject && !aScriptGlobalObject) { // We're detaching from the window. We need to grab a pointer to // our layout history state now. mLayoutHistoryState = GetLayoutHistoryState(); if (mPresShell && !EventHandlingSuppressed()) { RevokeAnimationFrameNotifications(); } // Also make sure to remove our onload blocker now if we haven't done it yet if (mOnloadBlockCount != 0) { nsCOMPtr loadGroup = GetDocumentLoadGroup(); if (loadGroup) { loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK); } } } mScriptGlobalObject = aScriptGlobalObject; if (aScriptGlobalObject) { mHasHadScriptHandlingObject = true; mHasHadDefaultView = true; // Go back to using the docshell for the layout history state mLayoutHistoryState = nullptr; mScopeObject = do_GetWeakReference(aScriptGlobalObject); #ifdef DEBUG if (!mWillReparent) { // We really shouldn't have a wrapper here but if we do we need to make sure // it has the correct parent. JSObject *obj = GetWrapperPreserveColor(); if (obj) { JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject(); NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope, "Wrong scope, this is really bad!"); } } #endif if (mAllowDNSPrefetch) { nsCOMPtr docShell(mDocumentContainer); if (docShell) { #ifdef DEBUG nsCOMPtr webNav = do_GetInterface(aScriptGlobalObject); NS_ASSERTION(SameCOMIdentity(webNav, docShell), "Unexpected container or script global?"); #endif bool allowDNSPrefetch; docShell->GetAllowDNSPrefetch(&allowDNSPrefetch); mAllowDNSPrefetch = allowDNSPrefetch; } } MaybeRescheduleAnimationFrameNotifications(); mRegistry = new Registry(); } // Remember the pointer to our window (or lack there of), to avoid // having to QI every time it's asked for. nsCOMPtr window = do_QueryInterface(mScriptGlobalObject); mWindow = window; // Now that we know what our window is, we can flush the CSP errors to the // Web Console. We are flushing all messages that occured and were stored // in the queue prior to this point. FlushCSPWebConsoleErrorQueue(); nsCOMPtr internalChannel = do_QueryInterface(GetChannel()); if (internalChannel) { nsCOMArray messages; internalChannel->TakeAllSecurityMessages(messages); SendToConsole(messages); } // Set our visibility state, but do not fire the event. This is correct // because either we're coming out of bfcache (in which case IsVisible() will // still test false at this point and no state change will happen) or we're // doing the initial document load and don't want to fire the event for this // change. mVisibilityState = GetVisibilityState(); // The global in the template contents owner document should be the same. if (mTemplateContentsOwner && mTemplateContentsOwner != this) { mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject); } nsCOMPtr channel = GetChannel(); if (!mMaybeServiceWorkerControlled && channel) { nsLoadFlags loadFlags = 0; channel->GetLoadFlags(&loadFlags); // If we are shift-reloaded, don't associate with a ServiceWorker. // FIXME(nsm): Bug 1041339. if (loadFlags & nsIRequest::LOAD_BYPASS_CACHE) { NS_WARNING("Page was shift reloaded, skipping ServiceWorker control"); return; } nsCOMPtr swm = mozilla::services::GetServiceWorkerManager(); if (swm) { swm->MaybeStartControlling(this); mMaybeServiceWorkerControlled = true; } } } nsIScriptGlobalObject* nsDocument::GetScriptHandlingObjectInternal() const { MOZ_ASSERT(!mScriptGlobalObject, "Do not call this when mScriptGlobalObject is set!"); if (mHasHadDefaultView) { return nullptr; } nsCOMPtr scriptHandlingObject = do_QueryReferent(mScopeObject); nsCOMPtr win = do_QueryInterface(scriptHandlingObject); if (win) { NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!"); nsPIDOMWindow* outer = win->GetOuterWindow(); if (!outer || outer->GetCurrentInnerWindow() != win) { NS_WARNING("Wrong inner/outer window combination!"); return nullptr; } } return scriptHandlingObject; } void nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) { NS_ASSERTION(!mScriptGlobalObject || mScriptGlobalObject == aScriptObject, "Wrong script object!"); nsCOMPtr win = do_QueryInterface(aScriptObject); NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!"); if (aScriptObject) { mScopeObject = do_GetWeakReference(aScriptObject); mHasHadScriptHandlingObject = true; mHasHadDefaultView = false; } } bool nsDocument::IsTopLevelContentDocument() { return mIsTopLevelContentDocument; } void nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument) { mIsTopLevelContentDocument = aIsTopLevelContentDocument; } nsPIDOMWindow * nsDocument::GetWindowInternal() const { MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!"); // Let's use mScriptGlobalObject. Even if the document is already removed from // the docshell, the outer window might be still obtainable from the it. nsCOMPtr win; if (mRemovedFromDocShell) { // The docshell returns the outer window we are done. nsCOMPtr kungfuDeathGrip(mDocumentContainer); if (mDocumentContainer) { win = mDocumentContainer->GetWindow(); } } else { win = do_QueryInterface(mScriptGlobalObject); if (win) { // mScriptGlobalObject is always the inner window, let's get the outer. win = win->GetOuterWindow(); } } return win; } nsScriptLoader* nsDocument::ScriptLoader() { return mScriptLoader; } bool nsDocument::InternalAllowXULXBL() { if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) { mAllowXULXBL = eTriTrue; return true; } mAllowXULXBL = eTriFalse; return false; } // Note: We don't hold a reference to the document observer; we assume // that it has a live reference to the document. void nsDocument::AddObserver(nsIDocumentObserver* aObserver) { NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray::NoIndex, "Observer already in the list"); mObservers.AppendElement(aObserver); AddMutationObserver(aObserver); } bool nsDocument::RemoveObserver(nsIDocumentObserver* aObserver) { // If we're in the process of destroying the document (and we're // informing the observers of the destruction), don't remove the // observers from the list. This is not a big deal, since we // don't hold a live reference to the observers. if (!mInDestructor) { RemoveMutationObserver(aObserver); return mObservers.RemoveElement(aObserver); } return mObservers.Contains(aObserver); } void nsDocument::MaybeEndOutermostXBLUpdate() { // Only call BindingManager()->EndOutermostUpdate() when // we're not in an update and it is safe to run scripts. if (mUpdateNestLevel == 0 && mInXBLUpdate) { if (nsContentUtils::IsSafeToRunScript()) { mInXBLUpdate = false; BindingManager()->EndOutermostUpdate(); } else if (!mInDestructor) { nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate)); } } } void nsDocument::BeginUpdate(nsUpdateType aUpdateType) { if (mUpdateNestLevel == 0 && !mInXBLUpdate) { mInXBLUpdate = true; BindingManager()->BeginOutermostUpdate(); } ++mUpdateNestLevel; nsContentUtils::AddScriptBlocker(); NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType)); } void nsDocument::EndUpdate(nsUpdateType aUpdateType) { NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType)); nsContentUtils::RemoveScriptBlocker(); --mUpdateNestLevel; // This set of updates may have created XBL bindings. Let the // binding manager know we're done. MaybeEndOutermostXBLUpdate(); MaybeInitializeFinalizeFrameLoaders(); } void nsDocument::BeginLoad() { // Block onload here to prevent having to deal with blocking and // unblocking it while we know the document is loading. BlockOnload(); mDidFireDOMContentLoaded = false; BlockDOMContentLoaded(); if (mScriptLoader) { mScriptLoader->BeginDeferringScripts(); } NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this)); } void nsDocument::ReportEmptyGetElementByIdArg() { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), this, nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam"); } Element* nsDocument::GetElementById(const nsAString& aElementId) { if (!CheckGetElementByIdArg(aElementId)) { return nullptr; } nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); return entry ? entry->GetIdElement() : nullptr; } const nsSmallVoidArray* nsDocument::GetAllElementsForId(const nsAString& aElementId) const { if (aElementId.IsEmpty()) { return nullptr; } nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId); return entry ? entry->GetIdElements() : nullptr; } NS_IMETHODIMP nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn) { Element *content = GetElementById(aId); if (content) { return CallQueryInterface(content, aReturn); } *aReturn = nullptr; return NS_OK; } Element* nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, void* aData, bool aForImage) { nsDependentAtomString id(aID); if (!CheckGetElementByIdArg(id)) return nullptr; nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id); NS_ENSURE_TRUE(entry, nullptr); entry->AddContentChangeCallback(aObserver, aData, aForImage); return aForImage ? entry->GetImageIdElement() : entry->GetIdElement(); } void nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver, void* aData, bool aForImage) { nsDependentAtomString id(aID); if (!CheckGetElementByIdArg(id)) return; nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id); if (!entry) { return; } entry->RemoveContentChangeCallback(aObserver, aData, aForImage); } NS_IMETHODIMP nsDocument::MozSetImageElement(const nsAString& aImageElementId, nsIDOMElement* aImageElement) { nsCOMPtr el = do_QueryInterface(aImageElement); MozSetImageElement(aImageElementId, el); return NS_OK; } void nsDocument::MozSetImageElement(const nsAString& aImageElementId, Element* aElement) { if (aImageElementId.IsEmpty()) return; // Hold a script blocker while calling SetImageElement since that can call // out to id-observers nsAutoScriptBlocker scriptBlocker; nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId); if (entry) { entry->SetImageElement(aElement); if (entry->IsEmpty()) { mIdentifierMap.RemoveEntry(aImageElementId); } } } Element* nsDocument::LookupImageElement(const nsAString& aId) { if (aId.IsEmpty()) return nullptr; nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); return entry ? entry->GetImageIdElement() : nullptr; } void nsDocument::DispatchContentLoadedEvents() { // If you add early returns from this method, make sure you're // calling UnblockOnload properly. // Unpin references to preloaded images mPreloadingImages.Clear(); if (mTiming) { mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI()); } // Dispatch observer notification to notify observers document is interactive. nsCOMPtr os = mozilla::services::GetObserverService(); nsIPrincipal *principal = GetPrincipal(); os->NotifyObservers(static_cast(this), nsContentUtils::IsSystemPrincipal(principal) ? "chrome-document-interactive" : "content-document-interactive", nullptr); // Fire a DOM event notifying listeners that this document has been // loaded (excluding images and other loads initiated by this // document). nsContentUtils::DispatchTrustedEvent(this, static_cast(this), NS_LITERAL_STRING("DOMContentLoaded"), true, true); if (mTiming) { mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI()); } // If this document is a [i]frame, fire a DOMFrameContentLoaded // event on all parent documents notifying that the HTML (excluding // other external files such as images and stylesheets) in a frame // has finished loading. // target_frame is the [i]frame element that will be used as the // target for the event. It's the [i]frame whose content is done // loading. nsCOMPtr target_frame; if (mParentDocument) { target_frame = mParentDocument->FindContentForSubDocument(this); } if (target_frame) { nsCOMPtr parent = mParentDocument; do { nsCOMPtr domDoc = do_QueryInterface(parent); nsCOMPtr event; if (domDoc) { domDoc->CreateEvent(NS_LITERAL_STRING("Events"), getter_AddRefs(event)); } if (event) { event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true, true); event->SetTarget(target_frame); event->SetTrusted(true); // To dispatch this event we must manually call // EventDispatcher::Dispatch() on the ancestor document since the // target is not in the same document, so the event would never reach // the ancestor document if we used the normal event // dispatching code. WidgetEvent* innerEvent = event->GetInternalNSEvent(); if (innerEvent) { nsEventStatus status = nsEventStatus_eIgnore; nsIPresShell *shell = parent->GetShell(); if (shell) { nsRefPtr context = shell->GetPresContext(); if (context) { EventDispatcher::Dispatch(parent, context, innerEvent, event, &status); } } } } parent = parent->GetParentDocument(); } while (parent); } // If the document has a manifest attribute, fire a MozApplicationManifest // event. Element* root = GetRootElement(); if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) { nsContentUtils::DispatchChromeEvent(this, static_cast(this), NS_LITERAL_STRING("MozApplicationManifest"), true, true); } UnblockOnload(true); } void nsDocument::EndLoad() { // Drop the ref to our parser, if any, but keep hold of the sink so that we // can flush it from FlushPendingNotifications as needed. We might have to // do that to get a StartLayout() to happen. if (mParser) { mWeakSink = do_GetWeakReference(mParser->GetContentSink()); mParser = nullptr; } NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); UnblockDOMContentLoaded(); } void nsDocument::UnblockDOMContentLoaded() { MOZ_ASSERT(mBlockDOMContentLoaded); if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) { return; } mDidFireDOMContentLoaded = true; MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); if (!mSynchronousDOMContentLoaded) { nsRefPtr ev = NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); NS_DispatchToCurrentThread(ev); } else { DispatchContentLoadedEvents(); } } void nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) { NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(), "Someone forgot a scriptblocker"); NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged, (this, aContent, aStateMask)); } void nsDocument::DocumentStatesChanged(EventStates aStateMask) { // Invalidate our cached state. mGotDocumentState &= ~aStateMask; mDocumentState &= ~aStateMask; NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask)); } void nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet, nsIStyleRule* aOldStyleRule, nsIStyleRule* aNewStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (this, aSheet, aOldStyleRule, aNewStyleRule)); if (StyleSheetChangeEventsEnabled()) { nsCOMPtr rule = do_QueryInterface(aNewStyleRule); DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleChanged", mRule, rule ? rule->GetDOMRule() : nullptr); } } void nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet, nsIStyleRule* aStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (this, aSheet, aStyleRule)); if (StyleSheetChangeEventsEnabled()) { nsCOMPtr rule = do_QueryInterface(aStyleRule); DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleAdded", mRule, rule ? rule->GetDOMRule() : nullptr); } } void nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet, nsIStyleRule* aStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (this, aSheet, aStyleRule)); if (StyleSheetChangeEventsEnabled()) { nsCOMPtr rule = do_QueryInterface(aStyleRule); DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleRemoved", mRule, rule ? rule->GetDOMRule() : nullptr); } } #undef DO_STYLESHEET_NOTIFICATION // // nsIDOMDocument interface // DocumentType* nsIDocument::GetDoctype() const { for (nsIContent* child = GetFirstChild(); child; child = child->GetNextSibling()) { if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) { return static_cast(child); } } return nullptr; } NS_IMETHODIMP nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype) { MOZ_ASSERT(aDoctype); nsCOMPtr doctype = nsIDocument::GetDoctype(); doctype.forget(aDoctype); return NS_OK; } NS_IMETHODIMP nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation) { ErrorResult rv; *aImplementation = GetImplementation(rv); if (rv.Failed()) { MOZ_ASSERT(!*aImplementation); return rv.ErrorCode(); } NS_ADDREF(*aImplementation); return NS_OK; } DOMImplementation* nsDocument::GetImplementation(ErrorResult& rv) { if (!mDOMImplementation) { nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), "about:blank"); if (!uri) { rv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } bool hasHadScriptObject = true; nsIScriptGlobalObject* scriptObject = GetScriptHandlingObject(hasHadScriptObject); if (!scriptObject && hasHadScriptObject) { rv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } mDOMImplementation = new DOMImplementation(this, scriptObject ? scriptObject : GetScopeObject(), uri, uri); } return mDOMImplementation; } NS_IMETHODIMP nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement) { NS_ENSURE_ARG_POINTER(aDocumentElement); Element* root = GetRootElement(); if (root) { return CallQueryInterface(root, aDocumentElement); } *aDocumentElement = nullptr; return NS_OK; } NS_IMETHODIMP nsDocument::CreateElement(const nsAString& aTagName, nsIDOMElement** aReturn) { *aReturn = nullptr; ErrorResult rv; nsCOMPtr element = nsIDocument::CreateElement(aTagName, rv); NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode()); return CallQueryInterface(element, aReturn); } bool IsLowercaseASCII(const nsAString& aValue) { int32_t len = aValue.Length(); for (int32_t i = 0; i < len; ++i) { char16_t c = aValue[i]; if (!(0x0061 <= (c) && ((c) <= 0x007a))) { return false; } } return true; } already_AddRefed nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv) { rv = nsContentUtils::CheckQName(aTagName, false); if (rv.Failed()) { return nullptr; } bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName); nsAutoString lcTagName; if (needsLowercase) { nsContentUtils::ASCIIToLower(aTagName, lcTagName); } nsCOMPtr content; rv = CreateElem(needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, getter_AddRefs(content)); if (rv.Failed()) { return nullptr; } return dont_AddRef(content.forget().take()->AsElement()); } void nsDocument::SwizzleCustomElement(Element* aElement, const nsAString& aTypeExtension, uint32_t aNamespaceID, ErrorResult& rv) { nsCOMPtr typeAtom(do_GetAtom(aTypeExtension)); nsCOMPtr tagAtom = aElement->Tag(); if (!mRegistry || tagAtom == typeAtom) { return; } CustomElementDefinition* data; CustomElementHashKey key(aNamespaceID, typeAtom); if (!mRegistry->mCustomDefinitions.Get(&key, &data)) { // The type extension doesn't exist in the registry, // thus we don't need to swizzle, but it is possibly // an upgrade candidate. RegisterUnresolvedElement(aElement, typeAtom); return; } if (data->mLocalName != tagAtom) { // The element doesn't match the local name for the // definition, thus the element isn't a custom element // and we don't need to do anything more. return; } if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) { // Swizzling in the parser happens after the "is" attribute is added. aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true); } // Enqueuing the created callback will set the CustomElementData on the // element, causing prototype swizzling to occur in Element::WrapObject. EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data); } already_AddRefed nsDocument::CreateElement(const nsAString& aTagName, const nsAString& aTypeExtension, ErrorResult& rv) { nsRefPtr elem = nsIDocument::CreateElement(aTagName, rv); if (rv.Failed()) { return nullptr; } SwizzleCustomElement(elem, aTypeExtension, GetDefaultNamespaceID(), rv); if (rv.Failed()) { return nullptr; } return elem.forget(); } NS_IMETHODIMP nsDocument::CreateElementNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, nsIDOMElement** aReturn) { *aReturn = nullptr; ErrorResult rv; nsCOMPtr element = nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv); NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode()); return CallQueryInterface(element, aReturn); } already_AddRefed nsIDocument::CreateElementNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, ErrorResult& rv) { nsRefPtr nodeInfo; rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName, mNodeInfoManager, nsIDOMNode::ELEMENT_NODE, getter_AddRefs(nodeInfo)); if (rv.Failed()) { return nullptr; } nsCOMPtr element; rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(), NOT_FROM_PARSER); if (rv.Failed()) { return nullptr; } return element.forget(); } already_AddRefed nsDocument::CreateElementNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, const nsAString& aTypeExtension, ErrorResult& rv) { nsRefPtr elem = nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv); if (rv.Failed()) { return nullptr; } int32_t nameSpaceId = kNameSpaceID_Wildcard; if (!aNamespaceURI.EqualsLiteral("*")) { rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, nameSpaceId); if (rv.Failed()) { return nullptr; } } SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv); if (rv.Failed()) { return nullptr; } return elem.forget(); } NS_IMETHODIMP nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn) { *aReturn = nsIDocument::CreateTextNode(aData).take(); return NS_OK; } already_AddRefed nsIDocument::CreateTextNode(const nsAString& aData) const { nsRefPtr text = new nsTextNode(mNodeInfoManager); // Don't notify; this node is still being created. text->SetText(aData, false); return text.forget(); } NS_IMETHODIMP nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn) { *aReturn = nsIDocument::CreateDocumentFragment().take(); return NS_OK; } already_AddRefed nsIDocument::CreateDocumentFragment() const { nsRefPtr frag = new DocumentFragment(mNodeInfoManager); return frag.forget(); } NS_IMETHODIMP nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn) { *aReturn = nsIDocument::CreateComment(aData).take(); return NS_OK; } // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers. already_AddRefed nsIDocument::CreateComment(const nsAString& aData) const { nsRefPtr comment = new dom::Comment(mNodeInfoManager); // Don't notify; this node is still being created. comment->SetText(aData, false); return comment.forget(); } NS_IMETHODIMP nsDocument::CreateCDATASection(const nsAString& aData, nsIDOMCDATASection** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); ErrorResult rv; *aReturn = nsIDocument::CreateCDATASection(aData, rv).take(); return rv.ErrorCode(); } already_AddRefed nsIDocument::CreateCDATASection(const nsAString& aData, ErrorResult& rv) { if (IsHTML()) { rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) { rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); return nullptr; } nsRefPtr cdata = new CDATASection(mNodeInfoManager); // Don't notify; this node is still being created. cdata->SetText(aData, false); return cdata.forget(); } NS_IMETHODIMP nsDocument::CreateProcessingInstruction(const nsAString& aTarget, const nsAString& aData, nsIDOMProcessingInstruction** aReturn) { ErrorResult rv; *aReturn = nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take(); return rv.ErrorCode(); } already_AddRefed nsIDocument::CreateProcessingInstruction(const nsAString& aTarget, const nsAString& aData, ErrorResult& rv) const { nsresult res = nsContentUtils::CheckQName(aTarget, false); if (NS_FAILED(res)) { rv.Throw(res); return nullptr; } if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) { rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR); return nullptr; } nsRefPtr pi = NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData); return pi.forget(); } NS_IMETHODIMP nsDocument::CreateAttribute(const nsAString& aName, nsIDOMAttr** aReturn) { ErrorResult rv; *aReturn = nsIDocument::CreateAttribute(aName, rv).take(); return rv.ErrorCode(); } already_AddRefed nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv) { WarnOnceAbout(eCreateAttribute); if (!mNodeInfoManager) { rv.Throw(NS_ERROR_NOT_INITIALIZED); return nullptr; } nsresult res = nsContentUtils::CheckQName(aName, false); if (NS_FAILED(res)) { rv.Throw(res); return nullptr; } nsRefPtr nodeInfo; res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None, nsIDOMNode::ATTRIBUTE_NODE, getter_AddRefs(nodeInfo)); if (NS_FAILED(res)) { rv.Throw(res); return nullptr; } nsRefPtr attribute = new Attr(nullptr, nodeInfo.forget(), EmptyString(), false); return attribute.forget(); } NS_IMETHODIMP nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI, const nsAString & aQualifiedName, nsIDOMAttr **aResult) { ErrorResult rv; *aResult = nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take(); return rv.ErrorCode(); } already_AddRefed nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI, const nsAString& aQualifiedName, ErrorResult& rv) { WarnOnceAbout(eCreateAttributeNS); nsRefPtr nodeInfo; rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName, mNodeInfoManager, nsIDOMNode::ATTRIBUTE_NODE, getter_AddRefs(nodeInfo)); if (rv.Failed()) { return nullptr; } nsRefPtr attribute = new Attr(nullptr, nodeInfo.forget(), EmptyString(), true); return attribute.forget(); } bool nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); JS::Rooted global(aCx, JS_GetGlobalForObject(aCx, &args.callee())); nsCOMPtr window = do_QueryWrapper(aCx, global); MOZ_ASSERT(window, "Should have a non-null window"); nsDocument* document = static_cast(window->GetDoc()); // Function name is the type of the custom element. JSString* jsFunName = JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev())); nsAutoJSString elemName; if (!elemName.init(aCx, jsFunName)) { return true; } nsCOMPtr typeAtom(do_GetAtom(elemName)); CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom); CustomElementDefinition* definition; if (!document->mRegistry || !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) { return true; } nsDependentAtomString localName(definition->mLocalName); nsCOMPtr newElement; nsresult rv = document->CreateElem(localName, nullptr, definition->mNamespaceID, getter_AddRefs(newElement)); NS_ENSURE_SUCCESS(rv, true); ErrorResult errorResult; nsCOMPtr element = do_QueryInterface(newElement); document->SwizzleCustomElement(element, elemName, definition->mNamespaceID, errorResult); if (errorResult.Failed()) { return true; } rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval()); NS_ENSURE_SUCCESS(rv, true); return true; } bool nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject) { JS::Rooted obj(aCx, aObject); return Preferences::GetBool("dom.webcomponents.enabled") || IsInCertifiedApp(aCx, obj); } nsresult nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName) { if (!mRegistry) { return NS_OK; } mozilla::dom::NodeInfo* info = aElement->NodeInfo(); // Candidate may be a custom element through extension, // in which case the custom element type name will not // match the element tag name. e.g.