content/base/src/nsDocument.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et tw=78: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * Base class for all our document implementations.
     9  */
    11 #include "nsDocument.h"
    13 #include "mozilla/ArrayUtils.h"
    14 #include "mozilla/AutoRestore.h"
    15 #include "mozilla/DebugOnly.h"
    16 #include "mozilla/MemoryReporting.h"
    17 #include "mozilla/Likely.h"
    18 #include <algorithm>
    20 #ifdef MOZ_LOGGING
    21 // so we can get logging even in release builds
    22 #define FORCE_PR_LOG 1
    23 #endif
    24 #include "prlog.h"
    25 #include "plstr.h"
    26 #include "prprf.h"
    28 #include "mozilla/Telemetry.h"
    29 #include "nsIInterfaceRequestor.h"
    30 #include "nsIInterfaceRequestorUtils.h"
    31 #include "nsUnicharUtils.h"
    32 #include "nsContentList.h"
    33 #include "nsIObserver.h"
    34 #include "nsIBaseWindow.h"
    35 #include "mozilla/css/Loader.h"
    36 #include "mozilla/css/ImageLoader.h"
    37 #include "nsDocShell.h"
    38 #include "nsIDocShellTreeItem.h"
    39 #include "nsCOMArray.h"
    40 #include "nsDOMClassInfo.h"
    41 #include "nsCxPusher.h"
    43 #include "mozilla/AsyncEventDispatcher.h"
    44 #include "mozilla/BasicEvents.h"
    45 #include "mozilla/EventListenerManager.h"
    46 #include "mozilla/EventStateManager.h"
    47 #include "nsIDOMNodeFilter.h"
    49 #include "nsIDOMStyleSheet.h"
    50 #include "mozilla/dom/Attr.h"
    51 #include "nsIDOMDOMImplementation.h"
    52 #include "nsIDOMDocumentXBL.h"
    53 #include "mozilla/dom/Element.h"
    54 #include "nsGenericHTMLElement.h"
    55 #include "mozilla/dom/CDATASection.h"
    56 #include "mozilla/dom/ProcessingInstruction.h"
    57 #include "nsDOMString.h"
    58 #include "nsNodeUtils.h"
    59 #include "nsLayoutUtils.h" // for GetFrameForPoint
    60 #include "nsIFrame.h"
    61 #include "nsITabChild.h"
    63 #include "nsRange.h"
    64 #include "nsIDOMText.h"
    65 #include "nsIDOMComment.h"
    66 #include "mozilla/dom/DocumentType.h"
    67 #include "mozilla/dom/NodeIterator.h"
    68 #include "mozilla/dom/TreeWalker.h"
    70 #include "nsIServiceManager.h"
    72 #include "nsContentCID.h"
    73 #include "nsError.h"
    74 #include "nsPresShell.h"
    75 #include "nsPresContext.h"
    76 #include "nsIJSON.h"
    77 #include "nsThreadUtils.h"
    78 #include "nsNodeInfoManager.h"
    79 #include "nsIFileChannel.h"
    80 #include "nsIMultiPartChannel.h"
    81 #include "nsIRefreshURI.h"
    82 #include "nsIWebNavigation.h"
    83 #include "nsIScriptError.h"
    84 #include "nsStyleSheetService.h"
    86 #include "nsNetUtil.h"     // for NS_MakeAbsoluteURI
    88 #include "nsIScriptSecurityManager.h"
    89 #include "nsIPrincipal.h"
    91 #include "nsIDOMWindow.h"
    92 #include "nsPIDOMWindow.h"
    93 #include "nsIDOMElement.h"
    94 #include "nsFocusManager.h"
    96 // for radio group stuff
    97 #include "nsIDOMHTMLInputElement.h"
    98 #include "nsIRadioVisitor.h"
    99 #include "nsIFormControl.h"
   101 #include "nsBidiUtils.h"
   103 #include "nsIDOMUserDataHandler.h"
   104 #include "nsIDOMXPathExpression.h"
   105 #include "nsIDOMXPathNSResolver.h"
   106 #include "nsIParserService.h"
   107 #include "nsContentCreatorFunctions.h"
   109 #include "nsIScriptContext.h"
   110 #include "nsBindingManager.h"
   111 #include "nsIDOMHTMLDocument.h"
   112 #include "nsHTMLDocument.h"
   113 #include "nsIDOMHTMLFormElement.h"
   114 #include "nsIRequest.h"
   115 #include "nsHostObjectProtocolHandler.h"
   117 #include "nsCharsetAlias.h"
   118 #include "nsCharsetSource.h"
   119 #include "nsIParser.h"
   120 #include "nsIContentSink.h"
   122 #include "nsDateTimeFormatCID.h"
   123 #include "nsIDateTimeFormat.h"
   124 #include "mozilla/EventDispatcher.h"
   125 #include "mozilla/EventStates.h"
   126 #include "mozilla/InternalMutationEvent.h"
   127 #include "nsDOMCID.h"
   129 #include "jsapi.h"
   130 #include "nsIXPConnect.h"
   131 #include "nsCCUncollectableMarker.h"
   132 #include "nsIContentPolicy.h"
   133 #include "nsContentPolicyUtils.h"
   134 #include "nsICategoryManager.h"
   135 #include "nsIDocumentLoaderFactory.h"
   136 #include "nsIDocumentLoader.h"
   137 #include "nsIContentViewer.h"
   138 #include "nsIXMLContentSink.h"
   139 #include "nsIXULDocument.h"
   140 #include "nsIPrompt.h"
   141 #include "nsIPropertyBag2.h"
   142 #include "nsIDOMPageTransitionEvent.h"
   143 #include "nsIDOMStyleRuleChangeEvent.h"
   144 #include "nsIDOMStyleSheetChangeEvent.h"
   145 #include "nsIDOMStyleSheetApplicableStateChangeEvent.h"
   146 #include "nsJSUtils.h"
   147 #include "nsFrameLoader.h"
   148 #include "nsEscape.h"
   149 #include "nsObjectLoadingContent.h"
   150 #include "nsHtml5TreeOpExecutor.h"
   151 #include "nsIDOMElementReplaceEvent.h"
   152 #include "mozilla/dom/HTMLLinkElement.h"
   153 #include "mozilla/dom/HTMLMediaElement.h"
   154 #ifdef MOZ_MEDIA_NAVIGATOR
   155 #include "mozilla/MediaManager.h"
   156 #endif // MOZ_MEDIA_NAVIGATOR
   157 #ifdef MOZ_WEBRTC
   158 #include "IPeerConnection.h"
   159 #endif // MOZ_WEBRTC
   161 #include "mozAutoDocUpdate.h"
   162 #include "nsGlobalWindow.h"
   163 #include "mozilla/dom/EncodingUtils.h"
   164 #include "mozilla/dom/quota/QuotaManager.h"
   165 #include "nsDOMNavigationTiming.h"
   167 #include "nsSMILAnimationController.h"
   168 #include "imgIContainer.h"
   169 #include "nsSVGUtils.h"
   170 #include "SVGElementFactory.h"
   172 #include "nsRefreshDriver.h"
   174 // FOR CSP (autogenerated by xpidl)
   175 #include "nsIContentSecurityPolicy.h"
   176 #include "nsCSPService.h"
   177 #include "nsHTMLStyleSheet.h"
   178 #include "nsHTMLCSSStyleSheet.h"
   179 #include "mozilla/dom/DOMImplementation.h"
   180 #include "mozilla/dom/ShadowRoot.h"
   181 #include "mozilla/dom/Comment.h"
   182 #include "nsTextNode.h"
   183 #include "mozilla/dom/Link.h"
   184 #include "mozilla/dom/HTMLElementBinding.h"
   185 #include "mozilla/dom/SVGElementBinding.h"
   186 #include "nsXULAppAPI.h"
   187 #include "mozilla/dom/Touch.h"
   188 #include "mozilla/dom/TouchEvent.h"
   189 #include "GeneratedEvents.h"
   191 #include "mozilla/Preferences.h"
   193 #include "imgILoader.h"
   194 #include "imgRequestProxy.h"
   195 #include "nsWrapperCacheInlines.h"
   196 #include "nsSandboxFlags.h"
   197 #include "nsIAppsService.h"
   198 #include "mozilla/dom/BindingUtils.h"
   199 #include "mozilla/dom/DocumentFragment.h"
   200 #include "mozilla/dom/Event.h"
   201 #include "mozilla/dom/HTMLBodyElement.h"
   202 #include "mozilla/dom/HTMLInputElement.h"
   203 #include "mozilla/dom/NodeFilterBinding.h"
   204 #include "mozilla/dom/OwningNonNull.h"
   205 #include "mozilla/dom/UndoManager.h"
   206 #include "mozilla/dom/WebComponentsBinding.h"
   207 #include "nsFrame.h"
   208 #include "nsDOMCaretPosition.h"
   209 #include "nsIDOMHTMLTextAreaElement.h"
   210 #include "nsViewportInfo.h"
   211 #include "nsIContentPermissionPrompt.h"
   212 #include "mozilla/StaticPtr.h"
   213 #include "nsITextControlElement.h"
   214 #include "nsIDOMNSEditableElement.h"
   215 #include "nsIEditor.h"
   216 #include "nsIDOMCSSStyleRule.h"
   217 #include "mozilla/css/Rule.h"
   218 #include "nsIDOMLocation.h"
   219 #include "nsIHttpChannelInternal.h"
   220 #include "nsISecurityConsoleMessage.h"
   221 #include "nsCharSeparatedTokenizer.h"
   222 #include "mozilla/dom/XPathEvaluator.h"
   223 #include "nsIDocumentEncoder.h"
   224 #include "nsIStructuredCloneContainer.h"
   225 #include "nsIMutableArray.h"
   226 #include "nsContentPermissionHelper.h"
   227 #include "mozilla/dom/DOMStringList.h"
   228 #include "nsWindowMemoryReporter.h"
   230 using namespace mozilla;
   231 using namespace mozilla::dom;
   233 typedef nsTArray<Link*> LinkArray;
   235 #ifdef PR_LOGGING
   236 static PRLogModuleInfo* gDocumentLeakPRLog;
   237 static PRLogModuleInfo* gCspPRLog;
   238 #endif
   240 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
   242 nsIdentifierMapEntry::~nsIdentifierMapEntry()
   243 {
   244 }
   246 void
   247 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
   248 {
   249   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
   250                                      "mIdentifierMap mNameContentList");
   251   aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
   253   if (mImageElement) {
   254     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
   255                                        "mIdentifierMap mImageElement element");
   256     nsIContent* imageElement = mImageElement;
   257     aCallback->NoteXPCOMChild(imageElement);
   258   }
   259 }
   261 bool
   262 nsIdentifierMapEntry::IsEmpty()
   263 {
   264   return mIdContentList.Count() == 0 && !mNameContentList &&
   265          !mChangeCallbacks && !mImageElement;
   266 }
   268 Element*
   269 nsIdentifierMapEntry::GetIdElement()
   270 {
   271   return static_cast<Element*>(mIdContentList.SafeElementAt(0));
   272 }
   274 Element*
   275 nsIdentifierMapEntry::GetImageIdElement()
   276 {
   277   return mImageElement ? mImageElement.get() : GetIdElement();
   278 }
   280 void
   281 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
   282 {
   283   for (int32_t i = 0; i < mIdContentList.Count(); ++i) {
   284     aElements->AppendObject(static_cast<Element*>(mIdContentList[i]));
   285   }
   286 }
   288 void
   289 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
   290                                                void* aData, bool aForImage)
   291 {
   292   if (!mChangeCallbacks) {
   293     mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
   294     if (!mChangeCallbacks)
   295       return;
   296   }
   298   ChangeCallback cc = { aCallback, aData, aForImage };
   299   mChangeCallbacks->PutEntry(cc);
   300 }
   302 void
   303 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
   304                                                   void* aData, bool aForImage)
   305 {
   306   if (!mChangeCallbacks)
   307     return;
   308   ChangeCallback cc = { aCallback, aData, aForImage };
   309   mChangeCallbacks->RemoveEntry(cc);
   310   if (mChangeCallbacks->Count() == 0) {
   311     mChangeCallbacks = nullptr;
   312   }
   313 }
   315 struct FireChangeArgs {
   316   Element* mFrom;
   317   Element* mTo;
   318   bool mImageOnly;
   319   bool mHaveImageOverride;
   320 };
   322 namespace mozilla {
   323 namespace dom {
   325 static PLDHashOperator
   326 CustomDefinitionsTraverse(CustomElementHashKey* aKey,
   327                           CustomElementDefinition* aDefinition,
   328                           void* aArg)
   329 {
   330   nsCycleCollectionTraversalCallback* cb =
   331     static_cast<nsCycleCollectionTraversalCallback*>(aArg);
   333   nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks;
   335   if (callbacks->mAttributeChangedCallback.WasPassed()) {
   336     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   337       "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
   338     cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value());
   339   }
   341   if (callbacks->mCreatedCallback.WasPassed()) {
   342     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   343       "mCustomDefinitions->mCallbacks->mCreatedCallback");
   344     cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
   345   }
   347   if (callbacks->mAttachedCallback.WasPassed()) {
   348     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   349       "mCustomDefinitions->mCallbacks->mAttachedCallback");
   350     cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value());
   351   }
   353   if (callbacks->mDetachedCallback.WasPassed()) {
   354     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   355       "mCustomDefinitions->mCallbacks->mDetachedCallback");
   356     cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value());
   357   }
   359   return PL_DHASH_NEXT;
   360 }
   362 static PLDHashOperator
   363 CandidatesTraverse(CustomElementHashKey* aKey,
   364                    nsTArray<nsRefPtr<Element>>* aData,
   365                    void* aArg)
   366 {
   367   nsCycleCollectionTraversalCallback *cb =
   368     static_cast<nsCycleCollectionTraversalCallback*>(aArg);
   369   for (size_t i = 0; i < aData->Length(); ++i) {
   370     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element");
   371     cb->NoteXPCOMChild(aData->ElementAt(i));
   372   }
   373   return PL_DHASH_NEXT;
   374 }
   376 struct CustomDefinitionTraceArgs
   377 {
   378   const TraceCallbacks& callbacks;
   379   void* closure;
   380 };
   382 static PLDHashOperator
   383 CustomDefinitionTrace(CustomElementHashKey *aKey,
   384                       CustomElementDefinition *aData,
   385                       void *aArg)
   386 {
   387   CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg);
   388   MOZ_ASSERT(aData, "Definition must not be null");
   389   traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype",
   390                              traceArgs->closure);
   391   return PL_DHASH_NEXT;
   392 }
   394 NS_IMPL_CYCLE_COLLECTION_CLASS(Registry)
   396 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry)
   397   CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure };
   398   tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace,
   399                                         &customDefinitionArgs);
   400 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry)
   403   tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb);
   404   tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb);
   405   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   408 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry)
   409   tmp->mCustomDefinitions.Clear();
   410   tmp->mCandidatesMap.Clear();
   411 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   413 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry)
   414   NS_INTERFACE_MAP_ENTRY(nsISupports)
   415 NS_INTERFACE_MAP_END
   417 NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry)
   418 NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry)
   420 Registry::Registry()
   421 {
   422   mozilla::HoldJSObjects(this);
   423 }
   425 Registry::~Registry()
   426 {
   427   mozilla::DropJSObjects(this);
   428 }
   430 void
   431 CustomElementCallback::Call()
   432 {
   433   ErrorResult rv;
   434   switch (mType) {
   435     case nsIDocument::eCreated:
   436       // For the duration of this callback invocation, the element is being created
   437       // flag must be set to true.
   438       mOwnerData->mElementIsBeingCreated = true;
   439       mOwnerData->mCreatedCallbackInvoked = true;
   440       static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
   441       mOwnerData->mElementIsBeingCreated = false;
   442       break;
   443     case nsIDocument::eAttached:
   444       static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
   445       break;
   446     case nsIDocument::eDetached:
   447       static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
   448       break;
   449     case nsIDocument::eAttributeChanged:
   450       static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
   451         mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
   452       break;
   453   }
   454 }
   456 void
   457 CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
   458 {
   459   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
   460   aCb.NoteXPCOMChild(mThisObject);
   462   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
   463   aCb.NoteXPCOMChild(mCallback);
   464 }
   466 CustomElementCallback::CustomElementCallback(Element* aThisObject,
   467                                              nsIDocument::ElementCallbackType aCallbackType,
   468                                              mozilla::dom::CallbackFunction* aCallback,
   469                                              CustomElementData* aOwnerData)
   470   : mThisObject(aThisObject),
   471     mCallback(aCallback),
   472     mType(aCallbackType),
   473     mOwnerData(aOwnerData)
   474 {
   475 }
   477 CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
   478                                                  nsIAtom* aType,
   479                                                  nsIAtom* aLocalName,
   480                                                  LifecycleCallbacks* aCallbacks,
   481                                                  uint32_t aNamespaceID,
   482                                                  uint32_t aDocOrder)
   483   : mPrototype(aPrototype),
   484     mType(aType),
   485     mLocalName(aLocalName),
   486     mCallbacks(aCallbacks),
   487     mNamespaceID(aNamespaceID),
   488     mDocOrder(aDocOrder)
   489 {
   490 }
   492 CustomElementData::CustomElementData(nsIAtom* aType)
   493   : mType(aType),
   494     mCurrentCallback(-1),
   495     mElementIsBeingCreated(false),
   496     mCreatedCallbackInvoked(true),
   497     mAssociatedMicroTask(-1)
   498 {
   499 }
   501 void
   502 CustomElementData::RunCallbackQueue()
   503 {
   504   // Note: It's possible to re-enter this method.
   505   while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
   506     mCallbackQueue[mCurrentCallback]->Call();
   507   }
   509   mCallbackQueue.Clear();
   510   mCurrentCallback = -1;
   511 }
   513 } // namespace dom
   514 } // namespace mozilla
   516 static PLDHashOperator
   517 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
   518 {
   519   FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
   520   // Don't fire image changes for non-image observers, and don't fire element
   521   // changes for image observers when an image override is active.
   522   if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) :
   523                                args->mImageOnly)
   524     return PL_DHASH_NEXT;
   525   return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
   526       ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
   527 }
   529 void
   530 nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
   531                                           Element* aNewElement,
   532                                           bool aImageOnly)
   533 {
   534   if (!mChangeCallbacks)
   535     return;
   537   FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement };
   538   mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
   539 }
   541 bool
   542 nsIdentifierMapEntry::AddIdElement(Element* aElement)
   543 {
   544   NS_PRECONDITION(aElement, "Must have element");
   545   NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0,
   546                   "Why is null in our list?");
   548 #ifdef DEBUG
   549   Element* currentElement =
   550     static_cast<Element*>(mIdContentList.SafeElementAt(0));
   551 #endif
   553   // Common case
   554   if (mIdContentList.Count() == 0) {
   555     if (!mIdContentList.AppendElement(aElement))
   556       return false;
   557     NS_ASSERTION(currentElement == nullptr, "How did that happen?");
   558     FireChangeCallbacks(nullptr, aElement);
   559     return true;
   560   }
   562   // We seem to have multiple content nodes for the same id, or XUL is messing
   563   // with us.  Search for the right place to insert the content.
   564   int32_t start = 0;
   565   int32_t end = mIdContentList.Count();
   566   do {
   567     NS_ASSERTION(start < end, "Bogus start/end");
   569     int32_t cur = (start + end) / 2;
   570     NS_ASSERTION(cur >= start && cur < end, "What happened here?");
   572     Element* curElement = static_cast<Element*>(mIdContentList[cur]);
   573     if (curElement == aElement) {
   574       // Already in the list, so already in the right spot.  Get out of here.
   575       // XXXbz this only happens because XUL does all sorts of random
   576       // UpdateIdTableEntry calls.  Hate, hate, hate!
   577       return true;
   578     }
   580     if (nsContentUtils::PositionIsBefore(aElement, curElement)) {
   581       end = cur;
   582     } else {
   583       start = cur + 1;
   584     }
   585   } while (start != end);
   587   if (!mIdContentList.InsertElementAt(aElement, start))
   588     return false;
   590   if (start == 0) {
   591     Element* oldElement =
   592       static_cast<Element*>(mIdContentList.SafeElementAt(1));
   593     NS_ASSERTION(currentElement == oldElement, "How did that happen?");
   594     FireChangeCallbacks(oldElement, aElement);
   595   }
   596   return true;
   597 }
   599 void
   600 nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
   601 {
   602   NS_PRECONDITION(aElement, "Missing element");
   604   // This should only be called while the document is in an update.
   605   // Assertions near the call to this method guarantee this.
   607   // This could fire in OOM situations
   608   // Only assert this in HTML documents for now as XUL does all sorts of weird
   609   // crap.
   610   NS_ASSERTION(!aElement->OwnerDoc()->IsHTML() ||
   611                mIdContentList.IndexOf(aElement) >= 0,
   612                "Removing id entry that doesn't exist");
   614   // XXXbz should this ever Compact() I guess when all the content is gone
   615   // we'll just get cleaned up in the natural order of things...
   616   Element* currentElement =
   617     static_cast<Element*>(mIdContentList.SafeElementAt(0));
   618   mIdContentList.RemoveElement(aElement);
   619   if (currentElement == aElement) {
   620     FireChangeCallbacks(currentElement,
   621                         static_cast<Element*>(mIdContentList.SafeElementAt(0)));
   622   }
   623 }
   625 void
   626 nsIdentifierMapEntry::SetImageElement(Element* aElement)
   627 {
   628   Element* oldElement = GetImageIdElement();
   629   mImageElement = aElement;
   630   Element* newElement = GetImageIdElement();
   631   if (oldElement != newElement) {
   632     FireChangeCallbacks(oldElement, newElement, true);
   633   }
   634 }
   636 void
   637 nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
   638 {
   639   if (!mNameContentList) {
   640     mNameContentList = new nsSimpleContentList(aNode);
   641   }
   643   mNameContentList->AppendElement(aElement);
   644 }
   646 void
   647 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
   648 {
   649   if (mNameContentList) {
   650     mNameContentList->RemoveElement(aElement);
   651   }
   652 }
   654 bool
   655 nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
   656 {
   657   Element* idElement = GetIdElement();
   658   return idElement &&
   659          nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
   660 }
   662 // static
   663 size_t
   664 nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
   665                                           MallocSizeOf aMallocSizeOf,
   666                                           void*)
   667 {
   668   return aEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   669 }
   671 // Helper structs for the content->subdoc map
   673 class SubDocMapEntry : public PLDHashEntryHdr
   674 {
   675 public:
   676   // Both of these are strong references
   677   Element *mKey; // must be first, to look like PLDHashEntryStub
   678   nsIDocument *mSubDocument;
   679 };
   681 struct FindContentData
   682 {
   683   FindContentData(nsIDocument *aSubDoc)
   684     : mSubDocument(aSubDoc), mResult(nullptr)
   685   {
   686   }
   688   nsISupports *mSubDocument;
   689   Element *mResult;
   690 };
   693 /**
   694  * A struct that holds all the information about a radio group.
   695  */
   696 struct nsRadioGroupStruct
   697 {
   698   nsRadioGroupStruct()
   699     : mRequiredRadioCount(0)
   700     , mGroupSuffersFromValueMissing(false)
   701   {}
   703   /**
   704    * A strong pointer to the currently selected radio button.
   705    */
   706   nsRefPtr<HTMLInputElement> mSelectedRadioButton;
   707   nsCOMArray<nsIFormControl> mRadioButtons;
   708   uint32_t mRequiredRadioCount;
   709   bool mGroupSuffersFromValueMissing;
   710 };
   713 nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument)
   714 {
   715   mLength = -1;
   716   // Not reference counted to avoid circular references.
   717   // The document will tell us when its going away.
   718   mDocument = aDocument;
   719   mDocument->AddObserver(this);
   720 }
   722 nsDOMStyleSheetList::~nsDOMStyleSheetList()
   723 {
   724   if (mDocument) {
   725     mDocument->RemoveObserver(this);
   726   }
   727 }
   729 NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList,
   730                             nsIDocumentObserver,
   731                             nsIMutationObserver)
   733 uint32_t
   734 nsDOMStyleSheetList::Length()
   735 {
   736   if (!mDocument) {
   737     return 0;
   738   }
   740   // XXX Find the number and then cache it. We'll use the
   741   // observer notification to figure out if new ones have
   742   // been added or removed.
   743   if (-1 == mLength) {
   744     mLength = mDocument->GetNumberOfStyleSheets();
   746 #ifdef DEBUG
   747     int32_t i;
   748     for (i = 0; i < mLength; i++) {
   749       nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
   750       nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
   751       NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
   752     }
   753 #endif
   754   }
   755   return mLength;
   756 }
   758 nsCSSStyleSheet*
   759 nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
   760 {
   761   if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
   762     aFound = false;
   763     return nullptr;
   764   }
   766   aFound = true;
   767   nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
   768   NS_ASSERTION(sheet, "Must have a sheet");
   770   return static_cast<nsCSSStyleSheet*>(sheet);
   771 }
   773 void
   774 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
   775 {
   776   mDocument = nullptr;
   777 }
   779 void
   780 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
   781                                      nsIStyleSheet* aStyleSheet,
   782                                      bool aDocumentSheet)
   783 {
   784   if (aDocumentSheet && -1 != mLength) {
   785     nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
   786     if (domss) {
   787       mLength++;
   788     }
   789   }
   790 }
   792 void
   793 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
   794                                        nsIStyleSheet* aStyleSheet,
   795                                        bool aDocumentSheet)
   796 {
   797   if (aDocumentSheet && -1 != mLength) {
   798     nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
   799     if (domss) {
   800       mLength--;
   801     }
   802   }
   803 }
   805 // nsOnloadBlocker implementation
   806 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
   808 NS_IMETHODIMP
   809 nsOnloadBlocker::GetName(nsACString &aResult)
   810 {
   811   aResult.AssignLiteral("about:document-onload-blocker");
   812   return NS_OK;
   813 }
   815 NS_IMETHODIMP
   816 nsOnloadBlocker::IsPending(bool *_retval)
   817 {
   818   *_retval = true;
   819   return NS_OK;
   820 }
   822 NS_IMETHODIMP
   823 nsOnloadBlocker::GetStatus(nsresult *status)
   824 {
   825   *status = NS_OK;
   826   return NS_OK;
   827 }
   829 NS_IMETHODIMP
   830 nsOnloadBlocker::Cancel(nsresult status)
   831 {
   832   return NS_OK;
   833 }
   834 NS_IMETHODIMP
   835 nsOnloadBlocker::Suspend(void)
   836 {
   837   return NS_OK;
   838 }
   839 NS_IMETHODIMP
   840 nsOnloadBlocker::Resume(void)
   841 {
   842   return NS_OK;
   843 }
   845 NS_IMETHODIMP
   846 nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
   847 {
   848   *aLoadGroup = nullptr;
   849   return NS_OK;
   850 }
   852 NS_IMETHODIMP
   853 nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
   854 {
   855   return NS_OK;
   856 }
   858 NS_IMETHODIMP
   859 nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
   860 {
   861   *aLoadFlags = nsIRequest::LOAD_NORMAL;
   862   return NS_OK;
   863 }
   865 NS_IMETHODIMP
   866 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
   867 {
   868   return NS_OK;
   869 }
   871 // ==================================================================
   873 nsExternalResourceMap::nsExternalResourceMap()
   874   : mHaveShutDown(false)
   875 {
   876 }
   878 nsIDocument*
   879 nsExternalResourceMap::RequestResource(nsIURI* aURI,
   880                                        nsINode* aRequestingNode,
   881                                        nsDocument* aDisplayDocument,
   882                                        ExternalResourceLoad** aPendingLoad)
   883 {
   884   // If we ever start allowing non-same-origin loads here, we might need to do
   885   // something interesting with aRequestingPrincipal even for the hashtable
   886   // gets.
   887   NS_PRECONDITION(aURI, "Must have a URI");
   888   NS_PRECONDITION(aRequestingNode, "Must have a node");
   889   *aPendingLoad = nullptr;
   890   if (mHaveShutDown) {
   891     return nullptr;
   892   }
   894   // First, make sure we strip the ref from aURI.
   895   nsCOMPtr<nsIURI> clone;
   896   nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
   897   if (NS_FAILED(rv) || !clone) {
   898     return nullptr;
   899   }
   901   ExternalResource* resource;
   902   mMap.Get(clone, &resource);
   903   if (resource) {
   904     return resource->mDocument;
   905   }
   907   nsRefPtr<PendingLoad> load;
   908   mPendingLoads.Get(clone, getter_AddRefs(load));
   909   if (load) {
   910     load.forget(aPendingLoad);
   911     return nullptr;
   912   }
   914   load = new PendingLoad(aDisplayDocument);
   916   mPendingLoads.Put(clone, load);
   918   if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
   919     // Make sure we don't thrash things by trying this load again, since
   920     // chances are it failed for good reasons (security check, etc).
   921     AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
   922   } else {
   923     load.forget(aPendingLoad);
   924   }
   926   return nullptr;
   927 }
   929 struct
   930 nsExternalResourceEnumArgs
   931 {
   932   nsIDocument::nsSubDocEnumFunc callback;
   933   void *data;
   934 };
   936 static PLDHashOperator
   937 ExternalResourceEnumerator(nsIURI* aKey,
   938                            nsExternalResourceMap::ExternalResource* aData,
   939                            void* aClosure)
   940 {
   941   nsExternalResourceEnumArgs* args =
   942     static_cast<nsExternalResourceEnumArgs*>(aClosure);
   943   bool next =
   944     aData->mDocument ? args->callback(aData->mDocument, args->data) : true;
   945   return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
   946 }
   948 void
   949 nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
   950                                           void* aData)
   951 {
   952   nsExternalResourceEnumArgs args = { aCallback, aData };
   953   mMap.EnumerateRead(ExternalResourceEnumerator, &args);
   954 }
   956 static PLDHashOperator
   957 ExternalResourceTraverser(nsIURI* aKey,
   958                           nsExternalResourceMap::ExternalResource* aData,
   959                           void* aClosure)
   960 {
   961   nsCycleCollectionTraversalCallback *cb =
   962     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
   964   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   965                                      "mExternalResourceMap.mMap entry"
   966                                      "->mDocument");
   967   cb->NoteXPCOMChild(aData->mDocument);
   969   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   970                                      "mExternalResourceMap.mMap entry"
   971                                      "->mViewer");
   972   cb->NoteXPCOMChild(aData->mViewer);
   974   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
   975                                      "mExternalResourceMap.mMap entry"
   976                                      "->mLoadGroup");
   977   cb->NoteXPCOMChild(aData->mLoadGroup);
   979   return PL_DHASH_NEXT;
   980 }
   982 void
   983 nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
   984 {
   985   // mPendingLoads will get cleared out as the requests complete, so
   986   // no need to worry about those here.
   987   mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
   988 }
   990 static PLDHashOperator
   991 ExternalResourceHider(nsIURI* aKey,
   992                       nsExternalResourceMap::ExternalResource* aData,
   993                       void* aClosure)
   994 {
   995   if (aData->mViewer) {
   996     aData->mViewer->Hide();
   997   }
   998   return PL_DHASH_NEXT;
   999 }
  1001 void
  1002 nsExternalResourceMap::HideViewers()
  1004   mMap.EnumerateRead(ExternalResourceHider, nullptr);
  1007 static PLDHashOperator
  1008 ExternalResourceShower(nsIURI* aKey,
  1009                        nsExternalResourceMap::ExternalResource* aData,
  1010                        void* aClosure)
  1012   if (aData->mViewer) {
  1013     aData->mViewer->Show();
  1015   return PL_DHASH_NEXT;
  1018 void
  1019 nsExternalResourceMap::ShowViewers()
  1021   mMap.EnumerateRead(ExternalResourceShower, nullptr);
  1024 void
  1025 TransferZoomLevels(nsIDocument* aFromDoc,
  1026                    nsIDocument* aToDoc)
  1028   NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
  1029                     "transferring zoom levels from/to null doc");
  1031   nsIPresShell* fromShell = aFromDoc->GetShell();
  1032   if (!fromShell)
  1033     return;
  1035   nsPresContext* fromCtxt = fromShell->GetPresContext();
  1036   if (!fromCtxt)
  1037     return;
  1039   nsIPresShell* toShell = aToDoc->GetShell();
  1040   if (!toShell)
  1041     return;
  1043   nsPresContext* toCtxt = toShell->GetPresContext();
  1044   if (!toCtxt)
  1045     return;
  1047   toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
  1048   toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
  1049   toCtxt->SetTextZoom(fromCtxt->TextZoom());
  1052 void
  1053 TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
  1055   NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
  1056                     "transferring showing state from/to null doc");
  1058   if (aFromDoc->IsShowing()) {
  1059     aToDoc->OnPageShow(true, nullptr);
  1063 nsresult
  1064 nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
  1065                                            nsIContentViewer* aViewer,
  1066                                            nsILoadGroup* aLoadGroup,
  1067                                            nsIDocument* aDisplayDocument)
  1069   NS_PRECONDITION(aURI, "Unexpected call");
  1070   NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
  1071                   "Must have both or neither");
  1073   nsRefPtr<PendingLoad> load;
  1074   mPendingLoads.Get(aURI, getter_AddRefs(load));
  1075   mPendingLoads.Remove(aURI);
  1077   nsresult rv = NS_OK;
  1079   nsCOMPtr<nsIDocument> doc;
  1080   if (aViewer) {
  1081     doc = aViewer->GetDocument();
  1082     NS_ASSERTION(doc, "Must have a document");
  1084     nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
  1085     if (xulDoc) {
  1086       // We don't handle XUL stuff here yet.
  1087       rv = NS_ERROR_NOT_AVAILABLE;
  1088     } else {
  1089       doc->SetDisplayDocument(aDisplayDocument);
  1091       // Make sure that hiding our viewer will tear down its presentation.
  1092       aViewer->SetSticky(false);
  1094       rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
  1095       if (NS_SUCCEEDED(rv)) {
  1096         rv = aViewer->Open(nullptr, nullptr);
  1100     if (NS_FAILED(rv)) {
  1101       doc = nullptr;
  1102       aViewer = nullptr;
  1103       aLoadGroup = nullptr;
  1107   ExternalResource* newResource = new ExternalResource();
  1108   mMap.Put(aURI, newResource);
  1110   newResource->mDocument = doc;
  1111   newResource->mViewer = aViewer;
  1112   newResource->mLoadGroup = aLoadGroup;
  1113   if (doc) {
  1114     TransferZoomLevels(aDisplayDocument, doc);
  1115     TransferShowingState(aDisplayDocument, doc);
  1118   const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
  1119   for (uint32_t i = 0; i < obs.Length(); ++i) {
  1120     obs[i]->Observe(doc, "external-resource-document-created", nullptr);
  1123   return rv;
  1126 NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
  1127                   nsIStreamListener,
  1128                   nsIRequestObserver)
  1130 NS_IMETHODIMP
  1131 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
  1132                                                    nsISupports *aContext)
  1134   nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
  1135   if (map.HaveShutDown()) {
  1136     return NS_BINDING_ABORTED;
  1139   nsCOMPtr<nsIContentViewer> viewer;
  1140   nsCOMPtr<nsILoadGroup> loadGroup;
  1141   nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
  1142                             getter_AddRefs(loadGroup));
  1144   // Make sure to do this no matter what
  1145   nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
  1146                                          mDisplayDocument);
  1147   if (NS_FAILED(rv)) {
  1148     return rv;
  1150   if (NS_FAILED(rv2)) {
  1151     mTargetListener = nullptr;
  1152     return rv2;
  1155   return mTargetListener->OnStartRequest(aRequest, aContext);
  1158 nsresult
  1159 nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
  1160                                                 nsIContentViewer** aViewer,
  1161                                                 nsILoadGroup** aLoadGroup)
  1163   NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
  1164   *aViewer = nullptr;
  1165   *aLoadGroup = nullptr;
  1167   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
  1168   NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
  1170   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
  1171   if (httpChannel) {
  1172     bool requestSucceeded;
  1173     if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
  1174         !requestSucceeded) {
  1175       // Bail out on this load, since it looks like we have an HTTP error page
  1176       return NS_BINDING_ABORTED;
  1180   nsAutoCString type;
  1181   chan->GetContentType(type);
  1183   nsCOMPtr<nsILoadGroup> loadGroup;
  1184   chan->GetLoadGroup(getter_AddRefs(loadGroup));
  1186   // Give this document its own loadgroup
  1187   nsCOMPtr<nsILoadGroup> newLoadGroup =
  1188         do_CreateInstance(NS_LOADGROUP_CONTRACTID);
  1189   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
  1190   newLoadGroup->SetLoadGroup(loadGroup);
  1192   nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1193   loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
  1195   nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
  1196     new LoadgroupCallbacks(callbacks);
  1197   newLoadGroup->SetNotificationCallbacks(newCallbacks);
  1199   // This is some serious hackery cribbed from docshell
  1200   nsCOMPtr<nsICategoryManager> catMan =
  1201     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
  1202   NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
  1203   nsXPIDLCString contractId;
  1204   nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
  1205                                          getter_Copies(contractId));
  1206   NS_ENSURE_SUCCESS(rv, rv);
  1207   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
  1208     do_GetService(contractId);
  1209   NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
  1211   nsCOMPtr<nsIContentViewer> viewer;
  1212   nsCOMPtr<nsIStreamListener> listener;
  1213   rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
  1214                                         type.get(), nullptr, nullptr,
  1215                                         getter_AddRefs(listener),
  1216                                         getter_AddRefs(viewer));
  1217   NS_ENSURE_SUCCESS(rv, rv);
  1218   NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
  1220   nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
  1221   if (!parser) {
  1222     /// We don't want to deal with the various fake documents yet
  1223     return NS_ERROR_NOT_IMPLEMENTED;
  1226   // We can't handle HTML and other weird things here yet.
  1227   nsIContentSink* sink = parser->GetContentSink();
  1228   nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
  1229   if (!xmlSink) {
  1230     return NS_ERROR_NOT_IMPLEMENTED;
  1233   listener.swap(mTargetListener);
  1234   viewer.forget(aViewer);
  1235   newLoadGroup.forget(aLoadGroup);
  1236   return NS_OK;
  1239 NS_IMETHODIMP
  1240 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
  1241                                                     nsISupports* aContext,
  1242                                                     nsIInputStream* aStream,
  1243                                                     uint64_t aOffset,
  1244                                                     uint32_t aCount)
  1246   NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
  1247   if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
  1248     return NS_BINDING_ABORTED;
  1250   return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
  1251                                           aCount);
  1254 NS_IMETHODIMP
  1255 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
  1256                                                   nsISupports* aContext,
  1257                                                   nsresult aStatus)
  1259   // mTargetListener might be null if SetupViewer or AddExternalResource failed
  1260   if (mTargetListener) {
  1261     nsCOMPtr<nsIStreamListener> listener;
  1262     mTargetListener.swap(listener);
  1263     return listener->OnStopRequest(aRequest, aContext, aStatus);
  1266   return NS_OK;
  1269 nsresult
  1270 nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
  1271                                               nsINode* aRequestingNode)
  1273   NS_PRECONDITION(aURI, "Must have a URI");
  1274   NS_PRECONDITION(aRequestingNode, "Must have a node");
  1276   // Time to start a load.  First, the security checks.
  1278   nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal();
  1280   nsresult rv = nsContentUtils::GetSecurityManager()->
  1281     CheckLoadURIWithPrincipal(requestingPrincipal, aURI,
  1282                               nsIScriptSecurityManager::STANDARD);
  1283   NS_ENSURE_SUCCESS(rv, rv);
  1285   // Allow data URIs and other URI's that inherit their principal by passing
  1286   // true as the 3rd argument of CheckMayLoad, since we want
  1287   // to allow external resources from data URIs regardless of the difference
  1288   // in URI scheme.
  1289   rv = requestingPrincipal->CheckMayLoad(aURI, true, true);
  1290   NS_ENSURE_SUCCESS(rv, rv);
  1292   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  1293   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
  1294                                  aURI,
  1295                                  requestingPrincipal,
  1296                                  aRequestingNode,
  1297                                  EmptyCString(), //mime guess
  1298                                  nullptr,         //extra
  1299                                  &shouldLoad,
  1300                                  nsContentUtils::GetContentPolicy(),
  1301                                  nsContentUtils::GetSecurityManager());
  1302   if (NS_FAILED(rv)) return rv;
  1303   if (NS_CP_REJECTED(shouldLoad)) {
  1304     // Disallowed by content policy
  1305     return NS_ERROR_CONTENT_BLOCKED;
  1308   nsIDocument* doc = aRequestingNode->OwnerDoc();
  1310   nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
  1311   NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
  1313   nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
  1314   nsCOMPtr<nsIChannel> channel;
  1315   rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, loadGroup, req);
  1316   NS_ENSURE_SUCCESS(rv, rv);
  1318   mURI = aURI;
  1320   return channel->AsyncOpen(this, nullptr);
  1323 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
  1324                   nsIInterfaceRequestor)
  1326 #define IMPL_SHIM(_i) \
  1327   NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
  1329 IMPL_SHIM(nsILoadContext)
  1330 IMPL_SHIM(nsIProgressEventSink)
  1331 IMPL_SHIM(nsIChannelEventSink)
  1332 IMPL_SHIM(nsISecurityEventSink)
  1333 IMPL_SHIM(nsIApplicationCacheContainer)
  1335 #undef IMPL_SHIM
  1337 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
  1339 #define TRY_SHIM(_i)                                                       \
  1340   PR_BEGIN_MACRO                                                           \
  1341     if (IID_IS(_i)) {                                                      \
  1342       nsCOMPtr<_i> real = do_GetInterface(mCallbacks);                     \
  1343       if (!real) {                                                         \
  1344         return NS_NOINTERFACE;                                             \
  1345       }                                                                    \
  1346       nsCOMPtr<_i> shim = new _i##Shim(this, real);                        \
  1347       if (!shim) {                                                         \
  1348         return NS_ERROR_OUT_OF_MEMORY;                                     \
  1349       }                                                                    \
  1350       shim.forget(aSink);                                                  \
  1351       return NS_OK;                                                        \
  1352     }                                                                      \
  1353   PR_END_MACRO
  1355 NS_IMETHODIMP
  1356 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
  1357                                                         void **aSink)
  1359   if (mCallbacks &&
  1360       (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
  1361        IID_IS(nsITabChild))) {
  1362     return mCallbacks->GetInterface(aIID, aSink);
  1365   *aSink = nullptr;
  1367   TRY_SHIM(nsILoadContext);
  1368   TRY_SHIM(nsIProgressEventSink);
  1369   TRY_SHIM(nsIChannelEventSink);
  1370   TRY_SHIM(nsISecurityEventSink);
  1371   TRY_SHIM(nsIApplicationCacheContainer);
  1373   return NS_NOINTERFACE;
  1376 #undef TRY_SHIM
  1377 #undef IID_IS
  1379 nsExternalResourceMap::ExternalResource::~ExternalResource()
  1381   if (mViewer) {
  1382     mViewer->Close(nullptr);
  1383     mViewer->Destroy();
  1387 // ==================================================================
  1388 // =
  1389 // ==================================================================
  1391 // If we ever have an nsIDocumentObserver notification for stylesheet title
  1392 // changes we should update the list from that instead of overriding
  1393 // EnsureFresh.
  1394 class nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList
  1396 public:
  1397   nsDOMStyleSheetSetList(nsIDocument* aDocument);
  1399   void Disconnect()
  1401     mDocument = nullptr;
  1404   virtual void EnsureFresh() MOZ_OVERRIDE;
  1406 protected:
  1407   nsIDocument* mDocument;  // Our document; weak ref.  It'll let us know if it
  1408                            // dies.
  1409 };
  1411 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
  1412   : mDocument(aDocument)
  1414   NS_ASSERTION(mDocument, "Must have document!");
  1417 void
  1418 nsDOMStyleSheetSetList::EnsureFresh()
  1420   mNames.Clear();
  1422   if (!mDocument) {
  1423     return; // Spec says "no exceptions", and we have no style sets if we have
  1424             // no document, for sure
  1427   int32_t count = mDocument->GetNumberOfStyleSheets();
  1428   nsAutoString title;
  1429   for (int32_t index = 0; index < count; index++) {
  1430     nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
  1431     NS_ASSERTION(sheet, "Null sheet in sheet list!");
  1432     sheet->GetTitle(title);
  1433     if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
  1434       return;
  1439 // ==================================================================
  1440 nsIDocument::SelectorCache::SelectorCache()
  1441   : nsExpirationTracker<SelectorCacheKey, 4>(1000) { }
  1443 // CacheList takes ownership of aSelectorList.
  1444 void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector,
  1445                                            nsCSSSelectorList* aSelectorList)
  1447   SelectorCacheKey* key = new SelectorCacheKey(aSelector);
  1448   mTable.Put(key->mKey, aSelectorList);
  1449   AddObject(key);
  1452 class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable
  1454 public:
  1455   explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete)
  1456     : mSelector(aToDelete)
  1458     MOZ_COUNT_CTOR(SelectorCacheKeyDeleter);
  1461   ~SelectorCacheKeyDeleter()
  1463     MOZ_COUNT_DTOR(SelectorCacheKeyDeleter);
  1466   NS_IMETHOD Run()
  1468     return NS_OK;
  1471 private:
  1472   nsAutoPtr<SelectorCacheKey> mSelector;
  1473 };
  1475 void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
  1477   RemoveObject(aSelector);
  1478   mTable.Remove(aSelector->mKey);
  1479   nsCOMPtr<nsIRunnable> runnable = new SelectorCacheKeyDeleter(aSelector);
  1480   NS_DispatchToCurrentThread(runnable);
  1484 struct nsIDocument::FrameRequest
  1486   FrameRequest(const FrameRequestCallbackHolder& aCallback,
  1487                int32_t aHandle) :
  1488     mCallback(aCallback),
  1489     mHandle(aHandle)
  1490   {}
  1492   // Conversion operator so that we can append these to a
  1493   // FrameRequestCallbackList
  1494   operator const FrameRequestCallbackHolder& () const {
  1495     return mCallback;
  1498   // Comparator operators to allow RemoveElementSorted with an
  1499   // integer argument on arrays of FrameRequest
  1500   bool operator==(int32_t aHandle) const {
  1501     return mHandle == aHandle;
  1503   bool operator<(int32_t aHandle) const {
  1504     return mHandle < aHandle;
  1507   FrameRequestCallbackHolder mCallback;
  1508   int32_t mHandle;
  1509 };
  1511 static already_AddRefed<nsINodeInfo> nullNodeInfo(nullptr);
  1513 // ==================================================================
  1514 // =
  1515 // ==================================================================
  1516 nsIDocument::nsIDocument()
  1517   : nsINode(nullNodeInfo),
  1518     mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
  1519     mNodeInfoManager(nullptr),
  1520     mCompatMode(eCompatibility_FullStandards),
  1521     mVisibilityState(dom::VisibilityState::Hidden),
  1522     mIsInitialDocumentInWindow(false),
  1523     mMayStartLayout(true),
  1524     mVisible(true),
  1525     mRemovedFromDocShell(false),
  1526     // mAllowDNSPrefetch starts true, so that we can always reliably && it
  1527     // with various values that might disable it.  Since we never prefetch
  1528     // unless we get a window, and in that case the docshell value will get
  1529     // &&-ed in, this is safe.
  1530     mAllowDNSPrefetch(true),
  1531     mIsBeingUsedAsImage(false),
  1532     mHasLinksToUpdate(false),
  1533     mPartID(0),
  1534     mDidFireDOMContentLoaded(true)
  1536   SetInDocument();
  1539 // NOTE! nsDocument::operator new() zeroes out all members, so don't
  1540 // bother initializing members to 0.
  1542 nsDocument::nsDocument(const char* aContentType)
  1543   : nsIDocument()
  1544   , mAnimatingImages(true)
  1545   , mViewportType(Unknown)
  1547   SetContentTypeInternal(nsDependentCString(aContentType));
  1549 #ifdef PR_LOGGING
  1550   if (!gDocumentLeakPRLog)
  1551     gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
  1553   if (gDocumentLeakPRLog)
  1554     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
  1555            ("DOCUMENT %p created", this));
  1557   if (!gCspPRLog)
  1558     gCspPRLog = PR_NewLogModule("CSP");
  1559 #endif
  1561   // Start out mLastStyleSheetSet as null, per spec
  1562   SetDOMStringToNull(mLastStyleSheetSet);
  1564   if (sProcessingStack.empty()) {
  1565     sProcessingStack.construct();
  1566     // Add the base queue sentinel to the processing stack.
  1567     sProcessingStack.ref().AppendElement((CustomElementData*) nullptr);
  1571 static PLDHashOperator
  1572 ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
  1574   if (aBoxObject) {
  1575     aBoxObject->Clear();
  1577   return PL_DHASH_NEXT;
  1580 nsIDocument::~nsIDocument()
  1582   if (mNodeInfoManager) {
  1583     mNodeInfoManager->DropDocumentReference();
  1588 nsDocument::~nsDocument()
  1590 #ifdef PR_LOGGING
  1591   if (gDocumentLeakPRLog)
  1592     PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
  1593            ("DOCUMENT %p destroyed", this));
  1594 #endif
  1596   NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
  1598   if (IsTopLevelContentDocument()) {
  1599     //don't report for about: pages
  1600     nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
  1601     nsCOMPtr<nsIURI> uri;
  1602     principal->GetURI(getter_AddRefs(uri));
  1603     bool isAboutScheme = true;
  1604     if (uri) {
  1605       uri->SchemeIs("about", &isAboutScheme);
  1608     if (!isAboutScheme) {
  1609       // Record the page load
  1610       uint32_t pageLoaded = 1;
  1611       Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
  1612       // Record the mixed content status of the docshell in Telemetry
  1613       enum {
  1614         NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
  1615         MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
  1616         MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
  1617         MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
  1618       };
  1620       bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
  1621       bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
  1623       bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
  1624       bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
  1626       bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
  1627       bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
  1629       uint32_t mixedContentLevel = NO_MIXED_CONTENT;
  1630       if (hasMixedDisplay && hasMixedActive) {
  1631         mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
  1632       } else if (hasMixedActive){
  1633         mixedContentLevel = MIXED_ACTIVE_CONTENT;
  1634       } else if (hasMixedDisplay) {
  1635         mixedContentLevel = MIXED_DISPLAY_CONTENT;
  1637       Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
  1641   mInDestructor = true;
  1642   mInUnlinkOrDeletion = true;
  1644   mRegistry = nullptr;
  1646   mozilla::DropJSObjects(this);
  1648   // Clear mObservers to keep it in sync with the mutationobserver list
  1649   mObservers.Clear();
  1651   if (mStyleSheetSetList) {
  1652     mStyleSheetSetList->Disconnect();
  1655   if (mAnimationController) {
  1656     mAnimationController->Disconnect();
  1659   mParentDocument = nullptr;
  1661   // Kill the subdocument map, doing this will release its strong
  1662   // references, if any.
  1663   if (mSubDocuments) {
  1664     PL_DHashTableDestroy(mSubDocuments);
  1666     mSubDocuments = nullptr;
  1669   // Destroy link map now so we don't waste time removing
  1670   // links one by one
  1671   DestroyElementMaps();
  1673   nsAutoScriptBlocker scriptBlocker;
  1675   int32_t indx; // must be signed
  1676   uint32_t count = mChildren.ChildCount();
  1677   for (indx = int32_t(count) - 1; indx >= 0; --indx) {
  1678     mChildren.ChildAt(indx)->UnbindFromTree();
  1679     mChildren.RemoveChildAt(indx);
  1681   mFirstChild = nullptr;
  1682   mCachedRootElement = nullptr;
  1684   // Let the stylesheets know we're going away
  1685   indx = mStyleSheets.Count();
  1686   while (--indx >= 0) {
  1687     mStyleSheets[indx]->SetOwningDocument(nullptr);
  1689   indx = mCatalogSheets.Count();
  1690   while (--indx >= 0) {
  1691     static_cast<nsCSSStyleSheet*>(mCatalogSheets[indx])->SetOwningNode(nullptr);
  1692     mCatalogSheets[indx]->SetOwningDocument(nullptr);
  1694   if (mAttrStyleSheet) {
  1695     mAttrStyleSheet->SetOwningDocument(nullptr);
  1698   if (mListenerManager) {
  1699     mListenerManager->Disconnect();
  1700     UnsetFlags(NODE_HAS_LISTENERMANAGER);
  1703   if (mScriptLoader) {
  1704     mScriptLoader->DropDocumentReference();
  1707   if (mCSSLoader) {
  1708     // Could be null here if Init() failed
  1709     mCSSLoader->DropDocumentReference();
  1712   if (mStyleImageLoader) {
  1713     mStyleImageLoader->DropDocumentReference();
  1716   delete mHeaderData;
  1718   if (mBoxObjectTable) {
  1719     mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
  1720     delete mBoxObjectTable;
  1723   mPendingTitleChangeEvent.Revoke();
  1725   for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) {
  1726     nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]);
  1729   // We don't want to leave residual locks on images. Make sure we're in an
  1730   // unlocked state, and then clear the table.
  1731   SetImageLockingState(false);
  1732   mImageTracker.Clear();
  1734   mPlugins.Clear();
  1737 NS_INTERFACE_TABLE_HEAD(nsDocument)
  1738   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  1739   NS_INTERFACE_TABLE_BEGIN
  1740     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
  1741     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
  1742     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
  1743     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument)
  1744     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode)
  1745     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
  1746     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
  1747     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
  1748     NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
  1749     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
  1750     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
  1751     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
  1752     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
  1753     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver)
  1754     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator)
  1755   NS_INTERFACE_TABLE_END
  1756   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
  1757   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
  1758                                  new nsNode3Tearoff(this))
  1759 NS_INTERFACE_MAP_END
  1762 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
  1763 NS_IMETHODIMP_(MozExternalRefCountType)
  1764 nsDocument::Release()
  1766   NS_PRECONDITION(0 != mRefCnt, "dup release");
  1767   NS_ASSERT_OWNINGTHREAD(nsDocument);
  1768   nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
  1769   bool shouldDelete = false;
  1770   nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
  1771   NS_LOG_RELEASE(this, count, "nsDocument");
  1772   if (count == 0) {
  1773     if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
  1774       mNeedsReleaseAfterStackRefCntRelease = true;
  1775       NS_ADDREF_THIS();
  1776       return mRefCnt.get();
  1778     mRefCnt.incr(base);
  1779     nsNodeUtils::LastRelease(this);
  1780     mRefCnt.decr(base);
  1781     if (shouldDelete) {
  1782       mRefCnt.stabilizeForDeletion();
  1783       DeleteCycleCollectable();
  1786   return count;
  1789 NS_IMETHODIMP_(void)
  1790 nsDocument::DeleteCycleCollectable()
  1792   delete this;
  1795 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
  1796   if (Element::CanSkip(tmp, aRemovingAllowed)) {
  1797     EventListenerManager* elm = tmp->GetExistingListenerManager();
  1798     if (elm) {
  1799       elm->MarkForCC();
  1801     return true;
  1803 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
  1805 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
  1806   return Element::CanSkipInCC(tmp);
  1807 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
  1809 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
  1810   return Element::CanSkipThis(tmp);
  1811 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
  1813 static PLDHashOperator
  1814 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
  1815                 void *arg)
  1817   SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
  1818   nsCycleCollectionTraversalCallback *cb =
  1819     static_cast<nsCycleCollectionTraversalCallback*>(arg);
  1821   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
  1822   cb->NoteXPCOMChild(entry->mKey);
  1823   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
  1824   cb->NoteXPCOMChild(entry->mSubDocument);
  1826   return PL_DHASH_NEXT;
  1829 static PLDHashOperator
  1830 RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
  1831                      void* aClosure)
  1833   nsCycleCollectionTraversalCallback *cb =
  1834     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
  1836   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
  1837                                    "mRadioGroups entry->mSelectedRadioButton");
  1838   cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton));
  1840   uint32_t i, count = aData->mRadioButtons.Count();
  1841   for (i = 0; i < count; ++i) {
  1842     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
  1843                                        "mRadioGroups entry->mRadioButtons[i]");
  1844     cb->NoteXPCOMChild(aData->mRadioButtons[i]);
  1847   return PL_DHASH_NEXT;
  1850 static PLDHashOperator
  1851 BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg)
  1853   nsCycleCollectionTraversalCallback *cb =
  1854     static_cast<nsCycleCollectionTraversalCallback*>(userArg);
  1856   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
  1857   cb->NoteXPCOMChild(boxObject);
  1859   return PL_DHASH_NEXT;
  1862 static PLDHashOperator
  1863 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
  1865   nsCycleCollectionTraversalCallback *cb =
  1866     static_cast<nsCycleCollectionTraversalCallback*>(aArg);
  1867   aEntry->Traverse(cb);
  1868   return PL_DHASH_NEXT;
  1871 static const char* kNSURIs[] = {
  1872   "([none])",
  1873   "(xmlns)",
  1874   "(xml)",
  1875   "(xhtml)",
  1876   "(XLink)",
  1877   "(XSLT)",
  1878   "(XBL)",
  1879   "(MathML)",
  1880   "(RDF)",
  1881   "(XUL)"
  1882 };
  1884 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
  1885   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
  1886     char name[512];
  1887     nsAutoCString loadedAsData;
  1888     if (tmp->IsLoadedAsData()) {
  1889       loadedAsData.AssignLiteral("data");
  1890     } else {
  1891       loadedAsData.AssignLiteral("normal");
  1893     uint32_t nsid = tmp->GetDefaultNamespaceID();
  1894     nsAutoCString uri;
  1895     if (tmp->mDocumentURI)
  1896       tmp->mDocumentURI->GetSpec(uri);
  1897     if (nsid < ArrayLength(kNSURIs)) {
  1898       PR_snprintf(name, sizeof(name), "nsDocument %s %s %s",
  1899                   loadedAsData.get(), kNSURIs[nsid], uri.get());
  1901     else {
  1902       PR_snprintf(name, sizeof(name), "nsDocument %s %s",
  1903                   loadedAsData.get(), uri.get());
  1905     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
  1907   else {
  1908     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
  1911   // Always need to traverse script objects, so do that before we check
  1912   // if we're uncollectable.
  1913   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
  1915   if (!nsINode::Traverse(tmp, cb)) {
  1916     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
  1919   tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
  1921   tmp->mExternalResourceMap.Traverse(&cb);
  1923   // Traverse the mChildren nsAttrAndChildArray.
  1924   for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
  1925     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
  1926     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
  1929   // Traverse all nsIDocument pointer members.
  1930   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
  1931   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
  1933   // Traverse all nsDocument nsCOMPtrs.
  1934   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
  1935   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
  1936   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
  1937   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
  1938   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
  1939   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
  1941   tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
  1943   // The boxobject for an element will only exist as long as it's in the
  1944   // document, so we'll traverse the table here instead of from the element.
  1945   if (tmp->mBoxObjectTable) {
  1946     tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
  1949   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
  1950   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
  1951   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator)
  1952   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
  1953   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
  1954   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
  1955   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
  1956   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
  1957   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
  1958   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
  1959   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
  1960   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
  1961   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
  1962   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
  1963   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
  1965   // Traverse all our nsCOMArrays.
  1966   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
  1967   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCatalogSheets)
  1968   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
  1970   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
  1971     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
  1972     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
  1975   // Traverse animation components
  1976   if (tmp->mAnimationController) {
  1977     tmp->mAnimationController->Traverse(&cb);
  1980   if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
  1981     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
  1984   if (tmp->mCSSLoader) {
  1985     tmp->mCSSLoader->TraverseCachedSheets(cb);
  1988   for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
  1989     nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb);
  1991 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  1993 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
  1995 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
  1996   if (tmp->PreservingWrapper()) {
  1997     NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
  1999   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
  2000 NS_IMPL_CYCLE_COLLECTION_TRACE_END
  2003 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
  2004   tmp->mInUnlinkOrDeletion = true;
  2006   // Clear out our external resources
  2007   tmp->mExternalResourceMap.Shutdown();
  2009   nsAutoScriptBlocker scriptBlocker;
  2011   nsINode::Unlink(tmp);
  2013   // Unlink the mChildren nsAttrAndChildArray.
  2014   for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1;
  2015        indx >= 0; --indx) {
  2016     tmp->mChildren.ChildAt(indx)->UnbindFromTree();
  2017     tmp->mChildren.RemoveChildAt(indx);
  2019   tmp->mFirstChild = nullptr;
  2021   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator)
  2022   tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
  2023   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
  2024   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
  2025   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
  2026   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
  2027   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
  2028   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
  2029   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
  2030   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
  2031   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
  2032   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
  2034   tmp->mParentDocument = nullptr;
  2036   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
  2039   if (tmp->mBoxObjectTable) {
  2040    tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
  2041    delete tmp->mBoxObjectTable;
  2042    tmp->mBoxObjectTable = nullptr;
  2045   if (tmp->mListenerManager) {
  2046     tmp->mListenerManager->Disconnect();
  2047     tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
  2048     tmp->mListenerManager = nullptr;
  2051   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
  2053   if (tmp->mStyleSheetSetList) {
  2054     tmp->mStyleSheetSetList->Disconnect();
  2055     tmp->mStyleSheetSetList = nullptr;
  2058   if (tmp->mSubDocuments) {
  2059     PL_DHashTableDestroy(tmp->mSubDocuments);
  2060     tmp->mSubDocuments = nullptr;
  2063   tmp->mFrameRequestCallbacks.Clear();
  2065   tmp->mRadioGroups.Clear();
  2067   // nsDocument has a pretty complex destructor, so we're going to
  2068   // assume that *most* cycles you actually want to break somewhere
  2069   // else, and not unlink an awful lot here.
  2071   tmp->mIdentifierMap.Clear();
  2072   tmp->mExpandoAndGeneration.Unlink();
  2074   if (tmp->mAnimationController) {
  2075     tmp->mAnimationController->Unlink();
  2078   tmp->mPendingTitleChangeEvent.Revoke();
  2080   if (tmp->mCSSLoader) {
  2081     tmp->mCSSLoader->UnlinkCachedSheets();
  2084   for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
  2085     nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]);
  2088   tmp->mInUnlinkOrDeletion = false;
  2089 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  2091 static bool sPrefsInitialized = false;
  2092 static uint32_t sOnloadDecodeLimit = 0;
  2094 nsresult
  2095 nsDocument::Init()
  2097   if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
  2098     return NS_ERROR_ALREADY_INITIALIZED;
  2101   if (!sPrefsInitialized) {
  2102     sPrefsInitialized = true;
  2103     Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0);
  2106   // Force initialization.
  2107   nsINode::nsSlots* slots = Slots();
  2109   // Prepend self as mutation-observer whether we need it or not (some
  2110   // subclasses currently do, other don't). This is because the code in
  2111   // nsNodeUtils always notifies the first observer first, expecting the
  2112   // first observer to be the document.
  2113   NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
  2114                  NS_ERROR_OUT_OF_MEMORY);
  2117   mOnloadBlocker = new nsOnloadBlocker();
  2118   mCSSLoader = new mozilla::css::Loader(this);
  2119   // Assume we're not quirky, until we know otherwise
  2120   mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
  2122   mStyleImageLoader = new mozilla::css::ImageLoader(this);
  2124   mNodeInfoManager = new nsNodeInfoManager();
  2125   nsresult rv = mNodeInfoManager->Init(this);
  2126   NS_ENSURE_SUCCESS(rv, rv);
  2128   // mNodeInfo keeps NodeInfoManager alive!
  2129   mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
  2130   NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
  2131   NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
  2132                     "Bad NodeType in aNodeInfo");
  2134   NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
  2136   // If after creation the owner js global is not set for a document
  2137   // we use the default compartment for this document, instead of creating
  2138   // wrapper in some random compartment when the document is exposed to js
  2139   // via some events.
  2140   nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
  2141   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
  2142   mScopeObject = do_GetWeakReference(global);
  2143   MOZ_ASSERT(mScopeObject);
  2145   mScriptLoader = new nsScriptLoader(this);
  2147   mozilla::HoldJSObjects(this);
  2149   return NS_OK;
  2152 void
  2153 nsIDocument::DeleteAllProperties()
  2155   for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
  2156     PropertyTable(i)->DeleteAllProperties();
  2160 void
  2161 nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
  2163   for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
  2164     PropertyTable(i)->DeleteAllPropertiesFor(aNode);
  2168 nsPropertyTable*
  2169 nsIDocument::GetExtraPropertyTable(uint16_t aCategory)
  2171   NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
  2172   while (aCategory >= mExtraPropertyTables.Length() + 1) {
  2173     mExtraPropertyTables.AppendElement(new nsPropertyTable());
  2175   return mExtraPropertyTables[aCategory - 1];
  2178 bool
  2179 nsIDocument::IsVisibleConsideringAncestors() const
  2181   const nsIDocument *parent = this;
  2182   do {
  2183     if (!parent->IsVisible()) {
  2184       return false;
  2186   } while ((parent = parent->GetParentDocument()));
  2188   return true;
  2191 void
  2192 nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
  2194   nsCOMPtr<nsIURI> uri;
  2195   nsCOMPtr<nsIPrincipal> principal;
  2196   if (aChannel) {
  2197     // Note: this code is duplicated in XULDocument::StartDocumentLoad and
  2198     // nsScriptSecurityManager::GetChannelPrincipal.
  2199     // Note: this should match nsDocShell::OnLoadingSite
  2200     NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
  2202     nsIScriptSecurityManager *securityManager =
  2203       nsContentUtils::GetSecurityManager();
  2204     if (securityManager) {
  2205       securityManager->GetChannelPrincipal(aChannel,
  2206                                            getter_AddRefs(principal));
  2210   ResetToURI(uri, aLoadGroup, principal);
  2212   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
  2213   if (bag) {
  2214     nsCOMPtr<nsIURI> baseURI;
  2215     bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
  2216                                 NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
  2217     if (baseURI) {
  2218       mDocumentBaseURI = baseURI;
  2219       mChromeXHRDocBaseURI = baseURI;
  2223   mChannel = aChannel;
  2226 void
  2227 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
  2228                        nsIPrincipal* aPrincipal)
  2230   NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
  2232 #ifdef PR_LOGGING
  2233   if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
  2234     nsAutoCString spec;
  2235     aURI->GetSpec(spec);
  2236     PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
  2238 #endif
  2240   mSecurityInfo = nullptr;
  2242   mDocumentLoadGroup = nullptr;
  2244   // Delete references to sub-documents and kill the subdocument map,
  2245   // if any. It holds strong references
  2246   if (mSubDocuments) {
  2247     PL_DHashTableDestroy(mSubDocuments);
  2249     mSubDocuments = nullptr;
  2252   // Destroy link map now so we don't waste time removing
  2253   // links one by one
  2254   DestroyElementMaps();
  2256   bool oldVal = mInUnlinkOrDeletion;
  2257   mInUnlinkOrDeletion = true;
  2258   uint32_t count = mChildren.ChildCount();
  2259   { // Scope for update
  2260     MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true);
  2261     for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
  2262       nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
  2264       nsIContent* previousSibling = content->GetPreviousSibling();
  2266       if (nsINode::GetFirstChild() == content) {
  2267         mFirstChild = content->GetNextSibling();
  2269       mChildren.RemoveChildAt(i);
  2270       nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
  2271       content->UnbindFromTree();
  2273     mCachedRootElement = nullptr;
  2275   mInUnlinkOrDeletion = oldVal;
  2277   mRegistry = nullptr;
  2279   // Reset our stylesheets
  2280   ResetStylesheetsToURI(aURI);
  2282   // Release the listener manager
  2283   if (mListenerManager) {
  2284     mListenerManager->Disconnect();
  2285     mListenerManager = nullptr;
  2288   // Release the stylesheets list.
  2289   mDOMStyleSheets = nullptr;
  2291   // Release our principal after tearing down the document, rather than before.
  2292   // This ensures that, during teardown, the document and the dying window (which
  2293   // already nulled out its document pointer and cached the principal) have
  2294   // matching principals.
  2295   SetPrincipal(nullptr);
  2297   // Clear the original URI so SetDocumentURI sets it.
  2298   mOriginalURI = nullptr;
  2300   SetDocumentURI(aURI);
  2301   mChromeXHRDocURI = aURI;
  2302   // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
  2303   // mDocumentURI.
  2304   mDocumentBaseURI = nullptr;
  2305   mChromeXHRDocBaseURI = nullptr;
  2307   if (aLoadGroup) {
  2308     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
  2309     // there was an assertion here that aLoadGroup was not null.  This
  2310     // is no longer valid: nsDocShell::SetDocument does not create a
  2311     // load group, and it works just fine
  2313     // XXXbz what does "just fine" mean exactly?  And given that there
  2314     // is no nsDocShell::SetDocument, what is this talking about?
  2317   mLastModified.Truncate();
  2318   // XXXbz I guess we're assuming that the caller will either pass in
  2319   // a channel with a useful type or call SetContentType?
  2320   SetContentTypeInternal(EmptyCString());
  2321   mContentLanguage.Truncate();
  2322   mBaseTarget.Truncate();
  2323   mReferrer.Truncate();
  2325   mXMLDeclarationBits = 0;
  2327   // Now get our new principal
  2328   if (aPrincipal) {
  2329     SetPrincipal(aPrincipal);
  2330   } else {
  2331     nsIScriptSecurityManager *securityManager =
  2332       nsContentUtils::GetSecurityManager();
  2333     if (securityManager) {
  2334       nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  2336       if (!docShell && aLoadGroup) {
  2337         nsCOMPtr<nsIInterfaceRequestor> cbs;
  2338         aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
  2339         docShell = do_GetInterface(cbs);
  2342       MOZ_ASSERT(docShell,
  2343                  "must be in a docshell or pass in an explicit principal");
  2345       nsCOMPtr<nsIPrincipal> principal;
  2346       nsresult rv = securityManager->
  2347         GetDocShellCodebasePrincipal(mDocumentURI, docShell,
  2348                                      getter_AddRefs(principal));
  2349       if (NS_SUCCEEDED(rv)) {
  2350         SetPrincipal(principal);
  2355   // Refresh the principal on the compartment.
  2356   nsPIDOMWindow* win = GetInnerWindow();
  2357   if (win) {
  2358     win->RefreshCompartmentPrincipal();
  2362 void
  2363 nsDocument::RemoveDocStyleSheetsFromStyleSets()
  2365   // The stylesheets should forget us
  2366   int32_t indx = mStyleSheets.Count();
  2367   while (--indx >= 0) {
  2368     nsIStyleSheet* sheet = mStyleSheets[indx];
  2369     sheet->SetOwningDocument(nullptr);
  2371     if (sheet->IsApplicable()) {
  2372       nsCOMPtr<nsIPresShell> shell = GetShell();
  2373       if (shell) {
  2374         shell->StyleSet()->RemoveDocStyleSheet(sheet);
  2377     // XXX Tell observers?
  2381 void
  2382 nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType)
  2384   // The stylesheets should forget us
  2385   int32_t indx = aSheets.Count();
  2386   while (--indx >= 0) {
  2387     nsIStyleSheet* sheet = aSheets[indx];
  2388     sheet->SetOwningDocument(nullptr);
  2390     if (sheet->IsApplicable()) {
  2391       nsCOMPtr<nsIPresShell> shell = GetShell();
  2392       if (shell) {
  2393         shell->StyleSet()->RemoveStyleSheet(aType, sheet);
  2397     // XXX Tell observers?
  2402 void
  2403 nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
  2405   MOZ_ASSERT(aURI);
  2407   mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
  2408   RemoveDocStyleSheetsFromStyleSets();
  2409   RemoveStyleSheetsFromStyleSets(mCatalogSheets, nsStyleSet::eAgentSheet);
  2410   RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet);
  2411   RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet);
  2412   RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet);
  2414   // Release all the sheets
  2415   mStyleSheets.Clear();
  2416   for (uint32_t i = 0; i < SheetTypeCount; ++i)
  2417     mAdditionalSheets[i].Clear();
  2419   // NOTE:  We don't release the catalog sheets.  It doesn't really matter
  2420   // now, but it could in the future -- in which case not releasing them
  2421   // is probably the right thing to do.
  2423   // Now reset our inline style and attribute sheets.
  2424   if (mAttrStyleSheet) {
  2425     mAttrStyleSheet->Reset();
  2426     mAttrStyleSheet->SetOwningDocument(this);
  2427   } else {
  2428     mAttrStyleSheet = new nsHTMLStyleSheet(this);
  2431   if (!mStyleAttrStyleSheet) {
  2432     mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
  2435   // Now set up our style sets
  2436   nsCOMPtr<nsIPresShell> shell = GetShell();
  2437   if (shell) {
  2438     FillStyleSet(shell->StyleSet());
  2442 static bool
  2443 AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
  2445   nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
  2446   styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet);
  2447   return true;
  2450 static void
  2451 AppendSheetsToStyleSet(nsStyleSet* aStyleSet,
  2452                        const nsCOMArray<nsIStyleSheet>& aSheets,
  2453                        nsStyleSet::sheetType aType)
  2455   for (int32_t i = aSheets.Count() - 1; i >= 0; --i) {
  2456     aStyleSet->AppendStyleSheet(aType, aSheets[i]);
  2461 void
  2462 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
  2464   NS_PRECONDITION(aStyleSet, "Must have a style set");
  2465   NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
  2466                   "Style set already has document sheets?");
  2468   // We could consider moving this to nsStyleSet::Init, to match its
  2469   // handling of the eAnimationSheet and eTransitionSheet levels.
  2470   aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet);
  2471   aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet);
  2473   int32_t i;
  2474   for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
  2475     nsIStyleSheet* sheet = mStyleSheets[i];
  2476     if (sheet->IsApplicable()) {
  2477       aStyleSet->AddDocStyleSheet(sheet, this);
  2481   nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
  2482   if (sheetService) {
  2483     sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet,
  2484                                                          aStyleSet);
  2487   for (i = mCatalogSheets.Count() - 1; i >= 0; --i) {
  2488     nsIStyleSheet* sheet = mCatalogSheets[i];
  2489     if (sheet->IsApplicable()) {
  2490       aStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
  2494   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
  2495                          nsStyleSet::eAgentSheet);
  2496   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
  2497                          nsStyleSet::eUserSheet);
  2498   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
  2499                          nsStyleSet::eDocSheet);
  2502 nsresult
  2503 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
  2504                               nsILoadGroup* aLoadGroup,
  2505                               nsISupports* aContainer,
  2506                               nsIStreamListener **aDocListener,
  2507                               bool aReset, nsIContentSink* aSink)
  2509 #ifdef PR_LOGGING
  2510   if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
  2511     nsCOMPtr<nsIURI> uri;
  2512     aChannel->GetURI(getter_AddRefs(uri));
  2513     nsAutoCString spec;
  2514     if (uri)
  2515       uri->GetSpec(spec);
  2516     PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get());
  2518 #endif
  2520 #ifdef DEBUG
  2522     uint32_t appId;
  2523     nsresult rv = NodePrincipal()->GetAppId(&appId);
  2524     NS_ENSURE_SUCCESS(rv, rv);
  2525     MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
  2526                "Document should never have UNKNOWN_APP_ID");
  2528 #endif
  2530   MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
  2531              "Bad readyState");
  2532   SetReadyStateInternal(READYSTATE_LOADING);
  2534   if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
  2535     mLoadedAsData = true;
  2536     // We need to disable script & style loading in this case.
  2537     // We leave them disabled even in EndLoad(), and let anyone
  2538     // who puts the document on display to worry about enabling.
  2540     // Do not load/process scripts when loading as data
  2541     ScriptLoader()->SetEnabled(false);
  2543     // styles
  2544     CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
  2545   } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
  2546     // Allow CSS, but not scripts
  2547     ScriptLoader()->SetEnabled(false);
  2550   mMayStartLayout = false;
  2552   mHaveInputEncoding = true;
  2554   if (aReset) {
  2555     Reset(aChannel, aLoadGroup);
  2558   nsAutoCString contentType;
  2559   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
  2560   if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
  2561                 NS_LITERAL_STRING("contentType"), contentType))) ||
  2562       NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
  2563     // XXX this is only necessary for viewsource:
  2564     nsACString::const_iterator start, end, semicolon;
  2565     contentType.BeginReading(start);
  2566     contentType.EndReading(end);
  2567     semicolon = start;
  2568     FindCharInReadable(';', semicolon, end);
  2569     SetContentTypeInternal(Substring(start, semicolon));
  2572   RetrieveRelevantHeaders(aChannel);
  2574   mChannel = aChannel;
  2575   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
  2576   if (inStrmChan) {
  2577     bool isSrcdocChannel;
  2578     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
  2579     if (isSrcdocChannel) {
  2580       mIsSrcdocDocument = true;
  2584   // If this document is being loaded by a docshell, copy its sandbox flags
  2585   // to the document. These are immutable after being set here.
  2586   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
  2588   if (docShell) {
  2589     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
  2590     NS_ENSURE_SUCCESS(rv, rv);
  2593   // If this is not a data document, set CSP.
  2594   if (!mLoadedAsData) {
  2595     nsresult rv = InitCSP(aChannel);
  2596     NS_ENSURE_SUCCESS(rv, rv);
  2599   return NS_OK;
  2602 void
  2603 CSPErrorQueue::Add(const char* aMessageName)
  2605   mErrors.AppendElement(aMessageName);
  2608 void
  2609 CSPErrorQueue::Flush(nsIDocument* aDocument)
  2611   for (uint32_t i = 0; i < mErrors.Length(); i++) {
  2612     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  2613         NS_LITERAL_CSTRING("CSP"), aDocument,
  2614         nsContentUtils::eSECURITY_PROPERTIES,
  2615         mErrors[i]);
  2617   mErrors.Clear();
  2620 void
  2621 nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
  2623   for (uint32_t i = 0; i < aMessages.Length(); ++i) {
  2624     nsAutoString messageTag;
  2625     aMessages[i]->GetTag(messageTag);
  2627     nsAutoString category;
  2628     aMessages[i]->GetCategory(category);
  2630     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  2631                                     NS_ConvertUTF16toUTF8(category),
  2632                                     this, nsContentUtils::eSECURITY_PROPERTIES,
  2633                                     NS_ConvertUTF16toUTF8(messageTag).get());
  2637 static nsresult
  2638 AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue,
  2639                     nsIURI* aSelfURI, bool aReportOnly, bool aSpecCompliant)
  2641   // Need to tokenize the header value since multiple headers could be
  2642   // concatenated into one comma-separated list of policies.
  2643   // See RFC2616 section 4.2 (last paragraph)
  2644   nsresult rv = NS_OK;
  2645   nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
  2646   while (tokenizer.hasMoreTokens()) {
  2647       const nsSubstring& policy = tokenizer.nextToken();
  2648       rv = csp->AppendPolicy(policy, aSelfURI, aReportOnly, aSpecCompliant);
  2649       NS_ENSURE_SUCCESS(rv, rv);
  2650 #ifdef PR_LOGGING
  2652         PR_LOG(gCspPRLog, PR_LOG_DEBUG,
  2653                 ("CSP refined with policy: \"%s\"",
  2654                 NS_ConvertUTF16toUTF8(policy).get()));
  2656 #endif
  2658   return NS_OK;
  2661 nsresult
  2662 nsDocument::InitCSP(nsIChannel* aChannel)
  2664   nsCOMPtr<nsIContentSecurityPolicy> csp;
  2665   if (!CSPService::sCSPEnabled) {
  2666 #ifdef PR_LOGGING
  2667     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
  2668            ("CSP is disabled, skipping CSP init for document %p", this));
  2669 #endif
  2670     return NS_OK;
  2673   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
  2674   nsAutoCString tCspOldHeaderValue, tCspOldROHeaderValue;
  2676   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
  2677   if (httpChannel) {
  2678     httpChannel->GetResponseHeader(
  2679         NS_LITERAL_CSTRING("x-content-security-policy"),
  2680         tCspOldHeaderValue);
  2682     httpChannel->GetResponseHeader(
  2683         NS_LITERAL_CSTRING("x-content-security-policy-report-only"),
  2684         tCspOldROHeaderValue);
  2686     httpChannel->GetResponseHeader(
  2687         NS_LITERAL_CSTRING("content-security-policy"),
  2688         tCspHeaderValue);
  2690     httpChannel->GetResponseHeader(
  2691         NS_LITERAL_CSTRING("content-security-policy-report-only"),
  2692         tCspROHeaderValue);
  2694   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
  2695   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
  2696   NS_ConvertASCIItoUTF16 cspOldHeaderValue(tCspOldHeaderValue);
  2697   NS_ConvertASCIItoUTF16 cspOldROHeaderValue(tCspOldROHeaderValue);
  2699   // Only use the CSP 1.0 spec compliant headers if a pref to do so
  2700   // is set. This lets us turn on the 1.0 parser per platform. This
  2701   // pref is also set by the tests for 1.0 spec compliant CSP.
  2702   bool specCompliantEnabled =
  2703     Preferences::GetBool("security.csp.speccompliant");
  2705   // If spec compliant pref isn't set, pretend we never got these headers.
  2706   if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) &&
  2707        !specCompliantEnabled) {
  2708     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
  2709             ("Got spec compliant CSP headers but pref was not set"));
  2710     cspHeaderValue.Truncate();
  2711     cspROHeaderValue.Truncate();
  2714   // If both the new header AND the old header are present, warn that
  2715   // the old header will be ignored. Otherwise, if the old header is
  2716   // present, warn that it will be deprecated.
  2717   bool oldHeaderIsPresent = !cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty();
  2718   bool newHeaderIsPresent = !cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty();
  2720   if (oldHeaderIsPresent && newHeaderIsPresent) {
  2721     mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent");
  2722   } else if (oldHeaderIsPresent) {
  2723     mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
  2726   // Figure out if we need to apply an app default CSP or a CSP from an app manifest
  2727   nsIPrincipal* principal = NodePrincipal();
  2729   uint16_t appStatus = principal->GetAppStatus();
  2730   bool applyAppDefaultCSP = appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
  2731                             appStatus == nsIPrincipal::APP_STATUS_CERTIFIED;
  2732   bool applyAppManifestCSP = false;
  2734   nsAutoString appManifestCSP;
  2735   if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
  2736     nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
  2737     if (appsService) {
  2738       uint32_t appId = 0;
  2739       if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
  2740         appsService->GetCSPByLocalId(appId, appManifestCSP);
  2741         if (!appManifestCSP.IsEmpty()) {
  2742           applyAppManifestCSP = true;
  2748   // If there's no CSP to apply, go ahead and return early
  2749   if (!applyAppDefaultCSP &&
  2750       !applyAppManifestCSP &&
  2751       cspHeaderValue.IsEmpty() &&
  2752       cspROHeaderValue.IsEmpty() &&
  2753       cspOldHeaderValue.IsEmpty() &&
  2754       cspOldROHeaderValue.IsEmpty()) {
  2755 #ifdef PR_LOGGING
  2756     nsCOMPtr<nsIURI> chanURI;
  2757     aChannel->GetURI(getter_AddRefs(chanURI));
  2758     nsAutoCString aspec;
  2759     chanURI->GetAsciiSpec(aspec);
  2760     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
  2761            ("no CSP for document, %s, %s",
  2762             aspec.get(),
  2763             applyAppDefaultCSP ? "is app" : "not an app"));
  2764 #endif
  2765     return NS_OK;
  2768 #ifdef PR_LOGGING
  2769   PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
  2770 #endif
  2772   nsresult rv;
  2774   // If Document is an app check to see if we already set CSP and return early
  2775   // if that is indeed the case.
  2776   //
  2777   // In general (see bug 947831), we should not be setting CSP on a principal
  2778   // that aliases another document. For non-app code this is not a problem
  2779   // since we only share the underlying principal with nested browsing
  2780   // contexts for which a header cannot be set (e.g., about:blank and
  2781   // about:srcodoc iframes) and thus won't try to set the CSP again. This
  2782   // check ensures that we do not try to set CSP for an app.
  2783   if (applyAppDefaultCSP || applyAppManifestCSP) {
  2784     nsCOMPtr<nsIContentSecurityPolicy> csp;
  2785     rv = principal->GetCsp(getter_AddRefs(csp));
  2786     NS_ENSURE_SUCCESS(rv, rv);
  2788     if (csp) {
  2789 #ifdef PR_LOGGING
  2790       PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s",
  2791            "This document is sharing principal with another document.",
  2792            "Since the document is an app, CSP was already set.",
  2793            "Skipping attempt to set CSP."));
  2794 #endif
  2795       return NS_OK;
  2799   // create new CSP object
  2800   csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
  2802   if (NS_FAILED(rv)) {
  2803 #ifdef PR_LOGGING
  2804     PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
  2805 #endif
  2806     return rv;
  2809   // used as a "self" identifier for the CSP.
  2810   nsCOMPtr<nsIURI> selfURI;
  2811   aChannel->GetURI(getter_AddRefs(selfURI));
  2813   // Store the request context for violation reports
  2814   csp->SetRequestContext(nullptr, nullptr, nullptr, aChannel);
  2816   // ----- if the doc is an app and we want a default CSP, apply it.
  2817   if (applyAppDefaultCSP) {
  2818     nsAdoptingString appCSP;
  2819     if (appStatus ==  nsIPrincipal::APP_STATUS_PRIVILEGED) {
  2820       appCSP = Preferences::GetString("security.apps.privileged.CSP.default");
  2821       NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default");
  2822     } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
  2823       appCSP = Preferences::GetString("security.apps.certified.CSP.default");
  2824       NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
  2827     if (appCSP) {
  2828       // Use the 1.0 CSP parser for apps if the pref to do so is set.
  2829       csp->AppendPolicy(appCSP, selfURI, false, specCompliantEnabled);
  2833   // ----- if the doc is an app and specifies a CSP in its manifest, apply it.
  2834   if (applyAppManifestCSP) {
  2835     // Use the 1.0 CSP parser for apps if the pref to do so is set.
  2836     csp->AppendPolicy(appManifestCSP, selfURI, false, specCompliantEnabled);
  2839   // While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
  2840   // take priority.  If both are present, the x-* headers are ignored.
  2842   // ----- if there's a full-strength CSP header, apply it.
  2843   if (!cspHeaderValue.IsEmpty()) {
  2844     rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true);
  2845     NS_ENSURE_SUCCESS(rv, rv);
  2846   } else if (!cspOldHeaderValue.IsEmpty()) {
  2847     rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false);
  2848     NS_ENSURE_SUCCESS(rv, rv);
  2851   // ----- if there's a report-only CSP header, apply it.
  2852   if (!cspROHeaderValue.IsEmpty()) {
  2853     rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true);
  2854     NS_ENSURE_SUCCESS(rv, rv);
  2855   } else if (!cspOldROHeaderValue.IsEmpty()) {
  2856     rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false);
  2857     NS_ENSURE_SUCCESS(rv, rv);
  2860   // ----- Enforce frame-ancestor policy on any applied policies
  2861   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  2862   if (docShell) {
  2863     bool safeAncestry = false;
  2865     // PermitsAncestry sends violation reports when necessary
  2866     rv = csp->PermitsAncestry(docShell, &safeAncestry);
  2867     NS_ENSURE_SUCCESS(rv, rv);
  2869     if (!safeAncestry) {
  2870 #ifdef PR_LOGGING
  2871       PR_LOG(gCspPRLog, PR_LOG_DEBUG,
  2872               ("CSP doesn't like frame's ancestry, not loading."));
  2873 #endif
  2874       // stop!  ERROR page!
  2875       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
  2879   rv = principal->SetCsp(csp);
  2880   NS_ENSURE_SUCCESS(rv, rv);
  2881 #ifdef PR_LOGGING
  2882   PR_LOG(gCspPRLog, PR_LOG_DEBUG,
  2883          ("Inserted CSP into principal %p", principal));
  2884 #endif
  2886   return NS_OK;
  2889 void
  2890 nsDocument::StopDocumentLoad()
  2892   if (mParser) {
  2893     mParserAborted = true;
  2894     mParser->Terminate();
  2898 void
  2899 nsDocument::SetDocumentURI(nsIURI* aURI)
  2901   nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
  2902   mDocumentURI = NS_TryToMakeImmutable(aURI);
  2903   nsIURI* newBase = GetDocBaseURI();
  2905   bool equalBases = false;
  2906   // Changing just the ref of a URI does not change how relative URIs would
  2907   // resolve wrt to it, so we can treat the bases as equal as long as they're
  2908   // equal ignoring the ref.
  2909   if (oldBase && newBase) {
  2910     oldBase->EqualsExceptRef(newBase, &equalBases);
  2912   else {
  2913     equalBases = !oldBase && !newBase;
  2916   // If this is the first time we're setting the document's URI, set the
  2917   // document's original URI.
  2918   if (!mOriginalURI)
  2919     mOriginalURI = mDocumentURI;
  2921   // If changing the document's URI changed the base URI of the document, we
  2922   // need to refresh the hrefs of all the links on the page.
  2923   if (!equalBases) {
  2924     RefreshLinkHrefs();
  2928 void
  2929 nsDocument::SetChromeXHRDocURI(nsIURI* aURI)
  2931   mChromeXHRDocURI = aURI;
  2934 void
  2935 nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI)
  2937   mChromeXHRDocBaseURI = aURI;
  2940 NS_IMETHODIMP
  2941 nsDocument::GetLastModified(nsAString& aLastModified)
  2943   nsIDocument::GetLastModified(aLastModified);
  2944   return NS_OK;
  2947 void
  2948 nsIDocument::GetLastModified(nsAString& aLastModified) const
  2950   if (!mLastModified.IsEmpty()) {
  2951     aLastModified.Assign(mLastModified);
  2952   } else {
  2953     // If we for whatever reason failed to find the last modified time
  2954     // (or even the current time), fall back to what NS4.x returned.
  2955     aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00"));
  2959 void
  2960 nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
  2962   MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
  2963              "Only put elements that need to be exposed as document['name'] in "
  2964              "the named table.");
  2966   nsIdentifierMapEntry *entry =
  2967     mIdentifierMap.PutEntry(nsDependentAtomString(aName));
  2969   // Null for out-of-memory
  2970   if (entry) {
  2971     if (!entry->HasNameElement() &&
  2972         !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
  2973       ++mExpandoAndGeneration.generation;
  2975     entry->AddNameElement(this, aElement);
  2979 void
  2980 nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
  2982   // Speed up document teardown
  2983   if (mIdentifierMap.Count() == 0)
  2984     return;
  2986   nsIdentifierMapEntry *entry =
  2987     mIdentifierMap.GetEntry(nsDependentAtomString(aName));
  2988   if (!entry) // Could be false if the element was anonymous, hence never added
  2989     return;
  2991   entry->RemoveNameElement(aElement);
  2992   if (!entry->HasNameElement() &&
  2993       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
  2994     ++mExpandoAndGeneration.generation;
  2998 void
  2999 nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
  3001   nsIdentifierMapEntry *entry =
  3002     mIdentifierMap.PutEntry(nsDependentAtomString(aId));
  3004   if (entry) { /* True except on OOM */
  3005     if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
  3006         !entry->HasNameElement() &&
  3007         !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
  3008       ++mExpandoAndGeneration.generation;
  3010     entry->AddIdElement(aElement);
  3014 void
  3015 nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
  3017   NS_ASSERTION(aId, "huhwhatnow?");
  3019   // Speed up document teardown
  3020   if (mIdentifierMap.Count() == 0) {
  3021     return;
  3024   nsIdentifierMapEntry *entry =
  3025     mIdentifierMap.GetEntry(nsDependentAtomString(aId));
  3026   if (!entry) // Can be null for XML elements with changing ids.
  3027     return;
  3029   entry->RemoveIdElement(aElement);
  3030   if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
  3031       !entry->HasNameElement() &&
  3032       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
  3033     ++mExpandoAndGeneration.generation;
  3035   if (entry->IsEmpty()) {
  3036     mIdentifierMap.RawRemoveEntry(entry);
  3040 nsIPrincipal*
  3041 nsDocument::GetPrincipal()
  3043   return NodePrincipal();
  3046 extern bool sDisablePrefetchHTTPSPref;
  3048 void
  3049 nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
  3051   if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
  3052     nsCOMPtr<nsIURI> uri;
  3053     aNewPrincipal->GetURI(getter_AddRefs(uri));
  3054     bool isHTTPS;
  3055     if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
  3056         isHTTPS) {
  3057       mAllowDNSPrefetch = false;
  3060   mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
  3063 NS_IMETHODIMP
  3064 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
  3066   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
  3068   return NS_OK;
  3071 NS_IMETHODIMP
  3072 nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
  3074   mApplicationCache = aApplicationCache;
  3076   return NS_OK;
  3079 NS_IMETHODIMP
  3080 nsDocument::GetContentType(nsAString& aContentType)
  3082   CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
  3084   return NS_OK;
  3087 void
  3088 nsDocument::SetContentType(const nsAString& aContentType)
  3090   NS_ASSERTION(GetContentTypeInternal().IsEmpty() ||
  3091                GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)),
  3092                "Do you really want to change the content-type?");
  3094   SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
  3097 nsresult
  3098 nsDocument::GetAllowPlugins(bool * aAllowPlugins)
  3100   // First, we ask our docshell if it allows plugins.
  3101   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  3103   if (docShell) {
  3104     docShell->GetAllowPlugins(aAllowPlugins);
  3106     // If the docshell allows plugins, we check whether
  3107     // we are sandboxed and plugins should not be allowed.
  3108     if (*aAllowPlugins)
  3109       *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS);
  3112   return NS_OK;
  3115 already_AddRefed<UndoManager>
  3116 nsDocument::GetUndoManager()
  3118   Element* rootElement = GetRootElement();
  3119   if (!rootElement) {
  3120     return nullptr;
  3123   if (!mUndoManager) {
  3124     mUndoManager = new UndoManager(rootElement);
  3127   nsRefPtr<UndoManager> undoManager = mUndoManager;
  3128   return undoManager.forget();
  3131 /* Return true if the document is in the focused top-level window, and is an
  3132  * ancestor of the focused DOMWindow. */
  3133 NS_IMETHODIMP
  3134 nsDocument::HasFocus(bool* aResult)
  3136   ErrorResult rv;
  3137   *aResult = nsIDocument::HasFocus(rv);
  3138   return rv.ErrorCode();
  3141 bool
  3142 nsIDocument::HasFocus(ErrorResult& rv) const
  3144   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  3145   if (!fm) {
  3146     rv.Throw(NS_ERROR_NOT_AVAILABLE);
  3147     return false;
  3150   // Is there a focused DOMWindow?
  3151   nsCOMPtr<nsIDOMWindow> focusedWindow;
  3152   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
  3153   if (!focusedWindow) {
  3154     return false;
  3157   // Are we an ancestor of the focused DOMWindow?
  3158   nsCOMPtr<nsIDOMDocument> domDocument;
  3159   focusedWindow->GetDocument(getter_AddRefs(domDocument));
  3160   nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
  3162   for (nsIDocument* currentDoc = document; currentDoc;
  3163        currentDoc = currentDoc->GetParentDocument()) {
  3164     if (currentDoc == this) {
  3165       // Yes, we are an ancestor
  3166       return true;
  3170   return false;
  3173 NS_IMETHODIMP
  3174 nsDocument::GetReferrer(nsAString& aReferrer)
  3176   nsIDocument::GetReferrer(aReferrer);
  3177   return NS_OK;
  3180 void
  3181 nsIDocument::GetReferrer(nsAString& aReferrer) const
  3183   if (mIsSrcdocDocument && mParentDocument)
  3184       mParentDocument->GetReferrer(aReferrer);
  3185   else
  3186     CopyUTF8toUTF16(mReferrer, aReferrer);
  3189 nsresult
  3190 nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
  3192   if (mIsSrcdocDocument) {
  3193     nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
  3194     if (inStrmChan) {
  3195       return inStrmChan->GetSrcdocData(aSrcdocData);
  3198   aSrcdocData = NullString();
  3199   return NS_OK;
  3202 NS_IMETHODIMP
  3203 nsDocument::GetActiveElement(nsIDOMElement **aElement)
  3205   nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement()));
  3206   el.forget(aElement);
  3207   return NS_OK;
  3210 Element*
  3211 nsIDocument::GetActiveElement()
  3213   // Get the focused element.
  3214   nsCOMPtr<nsPIDOMWindow> window = GetWindow();
  3215   if (window) {
  3216     nsCOMPtr<nsPIDOMWindow> focusedWindow;
  3217     nsIContent* focusedContent =
  3218       nsFocusManager::GetFocusedDescendant(window, false,
  3219                                            getter_AddRefs(focusedWindow));
  3220     // be safe and make sure the element is from this document
  3221     if (focusedContent && focusedContent->OwnerDoc() == this) {
  3222       if (focusedContent->ChromeOnlyAccess()) {
  3223         focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
  3225       if (focusedContent) {
  3226         return focusedContent->AsElement();
  3231   // No focused element anywhere in this document.  Try to get the BODY.
  3232   nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
  3233   if (htmlDoc) {
  3234     // Because of IE compatibility, return null when html document doesn't have
  3235     // a body.
  3236     return htmlDoc->GetBody();
  3239   // If we couldn't get a BODY, return the root element.
  3240   return GetDocumentElement();
  3243 NS_IMETHODIMP
  3244 nsDocument::GetCurrentScript(nsIDOMElement **aElement)
  3246   nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript()));
  3247   el.forget(aElement);
  3248   return NS_OK;
  3251 Element*
  3252 nsIDocument::GetCurrentScript()
  3254   nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
  3255   return el;
  3258 NS_IMETHODIMP
  3259 nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
  3261   Element* el = nsIDocument::ElementFromPoint(aX, aY);
  3262   nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
  3263   retval.forget(aReturn);
  3264   return NS_OK;
  3267 Element*
  3268 nsIDocument::ElementFromPoint(float aX, float aY)
  3270   return ElementFromPointHelper(aX, aY, false, true);
  3273 Element*
  3274 nsDocument::ElementFromPointHelper(float aX, float aY,
  3275                                    bool aIgnoreRootScrollFrame,
  3276                                    bool aFlushLayout)
  3278   // As per the the spec, we return null if either coord is negative
  3279   if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
  3280     return nullptr;
  3283   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
  3284   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
  3285   nsPoint pt(x, y);
  3287   // Make sure the layout information we get is up-to-date, and
  3288   // ensure we get a root frame (for everything but XUL)
  3289   if (aFlushLayout)
  3290     FlushPendingNotifications(Flush_Layout);
  3292   nsIPresShell *ps = GetShell();
  3293   if (!ps) {
  3294     return nullptr;
  3296   nsIFrame *rootFrame = ps->GetRootFrame();
  3298   // XUL docs, unlike HTML, have no frame tree until everything's done loading
  3299   if (!rootFrame) {
  3300     return nullptr; // return null to premature XUL callers as a reminder to wait
  3303   nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
  3304     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
  3305     (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
  3306   if (!ptFrame) {
  3307     return nullptr;
  3310   nsIContent* elem = GetContentInThisDocument(ptFrame);
  3311   if (elem && !elem->IsElement()) {
  3312     elem = elem->GetParent();
  3314   return elem ? elem->AsElement() : nullptr;
  3317 nsresult
  3318 nsDocument::NodesFromRectHelper(float aX, float aY,
  3319                                 float aTopSize, float aRightSize,
  3320                                 float aBottomSize, float aLeftSize,
  3321                                 bool aIgnoreRootScrollFrame,
  3322                                 bool aFlushLayout,
  3323                                 nsIDOMNodeList** aReturn)
  3325   NS_ENSURE_ARG_POINTER(aReturn);
  3327   nsSimpleContentList* elements = new nsSimpleContentList(this);
  3328   NS_ADDREF(elements);
  3329   *aReturn = elements;
  3331   // Following the same behavior of elementFromPoint,
  3332   // we don't return anything if either coord is negative
  3333   if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
  3334     return NS_OK;
  3336   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
  3337   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
  3338   nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
  3339   nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
  3341   nsRect rect(x, y, w, h);
  3343   // Make sure the layout information we get is up-to-date, and
  3344   // ensure we get a root frame (for everything but XUL)
  3345   if (aFlushLayout) {
  3346     FlushPendingNotifications(Flush_Layout);
  3349   nsIPresShell *ps = GetShell();
  3350   NS_ENSURE_STATE(ps);
  3351   nsIFrame *rootFrame = ps->GetRootFrame();
  3353   // XUL docs, unlike HTML, have no frame tree until everything's done loading
  3354   if (!rootFrame)
  3355     return NS_OK; // return nothing to premature XUL callers as a reminder to wait
  3357   nsAutoTArray<nsIFrame*,8> outFrames;
  3358   nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
  3359     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
  3360     (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
  3362   // Used to filter out repeated elements in sequence.
  3363   nsIContent* lastAdded = nullptr;
  3365   for (uint32_t i = 0; i < outFrames.Length(); i++) {
  3366     nsIContent* node = GetContentInThisDocument(outFrames[i]);
  3368     if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
  3369       // We have a node that isn't an element or a text node,
  3370       // use its parent content instead.
  3371       node = node->GetParent();
  3373     if (node && node != lastAdded) {
  3374       elements->AppendElement(node);
  3375       lastAdded = node;
  3379   return NS_OK;
  3382 NS_IMETHODIMP
  3383 nsDocument::GetElementsByClassName(const nsAString& aClasses,
  3384                                    nsIDOMNodeList** aReturn)
  3386   *aReturn = nsIDocument::GetElementsByClassName(aClasses).take();
  3387   return NS_OK;
  3390 already_AddRefed<nsContentList>
  3391 nsIDocument::GetElementsByClassName(const nsAString& aClasses)
  3393   return nsContentUtils::GetElementsByClassName(this, aClasses);
  3396 NS_IMETHODIMP
  3397 nsDocument::ReleaseCapture()
  3399   nsIDocument::ReleaseCapture();
  3400   return NS_OK;
  3403 void
  3404 nsIDocument::ReleaseCapture() const
  3406   // only release the capture if the caller can access it. This prevents a
  3407   // page from stopping a scrollbar grab for example.
  3408   nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
  3409   if (node && nsContentUtils::CanCallerAccess(node)) {
  3410     nsIPresShell::SetCapturingContent(nullptr, 0);
  3414 already_AddRefed<nsIURI>
  3415 nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
  3417   nsCOMPtr<nsIURI> uri;
  3418   if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
  3419     uri = mChromeXHRDocBaseURI;
  3420   } else {
  3421     uri = GetDocBaseURI();
  3424   return uri.forget();
  3427 nsresult
  3428 nsDocument::SetBaseURI(nsIURI* aURI)
  3430   if (!aURI && !mDocumentBaseURI) {
  3431     return NS_OK;
  3434   // Don't do anything if the URI wasn't actually changed.
  3435   if (aURI && mDocumentBaseURI) {
  3436     bool equalBases = false;
  3437     mDocumentBaseURI->Equals(aURI, &equalBases);
  3438     if (equalBases) {
  3439       return NS_OK;
  3443   if (aURI) {
  3444     mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
  3445   } else {
  3446     mDocumentBaseURI = nullptr;
  3448   RefreshLinkHrefs();
  3450   return NS_OK;
  3453 void
  3454 nsDocument::GetBaseTarget(nsAString &aBaseTarget)
  3456   aBaseTarget = mBaseTarget;
  3459 void
  3460 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
  3462   // XXX it would be a good idea to assert the sanity of the argument,
  3463   // but before we figure out what to do about non-Encoding Standard
  3464   // encodings in the charset menu and in mailnews, assertions are futile.
  3465   if (!mCharacterSet.Equals(aCharSetID)) {
  3466     mCharacterSet = aCharSetID;
  3468     int32_t n = mCharSetObservers.Length();
  3470     for (int32_t i = 0; i < n; i++) {
  3471       nsIObserver* observer = mCharSetObservers.ElementAt(i);
  3473       observer->Observe(static_cast<nsIDocument *>(this), "charset",
  3474                         NS_ConvertASCIItoUTF16(aCharSetID).get());
  3479 nsresult
  3480 nsDocument::AddCharSetObserver(nsIObserver* aObserver)
  3482   NS_ENSURE_ARG_POINTER(aObserver);
  3484   NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE);
  3486   return NS_OK;
  3489 void
  3490 nsDocument::RemoveCharSetObserver(nsIObserver* aObserver)
  3492   mCharSetObservers.RemoveElement(aObserver);
  3495 void
  3496 nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const
  3498   aData.Truncate();
  3499   const nsDocHeaderData* data = mHeaderData;
  3500   while (data) {
  3501     if (data->mField == aHeaderField) {
  3502       aData = data->mData;
  3504       break;
  3506     data = data->mNext;
  3510 void
  3511 nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
  3513   if (!aHeaderField) {
  3514     NS_ERROR("null headerField");
  3515     return;
  3518   if (!mHeaderData) {
  3519     if (!aData.IsEmpty()) { // don't bother storing empty string
  3520       mHeaderData = new nsDocHeaderData(aHeaderField, aData);
  3523   else {
  3524     nsDocHeaderData* data = mHeaderData;
  3525     nsDocHeaderData** lastPtr = &mHeaderData;
  3526     bool found = false;
  3527     do {  // look for existing and replace
  3528       if (data->mField == aHeaderField) {
  3529         if (!aData.IsEmpty()) {
  3530           data->mData.Assign(aData);
  3532         else {  // don't store empty string
  3533           *lastPtr = data->mNext;
  3534           data->mNext = nullptr;
  3535           delete data;
  3537         found = true;
  3539         break;
  3541       lastPtr = &(data->mNext);
  3542       data = *lastPtr;
  3543     } while (data);
  3545     if (!aData.IsEmpty() && !found) {
  3546       // didn't find, append
  3547       *lastPtr = new nsDocHeaderData(aHeaderField, aData);
  3551   if (aHeaderField == nsGkAtoms::headerContentLanguage) {
  3552     CopyUTF16toUTF8(aData, mContentLanguage);
  3555   if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
  3556     // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
  3557     // spec.
  3558     if (DOMStringIsNull(mLastStyleSheetSet)) {
  3559       // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
  3560       // per spec.  The idea here is that we're changing our preferred set and
  3561       // that shouldn't change the value of lastStyleSheetSet.  Also, we're
  3562       // using the Internal version so we can update the CSSLoader and not have
  3563       // to worry about null strings.
  3564       EnableStyleSheetsForSetInternal(aData, true);
  3568   if (aHeaderField == nsGkAtoms::refresh) {
  3569     // We get into this code before we have a script global yet, so get to
  3570     // our container via mDocumentContainer.
  3571     nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
  3572     if (refresher) {
  3573       // Note: using mDocumentURI instead of mBaseURI here, for consistency
  3574       // (used to just use the current URI of our webnavigation, but that
  3575       // should really be the same thing).  Note that this code can run
  3576       // before the current URI of the webnavigation has been updated, so we
  3577       // can't assert equality here.
  3578       refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
  3579                                            NS_ConvertUTF16toUTF8(aData));
  3583   if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
  3584       mAllowDNSPrefetch) {
  3585     // Chromium treats any value other than 'on' (case insensitive) as 'off'.
  3586     mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
  3589   if (aHeaderField == nsGkAtoms::viewport ||
  3590       aHeaderField == nsGkAtoms::handheldFriendly ||
  3591       aHeaderField == nsGkAtoms::viewport_minimum_scale ||
  3592       aHeaderField == nsGkAtoms::viewport_maximum_scale ||
  3593       aHeaderField == nsGkAtoms::viewport_initial_scale ||
  3594       aHeaderField == nsGkAtoms::viewport_height ||
  3595       aHeaderField == nsGkAtoms::viewport_width ||
  3596       aHeaderField ==  nsGkAtoms::viewport_user_scalable) {
  3597     mViewportType = Unknown;
  3601 void
  3602 nsDocument::TryChannelCharset(nsIChannel *aChannel,
  3603                               int32_t& aCharsetSource,
  3604                               nsACString& aCharset,
  3605                               nsHtml5TreeOpExecutor* aExecutor)
  3607   if (aChannel) {
  3608     nsAutoCString charsetVal;
  3609     nsresult rv = aChannel->GetContentCharset(charsetVal);
  3610     if (NS_SUCCEEDED(rv)) {
  3611       nsAutoCString preferred;
  3612       if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) {
  3613         aCharset = preferred;
  3614         aCharsetSource = kCharsetFromChannel;
  3615         return;
  3616       } else if (aExecutor && !charsetVal.IsEmpty()) {
  3617         aExecutor->ComplainAboutBogusProtocolCharset(this);
  3623 already_AddRefed<nsIPresShell>
  3624 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
  3625                         nsStyleSet* aStyleSet)
  3627   // Don't add anything here.  Add it to |doCreateShell| instead.
  3628   // This exists so that subclasses can pass other values for the 4th
  3629   // parameter some of the time.
  3630   return doCreateShell(aContext, aViewManager, aStyleSet,
  3631                        eCompatibility_FullStandards);
  3634 already_AddRefed<nsIPresShell>
  3635 nsDocument::doCreateShell(nsPresContext* aContext,
  3636                           nsViewManager* aViewManager, nsStyleSet* aStyleSet,
  3637                           nsCompatibility aCompatMode)
  3639   NS_ASSERTION(!mPresShell, "We have a presshell already!");
  3641   NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
  3643   FillStyleSet(aStyleSet);
  3645   nsRefPtr<PresShell> shell = new PresShell;
  3646   shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
  3648   // Note: we don't hold a ref to the shell (it holds a ref to us)
  3649   mPresShell = shell;
  3651   // Make sure to never paint if we belong to an invisible DocShell.
  3652   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  3653   if (docShell && docShell->IsInvisible())
  3654     shell->SetNeverPainting(true);
  3656   mExternalResourceMap.ShowViewers();
  3658   MaybeRescheduleAnimationFrameNotifications();
  3660   return shell.forget();
  3663 void
  3664 nsDocument::MaybeRescheduleAnimationFrameNotifications()
  3666   if (!mPresShell || !IsEventHandlingEnabled()) {
  3667     // bail out for now, until one of those conditions changes
  3668     return;
  3671   nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver();
  3672   if (!mFrameRequestCallbacks.IsEmpty()) {
  3673     rd->ScheduleFrameRequestCallbacks(this);
  3677 void
  3678 nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
  3680   aCallbacks.AppendElements(mFrameRequestCallbacks);
  3681   mFrameRequestCallbacks.Clear();
  3684 PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey,
  3685                                          uint32_t aData,
  3686                                          void* userArg)
  3688   aKey->RequestDiscard();
  3689   return PL_DHASH_NEXT;
  3692 void
  3693 nsDocument::DeleteShell()
  3695   mExternalResourceMap.HideViewers();
  3696   if (IsEventHandlingEnabled()) {
  3697     RevokeAnimationFrameNotifications();
  3700   // When our shell goes away, request that all our images be immediately
  3701   // discarded, so we don't carry around decoded image data for a document we
  3702   // no longer intend to paint.
  3703   mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr);
  3705   mPresShell = nullptr;
  3708 void
  3709 nsDocument::RevokeAnimationFrameNotifications()
  3711   if (!mFrameRequestCallbacks.IsEmpty()) {
  3712     mPresShell->GetPresContext()->RefreshDriver()->
  3713       RevokeFrameRequestCallbacks(this);
  3717 static void
  3718 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
  3720   SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
  3722   NS_RELEASE(e->mKey);
  3723   if (e->mSubDocument) {
  3724     e->mSubDocument->SetParentDocument(nullptr);
  3725     NS_RELEASE(e->mSubDocument);
  3729 static bool
  3730 SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key)
  3732   SubDocMapEntry *e =
  3733     const_cast<SubDocMapEntry *>
  3734               (static_cast<const SubDocMapEntry *>(entry));
  3736   e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
  3737   NS_ADDREF(e->mKey);
  3739   e->mSubDocument = nullptr;
  3740   return true;
  3743 nsresult
  3744 nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
  3746   NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
  3748   if (!aSubDoc) {
  3749     // aSubDoc is nullptr, remove the mapping
  3751     if (mSubDocuments) {
  3752       SubDocMapEntry *entry =
  3753         static_cast<SubDocMapEntry*>
  3754                    (PL_DHashTableOperate(mSubDocuments, aElement,
  3755                                          PL_DHASH_LOOKUP));
  3757       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  3758         PL_DHashTableRawRemove(mSubDocuments, entry);
  3761   } else {
  3762     if (!mSubDocuments) {
  3763       // Create a new hashtable
  3765       static const PLDHashTableOps hash_table_ops =
  3767         PL_DHashAllocTable,
  3768         PL_DHashFreeTable,
  3769         PL_DHashVoidPtrKeyStub,
  3770         PL_DHashMatchEntryStub,
  3771         PL_DHashMoveEntryStub,
  3772         SubDocClearEntry,
  3773         PL_DHashFinalizeStub,
  3774         SubDocInitEntry
  3775       };
  3777       mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr,
  3778                                        sizeof(SubDocMapEntry), 16);
  3779       if (!mSubDocuments) {
  3780         return NS_ERROR_OUT_OF_MEMORY;
  3784     // Add a mapping to the hash table
  3785     SubDocMapEntry *entry =
  3786       static_cast<SubDocMapEntry*>
  3787                  (PL_DHashTableOperate(mSubDocuments, aElement,
  3788                                        PL_DHASH_ADD));
  3790     if (!entry) {
  3791       return NS_ERROR_OUT_OF_MEMORY;
  3794     if (entry->mSubDocument) {
  3795       entry->mSubDocument->SetParentDocument(nullptr);
  3797       // Release the old sub document
  3798       NS_RELEASE(entry->mSubDocument);
  3801     entry->mSubDocument = aSubDoc;
  3802     NS_ADDREF(entry->mSubDocument);
  3804     aSubDoc->SetParentDocument(this);
  3807   return NS_OK;
  3810 nsIDocument*
  3811 nsDocument::GetSubDocumentFor(nsIContent *aContent) const
  3813   if (mSubDocuments && aContent->IsElement()) {
  3814     SubDocMapEntry *entry =
  3815       static_cast<SubDocMapEntry*>
  3816                  (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(),
  3817                                        PL_DHASH_LOOKUP));
  3819     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  3820       return entry->mSubDocument;
  3824   return nullptr;
  3827 static PLDHashOperator
  3828 FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
  3829                       uint32_t number, void *arg)
  3831   SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
  3832   FindContentData *data = static_cast<FindContentData*>(arg);
  3834   if (entry->mSubDocument == data->mSubDocument) {
  3835     data->mResult = entry->mKey;
  3837     return PL_DHASH_STOP;
  3840   return PL_DHASH_NEXT;
  3843 Element*
  3844 nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
  3846   NS_ENSURE_TRUE(aDocument, nullptr);
  3848   if (!mSubDocuments) {
  3849     return nullptr;
  3852   FindContentData data(aDocument);
  3853   PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data);
  3855   return data.mResult;
  3858 bool
  3859 nsDocument::IsNodeOfType(uint32_t aFlags) const
  3861     return !(aFlags & ~eDOCUMENT);
  3864 Element*
  3865 nsIDocument::GetRootElement() const
  3867   return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
  3868          mCachedRootElement : GetRootElementInternal();
  3871 Element*
  3872 nsDocument::GetRootElementInternal() const
  3874   // Loop backwards because any non-elements, such as doctypes and PIs
  3875   // are likely to appear before the root element.
  3876   uint32_t i;
  3877   for (i = mChildren.ChildCount(); i > 0; --i) {
  3878     nsIContent* child = mChildren.ChildAt(i - 1);
  3879     if (child->IsElement()) {
  3880       const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
  3881       return child->AsElement();
  3885   const_cast<nsDocument*>(this)->mCachedRootElement = nullptr;
  3886   return nullptr;
  3889 nsIContent *
  3890 nsDocument::GetChildAt(uint32_t aIndex) const
  3892   return mChildren.GetSafeChildAt(aIndex);
  3895 int32_t
  3896 nsDocument::IndexOf(const nsINode* aPossibleChild) const
  3898   return mChildren.IndexOfChild(aPossibleChild);
  3901 uint32_t
  3902 nsDocument::GetChildCount() const
  3904   return mChildren.ChildCount();
  3907 nsIContent * const *
  3908 nsDocument::GetChildArray(uint32_t* aChildCount) const
  3910   return mChildren.GetChildArray(aChildCount);
  3914 nsresult
  3915 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
  3916                           bool aNotify)
  3918   if (aKid->IsElement() && GetRootElement()) {
  3919     NS_WARNING("Inserting root element when we already have one");
  3920     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
  3923   return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
  3926 nsresult
  3927 nsDocument::AppendChildTo(nsIContent* aKid, bool aNotify)
  3929   // Make sure to _not_ call the subclass InsertChildAt here.  If
  3930   // subclasses wanted to hook into this stuff, they would have
  3931   // overridden AppendChildTo.
  3932   // XXXbz maybe this should just be a non-virtual method on nsINode?
  3933   // Feels that way to me...
  3934   return nsDocument::InsertChildAt(aKid, GetChildCount(), aNotify);
  3937 void
  3938 nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
  3940   nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
  3941   if (!oldKid) {
  3942     return;
  3945   if (oldKid->IsElement()) {
  3946     // Destroy the link map up front before we mess with the child list.
  3947     DestroyElementMaps();
  3950   doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
  3951   mCachedRootElement = nullptr;
  3954 int32_t
  3955 nsDocument::GetNumberOfStyleSheets() const
  3957   return mStyleSheets.Count();
  3960 nsIStyleSheet*
  3961 nsDocument::GetStyleSheetAt(int32_t aIndex) const
  3963   NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr);
  3964   return mStyleSheets[aIndex];
  3967 int32_t
  3968 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
  3970   return mStyleSheets.IndexOf(aSheet);
  3973 void
  3974 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
  3976   nsCOMPtr<nsIPresShell> shell = GetShell();
  3977   if (shell) {
  3978     shell->StyleSet()->AddDocStyleSheet(aSheet, this);
  3982 #define DO_STYLESHEET_NOTIFICATION(createFunc, concreteInterface, initMethod, type, ...) \
  3983   do {                                                                  \
  3984     nsCOMPtr<nsIDOMEvent> event;                                        \
  3985     nsresult rv = createFunc(getter_AddRefs(event), this,               \
  3986                              mPresShell ?                               \
  3987                              mPresShell->GetPresContext() : nullptr,    \
  3988                              nullptr);                                  \
  3989     if (NS_FAILED(rv)) {                                                \
  3990       return;                                                           \
  3991     }                                                                   \
  3992     nsCOMPtr<nsIDOMCSSStyleSheet> cssSheet(do_QueryInterface(aSheet));  \
  3993     if (!cssSheet) {                                                    \
  3994       return;                                                           \
  3995     }                                                                   \
  3996     nsCOMPtr<concreteInterface> ssEvent(do_QueryInterface(event));      \
  3997     MOZ_ASSERT(ssEvent);                                                \
  3998     ssEvent->initMethod(NS_LITERAL_STRING(type), true, true,            \
  3999                         cssSheet, __VA_ARGS__);                         \
  4000     event->SetTrusted(true);                                            \
  4001     event->SetTarget(this);                                             \
  4002     nsRefPtr<AsyncEventDispatcher> asyncDispatcher =                    \
  4003       new AsyncEventDispatcher(this, event);                            \
  4004     asyncDispatcher->mDispatchChromeOnly = true;                        \
  4005     asyncDispatcher->PostDOMEvent();                                    \
  4006   } while (0);
  4008 void
  4009 nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
  4011   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
  4013   if (StyleSheetChangeEventsEnabled()) {
  4014     DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent,
  4015                                nsIDOMStyleSheetChangeEvent,
  4016                                InitStyleSheetChangeEvent,
  4017                                "StyleSheetAdded",
  4018                                aDocumentSheet);
  4022 void
  4023 nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet)
  4025   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
  4027   if (StyleSheetChangeEventsEnabled()) {
  4028     DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent,
  4029                                nsIDOMStyleSheetChangeEvent,
  4030                                InitStyleSheetChangeEvent,
  4031                                "StyleSheetRemoved",
  4032                                aDocumentSheet);
  4036 void
  4037 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
  4039   NS_PRECONDITION(aSheet, "null arg");
  4040   mStyleSheets.AppendObject(aSheet);
  4041   aSheet->SetOwningDocument(this);
  4043   if (aSheet->IsApplicable()) {
  4044     AddStyleSheetToStyleSets(aSheet);
  4047   NotifyStyleSheetAdded(aSheet, true);
  4050 void
  4051 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
  4053   nsCOMPtr<nsIPresShell> shell = GetShell();
  4054   if (shell) {
  4055     shell->StyleSet()->RemoveDocStyleSheet(aSheet);
  4059 void
  4060 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
  4062   NS_PRECONDITION(aSheet, "null arg");
  4063   nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
  4065   if (!mStyleSheets.RemoveObject(aSheet)) {
  4066     NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
  4067     return;
  4070   if (!mIsGoingAway) {
  4071     if (aSheet->IsApplicable()) {
  4072       RemoveStyleSheetFromStyleSets(aSheet);
  4075     NotifyStyleSheetRemoved(aSheet, true);
  4078   aSheet->SetOwningDocument(nullptr);
  4081 void
  4082 nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
  4083                               nsCOMArray<nsIStyleSheet>& aNewSheets)
  4085   BeginUpdate(UPDATE_STYLE);
  4087   // XXX Need to set the sheet on the ownernode, if any
  4088   NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(),
  4089                   "The lists must be the same length!");
  4090   int32_t count = aOldSheets.Count();
  4092   nsCOMPtr<nsIStyleSheet> oldSheet;
  4093   int32_t i;
  4094   for (i = 0; i < count; ++i) {
  4095     oldSheet = aOldSheets[i];
  4097     // First remove the old sheet.
  4098     NS_ASSERTION(oldSheet, "None of the old sheets should be null");
  4099     int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
  4100     RemoveStyleSheet(oldSheet);  // This does the right notifications
  4102     // Now put the new one in its place.  If it's null, just ignore it.
  4103     nsIStyleSheet* newSheet = aNewSheets[i];
  4104     if (newSheet) {
  4105       mStyleSheets.InsertObjectAt(newSheet, oldIndex);
  4106       newSheet->SetOwningDocument(this);
  4107       if (newSheet->IsApplicable()) {
  4108         AddStyleSheetToStyleSets(newSheet);
  4111       NotifyStyleSheetAdded(newSheet, true);
  4115   EndUpdate(UPDATE_STYLE);
  4118 void
  4119 nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex)
  4121   NS_PRECONDITION(aSheet, "null ptr");
  4122   mStyleSheets.InsertObjectAt(aSheet, aIndex);
  4124   aSheet->SetOwningDocument(this);
  4126   if (aSheet->IsApplicable()) {
  4127     AddStyleSheetToStyleSets(aSheet);
  4130   NotifyStyleSheetAdded(aSheet, true);
  4134 void
  4135 nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
  4136                                          bool aApplicable)
  4138   NS_PRECONDITION(aSheet, "null arg");
  4140   // If we're actually in the document style sheet list
  4141   if (-1 != mStyleSheets.IndexOf(aSheet)) {
  4142     if (aApplicable) {
  4143       AddStyleSheetToStyleSets(aSheet);
  4144     } else {
  4145       RemoveStyleSheetFromStyleSets(aSheet);
  4149   // We have to always notify, since this will be called for sheets
  4150   // that are children of sheets in our style set, as well as some
  4151   // sheets for nsHTMLEditor.
  4153   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
  4154                                (this, aSheet, aApplicable));
  4156   if (StyleSheetChangeEventsEnabled()) {
  4157     DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetApplicableStateChangeEvent,
  4158                                nsIDOMStyleSheetApplicableStateChangeEvent,
  4159                                InitStyleSheetApplicableStateChangeEvent,
  4160                                "StyleSheetApplicableStateChanged",
  4161                                aApplicable);
  4164   if (!mSSApplicableStateNotificationPending) {
  4165     nsRefPtr<nsIRunnable> notification = NS_NewRunnableMethod(this,
  4166       &nsDocument::NotifyStyleSheetApplicableStateChanged);
  4167     mSSApplicableStateNotificationPending =
  4168       NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
  4172 void
  4173 nsDocument::NotifyStyleSheetApplicableStateChanged()
  4175   mSSApplicableStateNotificationPending = false;
  4176   nsCOMPtr<nsIObserverService> observerService =
  4177     mozilla::services::GetObserverService();
  4178   if (observerService) {
  4179     observerService->NotifyObservers(static_cast<nsIDocument*>(this),
  4180                                      "style-sheet-applicable-state-changed",
  4181                                      nullptr);
  4185 // These three functions are a lot like the implementation of the
  4186 // corresponding API for regular stylesheets.
  4188 int32_t
  4189 nsDocument::GetNumberOfCatalogStyleSheets() const
  4191   return mCatalogSheets.Count();
  4194 nsIStyleSheet*
  4195 nsDocument::GetCatalogStyleSheetAt(int32_t aIndex) const
  4197   NS_ENSURE_TRUE(0 <= aIndex && aIndex < mCatalogSheets.Count(), nullptr);
  4198   return mCatalogSheets[aIndex];
  4201 void
  4202 nsDocument::AddCatalogStyleSheet(nsCSSStyleSheet* aSheet)
  4204   mCatalogSheets.AppendObject(aSheet);
  4205   aSheet->SetOwningDocument(this);
  4206   aSheet->SetOwningNode(this);
  4208   if (aSheet->IsApplicable()) {
  4209     // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
  4210     nsCOMPtr<nsIPresShell> shell = GetShell();
  4211     if (shell) {
  4212       shell->StyleSet()->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
  4216   NotifyStyleSheetAdded(aSheet, false);
  4219 void
  4220 nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI)
  4222   mozilla::css::Loader* cssLoader = CSSLoader();
  4223   if (cssLoader->GetEnabled()) {
  4224     int32_t sheetCount = GetNumberOfCatalogStyleSheets();
  4225     for (int32_t i = 0; i < sheetCount; i++) {
  4226       nsIStyleSheet* sheet = GetCatalogStyleSheetAt(i);
  4227       NS_ASSERTION(sheet, "unexpected null stylesheet in the document");
  4228       if (sheet) {
  4229         nsAutoCString uriStr;
  4230         sheet->GetSheetURI()->GetSpec(uriStr);
  4231         if (uriStr.Equals(aStyleSheetURI))
  4232           return;
  4236     nsCOMPtr<nsIURI> uri;
  4237     NS_NewURI(getter_AddRefs(uri), aStyleSheetURI);
  4238     if (uri) {
  4239       nsRefPtr<nsCSSStyleSheet> sheet;
  4240       cssLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet));
  4241       if (sheet) {
  4242         BeginUpdate(UPDATE_STYLE);
  4243         AddCatalogStyleSheet(sheet);
  4244         EndUpdate(UPDATE_STYLE);
  4250 static nsStyleSet::sheetType
  4251 ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
  4253   switch(aType) {
  4254     case nsIDocument::eAgentSheet:
  4255       return nsStyleSet::eAgentSheet;
  4256     case nsIDocument::eUserSheet:
  4257       return nsStyleSet::eUserSheet;
  4258     case nsIDocument::eAuthorSheet:
  4259       return nsStyleSet::eDocSheet;
  4260     default:
  4261       NS_ASSERTION(false, "wrong type");
  4262       // we must return something although this should never happen
  4263       return nsStyleSet::eSheetTypeCount;
  4267 static int32_t
  4268 FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI)
  4270   for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) {
  4271     bool bEqual;
  4272     nsIURI* uri = aSheets[i]->GetSheetURI();
  4274     if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
  4275       return i;
  4278   return -1;
  4281 nsresult
  4282 nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
  4284   NS_PRECONDITION(aSheetURI, "null arg");
  4286   // Checking if we have loaded this one already.
  4287   if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
  4288     return NS_ERROR_INVALID_ARG;
  4290   // Loading the sheet sync.
  4291   nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader();
  4293   nsRefPtr<nsCSSStyleSheet> sheet;
  4294   nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet,
  4295     true, getter_AddRefs(sheet));
  4296   NS_ENSURE_SUCCESS(rv, rv);
  4298   mAdditionalSheets[aType].AppendObject(sheet);
  4299   sheet->SetOwningDocument(this);
  4300   MOZ_ASSERT(sheet->IsApplicable());
  4302   BeginUpdate(UPDATE_STYLE);
  4303   nsCOMPtr<nsIPresShell> shell = GetShell();
  4304   if (shell) {
  4305     nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
  4306     shell->StyleSet()->AppendStyleSheet(type, sheet);
  4309   // Passing false, so documet.styleSheets.length will not be affected by
  4310   // these additional sheets.
  4311   NotifyStyleSheetAdded(sheet, false);
  4312   EndUpdate(UPDATE_STYLE);
  4314   return NS_OK;
  4317 void
  4318 nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
  4320   MOZ_ASSERT(aSheetURI);
  4322   nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType];
  4324   int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
  4325   if (i >= 0) {
  4326     nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i];
  4327     sheets.RemoveObjectAt(i);
  4329     BeginUpdate(UPDATE_STYLE);
  4330     if (!mIsGoingAway) {
  4331       MOZ_ASSERT(sheetRef->IsApplicable());
  4332       nsCOMPtr<nsIPresShell> shell = GetShell();
  4333       if (shell) {
  4334         nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
  4335         shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
  4339     // Passing false, so documet.styleSheets.length will not be affected by
  4340     // these additional sheets.
  4341     NotifyStyleSheetRemoved(sheetRef, false);
  4342     EndUpdate(UPDATE_STYLE);
  4344     sheetRef->SetOwningDocument(nullptr);
  4348 nsIStyleSheet*
  4349 nsDocument::FirstAdditionalAuthorSheet()
  4351   return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0);
  4354 nsIGlobalObject*
  4355 nsDocument::GetScopeObject() const
  4357   nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
  4358   return scope;
  4361 void
  4362 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
  4364   mScopeObject = do_GetWeakReference(aGlobal);
  4365   if (aGlobal) {
  4366     mHasHadScriptHandlingObject = true;
  4370 static void
  4371 NotifyActivityChanged(nsIContent *aContent, void *aUnused)
  4373   nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent));
  4374   if (domMediaElem) {
  4375     HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(aContent);
  4376     mediaElem->NotifyOwnerDocumentActivityChanged();
  4378   nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aContent));
  4379   if (objectLoadingContent) {
  4380     nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
  4381     olc->NotifyOwnerDocumentActivityChanged();
  4385 void
  4386 nsIDocument::SetContainer(nsDocShell* aContainer)
  4388   if (aContainer) {
  4389     mDocumentContainer = aContainer->asWeakPtr();
  4390   } else {
  4391     mDocumentContainer = WeakPtr<nsDocShell>();
  4394   EnumerateFreezableElements(NotifyActivityChanged, nullptr);
  4395   if (!aContainer) {
  4396     return;
  4399   // Get the Docshell
  4400   if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
  4401     // check if same type root
  4402     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
  4403     aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
  4404     NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
  4406     if (sameTypeRoot == aContainer) {
  4407       static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
  4412 nsISupports*
  4413 nsIDocument::GetContainer() const
  4415   return static_cast<nsIDocShell*>(mDocumentContainer);
  4418 void
  4419 nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
  4421 #ifdef DEBUG
  4423     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject));
  4425     NS_ASSERTION(!win || win->IsInnerWindow(),
  4426                  "Script global object must be an inner window!");
  4428 #endif
  4429   NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController ||
  4430                     mAnimationController->IsPausedByType(
  4431                         nsSMILTimeContainer::PAUSE_PAGEHIDE |
  4432                         nsSMILTimeContainer::PAUSE_BEGIN),
  4433                     "Clearing window pointer while animations are unpaused");
  4435   if (mScriptGlobalObject && !aScriptGlobalObject) {
  4436     // We're detaching from the window.  We need to grab a pointer to
  4437     // our layout history state now.
  4438     mLayoutHistoryState = GetLayoutHistoryState();
  4440     if (mPresShell && !EventHandlingSuppressed()) {
  4441       RevokeAnimationFrameNotifications();
  4444     // Also make sure to remove our onload blocker now if we haven't done it yet
  4445     if (mOnloadBlockCount != 0) {
  4446       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  4447       if (loadGroup) {
  4448         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
  4453   mScriptGlobalObject = aScriptGlobalObject;
  4455   if (aScriptGlobalObject) {
  4456     mHasHadScriptHandlingObject = true;
  4457     mHasHadDefaultView = true;
  4458     // Go back to using the docshell for the layout history state
  4459     mLayoutHistoryState = nullptr;
  4460     mScopeObject = do_GetWeakReference(aScriptGlobalObject);
  4461 #ifdef DEBUG
  4462     if (!mWillReparent) {
  4463       // We really shouldn't have a wrapper here but if we do we need to make sure
  4464       // it has the correct parent.
  4465       JSObject *obj = GetWrapperPreserveColor();
  4466       if (obj) {
  4467         JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
  4468         NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
  4469                      "Wrong scope, this is really bad!");
  4472 #endif
  4474     if (mAllowDNSPrefetch) {
  4475       nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  4476       if (docShell) {
  4477 #ifdef DEBUG
  4478         nsCOMPtr<nsIWebNavigation> webNav =
  4479           do_GetInterface(aScriptGlobalObject);
  4480         NS_ASSERTION(SameCOMIdentity(webNav, docShell),
  4481                      "Unexpected container or script global?");
  4482 #endif
  4483         bool allowDNSPrefetch;
  4484         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
  4485         mAllowDNSPrefetch = allowDNSPrefetch;
  4489     MaybeRescheduleAnimationFrameNotifications();
  4490     mRegistry = new Registry();
  4493   // Remember the pointer to our window (or lack there of), to avoid
  4494   // having to QI every time it's asked for.
  4495   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject);
  4496   mWindow = window;
  4498   // Now that we know what our window is, we can flush the CSP errors to the
  4499   // Web Console. We are flushing all messages that occured and were stored
  4500   // in the queue prior to this point.
  4501   FlushCSPWebConsoleErrorQueue();
  4502   nsCOMPtr<nsIHttpChannelInternal> internalChannel =
  4503     do_QueryInterface(GetChannel());
  4504   if (internalChannel) {
  4505     nsCOMArray<nsISecurityConsoleMessage> messages;
  4506     internalChannel->TakeAllSecurityMessages(messages);
  4507     SendToConsole(messages);
  4510   // Set our visibility state, but do not fire the event.  This is correct
  4511   // because either we're coming out of bfcache (in which case IsVisible() will
  4512   // still test false at this point and no state change will happen) or we're
  4513   // doing the initial document load and don't want to fire the event for this
  4514   // change.
  4515   mVisibilityState = GetVisibilityState();
  4518 nsIScriptGlobalObject*
  4519 nsDocument::GetScriptHandlingObjectInternal() const
  4521   MOZ_ASSERT(!mScriptGlobalObject,
  4522              "Do not call this when mScriptGlobalObject is set!");
  4523   if (mHasHadDefaultView) {
  4524     return nullptr;
  4527   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
  4528     do_QueryReferent(mScopeObject);
  4529   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject);
  4530   if (win) {
  4531     NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!");
  4532     nsPIDOMWindow* outer = win->GetOuterWindow();
  4533     if (!outer || outer->GetCurrentInnerWindow() != win) {
  4534       NS_WARNING("Wrong inner/outer window combination!");
  4535       return nullptr;
  4538   return scriptHandlingObject;
  4540 void
  4541 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
  4543   NS_ASSERTION(!mScriptGlobalObject ||
  4544                mScriptGlobalObject == aScriptObject,
  4545                "Wrong script object!");
  4546   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject);
  4547   NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!");
  4548   if (aScriptObject) {
  4549     mScopeObject = do_GetWeakReference(aScriptObject);
  4550     mHasHadScriptHandlingObject = true;
  4551     mHasHadDefaultView = false;
  4555 bool
  4556 nsDocument::IsTopLevelContentDocument()
  4558   return mIsTopLevelContentDocument;
  4561 void
  4562 nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument)
  4564   mIsTopLevelContentDocument = aIsTopLevelContentDocument;
  4567 nsPIDOMWindow *
  4568 nsDocument::GetWindowInternal() const
  4570   MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
  4571   // Let's use mScriptGlobalObject. Even if the document is already removed from
  4572   // the docshell, the outer window might be still obtainable from the it.
  4573   nsCOMPtr<nsPIDOMWindow> win;
  4574   if (mRemovedFromDocShell) {
  4575     nsCOMPtr<nsIInterfaceRequestor> requestor(mDocumentContainer);
  4576     if (requestor) {
  4577       // The docshell returns the outer window we are done.
  4578       win = do_GetInterface(requestor);
  4580   } else {
  4581     win = do_QueryInterface(mScriptGlobalObject);
  4582     if (win) {
  4583       // mScriptGlobalObject is always the inner window, let's get the outer.
  4584       win = win->GetOuterWindow();
  4588   return win;
  4591 nsScriptLoader*
  4592 nsDocument::ScriptLoader()
  4594   return mScriptLoader;
  4597 bool
  4598 nsDocument::InternalAllowXULXBL()
  4600   if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
  4601     mAllowXULXBL = eTriTrue;
  4602     return true;
  4605   mAllowXULXBL = eTriFalse;
  4606   return false;
  4609 // Note: We don't hold a reference to the document observer; we assume
  4610 // that it has a live reference to the document.
  4611 void
  4612 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
  4614   NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
  4615                "Observer already in the list");
  4616   mObservers.AppendElement(aObserver);
  4617   AddMutationObserver(aObserver);
  4620 bool
  4621 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
  4623   // If we're in the process of destroying the document (and we're
  4624   // informing the observers of the destruction), don't remove the
  4625   // observers from the list. This is not a big deal, since we
  4626   // don't hold a live reference to the observers.
  4627   if (!mInDestructor) {
  4628     RemoveMutationObserver(aObserver);
  4629     return mObservers.RemoveElement(aObserver);
  4632   return mObservers.Contains(aObserver);
  4635 void
  4636 nsDocument::MaybeEndOutermostXBLUpdate()
  4638   // Only call BindingManager()->EndOutermostUpdate() when
  4639   // we're not in an update and it is safe to run scripts.
  4640   if (mUpdateNestLevel == 0 && mInXBLUpdate) {
  4641     if (nsContentUtils::IsSafeToRunScript()) {
  4642       mInXBLUpdate = false;
  4643       BindingManager()->EndOutermostUpdate();
  4644     } else if (!mInDestructor) {
  4645       nsContentUtils::AddScriptRunner(
  4646         NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate));
  4651 void
  4652 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
  4654   if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
  4655     mInXBLUpdate = true;
  4656     BindingManager()->BeginOutermostUpdate();
  4659   ++mUpdateNestLevel;
  4660   nsContentUtils::AddScriptBlocker();
  4661   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
  4664 void
  4665 nsDocument::EndUpdate(nsUpdateType aUpdateType)
  4667   NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
  4669   nsContentUtils::RemoveScriptBlocker();
  4671   --mUpdateNestLevel;
  4673   // This set of updates may have created XBL bindings.  Let the
  4674   // binding manager know we're done.
  4675   MaybeEndOutermostXBLUpdate();
  4677   MaybeInitializeFinalizeFrameLoaders();
  4680 void
  4681 nsDocument::BeginLoad()
  4683   // Block onload here to prevent having to deal with blocking and
  4684   // unblocking it while we know the document is loading.
  4685   BlockOnload();
  4686   mDidFireDOMContentLoaded = false;
  4687   BlockDOMContentLoaded();
  4689   if (mScriptLoader) {
  4690     mScriptLoader->BeginDeferringScripts();
  4693   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
  4696 void
  4697 nsDocument::ReportEmptyGetElementByIdArg()
  4699   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  4700                                   NS_LITERAL_CSTRING("DOM"), this,
  4701                                   nsContentUtils::eDOM_PROPERTIES,
  4702                                   "EmptyGetElementByIdParam");
  4705 Element*
  4706 nsDocument::GetElementById(const nsAString& aElementId)
  4708   if (!CheckGetElementByIdArg(aElementId)) {
  4709     return nullptr;
  4712   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
  4713   return entry ? entry->GetIdElement() : nullptr;
  4716 const nsSmallVoidArray*
  4717 nsDocument::GetAllElementsForId(const nsAString& aElementId) const
  4719   if (aElementId.IsEmpty()) {
  4720     return nullptr;
  4723   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
  4724   return entry ? entry->GetIdElements() : nullptr;
  4727 NS_IMETHODIMP
  4728 nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
  4730   Element *content = GetElementById(aId);
  4731   if (content) {
  4732     return CallQueryInterface(content, aReturn);
  4735   *aReturn = nullptr;
  4737   return NS_OK;
  4740 Element*
  4741 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
  4742                                 void* aData, bool aForImage)
  4744   nsDependentAtomString id(aID);
  4746   if (!CheckGetElementByIdArg(id))
  4747     return nullptr;
  4749   nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
  4750   NS_ENSURE_TRUE(entry, nullptr);
  4752   entry->AddContentChangeCallback(aObserver, aData, aForImage);
  4753   return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
  4756 void
  4757 nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
  4758                                    void* aData, bool aForImage)
  4760   nsDependentAtomString id(aID);
  4762   if (!CheckGetElementByIdArg(id))
  4763     return;
  4765   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
  4766   if (!entry) {
  4767     return;
  4770   entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
  4773 NS_IMETHODIMP
  4774 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
  4775                                nsIDOMElement* aImageElement)
  4777   nsCOMPtr<Element> el = do_QueryInterface(aImageElement);
  4778   MozSetImageElement(aImageElementId, el);
  4779   return NS_OK;
  4782 void
  4783 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
  4784                                Element* aElement)
  4786   if (aImageElementId.IsEmpty())
  4787     return;
  4789   // Hold a script blocker while calling SetImageElement since that can call
  4790   // out to id-observers
  4791   nsAutoScriptBlocker scriptBlocker;
  4793   nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId);
  4794   if (entry) {
  4795     entry->SetImageElement(aElement);
  4796     if (entry->IsEmpty()) {
  4797       mIdentifierMap.RemoveEntry(aImageElementId);
  4802 Element*
  4803 nsDocument::LookupImageElement(const nsAString& aId)
  4805   if (aId.IsEmpty())
  4806     return nullptr;
  4808   nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
  4809   return entry ? entry->GetImageIdElement() : nullptr;
  4812 void
  4813 nsDocument::DispatchContentLoadedEvents()
  4815   // If you add early returns from this method, make sure you're
  4816   // calling UnblockOnload properly.
  4818   // Unpin references to preloaded images
  4819   mPreloadingImages.Clear();
  4821   if (mTiming) {
  4822     mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
  4825   // Dispatch observer notification to notify observers document is interactive.
  4826   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  4827   nsIPrincipal *principal = GetPrincipal();
  4828   os->NotifyObservers(static_cast<nsIDocument*>(this),
  4829                       nsContentUtils::IsSystemPrincipal(principal) ?
  4830                         "chrome-document-interactive" :
  4831                         "content-document-interactive",
  4832                       nullptr);
  4834   // Fire a DOM event notifying listeners that this document has been
  4835   // loaded (excluding images and other loads initiated by this
  4836   // document).
  4837   nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
  4838                                        NS_LITERAL_STRING("DOMContentLoaded"),
  4839                                        true, true);
  4841   if (mTiming) {
  4842     mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
  4845   // If this document is a [i]frame, fire a DOMFrameContentLoaded
  4846   // event on all parent documents notifying that the HTML (excluding
  4847   // other external files such as images and stylesheets) in a frame
  4848   // has finished loading.
  4850   // target_frame is the [i]frame element that will be used as the
  4851   // target for the event. It's the [i]frame whose content is done
  4852   // loading.
  4853   nsCOMPtr<EventTarget> target_frame;
  4855   if (mParentDocument) {
  4856     target_frame = mParentDocument->FindContentForSubDocument(this);
  4859   if (target_frame) {
  4860     nsCOMPtr<nsIDocument> parent = mParentDocument;
  4861     do {
  4862       nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent);
  4864       nsCOMPtr<nsIDOMEvent> event;
  4865       if (domDoc) {
  4866         domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
  4867                             getter_AddRefs(event));
  4871       if (event) {
  4872         event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
  4873                          true);
  4875         event->SetTarget(target_frame);
  4876         event->SetTrusted(true);
  4878         // To dispatch this event we must manually call
  4879         // EventDispatcher::Dispatch() on the ancestor document since the
  4880         // target is not in the same document, so the event would never reach
  4881         // the ancestor document if we used the normal event
  4882         // dispatching code.
  4884         WidgetEvent* innerEvent = event->GetInternalNSEvent();
  4885         if (innerEvent) {
  4886           nsEventStatus status = nsEventStatus_eIgnore;
  4888           nsIPresShell *shell = parent->GetShell();
  4889           if (shell) {
  4890             nsRefPtr<nsPresContext> context = shell->GetPresContext();
  4892             if (context) {
  4893               EventDispatcher::Dispatch(parent, context, innerEvent, event,
  4894                                         &status);
  4900       parent = parent->GetParentDocument();
  4901     } while (parent);
  4904   // If the document has a manifest attribute, fire a MozApplicationManifest
  4905   // event.
  4906   Element* root = GetRootElement();
  4907   if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
  4908     nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
  4909                                         NS_LITERAL_STRING("MozApplicationManifest"),
  4910                                         true, true);
  4913   UnblockOnload(true);
  4916 void
  4917 nsDocument::EndLoad()
  4919   // Drop the ref to our parser, if any, but keep hold of the sink so that we
  4920   // can flush it from FlushPendingNotifications as needed.  We might have to
  4921   // do that to get a StartLayout() to happen.
  4922   if (mParser) {
  4923     mWeakSink = do_GetWeakReference(mParser->GetContentSink());
  4924     mParser = nullptr;
  4927   NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
  4929   UnblockDOMContentLoaded();
  4932 void
  4933 nsDocument::UnblockDOMContentLoaded()
  4935   MOZ_ASSERT(mBlockDOMContentLoaded);
  4936   if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
  4937     return;
  4939   mDidFireDOMContentLoaded = true;
  4941   MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
  4942   if (!mSynchronousDOMContentLoaded) {
  4943     nsRefPtr<nsIRunnable> ev =
  4944       NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
  4945     NS_DispatchToCurrentThread(ev);
  4946   } else {
  4947     DispatchContentLoadedEvents();
  4951 void
  4952 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
  4954   NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
  4955                   "Someone forgot a scriptblocker");
  4956   NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
  4957                                (this, aContent, aStateMask));
  4960 void
  4961 nsDocument::DocumentStatesChanged(EventStates aStateMask)
  4963   // Invalidate our cached state.
  4964   mGotDocumentState &= ~aStateMask;
  4965   mDocumentState &= ~aStateMask;
  4967   NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
  4970 void
  4971 nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
  4972                              nsIStyleRule* aOldStyleRule,
  4973                              nsIStyleRule* aNewStyleRule)
  4975   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
  4976                                (this, aSheet,
  4977                                 aOldStyleRule, aNewStyleRule));
  4979   if (StyleSheetChangeEventsEnabled()) {
  4980     nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule);
  4981     DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent,
  4982                                nsIDOMStyleRuleChangeEvent,
  4983                                InitStyleRuleChangeEvent,
  4984                                "StyleRuleChanged",
  4985                                rule ? rule->GetDOMRule() : nullptr);
  4989 void
  4990 nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
  4991                            nsIStyleRule* aStyleRule)
  4993   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
  4994                                (this, aSheet, aStyleRule));
  4996   if (StyleSheetChangeEventsEnabled()) {
  4997     nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
  4998     DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent,
  4999                                nsIDOMStyleRuleChangeEvent,
  5000                                InitStyleRuleChangeEvent,
  5001                                "StyleRuleAdded",
  5002                                rule ? rule->GetDOMRule() : nullptr);
  5006 void
  5007 nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
  5008                              nsIStyleRule* aStyleRule)
  5010   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
  5011                                (this, aSheet, aStyleRule));
  5013   if (StyleSheetChangeEventsEnabled()) {
  5014     nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
  5015     DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent,
  5016                                nsIDOMStyleRuleChangeEvent,
  5017                                InitStyleRuleChangeEvent,
  5018                                "StyleRuleRemoved",
  5019                                rule ? rule->GetDOMRule() : nullptr);
  5023 #undef DO_STYLESHEET_NOTIFICATION
  5026 //
  5027 // nsIDOMDocument interface
  5028 //
  5029 DocumentType*
  5030 nsIDocument::GetDoctype() const
  5032   for (nsIContent* child = GetFirstChild();
  5033        child;
  5034        child = child->GetNextSibling()) {
  5035     if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
  5036       return static_cast<DocumentType*>(child);
  5039   return nullptr;
  5042 NS_IMETHODIMP
  5043 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
  5045   MOZ_ASSERT(aDoctype);
  5046   nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype();
  5047   doctype.forget(aDoctype);
  5048   return NS_OK;
  5051 NS_IMETHODIMP
  5052 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
  5054   ErrorResult rv;
  5055   *aImplementation = GetImplementation(rv);
  5056   if (rv.Failed()) {
  5057     MOZ_ASSERT(!*aImplementation);
  5058     return rv.ErrorCode();
  5060   NS_ADDREF(*aImplementation);
  5061   return NS_OK;
  5064 DOMImplementation*
  5065 nsDocument::GetImplementation(ErrorResult& rv)
  5067   if (!mDOMImplementation) {
  5068     nsCOMPtr<nsIURI> uri;
  5069     NS_NewURI(getter_AddRefs(uri), "about:blank");
  5070     if (!uri) {
  5071       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  5072       return nullptr;
  5074     bool hasHadScriptObject = true;
  5075     nsIScriptGlobalObject* scriptObject =
  5076       GetScriptHandlingObject(hasHadScriptObject);
  5077     if (!scriptObject && hasHadScriptObject) {
  5078       rv.Throw(NS_ERROR_UNEXPECTED);
  5079       return nullptr;
  5081     mDOMImplementation = new DOMImplementation(this,
  5082       scriptObject ? scriptObject : GetScopeObject(), uri, uri);
  5085   return mDOMImplementation;
  5088 NS_IMETHODIMP
  5089 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
  5091   NS_ENSURE_ARG_POINTER(aDocumentElement);
  5093   Element* root = GetRootElement();
  5094   if (root) {
  5095     return CallQueryInterface(root, aDocumentElement);
  5098   *aDocumentElement = nullptr;
  5100   return NS_OK;
  5103 NS_IMETHODIMP
  5104 nsDocument::CreateElement(const nsAString& aTagName,
  5105                           nsIDOMElement** aReturn)
  5107   *aReturn = nullptr;
  5108   ErrorResult rv;
  5109   nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
  5110   NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
  5111   return CallQueryInterface(element, aReturn);
  5114 bool IsLowercaseASCII(const nsAString& aValue)
  5116   int32_t len = aValue.Length();
  5117   for (int32_t i = 0; i < len; ++i) {
  5118     char16_t c = aValue[i];
  5119     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
  5120       return false;
  5123   return true;
  5126 already_AddRefed<Element>
  5127 nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
  5129   rv = nsContentUtils::CheckQName(aTagName, false);
  5130   if (rv.Failed()) {
  5131     return nullptr;
  5134   bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName);
  5135   nsAutoString lcTagName;
  5136   if (needsLowercase) {
  5137     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
  5140   nsCOMPtr<nsIContent> content;
  5141   rv = CreateElem(needsLowercase ? lcTagName : aTagName,
  5142                   nullptr, mDefaultElementType, getter_AddRefs(content));
  5143   if (rv.Failed()) {
  5144     return nullptr;
  5146   return dont_AddRef(content.forget().take()->AsElement());
  5149 void
  5150 nsDocument::SwizzleCustomElement(Element* aElement,
  5151                                  const nsAString& aTypeExtension,
  5152                                  uint32_t aNamespaceID,
  5153                                  ErrorResult& rv)
  5155   nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension));
  5156   nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
  5157   if (!mRegistry || tagAtom == typeAtom) {
  5158     return;
  5161   CustomElementDefinition* data;
  5162   CustomElementHashKey key(aNamespaceID, typeAtom);
  5163   if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
  5164     // The type extension doesn't exist in the registry,
  5165     // thus we don't need to swizzle, but it is possibly
  5166     // an upgrade candidate.
  5167     RegisterUnresolvedElement(aElement, typeAtom);
  5168     return;
  5171   if (data->mLocalName != tagAtom) {
  5172     // The element doesn't match the local name for the
  5173     // definition, thus the element isn't a custom element
  5174     // and we don't need to do anything more.
  5175     return;
  5178   if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
  5179     // Swizzling in the parser happens after the "is" attribute is added.
  5180     aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true);
  5183   // Enqueuing the created callback will set the CustomElementData on the
  5184   // element, causing prototype swizzling to occur in Element::WrapObject.
  5185   EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
  5188 already_AddRefed<Element>
  5189 nsDocument::CreateElement(const nsAString& aTagName,
  5190                           const nsAString& aTypeExtension,
  5191                           ErrorResult& rv)
  5193   nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
  5194   if (rv.Failed()) {
  5195     return nullptr;
  5198   SwizzleCustomElement(elem, aTypeExtension,
  5199                        GetDefaultNamespaceID(), rv);
  5200   if (rv.Failed()) {
  5201     return nullptr;
  5204   return elem.forget();
  5207 NS_IMETHODIMP
  5208 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
  5209                             const nsAString& aQualifiedName,
  5210                             nsIDOMElement** aReturn)
  5212   *aReturn = nullptr;
  5213   ErrorResult rv;
  5214   nsCOMPtr<Element> element =
  5215     nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
  5216   NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
  5217   return CallQueryInterface(element, aReturn);
  5220 already_AddRefed<Element>
  5221 nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
  5222                              const nsAString& aQualifiedName,
  5223                              ErrorResult& rv)
  5225   nsCOMPtr<nsINodeInfo> nodeInfo;
  5226   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
  5227                                             aQualifiedName,
  5228                                             mNodeInfoManager,
  5229                                             nsIDOMNode::ELEMENT_NODE,
  5230                                             getter_AddRefs(nodeInfo));
  5231   if (rv.Failed()) {
  5232     return nullptr;
  5235   nsCOMPtr<Element> element;
  5236   rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
  5237                      NOT_FROM_PARSER);
  5238   if (rv.Failed()) {
  5239     return nullptr;
  5241   return element.forget();
  5244 already_AddRefed<Element>
  5245 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
  5246                             const nsAString& aQualifiedName,
  5247                             const nsAString& aTypeExtension,
  5248                             ErrorResult& rv)
  5250   nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
  5251                                                         aQualifiedName,
  5252                                                         rv);
  5253   if (rv.Failed()) {
  5254     return nullptr;
  5257   int32_t nameSpaceId = kNameSpaceID_Wildcard;
  5258   if (!aNamespaceURI.EqualsLiteral("*")) {
  5259     rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
  5260                                                                nameSpaceId);
  5261     if (rv.Failed()) {
  5262       return nullptr;
  5266   SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv);
  5267   if (rv.Failed()) {
  5268     return nullptr;
  5271   return elem.forget();
  5274 NS_IMETHODIMP
  5275 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
  5277   *aReturn = nsIDocument::CreateTextNode(aData).take();
  5278   return NS_OK;
  5281 already_AddRefed<nsTextNode>
  5282 nsIDocument::CreateTextNode(const nsAString& aData) const
  5284   nsRefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
  5285   // Don't notify; this node is still being created.
  5286   text->SetText(aData, false);
  5287   return text.forget();
  5290 NS_IMETHODIMP
  5291 nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
  5293   *aReturn = nsIDocument::CreateDocumentFragment().take();
  5294   return NS_OK;
  5297 already_AddRefed<DocumentFragment>
  5298 nsIDocument::CreateDocumentFragment() const
  5300   nsRefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
  5301   return frag.forget();
  5304 NS_IMETHODIMP
  5305 nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
  5307   *aReturn = nsIDocument::CreateComment(aData).take();
  5308   return NS_OK;
  5311 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
  5312 already_AddRefed<dom::Comment>
  5313 nsIDocument::CreateComment(const nsAString& aData) const
  5315   nsRefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
  5317   // Don't notify; this node is still being created.
  5318   comment->SetText(aData, false);
  5319   return comment.forget();
  5322 NS_IMETHODIMP
  5323 nsDocument::CreateCDATASection(const nsAString& aData,
  5324                                nsIDOMCDATASection** aReturn)
  5326   NS_ENSURE_ARG_POINTER(aReturn);
  5327   ErrorResult rv;
  5328   *aReturn = nsIDocument::CreateCDATASection(aData, rv).take();
  5329   return rv.ErrorCode();
  5332 already_AddRefed<CDATASection>
  5333 nsIDocument::CreateCDATASection(const nsAString& aData,
  5334                                 ErrorResult& rv)
  5336   if (IsHTML()) {
  5337     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  5338     return nullptr;
  5341   if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
  5342     rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
  5343     return nullptr;
  5346   nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
  5348   // Don't notify; this node is still being created.
  5349   cdata->SetText(aData, false);
  5351   return cdata.forget();
  5354 NS_IMETHODIMP
  5355 nsDocument::CreateProcessingInstruction(const nsAString& aTarget,
  5356                                         const nsAString& aData,
  5357                                         nsIDOMProcessingInstruction** aReturn)
  5359   ErrorResult rv;
  5360   *aReturn =
  5361     nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take();
  5362   return rv.ErrorCode();
  5365 already_AddRefed<ProcessingInstruction>
  5366 nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
  5367                                          const nsAString& aData,
  5368                                          ErrorResult& rv) const
  5370   nsresult res = nsContentUtils::CheckQName(aTarget, false);
  5371   if (NS_FAILED(res)) {
  5372     rv.Throw(res);
  5373     return nullptr;
  5376   if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
  5377     rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
  5378     return nullptr;
  5381   nsRefPtr<ProcessingInstruction> pi =
  5382     NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
  5384   return pi.forget();
  5387 NS_IMETHODIMP
  5388 nsDocument::CreateAttribute(const nsAString& aName,
  5389                             nsIDOMAttr** aReturn)
  5391   ErrorResult rv;
  5392   *aReturn = nsIDocument::CreateAttribute(aName, rv).take();
  5393   return rv.ErrorCode();
  5396 already_AddRefed<Attr>
  5397 nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
  5399   WarnOnceAbout(eCreateAttribute);
  5401   if (!mNodeInfoManager) {
  5402     rv.Throw(NS_ERROR_NOT_INITIALIZED);
  5403     return nullptr;
  5406   nsresult res = nsContentUtils::CheckQName(aName, false);
  5407   if (NS_FAILED(res)) {
  5408     rv.Throw(res);
  5409     return nullptr;
  5412   nsCOMPtr<nsINodeInfo> nodeInfo;
  5413   res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None,
  5414                                       nsIDOMNode::ATTRIBUTE_NODE,
  5415                                       getter_AddRefs(nodeInfo));
  5416   if (NS_FAILED(res)) {
  5417     rv.Throw(res);
  5418     return nullptr;
  5421   nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
  5422                                       EmptyString(), false);
  5423   return attribute.forget();
  5426 NS_IMETHODIMP
  5427 nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI,
  5428                               const nsAString & aQualifiedName,
  5429                               nsIDOMAttr **aResult)
  5431   ErrorResult rv;
  5432   *aResult =
  5433     nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take();
  5434   return rv.ErrorCode();
  5437 already_AddRefed<Attr>
  5438 nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
  5439                                const nsAString& aQualifiedName,
  5440                                ErrorResult& rv)
  5442   WarnOnceAbout(eCreateAttributeNS);
  5444   nsCOMPtr<nsINodeInfo> nodeInfo;
  5445   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
  5446                                             aQualifiedName,
  5447                                             mNodeInfoManager,
  5448                                             nsIDOMNode::ATTRIBUTE_NODE,
  5449                                             getter_AddRefs(nodeInfo));
  5450   if (rv.Failed()) {
  5451     return nullptr;
  5454   nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
  5455                                       EmptyString(), true);
  5456   return attribute.forget();
  5459 bool
  5460 nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
  5462   JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
  5464   JS::Rooted<JSObject*> global(aCx,
  5465     JS_GetGlobalForObject(aCx, &args.callee()));
  5466   nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
  5467   MOZ_ASSERT(window, "Should have a non-null window");
  5469   nsDocument* document = static_cast<nsDocument*>(window->GetDoc());
  5471   // Function name is the type of the custom element.
  5472   JSString* jsFunName =
  5473     JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
  5474   nsDependentJSString elemName;
  5475   if (!elemName.init(aCx, jsFunName)) {
  5476     return true;
  5479   nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName));
  5480   CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom);
  5481   CustomElementDefinition* definition;
  5482   if (!document->mRegistry ||
  5483       !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
  5484     return true;
  5487   nsDependentAtomString localName(definition->mLocalName);
  5489   nsCOMPtr<nsIContent> newElement;
  5490   nsresult rv = document->CreateElem(localName, nullptr,
  5491                                      definition->mNamespaceID,
  5492                                      getter_AddRefs(newElement));
  5493   NS_ENSURE_SUCCESS(rv, true);
  5495   ErrorResult errorResult;
  5496   nsCOMPtr<Element> element = do_QueryInterface(newElement);
  5497   document->SwizzleCustomElement(element, elemName, definition->mNamespaceID,
  5498                                  errorResult);
  5499   if (errorResult.Failed()) {
  5500     return true;
  5503   rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval());
  5504   NS_ENSURE_SUCCESS(rv, true);
  5506   return true;
  5509 bool
  5510 nsDocument::IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject)
  5512   JS::Rooted<JSObject*> obj(aCx, aObject);
  5513   return Preferences::GetBool("dom.webcomponents.enabled") ||
  5514     IsInCertifiedApp(aCx, obj);
  5517 nsresult
  5518 nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
  5520   if (!mRegistry) {
  5521     return NS_OK;
  5524   nsINodeInfo* info = aElement->NodeInfo();
  5526   // Candidate may be a custom element through extension,
  5527   // in which case the custom element type name will not
  5528   // match the element tag name. e.g. <button is="x-button">.
  5529   nsCOMPtr<nsIAtom> typeName = aTypeName;
  5530   if (!typeName) {
  5531     typeName = info->NameAtom();
  5534   CustomElementHashKey key(info->NamespaceID(), typeName);
  5535   if (mRegistry->mCustomDefinitions.Get(&key)) {
  5536     return NS_OK;
  5539   nsTArray<nsRefPtr<Element>>* unresolved;
  5540   mRegistry->mCandidatesMap.Get(&key, &unresolved);
  5541   if (!unresolved) {
  5542     unresolved = new nsTArray<nsRefPtr<Element>>();
  5543     // Ownership of unresolved is taken by mCandidatesMap.
  5544     mRegistry->mCandidatesMap.Put(&key, unresolved);
  5547   nsRefPtr<Element>* elem = unresolved->AppendElement();
  5548   *elem = aElement;
  5550   return NS_OK;
  5553 namespace {
  5555 class ProcessStackRunner MOZ_FINAL : public nsIRunnable
  5557   public:
  5558   ProcessStackRunner(bool aIsBaseQueue = false)
  5559     : mIsBaseQueue(aIsBaseQueue)
  5562   NS_DECL_ISUPPORTS
  5563   NS_IMETHOD Run() MOZ_OVERRIDE
  5565     nsDocument::ProcessTopElementQueue(mIsBaseQueue);
  5566     return NS_OK;
  5568   bool mIsBaseQueue;
  5569 };
  5571 NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable);
  5573 } // anonymous namespace
  5575 void
  5576 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
  5577                                      Element* aCustomElement,
  5578                                      LifecycleCallbackArgs* aArgs,
  5579                                      CustomElementDefinition* aDefinition)
  5581   if (!mRegistry) {
  5582     // The element might not belong to a document that
  5583     // has a browsing context, and thus no registry.
  5584     return;
  5587   CustomElementData* elementData = aCustomElement->GetCustomElementData();
  5589   // Let DEFINITION be ELEMENT's definition
  5590   CustomElementDefinition* definition = aDefinition;
  5591   if (!definition) {
  5592     nsINodeInfo* info = aCustomElement->NodeInfo();
  5594     // Make sure we get the correct definition in case the element
  5595     // is a extended custom element e.g. <button is="x-button">.
  5596     nsCOMPtr<nsIAtom> typeAtom = elementData ?
  5597       elementData->mType.get() : info->NameAtom();
  5599     CustomElementHashKey key(info->NamespaceID(), typeAtom);
  5600     if (!mRegistry->mCustomDefinitions.Get(&key, &definition) ||
  5601         definition->mLocalName != info->NameAtom()) {
  5602       // Trying to enqueue a callback for an element that is not
  5603       // a custom element. We are done, nothing to do.
  5604       return;
  5608   if (!elementData) {
  5609     // Create the custom element data the first time
  5610     // that we try to enqueue a callback.
  5611     elementData = new CustomElementData(definition->mType);
  5612     // aCustomElement takes ownership of elementData
  5613     aCustomElement->SetCustomElementData(elementData);
  5614     MOZ_ASSERT(aType == nsIDocument::eCreated,
  5615                "First callback should be the created callback");
  5618   // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
  5619   CallbackFunction* func = nullptr;
  5620   switch (aType) {
  5621     case nsIDocument::eCreated:
  5622       if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
  5623         func = definition->mCallbacks->mCreatedCallback.Value();
  5625       break;
  5627     case nsIDocument::eAttached:
  5628       if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
  5629         func = definition->mCallbacks->mAttachedCallback.Value();
  5631       break;
  5633     case nsIDocument::eDetached:
  5634       if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
  5635         func = definition->mCallbacks->mDetachedCallback.Value();
  5637       break;
  5639     case nsIDocument::eAttributeChanged:
  5640       if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
  5641         func = definition->mCallbacks->mAttributeChangedCallback.Value();
  5643       break;
  5646   // If there is no such callback, stop.
  5647   if (!func) {
  5648     return;
  5651   if (aType == nsIDocument::eCreated) {
  5652     elementData->mCreatedCallbackInvoked = false;
  5653   } else if (!elementData->mCreatedCallbackInvoked) {
  5654     // Callbacks other than created callback must not be enqueued
  5655     // until after the created callback has been invoked.
  5656     return;
  5659   // Add CALLBACK to ELEMENT's callback queue.
  5660   CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
  5661                                                               aType,
  5662                                                               func,
  5663                                                               elementData);
  5664   // Ownership of callback is taken by mCallbackQueue.
  5665   elementData->mCallbackQueue.AppendElement(callback);
  5666   if (aArgs) {
  5667     callback->SetArgs(*aArgs);
  5670   if (!elementData->mElementIsBeingCreated) {
  5671     CustomElementData* lastData =
  5672       sProcessingStack.ref().SafeLastElement(nullptr);
  5674     // A new element queue needs to be pushed if the queue at the
  5675     // top of the stack is associated with another microtask level.
  5676     // Don't push a queue for the level 0 microtask (base element queue)
  5677     // because we don't want to process the queue until the
  5678     // microtask checkpoint.
  5679     bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 &&
  5680       (!lastData || lastData->mAssociatedMicroTask <
  5681          static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
  5683     // Push a new element queue onto the processing stack when appropriate
  5684     // (when we enter a new microtask).
  5685     if (shouldPushElementQueue) {
  5686       // Push a sentinel value on the processing stack to mark the
  5687       // boundary between the element queues.
  5688       sProcessingStack.ref().AppendElement((CustomElementData*) nullptr);
  5691     sProcessingStack.ref().AppendElement(elementData);
  5692     elementData->mAssociatedMicroTask =
  5693       static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
  5695     // Add a script runner to pop and process the element queue at
  5696     // the top of the processing stack.
  5697     if (shouldPushElementQueue) {
  5698       // Lifecycle callbacks enqueued by user agent implementation
  5699       // should be invoked prior to returning control back to script.
  5700       // Create a script runner to process the top of the processing
  5701       // stack as soon as it is safe to run script.
  5702       nsContentUtils::AddScriptRunner(new ProcessStackRunner());
  5707 // static
  5708 void
  5709 nsDocument::ProcessBaseElementQueue()
  5711   // Prevent re-entrance. Also, if a microtask checkpoint is reached
  5712   // and there is no processing stack to process, then we are done.
  5713   if (sProcessingBaseElementQueue || sProcessingStack.empty()) {
  5714     return;
  5717   MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0);
  5718   sProcessingBaseElementQueue = true;
  5719   nsContentUtils::AddScriptRunner(new ProcessStackRunner(true));
  5722 // static
  5723 void
  5724 nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
  5726   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
  5728   nsTArray<CustomElementData*>& stack = sProcessingStack.ref();
  5729   uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
  5731   if (aIsBaseQueue && firstQueue != 0) {
  5732     return;
  5735   for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
  5736     // Callback queue may have already been processed in an earlier
  5737     // element queue or in an element queue that was popped
  5738     // off more recently.
  5739     if (stack[i]->mAssociatedMicroTask != -1) {
  5740       stack[i]->RunCallbackQueue();
  5741       stack[i]->mAssociatedMicroTask = -1;
  5745   // If this was actually the base element queue, don't bother trying to pop
  5746   // the first "queue" marker (sentinel).
  5747   if (firstQueue != 0) {
  5748     stack.SetLength(firstQueue);
  5749   } else {
  5750     // Don't pop sentinel for base element queue.
  5751     stack.SetLength(1);
  5752     sProcessingBaseElementQueue = false;
  5756 bool
  5757 nsDocument::RegisterEnabled()
  5759   static bool sPrefValue =
  5760     Preferences::GetBool("dom.webcomponents.enabled", false);
  5761   return sPrefValue;
  5764 // static
  5765 Maybe<nsTArray<mozilla::dom::CustomElementData*>>
  5766 nsDocument::sProcessingStack;
  5768 // static
  5769 bool
  5770 nsDocument::sProcessingBaseElementQueue;
  5772 void
  5773 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
  5774                             const ElementRegistrationOptions& aOptions,
  5775                             JS::MutableHandle<JSObject*> aRetval,
  5776                             ErrorResult& rv)
  5778   if (!mRegistry) {
  5779     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  5780     return;
  5783   Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
  5785   // Unconditionally convert TYPE to lowercase.
  5786   nsAutoString lcType;
  5787   nsContentUtils::ASCIIToLower(aType, lcType);
  5789   // Only convert NAME to lowercase in HTML documents. Note that NAME is
  5790   // options.extends.
  5791   nsAutoString lcName;
  5792   if (IsHTML()) {
  5793     nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
  5794   } else {
  5795     lcName.Assign(aOptions.mExtends);
  5798   nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
  5799   if (!nsContentUtils::IsCustomElementName(typeAtom)) {
  5800     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  5801     return;
  5804   // If there already exists a definition with the same TYPE, set ERROR to
  5805   // DuplicateDefinition and stop.
  5806   // Note that we need to find existing custom elements from either namespace.
  5807   CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom);
  5808   if (definitions.Get(&duplicateFinder)) {
  5809     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  5810     return;
  5813   nsIGlobalObject* sgo = GetScopeObject();
  5814   if (!sgo) {
  5815     rv.Throw(NS_ERROR_UNEXPECTED);
  5816     return;
  5818   JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
  5820   JSAutoCompartment ac(aCx, global);
  5822   JS::Handle<JSObject*> htmlProto(
  5823     HTMLElementBinding::GetProtoObject(aCx, global));
  5824   if (!htmlProto) {
  5825     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  5826     return;
  5829   int32_t namespaceID = kNameSpaceID_XHTML;
  5830   JS::Rooted<JSObject*> protoObject(aCx);
  5831   if (!aOptions.mPrototype) {
  5832     protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr());
  5833     if (!protoObject) {
  5834       rv.Throw(NS_ERROR_UNEXPECTED);
  5835       return;
  5837   } else {
  5838     // If a prototype is provided, we must check to ensure that it is from the
  5839     // same browsing context as us.
  5840     protoObject = aOptions.mPrototype;
  5841     if (JS_GetGlobalForObject(aCx, protoObject) != global) {
  5842       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  5843       return;
  5846     // If PROTOTYPE is already an interface prototype object for any interface
  5847     // object or PROTOTYPE has a non-configurable property named constructor,
  5848     // throw a NotSupportedError and stop.
  5849     const js::Class* clasp = js::GetObjectClass(protoObject);
  5850     if (IsDOMIfaceAndProtoClass(clasp)) {
  5851       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  5852       return;
  5855     JS::Rooted<JSPropertyDescriptor> descRoot(aCx);
  5856     JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot);
  5857     if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
  5858       rv.Throw(NS_ERROR_UNEXPECTED);
  5859       return;
  5862     // Check if non-configurable
  5863     if (desc.isPermanent()) {
  5864       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  5865       return;
  5868     JS::Handle<JSObject*> svgProto(
  5869       SVGElementBinding::GetProtoObject(aCx, global));
  5870     if (!svgProto) {
  5871       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  5872       return;
  5875     JS::Rooted<JSObject*> protoProto(aCx, protoObject);
  5877     // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
  5878     // Namespace.
  5879     while (protoProto) {
  5880       if (protoProto == htmlProto) {
  5881         break;
  5884       if (protoProto == svgProto) {
  5885         namespaceID = kNameSpaceID_SVG;
  5886         break;
  5889       if (!JS_GetPrototype(aCx, protoProto, &protoProto)) {
  5890         rv.Throw(NS_ERROR_UNEXPECTED);
  5891         return;
  5896   // If name was provided and not null...
  5897   nsCOMPtr<nsIAtom> nameAtom;
  5898   if (!lcName.IsEmpty()) {
  5899     // Let BASE be the element interface for NAME and NAMESPACE.
  5900     bool known = false;
  5901     nameAtom = do_GetAtom(lcName);
  5902     if (namespaceID == kNameSpaceID_XHTML) {
  5903       nsIParserService* ps = nsContentUtils::GetParserService();
  5904       if (!ps) {
  5905         rv.Throw(NS_ERROR_UNEXPECTED);
  5906         return;
  5909       known =
  5910         ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined;
  5911     } else {
  5912       known = SVGElementFactory::Exists(nameAtom);
  5915     // If BASE does not exist or is an interface for a custom element, set ERROR
  5916     // to InvalidName and stop.
  5917     // If BASE exists, then it cannot be an interface for a custom element.
  5918     if (!known) {
  5919       rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  5920       return;
  5922   } else {
  5923     // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
  5924     if (namespaceID == kNameSpaceID_SVG) {
  5925       rv.Throw(NS_ERROR_UNEXPECTED);
  5926       return;
  5929     nameAtom = typeAtom;
  5932   nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
  5933   JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject));
  5934   if (!callbacksHolder->Init(aCx, rootedv)) {
  5935     rv.Throw(NS_ERROR_FAILURE);
  5936     return;
  5939   // Associate the definition with the custom element.
  5940   CustomElementHashKey key(namespaceID, typeAtom);
  5941   LifecycleCallbacks* callbacks = callbacksHolder.forget();
  5942   CustomElementDefinition* definition =
  5943     new CustomElementDefinition(protoObject,
  5944                                 typeAtom,
  5945                                 nameAtom,
  5946                                 callbacks,
  5947                                 namespaceID,
  5948                                 0 /* TODO dependent on HTML imports. Bug 877072 */);
  5949   definitions.Put(&key, definition);
  5951   // Do element upgrade.
  5952   nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates;
  5953   mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
  5954   if (candidates) {
  5955     for (size_t i = 0; i < candidates->Length(); ++i) {
  5956       Element *elem = candidates->ElementAt(i);
  5958       // Make sure that the element name matches the name in the definition.
  5959       // (e.g. a definition for x-button extending button should match
  5960       // <button is="x-button"> but not <x-button>.
  5961       if (elem->NodeInfo()->NameAtom() != nameAtom) {
  5962         // Skip over this element because definition does not apply.
  5963         continue;
  5966       nsWrapperCache* cache;
  5967       CallQueryInterface(elem, &cache);
  5968       MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
  5970       JS::RootedObject wrapper(aCx);
  5971       if ((wrapper = cache->GetWrapper())) {
  5972         if (!JS_SetPrototype(aCx, wrapper, protoObject)) {
  5973           continue;
  5977       EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
  5978       if (elem->GetCurrentDoc()) {
  5979         // Normally callbacks can not be enqueued until the created
  5980         // callback has been invoked, however, the attached callback
  5981         // in element upgrade is an exception so pretend the created
  5982         // callback has been invoked.
  5983         elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
  5985         EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition);
  5990   // Create constructor to return. Store the name of the custom element as the
  5991   // name of the function.
  5992   JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
  5993                                            JSFUN_CONSTRUCTOR, JS::NullPtr(),
  5994                                            NS_ConvertUTF16toUTF8(lcType).get());
  5995   if (!constructor) {
  5996     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  5997     return;
  6000   aRetval.set(JS_GetFunctionObject(constructor));
  6003 void
  6004 nsDocument::UseRegistryFromDocument(nsIDocument* aDocument)
  6006   nsDocument* doc = static_cast<nsDocument*>(aDocument);
  6007   MOZ_ASSERT(!mRegistry, "There should be no existing registry.");
  6008   mRegistry = doc->mRegistry;
  6011 NS_IMETHODIMP
  6012 nsDocument::GetElementsByTagName(const nsAString& aTagname,
  6013                                  nsIDOMNodeList** aReturn)
  6015   nsRefPtr<nsContentList> list = GetElementsByTagName(aTagname);
  6016   NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
  6018   // transfer ref to aReturn
  6019   list.forget(aReturn);
  6020   return NS_OK;
  6023 already_AddRefed<nsContentList>
  6024 nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
  6025                                     const nsAString& aLocalName,
  6026                                     ErrorResult& aResult)
  6028   int32_t nameSpaceId = kNameSpaceID_Wildcard;
  6030   if (!aNamespaceURI.EqualsLiteral("*")) {
  6031     aResult =
  6032       nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
  6033                                                             nameSpaceId);
  6034     if (aResult.Failed()) {
  6035       return nullptr;
  6039   NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
  6041   return NS_GetContentList(this, nameSpaceId, aLocalName);
  6044 NS_IMETHODIMP
  6045 nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
  6046                                    const nsAString& aLocalName,
  6047                                    nsIDOMNodeList** aReturn)
  6049   ErrorResult rv;
  6050   nsRefPtr<nsContentList> list =
  6051     nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
  6052   if (rv.Failed()) {
  6053     return rv.ErrorCode();
  6056   // transfer ref to aReturn
  6057   list.forget(aReturn);
  6058   return NS_OK;
  6061 NS_IMETHODIMP
  6062 nsDocument::GetAsync(bool *aAsync)
  6064   NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
  6066   return NS_ERROR_NOT_IMPLEMENTED;
  6069 NS_IMETHODIMP
  6070 nsDocument::SetAsync(bool aAsync)
  6072   NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!");
  6074   return NS_ERROR_NOT_IMPLEMENTED;
  6077 NS_IMETHODIMP
  6078 nsDocument::Load(const nsAString& aUrl, bool *aReturn)
  6080   NS_ERROR("nsDocument::Load() should be overriden by subclass!");
  6082   return NS_ERROR_NOT_IMPLEMENTED;
  6085 NS_IMETHODIMP
  6086 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
  6088   NS_ADDREF(*aStyleSheets = StyleSheets());
  6089   return NS_OK;
  6092 StyleSheetList*
  6093 nsDocument::StyleSheets()
  6095   if (!mDOMStyleSheets) {
  6096     mDOMStyleSheets = new nsDOMStyleSheetList(this);
  6098   return mDOMStyleSheets;
  6101 NS_IMETHODIMP
  6102 nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet)
  6104   nsIDocument::GetSelectedStyleSheetSet(aSheetSet);
  6105   return NS_OK;
  6108 void
  6109 nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
  6111   aSheetSet.Truncate();
  6113   // Look through our sheets, find the selected set title
  6114   int32_t count = GetNumberOfStyleSheets();
  6115   nsAutoString title;
  6116   for (int32_t index = 0; index < count; index++) {
  6117     nsIStyleSheet* sheet = GetStyleSheetAt(index);
  6118     NS_ASSERTION(sheet, "Null sheet in sheet list!");
  6120     nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet);
  6121     NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet");
  6122     bool disabled;
  6123     domSheet->GetDisabled(&disabled);
  6124     if (disabled) {
  6125       // Disabled sheets don't affect the currently selected set
  6126       continue;
  6129     sheet->GetTitle(title);
  6131     if (aSheetSet.IsEmpty()) {
  6132       aSheetSet = title;
  6133     } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
  6134       // Sheets from multiple sets enabled; return null string, per spec.
  6135       SetDOMStringToNull(aSheetSet);
  6136       return;
  6141 NS_IMETHODIMP
  6142 nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet)
  6144   SetSelectedStyleSheetSet(aSheetSet);
  6145   return NS_OK;
  6148 void
  6149 nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
  6151   if (DOMStringIsNull(aSheetSet)) {
  6152     return;
  6155   // Must update mLastStyleSheetSet before doing anything else with stylesheets
  6156   // or CSSLoaders.
  6157   mLastStyleSheetSet = aSheetSet;
  6158   EnableStyleSheetsForSetInternal(aSheetSet, true);
  6161 NS_IMETHODIMP
  6162 nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet)
  6164   nsString sheetSet;
  6165   GetLastStyleSheetSet(sheetSet);
  6166   aSheetSet = sheetSet;
  6167   return NS_OK;
  6170 void
  6171 nsDocument::GetLastStyleSheetSet(nsString& aSheetSet)
  6173   aSheetSet = mLastStyleSheetSet;
  6176 NS_IMETHODIMP
  6177 nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
  6179   nsIDocument::GetPreferredStyleSheetSet(aSheetSet);
  6180   return NS_OK;
  6183 void
  6184 nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
  6186   GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
  6189 NS_IMETHODIMP
  6190 nsDocument::GetStyleSheetSets(nsISupports** aList)
  6192   NS_ADDREF(*aList = StyleSheetSets());
  6193   return NS_OK;
  6196 DOMStringList*
  6197 nsDocument::StyleSheetSets()
  6199   if (!mStyleSheetSetList) {
  6200     mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
  6202   return mStyleSheetSetList;
  6205 NS_IMETHODIMP
  6206 nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet)
  6208   EnableStyleSheetsForSet(aSheetSet);
  6209   return NS_OK;
  6212 void
  6213 nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
  6215   // Per spec, passing in null is a no-op.
  6216   if (!DOMStringIsNull(aSheetSet)) {
  6217     // Note: must make sure to not change the CSSLoader's preferred sheet --
  6218     // that value should be equal to either our lastStyleSheetSet (if that's
  6219     // non-null) or to our preferredStyleSheetSet.  And this method doesn't
  6220     // change either of those.
  6221     EnableStyleSheetsForSetInternal(aSheetSet, false);
  6225 void
  6226 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
  6227                                             bool aUpdateCSSLoader)
  6229   BeginUpdate(UPDATE_STYLE);
  6230   int32_t count = GetNumberOfStyleSheets();
  6231   nsAutoString title;
  6232   for (int32_t index = 0; index < count; index++) {
  6233     nsIStyleSheet* sheet = GetStyleSheetAt(index);
  6234     NS_ASSERTION(sheet, "Null sheet in sheet list!");
  6235     sheet->GetTitle(title);
  6236     if (!title.IsEmpty()) {
  6237       sheet->SetEnabled(title.Equals(aSheetSet));
  6240   if (aUpdateCSSLoader) {
  6241     CSSLoader()->SetPreferredSheet(aSheetSet);
  6243   EndUpdate(UPDATE_STYLE);
  6246 NS_IMETHODIMP
  6247 nsDocument::GetCharacterSet(nsAString& aCharacterSet)
  6249   nsIDocument::GetCharacterSet(aCharacterSet);
  6250   return NS_OK;
  6253 void
  6254 nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
  6256   CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet);
  6259 NS_IMETHODIMP
  6260 nsDocument::ImportNode(nsIDOMNode* aImportedNode,
  6261                        bool aDeep,
  6262                        uint8_t aArgc,
  6263                        nsIDOMNode** aResult)
  6265   if (aArgc == 0) {
  6266     aDeep = true;
  6269   *aResult = nullptr;
  6271   nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode);
  6272   NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED);
  6274   ErrorResult rv;
  6275   nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv);
  6276   if (rv.Failed()) {
  6277     return rv.ErrorCode();
  6280   NS_ADDREF(*aResult = result->AsDOMNode());
  6281   return NS_OK;
  6284 already_AddRefed<nsINode>
  6285 nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
  6287   nsINode* imported = &aNode;
  6289   switch (imported->NodeType()) {
  6290     case nsIDOMNode::ATTRIBUTE_NODE:
  6291     case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
  6292     case nsIDOMNode::ELEMENT_NODE:
  6293     case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
  6294     case nsIDOMNode::TEXT_NODE:
  6295     case nsIDOMNode::CDATA_SECTION_NODE:
  6296     case nsIDOMNode::COMMENT_NODE:
  6297     case nsIDOMNode::DOCUMENT_TYPE_NODE:
  6299       nsCOMPtr<nsINode> newNode;
  6300       nsCOMArray<nsINode> nodesWithProperties;
  6301       rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
  6302                               nodesWithProperties, getter_AddRefs(newNode));
  6303       if (rv.Failed()) {
  6304         return nullptr;
  6307       nsIDocument *ownerDoc = imported->OwnerDoc();
  6308       rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, ownerDoc,
  6309                                              nsIDOMUserDataHandler::NODE_IMPORTED,
  6310                                              true);
  6311       if (rv.Failed()) {
  6312         return nullptr;
  6315       return newNode.forget();
  6317     default:
  6319       NS_WARNING("Don't know how to clone this nodetype for importNode.");
  6321       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  6325   return nullptr;
  6328 NS_IMETHODIMP
  6329 nsDocument::LoadBindingDocument(const nsAString& aURI)
  6331   ErrorResult rv;
  6332   nsIDocument::LoadBindingDocument(aURI, rv);
  6333   return rv.ErrorCode();
  6336 void
  6337 nsIDocument::LoadBindingDocument(const nsAString& aURI, ErrorResult& rv)
  6339   nsCOMPtr<nsIURI> uri;
  6340   rv = NS_NewURI(getter_AddRefs(uri), aURI,
  6341                  mCharacterSet.get(),
  6342                  GetDocBaseURI());
  6343   if (rv.Failed()) {
  6344     return;
  6347   // Figure out the right principal to use
  6348   nsCOMPtr<nsIPrincipal> subject;
  6349   nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
  6350   if (secMan) {
  6351     rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
  6352     if (rv.Failed()) {
  6353       return;
  6357   if (!subject) {
  6358     // Fall back to our principal.  Or should we fall back to the null
  6359     // principal?  The latter would just mean no binding loads....
  6360     subject = NodePrincipal();
  6363   BindingManager()->LoadBindingDocument(this, uri, subject);
  6366 NS_IMETHODIMP
  6367 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
  6369   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  6370   NS_ENSURE_ARG_POINTER(node);
  6372   Element* bindingParent = nsIDocument::GetBindingParent(*node);
  6373   nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent);
  6374   retval.forget(aResult);
  6375   return NS_OK;
  6378 Element*
  6379 nsIDocument::GetBindingParent(nsINode& aNode)
  6381   nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
  6382   if (!content)
  6383     return nullptr;
  6385   nsIContent* bindingParent = content->GetBindingParent();
  6386   return bindingParent ? bindingParent->AsElement() : nullptr;
  6389 static Element*
  6390 GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
  6391                       const nsAString& aAttrValue, bool aUniversalMatch)
  6393   if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
  6394                         aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
  6395                                               aAttrValue, eCaseMatters)) {
  6396     return aContent->AsElement();
  6399   for (nsIContent* child = aContent->GetFirstChild();
  6400        child;
  6401        child = child->GetNextSibling()) {
  6403     Element* matchedElement =
  6404       GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch);
  6405     if (matchedElement)
  6406       return matchedElement;
  6409   return nullptr;
  6412 Element*
  6413 nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
  6414                                            nsIAtom* aAttrName,
  6415                                            const nsAString& aAttrValue) const
  6417   nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
  6418   if (!nodeList)
  6419     return nullptr;
  6421   uint32_t length = 0;
  6422   nodeList->GetLength(&length);
  6424   bool universalMatch = aAttrValue.EqualsLiteral("*");
  6426   for (uint32_t i = 0; i < length; ++i) {
  6427     nsIContent* current = nodeList->Item(i);
  6428     Element* matchedElm =
  6429       GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
  6430     if (matchedElm)
  6431       return matchedElm;
  6434   return nullptr;
  6437 NS_IMETHODIMP
  6438 nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
  6439                                            const nsAString& aAttrName,
  6440                                            const nsAString& aAttrValue,
  6441                                            nsIDOMElement** aResult)
  6443   nsCOMPtr<Element> element = do_QueryInterface(aElement);
  6444   NS_ENSURE_ARG_POINTER(element);
  6446   Element* anonEl =
  6447     nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName,
  6448                                                 aAttrValue);
  6449   nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl);
  6450   retval.forget(aResult);
  6451   return NS_OK;
  6454 Element*
  6455 nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
  6456                                             const nsAString& aAttrName,
  6457                                             const nsAString& aAttrValue)
  6459   nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
  6461   return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
  6465 NS_IMETHODIMP
  6466 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
  6467                               nsIDOMNodeList** aResult)
  6469   *aResult = nullptr;
  6471   nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
  6472   return BindingManager()->GetAnonymousNodesFor(content, aResult);
  6475 nsINodeList*
  6476 nsIDocument::GetAnonymousNodes(Element& aElement)
  6478   return BindingManager()->GetAnonymousNodesFor(&aElement);
  6481 NS_IMETHODIMP
  6482 nsDocument::CreateRange(nsIDOMRange** aReturn)
  6484   ErrorResult rv;
  6485   *aReturn = nsIDocument::CreateRange(rv).take();
  6486   return rv.ErrorCode();
  6489 already_AddRefed<nsRange>
  6490 nsIDocument::CreateRange(ErrorResult& rv)
  6492   nsRefPtr<nsRange> range = new nsRange(this);
  6493   nsresult res = range->Set(this, 0, this, 0);
  6494   if (NS_FAILED(res)) {
  6495     rv.Throw(res);
  6496     return nullptr;
  6499   return range.forget();
  6502 NS_IMETHODIMP
  6503 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
  6504                                uint32_t aWhatToShow,
  6505                                nsIDOMNodeFilter *aFilter,
  6506                                uint8_t aOptionalArgc,
  6507                                nsIDOMNodeIterator **_retval)
  6509   *_retval = nullptr;
  6511   if (!aOptionalArgc) {
  6512     aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
  6515   if (!aRoot) {
  6516     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
  6519   nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
  6520   NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
  6522   ErrorResult rv;
  6523   NodeFilterHolder holder(aFilter);
  6524   *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow, holder,
  6525                                              rv).take();
  6526   return rv.ErrorCode();
  6529 already_AddRefed<NodeIterator>
  6530 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
  6531                                 NodeFilter* aFilter,
  6532                                 ErrorResult& rv) const
  6534   NodeFilterHolder holder(aFilter);
  6535   return CreateNodeIterator(aRoot, aWhatToShow, holder, rv);
  6538 already_AddRefed<NodeIterator>
  6539 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
  6540                                 const NodeFilterHolder& aFilter,
  6541                                 ErrorResult& rv) const
  6543   nsINode* root = &aRoot;
  6544   nsRefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow,
  6545                                                      aFilter);
  6546   return iterator.forget();
  6549 NS_IMETHODIMP
  6550 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
  6551                              uint32_t aWhatToShow,
  6552                              nsIDOMNodeFilter *aFilter,
  6553                              uint8_t aOptionalArgc,
  6554                              nsIDOMTreeWalker **_retval)
  6556   *_retval = nullptr;
  6558   if (!aOptionalArgc) {
  6559     aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
  6562   nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
  6563   NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  6565   ErrorResult rv;
  6566   NodeFilterHolder holder(aFilter);
  6567   *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow, holder,
  6568                                            rv).take();
  6569   return rv.ErrorCode();
  6572 already_AddRefed<TreeWalker>
  6573 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
  6574                               NodeFilter* aFilter,
  6575                               ErrorResult& rv) const
  6577   NodeFilterHolder holder(aFilter);
  6578   return CreateTreeWalker(aRoot, aWhatToShow, holder, rv);
  6581 already_AddRefed<TreeWalker>
  6582 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
  6583                               const NodeFilterHolder& aFilter,
  6584                               ErrorResult& rv) const
  6586   nsINode* root = &aRoot;
  6587   nsRefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, aFilter);
  6588   return walker.forget();
  6592 NS_IMETHODIMP
  6593 nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView)
  6595   *aDefaultView = nullptr;
  6596   nsCOMPtr<nsPIDOMWindow> win = GetWindow();
  6597   win.forget(aDefaultView);
  6598   return NS_OK;
  6601 NS_IMETHODIMP
  6602 nsDocument::GetLocation(nsIDOMLocation **_retval)
  6604   *_retval = nsIDocument::GetLocation().take();
  6605   return NS_OK;
  6608 already_AddRefed<nsIDOMLocation>
  6609 nsIDocument::GetLocation() const
  6611   nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject);
  6613   if (!w) {
  6614     return nullptr;
  6617   nsCOMPtr<nsIDOMLocation> loc;
  6618   w->GetLocation(getter_AddRefs(loc));
  6619   return loc.forget();
  6622 Element*
  6623 nsIDocument::GetHtmlElement() const
  6625   Element* rootElement = GetRootElement();
  6626   if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
  6627     return rootElement;
  6628   return nullptr;
  6631 Element*
  6632 nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
  6634   Element* html = GetHtmlElement();
  6635   if (!html)
  6636     return nullptr;
  6638   // Look for the element with aTag inside html. This needs to run
  6639   // forwards to find the first such element.
  6640   for (nsIContent* child = html->GetFirstChild();
  6641        child;
  6642        child = child->GetNextSibling()) {
  6643     if (child->IsHTML(aTag))
  6644       return child->AsElement();
  6646   return nullptr;
  6649 nsIContent*
  6650 nsDocument::GetTitleContent(uint32_t aNamespace)
  6652   // mMayHaveTitleElement will have been set to true if any HTML or SVG
  6653   // <title> element has been bound to this document. So if it's false,
  6654   // we know there is nothing to do here. This avoids us having to search
  6655   // the whole DOM if someone calls document.title on a large document
  6656   // without a title.
  6657   if (!mMayHaveTitleElement)
  6658     return nullptr;
  6660   nsRefPtr<nsContentList> list =
  6661     NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title"));
  6663   return list->Item(0, false);
  6666 void
  6667 nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle)
  6669   nsIContent* title = GetTitleContent(aNamespace);
  6670   if (!title)
  6671     return;
  6672   if(!nsContentUtils::GetNodeTextContent(title, false, aTitle))
  6673     NS_RUNTIMEABORT("OOM");
  6676 NS_IMETHODIMP
  6677 nsDocument::GetTitle(nsAString& aTitle)
  6679   nsString title;
  6680   GetTitle(title);
  6681   aTitle = title;
  6682   return NS_OK;
  6685 void
  6686 nsDocument::GetTitle(nsString& aTitle)
  6688   aTitle.Truncate();
  6690   nsIContent *rootElement = GetRootElement();
  6691   if (!rootElement)
  6692     return;
  6694   nsAutoString tmp;
  6696   switch (rootElement->GetNameSpaceID()) {
  6697 #ifdef MOZ_XUL
  6698     case kNameSpaceID_XUL:
  6699       rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
  6700       break;
  6701 #endif
  6702     case kNameSpaceID_SVG:
  6703       if (rootElement->Tag() == nsGkAtoms::svg) {
  6704         GetTitleFromElement(kNameSpaceID_SVG, tmp);
  6705         break;
  6706       } // else fall through
  6707     default:
  6708       GetTitleFromElement(kNameSpaceID_XHTML, tmp);
  6709       break;
  6712   tmp.CompressWhitespace();
  6713   aTitle = tmp;
  6716 NS_IMETHODIMP
  6717 nsDocument::SetTitle(const nsAString& aTitle)
  6719   Element *rootElement = GetRootElement();
  6720   if (!rootElement)
  6721     return NS_OK;
  6723   switch (rootElement->GetNameSpaceID()) {
  6724     case kNameSpaceID_SVG:
  6725       return NS_OK; // SVG doesn't support setting a title
  6726 #ifdef MOZ_XUL
  6727     case kNameSpaceID_XUL:
  6728       return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
  6729                                   aTitle, true);
  6730 #endif
  6733   // Batch updates so that mutation events don't change "the title
  6734   // element" under us
  6735   mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
  6737   nsIContent* title = GetTitleContent(kNameSpaceID_XHTML);
  6738   if (!title) {
  6739     Element *head = GetHeadElement();
  6740     if (!head)
  6741       return NS_OK;
  6744       nsCOMPtr<nsINodeInfo> titleInfo;
  6745       titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
  6746                                                 kNameSpaceID_XHTML,
  6747                                                 nsIDOMNode::ELEMENT_NODE);
  6748       title = NS_NewHTMLTitleElement(titleInfo.forget());
  6749       if (!title)
  6750         return NS_OK;
  6753     head->AppendChildTo(title, true);
  6756   return nsContentUtils::SetNodeTextContent(title, aTitle, false);
  6759 void
  6760 nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv)
  6762   rv = SetTitle(aTitle);
  6765 void
  6766 nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
  6768   NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
  6769                "Setting a title while unlinking or destroying the element?");
  6770   if (mInUnlinkOrDeletion) {
  6771     return;
  6774   if (aBoundTitleElement) {
  6775     mMayHaveTitleElement = true;
  6777   if (mPendingTitleChangeEvent.IsPending())
  6778     return;
  6780   nsRefPtr<nsRunnableMethod<nsDocument, void, false> > event =
  6781     NS_NewNonOwningRunnableMethod(this,
  6782       &nsDocument::DoNotifyPossibleTitleChange);
  6783   nsresult rv = NS_DispatchToCurrentThread(event);
  6784   if (NS_SUCCEEDED(rv)) {
  6785     mPendingTitleChangeEvent = event;
  6789 void
  6790 nsDocument::DoNotifyPossibleTitleChange()
  6792   mPendingTitleChangeEvent.Forget();
  6793   mHaveFiredTitleChange = true;
  6795   nsAutoString title;
  6796   GetTitle(title);
  6798   nsCOMPtr<nsIPresShell> shell = GetShell();
  6799   if (shell) {
  6800     nsCOMPtr<nsISupports> container =
  6801       shell->GetPresContext()->GetContainerWeak();
  6802     if (container) {
  6803       nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
  6804       if (docShellWin) {
  6805         docShellWin->SetTitle(title.get());
  6810   // Fire a DOM event for the title change.
  6811   nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
  6812                                       NS_LITERAL_STRING("DOMTitleChanged"),
  6813                                       true, true);
  6816 already_AddRefed<nsIBoxObject>
  6817 nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
  6819   if (!aElement) {
  6820     aRv.Throw(NS_ERROR_UNEXPECTED);
  6821     return nullptr;
  6824   nsIDocument* doc = aElement->OwnerDoc();
  6825   if (doc != this) {
  6826     aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
  6827     return nullptr;
  6830   if (!mHasWarnedAboutBoxObjects && !aElement->IsXUL()) {
  6831     mHasWarnedAboutBoxObjects = true;
  6832     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  6833                                     NS_LITERAL_CSTRING("BoxObjects"), this,
  6834                                     nsContentUtils::eDOM_PROPERTIES,
  6835                                     "UseOfGetBoxObjectForWarning");
  6838   if (!mBoxObjectTable) {
  6839     mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(12);
  6840   } else {
  6841     nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement);
  6842     if (boxObject) {
  6843       return boxObject.forget();
  6847   int32_t namespaceID;
  6848   nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
  6850   nsAutoCString contractID("@mozilla.org/layout/xul-boxobject");
  6851   if (namespaceID == kNameSpaceID_XUL) {
  6852     if (tag == nsGkAtoms::browser ||
  6853         tag == nsGkAtoms::editor ||
  6854         tag == nsGkAtoms::iframe)
  6855       contractID += "-container";
  6856     else if (tag == nsGkAtoms::menu)
  6857       contractID += "-menu";
  6858     else if (tag == nsGkAtoms::popup ||
  6859              tag == nsGkAtoms::menupopup ||
  6860              tag == nsGkAtoms::panel ||
  6861              tag == nsGkAtoms::tooltip)
  6862       contractID += "-popup";
  6863     else if (tag == nsGkAtoms::tree)
  6864       contractID += "-tree";
  6865     else if (tag == nsGkAtoms::listbox)
  6866       contractID += "-listbox";
  6867     else if (tag == nsGkAtoms::scrollbox)
  6868       contractID += "-scrollbox";
  6870   contractID += ";1";
  6872   nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get()));
  6873   if (!boxObject) {
  6874     aRv.Throw(NS_ERROR_FAILURE);
  6875     return nullptr;
  6878   boxObject->Init(aElement);
  6880   if (mBoxObjectTable) {
  6881     mBoxObjectTable->Put(aElement, boxObject.get());
  6884   return boxObject.forget();
  6887 void
  6888 nsDocument::ClearBoxObjectFor(nsIContent* aContent)
  6890   if (mBoxObjectTable) {
  6891     nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
  6892     if (boxObject) {
  6893       boxObject->Clear();
  6894       mBoxObjectTable->Remove(aContent);
  6899 void
  6900 nsDocument::FlushSkinBindings()
  6902   BindingManager()->FlushSkinBindings();
  6905 nsresult
  6906 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
  6908   mInitializableFrameLoaders.RemoveElement(aLoader);
  6909   // Don't even try to initialize.
  6910   if (mInDestructor) {
  6911     NS_WARNING("Trying to initialize a frame loader while"
  6912                "document is being deleted");
  6913     return NS_ERROR_FAILURE;
  6916   mInitializableFrameLoaders.AppendElement(aLoader);
  6917   if (!mFrameLoaderRunner) {
  6918     mFrameLoaderRunner =
  6919       NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
  6920     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
  6921     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
  6923   return NS_OK;
  6926 nsresult
  6927 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
  6929   mInitializableFrameLoaders.RemoveElement(aLoader);
  6930   if (mInDestructor) {
  6931     return NS_ERROR_FAILURE;
  6934   mFinalizableFrameLoaders.AppendElement(aLoader);
  6935   if (!mFrameLoaderRunner) {
  6936     mFrameLoaderRunner =
  6937       NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
  6938     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
  6939     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
  6941   return NS_OK;
  6944 void
  6945 nsDocument::MaybeInitializeFinalizeFrameLoaders()
  6947   if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
  6948     // This method will be recalled when mUpdateNestLevel drops to 0,
  6949     // or when !mDelayFrameLoaderInitialization.
  6950     mFrameLoaderRunner = nullptr;
  6951     return;
  6954   // We're not in an update, but it is not safe to run scripts, so
  6955   // postpone frameloader initialization and finalization.
  6956   if (!nsContentUtils::IsSafeToRunScript()) {
  6957     if (!mInDestructor && !mFrameLoaderRunner &&
  6958         (mInitializableFrameLoaders.Length() ||
  6959          mFinalizableFrameLoaders.Length())) {
  6960       mFrameLoaderRunner =
  6961         NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
  6962       nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
  6964     return;
  6966   mFrameLoaderRunner = nullptr;
  6968   // Don't use a temporary array for mInitializableFrameLoaders, because
  6969   // loading a frame may cause some other frameloader to be removed from the
  6970   // array. But be careful to keep the loader alive when starting the load!
  6971   while (mInitializableFrameLoaders.Length()) {
  6972     nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
  6973     mInitializableFrameLoaders.RemoveElementAt(0);
  6974     NS_ASSERTION(loader, "null frameloader in the array?");
  6975     loader->ReallyStartLoading();
  6978   uint32_t length = mFinalizableFrameLoaders.Length();
  6979   if (length > 0) {
  6980     nsTArray<nsRefPtr<nsFrameLoader> > loaders;
  6981     mFinalizableFrameLoaders.SwapElements(loaders);
  6982     for (uint32_t i = 0; i < length; ++i) {
  6983       loaders[i]->Finalize();
  6988 void
  6989 nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
  6991   uint32_t length = mInitializableFrameLoaders.Length();
  6992   for (uint32_t i = 0; i < length; ++i) {
  6993     if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
  6994       mInitializableFrameLoaders.RemoveElementAt(i);
  6995       return;
  7000 bool
  7001 nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
  7003   if (aShell) {
  7004     uint32_t length = mFinalizableFrameLoaders.Length();
  7005     for (uint32_t i = 0; i < length; ++i) {
  7006       if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
  7007         return true;
  7011   return false;
  7014 nsIDocument*
  7015 nsDocument::RequestExternalResource(nsIURI* aURI,
  7016                                     nsINode* aRequestingNode,
  7017                                     ExternalResourceLoad** aPendingLoad)
  7019   NS_PRECONDITION(aURI, "Must have a URI");
  7020   NS_PRECONDITION(aRequestingNode, "Must have a node");
  7021   if (mDisplayDocument) {
  7022     return mDisplayDocument->RequestExternalResource(aURI,
  7023                                                      aRequestingNode,
  7024                                                      aPendingLoad);
  7027   return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
  7028                                               this, aPendingLoad);
  7031 void
  7032 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
  7034   mExternalResourceMap.EnumerateResources(aCallback, aData);
  7037 nsSMILAnimationController*
  7038 nsDocument::GetAnimationController()
  7040   // We create the animation controller lazily because most documents won't want
  7041   // one and only SVG documents and the like will call this
  7042   if (mAnimationController)
  7043     return mAnimationController;
  7044   // Refuse to create an Animation Controller for data documents.
  7045   if (mLoadedAsData || mLoadedAsInteractiveData)
  7046     return nullptr;
  7048   mAnimationController = new nsSMILAnimationController(this);
  7050   // If there's a presContext then check the animation mode and pause if
  7051   // necessary.
  7052   nsIPresShell *shell = GetShell();
  7053   if (mAnimationController && shell) {
  7054     nsPresContext *context = shell->GetPresContext();
  7055     if (context &&
  7056         context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
  7057       mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
  7061   // If we're hidden (or being hidden), notify the newly-created animation
  7062   // controller. (Skip this check for SVG-as-an-image documents, though,
  7063   // because they don't get OnPageShow / OnPageHide calls).
  7064   if (!mIsShowing && !mIsBeingUsedAsImage) {
  7065     mAnimationController->OnPageHide();
  7068   return mAnimationController;
  7071 /**
  7072  * Retrieve the "direction" property of the document.
  7074  * @lina 01/09/2001
  7075  */
  7076 NS_IMETHODIMP
  7077 nsDocument::GetDir(nsAString& aDirection)
  7079   nsIDocument::GetDir(aDirection);
  7080   return NS_OK;
  7083 void
  7084 nsIDocument::GetDir(nsAString& aDirection) const
  7086   aDirection.Truncate();
  7087   Element* rootElement = GetHtmlElement();
  7088   if (rootElement) {
  7089     static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
  7093 /**
  7094  * Set the "direction" property of the document.
  7096  * @lina 01/09/2001
  7097  */
  7098 NS_IMETHODIMP
  7099 nsDocument::SetDir(const nsAString& aDirection)
  7101   nsIDocument::SetDir(aDirection);
  7102   return NS_OK;
  7105 void
  7106 nsIDocument::SetDir(const nsAString& aDirection)
  7108   Element* rootElement = GetHtmlElement();
  7109   if (rootElement) {
  7110     rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
  7111                          aDirection, true);
  7115 NS_IMETHODIMP
  7116 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
  7118   nsIDocument::GetInputEncoding(aInputEncoding);
  7119   return NS_OK;
  7122 void
  7123 nsIDocument::GetInputEncoding(nsAString& aInputEncoding)
  7125   // Not const function, because WarnOnceAbout is not a const method
  7126   WarnOnceAbout(eInputEncoding);
  7127   if (mHaveInputEncoding) {
  7128     return GetCharacterSet(aInputEncoding);
  7131   SetDOMStringToNull(aInputEncoding);
  7134 NS_IMETHODIMP
  7135 nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument)
  7137   *aSyntheticDocument = mIsSyntheticDocument;
  7138   return NS_OK;
  7141 NS_IMETHODIMP
  7142 nsDocument::GetDocumentURI(nsAString& aDocumentURI)
  7144   nsString temp;
  7145   nsIDocument::GetDocumentURI(temp);
  7146   aDocumentURI = temp;
  7147   return NS_OK;
  7150 void
  7151 nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
  7153   if (mDocumentURI) {
  7154     nsAutoCString uri;
  7155     mDocumentURI->GetSpec(uri);
  7156     CopyUTF8toUTF16(uri, aDocumentURI);
  7157   } else {
  7158     aDocumentURI.Truncate();
  7162 // Alias of above
  7163 NS_IMETHODIMP
  7164 nsDocument::GetURL(nsAString& aURL)
  7166   return GetDocumentURI(aURL);
  7169 void
  7170 nsIDocument::GetURL(nsString& aURL) const
  7172   return GetDocumentURI(aURL);
  7175 void
  7176 nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI) const
  7178   if (!mChromeXHRDocURI || !nsContentUtils::IsCallerChrome()) {
  7179     return GetDocumentURI(aDocumentURI);
  7182   nsAutoCString uri;
  7183   mChromeXHRDocURI->GetSpec(uri);
  7184   CopyUTF8toUTF16(uri, aDocumentURI);
  7187 nsIURI*
  7188 nsIDocument::GetDocumentURIObject() const
  7190   if (!mChromeXHRDocURI) {
  7191     return GetDocumentURI();
  7194   return mChromeXHRDocURI;
  7198 // readonly attribute DOMString compatMode;
  7199 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
  7200 // in almost standards or full standards mode. See bug 105640.  This was
  7201 // implemented to match MSIE's compatMode property.
  7202 NS_IMETHODIMP
  7203 nsDocument::GetCompatMode(nsAString& aCompatMode)
  7205   nsString temp;
  7206   nsIDocument::GetCompatMode(temp);
  7207   aCompatMode = temp;
  7208   return NS_OK;
  7211 void
  7212 nsIDocument::GetCompatMode(nsString& aCompatMode) const
  7214   NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
  7215                mCompatMode == eCompatibility_AlmostStandards ||
  7216                mCompatMode == eCompatibility_FullStandards,
  7217                "mCompatMode is neither quirks nor strict for this document");
  7219   if (mCompatMode == eCompatibility_NavQuirks) {
  7220     aCompatMode.AssignLiteral("BackCompat");
  7221   } else {
  7222     aCompatMode.AssignLiteral("CSS1Compat");
  7226 static void BlastSubtreeToPieces(nsINode *aNode);
  7228 PLDHashOperator
  7229 BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg)
  7231   nsCOMPtr<nsIAttribute> *attr =
  7232     static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
  7234   *attr = aData;
  7236   NS_ASSERTION(attr->get(),
  7237                "non-nsIAttribute somehow made it into the hashmap?!");
  7239   return PL_DHASH_STOP;
  7242 static void
  7243 BlastSubtreeToPieces(nsINode *aNode)
  7245   if (aNode->IsElement()) {
  7246     Element *element = aNode->AsElement();
  7247     const nsDOMAttributeMap *map = element->GetAttributeMap();
  7248     if (map) {
  7249       nsCOMPtr<nsIAttribute> attr;
  7250       while (map->Enumerate(BlastFunc, &attr) > 0) {
  7251         BlastSubtreeToPieces(attr);
  7253 #ifdef DEBUG
  7254         nsresult rv =
  7255 #endif
  7256           element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
  7257                              attr->NodeInfo()->NameAtom(),
  7258                              false);
  7260         // XXX Should we abort here?
  7261         NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!");
  7266   uint32_t count = aNode->GetChildCount();
  7267   for (uint32_t i = 0; i < count; ++i) {
  7268     BlastSubtreeToPieces(aNode->GetFirstChild());
  7269     aNode->RemoveChildAt(0, false);
  7274 class nsUserDataCaller : public nsRunnable
  7276 public:
  7277   nsUserDataCaller(nsCOMArray<nsINode>& aNodesWithProperties,
  7278                    nsIDocument* aOwnerDoc)
  7279     : mNodesWithProperties(aNodesWithProperties),
  7280       mOwnerDoc(aOwnerDoc)
  7284   NS_IMETHOD Run()
  7286     nsNodeUtils::CallUserDataHandlers(mNodesWithProperties, mOwnerDoc,
  7287                                       nsIDOMUserDataHandler::NODE_ADOPTED,
  7288                                       false);
  7289     return NS_OK;
  7292 private:
  7293   nsCOMArray<nsINode> mNodesWithProperties;
  7294   nsCOMPtr<nsIDocument> mOwnerDoc;
  7295 };
  7297 NS_IMETHODIMP
  7298 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
  7300   *aResult = nullptr;
  7302   nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
  7303   NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED);
  7305   ErrorResult rv;
  7306   nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv);
  7307   if (rv.Failed()) {
  7308     return rv.ErrorCode();
  7311   NS_ADDREF(*aResult = result->AsDOMNode());
  7312   return NS_OK;
  7315 nsINode*
  7316 nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
  7318   nsINode* adoptedNode = &aAdoptedNode;
  7320   // Scope firing mutation events so that we don't carry any state that
  7321   // might be stale
  7323     nsINode* parent = adoptedNode->GetParentNode();
  7324     if (parent) {
  7325       nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
  7326                                            adoptedNode->OwnerDoc());
  7330   nsAutoScriptBlocker scriptBlocker;
  7332   switch (adoptedNode->NodeType()) {
  7333     case nsIDOMNode::ATTRIBUTE_NODE:
  7335       // Remove from ownerElement.
  7336       nsRefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
  7338       nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
  7339       if (rv.Failed()) {
  7340         return nullptr;
  7343       if (ownerElement) {
  7344         nsRefPtr<Attr> newAttr =
  7345           ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
  7346         if (rv.Failed()) {
  7347           return nullptr;
  7350         newAttr.swap(adoptedAttr);
  7353       break;
  7355     case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
  7356     case nsIDOMNode::ELEMENT_NODE:
  7357     case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
  7358     case nsIDOMNode::TEXT_NODE:
  7359     case nsIDOMNode::CDATA_SECTION_NODE:
  7360     case nsIDOMNode::COMMENT_NODE:
  7361     case nsIDOMNode::DOCUMENT_TYPE_NODE:
  7363       // Don't allow adopting a node's anonymous subtree out from under it.
  7364       if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
  7365         rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  7366         return nullptr;
  7369       // We don't want to adopt an element into its own contentDocument or into
  7370       // a descendant contentDocument, so we check if the frameElement of this
  7371       // document or any of its parents is the adopted node or one of its
  7372       // descendants.
  7373       nsIDocument *doc = this;
  7374       do {
  7375         nsPIDOMWindow *win = doc->GetWindow();
  7376         if (win) {
  7377           nsCOMPtr<nsINode> node =
  7378             do_QueryInterface(win->GetFrameElementInternal());
  7379           if (node &&
  7380               nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
  7381             rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
  7382             return nullptr;
  7385       } while ((doc = doc->GetParentDocument()));
  7387       // Remove from parent.
  7388       nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
  7389       if (parent) {
  7390         int32_t idx = parent->IndexOf(adoptedNode);
  7391         MOZ_ASSERT(idx >= 0);
  7392         parent->RemoveChildAt(idx, true);
  7393       } else {
  7394         MOZ_ASSERT(!adoptedNode->IsInDoc());
  7396         // If we're adopting a node that's not in a document, it might still
  7397         // have a binding applied. Remove the binding from the element now
  7398         // that it's getting adopted into a new document.
  7399         // TODO Fully tear down the binding.
  7400         adoptedNode->AsContent()->SetXBLBinding(nullptr);
  7403       break;
  7405     case nsIDOMNode::DOCUMENT_NODE:
  7407       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  7408       return nullptr;
  7410     default:
  7412       NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
  7414       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  7415       return nullptr;
  7419   nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
  7420   bool sameDocument = oldDocument == this;
  7422   AutoJSContext cx;
  7423   JS::Rooted<JSObject*> newScope(cx, nullptr);
  7424   if (!sameDocument) {
  7425     newScope = GetWrapper();
  7426     if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
  7427       // Make sure cx is in a semi-sane compartment before we call WrapNative.
  7428       // It's kind of irrelevant, given that we're passing aAllowWrapping =
  7429       // false, and documents should always insist on being wrapped in an
  7430       // canonical scope. But we try to pass something sane anyway.
  7431       JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject());
  7432       JS::Rooted<JS::Value> v(cx);
  7433       rv = nsContentUtils::WrapNative(cx, this, this, &v,
  7434                                       /* aAllowWrapping = */ false);
  7435       if (rv.Failed())
  7436         return nullptr;
  7437       newScope = &v.toObject();
  7441   nsCOMArray<nsINode> nodesWithProperties;
  7442   rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
  7443                           newScope, nodesWithProperties);
  7444   if (rv.Failed()) {
  7445     // Disconnect all nodes from their parents, since some have the old document
  7446     // as their ownerDocument and some have this as their ownerDocument.
  7447     BlastSubtreeToPieces(adoptedNode);
  7449     if (!sameDocument && oldDocument) {
  7450       uint32_t count = nodesWithProperties.Count();
  7451       for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
  7452         for (uint32_t i = 0; i < count; ++i) {
  7453           // Remove all properties.
  7454           oldDocument->PropertyTable(j)->
  7455             DeleteAllPropertiesFor(nodesWithProperties[i]);
  7460     return nullptr;
  7463   uint32_t count = nodesWithProperties.Count();
  7464   if (!sameDocument && oldDocument) {
  7465     for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
  7466       nsPropertyTable *oldTable = oldDocument->PropertyTable(j);
  7467       nsPropertyTable *newTable = PropertyTable(j);
  7468       for (uint32_t i = 0; i < count; ++i) {
  7469         rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
  7470                                                         newTable);
  7474     if (rv.Failed()) {
  7475       // Disconnect all nodes from their parents.
  7476       BlastSubtreeToPieces(adoptedNode);
  7478       return nullptr;
  7482   if (nodesWithProperties.Count()) {
  7483     nsContentUtils::AddScriptRunner(new nsUserDataCaller(nodesWithProperties,
  7484                                                          this));
  7487   NS_ASSERTION(adoptedNode->OwnerDoc() == this,
  7488                "Should still be in the document we just got adopted into");
  7490   return adoptedNode;
  7493 nsViewportInfo
  7494 nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
  7496   // In cases where the width of the CSS viewport is less than or equal to the width
  7497   // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
  7498   // behaviour. See bug 941995 for details.
  7500   switch (mViewportType) {
  7501   case DisplayWidthHeight:
  7502     return nsViewportInfo(aDisplaySize);
  7503   case DisplayWidthHeightNoZoom:
  7504     return nsViewportInfo(aDisplaySize, /*allowZoom*/ false, /*allowDoubleTapZoom*/ false);
  7505   case Unknown:
  7507     nsAutoString viewport;
  7508     GetHeaderData(nsGkAtoms::viewport, viewport);
  7509     if (viewport.IsEmpty()) {
  7510       // If the docType specifies that we are on a site optimized for mobile,
  7511       // then we want to return specially crafted defaults for the viewport info.
  7512       nsCOMPtr<nsIDOMDocumentType> docType;
  7513       nsresult rv = GetDoctype(getter_AddRefs(docType));
  7514       if (NS_SUCCEEDED(rv) && docType) {
  7515         nsAutoString docId;
  7516         rv = docType->GetPublicId(docId);
  7517         if (NS_SUCCEEDED(rv)) {
  7518           if ((docId.Find("WAP") != -1) ||
  7519               (docId.Find("Mobile") != -1) ||
  7520               (docId.Find("WML") != -1))
  7522             // We're making an assumption that the docType can't change here
  7523             mViewportType = DisplayWidthHeight;
  7524             return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
  7529       nsAutoString handheldFriendly;
  7530       GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
  7531       if (handheldFriendly.EqualsLiteral("true")) {
  7532         mViewportType = DisplayWidthHeight;
  7533         return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
  7536       // Bug 940036. This is bad. When FirefoxOS was built, apps installed
  7537       // where not using the AsyncPanZoom code. As a result a lot of apps
  7538       // in the marketplace does not use it yet and instead are built to
  7539       // render correctly in FirefoxOS only. For a smooth transition the above
  7540       // code force installed apps to render as if they have a viewport with
  7541       // content="width=device-width, height=device-height, user-scalable=no".
  7542       // This could be safely remove once it is known that most apps in the
  7543       // marketplace use it and that users does not use an old version of the
  7544       // app that does not use it.
  7545       nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  7546       if (docShell && docShell->GetIsApp()) {
  7547         nsString uri;
  7548         GetDocumentURI(uri);
  7549         if (!uri.EqualsLiteral("about:blank")) {
  7550           nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  7551                                           NS_LITERAL_CSTRING("DOM"), this,
  7552                                           nsContentUtils::eDOM_PROPERTIES,
  7553                                           "ImplicitMetaViewportTagFallback");
  7555         mViewportType = DisplayWidthHeightNoZoom;
  7556         return nsViewportInfo(aDisplaySize, /*allowZoom*/false, /*allowDoubleTapZoom*/false);
  7560     nsAutoString minScaleStr;
  7561     GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
  7563     nsresult errorCode;
  7564     mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
  7566     if (NS_FAILED(errorCode)) {
  7567       mScaleMinFloat = kViewportMinScale;
  7570     mScaleMinFloat = mozilla::clamped(
  7571         mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
  7573     nsAutoString maxScaleStr;
  7574     GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
  7576     // We define a special error code variable for the scale and max scale,
  7577     // because they are used later (see the width calculations).
  7578     nsresult scaleMaxErrorCode;
  7579     mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
  7581     if (NS_FAILED(scaleMaxErrorCode)) {
  7582       mScaleMaxFloat = kViewportMaxScale;
  7585     mScaleMaxFloat = mozilla::clamped(
  7586         mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
  7588     nsAutoString scaleStr;
  7589     GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
  7591     nsresult scaleErrorCode;
  7592     mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
  7594     nsAutoString widthStr, heightStr;
  7596     GetHeaderData(nsGkAtoms::viewport_height, heightStr);
  7597     GetHeaderData(nsGkAtoms::viewport_width, widthStr);
  7599     mAutoSize = false;
  7601     if (widthStr.EqualsLiteral("device-width")) {
  7602       mAutoSize = true;
  7605     if (widthStr.IsEmpty() &&
  7606         (heightStr.EqualsLiteral("device-height") ||
  7607          (mScaleFloat.scale == 1.0)))
  7609       mAutoSize = true;
  7612     nsresult widthErrorCode, heightErrorCode;
  7613     mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
  7614     mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
  7616     // If width or height has not been set to a valid number by this point,
  7617     // fall back to a default value.
  7618     mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
  7619     mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
  7621     mAllowZoom = true;
  7622     nsAutoString userScalable;
  7623     GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
  7625     if ((userScalable.EqualsLiteral("0")) ||
  7626         (userScalable.EqualsLiteral("no")) ||
  7627         (userScalable.EqualsLiteral("false"))) {
  7628       mAllowZoom = false;
  7630     mAllowDoubleTapZoom = mAllowZoom;
  7632     mScaleStrEmpty = scaleStr.IsEmpty();
  7633     mWidthStrEmpty = widthStr.IsEmpty();
  7634     mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
  7635     mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
  7637     mViewportType = Specified;
  7639   case Specified:
  7640   default:
  7641     CSSIntSize size = mViewportSize;
  7643     if (!mValidWidth) {
  7644       if (mValidHeight && !aDisplaySize.IsEmpty()) {
  7645         size.width = int32_t(size.height * aDisplaySize.width / aDisplaySize.height);
  7646       } else {
  7647         size.width = Preferences::GetInt("browser.viewport.desktopWidth",
  7648                                          kViewportDefaultScreenWidth);
  7652     if (!mValidHeight) {
  7653       if (!aDisplaySize.IsEmpty()) {
  7654         size.height = int32_t(size.width * aDisplaySize.height / aDisplaySize.width);
  7655       } else {
  7656         size.height = size.width;
  7659     // Now convert the scale into device pixels per CSS pixel.
  7660     nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
  7661     CSSToLayoutDeviceScale pixelRatio = widget ? widget->GetDefaultScale()
  7662                                                : CSSToLayoutDeviceScale(1.0f);
  7663     CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio;
  7664     CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio;
  7665     CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio;
  7667     if (mAutoSize) {
  7668       // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
  7669       CSSToScreenScale defaultPixelScale = pixelRatio * LayoutDeviceToScreenScale(1.0f);
  7670       size = mozilla::gfx::RoundedToInt(ScreenSize(aDisplaySize) / defaultPixelScale);
  7673     size.width = clamped(size.width, kViewportMinSize.width, kViewportMaxSize.width);
  7675     // Also recalculate the default zoom, if it wasn't specified in the metadata,
  7676     // and the width is specified.
  7677     if (mScaleStrEmpty && !mWidthStrEmpty) {
  7678       CSSToScreenScale defaultScale(float(aDisplaySize.width) / float(size.width));
  7679       scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
  7682     size.height = clamped(size.height, kViewportMinSize.height, kViewportMaxSize.height);
  7684     // We need to perform a conversion, but only if the initial or maximum
  7685     // scale were set explicitly by the user.
  7686     if (mValidScaleFloat) {
  7687       CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleFloat);
  7688       size.width = std::max(size.width, displaySize.width);
  7689       size.height = std::max(size.height, displaySize.height);
  7690     } else if (mValidMaxScale) {
  7691       CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleMaxFloat);
  7692       size.width = std::max(size.width, displaySize.width);
  7693       size.height = std::max(size.height, displaySize.height);
  7696     return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
  7697                           mAutoSize, mAllowZoom, mAllowDoubleTapZoom);
  7701 EventListenerManager*
  7702 nsDocument::GetOrCreateListenerManager()
  7704   if (!mListenerManager) {
  7705     mListenerManager =
  7706       new EventListenerManager(static_cast<EventTarget*>(this));
  7707     SetFlags(NODE_HAS_LISTENERMANAGER);
  7710   return mListenerManager;
  7713 EventListenerManager*
  7714 nsDocument::GetExistingListenerManager() const
  7716   return mListenerManager;
  7719 nsresult
  7720 nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor)
  7722   aVisitor.mCanHandle = true;
  7723    // FIXME! This is a hack to make middle mouse paste working also in Editor.
  7724    // Bug 329119
  7725   aVisitor.mForceContentDispatch = true;
  7727   // Load events must not propagate to |window| object, see bug 335251.
  7728   if (aVisitor.mEvent->message != NS_LOAD) {
  7729     nsGlobalWindow* window = static_cast<nsGlobalWindow*>(GetWindow());
  7730     aVisitor.mParentTarget =
  7731       window ? window->GetTargetForEventTargetChain() : nullptr;
  7733   return NS_OK;
  7736 NS_IMETHODIMP
  7737 nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
  7739   NS_ENSURE_ARG_POINTER(aReturn);
  7740   ErrorResult rv;
  7741   *aReturn = nsIDocument::CreateEvent(aEventType, rv).take();
  7742   return rv.ErrorCode();
  7745 already_AddRefed<Event>
  7746 nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
  7748   nsIPresShell *shell = GetShell();
  7750   nsPresContext *presContext = nullptr;
  7752   if (shell) {
  7753     // Retrieve the context
  7754     presContext = shell->GetPresContext();
  7757   // Create event even without presContext.
  7758   nsCOMPtr<nsIDOMEvent> ev;
  7759   rv = EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this),
  7760                                     presContext, nullptr, aEventType,
  7761                                     getter_AddRefs(ev));
  7762   if (!ev) {
  7763     return nullptr;
  7765   WidgetEvent* e = ev->GetInternalNSEvent();
  7766   e->mFlags.mBubbles = false;
  7767   e->mFlags.mCancelable = false;
  7768   return dont_AddRef(ev.forget().take()->InternalDOMEvent());
  7771 void
  7772 nsDocument::FlushPendingNotifications(mozFlushType aType)
  7774   nsDocumentOnStack dos(this);
  7776   // We need to flush the sink for non-HTML documents (because the XML
  7777   // parser still does insertion with deferred notifications).  We
  7778   // also need to flush the sink if this is a layout-related flush, to
  7779   // make sure that layout is started as needed.  But we can skip that
  7780   // part if we have no presshell or if it's already done an initial
  7781   // reflow.
  7782   if ((!IsHTML() ||
  7783        (aType > Flush_ContentAndNotify && mPresShell &&
  7784         !mPresShell->DidInitialize())) &&
  7785       (mParser || mWeakSink)) {
  7786     nsCOMPtr<nsIContentSink> sink;
  7787     if (mParser) {
  7788       sink = mParser->GetContentSink();
  7789     } else {
  7790       sink = do_QueryReferent(mWeakSink);
  7791       if (!sink) {
  7792         mWeakSink = nullptr;
  7795     // Determine if it is safe to flush the sink notifications
  7796     // by determining if it safe to flush all the presshells.
  7797     if (sink && (aType == Flush_Content || IsSafeToFlush())) {
  7798       sink->FlushPendingNotifications(aType);
  7802   // Should we be flushing pending binding constructors in here?
  7804   if (aType <= Flush_ContentAndNotify) {
  7805     // Nothing to do here
  7806     return;
  7809   // If we have a parent we must flush the parent too to ensure that our
  7810   // container is reflowed if its size was changed.  But if it's not safe to
  7811   // flush ourselves, then don't flush the parent, since that can cause things
  7812   // like resizes of our frame's widget, which we can't handle while flushing
  7813   // is unsafe.
  7814   // Since media queries mean that a size change of our container can
  7815   // affect style, we need to promote a style flush on ourself to a
  7816   // layout flush on our parent, since we need our container to be the
  7817   // correct size to determine the correct style.
  7818   if (mParentDocument && IsSafeToFlush()) {
  7819     mozFlushType parentType = aType;
  7820     if (aType >= Flush_Style)
  7821       parentType = std::max(Flush_Layout, aType);
  7822     mParentDocument->FlushPendingNotifications(parentType);
  7825   // We can optimize away getting our presshell and calling
  7826   // FlushPendingNotifications on it if we don't need a flush of the sort we're
  7827   // looking at.  The one exception is if mInFlush is true, because in that
  7828   // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false
  7829   // already but the presshell hasn't actually done the corresponding work yet.
  7830   // So if mInFlush and reentering this code, we need to flush the presshell.
  7831   if (mNeedStyleFlush ||
  7832       (mNeedLayoutFlush && aType >= Flush_InterruptibleLayout) ||
  7833       aType >= Flush_Display ||
  7834       mInFlush) {
  7835     nsCOMPtr<nsIPresShell> shell = GetShell();
  7836     if (shell) {
  7837       mNeedStyleFlush = false;
  7838       mNeedLayoutFlush = mNeedLayoutFlush && (aType < Flush_InterruptibleLayout);
  7839       // mInFlush is a bitfield, so can't us AutoRestore here.  But we
  7840       // need to keep track of multi-level reentry correctly, so need
  7841       // to restore the old mInFlush value.
  7842       bool oldInFlush = mInFlush;
  7843       mInFlush = true;
  7844       shell->FlushPendingNotifications(aType);
  7845       mInFlush = oldInFlush;
  7850 static bool
  7851 Copy(nsIDocument* aDocument, void* aData)
  7853   nsTArray<nsCOMPtr<nsIDocument> >* resources =
  7854     static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
  7855   resources->AppendElement(aDocument);
  7856   return true;
  7859 void
  7860 nsDocument::FlushExternalResources(mozFlushType aType)
  7862   NS_ASSERTION(aType >= Flush_Style,
  7863     "should only need to flush for style or higher in external resources");
  7864   if (GetDisplayDocument()) {
  7865     return;
  7867   nsTArray<nsCOMPtr<nsIDocument> > resources;
  7868   EnumerateExternalResources(Copy, &resources);
  7870   for (uint32_t i = 0; i < resources.Length(); i++) {
  7871     resources[i]->FlushPendingNotifications(aType);
  7875 void
  7876 nsDocument::SetXMLDeclaration(const char16_t *aVersion,
  7877                               const char16_t *aEncoding,
  7878                               const int32_t aStandalone)
  7880   if (!aVersion || *aVersion == '\0') {
  7881     mXMLDeclarationBits = 0;
  7882     return;
  7885   mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
  7887   if (aEncoding && *aEncoding != '\0') {
  7888     mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
  7891   if (aStandalone == 1) {
  7892     mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
  7893                            XML_DECLARATION_BITS_STANDALONE_YES;
  7895   else if (aStandalone == 0) {
  7896     mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
  7900 void
  7901 nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
  7902                               nsAString& aStandalone)
  7904   aVersion.Truncate();
  7905   aEncoding.Truncate();
  7906   aStandalone.Truncate();
  7908   if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
  7909     return;
  7912   // always until we start supporting 1.1 etc.
  7913   aVersion.AssignLiteral("1.0");
  7915   if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
  7916     // This is what we have stored, not necessarily what was written
  7917     // in the original
  7918     GetCharacterSet(aEncoding);
  7921   if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
  7922     if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
  7923       aStandalone.AssignLiteral("yes");
  7924     } else {
  7925       aStandalone.AssignLiteral("no");
  7930 bool
  7931 nsDocument::IsScriptEnabled()
  7933   // If this document is sandboxed without 'allow-scripts'
  7934   // script is not enabled
  7935   if (mSandboxFlags & SANDBOXED_SCRIPTS) {
  7936     return false;
  7939   nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
  7940   NS_ENSURE_TRUE(sm, false);
  7942   nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
  7943   NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false);
  7945   return sm->ScriptAllowed(globalObject->GetGlobalJSObject());
  7948 nsRadioGroupStruct*
  7949 nsDocument::GetRadioGroupInternal(const nsAString& aName) const
  7951 #ifdef DEBUG
  7952   if (IsHTML()) {
  7953     nsAutoString lcName;
  7954     ToLowerCase(aName, lcName);
  7955     MOZ_ASSERT(aName == lcName);
  7957 #endif
  7959   nsRadioGroupStruct* radioGroup;
  7960   if (!mRadioGroups.Get(aName, &radioGroup)) {
  7961     return nullptr;
  7964   return radioGroup;
  7967 nsRadioGroupStruct*
  7968 nsDocument::GetRadioGroup(const nsAString& aName) const
  7970   nsAutoString tmKey(aName);
  7971   if (IsHTML()) {
  7972     ToLowerCase(tmKey); //should case-insensitive.
  7975   return GetRadioGroupInternal(tmKey);
  7978 nsRadioGroupStruct*
  7979 nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
  7981   nsAutoString tmKey(aName);
  7982   if (IsHTML()) {
  7983     ToLowerCase(tmKey); //should case-insensitive.
  7986   if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) {
  7987     return radioGroup;
  7990   nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct());
  7991   mRadioGroups.Put(tmKey, newRadioGroup);
  7993   return newRadioGroup.forget();
  7996 void
  7997 nsDocument::SetCurrentRadioButton(const nsAString& aName,
  7998                                   HTMLInputElement* aRadio)
  8000   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8001   radioGroup->mSelectedRadioButton = aRadio;
  8004 HTMLInputElement*
  8005 nsDocument::GetCurrentRadioButton(const nsAString& aName)
  8007   return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
  8010 NS_IMETHODIMP
  8011 nsDocument::GetNextRadioButton(const nsAString& aName,
  8012                                const bool aPrevious,
  8013                                HTMLInputElement* aFocusedRadio,
  8014                                HTMLInputElement** aRadioOut)
  8016   // XXX Can we combine the HTML radio button method impls of
  8017   //     nsDocument and nsHTMLFormControl?
  8018   // XXX Why is HTML radio button stuff in nsDocument, as
  8019   //     opposed to nsHTMLDocument?
  8020   *aRadioOut = nullptr;
  8022   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8024   // Return the radio button relative to the focused radio button.
  8025   // If no radio is focused, get the radio relative to the selected one.
  8026   nsRefPtr<HTMLInputElement> currentRadio;
  8027   if (aFocusedRadio) {
  8028     currentRadio = aFocusedRadio;
  8030   else {
  8031     currentRadio = radioGroup->mSelectedRadioButton;
  8032     if (!currentRadio) {
  8033       return NS_ERROR_FAILURE;
  8036   int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
  8037   if (index < 0) {
  8038     return NS_ERROR_FAILURE;
  8041   int32_t numRadios = radioGroup->mRadioButtons.Count();
  8042   nsRefPtr<HTMLInputElement> radio;
  8043   do {
  8044     if (aPrevious) {
  8045       if (--index < 0) {
  8046         index = numRadios -1;
  8049     else if (++index >= numRadios) {
  8050       index = 0;
  8052     NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTML(nsGkAtoms::input),
  8053                  "mRadioButtons holding a non-radio button");
  8054     radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
  8055   } while (radio->Disabled() && radio != currentRadio);
  8057   radio.forget(aRadioOut);
  8058   return NS_OK;
  8061 void
  8062 nsDocument::AddToRadioGroup(const nsAString& aName,
  8063                             nsIFormControl* aRadio)
  8065   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8066   radioGroup->mRadioButtons.AppendObject(aRadio);
  8068   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
  8069   NS_ASSERTION(element, "radio controls have to be content elements");
  8070   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
  8071     radioGroup->mRequiredRadioCount++;
  8075 void
  8076 nsDocument::RemoveFromRadioGroup(const nsAString& aName,
  8077                                  nsIFormControl* aRadio)
  8079   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8080   radioGroup->mRadioButtons.RemoveObject(aRadio);
  8082   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
  8083   NS_ASSERTION(element, "radio controls have to be content elements");
  8084   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
  8085     NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
  8086                  "mRequiredRadioCount about to wrap below 0!");
  8087     radioGroup->mRequiredRadioCount--;
  8091 NS_IMETHODIMP
  8092 nsDocument::WalkRadioGroup(const nsAString& aName,
  8093                            nsIRadioVisitor* aVisitor,
  8094                            bool aFlushContent)
  8096   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8098   for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
  8099     if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
  8100       return NS_OK;
  8104   return NS_OK;
  8107 uint32_t
  8108 nsDocument::GetRequiredRadioCount(const nsAString& aName) const
  8110   nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
  8111   return radioGroup ? radioGroup->mRequiredRadioCount : 0;
  8114 void
  8115 nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio)
  8117   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8119   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
  8120   NS_ASSERTION(element, "radio controls have to be content elements");
  8121   if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
  8122     radioGroup->mRequiredRadioCount++;
  8123   } else {
  8124     NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
  8125                  "mRequiredRadioCount about to wrap below 0!");
  8126     radioGroup->mRequiredRadioCount--;
  8130 bool
  8131 nsDocument::GetValueMissingState(const nsAString& aName) const
  8133   nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
  8134   return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
  8137 void
  8138 nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
  8140   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
  8141   radioGroup->mGroupSuffersFromValueMissing = aValue;
  8144 void
  8145 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
  8147   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
  8148   PRTime modDate = 0;
  8149   nsresult rv;
  8151   if (httpChannel) {
  8152     nsAutoCString tmp;
  8153     rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
  8154                                         tmp);
  8156     if (NS_SUCCEEDED(rv)) {
  8157       PRTime time;
  8158       PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
  8159       if (st == PR_SUCCESS) {
  8160         modDate = time;
  8164     // The misspelled key 'referer' is as per the HTTP spec
  8165     rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
  8166                                        mReferrer);
  8167     if (NS_FAILED(rv)) {
  8168       mReferrer.Truncate();
  8171     static const char *const headers[] = {
  8172       "default-style",
  8173       "content-style-type",
  8174       "content-language",
  8175       "content-disposition",
  8176       "refresh",
  8177       "x-dns-prefetch-control",
  8178       "x-frame-options",
  8179       // add more http headers if you need
  8180       // XXXbz don't add content-location support without reading bug
  8181       // 238654 and its dependencies/dups first.
  8183     };
  8185     nsAutoCString headerVal;
  8186     const char *const *name = headers;
  8187     while (*name) {
  8188       rv =
  8189         httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
  8190       if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
  8191         nsCOMPtr<nsIAtom> key = do_GetAtom(*name);
  8192         SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
  8194       ++name;
  8196   } else {
  8197     nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
  8198     if (fileChannel) {
  8199       nsCOMPtr<nsIFile> file;
  8200       fileChannel->GetFile(getter_AddRefs(file));
  8201       if (file) {
  8202         PRTime msecs;
  8203         rv = file->GetLastModifiedTime(&msecs);
  8205         if (NS_SUCCEEDED(rv)) {
  8206           modDate = msecs * int64_t(PR_USEC_PER_MSEC);
  8209     } else {
  8210       nsAutoCString contentDisp;
  8211       rv = aChannel->GetContentDispositionHeader(contentDisp);
  8212       if (NS_SUCCEEDED(rv)) {
  8213         SetHeaderData(nsGkAtoms::headerContentDisposition,
  8214                       NS_ConvertASCIItoUTF16(contentDisp));
  8219   if (modDate == 0) {
  8220     // We got nothing from our attempt to ask nsIFileChannel and
  8221     // nsIHttpChannel for the last modified time. Return the current
  8222     // time.
  8223     modDate = PR_Now();
  8226   mLastModified.Truncate();
  8227   if (modDate != 0) {
  8228     PRExplodedTime prtime;
  8229     PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime);
  8230     // "MM/DD/YYYY hh:mm:ss"
  8231     char formatedTime[24];
  8232     if (PR_snprintf(formatedTime, sizeof(formatedTime),
  8233                     "%02ld/%02ld/%04hd %02ld:%02ld:%02ld",
  8234                     prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year,
  8235                     prtime.tm_hour     ,  prtime.tm_min,  prtime.tm_sec)) {
  8236       CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified);
  8241 nsresult
  8242 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix, int32_t aNamespaceID,
  8243                        nsIContent **aResult)
  8245 #ifdef DEBUG
  8246   nsAutoString qName;
  8247   if (aPrefix) {
  8248     aPrefix->ToString(qName);
  8249     qName.Append(':');
  8251   qName.Append(aName);
  8253   // Note: "a:b:c" is a valid name in non-namespaces XML, and
  8254   // nsDocument::CreateElement can call us with such a name and no prefix,
  8255   // which would cause an error if we just used true here.
  8256   bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
  8257   NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
  8258                "Don't pass invalid prefixes to nsDocument::CreateElem, "
  8259                "check caller.");
  8260 #endif
  8262   *aResult = nullptr;
  8264   nsCOMPtr<nsINodeInfo> nodeInfo;
  8265   mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
  8266                                 nsIDOMNode::ELEMENT_NODE,
  8267                                 getter_AddRefs(nodeInfo));
  8268   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
  8270   nsCOMPtr<Element> element;
  8271   nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
  8272                               NOT_FROM_PARSER);
  8273   element.forget(aResult);
  8274   return rv;
  8277 bool
  8278 nsDocument::IsSafeToFlush() const
  8280   nsIPresShell* shell = GetShell();
  8281   if (!shell)
  8282     return true;
  8284   return shell->IsSafeToFlush();
  8287 void
  8288 nsDocument::Sanitize()
  8290   // Sanitize the document by resetting all password fields and any form
  8291   // fields with autocomplete=off to their default values.  We do this now,
  8292   // instead of when the presentation is restored, to offer some protection
  8293   // in case there is ever an exploit that allows a cached document to be
  8294   // accessed from a different document.
  8296   // First locate all input elements, regardless of whether they are
  8297   // in a form, and reset the password and autocomplete=off elements.
  8299   nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
  8301   nsCOMPtr<nsIContent> item;
  8302   nsAutoString value;
  8304   uint32_t length = nodes->Length(true);
  8305   for (uint32_t i = 0; i < length; ++i) {
  8306     NS_ASSERTION(nodes->Item(i), "null item in node list!");
  8308     nsRefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i));
  8309     if (!input)
  8310       continue;
  8312     bool resetValue = false;
  8314     input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
  8315     if (value.LowerCaseEqualsLiteral("off")) {
  8316       resetValue = true;
  8317     } else {
  8318       input->GetType(value);
  8319       if (value.LowerCaseEqualsLiteral("password"))
  8320         resetValue = true;
  8323     if (resetValue) {
  8324       input->Reset();
  8328   // Now locate all _form_ elements that have autocomplete=off and reset them
  8329   nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
  8331   length = nodes->Length(true);
  8332   for (uint32_t i = 0; i < length; ++i) {
  8333     NS_ASSERTION(nodes->Item(i), "null item in nodelist");
  8335     nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i));
  8336     if (!form)
  8337       continue;
  8339     nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None,
  8340                                          nsGkAtoms::autocomplete, value);
  8341     if (value.LowerCaseEqualsLiteral("off"))
  8342       form->Reset();
  8346 struct SubDocEnumArgs
  8348   nsIDocument::nsSubDocEnumFunc callback;
  8349   void *data;
  8350 };
  8352 static PLDHashOperator
  8353 SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr,
  8354                uint32_t number, void *arg)
  8356   SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
  8357   SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg);
  8359   nsIDocument *subdoc = entry->mSubDocument;
  8360   bool next = subdoc ? args->callback(subdoc, args->data) : true;
  8362   return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
  8365 void
  8366 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
  8368   if (mSubDocuments) {
  8369     SubDocEnumArgs args = { aCallback, aData };
  8370     PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args);
  8374 static PLDHashOperator
  8375 CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr,
  8376                     uint32_t number, void *arg)
  8378   SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
  8379   bool *canCacheArg = static_cast<bool*>(arg);
  8381   nsIDocument *subdoc = entry->mSubDocument;
  8383   // The aIgnoreRequest we were passed is only for us, so don't pass it on.
  8384   bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
  8385   if (!canCache) {
  8386     *canCacheArg = false;
  8387     return PL_DHASH_STOP;
  8390   return PL_DHASH_NEXT;
  8393 #ifdef DEBUG_bryner
  8394 #define DEBUG_PAGE_CACHE
  8395 #endif
  8397 bool
  8398 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
  8400   if (EventHandlingSuppressed()) {
  8401     return false;
  8404   nsPIDOMWindow* win = GetInnerWindow();
  8405   if (win && win->TimeoutSuspendCount()) {
  8406     return false;
  8409   // Check our event listener manager for unload/beforeunload listeners.
  8410   nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
  8411   if (piTarget) {
  8412     EventListenerManager* manager = piTarget->GetExistingListenerManager();
  8413     if (manager && manager->HasUnloadListeners()) {
  8414       return false;
  8418   // Check if we have pending network requests
  8419   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  8420   if (loadGroup) {
  8421     nsCOMPtr<nsISimpleEnumerator> requests;
  8422     loadGroup->GetRequests(getter_AddRefs(requests));
  8424     bool hasMore = false;
  8426     // We want to bail out if we have any requests other than aNewRequest (or
  8427     // in the case when aNewRequest is a part of a multipart response the base
  8428     // channel the multipart response is coming in on).
  8429     nsCOMPtr<nsIChannel> baseChannel;
  8430     nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
  8431     if (part) {
  8432       part->GetBaseChannel(getter_AddRefs(baseChannel));
  8435     while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
  8436       nsCOMPtr<nsISupports> elem;
  8437       requests->GetNext(getter_AddRefs(elem));
  8439       nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
  8440       if (request && request != aNewRequest && request != baseChannel) {
  8441 #ifdef DEBUG_PAGE_CACHE
  8442         nsAutoCString requestName, docSpec;
  8443         request->GetName(requestName);
  8444         if (mDocumentURI)
  8445           mDocumentURI->GetSpec(docSpec);
  8447         printf("document %s has request %s\n",
  8448                docSpec.get(), requestName.get());
  8449 #endif
  8450         return false;
  8455   // Check if we have running offline storage transactions
  8456   quota::QuotaManager* quotaManager =
  8457     win ? quota::QuotaManager::Get() : nullptr;
  8458   if (quotaManager && quotaManager->HasOpenTransactions(win)) {
  8459    return false;
  8462 #ifdef MOZ_MEDIA_NAVIGATOR
  8463   // Check if we have active GetUserMedia use
  8464   if (MediaManager::Exists() && win &&
  8465       MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
  8466     return false;
  8468 #endif // MOZ_MEDIA_NAVIGATOR
  8470 #ifdef MOZ_WEBRTC
  8471   // Check if we have active PeerConnections
  8472   nsCOMPtr<IPeerConnectionManager> pcManager =
  8473     do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
  8475   if (pcManager && win) {
  8476     bool active;
  8477     pcManager->HasActivePeerConnection(win->WindowID(), &active);
  8478     if (active) {
  8479       return false;
  8482 #endif // MOZ_WEBRTC
  8484   bool canCache = true;
  8485   if (mSubDocuments)
  8486     PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache);
  8488   return canCache;
  8491 void
  8492 nsDocument::Destroy()
  8494   // The ContentViewer wants to release the document now.  So, tell our content
  8495   // to drop any references to the document so that it can be destroyed.
  8496   if (mIsGoingAway)
  8497     return;
  8499   mIsGoingAway = true;
  8501   RemovedFromDocShell();
  8503   bool oldVal = mInUnlinkOrDeletion;
  8504   mInUnlinkOrDeletion = true;
  8505   uint32_t i, count = mChildren.ChildCount();
  8506   for (i = 0; i < count; ++i) {
  8507     mChildren.ChildAt(i)->DestroyContent();
  8509   mInUnlinkOrDeletion = oldVal;
  8511   mLayoutHistoryState = nullptr;
  8513   // Shut down our external resource map.  We might not need this for
  8514   // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
  8515   // tearing down all those frame trees right now is the right thing to do.
  8516   mExternalResourceMap.Shutdown();
  8518   mRegistry = nullptr;
  8520   // XXX We really should let cycle collection do this, but that currently still
  8521   //     leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
  8522   ReleaseWrapper(static_cast<nsINode*>(this));
  8525 void
  8526 nsDocument::RemovedFromDocShell()
  8528   if (mRemovedFromDocShell)
  8529     return;
  8531   mRemovedFromDocShell = true;
  8532   EnumerateFreezableElements(NotifyActivityChanged, nullptr);
  8534   uint32_t i, count = mChildren.ChildCount();
  8535   for (i = 0; i < count; ++i) {
  8536     mChildren.ChildAt(i)->SaveSubtreeState();
  8540 already_AddRefed<nsILayoutHistoryState>
  8541 nsDocument::GetLayoutHistoryState() const
  8543   nsCOMPtr<nsILayoutHistoryState> state;
  8544   if (!mScriptGlobalObject) {
  8545     state = mLayoutHistoryState;
  8546   } else {
  8547     nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  8548     if (docShell) {
  8549       docShell->GetLayoutHistoryState(getter_AddRefs(state));
  8553   return state.forget();
  8556 void
  8557 nsDocument::EnsureOnloadBlocker()
  8559   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
  8560   // -- it's not ours.
  8561   if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
  8562     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  8563     if (loadGroup) {
  8564       // Check first to see if mOnloadBlocker is in the loadgroup.
  8565       nsCOMPtr<nsISimpleEnumerator> requests;
  8566       loadGroup->GetRequests(getter_AddRefs(requests));
  8568       bool hasMore = false;
  8569       while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
  8570         nsCOMPtr<nsISupports> elem;
  8571         requests->GetNext(getter_AddRefs(elem));
  8572         nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
  8573         if (request && request == mOnloadBlocker) {
  8574           return;
  8578       // Not in the loadgroup, so add it.
  8579       loadGroup->AddRequest(mOnloadBlocker, nullptr);
  8584 void
  8585 nsDocument::AsyncBlockOnload()
  8587   while (mAsyncOnloadBlockCount) {
  8588     --mAsyncOnloadBlockCount;
  8589     BlockOnload();
  8593 void
  8594 nsDocument::BlockOnload()
  8596   if (mDisplayDocument) {
  8597     mDisplayDocument->BlockOnload();
  8598     return;
  8601   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
  8602   // -- it's not ours.
  8603   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
  8604     if (!nsContentUtils::IsSafeToRunScript()) {
  8605       // Because AddRequest may lead to OnStateChange calls in chrome,
  8606       // block onload only when there are no script blockers.
  8607       ++mAsyncOnloadBlockCount;
  8608       if (mAsyncOnloadBlockCount == 1) {
  8609         bool success = nsContentUtils::AddScriptRunner(
  8610           NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
  8612         // The script runner shouldn't fail to add. But if somebody broke
  8613         // something and it does, we'll thrash at 100% cpu forever. The best
  8614         // response is just to ignore the onload blocking request. See bug 579535.
  8615         if (!success) {
  8616           NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
  8617           mAsyncOnloadBlockCount = 0;
  8620       return;
  8622     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  8623     if (loadGroup) {
  8624       loadGroup->AddRequest(mOnloadBlocker, nullptr);
  8627   ++mOnloadBlockCount;
  8630 void
  8631 nsDocument::UnblockOnload(bool aFireSync)
  8633   if (mDisplayDocument) {
  8634     mDisplayDocument->UnblockOnload(aFireSync);
  8635     return;
  8638   if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
  8639     NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
  8640     return;
  8643   --mOnloadBlockCount;
  8645   if (mOnloadBlockCount == 0) {
  8646     if (mScriptGlobalObject) {
  8647       // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
  8648       // is null, it's not ours.
  8649       if (aFireSync && mAsyncOnloadBlockCount == 0) {
  8650         // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
  8651         ++mOnloadBlockCount;
  8652         DoUnblockOnload();
  8653       } else {
  8654         PostUnblockOnloadEvent();
  8656     } else if (mIsBeingUsedAsImage) {
  8657       // To correctly unblock onload for a document that contains an SVG
  8658       // image, we need to know when all of the SVG document's resources are
  8659       // done loading, in a way comparable to |window.onload|. We fire this
  8660       // event to indicate that the SVG should be considered fully loaded.
  8661       // Because scripting is disabled on SVG-as-image documents, this event
  8662       // is not accessible to content authors. (See bug 837135.)
  8663       nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  8664         new AsyncEventDispatcher(this,
  8665                                  NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
  8666                                  false,
  8667                                  false);
  8668       asyncDispatcher->PostDOMEvent();
  8673 class nsUnblockOnloadEvent : public nsRunnable {
  8674 public:
  8675   nsUnblockOnloadEvent(nsDocument *doc) : mDoc(doc) {}
  8676   NS_IMETHOD Run() {
  8677     mDoc->DoUnblockOnload();
  8678     return NS_OK;
  8680 private:
  8681   nsRefPtr<nsDocument> mDoc;
  8682 };
  8684 void
  8685 nsDocument::PostUnblockOnloadEvent()
  8687   nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
  8688   nsresult rv = NS_DispatchToCurrentThread(evt);
  8689   if (NS_SUCCEEDED(rv)) {
  8690     // Stabilize block count so we don't post more events while this one is up
  8691     ++mOnloadBlockCount;
  8692   } else {
  8693     NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
  8697 void
  8698 nsDocument::DoUnblockOnload()
  8700   NS_PRECONDITION(!mDisplayDocument,
  8701                   "Shouldn't get here for resource document");
  8702   NS_PRECONDITION(mOnloadBlockCount != 0,
  8703                   "Shouldn't have a count of zero here, since we stabilized in "
  8704                   "PostUnblockOnloadEvent");
  8706   --mOnloadBlockCount;
  8708   if (mOnloadBlockCount != 0) {
  8709     // We blocked again after the last unblock.  Nothing to do here.  We'll
  8710     // post a new event when we unblock again.
  8711     return;
  8714   if (mAsyncOnloadBlockCount != 0) {
  8715     // We need to wait until the async onload block has been handled.
  8716     PostUnblockOnloadEvent();
  8719   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
  8720   // -- it's not ours.
  8721   if (mScriptGlobalObject) {
  8722     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  8723     if (loadGroup) {
  8724       loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
  8729 nsIContent*
  8730 nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
  8732   for (nsIFrame* f = aFrame; f;
  8733        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
  8734     nsIContent* content = f->GetContent();
  8735     if (!content || content->IsInAnonymousSubtree())
  8736       continue;
  8738     if (content->OwnerDoc() == this) {
  8739       return content;
  8741     // We must be in a subdocument so jump directly to the root frame.
  8742     // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
  8743     // the containing document.
  8744     f = f->PresContext()->GetPresShell()->GetRootFrame();
  8747   return nullptr;
  8750 void
  8751 nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
  8752                                    const nsAString& aType,
  8753                                    bool aPersisted)
  8755   if (aDispatchTarget) {
  8756     nsCOMPtr<nsIDOMEvent> event;
  8757     CreateEvent(NS_LITERAL_STRING("pagetransition"), getter_AddRefs(event));
  8758     nsCOMPtr<nsIDOMPageTransitionEvent> ptEvent = do_QueryInterface(event);
  8759     if (ptEvent && NS_SUCCEEDED(ptEvent->InitPageTransitionEvent(aType, true,
  8760                                                                  true,
  8761                                                                  aPersisted))) {
  8762       event->SetTrusted(true);
  8763       event->SetTarget(this);
  8764       EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
  8765                                         nullptr, nullptr);
  8770 static bool
  8771 NotifyPageShow(nsIDocument* aDocument, void* aData)
  8773   const bool* aPersistedPtr = static_cast<const bool*>(aData);
  8774   aDocument->OnPageShow(*aPersistedPtr, nullptr);
  8775   return true;
  8778 void
  8779 nsDocument::OnPageShow(bool aPersisted,
  8780                        EventTarget* aDispatchStartTarget)
  8782   mVisible = true;
  8784   EnumerateFreezableElements(NotifyActivityChanged, nullptr);
  8785   EnumerateExternalResources(NotifyPageShow, &aPersisted);
  8787   Element* root = GetRootElement();
  8788   if (aPersisted && root) {
  8789     // Send out notifications that our <link> elements are attached.
  8790     nsRefPtr<nsContentList> links = NS_GetContentList(root,
  8791                                                       kNameSpaceID_XHTML,
  8792                                                       NS_LITERAL_STRING("link"));
  8794     uint32_t linkCount = links->Length(true);
  8795     for (uint32_t i = 0; i < linkCount; ++i) {
  8796       static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
  8800   // See nsIDocument
  8801   if (!aDispatchStartTarget) {
  8802     // Set mIsShowing before firing events, in case those event handlers
  8803     // move us around.
  8804     mIsShowing = true;
  8807   if (mAnimationController) {
  8808     mAnimationController->OnPageShow();
  8811   if (aPersisted) {
  8812     SetImagesNeedAnimating(true);
  8815   UpdateVisibilityState();
  8817   nsCOMPtr<EventTarget> target = aDispatchStartTarget;
  8818   if (!target) {
  8819     target = do_QueryInterface(GetWindow());
  8822   // Dispatch observer notification to notify observers page is shown.
  8823   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  8824   nsIPrincipal *principal = GetPrincipal();
  8825   os->NotifyObservers(static_cast<nsIDocument*>(this),
  8826                       nsContentUtils::IsSystemPrincipal(principal) ?
  8827                         "chrome-page-shown" :
  8828                         "content-page-shown",
  8829                       nullptr);
  8832   DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
  8835 static bool
  8836 NotifyPageHide(nsIDocument* aDocument, void* aData)
  8838   const bool* aPersistedPtr = static_cast<const bool*>(aData);
  8839   aDocument->OnPageHide(*aPersistedPtr, nullptr);
  8840   return true;
  8843 static void
  8844 DispatchFullScreenChange(nsIDocument* aTarget)
  8846   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  8847     new AsyncEventDispatcher(aTarget,
  8848                              NS_LITERAL_STRING("mozfullscreenchange"),
  8849                              true,
  8850                              false);
  8851   asyncDispatcher->PostDOMEvent();
  8854 void
  8855 nsDocument::OnPageHide(bool aPersisted,
  8856                        EventTarget* aDispatchStartTarget)
  8858   // Send out notifications that our <link> elements are detached,
  8859   // but only if this is not a full unload.
  8860   Element* root = GetRootElement();
  8861   if (aPersisted && root) {
  8862     nsRefPtr<nsContentList> links = NS_GetContentList(root,
  8863                                                       kNameSpaceID_XHTML,
  8864                                                       NS_LITERAL_STRING("link"));
  8866     uint32_t linkCount = links->Length(true);
  8867     for (uint32_t i = 0; i < linkCount; ++i) {
  8868       static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
  8872   // See nsIDocument
  8873   if (!aDispatchStartTarget) {
  8874     // Set mIsShowing before firing events, in case those event handlers
  8875     // move us around.
  8876     mIsShowing = false;
  8879   if (mAnimationController) {
  8880     mAnimationController->OnPageHide();
  8883   if (aPersisted) {
  8884     SetImagesNeedAnimating(false);
  8887   MozExitPointerLock();
  8889   // Now send out a PageHide event.
  8890   nsCOMPtr<EventTarget> target = aDispatchStartTarget;
  8891   if (!target) {
  8892     target = do_QueryInterface(GetWindow());
  8895   // Dispatch observer notification to notify observers page is hidden.
  8896   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  8897   nsIPrincipal *principal = GetPrincipal();
  8898   os->NotifyObservers(static_cast<nsIDocument*>(this),
  8899                       nsContentUtils::IsSystemPrincipal(principal) ?
  8900                         "chrome-page-hidden" :
  8901                         "content-page-hidden",
  8902                       nullptr);
  8904   DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
  8906   mVisible = false;
  8908   UpdateVisibilityState();
  8910   EnumerateExternalResources(NotifyPageHide, &aPersisted);
  8911   EnumerateFreezableElements(NotifyActivityChanged, nullptr);
  8913   if (IsFullScreenDoc()) {
  8914     // If this document was fullscreen, we should exit fullscreen in this
  8915     // doctree branch. This ensures that if the user navigates while in
  8916     // fullscreen mode we don't leave its still visible ancestor documents
  8917     // in fullscreen mode. So exit fullscreen in the document's fullscreen
  8918     // root document, as this will exit fullscreen in all the root's
  8919     // descendant documents. Note that documents are removed from the
  8920     // doctree by the time OnPageHide() is called, so we must store a
  8921     // reference to the root (in nsDocument::mFullscreenRoot) since we can't
  8922     // just traverse the doctree to get the root.
  8923     nsIDocument::ExitFullscreen(this, /* async */ false);
  8925     // Since the document is removed from the doctree before OnPageHide() is
  8926     // called, ExitFullscreen() can't traverse from the root down to *this*
  8927     // document, so we must manually call CleanupFullscreenState() below too.
  8928     // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
  8929     // so we *must* call it after ExitFullscreen(), not before.
  8930     // OnPageHide() is called in every hidden (i.e. descendant) document,
  8931     // so calling CleanupFullscreenState() here will ensure all hidden
  8932     // documents have their fullscreen state reset.
  8933     CleanupFullscreenState();
  8935     // If anyone was listening to this document's state, advertizing the state
  8936     // change would be the least of the politeness.
  8937     DispatchFullScreenChange(this);
  8941 void
  8942 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
  8944   NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
  8945                mSubtreeModifiedTargets.Count() == 0,
  8946                "mSubtreeModifiedTargets not cleared after dispatching?");
  8947   ++mSubtreeModifiedDepth;
  8948   if (aTarget) {
  8949     // MayDispatchMutationEvent is often called just before this method,
  8950     // so it has already appended the node to mSubtreeModifiedTargets.
  8951     int32_t count = mSubtreeModifiedTargets.Count();
  8952     if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
  8953       mSubtreeModifiedTargets.AppendObject(aTarget);
  8958 void
  8959 nsDocument::MutationEventDispatched(nsINode* aTarget)
  8961   --mSubtreeModifiedDepth;
  8962   if (mSubtreeModifiedDepth == 0) {
  8963     int32_t count = mSubtreeModifiedTargets.Count();
  8964     if (!count) {
  8965       return;
  8968     nsCOMPtr<nsPIDOMWindow> window;
  8969     window = do_QueryInterface(GetWindow());
  8970     if (window &&
  8971         !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
  8972       mSubtreeModifiedTargets.Clear();
  8973       return;
  8976     nsCOMArray<nsINode> realTargets;
  8977     for (int32_t i = 0; i < count; ++i) {
  8978       nsINode* possibleTarget = mSubtreeModifiedTargets[i];
  8979       nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
  8980       if (content && content->ChromeOnlyAccess()) {
  8981         continue;
  8984       nsINode* commonAncestor = nullptr;
  8985       int32_t realTargetCount = realTargets.Count();
  8986       for (int32_t j = 0; j < realTargetCount; ++j) {
  8987         commonAncestor =
  8988           nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
  8989         if (commonAncestor) {
  8990           realTargets.ReplaceObjectAt(commonAncestor, j);
  8991           break;
  8994       if (!commonAncestor) {
  8995         realTargets.AppendObject(possibleTarget);
  8999     mSubtreeModifiedTargets.Clear();
  9001     int32_t realTargetCount = realTargets.Count();
  9002     for (int32_t k = 0; k < realTargetCount; ++k) {
  9003       InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED);
  9004       (new AsyncEventDispatcher(realTargets[k], mutation))->
  9005         RunDOMEventWhenSafe();
  9010 void
  9011 nsDocument::AddStyleRelevantLink(Link* aLink)
  9013   NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
  9014 #ifdef DEBUG
  9015   nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
  9016   NS_ASSERTION(!entry, "Document already knows about this Link!");
  9017   mStyledLinksCleared = false;
  9018 #endif
  9019   (void)mStyledLinks.PutEntry(aLink);
  9022 void
  9023 nsDocument::ForgetLink(Link* aLink)
  9025   NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
  9026 #ifdef DEBUG
  9027   nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
  9028   NS_ASSERTION(entry || mStyledLinksCleared,
  9029                "Document knows nothing about this Link!");
  9030 #endif
  9031   (void)mStyledLinks.RemoveEntry(aLink);
  9034 void
  9035 nsDocument::DestroyElementMaps()
  9037 #ifdef DEBUG
  9038   mStyledLinksCleared = true;
  9039 #endif
  9040   mStyledLinks.Clear();
  9041   mIdentifierMap.Clear();
  9042   ++mExpandoAndGeneration.generation;
  9045 static
  9046 PLDHashOperator
  9047 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
  9049   LinkArray* array = static_cast<LinkArray*>(aArray);
  9050   (void)array->AppendElement(aEntry->GetKey());
  9051   return PL_DHASH_NEXT;
  9054 void
  9055 nsDocument::RefreshLinkHrefs()
  9057   // Get a list of all links we know about.  We will reset them, which will
  9058   // remove them from the document, so we need a copy of what is in the
  9059   // hashtable.
  9060   LinkArray linksToNotify(mStyledLinks.Count());
  9061   (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
  9063   // Reset all of our styled links.
  9064   nsAutoScriptBlocker scriptBlocker;
  9065   for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
  9066     linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
  9070 nsresult
  9071 nsDocument::CloneDocHelper(nsDocument* clone) const
  9073   clone->mIsStaticDocument = mCreatingStaticClone;
  9075   // Init document
  9076   nsresult rv = clone->Init();
  9077   NS_ENSURE_SUCCESS(rv, rv);
  9079   // Set URI/principal
  9080   clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
  9081   clone->SetChromeXHRDocURI(mChromeXHRDocURI);
  9082   // Must set the principal first, since SetBaseURI checks it.
  9083   clone->SetPrincipal(NodePrincipal());
  9084   clone->mDocumentBaseURI = mDocumentBaseURI;
  9085   clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
  9087   if (mCreatingStaticClone) {
  9088     nsCOMPtr<nsILoadGroup> loadGroup;
  9090     // |mDocumentContainer| is the container of the document that is being
  9091     // created and not the original container. See CreateStaticClone function().
  9092     nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
  9093     if (docLoader) {
  9094       docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
  9096     nsCOMPtr<nsIChannel> channel = GetChannel();
  9097     if (channel && loadGroup) {
  9098       clone->Reset(channel, loadGroup);
  9099     } else {
  9100       nsIURI* uri = static_cast<const nsIDocument*>(this)->GetDocumentURI();
  9101       if (uri) {
  9102         clone->ResetToURI(uri, loadGroup, NodePrincipal());
  9105     clone->SetContainer(mDocumentContainer);
  9108   // Set scripting object
  9109   bool hasHadScriptObject = true;
  9110   nsIScriptGlobalObject* scriptObject =
  9111     GetScriptHandlingObject(hasHadScriptObject);
  9112   NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
  9113   if (scriptObject) {
  9114     clone->SetScriptHandlingObject(scriptObject);
  9115   } else {
  9116     clone->SetScopeObject(GetScopeObject());
  9118   // Make the clone a data document
  9119   clone->SetLoadedAsData(true);
  9121   // Misc state
  9123   // State from nsIDocument
  9124   clone->mCharacterSet = mCharacterSet;
  9125   clone->mCharacterSetSource = mCharacterSetSource;
  9126   clone->mCompatMode = mCompatMode;
  9127   clone->mBidiOptions = mBidiOptions;
  9128   clone->mContentLanguage = mContentLanguage;
  9129   clone->SetContentTypeInternal(GetContentTypeInternal());
  9130   clone->mSecurityInfo = mSecurityInfo;
  9132   // State from nsDocument
  9133   clone->mIsRegularHTML = mIsRegularHTML;
  9134   clone->mXMLDeclarationBits = mXMLDeclarationBits;
  9135   clone->mBaseTarget = mBaseTarget;
  9136   return NS_OK;
  9139 void
  9140 nsDocument::SetReadyStateInternal(ReadyState rs)
  9142   mReadyState = rs;
  9143   if (rs == READYSTATE_UNINITIALIZED) {
  9144     // Transition back to uninitialized happens only to keep assertions happy
  9145     // right before readyState transitions to something else. Make this
  9146     // transition undetectable by Web content.
  9147     return;
  9149   if (mTiming) {
  9150     switch (rs) {
  9151       case READYSTATE_LOADING:
  9152         mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
  9153         break;
  9154       case READYSTATE_INTERACTIVE:
  9155         mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
  9156         break;
  9157       case READYSTATE_COMPLETE:
  9158         mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
  9159         break;
  9160       default:
  9161         NS_WARNING("Unexpected ReadyState value");
  9162         break;
  9165   // At the time of loading start, we don't have timing object, record time.
  9166   if (READYSTATE_LOADING == rs) {
  9167     mLoadingTimeStamp = mozilla::TimeStamp::Now();
  9170   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
  9171     new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
  9172                              false, false);
  9173   asyncDispatcher->RunDOMEventWhenSafe();
  9176 NS_IMETHODIMP
  9177 nsDocument::GetReadyState(nsAString& aReadyState)
  9179   nsIDocument::GetReadyState(aReadyState);
  9180   return NS_OK;
  9183 void
  9184 nsIDocument::GetReadyState(nsAString& aReadyState) const
  9186   switch(mReadyState) {
  9187   case READYSTATE_LOADING :
  9188     aReadyState.Assign(NS_LITERAL_STRING("loading"));
  9189     break;
  9190   case READYSTATE_INTERACTIVE :
  9191     aReadyState.Assign(NS_LITERAL_STRING("interactive"));
  9192     break;
  9193   case READYSTATE_COMPLETE :
  9194     aReadyState.Assign(NS_LITERAL_STRING("complete"));
  9195     break;
  9196   default:
  9197     aReadyState.Assign(NS_LITERAL_STRING("uninitialized"));
  9201 namespace {
  9203 struct SuppressArgs
  9205   nsIDocument::SuppressionType mWhat;
  9206   uint32_t mIncrease;
  9207 };
  9211 static bool
  9212 SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
  9214   SuppressArgs* args = static_cast<SuppressArgs*>(aData);
  9215   aDocument->SuppressEventHandling(args->mWhat, args->mIncrease);
  9216   return true;
  9219 void
  9220 nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
  9221                                   uint32_t aIncrease)
  9223   if (mEventsSuppressed == 0 && mAnimationsPaused == 0 &&
  9224       aIncrease != 0 && mPresShell && mScriptGlobalObject) {
  9225     RevokeAnimationFrameNotifications();
  9228   if (aWhat == eAnimationsOnly) {
  9229     mAnimationsPaused += aIncrease;
  9230   } else {
  9231     mEventsSuppressed += aIncrease;
  9234   SuppressArgs args = { aWhat, aIncrease };
  9235   EnumerateSubDocuments(SuppressEventHandlingInDocument, &args);
  9238 static void
  9239 FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments,
  9240                          bool aFireEvents)
  9242   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
  9243   if (!fm)
  9244     return;
  9246   for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
  9247     // NB: Don't bother trying to fire delayed events on documents that were
  9248     // closed before this event ran.
  9249     if (!aDocuments[i]->EventHandlingSuppressed()) {
  9250       fm->FireDelayedEvents(aDocuments[i]);
  9251       nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
  9252       if (shell) {
  9253         // Only fire events for active documents.
  9254         bool fire = aFireEvents &&
  9255                     aDocuments[i]->GetInnerWindow() &&
  9256                     aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
  9257         shell->FireOrClearDelayedEvents(fire);
  9263 void
  9264 nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr)
  9266   // Early exit if the img is already present in the img-cache
  9267   // which indicates that the "real" load has already started and
  9268   // that we shouldn't preload it.
  9269   int16_t blockingStatus;
  9270   if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
  9271       !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
  9272                                     this, NodePrincipal(), &blockingStatus)) {
  9273     return;
  9276   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
  9277   switch (Element::StringToCORSMode(aCrossOriginAttr)) {
  9278   case CORS_NONE:
  9279     // Nothing to do
  9280     break;
  9281   case CORS_ANONYMOUS:
  9282     loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
  9283     break;
  9284   case CORS_USE_CREDENTIALS:
  9285     loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
  9286     break;
  9287   default:
  9288     MOZ_CRASH("Unknown CORS mode!");
  9291   // Image not in cache - trigger preload
  9292   nsRefPtr<imgRequestProxy> request;
  9293   nsresult rv =
  9294     nsContentUtils::LoadImage(uri,
  9295                               this,
  9296                               NodePrincipal(),
  9297                               mDocumentURI, // uri of document used as referrer
  9298                               nullptr,       // no observer
  9299                               loadFlags,
  9300                               NS_LITERAL_STRING("img"),
  9301                               getter_AddRefs(request));
  9303   // Pin image-reference to avoid evicting it from the img-cache before
  9304   // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
  9305   // unlink
  9306   if (NS_SUCCEEDED(rv)) {
  9307     mPreloadingImages.AppendObject(request);
  9311 EventStates
  9312 nsDocument::GetDocumentState()
  9314   if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
  9315     if (IsDocumentRightToLeft()) {
  9316       mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
  9318     mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
  9320   if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
  9321     nsIPresShell* shell = GetShell();
  9322     if (shell && shell->GetPresContext() &&
  9323         shell->GetPresContext()->IsTopLevelWindowInactive()) {
  9324       mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
  9326     mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
  9328   return mDocumentState;
  9331 namespace {
  9333 /**
  9334  * Stub for LoadSheet(), since all we want is to get the sheet into
  9335  * the CSSLoader's style cache
  9336  */
  9337 class StubCSSLoaderObserver MOZ_FINAL : public nsICSSLoaderObserver {
  9338 public:
  9339   NS_IMETHOD
  9340   StyleSheetLoaded(nsCSSStyleSheet*, bool, nsresult)
  9342     return NS_OK;
  9344   NS_DECL_ISUPPORTS
  9345 };
  9346 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
  9350 void
  9351 nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
  9352                          const nsAString& aCrossOriginAttr)
  9354   // The CSSLoader will retain this object after we return.
  9355   nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
  9357   // Charset names are always ASCII.
  9358   CSSLoader()->LoadSheet(uri, NodePrincipal(),
  9359                          NS_LossyConvertUTF16toASCII(charset),
  9360                          obs,
  9361                          Element::StringToCORSMode(aCrossOriginAttr));
  9364 nsresult
  9365 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
  9366                                 nsCSSStyleSheet** sheet)
  9368   return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet);
  9371 class nsDelayedEventDispatcher : public nsRunnable
  9373 public:
  9374   nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments)
  9376     mDocuments.SwapElements(aDocuments);
  9378   virtual ~nsDelayedEventDispatcher() {}
  9380   NS_IMETHOD Run()
  9382     FireOrClearDelayedEvents(mDocuments, true);
  9383     return NS_OK;
  9386 private:
  9387   nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
  9388 };
  9390 namespace {
  9392 struct UnsuppressArgs
  9394   UnsuppressArgs(nsIDocument::SuppressionType aWhat)
  9395     : mWhat(aWhat)
  9399   nsIDocument::SuppressionType mWhat;
  9400   nsTArray<nsCOMPtr<nsIDocument>> mDocs;
  9401 };
  9405 static bool
  9406 GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
  9407                              void* aData)
  9409   UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData);
  9410   if (args->mWhat != nsIDocument::eAnimationsOnly &&
  9411       aDocument->EventHandlingSuppressed() > 0) {
  9412     static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
  9413   } else if (args->mWhat == nsIDocument::eAnimationsOnly &&
  9414              aDocument->AnimationsPaused()) {
  9415     static_cast<nsDocument*>(aDocument)->ResumeAnimations();
  9418   if (args->mWhat != nsIDocument::eAnimationsOnly) {
  9419     // No need to remember documents if we only care about animation frames.
  9420     args->mDocs.AppendElement(aDocument);
  9423   aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
  9424   return true;
  9427 void
  9428 nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat,
  9429                                                  bool aFireEvents)
  9431   UnsuppressArgs args(aWhat);
  9432   GetAndUnsuppressSubDocuments(this, &args);
  9434   if (aWhat == nsIDocument::eAnimationsOnly) {
  9435     // No need to fire events if we only care about animations here.
  9436     return;
  9439   if (aFireEvents) {
  9440     NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs));
  9441   } else {
  9442     FireOrClearDelayedEvents(args.mDocs, false);
  9446 nsISupports*
  9447 nsDocument::GetCurrentContentSink()
  9449   return mParser ? mParser->GetContentSink() : nullptr;
  9452 nsIDocument*
  9453 nsDocument::GetTemplateContentsOwner()
  9455   if (!mTemplateContentsOwner) {
  9456     bool hasHadScriptObject = true;
  9457     nsIScriptGlobalObject* scriptObject =
  9458       GetScriptHandlingObject(hasHadScriptObject);
  9459     NS_ENSURE_TRUE(scriptObject || !hasHadScriptObject, nullptr);
  9461     nsCOMPtr<nsIDOMDocument> domDocument;
  9462     nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
  9463                                     EmptyString(), // aNamespaceURI
  9464                                     EmptyString(), // aQualifiedName
  9465                                     nullptr, // aDoctype
  9466                                     nsIDocument::GetDocumentURI(),
  9467                                     nsIDocument::GetDocBaseURI(),
  9468                                     NodePrincipal(),
  9469                                     true, // aLoadedAsData
  9470                                     scriptObject, // aEventObject
  9471                                     DocumentFlavorHTML);
  9472     NS_ENSURE_SUCCESS(rv, nullptr);
  9474     mTemplateContentsOwner = do_QueryInterface(domDocument);
  9475     NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
  9477     mTemplateContentsOwner->SetScriptHandlingObject(scriptObject);
  9479     // Set |doc| as the template contents owner of itself so that
  9480     // |doc| is the template contents owner of template elements created
  9481     // by |doc|.
  9482     nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
  9483     doc->mTemplateContentsOwner = doc;
  9486   return mTemplateContentsOwner;
  9489 void
  9490 nsDocument::RegisterHostObjectUri(const nsACString& aUri)
  9492   mHostObjectURIs.AppendElement(aUri);
  9495 void
  9496 nsDocument::UnregisterHostObjectUri(const nsACString& aUri)
  9498   mHostObjectURIs.RemoveElement(aUri);
  9501 void
  9502 nsDocument::SetScrollToRef(nsIURI *aDocumentURI)
  9504   if (!aDocumentURI) {
  9505     return;
  9508   nsAutoCString ref;
  9510   // Since all URI's that pass through here aren't URL's we can't
  9511   // rely on the nsIURI implementation for providing a way for
  9512   // finding the 'ref' part of the URI, we'll haveto revert to
  9513   // string routines for finding the data past '#'
  9515   aDocumentURI->GetSpec(ref);
  9517   nsReadingIterator<char> start, end;
  9519   ref.BeginReading(start);
  9520   ref.EndReading(end);
  9522   if (FindCharInReadable('#', start, end)) {
  9523     ++start; // Skip over the '#'
  9525     mScrollToRef = Substring(start, end);
  9529 void
  9530 nsDocument::ScrollToRef()
  9532   if (mScrolledToRefAlready) {
  9533     nsCOMPtr<nsIPresShell> shell = GetShell();
  9534     if (shell) {
  9535       shell->ScrollToAnchor();
  9537     return;
  9540   if (mScrollToRef.IsEmpty()) {
  9541     return;
  9544   char* tmpstr = ToNewCString(mScrollToRef);
  9545   if (!tmpstr) {
  9546     return;
  9549   nsUnescape(tmpstr);
  9550   nsAutoCString unescapedRef;
  9551   unescapedRef.Assign(tmpstr);
  9552   nsMemory::Free(tmpstr);
  9554   nsresult rv = NS_ERROR_FAILURE;
  9555   // We assume that the bytes are in UTF-8, as it says in the spec:
  9556   // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
  9557   NS_ConvertUTF8toUTF16 ref(unescapedRef);
  9559   nsCOMPtr<nsIPresShell> shell = GetShell();
  9560   if (shell) {
  9561     // Check an empty string which might be caused by the UTF-8 conversion
  9562     if (!ref.IsEmpty()) {
  9563       // Note that GoToAnchor will handle flushing layout as needed.
  9564       rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
  9565     } else {
  9566       rv = NS_ERROR_FAILURE;
  9569     // If UTF-8 URI failed then try to assume the string as a
  9570     // document's charset.
  9572     if (NS_FAILED(rv)) {
  9573       const nsACString &docCharset = GetDocumentCharacterSet();
  9575       rv = nsContentUtils::ConvertStringFromEncoding(docCharset,
  9576                                                      unescapedRef,
  9577                                                      ref);
  9579       if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
  9580         rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
  9583     if (NS_SUCCEEDED(rv)) {
  9584       mScrolledToRefAlready = true;
  9589 void
  9590 nsDocument::ResetScrolledToRefAlready()
  9592   mScrolledToRefAlready = false;
  9595 void
  9596 nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue)
  9598   mChangeScrollPosWhenScrollingToRef = aValue;
  9601 void
  9602 nsIDocument::RegisterFreezableElement(nsIContent* aContent)
  9604   if (!mFreezableElements) {
  9605     mFreezableElements = new nsTHashtable<nsPtrHashKey<nsIContent> >();
  9606     if (!mFreezableElements)
  9607       return;
  9609   mFreezableElements->PutEntry(aContent);
  9612 bool
  9613 nsIDocument::UnregisterFreezableElement(nsIContent* aContent)
  9615   if (!mFreezableElements)
  9616     return false;
  9617   if (!mFreezableElements->GetEntry(aContent))
  9618     return false;
  9619   mFreezableElements->RemoveEntry(aContent);
  9620   return true;
  9623 struct EnumerateFreezablesData {
  9624   nsIDocument::FreezableElementEnumerator mEnumerator;
  9625   void* mData;
  9626 };
  9628 static PLDHashOperator
  9629 EnumerateFreezables(nsPtrHashKey<nsIContent>* aEntry, void* aData)
  9631   EnumerateFreezablesData* data = static_cast<EnumerateFreezablesData*>(aData);
  9632   data->mEnumerator(aEntry->GetKey(), data->mData);
  9633   return PL_DHASH_NEXT;
  9636 void
  9637 nsIDocument::EnumerateFreezableElements(FreezableElementEnumerator aEnumerator,
  9638                                         void* aData)
  9640   if (!mFreezableElements)
  9641     return;
  9642   EnumerateFreezablesData data = { aEnumerator, aData };
  9643   mFreezableElements->EnumerateEntries(EnumerateFreezables, &data);
  9646 void
  9647 nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
  9649   MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
  9650   mLinksToUpdate.PutEntry(aLink);
  9651   mHasLinksToUpdate = true;
  9654 void
  9655 nsIDocument::UnregisterPendingLinkUpdate(Link* aLink)
  9657   MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
  9658   if (!mHasLinksToUpdate)
  9659     return;
  9661   mLinksToUpdate.RemoveEntry(aLink);
  9664 static PLDHashOperator
  9665 EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData)
  9667   aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState());
  9668   return PL_DHASH_NEXT;
  9671 void
  9672 nsIDocument::FlushPendingLinkUpdates()
  9674   MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
  9675   if (!mHasLinksToUpdate)
  9676     return;
  9678 #ifdef DEBUG
  9679   AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden);
  9680   mIsLinkUpdateRegistrationsForbidden = true;
  9681 #endif
  9682   mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nullptr);
  9683   mLinksToUpdate.Clear();
  9684   mHasLinksToUpdate = false;
  9687 already_AddRefed<nsIDocument>
  9688 nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
  9690   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(this);
  9691   NS_ENSURE_TRUE(domDoc, nullptr);
  9692   mCreatingStaticClone = true;
  9694   // Make document use different container during cloning.
  9695   nsRefPtr<nsDocShell> originalShell = mDocumentContainer.get();
  9696   SetContainer(static_cast<nsDocShell*>(aCloneContainer));
  9697   nsCOMPtr<nsIDOMNode> clonedNode;
  9698   nsresult rv = domDoc->CloneNode(true, 1, getter_AddRefs(clonedNode));
  9699   SetContainer(originalShell);
  9701   nsCOMPtr<nsIDocument> clonedDoc;
  9702   if (NS_SUCCEEDED(rv)) {
  9703     clonedDoc = do_QueryInterface(clonedNode);
  9704     if (clonedDoc) {
  9705       if (IsStaticDocument()) {
  9706         clonedDoc->mOriginalDocument = mOriginalDocument;
  9707       } else {
  9708         clonedDoc->mOriginalDocument = this;
  9710       int32_t sheetsCount = GetNumberOfStyleSheets();
  9711       for (int32_t i = 0; i < sheetsCount; ++i) {
  9712         nsRefPtr<nsCSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
  9713         if (sheet) {
  9714           if (sheet->IsApplicable()) {
  9715             nsRefPtr<nsCSSStyleSheet> clonedSheet =
  9716               sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
  9717             NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
  9718             if (clonedSheet) {
  9719               clonedDoc->AddStyleSheet(clonedSheet);
  9725       sheetsCount = GetNumberOfCatalogStyleSheets();
  9726       for (int32_t i = 0; i < sheetsCount; ++i) {
  9727         nsRefPtr<nsCSSStyleSheet> sheet =
  9728           do_QueryObject(GetCatalogStyleSheetAt(i));
  9729         if (sheet) {
  9730           if (sheet->IsApplicable()) {
  9731             nsRefPtr<nsCSSStyleSheet> clonedSheet =
  9732               sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
  9733             NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
  9734             if (clonedSheet) {
  9735               clonedDoc->AddCatalogStyleSheet(clonedSheet);
  9742   mCreatingStaticClone = false;
  9743   return clonedDoc.forget();
  9746 nsresult
  9747 nsIDocument::ScheduleFrameRequestCallback(const FrameRequestCallbackHolder& aCallback,
  9748                                           int32_t *aHandle)
  9750   if (mFrameRequestCallbackCounter == INT32_MAX) {
  9751     // Can't increment without overflowing; bail out
  9752     return NS_ERROR_NOT_AVAILABLE;
  9754   int32_t newHandle = ++mFrameRequestCallbackCounter;
  9756   bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty();
  9757   DebugOnly<FrameRequest*> request =
  9758     mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
  9759   NS_ASSERTION(request, "This is supposed to be infallible!");
  9760   if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
  9761     mPresShell->GetPresContext()->RefreshDriver()->
  9762       ScheduleFrameRequestCallbacks(this);
  9765   *aHandle = newHandle;
  9766   return NS_OK;
  9769 void
  9770 nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
  9772   // mFrameRequestCallbacks is stored sorted by handle
  9773   if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) &&
  9774       mFrameRequestCallbacks.IsEmpty() &&
  9775       mPresShell && IsEventHandlingEnabled()) {
  9776     mPresShell->GetPresContext()->RefreshDriver()->
  9777       RevokeFrameRequestCallbacks(this);
  9781 nsresult
  9782 nsDocument::GetStateObject(nsIVariant** aState)
  9784   // Get the document's current state object. This is the object backing both
  9785   // history.state and popStateEvent.state.
  9786   //
  9787   // mStateObjectContainer may be null; this just means that there's no
  9788   // current state object.
  9790   nsCOMPtr<nsIVariant> stateObj;
  9791   if (!mStateObjectCached && mStateObjectContainer) {
  9792     AutoJSContext cx;
  9793     nsIGlobalObject* sgo = GetScopeObject();
  9794     NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
  9795     JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
  9796     NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
  9797     JSAutoCompartment ac(cx, global);
  9799     mStateObjectContainer->
  9800       DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
  9803   NS_IF_ADDREF(*aState = mStateObjectCached);
  9805   return NS_OK;
  9808 nsDOMNavigationTiming*
  9809 nsDocument::GetNavigationTiming() const
  9811   return mTiming;
  9814 nsresult
  9815 nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
  9817   mTiming = aTiming;
  9818   if (!mLoadingTimeStamp.IsNull() && mTiming) {
  9819     mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp);
  9821   return NS_OK;
  9824 Element*
  9825 nsDocument::FindImageMap(const nsAString& aUseMapValue)
  9827   if (aUseMapValue.IsEmpty()) {
  9828     return nullptr;
  9831   nsAString::const_iterator start, end;
  9832   aUseMapValue.BeginReading(start);
  9833   aUseMapValue.EndReading(end);
  9835   int32_t hash = aUseMapValue.FindChar('#');
  9836   if (hash < 0) {
  9837     return nullptr;
  9839   // aUsemap contains a '#', set start to point right after the '#'
  9840   start.advance(hash + 1);
  9842   if (start == end) {
  9843     return nullptr; // aUsemap == "#"
  9846   const nsAString& mapName = Substring(start, end);
  9848   if (!mImageMaps) {
  9849     mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
  9852   uint32_t i, n = mImageMaps->Length(true);
  9853   nsString name;
  9854   for (i = 0; i < n; ++i) {
  9855     nsIContent* map = mImageMaps->Item(i);
  9856     if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
  9857                          eCaseMatters) ||
  9858         (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) &&
  9859          mapName.Equals(name, nsCaseInsensitiveStringComparator()))) {
  9860       return map->AsElement();
  9864   return nullptr;
  9867 #define DEPRECATED_OPERATION(_op) #_op "Warning",
  9868 static const char* kWarnings[] = {
  9869 #include "nsDeprecatedOperationList.h"
  9870   nullptr
  9871 };
  9872 #undef DEPRECATED_OPERATION
  9874 void
  9875 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
  9876                            bool asError /* = false */)
  9878   static_assert(eDeprecatedOperationCount <= 64,
  9879                 "Too many deprecated operations");
  9880   if (mWarnedAbout & (1ull << aOperation)) {
  9881     return;
  9883   mWarnedAbout |= (1ull << aOperation);
  9884   uint32_t flags = asError ? nsIScriptError::errorFlag
  9885                            : nsIScriptError::warningFlag;
  9886   nsContentUtils::ReportToConsole(flags,
  9887                                   NS_LITERAL_CSTRING("DOM Core"), this,
  9888                                   nsContentUtils::eDOM_PROPERTIES,
  9889                                   kWarnings[aOperation]);
  9892 nsresult
  9893 nsDocument::AddImage(imgIRequest* aImage)
  9895   NS_ENSURE_ARG_POINTER(aImage);
  9897   // See if the image is already in the hashtable. If it is, get the old count.
  9898   uint32_t oldCount = 0;
  9899   mImageTracker.Get(aImage, &oldCount);
  9901   // Put the image in the hashtable, with the proper count.
  9902   mImageTracker.Put(aImage, oldCount + 1);
  9904   nsresult rv = NS_OK;
  9906   // If this is the first insertion and we're locking images, lock this image
  9907   // too.
  9908   if (oldCount == 0) {
  9909     if (mLockingImages)
  9910       rv = aImage->LockImage();
  9911     if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit ||
  9912                              mImageTracker.Count() < sOnloadDecodeLimit))
  9913       rv = aImage->StartDecoding();
  9916   // If this is the first insertion and we're animating images, request
  9917   // that this image be animated too.
  9918   if (oldCount == 0 && mAnimatingImages) {
  9919     nsresult rv2 = aImage->IncrementAnimationConsumers();
  9920     rv = NS_SUCCEEDED(rv) ? rv2 : rv;
  9923   return rv;
  9926 nsresult
  9927 nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags)
  9929   NS_ENSURE_ARG_POINTER(aImage);
  9931   // Get the old count. It should exist and be > 0.
  9932   uint32_t count = 0;
  9933   DebugOnly<bool> found = mImageTracker.Get(aImage, &count);
  9934   NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!");
  9935   NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!");
  9937   // We're removing, so decrement the count.
  9938   count--;
  9940   // If the count is now zero, remove from the tracker.
  9941   // Otherwise, set the new value.
  9942   if (count != 0) {
  9943     mImageTracker.Put(aImage, count);
  9944     return NS_OK;
  9947   mImageTracker.Remove(aImage);
  9949   nsresult rv = NS_OK;
  9951   // Now that we're no longer tracking this image, unlock it if we'd
  9952   // previously locked it.
  9953   if (mLockingImages) {
  9954     rv = aImage->UnlockImage();
  9957   // If we're animating images, remove our request to animate this one.
  9958   if (mAnimatingImages) {
  9959     nsresult rv2 = aImage->DecrementAnimationConsumers();
  9960     rv = NS_SUCCEEDED(rv) ? rv2 : rv;
  9963   if (aFlags & REQUEST_DISCARD) {
  9964     // Request that the image be discarded if nobody else holds a lock on it.
  9965     // Do this even if !mLockingImages, because even if we didn't just unlock
  9966     // this image, it might still be a candidate for discarding.
  9967     aImage->RequestDiscard();
  9970   return rv;
  9973 nsresult
  9974 nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
  9976   MOZ_ASSERT(aPlugin);
  9977   if (!mPlugins.PutEntry(aPlugin)) {
  9978     return NS_ERROR_OUT_OF_MEMORY;
  9980   return NS_OK;
  9983 void
  9984 nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin)
  9986   MOZ_ASSERT(aPlugin);
  9987   mPlugins.RemoveEntry(aPlugin);
  9990 static bool
  9991 AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
  9993   nsTArray<nsIObjectLoadingContent*>* plugins =
  9994     reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
  9995   MOZ_ASSERT(plugins);
  9996   aDocument->GetPlugins(*plugins);
  9997   return true;
 10000 static PLDHashOperator
 10001 AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg)
 10003   nsTArray<nsIObjectLoadingContent*>* allPlugins =
 10004     reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
 10005   MOZ_ASSERT(allPlugins);
 10006   allPlugins->AppendElement(aPlugin->GetKey());
 10007   return PL_DHASH_NEXT;
 10010 void
 10011 nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
 10013   aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
 10014   mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins);
 10015   EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
 10018 PLDHashOperator LockEnumerator(imgIRequest* aKey,
 10019                                uint32_t aData,
 10020                                void*    userArg)
 10022   aKey->LockImage();
 10023   aKey->RequestDecode();
 10024   return PL_DHASH_NEXT;
 10027 PLDHashOperator UnlockEnumerator(imgIRequest* aKey,
 10028                                  uint32_t aData,
 10029                                  void*    userArg)
 10031   aKey->UnlockImage();
 10032   return PL_DHASH_NEXT;
 10036 nsresult
 10037 nsDocument::SetImageLockingState(bool aLocked)
 10039   if (XRE_GetProcessType() == GeckoProcessType_Content &&
 10040       !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) {
 10041     return NS_OK;
 10044   // If there's no change, there's nothing to do.
 10045   if (mLockingImages == aLocked)
 10046     return NS_OK;
 10048   // Otherwise, iterate over our images and perform the appropriate action.
 10049   mImageTracker.EnumerateRead(aLocked ? LockEnumerator
 10050                                       : UnlockEnumerator,
 10051                               nullptr);
 10053   // Update state.
 10054   mLockingImages = aLocked;
 10056   return NS_OK;
 10059 PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
 10060                                              uint32_t aData,
 10061                                              void*    userArg)
 10063   aKey->IncrementAnimationConsumers();
 10064   return PL_DHASH_NEXT;
 10067 PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
 10068                                              uint32_t aData,
 10069                                              void*    userArg)
 10071   aKey->DecrementAnimationConsumers();
 10072   return PL_DHASH_NEXT;
 10075 void
 10076 nsDocument::SetImagesNeedAnimating(bool aAnimating)
 10078   // If there's no change, there's nothing to do.
 10079   if (mAnimatingImages == aAnimating)
 10080     return;
 10082   // Otherwise, iterate over our images and perform the appropriate action.
 10083   mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
 10084                                          : DecrementAnimationEnumerator,
 10085                               nullptr);
 10087   // Update state.
 10088   mAnimatingImages = aAnimating;
 10091 already_AddRefed<Touch>
 10092 nsIDocument::CreateTouch(nsIDOMWindow* aView,
 10093                          EventTarget* aTarget,
 10094                          int32_t aIdentifier,
 10095                          int32_t aPageX, int32_t aPageY,
 10096                          int32_t aScreenX, int32_t aScreenY,
 10097                          int32_t aClientX, int32_t aClientY,
 10098                          int32_t aRadiusX, int32_t aRadiusY,
 10099                          float aRotationAngle,
 10100                          float aForce)
 10102   nsRefPtr<Touch> touch = new Touch(aTarget,
 10103                                     aIdentifier,
 10104                                     aPageX, aPageY,
 10105                                     aScreenX, aScreenY,
 10106                                     aClientX, aClientY,
 10107                                     aRadiusX, aRadiusY,
 10108                                     aRotationAngle,
 10109                                     aForce);
 10110   return touch.forget();
 10113 already_AddRefed<TouchList>
 10114 nsIDocument::CreateTouchList()
 10116   nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
 10117   return retval.forget();
 10120 already_AddRefed<TouchList>
 10121 nsIDocument::CreateTouchList(Touch& aTouch,
 10122                              const Sequence<OwningNonNull<Touch> >& aTouches)
 10124   nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
 10125   retval->Append(&aTouch);
 10126   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
 10127     retval->Append(aTouches[i].get());
 10129   return retval.forget();
 10132 already_AddRefed<TouchList>
 10133 nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
 10135   nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
 10136   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
 10137     retval->Append(aTouches[i].get());
 10139   return retval.forget();
 10142 already_AddRefed<nsDOMCaretPosition>
 10143 nsIDocument::CaretPositionFromPoint(float aX, float aY)
 10145   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
 10146   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
 10147   nsPoint pt(x, y);
 10149   FlushPendingNotifications(Flush_Layout);
 10151   nsIPresShell *ps = GetShell();
 10152   if (!ps) {
 10153     return nullptr;
 10156   nsIFrame *rootFrame = ps->GetRootFrame();
 10158   // XUL docs, unlike HTML, have no frame tree until everything's done loading
 10159   if (!rootFrame) {
 10160     return nullptr;
 10163   nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
 10164       nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
 10165   if (!ptFrame) {
 10166     return nullptr;
 10169   // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
 10170   // to adjust to frame-relative coordinates before we can perform this call.
 10171   // It should also not take into account the padding of the frame.
 10172   nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
 10174   nsFrame::ContentOffsets offsets =
 10175     ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
 10177   nsCOMPtr<nsIContent> node = offsets.content;
 10178   uint32_t offset = offsets.offset;
 10179   nsCOMPtr<nsIContent> anonNode = node;
 10180   bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
 10181   if (nodeIsAnonymous) {
 10182     node = ptFrame->GetContent();
 10183     nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
 10184     nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
 10185     nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
 10186     bool isText;
 10187     if (textArea || (input &&
 10188                      NS_SUCCEEDED(input->MozIsTextField(false, &isText)) &&
 10189                      isText)) {
 10190       // If the anonymous content node has a child, then we need to make sure
 10191       // that we get the appropriate child, as otherwise the offset may not be
 10192       // correct when we construct a range for it.
 10193       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
 10194       if (firstChild) {
 10195         anonNode = firstChild;
 10198       if (textArea) {
 10199         offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
 10202       node = nonanon;
 10203     } else {
 10204       node = nullptr;
 10205       offset = 0;
 10209   nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
 10210   if (nodeIsAnonymous) {
 10211     aCaretPos->SetAnonymousContentNode(anonNode);
 10213   return aCaretPos.forget();
 10216 NS_IMETHODIMP
 10217 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
 10219   NS_ENSURE_ARG_POINTER(aCaretPos);
 10220   *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take();
 10221   return NS_OK;
 10224 void
 10225 nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
 10227   nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
 10228   if (NS_FAILED(res)) {
 10229     rv.Throw(res);
 10233 void
 10234 nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
 10236   nsCOMPtr<nsIURI> uri;
 10237   nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
 10238   if (NS_FAILED(res)) {
 10239     rv.Throw(res);
 10240     return;
 10242   res = CSSLoader()->ObsoleteSheet(uri);
 10243   if (NS_FAILED(res)) {
 10244     rv.Throw(res);
 10248 nsIHTMLCollection*
 10249 nsIDocument::Children()
 10251   if (!mChildrenCollection) {
 10252     mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
 10253                                             nsGkAtoms::_asterix,
 10254                                             nsGkAtoms::_asterix,
 10255                                             false);
 10258   return mChildrenCollection;
 10261 uint32_t
 10262 nsIDocument::ChildElementCount()
 10264   return Children()->Length();
 10267 namespace mozilla {
 10269 // Singleton class to manage the list of fullscreen documents which are the
 10270 // root of a branch which contains fullscreen documents. We maintain this list
 10271 // so that we can easily exit all windows from fullscreen when the user
 10272 // presses the escape key.
 10273 class FullscreenRoots {
 10274 public:
 10275   // Adds a root to the manager. Adding a root multiple times does not result
 10276   // in duplicate entries for that item, only one.
 10277   static void Add(nsIDocument* aRoot);
 10279   // Iterates over every root in the root list, and calls aFunction, passing
 10280   // each root once to aFunction. It is safe to call Add() and Remove() while
 10281   // iterating over the list (i.e. in aFunction). Documents that are removed
 10282   // from the manager during traversal are not traversed, and documents that
 10283   // are added to the manager during traversal are also not traversed.
 10284   static void ForEach(void(*aFunction)(nsIDocument* aDoc));
 10286   // Removes a specific root from the manager.
 10287   static void Remove(nsIDocument* aRoot);
 10289   // Returns true if all roots added to the list have been removed.
 10290   static bool IsEmpty();
 10292 private:
 10294   FullscreenRoots() {
 10295     MOZ_COUNT_CTOR(FullscreenRoots);
 10297   ~FullscreenRoots() {
 10298     MOZ_COUNT_DTOR(FullscreenRoots);
 10301   enum {
 10302     NotFound = uint32_t(-1)
 10303   };
 10304   // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
 10305   static uint32_t Find(nsIDocument* aRoot);
 10307   // Returns true if aRoot is in the list of fullscreen roots.
 10308   static bool Contains(nsIDocument* aRoot);
 10310   // Singleton instance of the FullscreenRoots. This is instantiated when a
 10311   // root is added, and it is deleted when the last root is removed.
 10312   static FullscreenRoots* sInstance;
 10314   // List of weak pointers to roots.
 10315   nsTArray<nsWeakPtr> mRoots;
 10316 };
 10318 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
 10320 /* static */
 10321 void
 10322 FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
 10324   if (!sInstance) {
 10325     return;
 10327   // Create a copy of the roots array, and iterate over the copy. This is so
 10328   // that if an element is removed from mRoots we don't mess up our iteration.
 10329   nsTArray<nsWeakPtr> roots(sInstance->mRoots);
 10330   // Call aFunction on all entries.
 10331   for (uint32_t i = 0; i < roots.Length(); i++) {
 10332     nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
 10333     // Check that the root isn't in the manager. This is so that new additions
 10334     // while we were running don't get traversed.
 10335     if (root && FullscreenRoots::Contains(root)) {
 10336       aFunction(root);
 10341 /* static */
 10342 bool
 10343 FullscreenRoots::Contains(nsIDocument* aRoot)
 10345   return FullscreenRoots::Find(aRoot) != NotFound;
 10348 /* static */
 10349 void
 10350 FullscreenRoots::Add(nsIDocument* aRoot)
 10352   if (!FullscreenRoots::Contains(aRoot)) {
 10353     if (!sInstance) {
 10354       sInstance = new FullscreenRoots();
 10356     nsWeakPtr weakRoot = do_GetWeakReference(aRoot);
 10357     sInstance->mRoots.AppendElement(weakRoot);
 10361 /* static */
 10362 uint32_t
 10363 FullscreenRoots::Find(nsIDocument* aRoot)
 10365   if (!sInstance) {
 10366     return NotFound;
 10368   nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
 10369   for (uint32_t i = 0; i < roots.Length(); i++) {
 10370     nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
 10371     if (otherRoot == aRoot) {
 10372       return i;
 10375   return NotFound;
 10378 /* static */
 10379 void
 10380 FullscreenRoots::Remove(nsIDocument* aRoot)
 10382   uint32_t index = Find(aRoot);
 10383   NS_ASSERTION(index != NotFound,
 10384     "Should only try to remove roots which are still added!");
 10385   if (index == NotFound || !sInstance) {
 10386     return;
 10388   sInstance->mRoots.RemoveElementAt(index);
 10389   if (sInstance->mRoots.IsEmpty()) {
 10390     delete sInstance;
 10391     sInstance = nullptr;
 10395 /* static */
 10396 bool
 10397 FullscreenRoots::IsEmpty()
 10399   return !sInstance;
 10402 } // end namespace mozilla.
 10403 using mozilla::FullscreenRoots;
 10405 nsIDocument*
 10406 nsDocument::GetFullscreenRoot()
 10408   nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
 10409   return root;
 10412 void
 10413 nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
 10415   mFullscreenRoot = do_GetWeakReference(aRoot);
 10418 NS_IMETHODIMP
 10419 nsDocument::MozCancelFullScreen()
 10421   nsIDocument::MozCancelFullScreen();
 10422   return NS_OK;
 10425 void
 10426 nsIDocument::MozCancelFullScreen()
 10428   RestorePreviousFullScreenState();
 10431 // Runnable to set window full-screen mode. Used as a script runner
 10432 // to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
 10433 // run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
 10434 // (handled in chome code) which is unsafe to run if this is called in
 10435 // Element::UnbindFromTree().
 10436 class nsSetWindowFullScreen : public nsRunnable {
 10437 public:
 10438   nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
 10439     : mDoc(aDoc), mValue(aValue) {}
 10441   NS_IMETHOD Run()
 10443     if (mDoc->GetWindow()) {
 10444       mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
 10446     return NS_OK;
 10449 private:
 10450   nsCOMPtr<nsIDocument> mDoc;
 10451   bool mValue;
 10452 };
 10454 static nsIDocument*
 10455 GetFullscreenRootDocument(nsIDocument* aDoc)
 10457   if (!aDoc) {
 10458     return nullptr;
 10460   nsIDocument* doc = aDoc;
 10461   nsIDocument* parent;
 10462   while ((parent = doc->GetParentDocument()) &&
 10463          (!nsContentUtils::IsFullscreenApiContentOnly() ||
 10464           !nsContentUtils::IsChromeDoc(parent))) {
 10465     doc = parent;
 10467   return doc;
 10470 static void
 10471 SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
 10473   // Maintain list of fullscreen root documents.
 10474   nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc);
 10475   if (aValue) {
 10476     FullscreenRoots::Add(root);
 10477   } else {
 10478     FullscreenRoots::Remove(root);
 10480   if (!nsContentUtils::IsFullscreenApiContentOnly()) {
 10481     nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
 10485 class nsCallExitFullscreen : public nsRunnable {
 10486 public:
 10487   nsCallExitFullscreen(nsIDocument* aDoc)
 10488     : mDoc(aDoc) {}
 10489   NS_IMETHOD Run()
 10491     nsDocument::ExitFullscreen(mDoc);
 10492     return NS_OK;
 10494 private:
 10495   nsCOMPtr<nsIDocument> mDoc;
 10496 };
 10498 /* static */
 10499 void
 10500 nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
 10502   if (aDoc && !aDoc->IsFullScreenDoc()) {
 10503     return;
 10505   if (aRunAsync) {
 10506     NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
 10507     return;
 10509   nsDocument::ExitFullscreen(aDoc);
 10512 // Returns true if the document is a direct child of a cross process parent
 10513 // mozbrowser iframe. This is the case when the document has a null parent,
 10514 // and its DocShell reports that it is a browser frame.
 10515 static bool
 10516 HasCrossProcessParent(nsIDocument* aDocument)
 10518   if (XRE_GetProcessType() != GeckoProcessType_Content) {
 10519     return false;
 10521   if (aDocument->GetParentDocument() != nullptr) {
 10522     return false;
 10524   nsPIDOMWindow* win = aDocument->GetWindow();
 10525   if (!win) {
 10526     return false;
 10528   nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
 10529   if (!docShell) {
 10530     return false;
 10532   return docShell->GetIsBrowserOrApp();
 10535 static bool
 10536 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
 10538   if (aDoc->IsFullScreenDoc()) {
 10539     uint32_t* count = static_cast<uint32_t*>(aData);
 10540     (*count)++;
 10542   return true;
 10545 static uint32_t
 10546 CountFullscreenSubDocuments(nsIDocument* aDoc)
 10548   uint32_t count = 0;
 10549   aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
 10550   return count;
 10553 bool
 10554 nsDocument::IsFullscreenLeaf()
 10556   // A fullscreen leaf document is fullscreen, and has no fullscreen
 10557   // subdocuments.
 10558   if (!IsFullScreenDoc()) {
 10559     return false;
 10561   return CountFullscreenSubDocuments(this) == 0;
 10564 static bool
 10565 ResetFullScreen(nsIDocument* aDocument, void* aData)
 10567   if (aDocument->IsFullScreenDoc()) {
 10568     NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
 10569         "Should have at most 1 fullscreen subdocument.");
 10570     static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
 10571     NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
 10572     nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
 10573     changed->AppendElement(aDocument);
 10575     if (HasCrossProcessParent(aDocument)) {
 10576       // We're at the top of the content-process side doc tree. Ask the parent
 10577       // process to exit fullscreen.
 10578       nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10579       os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr);
 10582     // Dispatch a notification so that if this document has any
 10583     // cross-process subdocuments, they'll be notified to exit fullscreen.
 10584     // The BrowserElementParent listens for this event and performs the
 10585     // cross process notification if it has a remote child process.
 10586     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10587     os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
 10589     aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
 10591   return true;
 10594 static void
 10595 ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
 10597   MOZ_ASSERT(aMaybeNotARootDoc);
 10598   nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
 10599   NS_ASSERTION(root, "Should have root when in fullscreen!");
 10600   if (!root) {
 10601     return;
 10603   NS_ASSERTION(root->IsFullScreenDoc(),
 10604     "Fullscreen root should be a fullscreen doc...");
 10606   // Stores a list of documents to which we must dispatch "mozfullscreenchange".
 10607   // We're required by the spec to dispatch the events in leaf-to-root
 10608   // order when exiting fullscreen, but we traverse the doctree in a
 10609   // root-to-leaf order, so we save references to the documents we must
 10610   // dispatch to so that we dispatch in the specified order.
 10611   nsAutoTArray<nsIDocument*, 8> changed;
 10613   // Walk the tree of fullscreen documents, and reset their fullscreen state.
 10614   ResetFullScreen(root, static_cast<void*>(&changed));
 10616   // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
 10617   // order so that the events for the leaf document arrives before the root
 10618   // document, as required by the spec.
 10619   for (uint32_t i = 0; i < changed.Length(); ++i) {
 10620     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
 10623   NS_ASSERTION(!root->IsFullScreenDoc(),
 10624     "Fullscreen root should no longer be a fullscreen doc...");
 10626   // Move the top-level window out of fullscreen mode.
 10627   SetWindowFullScreen(root, false);
 10630 /* static */
 10631 void
 10632 nsDocument::ExitFullscreen(nsIDocument* aDoc)
 10634   // Unlock the pointer, if it's locked.
 10635   nsCOMPtr<Element> pointerLockedElement =
 10636     do_QueryReferent(EventStateManager::sPointerLockedElement);
 10637   if (pointerLockedElement) {
 10638     UnlockPointer();
 10641   if (aDoc)  {
 10642     ExitFullscreenInDocTree(aDoc);
 10643     return;
 10646   // Clear fullscreen stacks in all fullscreen roots' descendant documents.
 10647   FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
 10648   NS_ASSERTION(FullscreenRoots::IsEmpty(),
 10649       "Should have exited all fullscreen roots from fullscreen");
 10652 bool
 10653 GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
 10655   if (aDoc->IsFullscreenLeaf()) {
 10656     nsIDocument** result = static_cast<nsIDocument**>(aData);
 10657     *result = aDoc;
 10658     return false;
 10659   } else if (aDoc->IsFullScreenDoc()) {
 10660     aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
 10662   return true;
 10665 static nsIDocument*
 10666 GetFullscreenLeaf(nsIDocument* aDoc)
 10668   nsIDocument* leaf = nullptr;
 10669   GetFullscreenLeaf(aDoc, &leaf);
 10670   if (leaf) {
 10671     return leaf;
 10673   // Otherwise we could be either in a non-fullscreen doc tree, or we're
 10674   // below the fullscreen doc. Start the search from the root.
 10675   nsIDocument* root = GetFullscreenRootDocument(aDoc);
 10676   // Check that the root is actually fullscreen so we don't waste time walking
 10677   // around its descendants.
 10678   if (!root->IsFullScreenDoc()) {
 10679     return nullptr;
 10681   GetFullscreenLeaf(root, &leaf);
 10682   return leaf;
 10685 void
 10686 nsDocument::RestorePreviousFullScreenState()
 10688   NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(),
 10689     "Should have at least 1 fullscreen root when fullscreen!");
 10690   NS_ASSERTION(!nsContentUtils::IsFullscreenApiContentOnly() ||
 10691                !nsContentUtils::IsChromeDoc(this),
 10692                "Should not run RestorePreviousFullScreenState() on "
 10693                "chrome document when fullscreen is content only");
 10695   if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) {
 10696     return;
 10699   // If fullscreen mode is updated the pointer should be unlocked
 10700   nsCOMPtr<Element> pointerLockedElement =
 10701     do_QueryReferent(EventStateManager::sPointerLockedElement);
 10702   if (pointerLockedElement) {
 10703     UnlockPointer();
 10706   nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
 10708   // The fullscreen document may contain a <iframe mozbrowser> element which
 10709   // has a cross process child. So send a notification so that its browser
 10710   // parent will send a message to its child process to also exit fullscreen.
 10711   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10712   os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr);
 10714   // Clear full-screen stacks in all descendant in process documents, bottom up.
 10715   nsIDocument* doc = fullScreenDoc;
 10716   while (doc != this) {
 10717     NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc");
 10718     static_cast<nsDocument*>(doc)->CleanupFullscreenState();
 10719     UnlockPointer();
 10720     DispatchFullScreenChange(doc);
 10721     doc = doc->GetParentDocument();
 10724   // Roll-back full-screen state to previous full-screen element.
 10725   NS_ASSERTION(doc == this, "Must have reached this doc.");
 10726   while (doc != nullptr) {
 10727     static_cast<nsDocument*>(doc)->FullScreenStackPop();
 10728     UnlockPointer();
 10729     DispatchFullScreenChange(doc);
 10730     if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) {
 10731       if (HasCrossProcessParent(doc)) {
 10732         // Send notification to the parent process to tell it to rollback to
 10733         // the previous fullscreen elements in its fullscreen element stacks.
 10734         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10735         os->NotifyObservers(doc, "ask-parent-to-rollback-fullscreen", nullptr);
 10737       // Full-screen stack in document is empty. Go back up to the parent
 10738       // document. We'll pop the containing element off its stack, and use
 10739       // its next full-screen element as the full-screen element.
 10740       static_cast<nsDocument*>(doc)->CleanupFullscreenState();
 10741       doc = doc->GetParentDocument();
 10742     } else {
 10743       // Else we popped the top of the stack, and there's still another
 10744       // element in there, so that will become the full-screen element.
 10745       if (fullScreenDoc != doc) {
 10746         // We've popped so enough off the stack that we've rolled back to
 10747         // a fullscreen element in a parent document. If this document isn't
 10748         // approved for fullscreen, or if it's cross origin, dispatch an
 10749         // event to chrome so it knows to show the authorization/warning UI.
 10750         if (!nsContentUtils::HaveEqualPrincipals(fullScreenDoc, doc) ||
 10751             (!nsContentUtils::IsSitePermAllow(doc->NodePrincipal(), "fullscreen") &&
 10752              !static_cast<nsDocument*>(doc)->mIsApprovedForFullscreen)) {
 10753           nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
 10754             new AsyncEventDispatcher(doc,
 10755                   NS_LITERAL_STRING("MozEnteredDomFullscreen"),
 10756                   true,
 10757                   true);
 10758           asyncDispatcher->PostDOMEvent();
 10762       if (!nsContentUtils::HaveEqualPrincipals(doc, fullScreenDoc)) {
 10763         // The origin which is fullscreen changed. Send a notification to
 10764         // the root process so that a warning or approval UI can be shown
 10765         // as necessary.
 10766         nsAutoString origin;
 10767         nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin);
 10768         nsIDocument* root = GetFullscreenRootDocument(doc);
 10769         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10770         os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
 10773       break;
 10777   if (doc == nullptr) {
 10778     // We moved all documents in this doctree out of fullscreen mode,
 10779     // move the top-level window out of fullscreen mode.
 10780     NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(),
 10781       "Should have cleared all docs' stacks");
 10782     SetWindowFullScreen(this, false);
 10786 bool
 10787 nsDocument::IsFullScreenDoc()
 10789   return GetFullScreenElement() != nullptr;
 10792 class nsCallRequestFullScreen : public nsRunnable
 10794 public:
 10795   nsCallRequestFullScreen(Element* aElement)
 10796     : mElement(aElement),
 10797       mDoc(aElement->OwnerDoc()),
 10798       mWasCallerChrome(nsContentUtils::IsCallerChrome()),
 10799       mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
 10800                            mAsyncFullscreenPending)
 10802     static_cast<nsDocument*>(mDoc.get())->
 10803       mAsyncFullscreenPending = true;
 10806   NS_IMETHOD Run()
 10808     static_cast<nsDocument*>(mDoc.get())->
 10809       mAsyncFullscreenPending = mHadRequestPending;
 10810     nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
 10811     doc->RequestFullScreen(mElement,
 10812                            mWasCallerChrome,
 10813                            /* aNotifyOnOriginChange */ true);
 10814     return NS_OK;
 10817   nsRefPtr<Element> mElement;
 10818   nsCOMPtr<nsIDocument> mDoc;
 10819   bool mWasCallerChrome;
 10820   bool mHadRequestPending;
 10821 };
 10823 void
 10824 nsDocument::AsyncRequestFullScreen(Element* aElement)
 10826   NS_ASSERTION(aElement,
 10827     "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
 10828   if (!aElement) {
 10829     return;
 10831   // Request full-screen asynchronously.
 10832   nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement));
 10833   NS_DispatchToCurrentThread(event);
 10836 static void
 10837 LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc)
 10839   if (!aLogFailure) {
 10840     return;
 10842   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
 10843     new AsyncEventDispatcher(aDoc,
 10844                              NS_LITERAL_STRING("mozfullscreenerror"),
 10845                              true,
 10846                              false);
 10847   asyncDispatcher->PostDOMEvent();
 10848   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
 10849                                   NS_LITERAL_CSTRING("DOM"), aDoc,
 10850                                   nsContentUtils::eDOM_PROPERTIES,
 10851                                   aMessage);
 10854 nsresult
 10855 nsDocument::AddFullscreenApprovedObserver()
 10857   if (mHasFullscreenApprovedObserver ||
 10858       !Preferences::GetBool("full-screen-api.approval-required")) {
 10859     return NS_OK;
 10862   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10863   NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
 10865   nsresult res = os->AddObserver(this, "fullscreen-approved", true);
 10866   NS_ENSURE_SUCCESS(res, res);
 10868   mHasFullscreenApprovedObserver = true;
 10870   return NS_OK;
 10873 nsresult
 10874 nsDocument::RemoveFullscreenApprovedObserver()
 10876   if (!mHasFullscreenApprovedObserver) {
 10877     return NS_OK;
 10879   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 10880   NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
 10882   nsresult res = os->RemoveObserver(this, "fullscreen-approved");
 10883   NS_ENSURE_SUCCESS(res, res);
 10885   mHasFullscreenApprovedObserver = false;
 10887   return NS_OK;
 10890 void
 10891 nsDocument::CleanupFullscreenState()
 10893   if (!mFullScreenStack.IsEmpty()) {
 10894     // The top element in the full-screen stack will have full-screen
 10895     // style bits set on it and its ancestors. Remove the style bits.
 10896     // Note the non-top elements won't have the style bits set.
 10897     Element* top = FullScreenStackTop();
 10898     NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
 10899     if (top) {
 10900       EventStateManager::SetFullScreenState(top, false);
 10902     mFullScreenStack.Clear();
 10904   SetApprovedForFullscreen(false);
 10905   RemoveFullscreenApprovedObserver();
 10906   mFullscreenRoot = nullptr;
 10909 bool
 10910 nsDocument::FullScreenStackPush(Element* aElement)
 10912   NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
 10913   Element* top = FullScreenStackTop();
 10914   if (top == aElement || !aElement) {
 10915     return false;
 10917   if (top) {
 10918     // We're pushing a new element onto the full-screen stack, so we must
 10919     // remove the ancestor and full-screen styles from the former top of the
 10920     // stack.
 10921     EventStateManager::SetFullScreenState(top, false);
 10923   EventStateManager::SetFullScreenState(aElement, true);
 10924   nsWeakPtr weakElement = do_GetWeakReference(aElement);
 10925   mFullScreenStack.AppendElement(weakElement);
 10926   NS_ASSERTION(GetFullScreenElement() == aElement, "Should match");
 10927   return true;
 10930 void
 10931 nsDocument::FullScreenStackPop()
 10933   if (mFullScreenStack.IsEmpty()) {
 10934     return;
 10937   // Remove styles from existing top element.
 10938   Element* top = FullScreenStackTop();
 10939   EventStateManager::SetFullScreenState(top, false);
 10941   // Remove top element. Note the remaining top element in the stack
 10942   // will not have full-screen style bits set, so we will need to restore
 10943   // them on the new top element before returning.
 10944   uint32_t last = mFullScreenStack.Length() - 1;
 10945   mFullScreenStack.RemoveElementAt(last);
 10947   // Pop from the stack null elements (references to elements which have
 10948   // been GC'd since they were added to the stack) and elements which are
 10949   // no longer in this document.
 10950   while (!mFullScreenStack.IsEmpty()) {
 10951     Element* element = FullScreenStackTop();
 10952     if (!element || !element->IsInDoc() || element->OwnerDoc() != this) {
 10953       NS_ASSERTION(!element->IsFullScreenAncestor(),
 10954                    "Should have already removed full-screen styles");
 10955       uint32_t last = mFullScreenStack.Length() - 1;
 10956       mFullScreenStack.RemoveElementAt(last);
 10957     } else {
 10958       // The top element of the stack is now an in-doc element. Apply the
 10959       // full-screen styles and return.
 10960       EventStateManager::SetFullScreenState(element, true);
 10961       break;
 10966 Element*
 10967 nsDocument::FullScreenStackTop()
 10969   if (mFullScreenStack.IsEmpty()) {
 10970     return nullptr;
 10972   uint32_t last = mFullScreenStack.Length() - 1;
 10973   nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
 10974   NS_ASSERTION(element, "Should have full-screen element!");
 10975   NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc");
 10976   NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
 10977   return element;
 10980 // Returns true if aDoc is in the focused tab in the active window.
 10981 static bool
 10982 IsInActiveTab(nsIDocument* aDoc)
 10984   nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
 10985   if (!docshell) {
 10986     return false;
 10989   bool isActive = false;
 10990   docshell->GetIsActive(&isActive);
 10991   if (!isActive) {
 10992     return false;
 10995   nsCOMPtr<nsIDocShellTreeItem> rootItem;
 10996   docshell->GetRootTreeItem(getter_AddRefs(rootItem));
 10997   if (!rootItem) {
 10998     return false;
 11000   nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem);
 11001   if (!rootWin) {
 11002     return false;
 11005   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
 11006   if (!fm) {
 11007     return false;
 11010   nsCOMPtr<nsIDOMWindow> activeWindow;
 11011   fm->GetActiveWindow(getter_AddRefs(activeWindow));
 11012   if (!activeWindow) {
 11013     return false;
 11016   return activeWindow == rootWin;
 11019 nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
 11020                                                   const nsAString& aOrigin)
 11022   // Ensure the frame element is the fullscreen element in this document.
 11023   // If the frame element is already the fullscreen element in this document,
 11024   // this has no effect.
 11025   nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
 11026   RequestFullScreen(content->AsElement(),
 11027                     /* aWasCallerChrome */ false,
 11028                     /* aNotifyOnOriginChange */ false);
 11030   // Origin changed in child process, send notifiction, so that chrome can
 11031   // update the UI to reflect the fullscreen origin change if necessary.
 11032   // The BrowserElementChild listens on this, and forwards it over its
 11033   // parent process, where it is redispatched. Chrome (in the root process,
 11034   // which could be *this* process) listens for this notification so that
 11035   // it can show a warning or approval UI.
 11036   if (!aOrigin.IsEmpty()) {
 11037     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 11038     os->NotifyObservers(GetFullscreenRootDocument(this),
 11039                         "fullscreen-origin-change",
 11040                         PromiseFlatString(aOrigin).get());
 11043   return NS_OK;
 11046 nsresult nsDocument::RemoteFrameFullscreenReverted()
 11048   RestorePreviousFullScreenState();
 11049   return NS_OK;
 11052 void
 11053 nsDocument::RequestFullScreen(Element* aElement,
 11054                               bool aWasCallerChrome,
 11055                               bool aNotifyOnOriginChange)
 11057   NS_ASSERTION(aElement,
 11058     "Must pass non-null element to nsDocument::RequestFullScreen");
 11059   if (!aElement || aElement == GetFullScreenElement()) {
 11060     return;
 11062   if (!aElement->IsInDoc()) {
 11063     LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this);
 11064     return;
 11066   if (aElement->OwnerDoc() != this) {
 11067     LogFullScreenDenied(true, "FullScreenDeniedMovedDocument", this);
 11068     return;
 11070   if (!GetWindow()) {
 11071     LogFullScreenDenied(true, "FullScreenDeniedLostWindow", this);
 11072     return;
 11074   if (nsContentUtils::IsFullscreenApiContentOnly() &&
 11075       nsContentUtils::IsChromeDoc(this)) {
 11076     // Block fullscreen requests in the chrome document when the fullscreen API
 11077     // is configured for content only.
 11078     LogFullScreenDenied(true, "FullScreenDeniedContentOnly", this);
 11079     return;
 11081   if (!IsFullScreenEnabled(aWasCallerChrome, true)) {
 11082     // IsFullScreenEnabled calls LogFullScreenDenied, no need to log.
 11083     return;
 11085   if (GetFullScreenElement() &&
 11086       !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) {
 11087     // If this document is full-screen, only grant full-screen requests from
 11088     // a descendant of the current full-screen element.
 11089     LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this);
 11090     return;
 11092   if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
 11093     LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this);
 11094     return;
 11096   // Deny requests when a windowed plugin is focused.
 11097   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
 11098   if (!fm) {
 11099     NS_WARNING("Failed to retrieve focus manager in full-screen request.");
 11100     return;
 11102   nsCOMPtr<nsIDOMElement> focusedElement;
 11103   fm->GetFocusedElement(getter_AddRefs(focusedElement));
 11104   if (focusedElement) {
 11105     nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
 11106     if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
 11107       LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this);
 11108       return;
 11112   // Stash a reference to any existing fullscreen doc, we'll use this later
 11113   // to detect if the origin which is fullscreen has changed.
 11114   nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
 11116   AddFullscreenApprovedObserver();
 11118   // Stores a list of documents which we must dispatch "mozfullscreenchange"
 11119   // too. We're required by the spec to dispatch the events in root-to-leaf
 11120   // order, but we traverse the doctree in a leaf-to-root order, so we save
 11121   // references to the documents we must dispatch to so that we get the order
 11122   // as specified.
 11123   nsAutoTArray<nsIDocument*, 8> changed;
 11125   // Remember the root document, so that if a full-screen document is hidden
 11126   // we can reset full-screen state in the remaining visible full-screen documents.
 11127   nsIDocument* fullScreenRootDoc = GetFullscreenRootDocument(this);
 11128   if (fullScreenRootDoc->IsFullScreenDoc()) {
 11129     // A document is already in fullscreen, unlock the mouse pointer
 11130     // before setting a new document to fullscreen
 11131     UnlockPointer();
 11134   // If a document is already in fullscreen, then unlock the mouse pointer
 11135   // before setting a new document to fullscreen
 11136   nsCOMPtr<Element> pointerLockedElement =
 11137     do_QueryReferent(EventStateManager::sPointerLockedElement);
 11138   if (pointerLockedElement) {
 11139     UnlockPointer();
 11142   // Set the full-screen element. This sets the full-screen style on the
 11143   // element, and the full-screen-ancestor styles on ancestors of the element
 11144   // in this document.
 11145   DebugOnly<bool> x = FullScreenStackPush(aElement);
 11146   NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
 11147   changed.AppendElement(this);
 11149   // Propagate up the document hierarchy, setting the full-screen element as
 11150   // the element's container in ancestor documents. This also sets the
 11151   // appropriate css styles as well. Note we don't propagate down the
 11152   // document hierarchy, the full-screen element (or its container) is not
 11153   // visible there. Stop when we reach the root document.
 11154   nsIDocument* child = this;
 11155   while (true) {
 11156     child->SetFullscreenRoot(fullScreenRootDoc);
 11157     NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
 11158         "Fullscreen root should be set!");
 11159     if (child == fullScreenRootDoc) {
 11160       break;
 11162     nsIDocument* parent = child->GetParentDocument();
 11163     Element* element = parent->FindContentForSubDocument(child)->AsElement();
 11164     if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
 11165       changed.AppendElement(parent);
 11166       child = parent;
 11167     } else {
 11168       // We've reached either the root, or a point in the doctree where the
 11169       // new full-screen element container is the same as the previous
 11170       // full-screen element's container. No more changes need to be made
 11171       // to the full-screen stacks of documents further up the tree.
 11172       break;
 11176   // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
 11177   // order so that the events for the root document arrives before the leaf
 11178   // document, as required by the spec.
 11179   for (uint32_t i = 0; i < changed.Length(); ++i) {
 11180     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
 11183   // If this document hasn't already been approved in this session,
 11184   // check to see if the user has granted the fullscreen access
 11185   // to the document's principal's host, if it has one. Note that documents
 11186   // in web apps which are the same origin as the web app are considered
 11187   // trusted and so are automatically approved.
 11188   if (!mIsApprovedForFullscreen) {
 11189     mIsApprovedForFullscreen =
 11190       !Preferences::GetBool("full-screen-api.approval-required") ||
 11191       NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
 11192       nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen");
 11195   // If this document, or a document with the same principal has not
 11196   // already been approved for fullscreen this fullscreen-session, dispatch
 11197   // an event so that chrome knows to pop up a warning/approval UI.
 11198   // Note previousFullscreenDoc=nullptr upon first entry, so we always
 11199   // take this path on the first time we enter fullscreen in a fullscreen
 11200   // session.
 11201   if (!mIsApprovedForFullscreen ||
 11202       !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
 11203     nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
 11204       new AsyncEventDispatcher(this,
 11205                                NS_LITERAL_STRING("MozEnteredDomFullscreen"),
 11206                                true,
 11207                                true);
 11208     asyncDispatcher->PostDOMEvent();
 11211 #ifdef DEBUG
 11212   // Note assertions must run before SetWindowFullScreen() as that does
 11213   // synchronous event dispatch which can run script which exits full-screen!
 11214   NS_ASSERTION(GetFullScreenElement() == aElement,
 11215                "Full-screen element should be the requested element!");
 11216   NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
 11217   nsCOMPtr<nsIDOMElement> fse;
 11218   GetMozFullScreenElement(getter_AddRefs(fse));
 11219   nsCOMPtr<nsIContent> c(do_QueryInterface(fse));
 11220   NS_ASSERTION(c->AsElement() == aElement,
 11221     "GetMozFullScreenElement should match GetFullScreenElement()");
 11222 #endif
 11224   // The origin which is fullscreen changed, send a notifiction so that the
 11225   // root document knows the origin of the document which requested fullscreen.
 11226   // This is used for the fullscreen approval UI. If we're in a child
 11227   // process, the root BrowserElementChild listens for this notification,
 11228   // and forwards it across to its BrowserElementParent, which
 11229   // re-broadcasts the message for the root document in its process.
 11230   if (aNotifyOnOriginChange &&
 11231       !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
 11232     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 11233     nsIDocument* root = GetFullscreenRootDocument(this);
 11234     nsAutoString origin;
 11235     nsContentUtils::GetUTFOrigin(NodePrincipal(), origin);
 11236     os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
 11239   // Make the window full-screen. Note we must make the state changes above
 11240   // before making the window full-screen, as then the document reports as
 11241   // being in full-screen mode when the chrome "fullscreen" event fires,
 11242   // enabling chrome to distinguish between browser and dom full-screen
 11243   // modes. Also note that nsGlobalWindow::SetFullScreen() (which
 11244   // SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
 11245   // and does not operate on the a per-nsIDOMWindow basis.
 11246   SetWindowFullScreen(this, true);
 11249 NS_IMETHODIMP
 11250 nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
 11252   ErrorResult rv;
 11253   Element* el = GetMozFullScreenElement(rv);
 11254   if (rv.Failed()) {
 11255     return rv.ErrorCode();
 11257   nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
 11258   retval.forget(aFullScreenElement);
 11259   return NS_OK;
 11262 Element*
 11263 nsDocument::GetMozFullScreenElement(ErrorResult& rv)
 11265   if (IsFullScreenDoc()) {
 11266     // Must have a full-screen element while in full-screen mode.
 11267     Element* el = GetFullScreenElement();
 11268     if (!el) {
 11269       rv.Throw(NS_ERROR_UNEXPECTED);
 11271     return el;
 11273   return nullptr;
 11276 Element*
 11277 nsDocument::GetFullScreenElement()
 11279   Element* element = FullScreenStackTop();
 11280   NS_ASSERTION(!element ||
 11281                element->IsFullScreenAncestor(),
 11282     "Fullscreen element should have fullscreen styles applied");
 11283   return element;
 11286 NS_IMETHODIMP
 11287 nsDocument::GetMozFullScreen(bool *aFullScreen)
 11289   *aFullScreen = MozFullScreen();
 11290   return NS_OK;
 11293 NS_IMETHODIMP
 11294 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
 11296   NS_ENSURE_ARG_POINTER(aFullScreen);
 11297   *aFullScreen = MozFullScreenEnabled();
 11298   return NS_OK;
 11301 bool
 11302 nsDocument::MozFullScreenEnabled()
 11304   return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false);
 11307 static bool
 11308 HasFullScreenSubDocument(nsIDocument* aDoc)
 11310   uint32_t count = CountFullscreenSubDocuments(aDoc);
 11311   NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
 11312   return count >= 1;
 11315 bool
 11316 nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
 11318   if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) {
 11319     // Chrome code can always use the full-screen API, provided it's not
 11320     // explicitly disabled. Note IsCallerChrome() returns true when running
 11321     // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an
 11322     // nsRunnable!
 11323     return true;
 11326   if (!nsContentUtils::IsFullScreenApiEnabled()) {
 11327     LogFullScreenDenied(aLogFailure, "FullScreenDeniedDisabled", this);
 11328     return false;
 11330   if (!IsVisible()) {
 11331     LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this);
 11332     return false;
 11334   if (HasFullScreenSubDocument(this)) {
 11335     LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this);
 11336     return false;
 11339   // Ensure that all ancestor <iframe> elements have the allowfullscreen
 11340   // boolean attribute set.
 11341   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
 11342   bool allowed = false;
 11343   if (docShell) {
 11344     docShell->GetFullscreenAllowed(&allowed);
 11346   if (!allowed) {
 11347     LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this);
 11350   return allowed;
 11353 static void
 11354 DispatchPointerLockChange(nsIDocument* aTarget)
 11356   if (!aTarget) {
 11357     return;
 11360   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
 11361     new AsyncEventDispatcher(aTarget,
 11362                              NS_LITERAL_STRING("mozpointerlockchange"),
 11363                              true,
 11364                              false);
 11365   asyncDispatcher->PostDOMEvent();
 11368 static void
 11369 DispatchPointerLockError(nsIDocument* aTarget)
 11371   if (!aTarget) {
 11372     return;
 11375   nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
 11376     new AsyncEventDispatcher(aTarget,
 11377                              NS_LITERAL_STRING("mozpointerlockerror"),
 11378                              true,
 11379                              false);
 11380   asyncDispatcher->PostDOMEvent();
 11383 mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest;
 11385 class nsPointerLockPermissionRequest : public nsRunnable,
 11386                                        public nsIContentPermissionRequest
 11388 public:
 11389   nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller)
 11390   : mElement(do_GetWeakReference(aElement)),
 11391     mDocument(do_GetWeakReference(aElement->OwnerDoc())),
 11392     mUserInputOrChromeCaller(aUserInputOrChromeCaller) {}
 11394   virtual ~nsPointerLockPermissionRequest() {}
 11396   NS_DECL_ISUPPORTS
 11397   NS_DECL_NSICONTENTPERMISSIONREQUEST
 11399   NS_IMETHOD Run()
 11401     nsCOMPtr<Element> e = do_QueryReferent(mElement);
 11402     nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
 11403     if (!e || !d || gPendingPointerLockRequest != this ||
 11404         e->GetCurrentDoc() != d) {
 11405       Handled();
 11406       DispatchPointerLockError(d);
 11407       return NS_OK;
 11410     // We're about to enter fullscreen mode.
 11411     nsDocument* doc = static_cast<nsDocument*>(d.get());
 11412     if (doc->mAsyncFullscreenPending ||
 11413         (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) {
 11414       // We're still waiting for approval.
 11415       return NS_OK;
 11418     if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) {
 11419       Allow(JS::UndefinedHandleValue);
 11420       return NS_OK;
 11423     // In non-fullscreen mode user input (or chrome caller) is required!
 11424     // Also, don't let the page to try to get the permission too many times.
 11425     if (!mUserInputOrChromeCaller ||
 11426         doc->mCancelledPointerLockRequests > 2) {
 11427       Handled();
 11428       DispatchPointerLockError(d);
 11429       return NS_OK;
 11432     // Handling a request from user input in non-fullscreen mode.
 11433     // Do a normal permission check.
 11434     nsCOMPtr<nsIContentPermissionPrompt> prompt =
 11435       do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
 11436     if (prompt) {
 11437       prompt->Prompt(this);
 11440     return NS_OK;
 11443   void Handled()
 11445     mElement = nullptr;
 11446     mDocument = nullptr;
 11447     if (gPendingPointerLockRequest == this) {
 11448       gPendingPointerLockRequest = nullptr;
 11452   nsWeakPtr mElement;
 11453   nsWeakPtr mDocument;
 11454   bool mUserInputOrChromeCaller;
 11455 };
 11457 NS_IMPL_ISUPPORTS_INHERITED(nsPointerLockPermissionRequest,
 11458                             nsRunnable,
 11459                             nsIContentPermissionRequest)
 11461 NS_IMETHODIMP
 11462 nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
 11464   nsTArray<nsString> emptyOptions;
 11465   return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
 11466                                NS_LITERAL_CSTRING("unused"),
 11467                                emptyOptions,
 11468                                aTypes);
 11471 NS_IMETHODIMP
 11472 nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
 11474   nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
 11475   if (d) {
 11476     NS_ADDREF(*aPrincipal = d->NodePrincipal());
 11478   return NS_OK;
 11481 NS_IMETHODIMP
 11482 nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow)
 11484   nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
 11485   if (d) {
 11486     NS_IF_ADDREF(*aWindow = d->GetInnerWindow());
 11488   return NS_OK;
 11491 NS_IMETHODIMP
 11492 nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement)
 11494   // It is enough to implement GetWindow.
 11495   *aElement = nullptr;
 11496   return NS_OK;
 11499 NS_IMETHODIMP
 11500 nsPointerLockPermissionRequest::Cancel()
 11502   nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
 11503   Handled();
 11504   if (d) {
 11505     static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++;
 11506     DispatchPointerLockError(d);
 11508   return NS_OK;
 11511 NS_IMETHODIMP
 11512 nsPointerLockPermissionRequest::Allow(JS::HandleValue aChoices)
 11514   MOZ_ASSERT(aChoices.isUndefined());
 11516   nsCOMPtr<Element> e = do_QueryReferent(mElement);
 11517   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
 11518   nsDocument* d = static_cast<nsDocument*>(doc.get());
 11519   if (!e || !d || gPendingPointerLockRequest != this ||
 11520       e->GetCurrentDoc() != d ||
 11521       (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) {
 11522     Handled();
 11523     DispatchPointerLockError(d);
 11524     return NS_OK;
 11527   // Mark handled here so that we don't need to call it everywhere below.
 11528   Handled();
 11530   nsCOMPtr<Element> pointerLockedElement =
 11531     do_QueryReferent(EventStateManager::sPointerLockedElement);
 11532   if (e == pointerLockedElement) {
 11533     DispatchPointerLockChange(d);
 11534     return NS_OK;
 11537   // Note, we must bypass focus change, so pass true as the last parameter!
 11538   if (!d->ShouldLockPointer(e, pointerLockedElement, true)) {
 11539     DispatchPointerLockError(d);
 11540     return NS_OK;
 11543   if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
 11544     DispatchPointerLockError(d);
 11545     return NS_OK;
 11548   d->mCancelledPointerLockRequests = 0;
 11549   e->SetPointerLock();
 11550   EventStateManager::sPointerLockedElement = do_GetWeakReference(e);
 11551   EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
 11552   NS_ASSERTION(EventStateManager::sPointerLockedElement &&
 11553                EventStateManager::sPointerLockedDoc,
 11554                "aElement and this should support weak references!");
 11556   DispatchPointerLockChange(d);
 11557   return NS_OK;
 11560 void
 11561 nsDocument::SetApprovedForFullscreen(bool aIsApproved)
 11563   mIsApprovedForFullscreen = aIsApproved;
 11566 nsresult
 11567 nsDocument::Observe(nsISupports *aSubject,
 11568                     const char *aTopic,
 11569                     const char16_t *aData)
 11571   if (strcmp("fullscreen-approved", aTopic) == 0) {
 11572     nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
 11573     if (subject != this) {
 11574       return NS_OK;
 11576     SetApprovedForFullscreen(true);
 11577     if (gPendingPointerLockRequest) {
 11578       // We have a request pending. Create a clone of it and re-dispatch so that
 11579       // Run() method gets called again.
 11580       nsCOMPtr<Element> el =
 11581         do_QueryReferent(gPendingPointerLockRequest->mElement);
 11582       nsCOMPtr<nsIDocument> doc =
 11583         do_QueryReferent(gPendingPointerLockRequest->mDocument);
 11584       bool userInputOrChromeCaller =
 11585         gPendingPointerLockRequest->mUserInputOrChromeCaller;
 11586       gPendingPointerLockRequest->Handled();
 11587       if (doc == this && el && el->GetCurrentDoc() == doc) {
 11588         nsPointerLockPermissionRequest* clone =
 11589           new nsPointerLockPermissionRequest(el, userInputOrChromeCaller);
 11590         gPendingPointerLockRequest = clone;
 11591         nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
 11592         NS_DispatchToMainThread(r);
 11596   return NS_OK;
 11599 void
 11600 nsDocument::RequestPointerLock(Element* aElement)
 11602   NS_ASSERTION(aElement,
 11603     "Must pass non-null element to nsDocument::RequestPointerLock");
 11605   nsCOMPtr<Element> pointerLockedElement =
 11606     do_QueryReferent(EventStateManager::sPointerLockedElement);
 11607   if (aElement == pointerLockedElement) {
 11608     DispatchPointerLockChange(this);
 11609     return;
 11612   if (!ShouldLockPointer(aElement, pointerLockedElement)) {
 11613     DispatchPointerLockError(this);
 11614     return;
 11617   bool userInputOrChromeCaller = EventStateManager::IsHandlingUserInput() ||
 11618                                  nsContentUtils::IsCallerChrome();
 11620   gPendingPointerLockRequest =
 11621     new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller);
 11622   nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
 11623   NS_DispatchToMainThread(r);
 11626 bool
 11627 nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock,
 11628                               bool aNoFocusCheck)
 11630   // Check if pointer lock pref is enabled
 11631   if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
 11632     NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled");
 11633     return false;
 11636   if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) {
 11637     NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document");
 11638     return false;
 11641   if (!aElement->IsInDoc()) {
 11642     NS_WARNING("ShouldLockPointer(): Element without Document");
 11643     return false;
 11646   if (mSandboxFlags & SANDBOXED_POINTER_LOCK) {
 11647     NS_WARNING("ShouldLockPointer(): Document is sandboxed and doesn't allow pointer-lock");
 11648     return false;
 11651   // Check if the element is in a document with a docshell.
 11652   nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
 11653   if (!ownerDoc->GetContainer()) {
 11654     return false;
 11656   nsCOMPtr<nsPIDOMWindow> ownerWindow = ownerDoc->GetWindow();
 11657   if (!ownerWindow) {
 11658     return false;
 11660   nsCOMPtr<nsPIDOMWindow> ownerInnerWindow = ownerDoc->GetInnerWindow();
 11661   if (!ownerInnerWindow) {
 11662     return false;
 11664   if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
 11665     return false;
 11668   nsCOMPtr<nsIDOMWindow> top;
 11669   ownerWindow->GetScriptableTop(getter_AddRefs(top));
 11670   nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top);
 11671   if (!piTop || !piTop->GetExtantDoc() ||
 11672       piTop->GetExtantDoc()->Hidden()) {
 11673     NS_WARNING("ShouldLockPointer(): Top document isn't visible.");
 11674     return false;
 11677   if (!aNoFocusCheck) {
 11678     mozilla::ErrorResult rv;
 11679     if (!piTop->GetExtantDoc()->HasFocus(rv)) {
 11680       NS_WARNING("ShouldLockPointer(): Top document isn't focused.");
 11681       return false;
 11685   return true;
 11688 bool
 11689 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
 11691   // NOTE: aElement will be nullptr when unlocking.
 11692   nsCOMPtr<nsPIDOMWindow> window = GetWindow();
 11693   if (!window) {
 11694     NS_WARNING("SetPointerLock(): No Window");
 11695     return false;
 11698   nsIDocShell *docShell = window->GetDocShell();
 11699   if (!docShell) {
 11700     NS_WARNING("SetPointerLock(): No DocShell (window already closed?)");
 11701     return false;
 11704   nsRefPtr<nsPresContext> presContext;
 11705   docShell->GetPresContext(getter_AddRefs(presContext));
 11706   if (!presContext) {
 11707     NS_WARNING("SetPointerLock(): Unable to get presContext in \
 11708                 domWindow->GetDocShell()->GetPresContext()");
 11709     return false;
 11712   nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
 11713   if (!shell) {
 11714     NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()");
 11715     return false;
 11718   nsIFrame* rootFrame = shell->GetRootFrame();
 11719   if (!rootFrame) {
 11720     NS_WARNING("SetPointerLock(): Unable to get root frame");
 11721     return false;
 11724   nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
 11725   if (!widget) {
 11726     NS_WARNING("SetPointerLock(): Unable to find widget in \
 11727                 shell->GetRootFrame()->GetNearestWidget();");
 11728     return false;
 11731   if (aElement && (aElement->OwnerDoc() != this)) {
 11732     NS_WARNING("SetPointerLock(): Element not in this document.");
 11733     return false;
 11736   // Hide the cursor and set pointer lock for future mouse events
 11737   nsRefPtr<EventStateManager> esm = presContext->EventStateManager();
 11738   esm->SetCursor(aCursorStyle, nullptr, false,
 11739                  0.0f, 0.0f, widget, true);
 11740   esm->SetPointerLock(widget, aElement);
 11742   return true;
 11745 void
 11746 nsDocument::UnlockPointer(nsIDocument* aDoc)
 11748   if (!EventStateManager::sIsPointerLocked) {
 11749     return;
 11752   nsCOMPtr<nsIDocument> pointerLockedDoc =
 11753     do_QueryReferent(EventStateManager::sPointerLockedDoc);
 11754   if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
 11755     return;
 11757   nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
 11758   if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
 11759     return;
 11762   nsCOMPtr<Element> pointerLockedElement =
 11763     do_QueryReferent(EventStateManager::sPointerLockedElement);
 11764   if (pointerLockedElement) {
 11765     pointerLockedElement->ClearPointerLock();
 11768   EventStateManager::sPointerLockedElement = nullptr;
 11769   EventStateManager::sPointerLockedDoc = nullptr;
 11770   static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc;
 11771   gPendingPointerLockRequest = nullptr;
 11772   DispatchPointerLockChange(pointerLockedDoc);
 11775 void
 11776 nsIDocument::UnlockPointer(nsIDocument* aDoc)
 11778   nsDocument::UnlockPointer(aDoc);
 11781 NS_IMETHODIMP
 11782 nsDocument::MozExitPointerLock()
 11784   nsIDocument::MozExitPointerLock();
 11785   return NS_OK;
 11788 NS_IMETHODIMP
 11789 nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
 11791   Element* el = nsIDocument::GetMozPointerLockElement();
 11792   nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
 11793   retval.forget(aPointerLockedElement);
 11794   return NS_OK;
 11797 Element*
 11798 nsIDocument::GetMozPointerLockElement()
 11800   nsCOMPtr<Element> pointerLockedElement =
 11801     do_QueryReferent(EventStateManager::sPointerLockedElement);
 11802   if (!pointerLockedElement) {
 11803     return nullptr;
 11806   // Make sure pointer locked element is in the same document.
 11807   nsCOMPtr<nsIDocument> pointerLockedDoc =
 11808     do_QueryReferent(EventStateManager::sPointerLockedDoc);
 11809   if (pointerLockedDoc != this) {
 11810     return nullptr;
 11813   return pointerLockedElement;
 11816 void
 11817 nsDocument::XPCOMShutdown()
 11819   gPendingPointerLockRequest = nullptr;
 11820   sProcessingStack.destroyIfConstructed();
 11823 void
 11824 nsDocument::UpdateVisibilityState()
 11826   dom::VisibilityState oldState = mVisibilityState;
 11827   mVisibilityState = GetVisibilityState();
 11828   if (oldState != mVisibilityState) {
 11829     nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
 11830                                          NS_LITERAL_STRING("visibilitychange"),
 11831                                          /* bubbles = */ true,
 11832                                          /* cancelable = */ false);
 11833     nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
 11834                                          NS_LITERAL_STRING("mozvisibilitychange"),
 11835                                          /* bubbles = */ true,
 11836                                          /* cancelable = */ false);
 11838     EnumerateFreezableElements(NotifyActivityChanged, nullptr);
 11842 VisibilityState
 11843 nsDocument::GetVisibilityState() const
 11845   // We have to check a few pieces of information here:
 11846   // 1)  Are we in bfcache (!IsVisible())?  If so, nothing else matters.
 11847   // 2)  Do we have an outer window?  If not, we're hidden.  Note that we don't
 11848   //     want to use GetWindow here because it does weird groveling for windows
 11849   //     in some cases.
 11850   // 3)  Is our outer window background?  If so, we're hidden.
 11851   // Otherwise, we're visible.
 11852   if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
 11853       mWindow->GetOuterWindow()->IsBackground()) {
 11854     return dom::VisibilityState::Hidden;
 11857   return dom::VisibilityState::Visible;
 11860 /* virtual */ void
 11861 nsDocument::PostVisibilityUpdateEvent()
 11863   nsCOMPtr<nsIRunnable> event =
 11864     NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState);
 11865   NS_DispatchToMainThread(event);
 11868 NS_IMETHODIMP
 11869 nsDocument::GetMozHidden(bool* aHidden)
 11871   *aHidden = MozHidden();
 11872   return NS_OK;
 11875 NS_IMETHODIMP
 11876 nsDocument::GetHidden(bool* aHidden)
 11878   *aHidden = Hidden();
 11879   return NS_OK;
 11882 NS_IMETHODIMP
 11883 nsDocument::GetMozVisibilityState(nsAString& aState)
 11885   WarnOnceAbout(ePrefixedVisibilityAPI);
 11886   return GetVisibilityState(aState);
 11889 NS_IMETHODIMP
 11890 nsDocument::GetVisibilityState(nsAString& aState)
 11892   const EnumEntry& entry =
 11893     VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
 11894   aState.AssignASCII(entry.value, entry.length);
 11895   return NS_OK;
 11898 /* virtual */ void
 11899 nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
 11901   aWindowSizes->mDOMOtherSize +=
 11902     nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
 11904   if (mPresShell) {
 11905     mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
 11906                                        &aWindowSizes->mArenaStats,
 11907                                        &aWindowSizes->mLayoutPresShellSize,
 11908                                        &aWindowSizes->mLayoutStyleSetsSize,
 11909                                        &aWindowSizes->mLayoutTextRunsSize,
 11910                                        &aWindowSizes->mLayoutPresContextSize);
 11913   aWindowSizes->mPropertyTablesSize +=
 11914     mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
 11915   for (uint32_t i = 0, count = mExtraPropertyTables.Length();
 11916        i < count; ++i) {
 11917     aWindowSizes->mPropertyTablesSize +=
 11918       mExtraPropertyTables[i]->SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
 11921   if (EventListenerManager* elm = GetExistingListenerManager()) {
 11922     aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
 11925   // Measurement of the following members may be added later if DMD finds it
 11926   // is worthwhile:
 11927   // - many!
 11930 void
 11931 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
 11933   aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
 11934   DocAddSizeOfExcludingThis(aWindowSizes);
 11937 static size_t
 11938 SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
 11939                                       MallocSizeOf aMallocSizeOf,
 11940                                       void* aData)
 11942   return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
 11945 size_t
 11946 nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 11948   // This SizeOfExcludingThis() overrides the one from nsINode.  But
 11949   // nsDocuments can only appear at the top of the DOM tree, and we use the
 11950   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
 11951   // be called.
 11952   MOZ_CRASH();
 11955 void
 11956 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
 11958   nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
 11960   for (nsIContent* node = nsINode::GetFirstChild();
 11961        node;
 11962        node = node->GetNextNode(this))
 11964     size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
 11965     size_t* p;
 11967     switch (node->NodeType()) {
 11968     case nsIDOMNode::ELEMENT_NODE:
 11969       p = &aWindowSizes->mDOMElementNodesSize;
 11970       break;
 11971     case nsIDOMNode::TEXT_NODE:
 11972       p = &aWindowSizes->mDOMTextNodesSize;
 11973       break;
 11974     case nsIDOMNode::CDATA_SECTION_NODE:
 11975       p = &aWindowSizes->mDOMCDATANodesSize;
 11976       break;
 11977     case nsIDOMNode::COMMENT_NODE:
 11978       p = &aWindowSizes->mDOMCommentNodesSize;
 11979       break;
 11980     default:
 11981       p = &aWindowSizes->mDOMOtherSize;
 11982       break;
 11985     *p += nodeSize;
 11987     if (EventListenerManager* elm = node->GetExistingListenerManager()) {
 11988       aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
 11992   aWindowSizes->mStyleSheetsSize +=
 11993     mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
 11994                                      aWindowSizes->mMallocSizeOf);
 11995   aWindowSizes->mStyleSheetsSize +=
 11996     mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
 11997                                        aWindowSizes->mMallocSizeOf);
 11998   aWindowSizes->mStyleSheetsSize +=
 11999     mAdditionalSheets[eAgentSheet].
 12000       SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
 12001                           aWindowSizes->mMallocSizeOf);
 12002   aWindowSizes->mStyleSheetsSize +=
 12003     mAdditionalSheets[eUserSheet].
 12004       SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
 12005                           aWindowSizes->mMallocSizeOf);
 12006   aWindowSizes->mStyleSheetsSize +=
 12007     mAdditionalSheets[eAuthorSheet].
 12008       SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
 12009                           aWindowSizes->mMallocSizeOf);
 12010   // Lumping in the loader with the style-sheets size is not ideal,
 12011   // but most of the things in there are in fact stylesheets, so it
 12012   // doesn't seem worthwhile to separate it out.
 12013   aWindowSizes->mStyleSheetsSize +=
 12014     CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
 12016   aWindowSizes->mDOMOtherSize +=
 12017     mAttrStyleSheet ?
 12018     mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
 12019     0;
 12021   aWindowSizes->mDOMOtherSize +=
 12022     mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf);
 12024   aWindowSizes->mDOMOtherSize +=
 12025     mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis,
 12026                                        aWindowSizes->mMallocSizeOf);
 12028   // Measurement of the following members may be added later if DMD finds it
 12029   // is worthwhile:
 12030   // - many!
 12033 NS_IMETHODIMP
 12034 nsDocument::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn)
 12036   return nsINode::QuerySelector(aSelector, aReturn);
 12039 NS_IMETHODIMP
 12040 nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn)
 12042   return nsINode::QuerySelectorAll(aSelector, aReturn);
 12045 already_AddRefed<nsIDocument>
 12046 nsIDocument::Constructor(const GlobalObject& aGlobal,
 12047                          ErrorResult& rv)
 12049   nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 12050   if (!global) {
 12051     rv.Throw(NS_ERROR_UNEXPECTED);
 12052     return nullptr;
 12055   nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
 12056   if (!prin) {
 12057     rv.Throw(NS_ERROR_UNEXPECTED);
 12058     return nullptr;
 12061   nsCOMPtr<nsIURI> uri;
 12062   NS_NewURI(getter_AddRefs(uri), "about:blank");
 12063   if (!uri) {
 12064     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
 12065     return nullptr;
 12068   nsCOMPtr<nsIDOMDocument> document;
 12069   nsresult res =
 12070     NS_NewDOMDocument(getter_AddRefs(document),
 12071                       NullString(),
 12072                       EmptyString(),
 12073                       nullptr,
 12074                       uri,
 12075                       uri,
 12076                       prin->GetPrincipal(),
 12077                       true,
 12078                       global,
 12079                       DocumentFlavorLegacyGuess);
 12080   if (NS_FAILED(res)) {
 12081     rv.Throw(res);
 12082     return nullptr;
 12085   nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
 12086   doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
 12088   return doc.forget();
 12091 already_AddRefed<nsIDOMXPathExpression>
 12092 nsIDocument::CreateExpression(const nsAString& aExpression,
 12093                               nsIDOMXPathNSResolver* aResolver,
 12094                               ErrorResult& rv)
 12096   return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
 12099 already_AddRefed<nsIDOMXPathNSResolver>
 12100 nsIDocument::CreateNSResolver(nsINode* aNodeResolver,
 12101                               ErrorResult& rv)
 12103   return XPathEvaluator()->CreateNSResolver(aNodeResolver, rv);
 12106 already_AddRefed<nsISupports>
 12107 nsIDocument::Evaluate(const nsAString& aExpression, nsINode* aContextNode,
 12108                       nsIDOMXPathNSResolver* aResolver, uint16_t aType,
 12109                       nsISupports* aResult, ErrorResult& rv)
 12111   return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
 12112                                     aResult, rv);
 12115 NS_IMETHODIMP
 12116 nsDocument::CreateExpression(const nsAString& aExpression,
 12117                              nsIDOMXPathNSResolver* aResolver,
 12118                              nsIDOMXPathExpression** aResult)
 12120   return XPathEvaluator()->CreateExpression(aExpression, aResolver, aResult);
 12123 NS_IMETHODIMP
 12124 nsDocument::CreateNSResolver(nsIDOMNode* aNodeResolver,
 12125                              nsIDOMXPathNSResolver** aResult)
 12127   return XPathEvaluator()->CreateNSResolver(aNodeResolver, aResult);
 12130 NS_IMETHODIMP
 12131 nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode,
 12132                      nsIDOMXPathNSResolver* aResolver, uint16_t aType,
 12133                      nsISupports* aInResult, nsISupports** aResult)
 12135   return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
 12136                                     aInResult, aResult);
 12139 // This is just a hack around the fact that window.document is not
 12140 // [Unforgeable] yet.
 12141 JSObject*
 12142 nsIDocument::WrapObject(JSContext *aCx)
 12144   MOZ_ASSERT(IsDOMBinding());
 12146   JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx));
 12147   if (!obj) {
 12148     return nullptr;
 12151   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(GetInnerWindow());
 12152   if (!win) {
 12153     // No window, nothing else to do here
 12154     return obj;
 12157   if (this != win->GetExtantDoc()) {
 12158     // We're not the current document; we're also done here
 12159     return obj;
 12162   JSAutoCompartment ac(aCx, obj);
 12164   JS::Rooted<JS::Value> winVal(aCx);
 12165   nsresult rv = nsContentUtils::WrapNative(aCx, win, &NS_GET_IID(nsIDOMWindow),
 12166                                            &winVal,
 12167                                            false);
 12168   if (NS_FAILED(rv)) {
 12169     Throw(aCx, rv);
 12170     return nullptr;
 12173   NS_NAMED_LITERAL_STRING(doc_str, "document");
 12175   if (!JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(winVal), doc_str.get(),
 12176                            doc_str.Length(), JS::ObjectValue(*obj),
 12177                            JS_PropertyStub, JS_StrictPropertyStub,
 12178                            JSPROP_READONLY | JSPROP_ENUMERATE)) {
 12179     return nullptr;
 12182   return obj;
 12185 XPathEvaluator*
 12186 nsIDocument::XPathEvaluator()
 12188   if (!mXPathEvaluator) {
 12189     mXPathEvaluator = new dom::XPathEvaluator(this);
 12191   return mXPathEvaluator;
 12194 already_AddRefed<nsIDocumentEncoder>
 12195 nsIDocument::GetCachedEncoder()
 12197   return mCachedEncoder.forget();
 12200 void
 12201 nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
 12203   mCachedEncoder = aEncoder;
 12206 void
 12207 nsIDocument::SetContentTypeInternal(const nsACString& aType)
 12209   mCachedEncoder = nullptr;
 12210   mContentType = aType;
 12213 nsILoadContext*
 12214 nsIDocument::GetLoadContext() const
 12216   return mDocumentContainer;
 12219 nsIDocShell*
 12220 nsIDocument::GetDocShell() const
 12222   return mDocumentContainer;
 12225 void
 12226 nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
 12228   mStateObjectContainer = scContainer;
 12229   mStateObjectCached = nullptr;
 12232 already_AddRefed<Element>
 12233 nsIDocument::CreateHTMLElement(nsIAtom* aTag)
 12235   nsCOMPtr<nsINodeInfo> nodeInfo;
 12236   nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
 12237                                            nsIDOMNode::ELEMENT_NODE);
 12238   MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
 12240   nsCOMPtr<Element> element;
 12241   DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
 12242                                              nodeInfo.forget(),
 12243                                              mozilla::dom::NOT_FROM_PARSER);
 12245   MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
 12246   return element.forget();
 12249 bool
 12250 MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
 12252   nsCOMArray<nsIDocument>* documents =
 12253     static_cast<nsCOMArray<nsIDocument>*>(aData);
 12254   if (aDoc) {
 12255     aDoc->SetIsInSyncOperation(true);
 12256     documents->AppendObject(aDoc);
 12257     aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
 12259   return true;
 12262 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
 12264   mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
 12265   nsContentUtils::SetMicroTaskLevel(0);
 12266   if (aDoc) {
 12267     nsPIDOMWindow* win = aDoc->GetWindow();
 12268     if (win) {
 12269       nsCOMPtr<nsIDOMWindow> topWindow;
 12270       win->GetTop(getter_AddRefs(topWindow));
 12271       nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
 12272       if (top) {
 12273         nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
 12274         MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
 12280 nsAutoSyncOperation::~nsAutoSyncOperation()
 12282   for (int32_t i = 0; i < mDocuments.Count(); ++i) {
 12283     mDocuments[i]->SetIsInSyncOperation(false);
 12285   nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);

mercurial