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