1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
6 
7 /*
8  * Base class for all our document implementations.
9  */
10 
11 #include "AudioChannelService.h"
12 #include "nsDocument.h"
13 #include "nsIDocumentInlines.h"
14 #include "mozilla/AnimationComparator.h"
15 #include "mozilla/ArrayUtils.h"
16 #include "mozilla/AutoRestore.h"
17 #include "mozilla/BinarySearch.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/EffectSet.h"
20 #include "mozilla/EnumSet.h"
21 #include "mozilla/IntegerRange.h"
22 #include "mozilla/MemoryReporting.h"
23 #include "mozilla/Likely.h"
24 #include "mozilla/PresShell.h"
25 #include "mozilla/URLExtraData.h"
26 #include <algorithm>
27 
28 #include "mozilla/Logging.h"
29 #include "plstr.h"
30 #include "mozilla/Sprintf.h"
31 
32 #include "mozilla/Telemetry.h"
33 #include "nsIInterfaceRequestor.h"
34 #include "nsIInterfaceRequestorUtils.h"
35 #include "nsILoadContext.h"
36 #include "nsITextControlFrame.h"
37 #include "nsNumberControlFrame.h"
38 #include "nsUnicharUtils.h"
39 #include "nsContentList.h"
40 #include "nsCSSPseudoElements.h"
41 #include "nsIObserver.h"
42 #include "nsIBaseWindow.h"
43 #include "mozilla/css/Loader.h"
44 #include "mozilla/css/ImageLoader.h"
45 #include "nsDocShell.h"
46 #include "nsDocShellLoadTypes.h"
47 #include "nsIDocShellTreeItem.h"
48 #include "nsCOMArray.h"
49 #include "nsQueryObject.h"
50 #include "nsDOMClassInfo.h"
51 #include "mozilla/Services.h"
52 #include "nsScreen.h"
53 #include "ChildIterator.h"
54 
55 #include "mozilla/dom/AboutCapabilitiesBinding.h"
56 #include "mozilla/AsyncEventDispatcher.h"
57 #include "mozilla/BasicEvents.h"
58 #include "mozilla/EventListenerManager.h"
59 #include "mozilla/EventStateManager.h"
60 
61 #include "mozilla/dom/Attr.h"
62 #include "mozilla/dom/BindingDeclarations.h"
63 #include "mozilla/dom/Element.h"
64 #include "mozilla/dom/FramingChecker.h"
65 #include "nsGenericHTMLElement.h"
66 #include "mozilla/dom/CDATASection.h"
67 #include "mozilla/dom/ProcessingInstruction.h"
68 #include "nsDOMString.h"
69 #include "nsNodeUtils.h"
70 #include "nsLayoutUtils.h"  // for GetFrameForPoint
71 #include "nsIFrame.h"
72 #include "nsITabChild.h"
73 
74 #include "nsRange.h"
75 #include "nsIDOMText.h"
76 #include "nsIDOMComment.h"
77 #include "mozilla/dom/DocumentType.h"
78 #include "mozilla/dom/NodeIterator.h"
79 #include "mozilla/dom/Promise.h"
80 #include "mozilla/dom/PromiseNativeHandler.h"
81 #include "mozilla/dom/TreeWalker.h"
82 
83 #include "nsIServiceManager.h"
84 #include "mozilla/dom/ServiceWorkerManager.h"
85 #include "imgLoader.h"
86 
87 #include "nsCanvasFrame.h"
88 #include "nsContentCID.h"
89 #include "nsError.h"
90 #include "nsPresContext.h"
91 #include "nsThreadUtils.h"
92 #include "nsNodeInfoManager.h"
93 #include "nsIFileChannel.h"
94 #include "nsIMultiPartChannel.h"
95 #include "nsIRefreshURI.h"
96 #include "nsIWebNavigation.h"
97 #include "nsIScriptError.h"
98 #include "nsISimpleEnumerator.h"
99 #include "nsIRequestContext.h"
100 #include "nsStyleSheetService.h"
101 
102 #include "nsNetUtil.h"  // for NS_NewURI
103 #include "nsIInputStreamChannel.h"
104 #include "nsIAuthPrompt.h"
105 #include "nsIAuthPrompt2.h"
106 
107 #include "nsIScriptSecurityManager.h"
108 #include "nsIPermissionManager.h"
109 #include "nsIPrincipal.h"
110 #include "ExpandedPrincipal.h"
111 #include "NullPrincipal.h"
112 
113 #include "nsIDOMWindow.h"
114 #include "nsPIDOMWindow.h"
115 #include "nsIDOMElement.h"
116 #include "nsFocusManager.h"
117 
118 // for radio group stuff
119 #include "nsIRadioVisitor.h"
120 #include "nsIFormControl.h"
121 
122 #include "nsBidiUtils.h"
123 
124 #include "nsContentCreatorFunctions.h"
125 
126 #include "nsIScriptContext.h"
127 #include "nsBindingManager.h"
128 #include "nsHTMLDocument.h"
129 #include "nsIRequest.h"
130 #include "nsHostObjectProtocolHandler.h"
131 
132 #include "nsCharsetSource.h"
133 #include "nsIParser.h"
134 #include "nsIContentSink.h"
135 
136 #include "mozilla/EventDispatcher.h"
137 #include "mozilla/EventStates.h"
138 #include "mozilla/InternalMutationEvent.h"
139 #include "nsDOMCID.h"
140 
141 #include "jsapi.h"
142 #include "nsIXPConnect.h"
143 #include "xpcpublic.h"
144 #include "nsCCUncollectableMarker.h"
145 #include "nsIContentPolicy.h"
146 #include "nsContentPolicyUtils.h"
147 #include "nsICategoryManager.h"
148 #include "nsIDocumentLoaderFactory.h"
149 #include "nsIDocumentLoader.h"
150 #include "nsIContentViewer.h"
151 #include "nsIXMLContentSink.h"
152 #include "nsIPrompt.h"
153 #include "nsIPropertyBag2.h"
154 #include "mozilla/dom/PageTransitionEvent.h"
155 #include "mozilla/dom/StyleRuleChangeEvent.h"
156 #include "mozilla/dom/StyleSheetChangeEvent.h"
157 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
158 #include "nsJSUtils.h"
159 #include "nsFrameLoader.h"
160 #include "nsEscape.h"
161 #include "nsObjectLoadingContent.h"
162 #include "nsHtml5TreeOpExecutor.h"
163 #include "mozilla/dom/HTMLFormElement.h"
164 #include "mozilla/dom/HTMLLinkElement.h"
165 #include "mozilla/dom/HTMLMediaElement.h"
166 #include "mozilla/dom/HTMLIFrameElement.h"
167 #include "mozilla/dom/HTMLImageElement.h"
168 #include "mozilla/dom/HTMLTextAreaElement.h"
169 #include "mozilla/dom/MediaSource.h"
170 
171 #include "mozAutoDocUpdate.h"
172 #include "nsGlobalWindow.h"
173 #include "mozilla/Encoding.h"
174 #include "nsDOMNavigationTiming.h"
175 
176 #include "nsSMILAnimationController.h"
177 #include "imgIContainer.h"
178 #include "nsSVGUtils.h"
179 
180 #include "nsRefreshDriver.h"
181 
182 // FOR CSP (autogenerated by xpidl)
183 #include "nsIContentSecurityPolicy.h"
184 #include "mozilla/dom/nsCSPContext.h"
185 #include "mozilla/dom/nsCSPService.h"
186 #include "mozilla/dom/nsCSPUtils.h"
187 #include "nsHTMLStyleSheet.h"
188 #include "nsHTMLCSSStyleSheet.h"
189 #include "mozilla/dom/DOMImplementation.h"
190 #include "mozilla/dom/ShadowRoot.h"
191 #include "mozilla/dom/Comment.h"
192 #include "nsTextNode.h"
193 #include "mozilla/dom/Link.h"
194 #include "mozilla/dom/HTMLCollectionBinding.h"
195 #include "mozilla/dom/HTMLElementBinding.h"
196 #include "nsXULAppAPI.h"
197 #include "mozilla/dom/Touch.h"
198 #include "mozilla/dom/TouchEvent.h"
199 
200 #include "mozilla/Preferences.h"
201 
202 #include "imgILoader.h"
203 #include "imgRequestProxy.h"
204 #include "nsWrapperCacheInlines.h"
205 #include "nsSandboxFlags.h"
206 #include "mozilla/dom/AnimatableBinding.h"
207 #include "mozilla/dom/AnonymousContent.h"
208 #include "mozilla/dom/BindingUtils.h"
209 #include "mozilla/dom/ClientInfo.h"
210 #include "mozilla/dom/ClientState.h"
211 #include "mozilla/dom/DocumentFragment.h"
212 #include "mozilla/dom/DocumentTimeline.h"
213 #include "mozilla/dom/Event.h"
214 #include "mozilla/dom/HTMLBodyElement.h"
215 #include "mozilla/dom/HTMLInputElement.h"
216 #include "mozilla/dom/ImageTracker.h"
217 #include "mozilla/dom/MediaQueryList.h"
218 #include "mozilla/dom/NodeFilterBinding.h"
219 #include "mozilla/OwningNonNull.h"
220 #include "mozilla/dom/TabChild.h"
221 #include "mozilla/dom/WebComponentsBinding.h"
222 #include "mozilla/dom/CustomElementRegistryBinding.h"
223 #include "mozilla/dom/CustomElementRegistry.h"
224 #include "mozilla/dom/ServiceWorkerDescriptor.h"
225 #include "mozilla/dom/TimeoutManager.h"
226 #include "mozilla/ExtensionPolicyService.h"
227 #include "nsFrame.h"
228 #include "nsDOMCaretPosition.h"
229 #include "nsViewportInfo.h"
230 #include "mozilla/StaticPtr.h"
231 #include "nsITextControlElement.h"
232 #include "nsIDOMNSEditableElement.h"
233 #include "nsIEditor.h"
234 #ifdef MOZ_OLD_STYLE
235 #include "mozilla/css/StyleRule.h"
236 #endif
237 #include "nsIHttpChannelInternal.h"
238 #include "nsISecurityConsoleMessage.h"
239 #include "nsCharSeparatedTokenizer.h"
240 #include "mozilla/dom/XPathEvaluator.h"
241 #include "mozilla/dom/XPathNSResolverBinding.h"
242 #include "mozilla/dom/XPathResult.h"
243 #include "nsIDocumentEncoder.h"
244 #include "nsIDocumentActivity.h"
245 #include "nsIStructuredCloneContainer.h"
246 #include "nsIMutableArray.h"
247 #include "mozilla/dom/DOMStringList.h"
248 #include "nsWindowSizes.h"
249 #include "mozilla/dom/Location.h"
250 #include "mozilla/dom/FontFaceSet.h"
251 #include "gfxPrefs.h"
252 #include "nsISupportsPrimitives.h"
253 #include "mozilla/StyleSetHandle.h"
254 #include "mozilla/StyleSetHandleInlines.h"
255 #include "mozilla/StyleSheet.h"
256 #include "mozilla/StyleSheetInlines.h"
257 #include "mozilla/dom/SVGSVGElement.h"
258 #include "mozilla/dom/DocGroup.h"
259 #include "mozilla/dom/TabGroup.h"
260 #ifdef MOZ_XUL
261 #include "mozilla/dom/ContainerBoxObject.h"
262 #include "mozilla/dom/ListBoxObject.h"
263 #include "mozilla/dom/MenuBoxObject.h"
264 #include "mozilla/dom/PopupBoxObject.h"
265 #include "mozilla/dom/ScrollBoxObject.h"
266 #include "mozilla/dom/TreeBoxObject.h"
267 #endif
268 #include "nsIPresShellInlines.h"
269 
270 #include "mozilla/DocLoadingTimelineMarker.h"
271 
272 #include "nsISpeculativeConnect.h"
273 
274 #include "mozilla/MediaManager.h"
275 
276 #include "nsIURIClassifier.h"
277 #include "nsIURIMutator.h"
278 #include "mozilla/DocumentStyleRootIterator.h"
279 #include "mozilla/ServoRestyleManager.h"
280 #include "mozilla/ClearOnShutdown.h"
281 #include "nsHTMLTags.h"
282 
283 using namespace mozilla;
284 using namespace mozilla::dom;
285 
286 typedef nsTArray<Link*> LinkArray;
287 
288 static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
289 static LazyLogModule gCspPRLog("CSP");
290 static LazyLogModule gUserInteractionPRLog("UserInteraction");
291 
292 static const char kChromeInContentPref[] =
293     "security.allow_chrome_frames_inside_content";
294 static bool sChromeInContentAllowed = false;
295 static bool sChromeInContentPrefCached = false;
296 
GetHttpChannelHelper(nsIChannel * aChannel,nsIHttpChannel ** aHttpChannel)297 static nsresult GetHttpChannelHelper(nsIChannel* aChannel,
298                                      nsIHttpChannel** aHttpChannel) {
299   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
300   if (httpChannel) {
301     httpChannel.forget(aHttpChannel);
302     return NS_OK;
303   }
304 
305   nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
306   if (!multipart) {
307     *aHttpChannel = nullptr;
308     return NS_OK;
309   }
310 
311   nsCOMPtr<nsIChannel> baseChannel;
312   nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
313   if (NS_WARN_IF(NS_FAILED(rv))) {
314     return rv;
315   }
316 
317   httpChannel = do_QueryInterface(baseChannel);
318   httpChannel.forget(aHttpChannel);
319 
320   return NS_OK;
321 }
322 
323 ////////////////////////////////////////////////////////////////////
324 // PrincipalFlashClassifier
325 
326 // Classify the flash based on the document principal.
327 // The usage of this class is as follows:
328 //
329 // 1) Call AsyncClassify() as early as possible to asynchronously do
330 //    classification against all the flash blocking related tables
331 //    via nsIURIClassifier.asyncClassifyLocalWithTables.
332 //
333 // 2) At any time you need the classification result, call Result()
334 //    and it is guaranteed to give you the result. Note that you have
335 //    to specify "aIsThirdParty" to the function so please make sure
336 //    you can already correctly decide if the document is third-party.
337 //
338 //    Behind the scenes, the sync classification API
339 //    (nsIURIClassifier.classifyLocalWithTable) may be called as a fallback to
340 //    synchronously get the result if the asyncClassifyLocalWithTables hasn't
341 //    been done yet.
342 //
343 // 3) You can call Result() as many times as you want and only the first time
344 //    it may unfortunately call the blocking sync API. The subsequent call
345 //    will just return the result that came out at the first time.
346 //
347 class PrincipalFlashClassifier final : public nsIURIClassifierCallback {
348  public:
349   NS_DECL_THREADSAFE_ISUPPORTS
350   NS_DECL_NSIURICLASSIFIERCALLBACK
351 
352   PrincipalFlashClassifier();
353 
354   // Fire async classification based on the given principal.
355   void AsyncClassify(nsIPrincipal* aPrincipal);
356 
357   // Would block if the result hasn't come out.
358   mozilla::dom::FlashClassification ClassifyMaybeSync(nsIPrincipal* aPrincipal,
359                                                       bool aIsThirdParty);
360 
361  private:
362   ~PrincipalFlashClassifier() = default;
363 
364   void Reset();
365   bool EnsureUriClassifier();
366   mozilla::dom::FlashClassification CheckIfClassifyNeeded(
367       nsIPrincipal* aPrincipal);
368   mozilla::dom::FlashClassification Resolve(bool aIsThirdParty);
369   mozilla::dom::FlashClassification AsyncClassifyInternal(
370       nsIPrincipal* aPrincipal);
371   void GetClassificationTables(bool aIsThirdParty, nsACString& aTables);
372 
373   // For the fallback sync classification.
374   nsCOMPtr<nsIURI> mClassificationURI;
375 
376   nsCOMPtr<nsIURIClassifier> mUriClassifier;
377   bool mAsyncClassified;
378   nsTArray<nsCString> mMatchedTables;
379   mozilla::dom::FlashClassification mResult;
380 };
381 
382 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
383 
nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString & aKey)384 nsIdentifierMapEntry::nsIdentifierMapEntry(
385     const nsIdentifierMapEntry::AtomOrString& aKey)
386     : mKey(aKey) {}
387 
nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString * aKey)388 nsIdentifierMapEntry::nsIdentifierMapEntry(
389     const nsIdentifierMapEntry::AtomOrString* aKey)
390     : mKey(aKey ? *aKey : nullptr) {}
391 
~nsIdentifierMapEntry()392 nsIdentifierMapEntry::~nsIdentifierMapEntry() {}
393 
nsIdentifierMapEntry(nsIdentifierMapEntry && aOther)394 nsIdentifierMapEntry::nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther)
395     : mKey(mozilla::Move(aOther.mKey)),
396       mIdContentList(mozilla::Move(aOther.mIdContentList)),
397       mNameContentList(mozilla::Move(aOther.mNameContentList)),
398       mChangeCallbacks(mozilla::Move(aOther.mChangeCallbacks)),
399       mImageElement(mozilla::Move(aOther.mImageElement)) {}
400 
Traverse(nsCycleCollectionTraversalCallback * aCallback)401 void nsIdentifierMapEntry::Traverse(
402     nsCycleCollectionTraversalCallback* aCallback) {
403   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
404                                      "mIdentifierMap mNameContentList");
405   aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
406 
407   if (mImageElement) {
408     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
409                                        "mIdentifierMap mImageElement element");
410     nsIContent* imageElement = mImageElement;
411     aCallback->NoteXPCOMChild(imageElement);
412   }
413 }
414 
IsEmpty()415 bool nsIdentifierMapEntry::IsEmpty() {
416   return mIdContentList.IsEmpty() && !mNameContentList && !mChangeCallbacks &&
417          !mImageElement;
418 }
419 
HasNameElement() const420 bool nsIdentifierMapEntry::HasNameElement() const {
421   return mNameContentList && mNameContentList->Length() != 0;
422 }
423 
GetIdElement()424 Element* nsIdentifierMapEntry::GetIdElement() {
425   return mIdContentList.SafeElementAt(0);
426 }
427 
GetImageIdElement()428 Element* nsIdentifierMapEntry::GetImageIdElement() {
429   return mImageElement ? mImageElement.get() : GetIdElement();
430 }
431 
AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,void * aData,bool aForImage)432 void nsIdentifierMapEntry::AddContentChangeCallback(
433     nsIDocument::IDTargetObserver aCallback, void* aData, bool aForImage) {
434   if (!mChangeCallbacks) {
435     mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
436   }
437 
438   ChangeCallback cc = {aCallback, aData, aForImage};
439   mChangeCallbacks->PutEntry(cc);
440 }
441 
RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,void * aData,bool aForImage)442 void nsIdentifierMapEntry::RemoveContentChangeCallback(
443     nsIDocument::IDTargetObserver aCallback, void* aData, bool aForImage) {
444   if (!mChangeCallbacks) return;
445   ChangeCallback cc = {aCallback, aData, aForImage};
446   mChangeCallbacks->RemoveEntry(cc);
447   if (mChangeCallbacks->Count() == 0) {
448     mChangeCallbacks = nullptr;
449   }
450 }
451 
FireChangeCallbacks(Element * aOldElement,Element * aNewElement,bool aImageOnly)452 void nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
453                                                Element* aNewElement,
454                                                bool aImageOnly) {
455   if (!mChangeCallbacks) return;
456 
457   for (auto iter = mChangeCallbacks->ConstIter(); !iter.Done(); iter.Next()) {
458     nsIdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
459     // Don't fire image changes for non-image observers, and don't fire element
460     // changes for image observers when an image override is active.
461     if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
462       continue;
463     }
464 
465     if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
466       iter.Remove();
467     }
468   }
469 }
470 
471 namespace {
472 
473 struct PositionComparator {
474   Element* const mElement;
PositionComparator__anon937f70ae0111::PositionComparator475   explicit PositionComparator(Element* const aElement) : mElement(aElement) {}
476 
operator ()__anon937f70ae0111::PositionComparator477   int operator()(void* aElement) const {
478     Element* curElement = static_cast<Element*>(aElement);
479     if (mElement == curElement) {
480       return 0;
481     }
482     if (nsContentUtils::PositionIsBefore(mElement, curElement)) {
483       return -1;
484     }
485     return 1;
486   }
487 };
488 
489 }  // namespace
490 
AddIdElement(Element * aElement)491 bool nsIdentifierMapEntry::AddIdElement(Element* aElement) {
492   NS_PRECONDITION(aElement, "Must have element");
493   NS_PRECONDITION(!mIdContentList.Contains(nullptr),
494                   "Why is null in our list?");
495 
496 #ifdef DEBUG
497   Element* currentElement = mIdContentList.SafeElementAt(0);
498 #endif
499 
500   // Common case
501   if (mIdContentList.IsEmpty()) {
502     if (!mIdContentList.AppendElement(aElement)) return false;
503     NS_ASSERTION(currentElement == nullptr, "How did that happen?");
504     FireChangeCallbacks(nullptr, aElement);
505     return true;
506   }
507 
508   // We seem to have multiple content nodes for the same id, or XUL is messing
509   // with us.  Search for the right place to insert the content.
510 
511   size_t idx;
512   if (BinarySearchIf(mIdContentList, 0, mIdContentList.Length(),
513                      PositionComparator(aElement), &idx)) {
514     // Already in the list, so already in the right spot.  Get out of here.
515     // XXXbz this only happens because XUL does all sorts of random
516     // UpdateIdTableEntry calls.  Hate, hate, hate!
517     return true;
518   }
519 
520   if (!mIdContentList.InsertElementAt(idx, aElement)) {
521     return false;
522   }
523 
524   if (idx == 0) {
525     Element* oldElement = mIdContentList.SafeElementAt(1);
526     NS_ASSERTION(currentElement == oldElement, "How did that happen?");
527     FireChangeCallbacks(oldElement, aElement);
528   }
529   return true;
530 }
531 
RemoveIdElement(Element * aElement)532 void nsIdentifierMapEntry::RemoveIdElement(Element* aElement) {
533   NS_PRECONDITION(aElement, "Missing element");
534 
535   // This should only be called while the document is in an update.
536   // Assertions near the call to this method guarantee this.
537 
538   // This could fire in OOM situations
539   // Only assert this in HTML documents for now as XUL does all sorts of weird
540   // crap.
541   NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
542                    mIdContentList.Contains(aElement),
543                "Removing id entry that doesn't exist");
544 
545   // XXXbz should this ever Compact() I guess when all the content is gone
546   // we'll just get cleaned up in the natural order of things...
547   Element* currentElement = mIdContentList.SafeElementAt(0);
548   mIdContentList.RemoveElement(aElement);
549   if (currentElement == aElement) {
550     FireChangeCallbacks(currentElement, mIdContentList.SafeElementAt(0));
551   }
552 }
553 
SetImageElement(Element * aElement)554 void nsIdentifierMapEntry::SetImageElement(Element* aElement) {
555   Element* oldElement = GetImageIdElement();
556   mImageElement = aElement;
557   Element* newElement = GetImageIdElement();
558   if (oldElement != newElement) {
559     FireChangeCallbacks(oldElement, newElement, true);
560   }
561 }
562 
563 namespace mozilla {
564 namespace dom {
565 class SimpleHTMLCollection final : public nsSimpleContentList,
566                                    public nsIHTMLCollection {
567  public:
SimpleHTMLCollection(nsINode * aRoot)568   explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
569 
570   NS_DECL_ISUPPORTS_INHERITED
571 
GetParentObject()572   virtual nsINode* GetParentObject() override {
573     return nsSimpleContentList::GetParentObject();
574   }
Length()575   virtual uint32_t Length() override { return nsSimpleContentList::Length(); }
GetElementAt(uint32_t aIndex)576   virtual Element* GetElementAt(uint32_t aIndex) override {
577     return mElements.SafeElementAt(aIndex)->AsElement();
578   }
579 
GetFirstNamedElement(const nsAString & aName,bool & aFound)580   virtual Element* GetFirstNamedElement(const nsAString& aName,
581                                         bool& aFound) override {
582     aFound = false;
583     RefPtr<nsAtom> name = NS_Atomize(aName);
584     for (uint32_t i = 0; i < mElements.Length(); i++) {
585       MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
586       Element* element = mElements[i]->AsElement();
587       if (element->GetID() == name ||
588           (element->HasName() &&
589            element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
590         aFound = true;
591         return element;
592       }
593     }
594     return nullptr;
595   }
596 
GetSupportedNames(nsTArray<nsString> & aNames)597   virtual void GetSupportedNames(nsTArray<nsString>& aNames) override {
598     AutoTArray<nsAtom*, 8> atoms;
599     for (uint32_t i = 0; i < mElements.Length(); i++) {
600       MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
601       Element* element = mElements[i]->AsElement();
602 
603       nsAtom* id = element->GetID();
604       MOZ_ASSERT(id != nsGkAtoms::_empty);
605       if (id && !atoms.Contains(id)) {
606         atoms.AppendElement(id);
607       }
608 
609       if (element->HasName()) {
610         nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
611         MOZ_ASSERT(name && name != nsGkAtoms::_empty);
612         if (name && !atoms.Contains(name)) {
613           atoms.AppendElement(name);
614         }
615       }
616     }
617 
618     nsString* names = aNames.AppendElements(atoms.Length());
619     for (uint32_t i = 0; i < atoms.Length(); i++) {
620       atoms[i]->ToString(names[i]);
621     }
622   }
623 
GetWrapperPreserveColorInternal()624   virtual JSObject* GetWrapperPreserveColorInternal() override {
625     return nsWrapperCache::GetWrapperPreserveColor();
626   }
PreserveWrapperInternal(nsISupports * aScriptObjectHolder)627   virtual void PreserveWrapperInternal(
628       nsISupports* aScriptObjectHolder) override {
629     nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
630   }
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)631   virtual JSObject* WrapObject(JSContext* aCx,
632                                JS::Handle<JSObject*> aGivenProto) override {
633     return HTMLCollectionBinding::Wrap(aCx, this, aGivenProto);
634   }
635 
636   using nsBaseContentList::Item;
637 
638  private:
~SimpleHTMLCollection()639   virtual ~SimpleHTMLCollection() {}
640 };
641 
642 NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
643                             nsIHTMLCollection)
644 
645 }  // namespace dom
646 }  // namespace mozilla
647 
AddNameElement(nsINode * aNode,Element * aElement)648 void nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) {
649   if (!mNameContentList) {
650     mNameContentList = new SimpleHTMLCollection(aNode);
651   }
652 
653   mNameContentList->AppendElement(aElement);
654 }
655 
RemoveNameElement(Element * aElement)656 void nsIdentifierMapEntry::RemoveNameElement(Element* aElement) {
657   if (mNameContentList) {
658     mNameContentList->RemoveElement(aElement);
659   }
660 }
661 
HasIdElementExposedAsHTMLDocumentProperty()662 bool nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() {
663   Element* idElement = GetIdElement();
664   return idElement &&
665          nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
666 }
667 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const668 size_t nsIdentifierMapEntry::SizeOfExcludingThis(
669     MallocSizeOf aMallocSizeOf) const {
670   return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
671 }
672 
673 // Helper structs for the content->subdoc map
674 
675 class SubDocMapEntry : public PLDHashEntryHdr {
676  public:
677   // Both of these are strong references
678   Element* mKey;  // must be first, to look like PLDHashEntryStub
679   nsIDocument* mSubDocument;
680 };
681 
682 /**
683  * A struct that holds all the information about a radio group.
684  */
685 struct nsRadioGroupStruct {
nsRadioGroupStructnsRadioGroupStruct686   nsRadioGroupStruct()
687       : mRequiredRadioCount(0), mGroupSuffersFromValueMissing(false) {}
688 
689   /**
690    * A strong pointer to the currently selected radio button.
691    */
692   RefPtr<HTMLInputElement> mSelectedRadioButton;
693   nsCOMArray<nsIFormControl> mRadioButtons;
694   uint32_t mRequiredRadioCount;
695   bool mGroupSuffersFromValueMissing;
696 };
697 
698 // nsOnloadBlocker implementation
NS_IMPL_ISUPPORTS(nsOnloadBlocker,nsIRequest)699 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
700 
701 NS_IMETHODIMP
702 nsOnloadBlocker::GetName(nsACString& aResult) {
703   aResult.AssignLiteral("about:document-onload-blocker");
704   return NS_OK;
705 }
706 
707 NS_IMETHODIMP
IsPending(bool * _retval)708 nsOnloadBlocker::IsPending(bool* _retval) {
709   *_retval = true;
710   return NS_OK;
711 }
712 
713 NS_IMETHODIMP
GetStatus(nsresult * status)714 nsOnloadBlocker::GetStatus(nsresult* status) {
715   *status = NS_OK;
716   return NS_OK;
717 }
718 
719 NS_IMETHODIMP
Cancel(nsresult status)720 nsOnloadBlocker::Cancel(nsresult status) { return NS_OK; }
721 NS_IMETHODIMP
Suspend(void)722 nsOnloadBlocker::Suspend(void) { return NS_OK; }
723 NS_IMETHODIMP
Resume(void)724 nsOnloadBlocker::Resume(void) { return NS_OK; }
725 
726 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** aLoadGroup)727 nsOnloadBlocker::GetLoadGroup(nsILoadGroup** aLoadGroup) {
728   *aLoadGroup = nullptr;
729   return NS_OK;
730 }
731 
732 NS_IMETHODIMP
SetLoadGroup(nsILoadGroup * aLoadGroup)733 nsOnloadBlocker::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
734 
735 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * aLoadFlags)736 nsOnloadBlocker::GetLoadFlags(nsLoadFlags* aLoadFlags) {
737   *aLoadFlags = nsIRequest::LOAD_NORMAL;
738   return NS_OK;
739 }
740 
741 NS_IMETHODIMP
SetLoadFlags(nsLoadFlags aLoadFlags)742 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
743 
744 // ==================================================================
745 
nsExternalResourceMap()746 nsExternalResourceMap::nsExternalResourceMap() : mHaveShutDown(false) {}
747 
RequestResource(nsIURI * aURI,nsINode * aRequestingNode,nsDocument * aDisplayDocument,ExternalResourceLoad ** aPendingLoad)748 nsIDocument* nsExternalResourceMap::RequestResource(
749     nsIURI* aURI, nsINode* aRequestingNode, nsDocument* aDisplayDocument,
750     ExternalResourceLoad** aPendingLoad) {
751   // If we ever start allowing non-same-origin loads here, we might need to do
752   // something interesting with aRequestingPrincipal even for the hashtable
753   // gets.
754   NS_PRECONDITION(aURI, "Must have a URI");
755   NS_PRECONDITION(aRequestingNode, "Must have a node");
756   *aPendingLoad = nullptr;
757   if (mHaveShutDown) {
758     return nullptr;
759   }
760 
761   // First, make sure we strip the ref from aURI.
762   nsCOMPtr<nsIURI> clone;
763   nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
764   if (NS_FAILED(rv) || !clone) {
765     return nullptr;
766   }
767 
768   ExternalResource* resource;
769   mMap.Get(clone, &resource);
770   if (resource) {
771     return resource->mDocument;
772   }
773 
774   RefPtr<PendingLoad>& loadEntry = mPendingLoads.GetOrInsert(clone);
775   if (loadEntry) {
776     RefPtr<PendingLoad> load(loadEntry);
777     load.forget(aPendingLoad);
778     return nullptr;
779   }
780 
781   RefPtr<PendingLoad> load(new PendingLoad(aDisplayDocument));
782   loadEntry = load;
783 
784   if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
785     // Make sure we don't thrash things by trying this load again, since
786     // chances are it failed for good reasons (security check, etc).
787     AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
788   } else {
789     load.forget(aPendingLoad);
790   }
791 
792   return nullptr;
793 }
794 
EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,void * aData)795 void nsExternalResourceMap::EnumerateResources(
796     nsIDocument::nsSubDocEnumFunc aCallback, void* aData) {
797   for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
798     nsExternalResourceMap::ExternalResource* resource = iter.UserData();
799     if (resource->mDocument && !aCallback(resource->mDocument, aData)) {
800       break;
801     }
802   }
803 }
804 
Traverse(nsCycleCollectionTraversalCallback * aCallback) const805 void nsExternalResourceMap::Traverse(
806     nsCycleCollectionTraversalCallback* aCallback) const {
807   // mPendingLoads will get cleared out as the requests complete, so
808   // no need to worry about those here.
809   for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
810     nsExternalResourceMap::ExternalResource* resource = iter.UserData();
811 
812     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
813                                        "mExternalResourceMap.mMap entry"
814                                        "->mDocument");
815     aCallback->NoteXPCOMChild(resource->mDocument);
816 
817     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
818                                        "mExternalResourceMap.mMap entry"
819                                        "->mViewer");
820     aCallback->NoteXPCOMChild(resource->mViewer);
821 
822     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
823                                        "mExternalResourceMap.mMap entry"
824                                        "->mLoadGroup");
825     aCallback->NoteXPCOMChild(resource->mLoadGroup);
826   }
827 }
828 
HideViewers()829 void nsExternalResourceMap::HideViewers() {
830   for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
831     nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
832     if (viewer) {
833       viewer->Hide();
834     }
835   }
836 }
837 
ShowViewers()838 void nsExternalResourceMap::ShowViewers() {
839   for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
840     nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
841     if (viewer) {
842       viewer->Show();
843     }
844   }
845 }
846 
TransferZoomLevels(nsIDocument * aFromDoc,nsIDocument * aToDoc)847 void TransferZoomLevels(nsIDocument* aFromDoc, nsIDocument* aToDoc) {
848   MOZ_ASSERT(aFromDoc && aToDoc, "transferring zoom levels from/to null doc");
849 
850   nsPresContext* fromCtxt = aFromDoc->GetPresContext();
851   if (!fromCtxt) return;
852 
853   nsPresContext* toCtxt = aToDoc->GetPresContext();
854   if (!toCtxt) return;
855 
856   toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
857   toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
858   toCtxt->SetTextZoom(fromCtxt->TextZoom());
859   toCtxt->SetOverrideDPPX(fromCtxt->GetOverrideDPPX());
860 }
861 
TransferShowingState(nsIDocument * aFromDoc,nsIDocument * aToDoc)862 void TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc) {
863   MOZ_ASSERT(aFromDoc && aToDoc, "transferring showing state from/to null doc");
864 
865   if (aFromDoc->IsShowing()) {
866     aToDoc->OnPageShow(true, nullptr);
867   }
868 }
869 
AddExternalResource(nsIURI * aURI,nsIContentViewer * aViewer,nsILoadGroup * aLoadGroup,nsIDocument * aDisplayDocument)870 nsresult nsExternalResourceMap::AddExternalResource(
871     nsIURI* aURI, nsIContentViewer* aViewer, nsILoadGroup* aLoadGroup,
872     nsIDocument* aDisplayDocument) {
873   NS_PRECONDITION(aURI, "Unexpected call");
874   NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
875                   "Must have both or neither");
876 
877   RefPtr<PendingLoad> load;
878   mPendingLoads.Remove(aURI, getter_AddRefs(load));
879 
880   nsresult rv = NS_OK;
881 
882   nsCOMPtr<nsIDocument> doc;
883   if (aViewer) {
884     doc = aViewer->GetDocument();
885     NS_ASSERTION(doc, "Must have a document");
886 
887     if (doc->IsXULDocument()) {
888       // We don't handle XUL stuff here yet.
889       rv = NS_ERROR_NOT_AVAILABLE;
890     } else {
891       doc->SetDisplayDocument(aDisplayDocument);
892 
893       // Make sure that hiding our viewer will tear down its presentation.
894       aViewer->SetSticky(false);
895 
896       rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
897       if (NS_SUCCEEDED(rv)) {
898         rv = aViewer->Open(nullptr, nullptr);
899       }
900     }
901 
902     if (NS_FAILED(rv)) {
903       doc = nullptr;
904       aViewer = nullptr;
905       aLoadGroup = nullptr;
906     }
907   }
908 
909   ExternalResource* newResource = new ExternalResource();
910   mMap.Put(aURI, newResource);
911 
912   newResource->mDocument = doc;
913   newResource->mViewer = aViewer;
914   newResource->mLoadGroup = aLoadGroup;
915   if (doc) {
916     TransferZoomLevels(aDisplayDocument, doc);
917     TransferShowingState(aDisplayDocument, doc);
918   }
919 
920   const nsTArray<nsCOMPtr<nsIObserver>>& obs = load->Observers();
921   for (uint32_t i = 0; i < obs.Length(); ++i) {
922     obs[i]->Observe(doc, "external-resource-document-created", nullptr);
923   }
924 
925   return rv;
926 }
927 
NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,nsIStreamListener,nsIRequestObserver)928 NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad, nsIStreamListener,
929                   nsIRequestObserver)
930 
931 NS_IMETHODIMP
932 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest* aRequest,
933                                                    nsISupports* aContext) {
934   nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
935   if (map.HaveShutDown()) {
936     return NS_BINDING_ABORTED;
937   }
938 
939   nsCOMPtr<nsIContentViewer> viewer;
940   nsCOMPtr<nsILoadGroup> loadGroup;
941   nsresult rv =
942       SetupViewer(aRequest, getter_AddRefs(viewer), getter_AddRefs(loadGroup));
943 
944   // Make sure to do this no matter what
945   nsresult rv2 =
946       map.AddExternalResource(mURI, viewer, loadGroup, mDisplayDocument);
947   if (NS_FAILED(rv)) {
948     return rv;
949   }
950   if (NS_FAILED(rv2)) {
951     mTargetListener = nullptr;
952     return rv2;
953   }
954 
955   return mTargetListener->OnStartRequest(aRequest, aContext);
956 }
957 
SetupViewer(nsIRequest * aRequest,nsIContentViewer ** aViewer,nsILoadGroup ** aLoadGroup)958 nsresult nsExternalResourceMap::PendingLoad::SetupViewer(
959     nsIRequest* aRequest, nsIContentViewer** aViewer,
960     nsILoadGroup** aLoadGroup) {
961   NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
962   *aViewer = nullptr;
963   *aLoadGroup = nullptr;
964 
965   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
966   NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
967 
968   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
969   if (httpChannel) {
970     bool requestSucceeded;
971     if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
972         !requestSucceeded) {
973       // Bail out on this load, since it looks like we have an HTTP error page
974       return NS_BINDING_ABORTED;
975     }
976   }
977 
978   nsAutoCString type;
979   chan->GetContentType(type);
980 
981   nsCOMPtr<nsILoadGroup> loadGroup;
982   chan->GetLoadGroup(getter_AddRefs(loadGroup));
983 
984   // Give this document its own loadgroup
985   nsCOMPtr<nsILoadGroup> newLoadGroup =
986       do_CreateInstance(NS_LOADGROUP_CONTRACTID);
987   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
988   newLoadGroup->SetLoadGroup(loadGroup);
989 
990   nsCOMPtr<nsIInterfaceRequestor> callbacks;
991   loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
992 
993   nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
994       new LoadgroupCallbacks(callbacks);
995   newLoadGroup->SetNotificationCallbacks(newCallbacks);
996 
997   // This is some serious hackery cribbed from docshell
998   nsCOMPtr<nsICategoryManager> catMan =
999       do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1000   NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1001   nsCString contractId;
1002   nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1003                                          getter_Copies(contractId));
1004   NS_ENSURE_SUCCESS(rv, rv);
1005   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1006       do_GetService(contractId.get());
1007   NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1008 
1009   nsCOMPtr<nsIContentViewer> viewer;
1010   nsCOMPtr<nsIStreamListener> listener;
1011   rv = docLoaderFactory->CreateInstance(
1012       "external-resource", chan, newLoadGroup, type, nullptr, nullptr,
1013       getter_AddRefs(listener), getter_AddRefs(viewer));
1014   NS_ENSURE_SUCCESS(rv, rv);
1015   NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1016 
1017   nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1018   if (!parser) {
1019     /// We don't want to deal with the various fake documents yet
1020     return NS_ERROR_NOT_IMPLEMENTED;
1021   }
1022 
1023   // We can't handle HTML and other weird things here yet.
1024   nsIContentSink* sink = parser->GetContentSink();
1025   nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1026   if (!xmlSink) {
1027     return NS_ERROR_NOT_IMPLEMENTED;
1028   }
1029 
1030   listener.swap(mTargetListener);
1031   viewer.forget(aViewer);
1032   newLoadGroup.forget(aLoadGroup);
1033   return NS_OK;
1034 }
1035 
1036 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsISupports * aContext,nsIInputStream * aStream,uint64_t aOffset,uint32_t aCount)1037 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1038                                                     nsISupports* aContext,
1039                                                     nsIInputStream* aStream,
1040                                                     uint64_t aOffset,
1041                                                     uint32_t aCount) {
1042   // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1043   NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
1044   if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1045     return NS_BINDING_ABORTED;
1046   }
1047   return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1048                                           aCount);
1049 }
1050 
1051 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsISupports * aContext,nsresult aStatus)1052 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1053                                                   nsISupports* aContext,
1054                                                   nsresult aStatus) {
1055   // mTargetListener might be null if SetupViewer or AddExternalResource failed
1056   if (mTargetListener) {
1057     nsCOMPtr<nsIStreamListener> listener;
1058     mTargetListener.swap(listener);
1059     return listener->OnStopRequest(aRequest, aContext, aStatus);
1060   }
1061 
1062   return NS_OK;
1063 }
1064 
StartLoad(nsIURI * aURI,nsINode * aRequestingNode)1065 nsresult nsExternalResourceMap::PendingLoad::StartLoad(
1066     nsIURI* aURI, nsINode* aRequestingNode) {
1067   NS_PRECONDITION(aURI, "Must have a URI");
1068   NS_PRECONDITION(aRequestingNode, "Must have a node");
1069 
1070   nsCOMPtr<nsILoadGroup> loadGroup =
1071       aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
1072 
1073   nsresult rv = NS_OK;
1074   nsCOMPtr<nsIChannel> channel;
1075   rv = NS_NewChannel(getter_AddRefs(channel), aURI, aRequestingNode,
1076                      nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
1077                      nsIContentPolicy::TYPE_OTHER,
1078                      nullptr,  // aPerformanceStorage
1079                      loadGroup);
1080   NS_ENSURE_SUCCESS(rv, rv);
1081 
1082   mURI = aURI;
1083 
1084   return channel->AsyncOpen2(this);
1085 }
1086 
NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,nsIInterfaceRequestor)1087 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1088                   nsIInterfaceRequestor)
1089 
1090 #define IMPL_SHIM(_i) \
1091   NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1092 
1093 IMPL_SHIM(nsILoadContext)
1094 IMPL_SHIM(nsIProgressEventSink)
1095 IMPL_SHIM(nsIChannelEventSink)
1096 IMPL_SHIM(nsISecurityEventSink)
1097 IMPL_SHIM(nsIApplicationCacheContainer)
1098 
1099 #undef IMPL_SHIM
1100 
1101 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1102 
1103 #define TRY_SHIM(_i)                                 \
1104   PR_BEGIN_MACRO                                     \
1105   if (IID_IS(_i)) {                                  \
1106     nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1107     if (!real) {                                     \
1108       return NS_NOINTERFACE;                         \
1109     }                                                \
1110     nsCOMPtr<_i> shim = new _i##Shim(this, real);    \
1111     shim.forget(aSink);                              \
1112     return NS_OK;                                    \
1113   }                                                  \
1114   PR_END_MACRO
1115 
1116 NS_IMETHODIMP
1117 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID& aIID,
1118                                                         void** aSink) {
1119   if (mCallbacks && (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) ||
1120                      IID_IS(nsIAuthPrompt2) || IID_IS(nsITabChild))) {
1121     return mCallbacks->GetInterface(aIID, aSink);
1122   }
1123 
1124   *aSink = nullptr;
1125 
1126   TRY_SHIM(nsILoadContext);
1127   TRY_SHIM(nsIProgressEventSink);
1128   TRY_SHIM(nsIChannelEventSink);
1129   TRY_SHIM(nsISecurityEventSink);
1130   TRY_SHIM(nsIApplicationCacheContainer);
1131 
1132   return NS_NOINTERFACE;
1133 }
1134 
1135 #undef TRY_SHIM
1136 #undef IID_IS
1137 
~ExternalResource()1138 nsExternalResourceMap::ExternalResource::~ExternalResource() {
1139   if (mViewer) {
1140     mViewer->Close(nullptr);
1141     mViewer->Destroy();
1142   }
1143 }
1144 
1145 // ==================================================================
1146 // =
1147 // ==================================================================
1148 
1149 // If we ever have an nsIDocumentObserver notification for stylesheet title
1150 // changes we should update the list from that instead of overriding
1151 // EnsureFresh.
1152 class nsDOMStyleSheetSetList final : public DOMStringList {
1153  public:
1154   explicit nsDOMStyleSheetSetList(nsIDocument* aDocument);
1155 
Disconnect()1156   void Disconnect() { mDocument = nullptr; }
1157 
1158   virtual void EnsureFresh() override;
1159 
1160  protected:
1161   nsIDocument* mDocument;  // Our document; weak ref.  It'll let us know if it
1162                            // dies.
1163 };
1164 
nsDOMStyleSheetSetList(nsIDocument * aDocument)1165 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1166     : mDocument(aDocument) {
1167   NS_ASSERTION(mDocument, "Must have document!");
1168 }
1169 
EnsureFresh()1170 void nsDOMStyleSheetSetList::EnsureFresh() {
1171   MOZ_ASSERT(NS_IsMainThread());
1172 
1173   mNames.Clear();
1174 
1175   if (!mDocument) {
1176     return;  // Spec says "no exceptions", and we have no style sets if we have
1177              // no document, for sure
1178   }
1179 
1180   size_t count = mDocument->SheetCount();
1181   nsAutoString title;
1182   for (size_t index = 0; index < count; index++) {
1183     StyleSheet* sheet = mDocument->SheetAt(index);
1184     NS_ASSERTION(sheet, "Null sheet in sheet list!");
1185     sheet->GetTitle(title);
1186     if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1187       return;
1188     }
1189   }
1190 }
1191 
1192 // ==================================================================
SelectorCache(nsIEventTarget * aEventTarget)1193 nsIDocument::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget)
1194     : nsExpirationTracker<SelectorCacheKey, 4>(
1195           1000, "nsIDocument::SelectorCache", aEventTarget) {}
1196 
~SelectorCache()1197 nsIDocument::SelectorCache::~SelectorCache() { AgeAllGenerations(); }
1198 
Reset()1199 void nsIDocument::SelectorCache::SelectorList::Reset() {
1200   if (mIsServo) {
1201     if (mServo) {
1202       Servo_SelectorList_Drop(mServo);
1203       mServo = nullptr;
1204     }
1205   } else {
1206     if (mGecko) {
1207 #ifdef MOZ_OLD_STYLE
1208       delete mGecko;
1209       mGecko = nullptr;
1210 #else
1211       MOZ_CRASH("old style system disabled");
1212 #endif
1213     }
1214   }
1215 }
1216 
1217 #ifdef MOZ_OLD_STYLE
1218 // CacheList takes ownership of aSelectorList.
CacheList(const nsAString & aSelector,mozilla::UniquePtr<nsCSSSelectorList> && aSelectorList)1219 void nsIDocument::SelectorCache::CacheList(
1220     const nsAString& aSelector,
1221     mozilla::UniquePtr<nsCSSSelectorList>&& aSelectorList) {
1222   MOZ_ASSERT(NS_IsMainThread());
1223   SelectorCacheKey* key = new SelectorCacheKey(aSelector);
1224   mTable.Put(key->mKey, SelectorList(Move(aSelectorList)));
1225   AddObject(key);
1226 }
1227 #endif
1228 
CacheList(const nsAString & aSelector,UniquePtr<RawServoSelectorList> && aSelectorList)1229 void nsIDocument::SelectorCache::CacheList(
1230     const nsAString& aSelector,
1231     UniquePtr<RawServoSelectorList>&& aSelectorList) {
1232   MOZ_ASSERT(NS_IsMainThread());
1233   SelectorCacheKey* key = new SelectorCacheKey(aSelector);
1234   mTable.Put(key->mKey, SelectorList(Move(aSelectorList)));
1235   AddObject(key);
1236 }
1237 
NotifyExpired(SelectorCacheKey * aSelector)1238 void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector) {
1239   MOZ_ASSERT(NS_IsMainThread());
1240   MOZ_ASSERT(aSelector);
1241 
1242   // There is no guarantee that this method won't be re-entered when selector
1243   // matching is ongoing because "memory-pressure" could be notified immediately
1244   // when OOM happens according to the design of nsExpirationTracker.
1245   // The perfect solution is to delete the |aSelector| and its nsCSSSelectorList
1246   // in mTable asynchronously.
1247   // We remove these objects synchronously for now because NotifiyExpired() will
1248   // never be triggered by "memory-pressure" which is not implemented yet in
1249   // the stage 2 of mozalloc_handle_oom().
1250   // Once these objects are removed asynchronously, we should update the warning
1251   // added in mozalloc_handle_oom() as well.
1252   RemoveObject(aSelector);
1253   mTable.Remove(aSelector->mKey);
1254   delete aSelector;
1255 }
1256 
1257 struct nsIDocument::FrameRequest {
FrameRequestnsIDocument::FrameRequest1258   FrameRequest(FrameRequestCallback& aCallback, int32_t aHandle)
1259       : mCallback(&aCallback), mHandle(aHandle) {}
1260 
1261   // Conversion operator so that we can append these to a
1262   // FrameRequestCallbackList
operator const RefPtr<FrameRequestCallback>&nsIDocument::FrameRequest1263   operator const RefPtr<FrameRequestCallback>&() const { return mCallback; }
1264 
1265   // Comparator operators to allow RemoveElementSorted with an
1266   // integer argument on arrays of FrameRequest
operator ==nsIDocument::FrameRequest1267   bool operator==(int32_t aHandle) const { return mHandle == aHandle; }
operator <nsIDocument::FrameRequest1268   bool operator<(int32_t aHandle) const { return mHandle < aHandle; }
1269 
1270   RefPtr<FrameRequestCallback> mCallback;
1271   int32_t mHandle;
1272 };
1273 
1274 static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
1275 
1276 // ==================================================================
1277 // =
1278 // ==================================================================
nsIDocument()1279 nsIDocument::nsIDocument()
1280     : nsINode(nullNodeInfo),
1281       DocumentOrShadowRoot(*this),
1282       mReferrerPolicySet(false),
1283       mReferrerPolicy(mozilla::net::RP_Unset),
1284       mBlockAllMixedContent(false),
1285       mBlockAllMixedContentPreloads(false),
1286       mUpgradeInsecureRequests(false),
1287       mUpgradeInsecurePreloads(false),
1288       mCharacterSet(WINDOWS_1252_ENCODING),
1289       mCharacterSetSource(0),
1290       mParentDocument(nullptr),
1291       mCachedRootElement(nullptr),
1292       mNodeInfoManager(nullptr),
1293       mBidiEnabled(false),
1294       mMathMLEnabled(false),
1295       mIsInitialDocumentInWindow(false),
1296       mIgnoreDocGroupMismatches(false),
1297       mLoadedAsData(false),
1298       mLoadedAsInteractiveData(false),
1299       mMayStartLayout(true),
1300       mHaveFiredTitleChange(false),
1301       mIsShowing(false),
1302       mVisible(true),
1303       mHasReferrerPolicyCSP(false),
1304       mRemovedFromDocShell(false),
1305       // mAllowDNSPrefetch starts true, so that we can always reliably && it
1306       // with various values that might disable it.  Since we never prefetch
1307       // unless we get a window, and in that case the docshell value will get
1308       // &&-ed in, this is safe.
1309       mAllowDNSPrefetch(true),
1310       mIsStaticDocument(false),
1311       mCreatingStaticClone(false),
1312       mInUnlinkOrDeletion(false),
1313       mHasHadScriptHandlingObject(false),
1314       mIsBeingUsedAsImage(false),
1315       mIsSyntheticDocument(false),
1316       mHasLinksToUpdateRunnable(false),
1317       mFlushingPendingLinkUpdates(false),
1318       mMayHaveDOMMutationObservers(false),
1319       mMayHaveAnimationObservers(false),
1320       mHasMixedActiveContentLoaded(false),
1321       mHasMixedActiveContentBlocked(false),
1322       mHasMixedDisplayContentLoaded(false),
1323       mHasMixedDisplayContentBlocked(false),
1324       mHasMixedContentObjectSubrequest(false),
1325       mHasCSP(false),
1326       mHasUnsafeEvalCSP(false),
1327       mHasUnsafeInlineCSP(false),
1328       mHasTrackingContentBlocked(false),
1329       mHasTrackingContentLoaded(false),
1330       mBFCacheDisallowed(false),
1331       mHasHadDefaultView(false),
1332       mStyleSheetChangeEventsEnabled(false),
1333       mIsSrcdocDocument(false),
1334       mDidDocumentOpen(false),
1335       mHasDisplayDocument(false),
1336       mFontFaceSetDirty(true),
1337       mGetUserFontSetCalled(false),
1338       mDidFireDOMContentLoaded(true),
1339       mHasScrollLinkedEffect(false),
1340       mFrameRequestCallbacksScheduled(false),
1341       mIsTopLevelContentDocument(false),
1342       mIsContentDocument(false),
1343       mDidCallBeginLoad(false),
1344       mBufferingCSPViolations(false),
1345       mAllowPaymentRequest(false),
1346       mEncodingMenuDisabled(false),
1347       mIsShadowDOMEnabled(false),
1348       mIsSVGGlyphsDocument(false),
1349       mAllowUnsafeHTML(false),
1350       mInDestructor(false),
1351       mIsGoingAway(false),
1352       mInXBLUpdate(false),
1353       mNeedsReleaseAfterStackRefCntRelease(false),
1354       mStyleSetFilled(false),
1355       mSSApplicableStateNotificationPending(false),
1356       mIsScopedStyleEnabled(eScopedStyle_Unknown),
1357       mCompatMode(eCompatibility_FullStandards),
1358       mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
1359       mStyleBackendType(StyleBackendType::None),
1360 #ifdef MOZILLA_INTERNAL_API
1361       mVisibilityState(dom::VisibilityState::Hidden),
1362 #else
1363       mDummy(0),
1364 #endif
1365       mType(eUnknown),
1366       mDefaultElementType(0),
1367       mAllowXULXBL(eTriUnset),
1368       mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
1369       mSandboxFlags(0),
1370       mPartID(0),
1371       mMarkedCCGeneration(0),
1372       mPresShell(nullptr),
1373       mSubtreeModifiedDepth(0),
1374       mEventsSuppressed(0),
1375       mIgnoreDestructiveWritesCounter(0),
1376       mFrameRequestCallbackCounter(0),
1377       mStaticCloneCount(0),
1378       mWindow(nullptr),
1379       mBFCacheEntry(nullptr),
1380       mInSyncOperationCount(0),
1381       mBlockDOMContentLoaded(0),
1382       mUseCounters(0),
1383       mChildDocumentUseCounters(0),
1384       mNotifiedPageForUseCounter(0),
1385       mIncCounters(),
1386       mUserHasInteracted(false),
1387       mUserHasActivatedInteraction(false),
1388       mStackRefCnt(0),
1389       mUpdateNestLevel(0),
1390       mServoRestyleRootDirtyBits(0),
1391       mThrowOnDynamicMarkupInsertionCounter(0),
1392       mIgnoreOpensDuringUnloadCounter(0) {
1393   SetIsInDocument();
1394   for (auto& cnt : mIncCounters) {
1395     cnt = 0;
1396   }
1397 }
1398 
nsDocument(const char * aContentType)1399 nsDocument::nsDocument(const char* aContentType)
1400     : nsIDocument(),
1401       mSubDocuments(nullptr),
1402       mFlashClassification(FlashClassification::Unclassified),
1403       mHeaderData(nullptr),
1404       mMayHaveTitleElement(false),
1405       mHasWarnedAboutBoxObjects(false),
1406       mDelayFrameLoaderInitialization(false),
1407       mSynchronousDOMContentLoaded(false),
1408       mParserAborted(false),
1409       mCurrentOrientationAngle(0),
1410       mCurrentOrientationType(OrientationType::Portrait_primary),
1411       mReportedUseCounters(false),
1412       mPendingFullscreenRequests(0),
1413       mXMLDeclarationBits(0),
1414       mBoxObjectTable(nullptr),
1415       mOnloadBlockCount(0),
1416       mAsyncOnloadBlockCount(0)
1417 #ifdef DEBUG
1418       ,
1419       mStyledLinksCleared(false)
1420 #endif
1421       ,
1422       mPreloadPictureDepth(0),
1423       mScrolledToRefAlready(0),
1424       mChangeScrollPosWhenScrollingToRef(0),
1425       mViewportType(Unknown),
1426       mValidWidth(false),
1427       mValidHeight(false),
1428       mAutoSize(false),
1429       mAllowZoom(false),
1430       mAllowDoubleTapZoom(false),
1431       mValidScaleFloat(false),
1432       mValidMaxScale(false),
1433       mScaleStrEmpty(false),
1434       mWidthStrEmpty(false),
1435       mMaybeServiceWorkerControlled(false)
1436 #ifdef DEBUG
1437       ,
1438       mWillReparent(false)
1439 #endif
1440       ,
1441       mDOMLoadingSet(false),
1442       mDOMInteractiveSet(false),
1443       mDOMCompleteSet(false),
1444       mAutoFocusFired(false) {
1445   SetContentTypeInternal(nsDependentCString(aContentType));
1446 
1447   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
1448 
1449   // Start out mLastStyleSheetSet as null, per spec
1450   SetDOMStringToNull(mLastStyleSheetSet);
1451 
1452   // void state used to differentiate an empty source from an unselected source
1453   mPreloadPictureFoundSource.SetIsVoid(true);
1454   // For determining if this is a flash document which should be
1455   // blocked based on its principal.
1456   mPrincipalFlashClassifier = new PrincipalFlashClassifier();
1457 }
1458 
ClearAllBoxObjects()1459 void nsDocument::ClearAllBoxObjects() {
1460   if (mBoxObjectTable) {
1461     for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1462       nsPIBoxObject* boxObject = iter.UserData();
1463       if (boxObject) {
1464         boxObject->Clear();
1465       }
1466     }
1467     delete mBoxObjectTable;
1468     mBoxObjectTable = nullptr;
1469   }
1470 }
1471 
~nsIDocument()1472 nsIDocument::~nsIDocument() {
1473   MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
1474              "must not have media query lists left");
1475 
1476   if (mNodeInfoManager) {
1477     mNodeInfoManager->DropDocumentReference();
1478   }
1479 
1480   if (mDocGroup) {
1481     mDocGroup->RemoveDocument(this);
1482   }
1483 
1484   UnlinkOriginalDocumentIfStatic();
1485 }
1486 
IsAboutPage() const1487 bool nsDocument::IsAboutPage() const {
1488   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
1489   nsCOMPtr<nsIURI> uri;
1490   principal->GetURI(getter_AddRefs(uri));
1491   bool isAboutScheme = true;
1492   if (uri) {
1493     uri->SchemeIs("about", &isAboutScheme);
1494   }
1495   return isAboutScheme;
1496 }
1497 
~nsDocument()1498 nsDocument::~nsDocument() {
1499   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
1500 
1501   NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1502 
1503   if (IsTopLevelContentDocument()) {
1504     // don't report for about: pages
1505     if (!IsAboutPage()) {
1506       // Record the page load
1507       uint32_t pageLoaded = 1;
1508       Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1509       // Record the mixed content status of the docshell in Telemetry
1510       enum {
1511         NO_MIXED_CONTENT = 0,  // There is no Mixed Content on the page
1512         MIXED_DISPLAY_CONTENT =
1513             1,  // The page attempted to load Mixed Display Content
1514         MIXED_ACTIVE_CONTENT =
1515             2,  // The page attempted to load Mixed Active Content
1516         MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3  // The page attempted to load
1517                                               // Mixed Display & Mixed Active
1518                                               // Content
1519       };
1520 
1521       bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1522       bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1523 
1524       bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1525       bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1526 
1527       bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1528       bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1529 
1530       uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1531       if (hasMixedDisplay && hasMixedActive) {
1532         mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1533       } else if (hasMixedActive) {
1534         mixedContentLevel = MIXED_ACTIVE_CONTENT;
1535       } else if (hasMixedDisplay) {
1536         mixedContentLevel = MIXED_DISPLAY_CONTENT;
1537       }
1538       Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1539 
1540       // record mixed object subrequest telemetry
1541       if (mHasMixedContentObjectSubrequest) {
1542         /* mixed object subrequest loaded on page*/
1543         Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 1);
1544       } else {
1545         /* no mixed object subrequests loaded on page*/
1546         Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 0);
1547       }
1548 
1549       // record CSP telemetry on this document
1550       if (mHasCSP) {
1551         Accumulate(Telemetry::CSP_DOCUMENTS_COUNT, 1);
1552         Accumulate(Telemetry::CSP_REFERRER_DIRECTIVE, mHasReferrerPolicyCSP);
1553       }
1554       if (mHasUnsafeInlineCSP) {
1555         Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT, 1);
1556       }
1557       if (mHasUnsafeEvalCSP) {
1558         Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT, 1);
1559       }
1560 
1561       if (MOZ_UNLIKELY(GetMathMLEnabled())) {
1562         ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT, 1);
1563       }
1564     }
1565   }
1566 
1567   ReportUseCounters();
1568 
1569   mInDestructor = true;
1570   mInUnlinkOrDeletion = true;
1571 
1572   mozilla::DropJSObjects(this);
1573 
1574   // Clear mObservers to keep it in sync with the mutationobserver list
1575   mObservers.Clear();
1576 
1577   mIntersectionObservers.Clear();
1578 
1579   if (mStyleSheetSetList) {
1580     mStyleSheetSetList->Disconnect();
1581   }
1582 
1583   if (mAnimationController) {
1584     mAnimationController->Disconnect();
1585   }
1586 
1587   MOZ_ASSERT(mTimelines.isEmpty());
1588 
1589   mParentDocument = nullptr;
1590 
1591   // Kill the subdocument map, doing this will release its strong
1592   // references, if any.
1593   delete mSubDocuments;
1594   mSubDocuments = nullptr;
1595 
1596   // Destroy link map now so we don't waste time removing
1597   // links one by one
1598   DestroyElementMaps();
1599 
1600   nsAutoScriptBlocker scriptBlocker;
1601 
1602   // Invalidate cached array of child nodes
1603   InvalidateChildNodes();
1604 
1605   for (uint32_t indx = mChildren.ChildCount(); indx-- != 0;) {
1606     mChildren.ChildAt(indx)->UnbindFromTree();
1607     mChildren.RemoveChildAt(indx);
1608   }
1609   mFirstChild = nullptr;
1610   mCachedRootElement = nullptr;
1611 
1612   // Let the stylesheets know we're going away
1613   for (StyleSheet* sheet : mStyleSheets) {
1614     sheet->ClearAssociatedDocument();
1615   }
1616   for (auto& sheets : mAdditionalSheets) {
1617     for (StyleSheet* sheet : sheets) {
1618       sheet->ClearAssociatedDocument();
1619     }
1620   }
1621   if (mAttrStyleSheet) {
1622     mAttrStyleSheet->SetOwningDocument(nullptr);
1623   }
1624   // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
1625 
1626   if (mListenerManager) {
1627     mListenerManager->Disconnect();
1628     UnsetFlags(NODE_HAS_LISTENERMANAGER);
1629   }
1630 
1631   if (mScriptLoader) {
1632     mScriptLoader->DropDocumentReference();
1633   }
1634 
1635   if (mCSSLoader) {
1636     // Could be null here if Init() failed or if we have been unlinked.
1637     mCSSLoader->DropDocumentReference();
1638   }
1639 
1640   if (mStyleImageLoader) {
1641     mStyleImageLoader->DropDocumentReference();
1642   }
1643 
1644   delete mHeaderData;
1645 
1646   ClearAllBoxObjects();
1647 
1648   mPendingTitleChangeEvent.Revoke();
1649 
1650   mPlugins.Clear();
1651 }
1652 
NS_INTERFACE_TABLE_HEAD(nsDocument)1653 NS_INTERFACE_TABLE_HEAD(nsDocument)
1654   NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
1655   NS_INTERFACE_TABLE_BEGIN
1656     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1657     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1658     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1659     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument)
1660     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode)
1661     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1662     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
1663     NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1664     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1665     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1666     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1667     NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1668   NS_INTERFACE_TABLE_END
1669   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1670 NS_INTERFACE_MAP_END
1671 
1672 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1673 NS_IMETHODIMP_(MozExternalRefCountType)
1674 nsDocument::Release() {
1675   NS_PRECONDITION(0 != mRefCnt, "dup release");
1676   NS_ASSERT_OWNINGTHREAD(nsDocument);
1677   nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1678   bool shouldDelete = false;
1679   nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1680   NS_LOG_RELEASE(this, count, "nsDocument");
1681   if (count == 0) {
1682     if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1683       mNeedsReleaseAfterStackRefCntRelease = true;
1684       NS_ADDREF_THIS();
1685       return mRefCnt.get();
1686     }
1687     mRefCnt.incr(base);
1688     nsNodeUtils::LastRelease(this);
1689     mRefCnt.decr(base);
1690     if (shouldDelete) {
1691       mRefCnt.stabilizeForDeletion();
1692       DeleteCycleCollectable();
1693     }
1694   }
1695   return count;
1696 }
1697 
NS_IMETHODIMP_(void)1698 NS_IMETHODIMP_(void)
1699 nsDocument::DeleteCycleCollectable() { delete this; }
1700 
1701 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1702   if (Element::CanSkip(tmp, aRemovingAllowed)) {
1703     EventListenerManager* elm = tmp->GetExistingListenerManager();
1704     if (elm) {
1705       elm->MarkForCC();
1706     }
1707     return true;
1708   }
1709 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1710 
1711 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1712   return Element::CanSkipInCC(tmp);
1713 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1714 
1715 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1716   return Element::CanSkipThis(tmp);
1717 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1718 
1719 static const char* kNSURIs[] = {"([none])", "(xmlns)", "(xml)", "(xhtml)",
1720                                 "(XLink)",  "(XSLT)",  "(XBL)", "(MathML)",
1721                                 "(RDF)",    "(XUL)"};
1722 
1723 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1724   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1725     char name[512];
1726     nsAutoCString loadedAsData;
1727     if (tmp->IsLoadedAsData()) {
1728       loadedAsData.AssignLiteral("data");
1729     } else {
1730       loadedAsData.AssignLiteral("normal");
1731     }
1732     uint32_t nsid = tmp->GetDefaultNamespaceID();
1733     nsAutoCString uri;
1734     if (tmp->mDocumentURI) uri = tmp->mDocumentURI->GetSpecOrDefault();
1735     if (nsid < ArrayLength(kNSURIs)) {
1736       SprintfLiteral(name, "nsDocument %s %s %s", loadedAsData.get(),
1737                      kNSURIs[nsid], uri.get());
1738     } else {
1739       SprintfLiteral(name, "nsDocument %s %s", loadedAsData.get(), uri.get());
1740     }
1741     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1742   } else {
1743     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1744   }
1745 
1746   if (!nsINode::Traverse(tmp, cb)) {
1747     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1748   }
1749 
1750   if (tmp->mMaybeEndOutermostXBLUpdateRunner) {
1751     // The cached runnable keeps a reference to the document object..
1752     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1753         cb, "mMaybeEndOutermostXBLUpdateRunner.mObj");
1754     cb.NoteXPCOMChild(ToSupports(tmp));
1755   }
1756 
1757   for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
1758     iter.Get()->Traverse(&cb);
1759   }
1760 
1761   tmp->mExternalResourceMap.Traverse(&cb);
1762 
1763   // Traverse the mChildren nsAttrAndChildArray.
1764   for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1765     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1766     cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1767   }
1768 
1769   // Traverse all nsIDocument pointer members.
1770   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1771   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1772   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
1773   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
1774   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAboutCapabilities)
1775 
1776   // Traverse all nsDocument nsCOMPtrs.
1777   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1778   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1779   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1780   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1781   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1782   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1783 
1784   for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
1785     nsRadioGroupStruct* radioGroup = iter.UserData();
1786     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1787         cb, "mRadioGroups entry->mSelectedRadioButton");
1788     cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
1789 
1790     uint32_t i, count = radioGroup->mRadioButtons.Count();
1791     for (i = 0; i < count; ++i) {
1792       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1793           cb, "mRadioGroups entry->mRadioButtons[i]");
1794       cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
1795     }
1796   }
1797 
1798   // The boxobject for an element will only exist as long as it's in the
1799   // document, so we'll traverse the table here instead of from the element.
1800   if (tmp->mBoxObjectTable) {
1801     for (auto iter = tmp->mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1802       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mBoxObjectTable entry");
1803       cb.NoteXPCOMChild(iter.UserData());
1804     }
1805   }
1806 
1807   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
1808 #ifdef MOZ_OLD_STYLE
1809   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
1810 #endif
1811   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
1812   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
1813   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
1814   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
1815   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
1816   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
1817   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
1818   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
1819   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
1820   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
1821   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
1822   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
1823   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
1824   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
1825 
1826   // Traverse all our nsCOMArrays.
1827   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
1828   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
1829   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
1830 
1831   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
1832     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
1833     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
1834   }
1835 
1836   // Traverse animation components
1837   if (tmp->mAnimationController) {
1838     tmp->mAnimationController->Traverse(&cb);
1839   }
1840 
1841   if (tmp->mSubDocuments) {
1842     for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
1843       auto entry = static_cast<SubDocMapEntry*>(iter.Get());
1844 
1845       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSubDocuments entry->mKey");
1846       cb.NoteXPCOMChild(entry->mKey);
1847       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1848                                          "mSubDocuments entry->mSubDocument");
1849       cb.NoteXPCOMChild(entry->mSubDocument);
1850     }
1851   }
1852 
1853   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
1854 
1855   // We own only the items in mDOMMediaQueryLists that have listeners;
1856   // this reference is managed by their AddListener and RemoveListener
1857   // methods.
1858   for (auto mql : tmp->mDOMMediaQueryLists) {
1859     if (mql->HasListeners()) {
1860       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
1861       cb.NoteXPCOMChild(mql);
1862     }
1863   }
1864 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1865 
1866 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
1867 
1868 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDocument)
1869 
1870 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
1871   tmp->mInUnlinkOrDeletion = true;
1872 
1873   // Clear out our external resources
1874   tmp->mExternalResourceMap.Shutdown();
1875 
1876   nsAutoScriptBlocker scriptBlocker;
1877 
1878   nsINode::Unlink(tmp);
1879 
1880   // Unlink the mChildren nsAttrAndChildArray.
1881   uint32_t childCount = tmp->mChildren.ChildCount();
1882   if (childCount) {
1883     while (childCount-- > 0) {
1884       // Hold a strong ref to the node when we remove it, because we may be
1885       // the last reference to it.  We need to call TakeChildAt() and
1886       // update mFirstChild before calling UnbindFromTree, since this last
1887       // can notify various observers and they should really see consistent
1888       // tree state.
1889       // If this code changes, change the corresponding code in
1890       // FragmentOrElement's unlink impl and ContentUnbinder::UnbindSubtree.
1891       nsCOMPtr<nsIContent> child = tmp->mChildren.TakeChildAt(childCount);
1892       if (childCount == 0) {
1893         tmp->mFirstChild = nullptr;
1894       }
1895       child->UnbindFromTree();
1896     }
1897   }
1898   tmp->mFirstChild = nullptr;
1899 
1900   tmp->UnlinkOriginalDocumentIfStatic();
1901 
1902   tmp->mCachedRootElement = nullptr;  // Avoid a dangling pointer
1903   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
1904   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
1905   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaybeEndOutermostXBLUpdateRunner)
1906   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
1907   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
1908   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
1909   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
1910   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
1911   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
1912   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
1913   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
1914   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
1915   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
1916   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAboutCapabilities)
1917 
1918   tmp->mParentDocument = nullptr;
1919 
1920   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
1921 
1922   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
1923 
1924   tmp->ClearAllBoxObjects();
1925 
1926   if (tmp->mListenerManager) {
1927     tmp->mListenerManager->Disconnect();
1928     tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
1929     tmp->mListenerManager = nullptr;
1930   }
1931 
1932   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
1933 
1934   if (tmp->mStyleSheetSetList) {
1935     tmp->mStyleSheetSetList->Disconnect();
1936     tmp->mStyleSheetSetList = nullptr;
1937   }
1938 
1939   delete tmp->mSubDocuments;
1940   tmp->mSubDocuments = nullptr;
1941 
1942   tmp->mFrameRequestCallbacks.Clear();
1943   MOZ_RELEASE_ASSERT(!tmp->mFrameRequestCallbacksScheduled,
1944                      "How did we get here without our presshell going away "
1945                      "first?");
1946 
1947   tmp->mRadioGroups.Clear();
1948 
1949   // nsDocument has a pretty complex destructor, so we're going to
1950   // assume that *most* cycles you actually want to break somewhere
1951   // else, and not unlink an awful lot here.
1952 
1953   tmp->mIdentifierMap.Clear();
1954   tmp->mExpandoAndGeneration.OwnerUnlinked();
1955 
1956   if (tmp->mAnimationController) {
1957     tmp->mAnimationController->Unlink();
1958   }
1959 
1960   tmp->mPendingTitleChangeEvent.Revoke();
1961 
1962   if (tmp->mCSSLoader) {
1963     tmp->mCSSLoader->DropDocumentReference();
1964     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
1965   }
1966 
1967   // We own only the items in mDOMMediaQueryLists that have listeners;
1968   // this reference is managed by their AddListener and RemoveListener
1969   // methods.
1970   for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
1971     MediaQueryList* next = mql->getNext();
1972     mql->Disconnect();
1973     mql = next;
1974   }
1975 
1976   tmp->mInUnlinkOrDeletion = false;
1977 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1978 
Init()1979 nsresult nsDocument::Init() {
1980   if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
1981     return NS_ERROR_ALREADY_INITIALIZED;
1982   }
1983 
1984   // Force initialization.
1985   nsINode::nsSlots* slots = Slots();
1986 
1987   // Prepend self as mutation-observer whether we need it or not (some
1988   // subclasses currently do, other don't). This is because the code in
1989   // nsNodeUtils always notifies the first observer first, expecting the
1990   // first observer to be the document.
1991   NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(
1992                      static_cast<nsIMutationObserver*>(this)),
1993                  NS_ERROR_OUT_OF_MEMORY);
1994 
1995   mOnloadBlocker = new nsOnloadBlocker();
1996   mCSSLoader = new mozilla::css::Loader(this);
1997   // Assume we're not quirky, until we know otherwise
1998   mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
1999 
2000   mStyleImageLoader = new mozilla::css::ImageLoader(this);
2001 
2002   mNodeInfoManager = new nsNodeInfoManager();
2003   nsresult rv = mNodeInfoManager->Init(this);
2004   NS_ENSURE_SUCCESS(rv, rv);
2005 
2006   // mNodeInfo keeps NodeInfoManager alive!
2007   mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2008   NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2009   MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
2010              "Bad NodeType in aNodeInfo");
2011 
2012   NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2013 
2014   UpdateStyleBackendType();
2015 
2016   // Set this when document is initialized and value stays the same for the
2017   // lifetime of the document.
2018   mIsShadowDOMEnabled = mStyleBackendType == StyleBackendType::Servo &&
2019                         nsContentUtils::IsShadowDOMEnabled();
2020 
2021   // If after creation the owner js global is not set for a document
2022   // we use the default compartment for this document, instead of creating
2023   // wrapper in some random compartment when the document is exposed to js
2024   // via some events.
2025   nsCOMPtr<nsIGlobalObject> global =
2026       xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2027   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2028   mScopeObject = do_GetWeakReference(global);
2029   MOZ_ASSERT(mScopeObject);
2030 
2031   mScriptLoader = new dom::ScriptLoader(this);
2032 
2033   mozilla::HoldJSObjects(this);
2034 
2035   return NS_OK;
2036 }
2037 
DeleteAllProperties()2038 void nsIDocument::DeleteAllProperties() {
2039   for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2040     PropertyTable(i)->DeleteAllProperties();
2041   }
2042 }
2043 
DeleteAllPropertiesFor(nsINode * aNode)2044 void nsIDocument::DeleteAllPropertiesFor(nsINode* aNode) {
2045   for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2046     PropertyTable(i)->DeleteAllPropertiesFor(aNode);
2047   }
2048 }
2049 
GetExtraPropertyTable(uint16_t aCategory)2050 nsPropertyTable* nsIDocument::GetExtraPropertyTable(uint16_t aCategory) {
2051   NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
2052   while (aCategory >= mExtraPropertyTables.Length() + 1) {
2053     mExtraPropertyTables.AppendElement(new nsPropertyTable());
2054   }
2055   return mExtraPropertyTables[aCategory - 1];
2056 }
2057 
IsVisibleConsideringAncestors() const2058 bool nsIDocument::IsVisibleConsideringAncestors() const {
2059   const nsIDocument* parent = this;
2060   do {
2061     if (!parent->IsVisible()) {
2062       return false;
2063     }
2064   } while ((parent = parent->GetParentDocument()));
2065 
2066   return true;
2067 }
2068 
Reset(nsIChannel * aChannel,nsILoadGroup * aLoadGroup)2069 void nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
2070   nsCOMPtr<nsIURI> uri;
2071   nsCOMPtr<nsIPrincipal> principal;
2072   if (aChannel) {
2073     // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2074     // nsScriptSecurityManager::GetChannelResultPrincipal.
2075     // Note: this should match nsDocShell::OnLoadingSite
2076     NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2077 
2078     bool isWyciwyg = false;
2079     uri->SchemeIs("wyciwyg", &isWyciwyg);
2080     if (isWyciwyg) {
2081       nsCOMPtr<nsIURI> cleanURI;
2082       nsresult rv =
2083           nsContentUtils::RemoveWyciwygScheme(uri, getter_AddRefs(cleanURI));
2084       if (NS_SUCCEEDED(rv)) {
2085         uri = cleanURI;
2086       }
2087     }
2088 
2089     nsIScriptSecurityManager* securityManager =
2090         nsContentUtils::GetSecurityManager();
2091     if (securityManager) {
2092       securityManager->GetChannelResultPrincipal(aChannel,
2093                                                  getter_AddRefs(principal));
2094     }
2095   }
2096 
2097   principal = MaybeDowngradePrincipal(principal);
2098 
2099   ResetToURI(uri, aLoadGroup, principal);
2100 
2101   // Note that, since mTiming does not change during a reset, the
2102   // navigationStart time remains unchanged and therefore any future new
2103   // timeline will have the same global clock time as the old one.
2104   mDocumentTimeline = nullptr;
2105 
2106   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2107   if (bag) {
2108     nsCOMPtr<nsIURI> baseURI;
2109     bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2110                                 NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2111     if (baseURI) {
2112       mDocumentBaseURI = baseURI;
2113       mChromeXHRDocBaseURI = nullptr;
2114     }
2115   }
2116 
2117   mChannel = aChannel;
2118 }
2119 
ResetToURI(nsIURI * aURI,nsILoadGroup * aLoadGroup,nsIPrincipal * aPrincipal)2120 void nsDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
2121                             nsIPrincipal* aPrincipal) {
2122   NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
2123 
2124   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2125           ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
2126 
2127   mSecurityInfo = nullptr;
2128 
2129   mDocumentLoadGroup = nullptr;
2130 
2131   // Delete references to sub-documents and kill the subdocument map,
2132   // if any. It holds strong references
2133   delete mSubDocuments;
2134   mSubDocuments = nullptr;
2135 
2136   // Destroy link map now so we don't waste time removing
2137   // links one by one
2138   DestroyElementMaps();
2139 
2140   bool oldVal = mInUnlinkOrDeletion;
2141   mInUnlinkOrDeletion = true;
2142   uint32_t count = mChildren.ChildCount();
2143   {  // Scope for update
2144     MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true);
2145 
2146     // Invalidate cached array of child nodes
2147     InvalidateChildNodes();
2148 
2149     for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
2150       nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
2151 
2152       nsIContent* previousSibling = content->GetPreviousSibling();
2153 
2154       if (nsINode::GetFirstChild() == content) {
2155         mFirstChild = content->GetNextSibling();
2156       }
2157       mChildren.RemoveChildAt(i);
2158       if (content == mCachedRootElement) {
2159         // Immediately clear mCachedRootElement, now that it's been removed
2160         // from mChildren, so that GetRootElement() will stop returning this
2161         // now-stale value.
2162         mCachedRootElement = nullptr;
2163       }
2164       nsNodeUtils::ContentRemoved(this, content, previousSibling);
2165       content->UnbindFromTree();
2166     }
2167     MOZ_ASSERT(!mCachedRootElement,
2168                "After removing all children, there should be no root elem");
2169   }
2170   mInUnlinkOrDeletion = oldVal;
2171 
2172   // Reset our stylesheets
2173   ResetStylesheetsToURI(aURI);
2174 
2175   // Release the listener manager
2176   if (mListenerManager) {
2177     mListenerManager->Disconnect();
2178     mListenerManager = nullptr;
2179   }
2180 
2181   // Release the stylesheets list.
2182   mDOMStyleSheets = nullptr;
2183 
2184   // Release our principal after tearing down the document, rather than before.
2185   // This ensures that, during teardown, the document and the dying window
2186   // (which already nulled out its document pointer and cached the principal)
2187   // have matching principals.
2188   SetPrincipal(nullptr);
2189 
2190   // Clear the original URI so SetDocumentURI sets it.
2191   mOriginalURI = nullptr;
2192 
2193   SetDocumentURI(aURI);
2194   mChromeXHRDocURI = nullptr;
2195   // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2196   // mDocumentURI.
2197   mDocumentBaseURI = nullptr;
2198   mChromeXHRDocBaseURI = nullptr;
2199 
2200   if (aLoadGroup) {
2201     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2202     // there was an assertion here that aLoadGroup was not null.  This
2203     // is no longer valid: nsDocShell::SetDocument does not create a
2204     // load group, and it works just fine
2205 
2206     // XXXbz what does "just fine" mean exactly?  And given that there
2207     // is no nsDocShell::SetDocument, what is this talking about?
2208 
2209     if (IsContentDocument()) {
2210       // Inform the associated request context about this load start so
2211       // any of its internal load progress flags gets reset.
2212       nsCOMPtr<nsIRequestContextService> rcsvc =
2213           do_GetService("@mozilla.org/network/request-context-service;1");
2214       if (rcsvc) {
2215         nsCOMPtr<nsIRequestContext> rc;
2216         rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
2217         if (rc) {
2218           rc->BeginLoad();
2219         }
2220       }
2221     }
2222   }
2223 
2224   mLastModified.Truncate();
2225   // XXXbz I guess we're assuming that the caller will either pass in
2226   // a channel with a useful type or call SetContentType?
2227   SetContentTypeInternal(EmptyCString());
2228   mContentLanguage.Truncate();
2229   mBaseTarget.Truncate();
2230   mReferrer.Truncate();
2231 
2232   mXMLDeclarationBits = 0;
2233 
2234   // Now get our new principal
2235   if (aPrincipal) {
2236     SetPrincipal(aPrincipal);
2237   } else {
2238     nsIScriptSecurityManager* securityManager =
2239         nsContentUtils::GetSecurityManager();
2240     if (securityManager) {
2241       nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2242 
2243       if (!loadContext && aLoadGroup) {
2244         nsCOMPtr<nsIInterfaceRequestor> cbs;
2245         aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2246         loadContext = do_GetInterface(cbs);
2247       }
2248 
2249       MOZ_ASSERT(loadContext,
2250                  "must have a load context or pass in an explicit principal");
2251 
2252       nsCOMPtr<nsIPrincipal> principal;
2253       nsresult rv = securityManager->GetLoadContextCodebasePrincipal(
2254           mDocumentURI, loadContext, getter_AddRefs(principal));
2255       if (NS_SUCCEEDED(rv)) {
2256         SetPrincipal(principal);
2257       }
2258     }
2259   }
2260 
2261   // Refresh the principal on the compartment.
2262   if (nsPIDOMWindowInner* win = GetInnerWindow()) {
2263     nsGlobalWindowInner::Cast(win)->RefreshCompartmentPrincipal();
2264   }
2265 }
2266 
MaybeDowngradePrincipal(nsIPrincipal * aPrincipal)2267 already_AddRefed<nsIPrincipal> nsDocument::MaybeDowngradePrincipal(
2268     nsIPrincipal* aPrincipal) {
2269   if (!aPrincipal) {
2270     return nullptr;
2271   }
2272 
2273   // We can't load a document with an expanded principal. If we're given one,
2274   // automatically downgrade it to the last principal it subsumes (which is the
2275   // extension principal, in the case of extension content scripts).
2276   auto* basePrin = BasePrincipal::Cast(aPrincipal);
2277   if (basePrin->Is<ExpandedPrincipal>()) {
2278     MOZ_DIAGNOSTIC_ASSERT(false,
2279                           "Should never try to create a document with "
2280                           "an expanded principal");
2281 
2282     auto* expanded = basePrin->As<ExpandedPrincipal>();
2283     return do_AddRef(expanded->WhiteList().LastElement());
2284   }
2285 
2286   if (!sChromeInContentPrefCached) {
2287     sChromeInContentPrefCached = true;
2288     Preferences::AddBoolVarCache(&sChromeInContentAllowed, kChromeInContentPref,
2289                                  false);
2290   }
2291   if (!sChromeInContentAllowed &&
2292       nsContentUtils::IsSystemPrincipal(aPrincipal)) {
2293     // We basically want the parent document here, but because this is very
2294     // early in the load, GetParentDocument() returns null, so we use the
2295     // docshell hierarchy to get this information instead.
2296     if (mDocumentContainer) {
2297       nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
2298       mDocumentContainer->GetParent(getter_AddRefs(parentDocShellItem));
2299       nsCOMPtr<nsIDocShell> parentDocShell =
2300           do_QueryInterface(parentDocShellItem);
2301       if (parentDocShell) {
2302         nsCOMPtr<nsIDocument> parentDoc;
2303         parentDoc = parentDocShell->GetDocument();
2304         if (!parentDoc ||
2305             !nsContentUtils::IsSystemPrincipal(parentDoc->NodePrincipal())) {
2306           nsCOMPtr<nsIPrincipal> nullPrincipal =
2307               do_CreateInstance("@mozilla.org/nullprincipal;1");
2308           return nullPrincipal.forget();
2309         }
2310       }
2311     }
2312   }
2313   nsCOMPtr<nsIPrincipal> principal(aPrincipal);
2314   return principal.forget();
2315 }
2316 
RemoveDocStyleSheetsFromStyleSets()2317 void nsIDocument::RemoveDocStyleSheetsFromStyleSets() {
2318   // The stylesheets should forget us
2319   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2320     sheet->ClearAssociatedDocument();
2321 
2322     if (sheet->IsApplicable()) {
2323       nsCOMPtr<nsIPresShell> shell = GetShell();
2324       if (shell) {
2325         shell->StyleSet()->RemoveDocStyleSheet(sheet);
2326       }
2327     }
2328     // XXX Tell observers?
2329   }
2330 }
2331 
RemoveStyleSheetsFromStyleSets(const nsTArray<RefPtr<StyleSheet>> & aSheets,SheetType aType)2332 void nsIDocument::RemoveStyleSheetsFromStyleSets(
2333     const nsTArray<RefPtr<StyleSheet>>& aSheets, SheetType aType) {
2334   // The stylesheets should forget us
2335   for (StyleSheet* sheet : Reversed(aSheets)) {
2336     sheet->ClearAssociatedDocument();
2337 
2338     if (sheet->IsApplicable()) {
2339       nsCOMPtr<nsIPresShell> shell = GetShell();
2340       if (shell) {
2341         shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2342       }
2343     }
2344     // XXX Tell observers?
2345   }
2346 }
2347 
ResetStylesheetsToURI(nsIURI * aURI)2348 void nsIDocument::ResetStylesheetsToURI(nsIURI* aURI) {
2349   MOZ_ASSERT(aURI);
2350 
2351   mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
2352   if (mStyleSetFilled) {
2353     // Skip removing style sheets from the style set if we know we haven't
2354     // filled the style set.  (This allows us to avoid calling
2355     // GetStyleBackendType() too early.)
2356     RemoveDocStyleSheetsFromStyleSets();
2357     RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, SheetType::Agent);
2358     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet],
2359                                    SheetType::Agent);
2360     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet],
2361                                    SheetType::User);
2362     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet],
2363                                    SheetType::Doc);
2364 
2365     if (nsStyleSheetService* sheetService =
2366             nsStyleSheetService::GetInstance()) {
2367       RemoveStyleSheetsFromStyleSets(
2368           *sheetService->AuthorStyleSheets(GetStyleBackendType()),
2369           SheetType::Doc);
2370     }
2371 
2372     mStyleSetFilled = false;
2373   }
2374 
2375   // Release all the sheets
2376   mStyleSheets.Clear();
2377   mOnDemandBuiltInUASheets.Clear();
2378   for (auto& sheets : mAdditionalSheets) {
2379     sheets.Clear();
2380   }
2381 
2382   // NOTE:  We don't release the catalog sheets.  It doesn't really matter
2383   // now, but it could in the future -- in which case not releasing them
2384   // is probably the right thing to do.
2385 
2386   // Now reset our inline style and attribute sheets.
2387   if (mAttrStyleSheet) {
2388     mAttrStyleSheet->Reset();
2389     mAttrStyleSheet->SetOwningDocument(this);
2390   } else {
2391     mAttrStyleSheet = new nsHTMLStyleSheet(this);
2392   }
2393 
2394   if (!mStyleAttrStyleSheet) {
2395     mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2396   }
2397 
2398   // Now set up our style sets
2399   nsCOMPtr<nsIPresShell> shell = GetShell();
2400   if (shell) {
2401     FillStyleSet(shell->StyleSet());
2402   }
2403 }
2404 
AppendSheetsToStyleSet(StyleSetHandle aStyleSet,const nsTArray<RefPtr<StyleSheet>> & aSheets,SheetType aType)2405 static void AppendSheetsToStyleSet(StyleSetHandle aStyleSet,
2406                                    const nsTArray<RefPtr<StyleSheet>>& aSheets,
2407                                    SheetType aType) {
2408   for (StyleSheet* sheet : Reversed(aSheets)) {
2409     aStyleSet->AppendStyleSheet(aType, sheet);
2410   }
2411 }
2412 
FillStyleSet(StyleSetHandle aStyleSet)2413 void nsIDocument::FillStyleSet(StyleSetHandle aStyleSet) {
2414   NS_PRECONDITION(aStyleSet, "Must have a style set");
2415   NS_PRECONDITION(aStyleSet->SheetCount(SheetType::Doc) == 0,
2416                   "Style set already has document sheets?");
2417 
2418   MOZ_ASSERT(!mStyleSetFilled);
2419 
2420   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2421     if (sheet->IsApplicable()) {
2422       aStyleSet->AddDocStyleSheet(sheet, this);
2423     }
2424   }
2425 
2426   if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2427     nsTArray<RefPtr<StyleSheet>>& sheets =
2428         *sheetService->AuthorStyleSheets(aStyleSet->BackendType());
2429     for (StyleSheet* sheet : sheets) {
2430       aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
2431     }
2432   }
2433 
2434   // Iterate backwards to maintain order
2435   for (StyleSheet* sheet : Reversed(mOnDemandBuiltInUASheets)) {
2436     if (sheet->IsApplicable()) {
2437       aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
2438     }
2439   }
2440 
2441   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2442                          SheetType::Agent);
2443   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2444                          SheetType::User);
2445   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2446                          SheetType::Doc);
2447 
2448   mStyleSetFilled = true;
2449 }
2450 
WarnIfSandboxIneffective(nsIDocShell * aDocShell,uint32_t aSandboxFlags,nsIChannel * aChannel)2451 static void WarnIfSandboxIneffective(nsIDocShell* aDocShell,
2452                                      uint32_t aSandboxFlags,
2453                                      nsIChannel* aChannel) {
2454   // If the document is sandboxed (via the HTML5 iframe sandbox
2455   // attribute) and both the allow-scripts and allow-same-origin
2456   // keywords are supplied, the sandboxed document can call into its
2457   // parent document and remove its sandboxing entirely - we print a
2458   // warning to the web console in this case.
2459   if (aSandboxFlags & SANDBOXED_NAVIGATION &&
2460       !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
2461       !(aSandboxFlags & SANDBOXED_ORIGIN)) {
2462     nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2463     aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2464     nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentAsItem);
2465     if (!parentDocShell) {
2466       return;
2467     }
2468 
2469     // Don't warn if our parent is not the top-level document.
2470     nsCOMPtr<nsIDocShellTreeItem> grandParentAsItem;
2471     parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem));
2472     if (grandParentAsItem) {
2473       return;
2474     }
2475 
2476     nsCOMPtr<nsIChannel> parentChannel;
2477     parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
2478     if (!parentChannel) {
2479       return;
2480     }
2481     nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
2482     if (NS_FAILED(rv)) {
2483       return;
2484     }
2485 
2486     nsCOMPtr<nsIDocument> parentDocument = parentDocShell->GetDocument();
2487     nsCOMPtr<nsIURI> iframeUri;
2488     parentChannel->GetURI(getter_AddRefs(iframeUri));
2489     nsContentUtils::ReportToConsole(
2490         nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Iframe Sandbox"),
2491         parentDocument, nsContentUtils::eSECURITY_PROPERTIES,
2492         "BothAllowScriptsAndSameOriginPresent", nullptr, 0, iframeUri);
2493   }
2494 }
2495 
IsSynthesized()2496 bool nsDocument::IsSynthesized() {
2497   nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);
2498   bool synthesized = false;
2499   if (internalChan) {
2500     DebugOnly<nsresult> rv = internalChan->GetResponseSynthesized(&synthesized);
2501     MOZ_ASSERT(NS_SUCCEEDED(rv), "GetResponseSynthesized shouldn't fail.");
2502   }
2503   return synthesized;
2504 }
2505 
IsShadowDOMEnabled(JSContext * aCx,JSObject * aObject)2506 bool nsDocument::IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject) {
2507   JS::Rooted<JSObject*> obj(aCx, aObject);
2508 
2509   JSAutoCompartment ac(aCx, obj);
2510   JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
2511   nsCOMPtr<nsPIDOMWindowInner> window =
2512       do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
2513 
2514   nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
2515   if (!doc) {
2516     return false;
2517   }
2518 
2519   return doc->IsShadowDOMEnabled();
2520 }
2521 
IsShadowDOMEnabled(const nsINode * aNode)2522 bool nsDocument::IsShadowDOMEnabled(const nsINode* aNode) {
2523   return aNode->OwnerDoc()->IsShadowDOMEnabled();
2524 }
2525 
StartDocumentLoad(const char * aCommand,nsIChannel * aChannel,nsILoadGroup * aLoadGroup,nsISupports * aContainer,nsIStreamListener ** aDocListener,bool aReset,nsIContentSink * aSink)2526 nsresult nsDocument::StartDocumentLoad(const char* aCommand,
2527                                        nsIChannel* aChannel,
2528                                        nsILoadGroup* aLoadGroup,
2529                                        nsISupports* aContainer,
2530                                        nsIStreamListener** aDocListener,
2531                                        bool aReset, nsIContentSink* aSink) {
2532   if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
2533     nsCOMPtr<nsIURI> uri;
2534     aChannel->GetURI(getter_AddRefs(uri));
2535     MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2536             ("DOCUMENT %p StartDocumentLoad %s", this,
2537              uri ? uri->GetSpecOrDefault().get() : ""));
2538   }
2539 
2540   MOZ_ASSERT(
2541       NodePrincipal()->GetAppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2542       "Document should never have UNKNOWN_APP_ID");
2543 
2544   MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2545              "Bad readyState");
2546   SetReadyStateInternal(READYSTATE_LOADING);
2547 
2548   if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2549     mLoadedAsData = true;
2550     // We need to disable script & style loading in this case.
2551     // We leave them disabled even in EndLoad(), and let anyone
2552     // who puts the document on display to worry about enabling.
2553 
2554     // Do not load/process scripts when loading as data
2555     ScriptLoader()->SetEnabled(false);
2556 
2557     // styles
2558     CSSLoader()->SetEnabled(
2559         false);  // Do not load/process styles when loading as data
2560   } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2561     // Allow CSS, but not scripts
2562     ScriptLoader()->SetEnabled(false);
2563   }
2564 
2565   mMayStartLayout = false;
2566   MOZ_ASSERT(!mReadyForIdle,
2567              "We should never hit DOMContentLoaded before this point");
2568 
2569   if (aReset) {
2570     Reset(aChannel, aLoadGroup);
2571   }
2572 
2573   nsAutoCString contentType;
2574   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2575   if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2576                   NS_LITERAL_STRING("contentType"), contentType))) ||
2577       NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2578     // XXX this is only necessary for viewsource:
2579     nsACString::const_iterator start, end, semicolon;
2580     contentType.BeginReading(start);
2581     contentType.EndReading(end);
2582     semicolon = start;
2583     FindCharInReadable(';', semicolon, end);
2584     SetContentTypeInternal(Substring(start, semicolon));
2585   }
2586 
2587   RetrieveRelevantHeaders(aChannel);
2588 
2589   mChannel = aChannel;
2590   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2591   if (inStrmChan) {
2592     bool isSrcdocChannel;
2593     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2594     if (isSrcdocChannel) {
2595       mIsSrcdocDocument = true;
2596     }
2597   }
2598 
2599   if (mChannel) {
2600     nsLoadFlags loadFlags;
2601     mChannel->GetLoadFlags(&loadFlags);
2602     bool isDocument = false;
2603     mChannel->GetIsDocument(&isDocument);
2604     if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument &&
2605         IsSynthesized() && XRE_IsContentProcess()) {
2606       ContentChild::UpdateCookieStatus(mChannel);
2607     }
2608   }
2609 
2610   // If this document is being loaded by a docshell, copy its sandbox flags
2611   // to the document, and store the fullscreen enabled flag. These are
2612   // immutable after being set here.
2613   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2614 
2615   // If this is an error page, don't inherit sandbox flags from docshell
2616   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2617   if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
2618     nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2619     NS_ENSURE_SUCCESS(rv, rv);
2620     WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
2621   }
2622 
2623   // The CSP directive upgrade-insecure-requests not only applies to the
2624   // toplevel document, but also to nested documents. Let's propagate that
2625   // flag from the parent to the nested document.
2626   nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
2627   if (treeItem) {
2628     nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
2629     treeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
2630     if (sameTypeParent) {
2631       nsIDocument* doc = sameTypeParent->GetDocument();
2632       mBlockAllMixedContent = doc->GetBlockAllMixedContent(false);
2633       // if the parent document makes use of block-all-mixed-content
2634       // then subdocument preloads should always be blocked.
2635       mBlockAllMixedContentPreloads =
2636           mBlockAllMixedContent || doc->GetBlockAllMixedContent(true);
2637 
2638       mUpgradeInsecureRequests = doc->GetUpgradeInsecureRequests(false);
2639       // if the parent document makes use of upgrade-insecure-requests
2640       // then subdocument preloads should always be upgraded.
2641       mUpgradeInsecurePreloads =
2642           mUpgradeInsecureRequests || doc->GetUpgradeInsecureRequests(true);
2643     }
2644   }
2645 
2646   // If this is not a data document, set CSP.
2647   if (!mLoadedAsData) {
2648     nsresult rv = InitCSP(aChannel);
2649     NS_ENSURE_SUCCESS(rv, rv);
2650   }
2651 
2652   // XFO needs to be checked after CSP because it is ignored if
2653   // the CSP defines frame-ancestors.
2654   if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
2655     MOZ_LOG(gCspPRLog, LogLevel::Debug,
2656             ("XFO doesn't like frame's ancestry, not loading."));
2657     // stop!  ERROR page!
2658     aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2659   }
2660 
2661   // Perform a async flash classification based on the doc principal
2662   // in an early stage to reduce the blocking time.
2663   mFlashClassification = FlashClassification::Unclassified;
2664   mPrincipalFlashClassifier->AsyncClassify(GetPrincipal());
2665 
2666   return NS_OK;
2667 }
2668 
SendToConsole(nsCOMArray<nsISecurityConsoleMessage> & aMessages)2669 void nsDocument::SendToConsole(
2670     nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
2671   for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2672     nsAutoString messageTag;
2673     aMessages[i]->GetTag(messageTag);
2674 
2675     nsAutoString category;
2676     aMessages[i]->GetCategory(category);
2677 
2678     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2679                                     NS_ConvertUTF16toUTF8(category), this,
2680                                     nsContentUtils::eSECURITY_PROPERTIES,
2681                                     NS_ConvertUTF16toUTF8(messageTag).get());
2682   }
2683 }
2684 
ApplySettingsFromCSP(bool aSpeculative)2685 void nsDocument::ApplySettingsFromCSP(bool aSpeculative) {
2686   nsresult rv = NS_OK;
2687   if (!aSpeculative) {
2688     // 1) apply settings from regular CSP
2689     nsCOMPtr<nsIContentSecurityPolicy> csp;
2690     rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
2691     NS_ENSURE_SUCCESS_VOID(rv);
2692     if (csp) {
2693       // Set up any Referrer Policy specified by CSP
2694       bool hasReferrerPolicy = false;
2695       uint32_t referrerPolicy = mozilla::net::RP_Unset;
2696       rv = csp->GetReferrerPolicy(&referrerPolicy, &hasReferrerPolicy);
2697       NS_ENSURE_SUCCESS_VOID(rv);
2698       if (hasReferrerPolicy) {
2699         mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
2700         mReferrerPolicySet = true;
2701       }
2702 
2703       // Set up 'block-all-mixed-content' if not already inherited
2704       // from the parent context or set by any other CSP.
2705       if (!mBlockAllMixedContent) {
2706         rv = csp->GetBlockAllMixedContent(&mBlockAllMixedContent);
2707         NS_ENSURE_SUCCESS_VOID(rv);
2708       }
2709       if (!mBlockAllMixedContentPreloads) {
2710         mBlockAllMixedContentPreloads = mBlockAllMixedContent;
2711       }
2712 
2713       // Set up 'upgrade-insecure-requests' if not already inherited
2714       // from the parent context or set by any other CSP.
2715       if (!mUpgradeInsecureRequests) {
2716         rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
2717         NS_ENSURE_SUCCESS_VOID(rv);
2718       }
2719       if (!mUpgradeInsecurePreloads) {
2720         mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
2721       }
2722     }
2723     return;
2724   }
2725 
2726   // 2) apply settings from speculative csp
2727   nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
2728   rv = NodePrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
2729   NS_ENSURE_SUCCESS_VOID(rv);
2730   if (preloadCsp) {
2731     if (!mBlockAllMixedContentPreloads) {
2732       rv = preloadCsp->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads);
2733       NS_ENSURE_SUCCESS_VOID(rv);
2734     }
2735     if (!mUpgradeInsecurePreloads) {
2736       rv = preloadCsp->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads);
2737       NS_ENSURE_SUCCESS_VOID(rv);
2738     }
2739   }
2740 }
2741 
InitCSP(nsIChannel * aChannel)2742 nsresult nsDocument::InitCSP(nsIChannel* aChannel) {
2743   MOZ_ASSERT(!mScriptGlobalObject,
2744              "CSP must be initialized before mScriptGlobalObject is set!");
2745   if (!CSPService::sCSPEnabled) {
2746     MOZ_LOG(gCspPRLog, LogLevel::Debug,
2747             ("CSP is disabled, skipping CSP init for document %p", this));
2748     return NS_OK;
2749   }
2750 
2751   // In case this channel was instrument to discard the CSP, then
2752   // there is nothing for us to do here.
2753   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2754   if (loadInfo && loadInfo->GetAllowDocumentToBeAgnosticToCSP()) {
2755     return NS_OK;
2756   }
2757 
2758   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2759 
2760   nsCOMPtr<nsIHttpChannel> httpChannel;
2761   nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
2762   if (NS_WARN_IF(NS_FAILED(rv))) {
2763     return rv;
2764   }
2765 
2766   if (httpChannel) {
2767     Unused << httpChannel->GetResponseHeader(
2768         NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue);
2769 
2770     Unused << httpChannel->GetResponseHeader(
2771         NS_LITERAL_CSTRING("content-security-policy-report-only"),
2772         tCspROHeaderValue);
2773   }
2774   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2775   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2776 
2777   // Check if this is a document from a WebExtension.
2778   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
2779   auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
2780 
2781   // Check if this is a signed content to apply default CSP.
2782   bool applySignedContentCSP = false;
2783   if (loadInfo && loadInfo->GetVerifySignedContent()) {
2784     applySignedContentCSP = true;
2785   }
2786 
2787   // If there's no CSP to apply, go ahead and return early
2788   if (!addonPolicy && !applySignedContentCSP && cspHeaderValue.IsEmpty() &&
2789       cspROHeaderValue.IsEmpty()) {
2790     if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
2791       nsCOMPtr<nsIURI> chanURI;
2792       aChannel->GetURI(getter_AddRefs(chanURI));
2793       nsAutoCString aspec;
2794       chanURI->GetAsciiSpec(aspec);
2795       MOZ_LOG(gCspPRLog, LogLevel::Debug,
2796               ("no CSP for document, %s", aspec.get()));
2797     }
2798 
2799     return NS_OK;
2800   }
2801 
2802   MOZ_LOG(gCspPRLog, LogLevel::Debug,
2803           ("Document is an add-on or CSP header specified %p", this));
2804 
2805   nsCOMPtr<nsIContentSecurityPolicy> csp;
2806   rv = principal->EnsureCSP(this, getter_AddRefs(csp));
2807   NS_ENSURE_SUCCESS(rv, rv);
2808 
2809   // ----- if the doc is an addon, apply its CSP.
2810   if (addonPolicy) {
2811     nsCOMPtr<nsIAddonPolicyService> aps =
2812         do_GetService("@mozilla.org/addons/policy-service;1");
2813 
2814     nsAutoString addonCSP;
2815     Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(addonCSP);
2816     csp->AppendPolicy(addonCSP, false, false);
2817 
2818     csp->AppendPolicy(addonPolicy->ContentSecurityPolicy(), false, false);
2819   }
2820 
2821   // ----- if the doc is a signed content, apply the default CSP.
2822   // Note that when the content signing becomes a standard, we might have
2823   // to restrict this enforcement to "remote content" only.
2824   if (applySignedContentCSP) {
2825     nsAutoString signedContentCSP;
2826     Preferences::GetString("security.signed_content.CSP.default",
2827                            signedContentCSP);
2828     csp->AppendPolicy(signedContentCSP, false, false);
2829   }
2830 
2831   // ----- if there's a full-strength CSP header, apply it.
2832   if (!cspHeaderValue.IsEmpty()) {
2833     rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
2834     NS_ENSURE_SUCCESS(rv, rv);
2835   }
2836 
2837   // ----- if there's a report-only CSP header, apply it.
2838   if (!cspROHeaderValue.IsEmpty()) {
2839     rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
2840     NS_ENSURE_SUCCESS(rv, rv);
2841   }
2842 
2843   // ----- Enforce sandbox policy if supplied in CSP header
2844   // The document may already have some sandbox flags set (e.g. if the document
2845   // is an iframe with the sandbox attribute set). If we have a CSP sandbox
2846   // directive, intersect the CSP sandbox flags with the existing flags. This
2847   // corresponds to the _least_ permissive policy.
2848   uint32_t cspSandboxFlags = SANDBOXED_NONE;
2849   rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
2850   NS_ENSURE_SUCCESS(rv, rv);
2851 
2852   // Probably the iframe sandbox attribute already caused the creation of a
2853   // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
2854   // and no one has been created yet.
2855   bool needNewNullPrincipal = (cspSandboxFlags & SANDBOXED_ORIGIN) &&
2856                               !(mSandboxFlags & SANDBOXED_ORIGIN);
2857 
2858   mSandboxFlags |= cspSandboxFlags;
2859 
2860   if (needNewNullPrincipal) {
2861     principal = NullPrincipal::CreateWithInheritedAttributes(principal);
2862     principal->SetCsp(csp);
2863     SetPrincipal(principal);
2864   }
2865 
2866   // ----- Enforce frame-ancestor policy on any applied policies
2867   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2868   if (docShell) {
2869     bool safeAncestry = false;
2870 
2871     // PermitsAncestry sends violation reports when necessary
2872     rv = csp->PermitsAncestry(docShell, &safeAncestry);
2873 
2874     if (NS_FAILED(rv) || !safeAncestry) {
2875       MOZ_LOG(gCspPRLog, LogLevel::Debug,
2876               ("CSP doesn't like frame's ancestry, not loading."));
2877       // stop!  ERROR page!
2878       aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2879     }
2880   }
2881   ApplySettingsFromCSP(false);
2882   return NS_OK;
2883 }
2884 
StopDocumentLoad()2885 void nsDocument::StopDocumentLoad() {
2886   if (mParser) {
2887     mParserAborted = true;
2888     mParser->Terminate();
2889   }
2890 }
2891 
SetDocumentURI(nsIURI * aURI)2892 void nsDocument::SetDocumentURI(nsIURI* aURI) {
2893   nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
2894   mDocumentURI = NS_TryToMakeImmutable(aURI);
2895   nsIURI* newBase = GetDocBaseURI();
2896 
2897   bool equalBases = false;
2898   // Changing just the ref of a URI does not change how relative URIs would
2899   // resolve wrt to it, so we can treat the bases as equal as long as they're
2900   // equal ignoring the ref.
2901   if (oldBase && newBase) {
2902     oldBase->EqualsExceptRef(newBase, &equalBases);
2903   } else {
2904     equalBases = !oldBase && !newBase;
2905   }
2906 
2907   // If this is the first time we're setting the document's URI, set the
2908   // document's original URI.
2909   if (!mOriginalURI) mOriginalURI = mDocumentURI;
2910 
2911   // If changing the document's URI changed the base URI of the document, we
2912   // need to refresh the hrefs of all the links on the page.
2913   if (!equalBases) {
2914     RefreshLinkHrefs();
2915   }
2916 }
2917 
SetChromeXHRDocURI(nsIURI * aURI)2918 void nsDocument::SetChromeXHRDocURI(nsIURI* aURI) { mChromeXHRDocURI = aURI; }
2919 
SetChromeXHRDocBaseURI(nsIURI * aURI)2920 void nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI) {
2921   mChromeXHRDocBaseURI = aURI;
2922 }
2923 
GetFormattedTimeString(PRTime aTime,nsAString & aFormattedTimeString)2924 static void GetFormattedTimeString(PRTime aTime,
2925                                    nsAString& aFormattedTimeString) {
2926   PRExplodedTime prtime;
2927   PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime);
2928   // "MM/DD/YYYY hh:mm:ss"
2929   char formatedTime[24];
2930   if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
2931                      prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
2932                      prtime.tm_hour, prtime.tm_min, prtime.tm_sec)) {
2933     CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
2934   } else {
2935     // If we for whatever reason failed to find the last modified time
2936     // (or even the current time), fall back to what NS4.x returned.
2937     aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
2938   }
2939 }
2940 
GetLastModified(nsAString & aLastModified) const2941 void nsIDocument::GetLastModified(nsAString& aLastModified) const {
2942   if (!mLastModified.IsEmpty()) {
2943     aLastModified.Assign(mLastModified);
2944   } else {
2945     GetFormattedTimeString(PR_Now(), aLastModified);
2946   }
2947 }
2948 
AddToNameTable(Element * aElement,nsAtom * aName)2949 void nsDocument::AddToNameTable(Element* aElement, nsAtom* aName) {
2950   MOZ_ASSERT(
2951       nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
2952       "Only put elements that need to be exposed as document['name'] in "
2953       "the named table.");
2954 
2955   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
2956 
2957   // Null for out-of-memory
2958   if (entry) {
2959     if (!entry->HasNameElement() &&
2960         !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2961       ++mExpandoAndGeneration.generation;
2962     }
2963     entry->AddNameElement(this, aElement);
2964   }
2965 }
2966 
RemoveFromNameTable(Element * aElement,nsAtom * aName)2967 void nsDocument::RemoveFromNameTable(Element* aElement, nsAtom* aName) {
2968   // Speed up document teardown
2969   if (mIdentifierMap.Count() == 0) return;
2970 
2971   nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
2972   if (!entry)  // Could be false if the element was anonymous, hence never added
2973     return;
2974 
2975   entry->RemoveNameElement(aElement);
2976   if (!entry->HasNameElement() &&
2977       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2978     ++mExpandoAndGeneration.generation;
2979   }
2980 }
2981 
AddToIdTable(Element * aElement,nsAtom * aId)2982 void nsDocument::AddToIdTable(Element* aElement, nsAtom* aId) {
2983   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
2984 
2985   if (entry) { /* True except on OOM */
2986     if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
2987         !entry->HasNameElement() &&
2988         !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2989       ++mExpandoAndGeneration.generation;
2990     }
2991     entry->AddIdElement(aElement);
2992   }
2993 }
2994 
RemoveFromIdTable(Element * aElement,nsAtom * aId)2995 void nsDocument::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
2996   NS_ASSERTION(aId, "huhwhatnow?");
2997 
2998   // Speed up document teardown
2999   if (mIdentifierMap.Count() == 0) {
3000     return;
3001   }
3002 
3003   nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
3004   if (!entry)  // Can be null for XML elements with changing ids.
3005     return;
3006 
3007   entry->RemoveIdElement(aElement);
3008   if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3009       !entry->HasNameElement() &&
3010       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3011     ++mExpandoAndGeneration.generation;
3012   }
3013   if (entry->IsEmpty()) {
3014     mIdentifierMap.RemoveEntry(entry);
3015   }
3016 }
3017 
GetPrincipal()3018 nsIPrincipal* nsDocument::GetPrincipal() { return NodePrincipal(); }
3019 
3020 extern bool sDisablePrefetchHTTPSPref;
3021 
SetPrincipal(nsIPrincipal * aNewPrincipal)3022 void nsDocument::SetPrincipal(nsIPrincipal* aNewPrincipal) {
3023   if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3024     nsCOMPtr<nsIURI> uri;
3025     aNewPrincipal->GetURI(getter_AddRefs(uri));
3026     bool isHTTPS;
3027     if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) || isHTTPS) {
3028       mAllowDNSPrefetch = false;
3029     }
3030   }
3031   mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3032 
3033 #ifdef DEBUG
3034   // Validate that the docgroup is set correctly by calling its getter and
3035   // triggering its sanity check.
3036   //
3037   // If we're setting the principal to null, we don't want to perform the check,
3038   // as the document is entering an intermediate state where it does not have a
3039   // principal. It will be given another real principal shortly which we will
3040   // check. It's not unsafe to have a document which has a null principal in the
3041   // same docgroup as another document, so this should not be a problem.
3042   if (aNewPrincipal) {
3043     GetDocGroup();
3044   }
3045 #endif
3046 }
3047 
GetDocGroup() const3048 mozilla::dom::DocGroup* nsIDocument::GetDocGroup() const {
3049 #ifdef DEBUG
3050   // Sanity check that we have an up-to-date and accurate docgroup
3051   if (mDocGroup) {
3052     nsAutoCString docGroupKey;
3053 
3054     // GetKey() can fail, e.g. after the TLD service has shut down.
3055     nsresult rv = mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
3056     if (NS_SUCCEEDED(rv)) {
3057       MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
3058     }
3059     // XXX: Check that the TabGroup is correct as well!
3060   }
3061 #endif
3062 
3063   return mDocGroup;
3064 }
3065 
Dispatch(TaskCategory aCategory,already_AddRefed<nsIRunnable> && aRunnable)3066 nsresult nsIDocument::Dispatch(TaskCategory aCategory,
3067                                already_AddRefed<nsIRunnable>&& aRunnable) {
3068   // Note that this method may be called off the main thread.
3069   if (mDocGroup) {
3070     return mDocGroup->Dispatch(aCategory, Move(aRunnable));
3071   }
3072   return DispatcherTrait::Dispatch(aCategory, Move(aRunnable));
3073 }
3074 
EventTargetFor(TaskCategory aCategory) const3075 nsISerialEventTarget* nsIDocument::EventTargetFor(
3076     TaskCategory aCategory) const {
3077   if (mDocGroup) {
3078     return mDocGroup->EventTargetFor(aCategory);
3079   }
3080   return DispatcherTrait::EventTargetFor(aCategory);
3081 }
3082 
AbstractMainThreadFor(mozilla::TaskCategory aCategory)3083 AbstractThread* nsIDocument::AbstractMainThreadFor(
3084     mozilla::TaskCategory aCategory) {
3085   MOZ_ASSERT(NS_IsMainThread());
3086   if (mDocGroup) {
3087     return mDocGroup->AbstractMainThreadFor(aCategory);
3088   }
3089   return DispatcherTrait::AbstractMainThreadFor(aCategory);
3090 }
3091 
NoteScriptTrackingStatus(const nsACString & aURL,bool aIsTracking)3092 void nsIDocument::NoteScriptTrackingStatus(const nsACString& aURL,
3093                                            bool aIsTracking) {
3094   if (aIsTracking) {
3095     mTrackingScripts.PutEntry(aURL);
3096   } else {
3097     MOZ_ASSERT(!mTrackingScripts.Contains(aURL));
3098   }
3099 }
3100 
IsScriptTracking(const nsACString & aURL) const3101 bool nsIDocument::IsScriptTracking(const nsACString& aURL) const {
3102   return mTrackingScripts.Contains(aURL);
3103 }
3104 
3105 NS_IMETHODIMP
GetApplicationCache(nsIApplicationCache ** aApplicationCache)3106 nsDocument::GetApplicationCache(nsIApplicationCache** aApplicationCache) {
3107   NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3108 
3109   return NS_OK;
3110 }
3111 
3112 NS_IMETHODIMP
SetApplicationCache(nsIApplicationCache * aApplicationCache)3113 nsDocument::SetApplicationCache(nsIApplicationCache* aApplicationCache) {
3114   mApplicationCache = aApplicationCache;
3115 
3116   return NS_OK;
3117 }
3118 
GetContentType(nsAString & aContentType)3119 void nsIDocument::GetContentType(nsAString& aContentType) {
3120   CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3121 }
3122 
SetContentType(const nsAString & aContentType)3123 void nsDocument::SetContentType(const nsAString& aContentType) {
3124   SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3125 }
3126 
GetAllowPlugins()3127 bool nsDocument::GetAllowPlugins() {
3128   // First, we ask our docshell if it allows plugins.
3129   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3130 
3131   if (docShell) {
3132     bool allowPlugins = false;
3133     docShell->GetAllowPlugins(&allowPlugins);
3134     if (!allowPlugins) {
3135       return false;
3136     }
3137 
3138     // If the docshell allows plugins, we check whether
3139     // we are sandboxed and plugins should not be allowed.
3140     if (mSandboxFlags & SANDBOXED_PLUGINS) {
3141       return false;
3142     }
3143   }
3144 
3145   FlashClassification classification = DocumentFlashClassification();
3146   if (classification == FlashClassification::Denied) {
3147     return false;
3148   }
3149 
3150   return true;
3151 }
3152 
CallerIsTrustedAboutPage(JSContext * aCx,JSObject * aObject)3153 bool nsDocument::CallerIsTrustedAboutPage(JSContext* aCx, JSObject* aObject) {
3154   /*
3155    * If you want an about: page to have access to AboutCapabilities,
3156    * please add it to the list of trusted about: pages underneath.
3157    */
3158   static const char* kTrustedAboutPages[] = {
3159       "about:privatebrowsing",
3160   };
3161 
3162   nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
3163   if (!principal) {
3164     return false;
3165   }
3166   nsCOMPtr<nsIURI> uri;
3167   principal->GetURI(getter_AddRefs(uri));
3168   if (!uri) {
3169     return false;
3170   }
3171   // getSpec is an expensive operation, hence we first check the scheme
3172   // to see if the caller is actually an about: page.
3173   bool isAboutScheme = false;
3174   uri->SchemeIs("about", &isAboutScheme);
3175   if (!isAboutScheme) {
3176     return false;
3177   }
3178 
3179   nsAutoCString aboutSpec;
3180   uri->GetSpec(aboutSpec);
3181   for (auto& aboutPageEntry : kTrustedAboutPages) {
3182     if (aboutSpec.EqualsIgnoreCase(aboutPageEntry)) {
3183       return true;
3184     }
3185   }
3186   return false;
3187 }
3188 
GetAboutCapabilities(ErrorResult & aRv)3189 already_AddRefed<AboutCapabilities> nsIDocument::GetAboutCapabilities(
3190     ErrorResult& aRv) {
3191   if (!mAboutCapabilities) {
3192     AutoJSContext cx;
3193     JS::Rooted<JSObject*> jsImplObj(cx);
3194     nsIGlobalObject* sgo = GetScopeObject();
3195     ConstructJSImplementation("@mozilla.org/aboutcapabilities;1", sgo,
3196                               &jsImplObj, aRv);
3197     if (aRv.Failed()) {
3198       return nullptr;
3199     }
3200     mAboutCapabilities = new AboutCapabilities(jsImplObj, sgo);
3201   }
3202   RefPtr<AboutCapabilities> aboutCapabilities =
3203       static_cast<AboutCapabilities*>(mAboutCapabilities.get());
3204   return aboutCapabilities.forget();
3205 }
3206 
IsElementAnimateEnabled(JSContext * aCx,JSObject *)3207 bool nsDocument::IsElementAnimateEnabled(JSContext* aCx, JSObject* /*unused*/) {
3208   MOZ_ASSERT(NS_IsMainThread());
3209 
3210   return nsContentUtils::IsSystemCaller(aCx) ||
3211          nsContentUtils::AnimationsAPICoreEnabled() ||
3212          nsContentUtils::AnimationsAPIElementAnimateEnabled();
3213 }
3214 
IsWebAnimationsEnabled(JSContext * aCx,JSObject *)3215 bool nsDocument::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/) {
3216   MOZ_ASSERT(NS_IsMainThread());
3217 
3218   return nsContentUtils::IsSystemCaller(aCx) ||
3219          nsContentUtils::AnimationsAPICoreEnabled();
3220 }
3221 
IsWebAnimationsEnabled(CallerType aCallerType)3222 bool nsDocument::IsWebAnimationsEnabled(CallerType aCallerType) {
3223   MOZ_ASSERT(NS_IsMainThread());
3224 
3225   return aCallerType == dom::CallerType::System ||
3226          nsContentUtils::AnimationsAPICoreEnabled();
3227 }
3228 
Timeline()3229 DocumentTimeline* nsDocument::Timeline() {
3230   if (!mDocumentTimeline) {
3231     mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0));
3232   }
3233 
3234   return mDocumentTimeline;
3235 }
3236 
GetAnimations(nsTArray<RefPtr<Animation>> & aAnimations)3237 void nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations) {
3238   // Hold a strong ref for the root element since Element::GetAnimations() calls
3239   // FlushPendingNotifications() which may destroy the element.
3240   RefPtr<Element> root = GetRootElement();
3241   if (!root) {
3242     return;
3243   }
3244   AnimationFilter filter;
3245   filter.mSubtree = true;
3246   root->GetAnimations(filter, aAnimations);
3247 }
3248 
GetSVGRootElement() const3249 SVGSVGElement* nsIDocument::GetSVGRootElement() const {
3250   Element* root = GetRootElement();
3251   if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
3252     return nullptr;
3253   }
3254   return static_cast<SVGSVGElement*>(root);
3255 }
3256 
3257 /* Return true if the document is in the focused top-level window, and is an
3258  * ancestor of the focused DOMWindow. */
HasFocus(ErrorResult & rv) const3259 bool nsIDocument::HasFocus(ErrorResult& rv) const {
3260   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3261   if (!fm) {
3262     rv.Throw(NS_ERROR_NOT_AVAILABLE);
3263     return false;
3264   }
3265 
3266   // Is there a focused DOMWindow?
3267   nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3268   fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3269   if (!focusedWindow) {
3270     return false;
3271   }
3272 
3273   nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(focusedWindow);
3274 
3275   // Are we an ancestor of the focused DOMWindow?
3276   for (nsIDocument* currentDoc = piWindow->GetDoc(); currentDoc;
3277        currentDoc = currentDoc->GetParentDocument()) {
3278     if (currentDoc == this) {
3279       // Yes, we are an ancestor
3280       return true;
3281     }
3282   }
3283 
3284   return false;
3285 }
3286 
LastFocusTime() const3287 TimeStamp nsIDocument::LastFocusTime() const { return mLastFocusTime; }
3288 
SetLastFocusTime(const TimeStamp & aFocusTime)3289 void nsIDocument::SetLastFocusTime(const TimeStamp& aFocusTime) {
3290   MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
3291   MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
3292                         aFocusTime >= mLastFocusTime);
3293   mLastFocusTime = aFocusTime;
3294 }
3295 
GetReferrer(nsAString & aReferrer) const3296 void nsIDocument::GetReferrer(nsAString& aReferrer) const {
3297   if (mIsSrcdocDocument && mParentDocument)
3298     mParentDocument->GetReferrer(aReferrer);
3299   else
3300     CopyUTF8toUTF16(mReferrer, aReferrer);
3301 }
3302 
GetSrcdocData(nsAString & aSrcdocData)3303 nsresult nsIDocument::GetSrcdocData(nsAString& aSrcdocData) {
3304   if (mIsSrcdocDocument) {
3305     nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3306     if (inStrmChan) {
3307       return inStrmChan->GetSrcdocData(aSrcdocData);
3308     }
3309   }
3310   aSrcdocData = VoidString();
3311   return NS_OK;
3312 }
3313 
GetActiveElement()3314 Element* nsIDocument::GetActiveElement() {
3315   // Get the focused element.
3316   Element* focusedElement = GetRetargetedFocusedElement();
3317   if (focusedElement) {
3318     return focusedElement;
3319   }
3320 
3321   // No focused element anywhere in this document.  Try to get the BODY.
3322   RefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
3323   if (htmlDoc) {
3324     // Because of IE compatibility, return null when html document doesn't have
3325     // a body.
3326     return htmlDoc->GetBody();
3327   }
3328 
3329   // If we couldn't get a BODY, return the root element.
3330   return GetDocumentElement();
3331 }
3332 
GetCurrentScript()3333 Element* nsIDocument::GetCurrentScript() {
3334   nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3335   return el;
3336 }
3337 
NodesFromRectHelper(float aX,float aY,float aTopSize,float aRightSize,float aBottomSize,float aLeftSize,bool aIgnoreRootScrollFrame,bool aFlushLayout,nsIDOMNodeList ** aReturn)3338 nsresult nsDocument::NodesFromRectHelper(float aX, float aY, float aTopSize,
3339                                          float aRightSize, float aBottomSize,
3340                                          float aLeftSize,
3341                                          bool aIgnoreRootScrollFrame,
3342                                          bool aFlushLayout,
3343                                          nsIDOMNodeList** aReturn) {
3344   NS_ENSURE_ARG_POINTER(aReturn);
3345 
3346   nsSimpleContentList* elements = new nsSimpleContentList(this);
3347   NS_ADDREF(elements);
3348   *aReturn = elements;
3349 
3350   // Following the same behavior of elementFromPoint,
3351   // we don't return anything if either coord is negative
3352   if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) return NS_OK;
3353 
3354   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3355   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3356   nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3357   nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3358 
3359   nsRect rect(x, y, w, h);
3360 
3361   // Make sure the layout information we get is up-to-date, and
3362   // ensure we get a root frame (for everything but XUL)
3363   if (aFlushLayout) {
3364     FlushPendingNotifications(FlushType::Layout);
3365   }
3366 
3367   nsIPresShell* ps = GetShell();
3368   NS_ENSURE_STATE(ps);
3369   nsIFrame* rootFrame = ps->GetRootFrame();
3370 
3371   // XUL docs, unlike HTML, have no frame tree until everything's done loading
3372   if (!rootFrame)
3373     return NS_OK;  // return nothing to premature XUL callers as a reminder to
3374                    // wait
3375 
3376   AutoTArray<nsIFrame*, 8> outFrames;
3377   nsLayoutUtils::GetFramesForArea(
3378       rootFrame, rect, outFrames,
3379       nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
3380           nsLayoutUtils::IGNORE_CROSS_DOC |
3381           (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME
3382                                   : 0));
3383 
3384   // Used to filter out repeated elements in sequence.
3385   nsIContent* lastAdded = nullptr;
3386 
3387   for (uint32_t i = 0; i < outFrames.Length(); i++) {
3388     nsIContent* node = GetContentInThisDocument(outFrames[i]);
3389 
3390     if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
3391       // We have a node that isn't an element or a text node,
3392       // use its parent content instead.
3393       node = node->GetParent();
3394     }
3395     if (node && node != lastAdded) {
3396       elements->AppendElement(node);
3397       lastAdded = node;
3398     }
3399   }
3400 
3401   return NS_OK;
3402 }
3403 
ReleaseCapture() const3404 void nsIDocument::ReleaseCapture() const {
3405   // only release the capture if the caller can access it. This prevents a
3406   // page from stopping a scrollbar grab for example.
3407   nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3408   if (node && nsContentUtils::CanCallerAccess(node)) {
3409     nsIPresShell::SetCapturingContent(nullptr, 0);
3410   }
3411 }
3412 
GetBaseURI(bool aTryUseXHRDocBaseURI) const3413 already_AddRefed<nsIURI> nsIDocument::GetBaseURI(
3414     bool aTryUseXHRDocBaseURI) const {
3415   nsCOMPtr<nsIURI> uri;
3416   if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3417     uri = mChromeXHRDocBaseURI;
3418   } else {
3419     uri = GetDocBaseURI();
3420   }
3421 
3422   return uri.forget();
3423 }
3424 
SetBaseURI(nsIURI * aURI)3425 void nsDocument::SetBaseURI(nsIURI* aURI) {
3426   if (!aURI && !mDocumentBaseURI) {
3427     return;
3428   }
3429 
3430   // Don't do anything if the URI wasn't actually changed.
3431   if (aURI && mDocumentBaseURI) {
3432     bool equalBases = false;
3433     mDocumentBaseURI->Equals(aURI, &equalBases);
3434     if (equalBases) {
3435       return;
3436     }
3437   }
3438 
3439   if (aURI) {
3440     mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
3441   } else {
3442     mDocumentBaseURI = nullptr;
3443   }
3444   RefreshLinkHrefs();
3445 }
3446 
DefaultStyleAttrURLData()3447 URLExtraData* nsIDocument::DefaultStyleAttrURLData() {
3448 #ifdef MOZ_STYLO
3449   MOZ_ASSERT(NS_IsMainThread());
3450   nsIURI* baseURI = GetDocBaseURI();
3451   nsIURI* docURI = GetDocumentURI();
3452   nsIPrincipal* principal = NodePrincipal();
3453   if (!mCachedURLData || mCachedURLData->BaseURI() != baseURI ||
3454       mCachedURLData->GetReferrer() != docURI ||
3455       mCachedURLData->GetPrincipal() != principal) {
3456     mCachedURLData = new URLExtraData(baseURI, docURI, principal);
3457   }
3458   return mCachedURLData;
3459 #else
3460   MOZ_CRASH("Should not be called for non-stylo build");
3461   return nullptr;
3462 #endif
3463 }
3464 
GetBaseTarget(nsAString & aBaseTarget)3465 void nsDocument::GetBaseTarget(nsAString& aBaseTarget) {
3466   aBaseTarget = mBaseTarget;
3467 }
3468 
SetDocumentCharacterSet(NotNull<const Encoding * > aEncoding)3469 void nsDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding) {
3470   if (mCharacterSet != aEncoding) {
3471     mCharacterSet = aEncoding;
3472 
3473     if (nsPresContext* context = GetPresContext()) {
3474       context->DispatchCharSetChange(aEncoding);
3475     }
3476   }
3477 }
3478 
GetSandboxFlagsAsString(nsAString & aFlags)3479 void nsIDocument::GetSandboxFlagsAsString(nsAString& aFlags) {
3480   nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags);
3481 }
3482 
GetHeaderData(nsAtom * aHeaderField,nsAString & aData) const3483 void nsDocument::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
3484   aData.Truncate();
3485   const nsDocHeaderData* data = mHeaderData;
3486   while (data) {
3487     if (data->mField == aHeaderField) {
3488       aData = data->mData;
3489 
3490       break;
3491     }
3492     data = data->mNext;
3493   }
3494 }
3495 
SetHeaderData(nsAtom * aHeaderField,const nsAString & aData)3496 void nsDocument::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
3497   if (!aHeaderField) {
3498     NS_ERROR("null headerField");
3499     return;
3500   }
3501 
3502   if (!mHeaderData) {
3503     if (!aData.IsEmpty()) {  // don't bother storing empty string
3504       mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3505     }
3506   } else {
3507     nsDocHeaderData* data = mHeaderData;
3508     nsDocHeaderData** lastPtr = &mHeaderData;
3509     bool found = false;
3510     do {  // look for existing and replace
3511       if (data->mField == aHeaderField) {
3512         if (!aData.IsEmpty()) {
3513           data->mData.Assign(aData);
3514         } else {  // don't store empty string
3515           *lastPtr = data->mNext;
3516           data->mNext = nullptr;
3517           delete data;
3518         }
3519         found = true;
3520 
3521         break;
3522       }
3523       lastPtr = &(data->mNext);
3524       data = *lastPtr;
3525     } while (data);
3526 
3527     if (!aData.IsEmpty() && !found) {
3528       // didn't find, append
3529       *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3530     }
3531   }
3532 
3533   if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3534     CopyUTF16toUTF8(aData, mContentLanguage);
3535   }
3536 
3537   if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3538     // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
3539     // spec.
3540     if (DOMStringIsNull(mLastStyleSheetSet)) {
3541       // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
3542       // per spec.  The idea here is that we're changing our preferred set and
3543       // that shouldn't change the value of lastStyleSheetSet.  Also, we're
3544       // using the Internal version so we can update the CSSLoader and not have
3545       // to worry about null strings.
3546       EnableStyleSheetsForSetInternal(aData, true);
3547     }
3548   }
3549 
3550   if (aHeaderField == nsGkAtoms::refresh) {
3551     // We get into this code before we have a script global yet, so get to
3552     // our container via mDocumentContainer.
3553     nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3554     if (refresher) {
3555       // Note: using mDocumentURI instead of mBaseURI here, for consistency
3556       // (used to just use the current URI of our webnavigation, but that
3557       // should really be the same thing).  Note that this code can run
3558       // before the current URI of the webnavigation has been updated, so we
3559       // can't assert equality here.
3560       refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3561                                            NS_ConvertUTF16toUTF8(aData));
3562     }
3563   }
3564 
3565   if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3566       mAllowDNSPrefetch) {
3567     // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3568     mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3569   }
3570 
3571   if (aHeaderField == nsGkAtoms::viewport ||
3572       aHeaderField == nsGkAtoms::handheldFriendly ||
3573       aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3574       aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3575       aHeaderField == nsGkAtoms::viewport_initial_scale ||
3576       aHeaderField == nsGkAtoms::viewport_height ||
3577       aHeaderField == nsGkAtoms::viewport_width ||
3578       aHeaderField == nsGkAtoms::viewport_user_scalable) {
3579     mViewportType = Unknown;
3580   }
3581 
3582   // Referrer policy spec says to ignore any empty referrer policies.
3583   if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
3584     ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
3585     // If policy is not the empty string, then set element's node document's
3586     // referrer policy to policy
3587     if (policy != mozilla::net::RP_Unset) {
3588       // Referrer policy spec (section 6.1) says that we always use the newest
3589       // referrer policy we find
3590       mReferrerPolicy = policy;
3591       mReferrerPolicySet = true;
3592     }
3593   }
3594 
3595   if (aHeaderField == nsGkAtoms::headerReferrerPolicy && !aData.IsEmpty()) {
3596     ReferrerPolicy policy = nsContentUtils::GetReferrerPolicyFromHeader(aData);
3597     if (policy != mozilla::net::RP_Unset) {
3598       mReferrerPolicy = policy;
3599       mReferrerPolicySet = true;
3600     }
3601   }
3602 }
TryChannelCharset(nsIChannel * aChannel,int32_t & aCharsetSource,NotNull<const Encoding * > & aEncoding,nsHtml5TreeOpExecutor * aExecutor)3603 void nsDocument::TryChannelCharset(nsIChannel* aChannel,
3604                                    int32_t& aCharsetSource,
3605                                    NotNull<const Encoding*>& aEncoding,
3606                                    nsHtml5TreeOpExecutor* aExecutor) {
3607   if (aChannel) {
3608     nsAutoCString charsetVal;
3609     nsresult rv = aChannel->GetContentCharset(charsetVal);
3610     if (NS_SUCCEEDED(rv)) {
3611       const Encoding* preferred = Encoding::ForLabel(charsetVal);
3612       if (preferred) {
3613         aEncoding = WrapNotNull(preferred);
3614         aCharsetSource = kCharsetFromChannel;
3615         return;
3616       } else if (aExecutor && !charsetVal.IsEmpty()) {
3617         aExecutor->ComplainAboutBogusProtocolCharset(this);
3618       }
3619     }
3620   }
3621 }
3622 
AssertNoStaleServoDataIn(const nsINode & aSubtreeRoot)3623 static inline void AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot) {
3624 #ifdef DEBUG
3625   for (const nsINode* node = aSubtreeRoot.GetFirstChild(); node;
3626        node = node->GetNextNode()) {
3627     if (node->IsElement()) {
3628       MOZ_ASSERT(!node->AsElement()->HasServoData());
3629       if (auto* shadow = node->AsElement()->GetShadowRoot()) {
3630         AssertNoStaleServoDataIn(*shadow);
3631       }
3632     }
3633   }
3634 #endif
3635 }
3636 
CreateShell(nsPresContext * aContext,nsViewManager * aViewManager,StyleSetHandle aStyleSet)3637 already_AddRefed<nsIPresShell> nsDocument::CreateShell(
3638     nsPresContext* aContext, nsViewManager* aViewManager,
3639     StyleSetHandle aStyleSet) {
3640   NS_ASSERTION(!mPresShell, "We have a presshell already!");
3641 
3642   NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3643 
3644   FillStyleSet(aStyleSet);
3645   AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
3646 
3647   RefPtr<PresShell> shell = new PresShell;
3648   // Note: we don't hold a ref to the shell (it holds a ref to us)
3649   mPresShell = shell;
3650   shell->Init(this, aContext, aViewManager, aStyleSet);
3651 
3652   // Make sure to never paint if we belong to an invisible DocShell.
3653   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3654   if (docShell && docShell->IsInvisible()) shell->SetNeverPainting(true);
3655 
3656   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
3657           ("DOCUMENT %p with PressShell %p and DocShell %p", this, shell.get(),
3658            docShell.get()));
3659 
3660   mExternalResourceMap.ShowViewers();
3661 
3662   UpdateFrameRequestCallbackSchedulingState();
3663 
3664   // Now that we have a shell, we might have @font-face rules (the presence of a
3665   // shell may change which rules apply to us). We don't need to do anything
3666   // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
3667   // is ready to update we'll flush the font set.
3668   MarkUserFontSetDirty();
3669 
3670   return shell.forget();
3671 }
3672 
UpdateFrameRequestCallbackSchedulingState(nsIPresShell * aOldShell)3673 void nsIDocument::UpdateFrameRequestCallbackSchedulingState(
3674     nsIPresShell* aOldShell) {
3675   // If the condition for shouldBeScheduled changes to depend on some other
3676   // variable, add UpdateFrameRequestCallbackSchedulingState() calls to the
3677   // places where that variable can change.
3678   bool shouldBeScheduled = mPresShell && IsEventHandlingEnabled() &&
3679                            !mFrameRequestCallbacks.IsEmpty();
3680   if (shouldBeScheduled == mFrameRequestCallbacksScheduled) {
3681     // nothing to do
3682     return;
3683   }
3684 
3685   nsIPresShell* presShell = aOldShell ? aOldShell : mPresShell;
3686   MOZ_RELEASE_ASSERT(presShell);
3687 
3688   nsRefreshDriver* rd = presShell->GetPresContext()->RefreshDriver();
3689   if (shouldBeScheduled) {
3690     rd->ScheduleFrameRequestCallbacks(this);
3691   } else {
3692     rd->RevokeFrameRequestCallbacks(this);
3693   }
3694 
3695   mFrameRequestCallbacksScheduled = shouldBeScheduled;
3696 }
3697 
TakeFrameRequestCallbacks(FrameRequestCallbackList & aCallbacks)3698 void nsIDocument::TakeFrameRequestCallbacks(
3699     FrameRequestCallbackList& aCallbacks) {
3700   aCallbacks.AppendElements(mFrameRequestCallbacks);
3701   mFrameRequestCallbacks.Clear();
3702   // No need to manually remove ourselves from the refresh driver; it will
3703   // handle that part.  But we do have to update our state.
3704   mFrameRequestCallbacksScheduled = false;
3705 }
3706 
ShouldThrottleFrameRequests()3707 bool nsIDocument::ShouldThrottleFrameRequests() {
3708   if (mStaticCloneCount > 0) {
3709     // Even if we're not visible, a static clone may be, so run at full speed.
3710     return false;
3711   }
3712 
3713   if (Hidden()) {
3714     // We're not visible (probably in a background tab or the bf cache).
3715     return true;
3716   }
3717 
3718   if (!mPresShell) {
3719     return false;  // Can't do anything smarter.
3720   }
3721 
3722   nsIFrame* frame = mPresShell->GetRootFrame();
3723   if (!frame) {
3724     return false;  // Can't do anything smarter.
3725   }
3726 
3727   nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame);
3728   if (!displayRootFrame) {
3729     return false;  // Can't do anything smarter.
3730   }
3731 
3732   if (!displayRootFrame->DidPaintPresShell(mPresShell)) {
3733     // We didn't get painted during the last paint, so we're not visible.
3734     // Throttle. Note that because we have to paint this document at least
3735     // once to unthrottle it, we will drop one requestAnimationFrame frame
3736     // when a document that previously wasn't visible scrolls into view. This
3737     // is acceptable since it would happen outside the viewport on APZ
3738     // platforms and is unlikely to be human-perceivable on non-APZ platforms.
3739     return true;
3740   }
3741 
3742   // We got painted during the last paint, so run at full speed.
3743   return false;
3744 }
3745 
DeleteShell()3746 void nsDocument::DeleteShell() {
3747   mExternalResourceMap.HideViewers();
3748   if (nsPresContext* presContext = mPresShell->GetPresContext()) {
3749     presContext->RefreshDriver()->CancelPendingEvents(this);
3750   }
3751 
3752   // When our shell goes away, request that all our images be immediately
3753   // discarded, so we don't carry around decoded image data for a document we
3754   // no longer intend to paint.
3755   ImageTracker()->RequestDiscardAll();
3756 
3757   // Now that we no longer have a shell, we need to forget about any FontFace
3758   // objects for @font-face rules that came from the style set. There's no need
3759   // to call EnsureStyleFlush either, the shell is going away anyway, so there's
3760   // no point on it.
3761   MarkUserFontSetDirty();
3762 
3763   nsIPresShell* oldShell = mPresShell;
3764   mPresShell = nullptr;
3765   UpdateFrameRequestCallbackSchedulingState(oldShell);
3766   mStyleSetFilled = false;
3767 
3768   if (IsStyledByServo()) {
3769     ClearStaleServoData();
3770     AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
3771   }
3772 }
3773 
SubDocClearEntry(PLDHashTable * table,PLDHashEntryHdr * entry)3774 static void SubDocClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) {
3775   SubDocMapEntry* e = static_cast<SubDocMapEntry*>(entry);
3776 
3777   NS_RELEASE(e->mKey);
3778   if (e->mSubDocument) {
3779     e->mSubDocument->SetParentDocument(nullptr);
3780     NS_RELEASE(e->mSubDocument);
3781   }
3782 }
3783 
SubDocInitEntry(PLDHashEntryHdr * entry,const void * key)3784 static void SubDocInitEntry(PLDHashEntryHdr* entry, const void* key) {
3785   SubDocMapEntry* e =
3786       const_cast<SubDocMapEntry*>(static_cast<const SubDocMapEntry*>(entry));
3787 
3788   e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
3789   NS_ADDREF(e->mKey);
3790 
3791   e->mSubDocument = nullptr;
3792 }
3793 
AllowPaymentRequest() const3794 bool nsDocument::AllowPaymentRequest() const { return mAllowPaymentRequest; }
3795 
SetAllowPaymentRequest(bool aAllowPaymentRequest)3796 void nsDocument::SetAllowPaymentRequest(bool aAllowPaymentRequest) {
3797   mAllowPaymentRequest = aAllowPaymentRequest;
3798 }
3799 
SetSubDocumentFor(Element * aElement,nsIDocument * aSubDoc)3800 nsresult nsDocument::SetSubDocumentFor(Element* aElement,
3801                                        nsIDocument* aSubDoc) {
3802   NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
3803 
3804   if (!aSubDoc) {
3805     // aSubDoc is nullptr, remove the mapping
3806 
3807     if (mSubDocuments) {
3808       nsIDocument* subDoc = GetSubDocumentFor(aElement);
3809       if (subDoc) {
3810         subDoc->SetAllowPaymentRequest(false);
3811       }
3812       mSubDocuments->Remove(aElement);
3813     }
3814   } else {
3815     if (!mSubDocuments) {
3816       // Create a new hashtable
3817 
3818       static const PLDHashTableOps hash_table_ops = {
3819           PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
3820           PLDHashTable::MoveEntryStub, SubDocClearEntry, SubDocInitEntry};
3821 
3822       mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
3823     }
3824 
3825     // Add a mapping to the hash table
3826     auto entry =
3827         static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible));
3828 
3829     if (!entry) {
3830       return NS_ERROR_OUT_OF_MEMORY;
3831     }
3832 
3833     if (entry->mSubDocument) {
3834       entry->mSubDocument->SetAllowPaymentRequest(false);
3835       entry->mSubDocument->SetParentDocument(nullptr);
3836 
3837       // Release the old sub document
3838       NS_RELEASE(entry->mSubDocument);
3839     }
3840 
3841     entry->mSubDocument = aSubDoc;
3842     NS_ADDREF(entry->mSubDocument);
3843 
3844     // set allowpaymentrequest for the binding subdocument
3845     if (!mAllowPaymentRequest) {
3846       aSubDoc->SetAllowPaymentRequest(false);
3847     } else {
3848       nsresult rv = nsContentUtils::CheckSameOrigin(aElement, aSubDoc);
3849       if (NS_SUCCEEDED(rv)) {
3850         aSubDoc->SetAllowPaymentRequest(true);
3851       } else {
3852         if (aElement->IsHTMLElement(nsGkAtoms::iframe) &&
3853             aElement->GetBoolAttr(nsGkAtoms::allowpaymentrequest)) {
3854           aSubDoc->SetAllowPaymentRequest(true);
3855         } else {
3856           aSubDoc->SetAllowPaymentRequest(false);
3857         }
3858       }
3859     }
3860 
3861     aSubDoc->SetParentDocument(this);
3862   }
3863 
3864   return NS_OK;
3865 }
3866 
GetSubDocumentFor(nsIContent * aContent) const3867 nsIDocument* nsDocument::GetSubDocumentFor(nsIContent* aContent) const {
3868   if (mSubDocuments && aContent->IsElement()) {
3869     auto entry = static_cast<SubDocMapEntry*>(
3870         mSubDocuments->Search(aContent->AsElement()));
3871 
3872     if (entry) {
3873       return entry->mSubDocument;
3874     }
3875   }
3876 
3877   return nullptr;
3878 }
3879 
FindContentForSubDocument(nsIDocument * aDocument) const3880 Element* nsDocument::FindContentForSubDocument(nsIDocument* aDocument) const {
3881   NS_ENSURE_TRUE(aDocument, nullptr);
3882 
3883   if (!mSubDocuments) {
3884     return nullptr;
3885   }
3886 
3887   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
3888     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
3889     if (entry->mSubDocument == aDocument) {
3890       return entry->mKey;
3891     }
3892   }
3893   return nullptr;
3894 }
3895 
IsNodeOfType(uint32_t aFlags) const3896 bool nsDocument::IsNodeOfType(uint32_t aFlags) const {
3897   return !(aFlags & ~eDOCUMENT);
3898 }
3899 
GetRootElement() const3900 Element* nsIDocument::GetRootElement() const {
3901   return (mCachedRootElement && mCachedRootElement->GetParentNode() == this)
3902              ? mCachedRootElement
3903              : GetRootElementInternal();
3904 }
3905 
GetUnfocusedKeyEventTarget()3906 nsIContent* nsIDocument::GetUnfocusedKeyEventTarget() {
3907   return GetRootElement();
3908 }
3909 
GetRootElementInternal() const3910 Element* nsDocument::GetRootElementInternal() const {
3911   // We invoke GetRootElement() immediately before the servo traversal, so we
3912   // should always have a cache hit from Servo.
3913   MOZ_ASSERT(NS_IsMainThread());
3914 
3915   // Loop backwards because any non-elements, such as doctypes and PIs
3916   // are likely to appear before the root element.
3917   uint32_t i;
3918   for (i = mChildren.ChildCount(); i > 0; --i) {
3919     nsIContent* child = mChildren.ChildAt(i - 1);
3920     if (child->IsElement()) {
3921       const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
3922       return child->AsElement();
3923     }
3924   }
3925 
3926   const_cast<nsDocument*>(this)->mCachedRootElement = nullptr;
3927   return nullptr;
3928 }
3929 
GetChildAt_Deprecated(uint32_t aIndex) const3930 nsIContent* nsDocument::GetChildAt_Deprecated(uint32_t aIndex) const {
3931   return mChildren.GetSafeChildAt(aIndex);
3932 }
3933 
ComputeIndexOf(const nsINode * aPossibleChild) const3934 int32_t nsDocument::ComputeIndexOf(const nsINode* aPossibleChild) const {
3935   return mChildren.IndexOfChild(aPossibleChild);
3936 }
3937 
GetChildCount() const3938 uint32_t nsDocument::GetChildCount() const { return mChildren.ChildCount(); }
3939 
InsertChildBefore(nsIContent * aKid,nsIContent * aBeforeThis,bool aNotify)3940 nsresult nsDocument::InsertChildBefore(nsIContent* aKid,
3941                                        nsIContent* aBeforeThis, bool aNotify) {
3942   if (aKid->IsElement() && GetRootElement()) {
3943     NS_WARNING("Inserting root element when we already have one");
3944     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
3945   }
3946 
3947   int32_t index = aBeforeThis ? ComputeIndexOf(aBeforeThis) : GetChildCount();
3948   MOZ_ASSERT(index >= 0);
3949 
3950   return doInsertChildAt(aKid, index, aNotify, mChildren);
3951 }
3952 
InsertChildAt_Deprecated(nsIContent * aKid,uint32_t aIndex,bool aNotify)3953 nsresult nsDocument::InsertChildAt_Deprecated(nsIContent* aKid, uint32_t aIndex,
3954                                               bool aNotify) {
3955   if (aKid->IsElement() && GetRootElement()) {
3956     NS_WARNING("Inserting root element when we already have one");
3957     return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
3958   }
3959 
3960   return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
3961 }
3962 
RemoveChildAt_Deprecated(uint32_t aIndex,bool aNotify)3963 void nsDocument::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify) {
3964   nsCOMPtr<nsIContent> oldKid = GetChildAt_Deprecated(aIndex);
3965   if (!oldKid) {
3966     return;
3967   }
3968 
3969   if (oldKid->IsElement()) {
3970     // Destroy the link map up front before we mess with the child list.
3971     DestroyElementMaps();
3972   }
3973 
3974   // Preemptively clear mCachedRootElement, since we may be about to remove it
3975   // from our child list, and we don't want to return this maybe-obsolete value
3976   // from any GetRootElement() calls that happen inside of doRemoveChildAt().
3977   // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
3978   // GetRootElement() calls until after it's removed the child from mChildren.
3979   // Any call before that point would restore this soon-to-be-obsolete cached
3980   // answer, and our clearing here would be fruitless.)
3981   mCachedRootElement = nullptr;
3982   doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
3983   MOZ_ASSERT(mCachedRootElement != oldKid,
3984              "Stale pointer in mCachedRootElement, after we tried to clear it "
3985              "(maybe somebody called GetRootElement() too early?)");
3986 }
3987 
RemoveChildNode(nsIContent * aKid,bool aNotify)3988 void nsDocument::RemoveChildNode(nsIContent* aKid, bool aNotify) {
3989   if (aKid->IsElement()) {
3990     // Destroy the link map up front before we mess with the child list.
3991     DestroyElementMaps();
3992   }
3993 
3994   // Preemptively clear mCachedRootElement, since we may be about to remove it
3995   // from our child list, and we don't want to return this maybe-obsolete value
3996   // from any GetRootElement() calls that happen inside of doRemoveChildAt().
3997   // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
3998   // GetRootElement() calls until after it's removed the child from mChildren.
3999   // Any call before that point would restore this soon-to-be-obsolete cached
4000   // answer, and our clearing here would be fruitless.)
4001   mCachedRootElement = nullptr;
4002   doRemoveChildAt(ComputeIndexOf(aKid), aNotify, aKid, mChildren);
4003   MOZ_ASSERT(mCachedRootElement != aKid,
4004              "Stale pointer in mCachedRootElement, after we tried to clear it "
4005              "(maybe somebody called GetRootElement() too early?)");
4006 }
4007 
EnsureOnDemandBuiltInUASheet(StyleSheet * aSheet)4008 void nsIDocument::EnsureOnDemandBuiltInUASheet(StyleSheet* aSheet) {
4009   if (mOnDemandBuiltInUASheets.Contains(aSheet)) {
4010     return;
4011   }
4012   BeginUpdate(UPDATE_STYLE);
4013   AddOnDemandBuiltInUASheet(aSheet);
4014   EndUpdate(UPDATE_STYLE);
4015 }
4016 
AddOnDemandBuiltInUASheet(StyleSheet * aSheet)4017 void nsIDocument::AddOnDemandBuiltInUASheet(StyleSheet* aSheet) {
4018   MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(aSheet));
4019   MOZ_DIAGNOSTIC_ASSERT(aSheet->IsServo() == IsStyledByServo());
4020 
4021   // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in
4022   // the same order that they should end up in the style set.
4023   mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet);
4024 
4025   if (aSheet->IsApplicable()) {
4026     // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
4027     nsCOMPtr<nsIPresShell> shell = GetShell();
4028     if (shell) {
4029       // Note that prepending here is necessary to make sure that html.css etc.
4030       // do not override Firefox OS/Mobile's content.css sheet. Maybe we should
4031       // have an insertion point to match the order of
4032       // nsDocumentViewer::CreateStyleSet though?
4033       shell->StyleSet()->PrependStyleSheet(SheetType::Agent, aSheet);
4034     }
4035   }
4036 
4037   NotifyStyleSheetAdded(aSheet, false);
4038 }
4039 
AddStyleSheetToStyleSets(StyleSheet * aSheet)4040 void nsIDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet) {
4041   MOZ_DIAGNOSTIC_ASSERT(aSheet->IsServo() == IsStyledByServo());
4042   nsCOMPtr<nsIPresShell> shell = GetShell();
4043   if (shell) {
4044     shell->StyleSet()->AddDocStyleSheet(aSheet, this);
4045   }
4046 }
4047 
4048 #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \
4049   do {                                                                   \
4050     className##Init init;                                                \
4051     init.mBubbles = true;                                                \
4052     init.mCancelable = true;                                             \
4053     init.mStylesheet = aSheet;                                           \
4054     init.memberName = argName;                                           \
4055                                                                          \
4056     RefPtr<className> event =                                            \
4057         className::Constructor(this, NS_LITERAL_STRING(type), init);     \
4058     event->SetTrusted(true);                                             \
4059     event->SetTarget(this);                                              \
4060     RefPtr<AsyncEventDispatcher> asyncDispatcher =                       \
4061         new AsyncEventDispatcher(this, event);                           \
4062     asyncDispatcher->mOnlyChromeDispatch = true;                         \
4063     asyncDispatcher->PostDOMEvent();                                     \
4064   } while (0);
4065 
NotifyStyleSheetAdded(StyleSheet * aSheet,bool aDocumentSheet)4066 void nsIDocument::NotifyStyleSheetAdded(StyleSheet* aSheet,
4067                                         bool aDocumentSheet) {
4068   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (aSheet, aDocumentSheet));
4069 
4070   if (StyleSheetChangeEventsEnabled()) {
4071     DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent, "StyleSheetAdded",
4072                                mDocumentSheet, aDocumentSheet);
4073   }
4074 }
4075 
NotifyStyleSheetRemoved(StyleSheet * aSheet,bool aDocumentSheet)4076 void nsIDocument::NotifyStyleSheetRemoved(StyleSheet* aSheet,
4077                                           bool aDocumentSheet) {
4078   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (aSheet, aDocumentSheet));
4079 
4080   if (StyleSheetChangeEventsEnabled()) {
4081     DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent, "StyleSheetRemoved",
4082                                mDocumentSheet, aDocumentSheet);
4083   }
4084 }
4085 
AddStyleSheet(StyleSheet * aSheet)4086 void nsIDocument::AddStyleSheet(StyleSheet* aSheet) {
4087   MOZ_ASSERT(aSheet);
4088   MOZ_DIAGNOSTIC_ASSERT(aSheet->IsServo() == IsStyledByServo());
4089   mStyleSheets.AppendElement(aSheet);
4090   aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4091 
4092   if (aSheet->IsApplicable()) {
4093     AddStyleSheetToStyleSets(aSheet);
4094   }
4095 
4096   NotifyStyleSheetAdded(aSheet, true);
4097 }
4098 
RemoveStyleSheetFromStyleSets(StyleSheet * aSheet)4099 void nsIDocument::RemoveStyleSheetFromStyleSets(StyleSheet* aSheet) {
4100   nsCOMPtr<nsIPresShell> shell = GetShell();
4101   if (shell) {
4102     shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4103   }
4104 }
4105 
RemoveStyleSheet(StyleSheet * aSheet)4106 void nsIDocument::RemoveStyleSheet(StyleSheet* aSheet) {
4107   NS_PRECONDITION(aSheet, "null arg");
4108   RefPtr<StyleSheet> sheet = aSheet;  // hold ref so it won't die too soon
4109 
4110   if (!mStyleSheets.RemoveElement(aSheet)) {
4111     NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4112     return;
4113   }
4114 
4115   if (!mIsGoingAway) {
4116     if (aSheet->IsApplicable()) {
4117       RemoveStyleSheetFromStyleSets(aSheet);
4118     }
4119 
4120     NotifyStyleSheetRemoved(aSheet, true);
4121   }
4122 
4123   aSheet->ClearAssociatedDocument();
4124 }
4125 
UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>> & aOldSheets,nsTArray<RefPtr<StyleSheet>> & aNewSheets)4126 void nsIDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets,
4127                                     nsTArray<RefPtr<StyleSheet>>& aNewSheets) {
4128   BeginUpdate(UPDATE_STYLE);
4129 
4130   // XXX Need to set the sheet on the ownernode, if any
4131   NS_PRECONDITION(aOldSheets.Length() == aNewSheets.Length(),
4132                   "The lists must be the same length!");
4133   int32_t count = aOldSheets.Length();
4134 
4135   RefPtr<StyleSheet> oldSheet;
4136   int32_t i;
4137   for (i = 0; i < count; ++i) {
4138     oldSheet = aOldSheets[i];
4139 
4140     // First remove the old sheet.
4141     NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4142     int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4143     RemoveStyleSheet(oldSheet);  // This does the right notifications
4144 
4145     // Now put the new one in its place.  If it's null, just ignore it.
4146     StyleSheet* newSheet = aNewSheets[i];
4147     if (newSheet) {
4148       MOZ_DIAGNOSTIC_ASSERT(newSheet->IsServo() == IsStyledByServo());
4149       mStyleSheets.InsertElementAt(oldIndex, newSheet);
4150       newSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4151       if (newSheet->IsApplicable()) {
4152         AddStyleSheetToStyleSets(newSheet);
4153       }
4154 
4155       NotifyStyleSheetAdded(newSheet, true);
4156     }
4157   }
4158 
4159   EndUpdate(UPDATE_STYLE);
4160 }
4161 
InsertStyleSheetAt(StyleSheet * aSheet,size_t aIndex)4162 void nsIDocument::InsertStyleSheetAt(StyleSheet* aSheet, size_t aIndex) {
4163   MOZ_ASSERT(aSheet);
4164   MOZ_DIAGNOSTIC_ASSERT(aSheet->IsServo() == IsStyledByServo());
4165 
4166   // FIXME(emilio): Stop touching DocumentOrShadowRoot's members directly, and
4167   // use an accessor.
4168   mStyleSheets.InsertElementAt(aIndex, aSheet);
4169 
4170   aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4171 
4172   if (aSheet->IsApplicable()) {
4173     AddStyleSheetToStyleSets(aSheet);
4174   }
4175 
4176   NotifyStyleSheetAdded(aSheet, true);
4177 }
4178 
SetStyleSheetApplicableState(StyleSheet * aSheet,bool aApplicable)4179 void nsIDocument::SetStyleSheetApplicableState(StyleSheet* aSheet,
4180                                                bool aApplicable) {
4181   NS_PRECONDITION(aSheet, "null arg");
4182 
4183   // If we're actually in the document style sheet list
4184   //
4185   // FIXME(emilio): Shadow DOM.
4186   MOZ_DIAGNOSTIC_ASSERT(aSheet->IsServo() == IsStyledByServo());
4187   if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) {
4188     if (aApplicable) {
4189       AddStyleSheetToStyleSets(aSheet);
4190     } else {
4191       RemoveStyleSheetFromStyleSets(aSheet);
4192     }
4193   }
4194 
4195   // We have to always notify, since this will be called for sheets
4196   // that are children of sheets in our style set, as well as some
4197   // sheets for HTMLEditor.
4198 
4199   NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, (aSheet));
4200 
4201   if (StyleSheetChangeEventsEnabled()) {
4202     DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
4203                                "StyleSheetApplicableStateChanged", mApplicable,
4204                                aApplicable);
4205   }
4206 
4207   if (!mSSApplicableStateNotificationPending) {
4208     MOZ_RELEASE_ASSERT(NS_IsMainThread());
4209     nsCOMPtr<nsIRunnable> notification = NewRunnableMethod(
4210         "nsIDocument::NotifyStyleSheetApplicableStateChanged", this,
4211         &nsIDocument::NotifyStyleSheetApplicableStateChanged);
4212     mSSApplicableStateNotificationPending =
4213         NS_SUCCEEDED(Dispatch(TaskCategory::Other, notification.forget()));
4214   }
4215 }
4216 
NotifyStyleSheetApplicableStateChanged()4217 void nsIDocument::NotifyStyleSheetApplicableStateChanged() {
4218   mSSApplicableStateNotificationPending = false;
4219   nsCOMPtr<nsIObserverService> observerService =
4220       mozilla::services::GetObserverService();
4221   if (observerService) {
4222     observerService->NotifyObservers(
4223         this, "style-sheet-applicable-state-changed", nullptr);
4224   }
4225 }
4226 
ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)4227 static SheetType ConvertAdditionalSheetType(
4228     nsIDocument::additionalSheetType aType) {
4229   switch (aType) {
4230     case nsIDocument::eAgentSheet:
4231       return SheetType::Agent;
4232     case nsIDocument::eUserSheet:
4233       return SheetType::User;
4234     case nsIDocument::eAuthorSheet:
4235       return SheetType::Doc;
4236     default:
4237       MOZ_ASSERT(false, "wrong type");
4238       // we must return something although this should never happen
4239       return SheetType::Count;
4240   }
4241 }
4242 
FindSheet(const nsTArray<RefPtr<StyleSheet>> & aSheets,nsIURI * aSheetURI)4243 static int32_t FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets,
4244                          nsIURI* aSheetURI) {
4245   for (int32_t i = aSheets.Length() - 1; i >= 0; i--) {
4246     bool bEqual;
4247     nsIURI* uri = aSheets[i]->GetSheetURI();
4248 
4249     if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4250       return i;
4251   }
4252 
4253   return -1;
4254 }
4255 
LoadAdditionalStyleSheet(additionalSheetType aType,nsIURI * aSheetURI)4256 nsresult nsIDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
4257                                                nsIURI* aSheetURI) {
4258   NS_PRECONDITION(aSheetURI, "null arg");
4259 
4260   // Checking if we have loaded this one already.
4261   if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4262     return NS_ERROR_INVALID_ARG;
4263 
4264   // Loading the sheet sync.
4265   RefPtr<css::Loader> loader =
4266       new css::Loader(GetStyleBackendType(), GetDocGroup());
4267 
4268   css::SheetParsingMode parsingMode;
4269   switch (aType) {
4270     case nsIDocument::eAgentSheet:
4271       parsingMode = css::eAgentSheetFeatures;
4272       break;
4273 
4274     case nsIDocument::eUserSheet:
4275       parsingMode = css::eUserSheetFeatures;
4276       break;
4277 
4278     case nsIDocument::eAuthorSheet:
4279       parsingMode = css::eAuthorSheetFeatures;
4280       break;
4281 
4282     default:
4283       MOZ_CRASH("impossible value for aType");
4284   }
4285 
4286   RefPtr<StyleSheet> sheet;
4287   nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet);
4288   NS_ENSURE_SUCCESS(rv, rv);
4289 
4290   sheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4291   MOZ_ASSERT(sheet->IsApplicable());
4292 
4293   return AddAdditionalStyleSheet(aType, sheet);
4294 }
4295 
AddAdditionalStyleSheet(additionalSheetType aType,StyleSheet * aSheet)4296 nsresult nsIDocument::AddAdditionalStyleSheet(additionalSheetType aType,
4297                                               StyleSheet* aSheet) {
4298   if (mAdditionalSheets[aType].Contains(aSheet)) return NS_ERROR_INVALID_ARG;
4299 
4300   if (!aSheet->IsApplicable()) return NS_ERROR_INVALID_ARG;
4301 
4302   mAdditionalSheets[aType].AppendElement(aSheet);
4303 
4304   BeginUpdate(UPDATE_STYLE);
4305   nsCOMPtr<nsIPresShell> shell = GetShell();
4306   if (shell) {
4307     SheetType type = ConvertAdditionalSheetType(aType);
4308     shell->StyleSet()->AppendStyleSheet(type, aSheet);
4309   }
4310 
4311   // Passing false, so documet.styleSheets.length will not be affected by
4312   // these additional sheets.
4313   NotifyStyleSheetAdded(aSheet, false);
4314   EndUpdate(UPDATE_STYLE);
4315   return NS_OK;
4316 }
4317 
RemoveAdditionalStyleSheet(additionalSheetType aType,nsIURI * aSheetURI)4318 void nsIDocument::RemoveAdditionalStyleSheet(additionalSheetType aType,
4319                                              nsIURI* aSheetURI) {
4320   MOZ_ASSERT(aSheetURI);
4321 
4322   nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType];
4323 
4324   int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4325   if (i >= 0) {
4326     RefPtr<StyleSheet> sheetRef = sheets[i];
4327     sheets.RemoveElementAt(i);
4328 
4329     BeginUpdate(UPDATE_STYLE);
4330     if (!mIsGoingAway) {
4331       MOZ_ASSERT(sheetRef->IsApplicable());
4332       nsCOMPtr<nsIPresShell> shell = GetShell();
4333       if (shell) {
4334         SheetType type = ConvertAdditionalSheetType(aType);
4335         shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4336       }
4337     }
4338 
4339     // Passing false, so documet.styleSheets.length will not be affected by
4340     // these additional sheets.
4341     NotifyStyleSheetRemoved(sheetRef, false);
4342     EndUpdate(UPDATE_STYLE);
4343 
4344     sheetRef->ClearAssociatedDocument();
4345   }
4346 }
4347 
GetScopeObject() const4348 nsIGlobalObject* nsDocument::GetScopeObject() const {
4349   nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4350   return scope;
4351 }
4352 
SetScopeObject(nsIGlobalObject * aGlobal)4353 void nsDocument::SetScopeObject(nsIGlobalObject* aGlobal) {
4354   mScopeObject = do_GetWeakReference(aGlobal);
4355   if (aGlobal) {
4356     mHasHadScriptHandlingObject = true;
4357 
4358     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
4359     if (window) {
4360       // We want to get the tabgroup unconditionally, such that we can make
4361       // certain that it is cached in the inner window early enough.
4362       mozilla::dom::TabGroup* tabgroup = window->TabGroup();
4363       // We should already have the principal, and now that we have been added
4364       // to a window, we should be able to join a DocGroup!
4365       nsAutoCString docGroupKey;
4366       nsresult rv =
4367           mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
4368       if (mDocGroup) {
4369         if (NS_SUCCEEDED(rv)) {
4370           MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
4371         }
4372         MOZ_RELEASE_ASSERT(mDocGroup->GetTabGroup() == tabgroup);
4373       } else {
4374         mDocGroup = tabgroup->AddDocument(docGroupKey, this);
4375         MOZ_ASSERT(mDocGroup);
4376       }
4377     }
4378   }
4379 }
4380 
CheckIfContainsEMEContent(nsISupports * aSupports,void * aContainsEME)4381 static void CheckIfContainsEMEContent(nsISupports* aSupports,
4382                                       void* aContainsEME) {
4383   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4384   if (auto mediaElem = HTMLMediaElement::FromContentOrNull(content)) {
4385     bool* contains = static_cast<bool*>(aContainsEME);
4386     if (mediaElem->GetMediaKeys()) {
4387       *contains = true;
4388     }
4389   }
4390 }
4391 
ContainsEMEContent()4392 bool nsDocument::ContainsEMEContent() {
4393   bool containsEME = false;
4394   EnumerateActivityObservers(CheckIfContainsEMEContent,
4395                              static_cast<void*>(&containsEME));
4396   return containsEME;
4397 }
4398 
CheckIfContainsMSEContent(nsISupports * aSupports,void * aContainsMSE)4399 static void CheckIfContainsMSEContent(nsISupports* aSupports,
4400                                       void* aContainsMSE) {
4401   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4402   if (auto mediaElem = HTMLMediaElement::FromContentOrNull(content)) {
4403     bool* contains = static_cast<bool*>(aContainsMSE);
4404     RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
4405     if (ms) {
4406       *contains = true;
4407     }
4408   }
4409 }
4410 
ContainsMSEContent()4411 bool nsDocument::ContainsMSEContent() {
4412   bool containsMSE = false;
4413   EnumerateActivityObservers(CheckIfContainsMSEContent,
4414                              static_cast<void*>(&containsMSE));
4415   return containsMSE;
4416 }
4417 
NotifyActivityChanged(nsISupports * aSupports,void * aUnused)4418 static void NotifyActivityChanged(nsISupports* aSupports, void* aUnused) {
4419   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4420   if (auto mediaElem = HTMLMediaElement::FromContentOrNull(content)) {
4421     mediaElem->NotifyOwnerDocumentActivityChanged();
4422   }
4423   nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(
4424       do_QueryInterface(aSupports));
4425   if (objectLoadingContent) {
4426     nsObjectLoadingContent* olc =
4427         static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4428     olc->NotifyOwnerDocumentActivityChanged();
4429   }
4430   nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(
4431       do_QueryInterface(aSupports));
4432   if (objectDocumentActivity) {
4433     objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
4434   }
4435 }
4436 
IsTopLevelWindowInactive() const4437 bool nsIDocument::IsTopLevelWindowInactive() const {
4438   nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShell();
4439   if (!treeItem) {
4440     return false;
4441   }
4442 
4443   nsCOMPtr<nsIDocShellTreeItem> rootItem;
4444   treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
4445   if (!rootItem) {
4446     return false;
4447   }
4448 
4449   nsCOMPtr<nsPIDOMWindowOuter> domWindow = rootItem->GetWindow();
4450   return domWindow && !domWindow->IsActive();
4451 }
4452 
SetContainer(nsDocShell * aContainer)4453 void nsIDocument::SetContainer(nsDocShell* aContainer) {
4454   if (aContainer) {
4455     mDocumentContainer = aContainer;
4456   } else {
4457     mDocumentContainer = WeakPtr<nsDocShell>();
4458   }
4459 
4460   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4461 
4462   // IsTopLevelWindowInactive depends on the docshell, so
4463   // update the cached value now that it's available.
4464   UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
4465   if (!aContainer) {
4466     return;
4467   }
4468 
4469   // Get the Docshell
4470   if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4471     // check if same type root
4472     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4473     aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4474     NS_ASSERTION(
4475         sameTypeRoot,
4476         "No document shell root tree item from document shell tree item!");
4477 
4478     if (sameTypeRoot == aContainer) {
4479       static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4480     }
4481 
4482     static_cast<nsDocument*>(this)->SetIsContentDocument(true);
4483   }
4484 
4485   mAncestorPrincipals = aContainer->AncestorPrincipals();
4486   mAncestorOuterWindowIDs = aContainer->AncestorOuterWindowIDs();
4487 }
4488 
GetContainer() const4489 nsISupports* nsIDocument::GetContainer() const {
4490   return static_cast<nsIDocShell*>(mDocumentContainer);
4491 }
4492 
SetScriptGlobalObject(nsIScriptGlobalObject * aScriptGlobalObject)4493 void nsDocument::SetScriptGlobalObject(
4494     nsIScriptGlobalObject* aScriptGlobalObject) {
4495   MOZ_ASSERT(aScriptGlobalObject || !mAnimationController ||
4496                  mAnimationController->IsPausedByType(
4497                      nsSMILTimeContainer::PAUSE_PAGEHIDE |
4498                      nsSMILTimeContainer::PAUSE_BEGIN),
4499              "Clearing window pointer while animations are unpaused");
4500 
4501   if (mScriptGlobalObject && !aScriptGlobalObject) {
4502     // We're detaching from the window.  We need to grab a pointer to
4503     // our layout history state now.
4504     mLayoutHistoryState = GetLayoutHistoryState();
4505 
4506     // Also make sure to remove our onload blocker now if we haven't done it yet
4507     if (mOnloadBlockCount != 0) {
4508       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4509       if (loadGroup) {
4510         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4511       }
4512     }
4513 
4514     ErrorResult error;
4515     if (GetController().isSome()) {
4516       imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
4517       if (loader) {
4518         loader->ClearCacheForControlledDocument(this);
4519       }
4520 
4521       // We may become controlled again if this document comes back out
4522       // of bfcache.  Clear our state to allow that to happen.  Only
4523       // clear this flag if we are actually controlled, though, so pages
4524       // that were force reloaded don't become controlled when they
4525       // come out of bfcache.
4526       mMaybeServiceWorkerControlled = false;
4527     }
4528   }
4529 
4530   // BlockOnload() might be called before mScriptGlobalObject is set.
4531   // We may need to add the blocker once mScriptGlobalObject is set.
4532   bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
4533 
4534   mScriptGlobalObject = aScriptGlobalObject;
4535 
4536   if (needOnloadBlocker) {
4537     EnsureOnloadBlocker();
4538   }
4539 
4540   UpdateFrameRequestCallbackSchedulingState();
4541 
4542   if (aScriptGlobalObject) {
4543     // Go back to using the docshell for the layout history state
4544     mLayoutHistoryState = nullptr;
4545     SetScopeObject(aScriptGlobalObject);
4546     mHasHadDefaultView = true;
4547 #ifdef DEBUG
4548     if (!mWillReparent) {
4549       // We really shouldn't have a wrapper here but if we do we need to make
4550       // sure it has the correct parent.
4551       JSObject* obj = GetWrapperPreserveColor();
4552       if (obj) {
4553         JSObject* newScope = aScriptGlobalObject->GetGlobalJSObject();
4554         NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
4555                      "Wrong scope, this is really bad!");
4556       }
4557     }
4558 #endif
4559 
4560     if (mAllowDNSPrefetch) {
4561       nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4562       if (docShell) {
4563 #ifdef DEBUG
4564         nsCOMPtr<nsIWebNavigation> webNav =
4565             do_GetInterface(aScriptGlobalObject);
4566         NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4567                      "Unexpected container or script global?");
4568 #endif
4569         bool allowDNSPrefetch;
4570         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4571         mAllowDNSPrefetch = allowDNSPrefetch;
4572       }
4573     }
4574 
4575     // If we are set in a window that is already focused we should remember this
4576     // as the time the document gained focus.
4577     IgnoredErrorResult ignored;
4578     bool focused = HasFocus(ignored);
4579     if (focused) {
4580       SetLastFocusTime(TimeStamp::Now());
4581     }
4582   }
4583 
4584   // Remember the pointer to our window (or lack there of), to avoid
4585   // having to QI every time it's asked for.
4586   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
4587   mWindow = window;
4588 
4589   // Now that we know what our window is, we can flush the CSP errors to the
4590   // Web Console. We are flushing all messages that occured and were stored
4591   // in the queue prior to this point.
4592   nsCOMPtr<nsIContentSecurityPolicy> csp;
4593   NodePrincipal()->GetCsp(getter_AddRefs(csp));
4594   if (csp) {
4595     static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages();
4596   }
4597 
4598   nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4599       do_QueryInterface(GetChannel());
4600   if (internalChannel) {
4601     nsCOMArray<nsISecurityConsoleMessage> messages;
4602     DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
4603     MOZ_ASSERT(NS_SUCCEEDED(rv));
4604     SendToConsole(messages);
4605   }
4606 
4607   // Set our visibility state, but do not fire the event.  This is correct
4608   // because either we're coming out of bfcache (in which case IsVisible() will
4609   // still test false at this point and no state change will happen) or we're
4610   // doing the initial document load and don't want to fire the event for this
4611   // change.
4612   dom::VisibilityState oldState = mVisibilityState;
4613   mVisibilityState = GetVisibilityState();
4614   // When the visibility is changed, notify it to observers.
4615   // Some observers need the notification, for example HTMLMediaElement uses
4616   // it to update internal media resource allocation.
4617   // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
4618   // creation are already done before nsDocument::SetScriptGlobalObject() call.
4619   // MediaDecoder decides whether starting decoding is decided based on
4620   // document's visibility. When the MediaDecoder is created,
4621   // nsDocument::SetScriptGlobalObject() is not yet called and document is
4622   // hidden state. Therefore the MediaDecoder decides that decoding is
4623   // not yet necessary. But soon after nsDocument::SetScriptGlobalObject()
4624   // call, the document becomes not hidden. At the time, MediaDecoder needs
4625   // to know it and needs to start updating decoding.
4626   if (oldState != mVisibilityState) {
4627     EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4628   }
4629 
4630   // The global in the template contents owner document should be the same.
4631   if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
4632     mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
4633   }
4634 
4635   if (!mMaybeServiceWorkerControlled && mDocumentContainer &&
4636       mScriptGlobalObject && GetChannel()) {
4637     nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4638     uint32_t loadType;
4639     docShell->GetLoadType(&loadType);
4640 
4641     // If we are shift-reloaded, don't associate with a ServiceWorker.
4642     if (IsForceReloadType(loadType)) {
4643       NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
4644       return;
4645     }
4646 
4647     mMaybeServiceWorkerControlled = true;
4648   }
4649 }
4650 
GetScriptHandlingObjectInternal() const4651 nsIScriptGlobalObject* nsDocument::GetScriptHandlingObjectInternal() const {
4652   MOZ_ASSERT(!mScriptGlobalObject,
4653              "Do not call this when mScriptGlobalObject is set!");
4654   if (mHasHadDefaultView) {
4655     return nullptr;
4656   }
4657 
4658   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4659       do_QueryReferent(mScopeObject);
4660   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject);
4661   if (win) {
4662     nsPIDOMWindowOuter* outer = win->GetOuterWindow();
4663     if (!outer || outer->GetCurrentInnerWindow() != win) {
4664       NS_WARNING("Wrong inner/outer window combination!");
4665       return nullptr;
4666     }
4667   }
4668   return scriptHandlingObject;
4669 }
SetScriptHandlingObject(nsIScriptGlobalObject * aScriptObject)4670 void nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) {
4671   NS_ASSERTION(!mScriptGlobalObject || mScriptGlobalObject == aScriptObject,
4672                "Wrong script object!");
4673   if (aScriptObject) {
4674     SetScopeObject(aScriptObject);
4675     mHasHadDefaultView = false;
4676   }
4677 }
4678 
GetWindowInternal() const4679 nsPIDOMWindowOuter* nsDocument::GetWindowInternal() const {
4680   MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4681   // Let's use mScriptGlobalObject. Even if the document is already removed from
4682   // the docshell, the outer window might be still obtainable from the it.
4683   nsCOMPtr<nsPIDOMWindowOuter> win;
4684   if (mRemovedFromDocShell) {
4685     // The docshell returns the outer window we are done.
4686     nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer);
4687     if (kungFuDeathGrip) {
4688       win = kungFuDeathGrip->GetWindow();
4689     }
4690   } else {
4691     if (nsCOMPtr<nsPIDOMWindowInner> inner =
4692             do_QueryInterface(mScriptGlobalObject)) {
4693       // mScriptGlobalObject is always the inner window, let's get the outer.
4694       win = inner->GetOuterWindow();
4695     }
4696   }
4697 
4698   return win;
4699 }
4700 
ScriptLoader()4701 ScriptLoader* nsDocument::ScriptLoader() { return mScriptLoader; }
4702 
InternalAllowXULXBL()4703 bool nsDocument::InternalAllowXULXBL() {
4704   if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
4705     mAllowXULXBL = eTriTrue;
4706     return true;
4707   }
4708 
4709   mAllowXULXBL = eTriFalse;
4710   return false;
4711 }
4712 
4713 // Note: We don't hold a reference to the document observer; we assume
4714 // that it has a live reference to the document.
AddObserver(nsIDocumentObserver * aObserver)4715 void nsIDocument::AddObserver(nsIDocumentObserver* aObserver) {
4716   NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
4717                "Observer already in the list");
4718   mObservers.AppendElement(aObserver);
4719   AddMutationObserver(aObserver);
4720 }
4721 
RemoveObserver(nsIDocumentObserver * aObserver)4722 bool nsIDocument::RemoveObserver(nsIDocumentObserver* aObserver) {
4723   // If we're in the process of destroying the document (and we're
4724   // informing the observers of the destruction), don't remove the
4725   // observers from the list. This is not a big deal, since we
4726   // don't hold a live reference to the observers.
4727   if (!mInDestructor) {
4728     RemoveMutationObserver(aObserver);
4729     return mObservers.RemoveElement(aObserver);
4730   }
4731 
4732   return mObservers.Contains(aObserver);
4733 }
4734 
MaybeEndOutermostXBLUpdate()4735 void nsDocument::MaybeEndOutermostXBLUpdate() {
4736   // Only call BindingManager()->EndOutermostUpdate() when
4737   // we're not in an update and it is safe to run scripts.
4738   if (mUpdateNestLevel == 0 && mInXBLUpdate) {
4739     if (nsContentUtils::IsSafeToRunScript()) {
4740       mInXBLUpdate = false;
4741       BindingManager()->EndOutermostUpdate();
4742     } else if (!mInDestructor) {
4743       if (!mMaybeEndOutermostXBLUpdateRunner) {
4744         mMaybeEndOutermostXBLUpdateRunner =
4745             NewRunnableMethod("nsDocument::MaybeEndOutermostXBLUpdate", this,
4746                               &nsDocument::MaybeEndOutermostXBLUpdate);
4747       }
4748       nsContentUtils::AddScriptRunner(mMaybeEndOutermostXBLUpdateRunner);
4749     }
4750   }
4751 }
4752 
BeginUpdate(nsUpdateType aUpdateType)4753 void nsIDocument::BeginUpdate(nsUpdateType aUpdateType) {
4754   // If the document is going away, then it's probably okay to do things to it
4755   // in the wrong DocGroup. We're unlikely to run JS or do anything else
4756   // observable at this point. We reach this point when cycle collecting a
4757   // <link> element and the unlink code removes a style sheet.
4758   if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion &&
4759       !mIgnoreDocGroupMismatches) {
4760     mDocGroup->ValidateAccess();
4761   }
4762 
4763   if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
4764     mInXBLUpdate = true;
4765     BindingManager()->BeginOutermostUpdate();
4766   }
4767 
4768   ++mUpdateNestLevel;
4769   nsContentUtils::AddScriptBlocker();
4770   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
4771 }
4772 
EndUpdate(nsUpdateType aUpdateType)4773 void nsDocument::EndUpdate(nsUpdateType aUpdateType) {
4774   NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
4775 
4776   nsContentUtils::RemoveScriptBlocker();
4777 
4778   --mUpdateNestLevel;
4779 
4780   // This set of updates may have created XBL bindings.  Let the
4781   // binding manager know we're done.
4782   MaybeEndOutermostXBLUpdate();
4783 
4784   MaybeInitializeFinalizeFrameLoaders();
4785 }
4786 
BeginLoad()4787 void nsDocument::BeginLoad() {
4788   MOZ_ASSERT(!mDidCallBeginLoad);
4789   mDidCallBeginLoad = true;
4790 
4791   // Block onload here to prevent having to deal with blocking and
4792   // unblocking it while we know the document is loading.
4793   BlockOnload();
4794   mDidFireDOMContentLoaded = false;
4795   BlockDOMContentLoaded();
4796 
4797   if (mScriptLoader) {
4798     mScriptLoader->BeginDeferringScripts();
4799   }
4800 
4801   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
4802 }
4803 
ReportEmptyGetElementByIdArg()4804 void nsDocument::ReportEmptyGetElementByIdArg() {
4805   nsContentUtils::ReportEmptyGetElementByIdArg(this);
4806 }
4807 
AddIDTargetObserver(nsAtom * aID,IDTargetObserver aObserver,void * aData,bool aForImage)4808 Element* nsDocument::AddIDTargetObserver(nsAtom* aID,
4809                                          IDTargetObserver aObserver,
4810                                          void* aData, bool aForImage) {
4811   nsDependentAtomString id(aID);
4812 
4813   if (!CheckGetElementByIdArg(id)) return nullptr;
4814 
4815   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
4816   NS_ENSURE_TRUE(entry, nullptr);
4817 
4818   entry->AddContentChangeCallback(aObserver, aData, aForImage);
4819   return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
4820 }
4821 
RemoveIDTargetObserver(nsAtom * aID,IDTargetObserver aObserver,void * aData,bool aForImage)4822 void nsDocument::RemoveIDTargetObserver(nsAtom* aID, IDTargetObserver aObserver,
4823                                         void* aData, bool aForImage) {
4824   nsDependentAtomString id(aID);
4825 
4826   if (!CheckGetElementByIdArg(id)) return;
4827 
4828   nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
4829   if (!entry) {
4830     return;
4831   }
4832 
4833   entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
4834 }
4835 
MozSetImageElement(const nsAString & aImageElementId,Element * aElement)4836 void nsDocument::MozSetImageElement(const nsAString& aImageElementId,
4837                                     Element* aElement) {
4838   if (aImageElementId.IsEmpty()) return;
4839 
4840   // Hold a script blocker while calling SetImageElement since that can call
4841   // out to id-observers
4842   nsAutoScriptBlocker scriptBlocker;
4843 
4844   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aImageElementId);
4845   if (entry) {
4846     entry->SetImageElement(aElement);
4847     if (entry->IsEmpty()) {
4848       mIdentifierMap.RemoveEntry(entry);
4849     }
4850   }
4851 }
4852 
LookupImageElement(const nsAString & aId)4853 Element* nsDocument::LookupImageElement(const nsAString& aId) {
4854   if (aId.IsEmpty()) return nullptr;
4855 
4856   nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
4857   return entry ? entry->GetImageIdElement() : nullptr;
4858 }
4859 
DispatchContentLoadedEvents()4860 void nsDocument::DispatchContentLoadedEvents() {
4861   // If you add early returns from this method, make sure you're
4862   // calling UnblockOnload properly.
4863 
4864   // Unpin references to preloaded images
4865   mPreloadingImages.Clear();
4866 
4867   // DOM manipulation after content loaded should not care if the element
4868   // came from the preloader.
4869   mPreloadedPreconnects.Clear();
4870 
4871   if (mTiming) {
4872     mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
4873   }
4874 
4875   // Dispatch observer notification to notify observers document is interactive.
4876   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
4877   if (os) {
4878     nsIPrincipal* principal = GetPrincipal();
4879     os->NotifyObservers(static_cast<nsIDocument*>(this),
4880                         nsContentUtils::IsSystemPrincipal(principal)
4881                             ? "chrome-document-interactive"
4882                             : "content-document-interactive",
4883                         nullptr);
4884   }
4885 
4886   // Fire a DOM event notifying listeners that this document has been
4887   // loaded (excluding images and other loads initiated by this
4888   // document).
4889   nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
4890                                        NS_LITERAL_STRING("DOMContentLoaded"),
4891                                        true, false);
4892 
4893   if (MayStartLayout()) {
4894     MaybeResolveReadyForIdle();
4895   }
4896 
4897   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
4898   nsIDocShell* docShell = this->GetDocShell();
4899 
4900   if (timelines && timelines->HasConsumer(docShell)) {
4901     timelines->AddMarkerForDocShell(
4902         docShell,
4903         MakeUnique<DocLoadingTimelineMarker>("document::DOMContentLoaded"));
4904   }
4905 
4906   if (mTiming) {
4907     mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
4908   }
4909 
4910   // If this document is a [i]frame, fire a DOMFrameContentLoaded
4911   // event on all parent documents notifying that the HTML (excluding
4912   // other external files such as images and stylesheets) in a frame
4913   // has finished loading.
4914 
4915   // target_frame is the [i]frame element that will be used as the
4916   // target for the event. It's the [i]frame whose content is done
4917   // loading.
4918   nsCOMPtr<EventTarget> target_frame;
4919 
4920   if (mParentDocument) {
4921     target_frame = mParentDocument->FindContentForSubDocument(this);
4922   }
4923 
4924   if (target_frame) {
4925     nsCOMPtr<nsIDocument> parent = mParentDocument;
4926     do {
4927       RefPtr<Event> event;
4928       if (parent) {
4929         IgnoredErrorResult ignored;
4930         event = parent->CreateEvent(NS_LITERAL_STRING("Events"),
4931                                     CallerType::System, ignored);
4932       }
4933 
4934       if (event) {
4935         event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
4936                          true);
4937 
4938         event->SetTarget(target_frame);
4939         event->SetTrusted(true);
4940 
4941         // To dispatch this event we must manually call
4942         // EventDispatcher::Dispatch() on the ancestor document since the
4943         // target is not in the same document, so the event would never reach
4944         // the ancestor document if we used the normal event
4945         // dispatching code.
4946 
4947         WidgetEvent* innerEvent = event->WidgetEventPtr();
4948         if (innerEvent) {
4949           nsEventStatus status = nsEventStatus_eIgnore;
4950 
4951           RefPtr<nsPresContext> context = parent->GetPresContext();
4952 
4953           if (context) {
4954             EventDispatcher::Dispatch(parent, context, innerEvent, event,
4955                                       &status);
4956           }
4957         }
4958       }
4959 
4960       parent = parent->GetParentDocument();
4961     } while (parent);
4962   }
4963 
4964   // If the document has a manifest attribute, fire a MozApplicationManifest
4965   // event.
4966   Element* root = GetRootElement();
4967   if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
4968     nsContentUtils::DispatchChromeEvent(
4969         this, static_cast<nsIDocument*>(this),
4970         NS_LITERAL_STRING("MozApplicationManifest"), true, true);
4971   }
4972 
4973   if (mMaybeServiceWorkerControlled) {
4974     using mozilla::dom::ServiceWorkerManager;
4975     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
4976     if (swm) {
4977       Maybe<ClientInfo> clientInfo = GetClientInfo();
4978       if (clientInfo.isSome()) {
4979         swm->MaybeCheckNavigationUpdate(clientInfo.ref());
4980       }
4981     }
4982   }
4983 
4984   UnblockOnload(true);
4985 }
4986 
EndLoad()4987 void nsDocument::EndLoad() {
4988   // EndLoad may have been called without a matching call to BeginLoad, in the
4989   // case of a failed parse (for example, due to timeout). In such a case, we
4990   // still want to execute part of this code to do appropriate cleanup, but we
4991   // gate part of it because it is intended to match 1-for-1 with calls to
4992   // BeginLoad. We have an explicit flag bit for this purpose, since it's
4993   // complicated and error prone to derive this condition from other related
4994   // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
4995 
4996   // Part 1: Code that always executes to cleanup end of parsing, whether
4997   // that parsing was successful or not.
4998 
4999   // Drop the ref to our parser, if any, but keep hold of the sink so that we
5000   // can flush it from FlushPendingNotifications as needed.  We might have to
5001   // do that to get a StartLayout() to happen.
5002   if (mParser) {
5003     mWeakSink = do_GetWeakReference(mParser->GetContentSink());
5004     mParser = nullptr;
5005   }
5006 
5007   NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
5008 
5009   // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
5010 
5011   if (!mDidCallBeginLoad) {
5012     return;
5013   }
5014   mDidCallBeginLoad = false;
5015 
5016   UnblockDOMContentLoaded();
5017 }
5018 
UnblockDOMContentLoaded()5019 void nsDocument::UnblockDOMContentLoaded() {
5020   MOZ_ASSERT(mBlockDOMContentLoaded);
5021   if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
5022     return;
5023   }
5024 
5025   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
5026           ("DOCUMENT %p UnblockDOMContentLoaded", this));
5027 
5028   mDidFireDOMContentLoaded = true;
5029 
5030   MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
5031   if (!mSynchronousDOMContentLoaded) {
5032     MOZ_RELEASE_ASSERT(NS_IsMainThread());
5033     nsCOMPtr<nsIRunnable> ev =
5034         NewRunnableMethod("nsDocument::DispatchContentLoadedEvents", this,
5035                           &nsDocument::DispatchContentLoadedEvents);
5036     Dispatch(TaskCategory::Other, ev.forget());
5037   } else {
5038     DispatchContentLoadedEvents();
5039   }
5040 }
5041 
ContentStateChanged(nsIContent * aContent,EventStates aStateMask)5042 void nsIDocument::ContentStateChanged(nsIContent* aContent,
5043                                       EventStates aStateMask) {
5044   NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
5045                   "Someone forgot a scriptblocker");
5046   NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
5047                                (this, aContent, aStateMask));
5048 }
5049 
DocumentStatesChanged(EventStates aStateMask)5050 void nsIDocument::DocumentStatesChanged(EventStates aStateMask) {
5051   UpdateDocumentStates(aStateMask);
5052   NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
5053 }
5054 
StyleRuleChanged(StyleSheet * aSheet,css::Rule * aStyleRule)5055 void nsIDocument::StyleRuleChanged(StyleSheet* aSheet, css::Rule* aStyleRule) {
5056   if (!StyleSheetChangeEventsEnabled()) {
5057     return;
5058   }
5059 
5060   DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleChanged", mRule,
5061                              aStyleRule);
5062 }
5063 
StyleRuleAdded(StyleSheet * aSheet,css::Rule * aStyleRule)5064 void nsIDocument::StyleRuleAdded(StyleSheet* aSheet, css::Rule* aStyleRule) {
5065   if (!StyleSheetChangeEventsEnabled()) {
5066     return;
5067   }
5068 
5069   DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleAdded", mRule,
5070                              aStyleRule);
5071 }
5072 
StyleRuleRemoved(StyleSheet * aSheet,css::Rule * aStyleRule)5073 void nsIDocument::StyleRuleRemoved(StyleSheet* aSheet, css::Rule* aStyleRule) {
5074   if (!StyleSheetChangeEventsEnabled()) {
5075     return;
5076   }
5077 
5078   DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleRemoved", mRule,
5079                              aStyleRule);
5080 }
5081 
5082 #undef DO_STYLESHEET_NOTIFICATION
5083 
InsertAnonymousContent(Element & aElement,ErrorResult & aRv)5084 already_AddRefed<AnonymousContent> nsIDocument::InsertAnonymousContent(
5085     Element& aElement, ErrorResult& aRv) {
5086   nsIPresShell* shell = GetShell();
5087   if (!shell || !shell->GetCanvasFrame()) {
5088     aRv.Throw(NS_ERROR_UNEXPECTED);
5089     return nullptr;
5090   }
5091 
5092   nsAutoScriptBlocker scriptBlocker;
5093   nsCOMPtr<Element> container =
5094       shell->GetCanvasFrame()->GetCustomContentContainer();
5095   if (!container) {
5096     aRv.Throw(NS_ERROR_UNEXPECTED);
5097     return nullptr;
5098   }
5099 
5100   // Clone the node to avoid returning a direct reference
5101   nsCOMPtr<nsINode> clonedElement = aElement.CloneNode(true, aRv);
5102   if (aRv.Failed()) {
5103     return nullptr;
5104   }
5105 
5106   // Insert the element into the container
5107   nsresult rv;
5108   rv = container->AppendChildTo(clonedElement->AsContent(), true);
5109   if (NS_FAILED(rv)) {
5110     return nullptr;
5111   }
5112 
5113   RefPtr<AnonymousContent> anonymousContent =
5114       new AnonymousContent(clonedElement->AsElement());
5115   mAnonymousContents.AppendElement(anonymousContent);
5116 
5117   shell->GetCanvasFrame()->ShowCustomContentContainer();
5118 
5119   return anonymousContent.forget();
5120 }
5121 
RemoveAnonymousContent(AnonymousContent & aContent,ErrorResult & aRv)5122 void nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
5123                                          ErrorResult& aRv) {
5124   nsIPresShell* shell = GetShell();
5125   if (!shell || !shell->GetCanvasFrame()) {
5126     aRv.Throw(NS_ERROR_UNEXPECTED);
5127     return;
5128   }
5129 
5130   nsAutoScriptBlocker scriptBlocker;
5131   nsCOMPtr<Element> container =
5132       shell->GetCanvasFrame()->GetCustomContentContainer();
5133   if (!container) {
5134     aRv.Throw(NS_ERROR_UNEXPECTED);
5135     return;
5136   }
5137 
5138   // Iterate over mAnonymousContents to find and remove the given node.
5139   for (size_t i = 0, len = mAnonymousContents.Length(); i < len; ++i) {
5140     if (mAnonymousContents[i] == &aContent) {
5141       // Get the node from the customContent
5142       nsCOMPtr<Element> node = aContent.GetContentNode();
5143 
5144       // Remove the entry in mAnonymousContents
5145       mAnonymousContents.RemoveElementAt(i);
5146 
5147       // Remove the node from its container
5148       container->RemoveChild(*node, aRv);
5149       if (aRv.Failed()) {
5150         return;
5151       }
5152 
5153       break;
5154     }
5155   }
5156   if (mAnonymousContents.IsEmpty()) {
5157     shell->GetCanvasFrame()->HideCustomContentContainer();
5158   }
5159 }
5160 
GetAnonRootIfInAnonymousContentContainer(nsINode * aNode) const5161 Element* nsIDocument::GetAnonRootIfInAnonymousContentContainer(
5162     nsINode* aNode) const {
5163   if (!aNode->IsInNativeAnonymousSubtree()) {
5164     return nullptr;
5165   }
5166 
5167   nsIPresShell* shell = GetShell();
5168   if (!shell || !shell->GetCanvasFrame()) {
5169     return nullptr;
5170   }
5171 
5172   nsAutoScriptBlocker scriptBlocker;
5173   nsCOMPtr<Element> customContainer =
5174       shell->GetCanvasFrame()->GetCustomContentContainer();
5175   if (!customContainer) {
5176     return nullptr;
5177   }
5178 
5179   // An arbitrary number of elements can be inserted as children of the custom
5180   // container frame.  We want the one that was added that contains aNode, so
5181   // we need to keep track of the last child separately using |child| here.
5182   nsINode* child = aNode;
5183   nsINode* parent = aNode->GetParentNode();
5184   while (parent && parent->IsInNativeAnonymousSubtree()) {
5185     if (parent == customContainer) {
5186       return child->IsElement() ? child->AsElement() : nullptr;
5187     }
5188     child = parent;
5189     parent = child->GetParentNode();
5190   }
5191   return nullptr;
5192 }
5193 
GetClientInfo() const5194 Maybe<ClientInfo> nsIDocument::GetClientInfo() const {
5195   nsPIDOMWindowInner* inner = GetInnerWindow();
5196   if (inner) {
5197     return Move(inner->GetClientInfo());
5198   }
5199   return Move(Maybe<ClientInfo>());
5200 }
5201 
GetClientState() const5202 Maybe<ClientState> nsIDocument::GetClientState() const {
5203   nsPIDOMWindowInner* inner = GetInnerWindow();
5204   if (inner) {
5205     return Move(inner->GetClientState());
5206   }
5207   return Move(Maybe<ClientState>());
5208 }
5209 
GetController() const5210 Maybe<ServiceWorkerDescriptor> nsIDocument::GetController() const {
5211   nsPIDOMWindowInner* inner = GetInnerWindow();
5212   if (inner) {
5213     return Move(inner->GetController());
5214   }
5215   return Move(Maybe<ServiceWorkerDescriptor>());
5216 }
5217 
5218 //
5219 // nsIDOMDocument interface
5220 //
GetDoctype() const5221 DocumentType* nsIDocument::GetDoctype() const {
5222   for (nsIContent* child = GetFirstChild(); child;
5223        child = child->GetNextSibling()) {
5224     if (child->NodeType() == DOCUMENT_TYPE_NODE) {
5225       return static_cast<DocumentType*>(child);
5226     }
5227   }
5228   return nullptr;
5229 }
5230 
GetImplementation(ErrorResult & rv)5231 DOMImplementation* nsDocument::GetImplementation(ErrorResult& rv) {
5232   if (!mDOMImplementation) {
5233     nsCOMPtr<nsIURI> uri;
5234     NS_NewURI(getter_AddRefs(uri), "about:blank");
5235     if (!uri) {
5236       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5237       return nullptr;
5238     }
5239     bool hasHadScriptObject = true;
5240     nsIScriptGlobalObject* scriptObject =
5241         GetScriptHandlingObject(hasHadScriptObject);
5242     if (!scriptObject && hasHadScriptObject) {
5243       rv.Throw(NS_ERROR_UNEXPECTED);
5244       return nullptr;
5245     }
5246     mDOMImplementation = new DOMImplementation(
5247         this, scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5248   }
5249 
5250   return mDOMImplementation;
5251 }
5252 
IsLowercaseASCII(const nsAString & aValue)5253 bool IsLowercaseASCII(const nsAString& aValue) {
5254   int32_t len = aValue.Length();
5255   for (int32_t i = 0; i < len; ++i) {
5256     char16_t c = aValue[i];
5257     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5258       return false;
5259     }
5260   }
5261   return true;
5262 }
5263 
5264 // We only support pseudo-elements with two colons in this function.
GetPseudoElementType(const nsString & aString,ErrorResult & aRv)5265 static CSSPseudoElementType GetPseudoElementType(const nsString& aString,
5266                                                  ErrorResult& aRv) {
5267   MOZ_ASSERT(!aString.IsEmpty(),
5268              "GetPseudoElementType aString should be non-null");
5269   if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
5270     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5271     return CSSPseudoElementType::NotPseudo;
5272   }
5273   RefPtr<nsAtom> pseudo = NS_Atomize(Substring(aString, 1));
5274   return nsCSSPseudoElements::GetPseudoType(
5275       pseudo, nsCSSProps::EnabledState::eInUASheets);
5276 }
5277 
CreateElement(const nsAString & aTagName,const ElementCreationOptionsOrString & aOptions,ErrorResult & rv)5278 already_AddRefed<Element> nsDocument::CreateElement(
5279     const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions,
5280     ErrorResult& rv) {
5281   rv = nsContentUtils::CheckQName(aTagName, false);
5282   if (rv.Failed()) {
5283     return nullptr;
5284   }
5285 
5286   bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
5287   nsAutoString lcTagName;
5288   if (needsLowercase) {
5289     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5290   }
5291 
5292   const nsString* is = nullptr;
5293   CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
5294   if (aOptions.IsElementCreationOptions()) {
5295     const ElementCreationOptions& options =
5296         aOptions.GetAsElementCreationOptions();
5297 
5298     if (CustomElementRegistry::IsCustomElementEnabled() &&
5299         options.mIs.WasPassed()) {
5300       is = &options.mIs.Value();
5301     }
5302 
5303     // Check 'pseudo' and throw an exception if it's not one allowed
5304     // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
5305     if (options.mPseudo.WasPassed()) {
5306       pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
5307       if (rv.Failed() || pseudoType == CSSPseudoElementType::NotPseudo ||
5308           !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) {
5309         rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5310         return nullptr;
5311       }
5312     }
5313   }
5314 
5315   RefPtr<Element> elem = CreateElem(needsLowercase ? lcTagName : aTagName,
5316                                     nullptr, mDefaultElementType, is);
5317 
5318   if (pseudoType != CSSPseudoElementType::NotPseudo) {
5319     elem->SetPseudoElementType(pseudoType);
5320   }
5321 
5322   if (is) {
5323     elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
5324   }
5325 
5326   return elem.forget();
5327 }
5328 
CreateElementNS(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,const ElementCreationOptionsOrString & aOptions,ErrorResult & rv)5329 already_AddRefed<Element> nsDocument::CreateElementNS(
5330     const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
5331     const ElementCreationOptionsOrString& aOptions, ErrorResult& rv) {
5332   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5333   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
5334                                             mNodeInfoManager, ELEMENT_NODE,
5335                                             getter_AddRefs(nodeInfo));
5336   if (rv.Failed()) {
5337     return nullptr;
5338   }
5339 
5340   const nsString* is = nullptr;
5341   if (CustomElementRegistry::IsCustomElementEnabled() &&
5342       aOptions.IsElementCreationOptions()) {
5343     const ElementCreationOptions& options =
5344         aOptions.GetAsElementCreationOptions();
5345     if (options.mIs.WasPassed()) {
5346       is = &options.mIs.Value();
5347     }
5348   }
5349 
5350   nsCOMPtr<Element> element;
5351   rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5352                      NOT_FROM_PARSER, is);
5353   if (rv.Failed()) {
5354     return nullptr;
5355   }
5356 
5357   if (is) {
5358     element->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
5359   }
5360 
5361   return element.forget();
5362 }
5363 
CreateEmptyTextNode() const5364 already_AddRefed<nsTextNode> nsIDocument::CreateEmptyTextNode() const {
5365   RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5366   return text.forget();
5367 }
5368 
CreateTextNode(const nsAString & aData) const5369 already_AddRefed<nsTextNode> nsIDocument::CreateTextNode(
5370     const nsAString& aData) const {
5371   RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5372   // Don't notify; this node is still being created.
5373   text->SetText(aData, false);
5374   return text.forget();
5375 }
5376 
CreateDocumentFragment() const5377 already_AddRefed<DocumentFragment> nsIDocument::CreateDocumentFragment() const {
5378   RefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5379   return frag.forget();
5380 }
5381 
5382 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
CreateComment(const nsAString & aData) const5383 already_AddRefed<dom::Comment> nsIDocument::CreateComment(
5384     const nsAString& aData) const {
5385   RefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5386 
5387   // Don't notify; this node is still being created.
5388   comment->SetText(aData, false);
5389   return comment.forget();
5390 }
5391 
CreateCDATASection(const nsAString & aData,ErrorResult & rv)5392 already_AddRefed<CDATASection> nsIDocument::CreateCDATASection(
5393     const nsAString& aData, ErrorResult& rv) {
5394   if (IsHTMLDocument()) {
5395     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5396     return nullptr;
5397   }
5398 
5399   if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5400     rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5401     return nullptr;
5402   }
5403 
5404   RefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5405 
5406   // Don't notify; this node is still being created.
5407   cdata->SetText(aData, false);
5408 
5409   return cdata.forget();
5410 }
5411 
5412 already_AddRefed<ProcessingInstruction>
CreateProcessingInstruction(const nsAString & aTarget,const nsAString & aData,ErrorResult & rv) const5413 nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5414                                          const nsAString& aData,
5415                                          ErrorResult& rv) const {
5416   nsresult res = nsContentUtils::CheckQName(aTarget, false);
5417   if (NS_FAILED(res)) {
5418     rv.Throw(res);
5419     return nullptr;
5420   }
5421 
5422   if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5423     rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5424     return nullptr;
5425   }
5426 
5427   RefPtr<ProcessingInstruction> pi =
5428       NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5429 
5430   return pi.forget();
5431 }
5432 
CreateAttribute(const nsAString & aName,ErrorResult & rv)5433 already_AddRefed<Attr> nsIDocument::CreateAttribute(const nsAString& aName,
5434                                                     ErrorResult& rv) {
5435   if (!mNodeInfoManager) {
5436     rv.Throw(NS_ERROR_NOT_INITIALIZED);
5437     return nullptr;
5438   }
5439 
5440   nsresult res = nsContentUtils::CheckQName(aName, false);
5441   if (NS_FAILED(res)) {
5442     rv.Throw(res);
5443     return nullptr;
5444   }
5445 
5446   nsAutoString name;
5447   if (IsHTMLDocument()) {
5448     nsContentUtils::ASCIIToLower(aName, name);
5449   } else {
5450     name = aName;
5451   }
5452 
5453   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5454   res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None,
5455                                       ATTRIBUTE_NODE, getter_AddRefs(nodeInfo));
5456   if (NS_FAILED(res)) {
5457     rv.Throw(res);
5458     return nullptr;
5459   }
5460 
5461   RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), EmptyString());
5462   return attribute.forget();
5463 }
5464 
CreateAttributeNS(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,ErrorResult & rv)5465 already_AddRefed<Attr> nsIDocument::CreateAttributeNS(
5466     const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
5467     ErrorResult& rv) {
5468   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5469   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
5470                                             mNodeInfoManager, ATTRIBUTE_NODE,
5471                                             getter_AddRefs(nodeInfo));
5472   if (rv.Failed()) {
5473     return nullptr;
5474   }
5475 
5476   RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(), EmptyString());
5477   return attribute.forget();
5478 }
5479 
AllowUnsafeHTML() const5480 bool nsIDocument::AllowUnsafeHTML() const {
5481   return (!nsContentUtils::IsSystemPrincipal(NodePrincipal()) ||
5482           mAllowUnsafeHTML);
5483 }
5484 
ResolveScheduledSVGPresAttrs()5485 void nsIDocument::ResolveScheduledSVGPresAttrs() {
5486   for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
5487     nsSVGElement* svg = iter.Get()->GetKey();
5488     svg->UpdateContentDeclarationBlock(StyleBackendType::Servo);
5489   }
5490   mLazySVGPresElements.Clear();
5491 }
5492 
BlockedTrackingNodeCount() const5493 long nsDocument::BlockedTrackingNodeCount() const {
5494   return mBlockedTrackingNodes.Length();
5495 }
5496 
BlockedTrackingNodes() const5497 already_AddRefed<nsSimpleContentList> nsDocument::BlockedTrackingNodes() const {
5498   RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
5499 
5500   nsTArray<nsWeakPtr> blockedTrackingNodes;
5501   blockedTrackingNodes = mBlockedTrackingNodes;
5502 
5503   for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
5504     nsWeakPtr weakNode = blockedTrackingNodes[i];
5505     nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
5506     // Consider only nodes to which we have managed to get strong references.
5507     // Coping with nullptrs since it's expected for nodes to disappear when
5508     // nobody else is referring to them.
5509     if (node) {
5510       list->AppendElement(node);
5511     }
5512   }
5513 
5514   return list.forget();
5515 }
5516 
GetSelectedStyleSheetSet(nsAString & aSheetSet)5517 void nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet) {
5518   aSheetSet.Truncate();
5519 
5520   // Look through our sheets, find the selected set title
5521   size_t count = SheetCount();
5522   nsAutoString title;
5523   for (size_t index = 0; index < count; index++) {
5524     StyleSheet* sheet = SheetAt(index);
5525     NS_ASSERTION(sheet, "Null sheet in sheet list!");
5526 
5527     if (sheet->Disabled()) {
5528       // Disabled sheets don't affect the currently selected set
5529       continue;
5530     }
5531 
5532     sheet->GetTitle(title);
5533 
5534     if (aSheetSet.IsEmpty()) {
5535       aSheetSet = title;
5536     } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
5537       // Sheets from multiple sets enabled; return null string, per spec.
5538       SetDOMStringToNull(aSheetSet);
5539       return;
5540     }
5541   }
5542 }
5543 
SetSelectedStyleSheetSet(const nsAString & aSheetSet)5544 void nsIDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet) {
5545   if (DOMStringIsNull(aSheetSet)) {
5546     return;
5547   }
5548 
5549   // Must update mLastStyleSheetSet before doing anything else with stylesheets
5550   // or CSSLoaders.
5551   mLastStyleSheetSet = aSheetSet;
5552   EnableStyleSheetsForSetInternal(aSheetSet, true);
5553 }
5554 
GetLastStyleSheetSet(nsAString & aSheetSet)5555 void nsIDocument::GetLastStyleSheetSet(nsAString& aSheetSet) {
5556   aSheetSet = mLastStyleSheetSet;
5557 }
5558 
GetPreferredStyleSheetSet(nsAString & aSheetSet)5559 void nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet) {
5560   GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
5561 }
5562 
StyleSheetSets()5563 DOMStringList* nsIDocument::StyleSheetSets() {
5564   if (!mStyleSheetSetList) {
5565     mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
5566   }
5567   return mStyleSheetSetList;
5568 }
5569 
EnableStyleSheetsForSet(const nsAString & aSheetSet)5570 void nsIDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet) {
5571   // Per spec, passing in null is a no-op.
5572   if (!DOMStringIsNull(aSheetSet)) {
5573     // Note: must make sure to not change the CSSLoader's preferred sheet --
5574     // that value should be equal to either our lastStyleSheetSet (if that's
5575     // non-null) or to our preferredStyleSheetSet.  And this method doesn't
5576     // change either of those.
5577     EnableStyleSheetsForSetInternal(aSheetSet, false);
5578   }
5579 }
5580 
EnableStyleSheetsForSetInternal(const nsAString & aSheetSet,bool aUpdateCSSLoader)5581 void nsIDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
5582                                                   bool aUpdateCSSLoader) {
5583   BeginUpdate(UPDATE_STYLE);
5584   size_t count = SheetCount();
5585   nsAutoString title;
5586   for (size_t index = 0; index < count; index++) {
5587     StyleSheet* sheet = SheetAt(index);
5588     NS_ASSERTION(sheet, "Null sheet in sheet list!");
5589 
5590     sheet->GetTitle(title);
5591     if (!title.IsEmpty()) {
5592       sheet->SetEnabled(title.Equals(aSheetSet));
5593     }
5594   }
5595   if (aUpdateCSSLoader) {
5596     CSSLoader()->SetPreferredSheet(aSheetSet);
5597   }
5598   EndUpdate(UPDATE_STYLE);
5599 }
5600 
GetCharacterSet(nsAString & aCharacterSet) const5601 void nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const {
5602   nsAutoCString charset;
5603   GetDocumentCharacterSet()->Name(charset);
5604   CopyASCIItoUTF16(charset, aCharacterSet);
5605 }
5606 
ImportNode(nsINode & aNode,bool aDeep,ErrorResult & rv) const5607 already_AddRefed<nsINode> nsIDocument::ImportNode(nsINode& aNode, bool aDeep,
5608                                                   ErrorResult& rv) const {
5609   nsINode* imported = &aNode;
5610 
5611   switch (imported->NodeType()) {
5612     case DOCUMENT_NODE: {
5613       break;
5614     }
5615     case DOCUMENT_FRAGMENT_NODE:
5616     case ATTRIBUTE_NODE:
5617     case ELEMENT_NODE:
5618     case PROCESSING_INSTRUCTION_NODE:
5619     case TEXT_NODE:
5620     case CDATA_SECTION_NODE:
5621     case COMMENT_NODE:
5622     case DOCUMENT_TYPE_NODE: {
5623       return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, nullptr, rv);
5624     }
5625     default: {
5626       NS_WARNING("Don't know how to clone this nodetype for importNode.");
5627     }
5628   }
5629 
5630   rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5631   return nullptr;
5632 }
5633 
LoadBindingDocument(const nsAString & aURI,nsIPrincipal & aSubjectPrincipal,ErrorResult & rv)5634 void nsIDocument::LoadBindingDocument(const nsAString& aURI,
5635                                       nsIPrincipal& aSubjectPrincipal,
5636                                       ErrorResult& rv) {
5637   nsCOMPtr<nsIURI> uri;
5638   rv = NS_NewURI(getter_AddRefs(uri), aURI, mCharacterSet, GetDocBaseURI());
5639   if (rv.Failed()) {
5640     return;
5641   }
5642 
5643   BindingManager()->LoadBindingDocument(this, uri, &aSubjectPrincipal);
5644 }
5645 
GetBindingParent(nsINode & aNode)5646 Element* nsIDocument::GetBindingParent(nsINode& aNode) {
5647   nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
5648   if (!content) return nullptr;
5649 
5650   nsIContent* bindingParent = content->GetBindingParent();
5651   return bindingParent ? bindingParent->AsElement() : nullptr;
5652 }
5653 
GetElementByAttribute(Element * aElement,nsAtom * aAttrName,const nsAString & aAttrValue,bool aUniversalMatch)5654 static Element* GetElementByAttribute(Element* aElement, nsAtom* aAttrName,
5655                                       const nsAString& aAttrValue,
5656                                       bool aUniversalMatch) {
5657   if (aUniversalMatch ? aElement->HasAttr(kNameSpaceID_None, aAttrName)
5658                       : aElement->AttrValueIs(kNameSpaceID_None, aAttrName,
5659                                               aAttrValue, eCaseMatters)) {
5660     return aElement;
5661   }
5662 
5663   for (nsIContent* child = aElement->GetFirstChild(); child;
5664        child = child->GetNextSibling()) {
5665     if (!child->IsElement()) {
5666       continue;
5667     }
5668 
5669     Element* matchedElement = GetElementByAttribute(
5670         child->AsElement(), aAttrName, aAttrValue, aUniversalMatch);
5671     if (matchedElement) return matchedElement;
5672   }
5673 
5674   return nullptr;
5675 }
5676 
GetAnonymousElementByAttribute(nsIContent * aElement,nsAtom * aAttrName,const nsAString & aAttrValue) const5677 Element* nsDocument::GetAnonymousElementByAttribute(
5678     nsIContent* aElement, nsAtom* aAttrName,
5679     const nsAString& aAttrValue) const {
5680   nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
5681   if (!nodeList) return nullptr;
5682 
5683   uint32_t length = 0;
5684   nodeList->GetLength(&length);
5685 
5686   bool universalMatch = aAttrValue.EqualsLiteral("*");
5687 
5688   for (uint32_t i = 0; i < length; ++i) {
5689     nsIContent* current = nodeList->Item(i);
5690     if (!current->IsElement()) {
5691       continue;
5692     }
5693 
5694     Element* matchedElm = GetElementByAttribute(current->AsElement(), aAttrName,
5695                                                 aAttrValue, universalMatch);
5696     if (matchedElm) return matchedElm;
5697   }
5698 
5699   return nullptr;
5700 }
5701 
GetAnonymousElementByAttribute(Element & aElement,const nsAString & aAttrName,const nsAString & aAttrValue)5702 Element* nsIDocument::GetAnonymousElementByAttribute(
5703     Element& aElement, const nsAString& aAttrName,
5704     const nsAString& aAttrValue) {
5705   RefPtr<nsAtom> attribute = NS_Atomize(aAttrName);
5706 
5707   return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
5708 }
5709 
GetAnonymousNodes(Element & aElement)5710 nsINodeList* nsIDocument::GetAnonymousNodes(Element& aElement) {
5711   return BindingManager()->GetAnonymousNodesFor(&aElement);
5712 }
5713 
CreateRange(ErrorResult & rv)5714 already_AddRefed<nsRange> nsIDocument::CreateRange(ErrorResult& rv) {
5715   RefPtr<nsRange> range = new nsRange(this);
5716   nsresult res = range->CollapseTo(this, 0);
5717   if (NS_FAILED(res)) {
5718     rv.Throw(res);
5719     return nullptr;
5720   }
5721 
5722   return range.forget();
5723 }
5724 
CreateNodeIterator(nsINode & aRoot,uint32_t aWhatToShow,NodeFilter * aFilter,ErrorResult & rv) const5725 already_AddRefed<NodeIterator> nsIDocument::CreateNodeIterator(
5726     nsINode& aRoot, uint32_t aWhatToShow, NodeFilter* aFilter,
5727     ErrorResult& rv) const {
5728   RefPtr<NodeIterator> iterator =
5729       new NodeIterator(&aRoot, aWhatToShow, aFilter);
5730   return iterator.forget();
5731 }
5732 
CreateTreeWalker(nsINode & aRoot,uint32_t aWhatToShow,NodeFilter * aFilter,ErrorResult & rv) const5733 already_AddRefed<TreeWalker> nsIDocument::CreateTreeWalker(
5734     nsINode& aRoot, uint32_t aWhatToShow, NodeFilter* aFilter,
5735     ErrorResult& rv) const {
5736   RefPtr<TreeWalker> walker = new TreeWalker(&aRoot, aWhatToShow, aFilter);
5737   return walker.forget();
5738 }
5739 
GetLocation() const5740 already_AddRefed<Location> nsIDocument::GetLocation() const {
5741   nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
5742 
5743   if (!w) {
5744     return nullptr;
5745   }
5746 
5747   nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(w);
5748   RefPtr<Location> loc = window->GetLocation();
5749   return loc.forget();
5750 }
5751 
GetHtmlElement() const5752 Element* nsIDocument::GetHtmlElement() const {
5753   Element* rootElement = GetRootElement();
5754   if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html))
5755     return rootElement;
5756   return nullptr;
5757 }
5758 
GetHtmlChildElement(nsAtom * aTag)5759 Element* nsIDocument::GetHtmlChildElement(nsAtom* aTag) {
5760   Element* html = GetHtmlElement();
5761   if (!html) return nullptr;
5762 
5763   // Look for the element with aTag inside html. This needs to run
5764   // forwards to find the first such element.
5765   for (nsIContent* child = html->GetFirstChild(); child;
5766        child = child->GetNextSibling()) {
5767     if (child->IsHTMLElement(aTag)) return child->AsElement();
5768   }
5769   return nullptr;
5770 }
5771 
GetBody()5772 nsGenericHTMLElement* nsIDocument::GetBody() {
5773   Element* html = GetHtmlElement();
5774   if (!html) {
5775     return nullptr;
5776   }
5777 
5778   for (nsIContent* child = html->GetFirstChild(); child;
5779        child = child->GetNextSibling()) {
5780     if (child->IsHTMLElement(nsGkAtoms::body) ||
5781         child->IsHTMLElement(nsGkAtoms::frameset)) {
5782       return static_cast<nsGenericHTMLElement*>(child);
5783     }
5784   }
5785 
5786   return nullptr;
5787 }
5788 
SetBody(nsGenericHTMLElement * newBody,ErrorResult & rv)5789 void nsIDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv) {
5790   nsCOMPtr<Element> root = GetRootElement();
5791 
5792   // The body element must be either a body tag or a frameset tag. And we must
5793   // have a root element to be able to add kids to it.
5794   if (!newBody ||
5795       !newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset) ||
5796       !root) {
5797     rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
5798     return;
5799   }
5800 
5801   // Use DOM methods so that we pass through the appropriate security checks.
5802   nsCOMPtr<Element> currentBody = GetBody();
5803   if (currentBody) {
5804     root->ReplaceChild(*newBody, *currentBody, rv);
5805   } else {
5806     root->AppendChild(*newBody, rv);
5807   }
5808 }
5809 
GetTitleElement()5810 Element* nsDocument::GetTitleElement() {
5811   // mMayHaveTitleElement will have been set to true if any HTML or SVG
5812   // <title> element has been bound to this document. So if it's false,
5813   // we know there is nothing to do here. This avoids us having to search
5814   // the whole DOM if someone calls document.title on a large document
5815   // without a title.
5816   if (!mMayHaveTitleElement) return nullptr;
5817 
5818   Element* root = GetRootElement();
5819   if (root && root->IsSVGElement(nsGkAtoms::svg)) {
5820     // In SVG, the document's title must be a child
5821     for (nsIContent* child = root->GetFirstChild(); child;
5822          child = child->GetNextSibling()) {
5823       if (child->IsSVGElement(nsGkAtoms::title)) {
5824         return child->AsElement();
5825       }
5826     }
5827     return nullptr;
5828   }
5829 
5830   // We check the HTML namespace even for non-HTML documents, except SVG.  This
5831   // matches the spec and the behavior of all tested browsers.
5832   // We avoid creating a live nsContentList since we don't need to watch for DOM
5833   // tree mutations.
5834   RefPtr<nsContentList> list = new nsContentList(
5835       this, kNameSpaceID_XHTML, nsGkAtoms::title, nsGkAtoms::title,
5836       /* aDeep = */ true,
5837       /* aLiveList = */ false);
5838 
5839   nsIContent* first = list->Item(0, false);
5840 
5841   return first ? first->AsElement() : nullptr;
5842 }
5843 
GetTitle(nsAString & aTitle)5844 void nsDocument::GetTitle(nsAString& aTitle) {
5845   aTitle.Truncate();
5846 
5847   Element* rootElement = GetRootElement();
5848   if (!rootElement) {
5849     return;
5850   }
5851 
5852   nsAutoString tmp;
5853 
5854 #ifdef MOZ_XUL
5855   if (rootElement->IsXULElement()) {
5856     rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
5857   } else
5858 #endif
5859   {
5860     Element* title = GetTitleElement();
5861     if (!title) {
5862       return;
5863     }
5864     nsContentUtils::GetNodeTextContent(title, false, tmp);
5865   }
5866 
5867   tmp.CompressWhitespace();
5868   aTitle = tmp;
5869 }
5870 
SetTitle(const nsAString & aTitle,ErrorResult & aRv)5871 void nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& aRv) {
5872   Element* rootElement = GetRootElement();
5873   if (!rootElement) {
5874     return;
5875   }
5876 
5877 #ifdef MOZ_XUL
5878   if (rootElement->IsXULElement()) {
5879     aRv =
5880         rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle, true);
5881     return;
5882   }
5883 #endif
5884 
5885   // Batch updates so that mutation events don't change "the title
5886   // element" under us
5887   mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
5888 
5889   nsCOMPtr<Element> title = GetTitleElement();
5890   if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
5891     if (!title) {
5892       RefPtr<mozilla::dom::NodeInfo> titleInfo = mNodeInfoManager->GetNodeInfo(
5893           nsGkAtoms::title, nullptr, kNameSpaceID_SVG, ELEMENT_NODE);
5894       NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
5895                        NOT_FROM_PARSER);
5896       if (!title) {
5897         return;
5898       }
5899       rootElement->InsertChildBefore(title, rootElement->GetFirstChild(), true);
5900     }
5901   } else if (rootElement->IsHTMLElement()) {
5902     if (!title) {
5903       Element* head = GetHeadElement();
5904       if (!head) {
5905         return;
5906       }
5907 
5908       RefPtr<mozilla::dom::NodeInfo> titleInfo;
5909       titleInfo = mNodeInfoManager->GetNodeInfo(
5910           nsGkAtoms::title, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
5911       title = NS_NewHTMLTitleElement(titleInfo.forget());
5912       if (!title) {
5913         return;
5914       }
5915 
5916       head->AppendChildTo(title, true);
5917     }
5918   } else {
5919     return;
5920   }
5921 
5922   aRv = nsContentUtils::SetNodeTextContent(title, aTitle, false);
5923 }
5924 
NotifyPossibleTitleChange(bool aBoundTitleElement)5925 void nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement) {
5926   NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
5927                "Setting a title while unlinking or destroying the element?");
5928   if (mInUnlinkOrDeletion) {
5929     return;
5930   }
5931 
5932   if (aBoundTitleElement) {
5933     mMayHaveTitleElement = true;
5934   }
5935   if (mPendingTitleChangeEvent.IsPending()) return;
5936 
5937   MOZ_RELEASE_ASSERT(NS_IsMainThread());
5938   RefPtr<nsRunnableMethod<nsDocument, void, false>> event =
5939       NewNonOwningRunnableMethod("nsDocument::DoNotifyPossibleTitleChange",
5940                                  this,
5941                                  &nsDocument::DoNotifyPossibleTitleChange);
5942   nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(event));
5943   if (NS_SUCCEEDED(rv)) {
5944     mPendingTitleChangeEvent = Move(event);
5945   }
5946 }
5947 
DoNotifyPossibleTitleChange()5948 void nsDocument::DoNotifyPossibleTitleChange() {
5949   mPendingTitleChangeEvent.Forget();
5950   mHaveFiredTitleChange = true;
5951 
5952   nsAutoString title;
5953   GetTitle(title);
5954 
5955   nsCOMPtr<nsIPresShell> shell = GetShell();
5956   if (shell) {
5957     nsCOMPtr<nsISupports> container =
5958         shell->GetPresContext()->GetContainerWeak();
5959     if (container) {
5960       nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
5961       if (docShellWin) {
5962         docShellWin->SetTitle(title);
5963       }
5964     }
5965   }
5966 
5967   // Fire a DOM event for the title change.
5968   nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
5969                                       NS_LITERAL_STRING("DOMTitleChanged"),
5970                                       true, true);
5971 }
5972 
GetBoxObjectFor(Element * aElement,ErrorResult & aRv)5973 already_AddRefed<BoxObject> nsDocument::GetBoxObjectFor(Element* aElement,
5974                                                         ErrorResult& aRv) {
5975   if (!aElement) {
5976     aRv.Throw(NS_ERROR_UNEXPECTED);
5977     return nullptr;
5978   }
5979 
5980   nsIDocument* doc = aElement->OwnerDoc();
5981   if (doc != this) {
5982     aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
5983     return nullptr;
5984   }
5985 
5986   if (!mHasWarnedAboutBoxObjects && !aElement->IsXULElement()) {
5987     mHasWarnedAboutBoxObjects = true;
5988     nsContentUtils::ReportToConsole(
5989         nsIScriptError::warningFlag, NS_LITERAL_CSTRING("BoxObjects"), this,
5990         nsContentUtils::eDOM_PROPERTIES, "UseOfGetBoxObjectForWarning");
5991   }
5992 
5993   if (!mBoxObjectTable) {
5994     mBoxObjectTable =
5995         new nsRefPtrHashtable<nsPtrHashKey<nsIContent>, BoxObject>(6);
5996   }
5997 
5998   RefPtr<BoxObject> boxObject;
5999   auto entry = mBoxObjectTable->LookupForAdd(aElement);
6000   if (entry) {
6001     boxObject = entry.Data();
6002     return boxObject.forget();
6003   }
6004 
6005   int32_t namespaceID;
6006   RefPtr<nsAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
6007 #ifdef MOZ_XUL
6008   if (namespaceID == kNameSpaceID_XUL) {
6009     if (tag == nsGkAtoms::browser || tag == nsGkAtoms::editor ||
6010         tag == nsGkAtoms::iframe) {
6011       boxObject = new ContainerBoxObject();
6012     } else if (tag == nsGkAtoms::menu) {
6013       boxObject = new MenuBoxObject();
6014     } else if (tag == nsGkAtoms::popup || tag == nsGkAtoms::menupopup ||
6015                tag == nsGkAtoms::panel || tag == nsGkAtoms::tooltip) {
6016       boxObject = new PopupBoxObject();
6017     } else if (tag == nsGkAtoms::tree) {
6018       boxObject = new TreeBoxObject();
6019     } else if (tag == nsGkAtoms::listbox) {
6020       boxObject = new ListBoxObject();
6021     } else if (tag == nsGkAtoms::scrollbox) {
6022       boxObject = new ScrollBoxObject();
6023     } else {
6024       boxObject = new BoxObject();
6025     }
6026   } else
6027 #endif  // MOZ_XUL
6028   {
6029     boxObject = new BoxObject();
6030   }
6031 
6032   boxObject->Init(aElement);
6033   entry.OrInsert([&boxObject]() { return boxObject; });
6034 
6035   return boxObject.forget();
6036 }
6037 
ClearBoxObjectFor(nsIContent * aContent)6038 void nsDocument::ClearBoxObjectFor(nsIContent* aContent) {
6039   if (mBoxObjectTable) {
6040     if (auto entry = mBoxObjectTable->Lookup(aContent)) {
6041       nsPIBoxObject* boxObject = entry.Data();
6042       boxObject->Clear();
6043       entry.Remove();
6044     }
6045   }
6046 }
6047 
MatchMedia(const nsAString & aMediaQueryList,CallerType aCallerType)6048 already_AddRefed<MediaQueryList> nsIDocument::MatchMedia(
6049     const nsAString& aMediaQueryList, CallerType aCallerType) {
6050   RefPtr<MediaQueryList> result =
6051       new MediaQueryList(this, aMediaQueryList, aCallerType);
6052 
6053   mDOMMediaQueryLists.insertBack(result);
6054 
6055   return result.forget();
6056 }
6057 
FlushSkinBindings()6058 void nsDocument::FlushSkinBindings() { BindingManager()->FlushSkinBindings(); }
6059 
SetMayStartLayout(bool aMayStartLayout)6060 void nsIDocument::SetMayStartLayout(bool aMayStartLayout) {
6061   mMayStartLayout = aMayStartLayout;
6062   if (MayStartLayout()) {
6063     ReadyState state = GetReadyStateEnum();
6064     if (state >= READYSTATE_INTERACTIVE) {
6065       // DOMContentLoaded has fired already.
6066       MaybeResolveReadyForIdle();
6067     }
6068   }
6069 }
6070 
InitializeFrameLoader(nsFrameLoader * aLoader)6071 nsresult nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader) {
6072   mInitializableFrameLoaders.RemoveElement(aLoader);
6073   // Don't even try to initialize.
6074   if (mInDestructor) {
6075     NS_WARNING(
6076         "Trying to initialize a frame loader while"
6077         "document is being deleted");
6078     return NS_ERROR_FAILURE;
6079   }
6080 
6081   mInitializableFrameLoaders.AppendElement(aLoader);
6082   if (!mFrameLoaderRunner) {
6083     mFrameLoaderRunner = NewRunnableMethod(
6084         "nsDocument::MaybeInitializeFinalizeFrameLoaders", this,
6085         &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6086     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6087     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6088   }
6089   return NS_OK;
6090 }
6091 
FinalizeFrameLoader(nsFrameLoader * aLoader,nsIRunnable * aFinalizer)6092 nsresult nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader,
6093                                          nsIRunnable* aFinalizer) {
6094   mInitializableFrameLoaders.RemoveElement(aLoader);
6095   if (mInDestructor) {
6096     return NS_ERROR_FAILURE;
6097   }
6098 
6099   mFrameLoaderFinalizers.AppendElement(aFinalizer);
6100   if (!mFrameLoaderRunner) {
6101     mFrameLoaderRunner = NewRunnableMethod(
6102         "nsDocument::MaybeInitializeFinalizeFrameLoaders", this,
6103         &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6104     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6105     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6106   }
6107   return NS_OK;
6108 }
6109 
MaybeInitializeFinalizeFrameLoaders()6110 void nsDocument::MaybeInitializeFinalizeFrameLoaders() {
6111   if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
6112     // This method will be recalled when mUpdateNestLevel drops to 0,
6113     // or when !mDelayFrameLoaderInitialization.
6114     mFrameLoaderRunner = nullptr;
6115     return;
6116   }
6117 
6118   // We're not in an update, but it is not safe to run scripts, so
6119   // postpone frameloader initialization and finalization.
6120   if (!nsContentUtils::IsSafeToRunScript()) {
6121     if (!mInDestructor && !mFrameLoaderRunner &&
6122         (mInitializableFrameLoaders.Length() ||
6123          mFrameLoaderFinalizers.Length())) {
6124       mFrameLoaderRunner = NewRunnableMethod(
6125           "nsDocument::MaybeInitializeFinalizeFrameLoaders", this,
6126           &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6127       nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6128     }
6129     return;
6130   }
6131   mFrameLoaderRunner = nullptr;
6132 
6133   // Don't use a temporary array for mInitializableFrameLoaders, because
6134   // loading a frame may cause some other frameloader to be removed from the
6135   // array. But be careful to keep the loader alive when starting the load!
6136   while (mInitializableFrameLoaders.Length()) {
6137     RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
6138     mInitializableFrameLoaders.RemoveElementAt(0);
6139     NS_ASSERTION(loader, "null frameloader in the array?");
6140     loader->ReallyStartLoading();
6141   }
6142 
6143   uint32_t length = mFrameLoaderFinalizers.Length();
6144   if (length > 0) {
6145     nsTArray<nsCOMPtr<nsIRunnable>> finalizers;
6146     mFrameLoaderFinalizers.SwapElements(finalizers);
6147     for (uint32_t i = 0; i < length; ++i) {
6148       finalizers[i]->Run();
6149     }
6150   }
6151 }
6152 
TryCancelFrameLoaderInitialization(nsIDocShell * aShell)6153 void nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell) {
6154   uint32_t length = mInitializableFrameLoaders.Length();
6155   for (uint32_t i = 0; i < length; ++i) {
6156     if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
6157       mInitializableFrameLoaders.RemoveElementAt(i);
6158       return;
6159     }
6160   }
6161 }
6162 
RequestExternalResource(nsIURI * aURI,nsINode * aRequestingNode,ExternalResourceLoad ** aPendingLoad)6163 nsIDocument* nsDocument::RequestExternalResource(
6164     nsIURI* aURI, nsINode* aRequestingNode,
6165     ExternalResourceLoad** aPendingLoad) {
6166   NS_PRECONDITION(aURI, "Must have a URI");
6167   NS_PRECONDITION(aRequestingNode, "Must have a node");
6168   if (mDisplayDocument) {
6169     return mDisplayDocument->RequestExternalResource(aURI, aRequestingNode,
6170                                                      aPendingLoad);
6171   }
6172 
6173   return mExternalResourceMap.RequestResource(aURI, aRequestingNode, this,
6174                                               aPendingLoad);
6175 }
6176 
EnumerateExternalResources(nsSubDocEnumFunc aCallback,void * aData)6177 void nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback,
6178                                             void* aData) {
6179   mExternalResourceMap.EnumerateResources(aCallback, aData);
6180 }
6181 
GetAnimationController()6182 nsSMILAnimationController* nsDocument::GetAnimationController() {
6183   // We create the animation controller lazily because most documents won't want
6184   // one and only SVG documents and the like will call this
6185   if (mAnimationController) return mAnimationController;
6186   // Refuse to create an Animation Controller for data documents.
6187   if (mLoadedAsData || mLoadedAsInteractiveData) return nullptr;
6188 
6189   mAnimationController = new nsSMILAnimationController(this);
6190 
6191   // If there's a presContext then check the animation mode and pause if
6192   // necessary.
6193   nsPresContext* context = GetPresContext();
6194   if (mAnimationController && context &&
6195       context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
6196     mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
6197   }
6198 
6199   // If we're hidden (or being hidden), notify the newly-created animation
6200   // controller. (Skip this check for SVG-as-an-image documents, though,
6201   // because they don't get OnPageShow / OnPageHide calls).
6202   if (!mIsShowing && !mIsBeingUsedAsImage) {
6203     mAnimationController->OnPageHide();
6204   }
6205 
6206   return mAnimationController;
6207 }
6208 
GetOrCreatePendingAnimationTracker()6209 PendingAnimationTracker* nsDocument::GetOrCreatePendingAnimationTracker() {
6210   if (!mPendingAnimationTracker) {
6211     mPendingAnimationTracker = new PendingAnimationTracker(this);
6212   }
6213 
6214   return mPendingAnimationTracker;
6215 }
6216 
6217 /**
6218  * Retrieve the "direction" property of the document.
6219  *
6220  * @lina 01/09/2001
6221  */
GetDir(nsAString & aDirection) const6222 void nsIDocument::GetDir(nsAString& aDirection) const {
6223   aDirection.Truncate();
6224   Element* rootElement = GetHtmlElement();
6225   if (rootElement) {
6226     static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
6227   }
6228 }
6229 
6230 /**
6231  * Set the "direction" property of the document.
6232  *
6233  * @lina 01/09/2001
6234  */
SetDir(const nsAString & aDirection)6235 void nsIDocument::SetDir(const nsAString& aDirection) {
6236   Element* rootElement = GetHtmlElement();
6237   if (rootElement) {
6238     rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, aDirection, true);
6239   }
6240 }
6241 
6242 /* static */
MatchNameAttribute(Element * aElement,int32_t aNamespaceID,nsAtom * aAtom,void * aData)6243 bool nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
6244                                      nsAtom* aAtom, void* aData) {
6245   NS_PRECONDITION(aElement, "Must have element to work with!");
6246 
6247   if (!aElement->HasName()) {
6248     return false;
6249   }
6250 
6251   nsString* elementName = static_cast<nsString*>(aData);
6252   return aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
6253          aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, *elementName,
6254                                eCaseMatters);
6255 }
6256 
6257 /* static */
UseExistingNameString(nsINode * aRootNode,const nsString * aName)6258 void* nsIDocument::UseExistingNameString(nsINode* aRootNode,
6259                                          const nsString* aName) {
6260   return const_cast<nsString*>(aName);
6261 }
6262 
GetDocumentURI(nsString & aDocumentURI) const6263 nsresult nsIDocument::GetDocumentURI(nsString& aDocumentURI) const {
6264   if (mDocumentURI) {
6265     nsAutoCString uri;
6266     nsresult rv = mDocumentURI->GetSpec(uri);
6267     NS_ENSURE_SUCCESS(rv, rv);
6268 
6269     CopyUTF8toUTF16(uri, aDocumentURI);
6270   } else {
6271     aDocumentURI.Truncate();
6272   }
6273 
6274   return NS_OK;
6275 }
6276 
6277 // Alias of above
GetURL(nsString & aURL) const6278 nsresult nsIDocument::GetURL(nsString& aURL) const {
6279   return GetDocumentURI(aURL);
6280 }
6281 
GetDocumentURIFromJS(nsString & aDocumentURI,CallerType aCallerType,ErrorResult & aRv) const6282 void nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI,
6283                                        CallerType aCallerType,
6284                                        ErrorResult& aRv) const {
6285   if (!mChromeXHRDocURI || aCallerType != CallerType::System) {
6286     aRv = GetDocumentURI(aDocumentURI);
6287     return;
6288   }
6289 
6290   nsAutoCString uri;
6291   nsresult res = mChromeXHRDocURI->GetSpec(uri);
6292   if (NS_FAILED(res)) {
6293     aRv.Throw(res);
6294     return;
6295   }
6296   CopyUTF8toUTF16(uri, aDocumentURI);
6297 }
6298 
GetDocumentURIObject() const6299 nsIURI* nsIDocument::GetDocumentURIObject() const {
6300   if (!mChromeXHRDocURI) {
6301     return GetDocumentURI();
6302   }
6303 
6304   return mChromeXHRDocURI;
6305 }
6306 
GetCompatMode(nsString & aCompatMode) const6307 void nsIDocument::GetCompatMode(nsString& aCompatMode) const {
6308   NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
6309                    mCompatMode == eCompatibility_AlmostStandards ||
6310                    mCompatMode == eCompatibility_FullStandards,
6311                "mCompatMode is neither quirks nor strict for this document");
6312 
6313   if (mCompatMode == eCompatibility_NavQuirks) {
6314     aCompatMode.AssignLiteral("BackCompat");
6315   } else {
6316     aCompatMode.AssignLiteral("CSS1Compat");
6317   }
6318 }
6319 
BlastSubtreeToPieces(nsINode * aNode)6320 void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode) {
6321   if (aNode->IsElement()) {
6322     Element* element = aNode->AsElement();
6323     const nsDOMAttributeMap* map = element->GetAttributeMap();
6324     if (map) {
6325       while (true) {
6326         nsCOMPtr<nsIAttribute> attr;
6327         {
6328           // Use an iterator to get an arbitrary attribute from the
6329           // cache. The iterator must be destroyed before any other
6330           // operations on mAttributeCache, to avoid hash table
6331           // assertions.
6332           auto iter = map->mAttributeCache.ConstIter();
6333           if (iter.Done()) {
6334             break;
6335           }
6336           attr = iter.UserData();
6337         }
6338         NS_ASSERTION(attr.get(),
6339                      "non-nsIAttribute somehow made it into the hashmap?!");
6340 
6341         BlastSubtreeToPieces(attr);
6342 
6343         DebugOnly<nsresult> rv =
6344             element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
6345                                attr->NodeInfo()->NameAtom(), false);
6346 
6347         // XXX Should we abort here?
6348         NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
6349       }
6350     }
6351   }
6352 
6353   while (aNode->HasChildren()) {
6354     nsIContent* node = aNode->GetFirstChild();
6355     BlastSubtreeToPieces(node);
6356     aNode->RemoveChildNode(node, false);
6357   }
6358 }
6359 
6360 enum class StyleDataType {
6361   InlineStyle,
6362   SMILOverride,
6363   RestyleBits,
6364   ServoData,
6365 };
6366 
6367 // Recursively check whether this node or its descendants contain any
6368 // pre-existing style declaration or any shared restyle flags.
StyleDataTypesWithNode(nsINode * aNode)6369 static EnumSet<StyleDataType> StyleDataTypesWithNode(nsINode* aNode) {
6370   EnumSet<StyleDataType> result;
6371   if (aNode->IsElement()) {
6372     Element* elem = aNode->AsElement();
6373     if (elem->GetInlineStyleDeclaration()) {
6374       result += StyleDataType::InlineStyle;
6375     }
6376     if (elem->GetSMILOverrideStyleDeclaration()) {
6377       result += StyleDataType::SMILOverride;
6378     }
6379     if (elem->HasAnyOfFlags(ELEMENT_SHARED_RESTYLE_BITS)) {
6380       result += StyleDataType::RestyleBits;
6381     }
6382     if (elem->HasServoData()) {
6383       result += StyleDataType::ServoData;
6384     }
6385   }
6386   for (nsIContent* child = aNode->GetFirstChild(); child;
6387        child = child->GetNextSibling()) {
6388     result += StyleDataTypesWithNode(child);
6389   }
6390   return result;
6391 }
6392 
CheckCrossStyleBackendAdoption(nsIDocument * aOldDoc,nsIDocument * aNewDoc,nsINode * aAdoptedNode)6393 static void CheckCrossStyleBackendAdoption(nsIDocument* aOldDoc,
6394                                            nsIDocument* aNewDoc,
6395                                            nsINode* aAdoptedNode) {
6396   EnumSet<StyleDataType> styleDataTypes = StyleDataTypesWithNode(aAdoptedNode);
6397   if (styleDataTypes.isEmpty()) {
6398     return;
6399   }
6400 
6401   // We are adopting node with pre-existing style data across style
6402   // backend. We want some more information to help diagnose when that
6403   // can happen.
6404   nsAutoCString note;
6405   nsIDocument* geckoDoc;
6406   if (aOldDoc->GetStyleBackendType() == StyleBackendType::Servo) {
6407     note.AppendLiteral("Servo -> Gecko");
6408     geckoDoc = aNewDoc;
6409   } else {
6410     note.AppendLiteral("Gecko -> Servo");
6411     geckoDoc = aOldDoc;
6412   }
6413   note.AppendLiteral(", with style data:");
6414 #define APPEND_STYLE_DATA_TYPE(type_)                  \
6415   if (styleDataTypes.contains(StyleDataType::type_)) { \
6416     note.AppendLiteral(" " #type_);                    \
6417   }
6418   APPEND_STYLE_DATA_TYPE(InlineStyle)
6419   APPEND_STYLE_DATA_TYPE(SMILOverride)
6420   APPEND_STYLE_DATA_TYPE(RestyleBits)
6421   APPEND_STYLE_DATA_TYPE(ServoData)
6422 #undef APPEND_STYLE_DATA_TYPE
6423 
6424   note.AppendLiteral("\nGecko doc: ");
6425   if (nsContentUtils::IsSystemPrincipal(geckoDoc->NodePrincipal())) {
6426     note.AppendLiteral("system, ");
6427   }
6428   if (geckoDoc->IsXULDocument()) {
6429     note.AppendLiteral("XUL, ");
6430   }
6431   note.AppendLiteral("content-type: ");
6432   nsAutoString contentType;
6433   geckoDoc->GetContentType(contentType);
6434   AppendUTF16toUTF8(contentType, note);
6435   if (nsIURI* geckoURI = geckoDoc->GetOriginalURI()) {
6436     static const char* const INTERNAL_SCHEMES[] = {
6437         "about", "addons", "chrome", "resource", "moz-extension", "moz-icon",
6438     };
6439     note.AppendLiteral(", url: ");
6440     bool internalScheme = false;
6441     for (const char* scheme : INTERNAL_SCHEMES) {
6442       if (nsContentUtils::SchemeIs(geckoURI, scheme)) {
6443         internalScheme = true;
6444         break;
6445       }
6446     }
6447     if (internalScheme) {
6448       nsAutoCString spec;
6449       geckoURI->GetSpec(spec);
6450       note.Append(spec);
6451     } else {
6452       nsAutoCString scheme;
6453       geckoURI->GetScheme(scheme);
6454       note.Append(scheme);
6455       note.AppendLiteral(": (omitted)");
6456     }
6457   }
6458   note.Append('\n');
6459   CrashReporter::AppendAppNotesToCrashReport(note);
6460 
6461   MOZ_CRASH(
6462       "Must not adopt a node with pre-existing style data "
6463       "into a document with different style backend");
6464 }
6465 
AdoptNode(nsINode & aAdoptedNode,ErrorResult & rv)6466 nsINode* nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) {
6467   nsINode* adoptedNode = &aAdoptedNode;
6468 
6469   // Scope firing mutation events so that we don't carry any state that
6470   // might be stale
6471   {
6472     nsINode* parent = adoptedNode->GetParentNode();
6473     if (parent) {
6474       nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
6475                                            adoptedNode->OwnerDoc());
6476     }
6477   }
6478 
6479   nsAutoScriptBlocker scriptBlocker;
6480 
6481   switch (adoptedNode->NodeType()) {
6482     case ATTRIBUTE_NODE: {
6483       // Remove from ownerElement.
6484       RefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
6485 
6486       nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
6487       if (rv.Failed()) {
6488         return nullptr;
6489       }
6490 
6491       if (ownerElement) {
6492         RefPtr<Attr> newAttr =
6493             ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
6494         if (rv.Failed()) {
6495           return nullptr;
6496         }
6497 
6498         newAttr.swap(adoptedAttr);
6499       }
6500 
6501       break;
6502     }
6503     case DOCUMENT_FRAGMENT_NODE: {
6504       if (adoptedNode->IsShadowRoot()) {
6505         rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
6506         return nullptr;
6507       }
6508       MOZ_FALLTHROUGH;
6509     }
6510     case ELEMENT_NODE:
6511     case PROCESSING_INSTRUCTION_NODE:
6512     case TEXT_NODE:
6513     case CDATA_SECTION_NODE:
6514     case COMMENT_NODE:
6515     case DOCUMENT_TYPE_NODE: {
6516       // Don't allow adopting a node's anonymous subtree out from under it.
6517       if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
6518         rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6519         return nullptr;
6520       }
6521 
6522       // We don't want to adopt an element into its own contentDocument or into
6523       // a descendant contentDocument, so we check if the frameElement of this
6524       // document or any of its parents is the adopted node or one of its
6525       // descendants.
6526       nsIDocument* doc = this;
6527       do {
6528         if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
6529           nsCOMPtr<nsINode> node = win->GetFrameElementInternal();
6530           if (node &&
6531               nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
6532             rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
6533             return nullptr;
6534           }
6535         }
6536       } while ((doc = doc->GetParentDocument()));
6537 
6538       // Remove from parent.
6539       nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
6540       if (parent) {
6541         parent->RemoveChildNode(adoptedNode->AsContent(), true);
6542       } else {
6543         MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc());
6544 
6545         // If we're adopting a node that's not in a document, it might still
6546         // have a binding applied. Remove the binding from the element now
6547         // that it's getting adopted into a new document.
6548         // TODO Fully tear down the binding.
6549         if (adoptedNode->IsElement()) {
6550           adoptedNode->AsElement()->SetXBLBinding(nullptr);
6551         }
6552       }
6553 
6554       break;
6555     }
6556     case DOCUMENT_NODE: {
6557       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6558       return nullptr;
6559     }
6560     default: {
6561       NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
6562 
6563       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6564       return nullptr;
6565     }
6566   }
6567 
6568   nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
6569   bool sameDocument = oldDocument == this;
6570 
6571   AutoJSContext cx;
6572   JS::Rooted<JSObject*> newScope(cx, nullptr);
6573   if (!sameDocument) {
6574     if (MOZ_UNLIKELY(oldDocument->GetStyleBackendType() !=
6575                      GetStyleBackendType())) {
6576       NS_WARNING("Adopting node across different style backend");
6577       CheckCrossStyleBackendAdoption(oldDocument, this, adoptedNode);
6578     }
6579     newScope = GetWrapper();
6580     if (!newScope && GetScopeObject() &&
6581         GetScopeObject()->GetGlobalJSObject()) {
6582       // Make sure cx is in a semi-sane compartment before we call WrapNative.
6583       // It's kind of irrelevant, given that we're passing aAllowWrapping =
6584       // false, and documents should always insist on being wrapped in an
6585       // canonical scope. But we try to pass something sane anyway.
6586       JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject());
6587       JS::Rooted<JS::Value> v(cx);
6588       rv = nsContentUtils::WrapNative(cx, this, this, &v,
6589                                       /* aAllowWrapping = */ false);
6590       if (rv.Failed()) return nullptr;
6591       newScope = &v.toObject();
6592     }
6593   }
6594 
6595   nsCOMArray<nsINode> nodesWithProperties;
6596   nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
6597                      newScope, nodesWithProperties, rv);
6598   if (rv.Failed()) {
6599     // Disconnect all nodes from their parents, since some have the old document
6600     // as their ownerDocument and some have this as their ownerDocument.
6601     nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
6602 
6603     if (!sameDocument && oldDocument) {
6604       uint32_t count = nodesWithProperties.Count();
6605       for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
6606         for (uint32_t i = 0; i < count; ++i) {
6607           // Remove all properties.
6608           oldDocument->PropertyTable(j)->DeleteAllPropertiesFor(
6609               nodesWithProperties[i]);
6610         }
6611       }
6612     }
6613 
6614     return nullptr;
6615   }
6616 
6617   uint32_t count = nodesWithProperties.Count();
6618   if (!sameDocument && oldDocument) {
6619     for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
6620       nsPropertyTable* oldTable = oldDocument->PropertyTable(j);
6621       nsPropertyTable* newTable = PropertyTable(j);
6622       for (uint32_t i = 0; i < count; ++i) {
6623         rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
6624                                                         newTable);
6625       }
6626     }
6627 
6628     if (rv.Failed()) {
6629       // Disconnect all nodes from their parents.
6630       nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
6631 
6632       return nullptr;
6633     }
6634   }
6635 
6636   NS_ASSERTION(adoptedNode->OwnerDoc() == this,
6637                "Should still be in the document we just got adopted into");
6638 
6639   return adoptedNode;
6640 }
6641 
GetViewportInfo(const ScreenIntSize & aDisplaySize)6642 nsViewportInfo nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize) {
6643   MOZ_ASSERT(mPresShell);
6644 
6645   // Compute the CSS-to-LayoutDevice pixel scale as the product of the
6646   // widget scale and the full zoom.
6647   nsPresContext* context = mPresShell->GetPresContext();
6648   // When querying the full zoom, get it from the device context rather than
6649   // directly from the pres context, because the device context's value can
6650   // include an adjustment necessay to keep the number of app units per device
6651   // pixel an integer, and we want the adjusted value.
6652   float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
6653   fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
6654   CSSToLayoutDeviceScale layoutDeviceScale =
6655       context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
6656 
6657   CSSToScreenScale defaultScale =
6658       layoutDeviceScale * LayoutDeviceToScreenScale(1.0);
6659 
6660   // Special behaviour for desktop mode, provided we are not on an about: page
6661   nsPIDOMWindowOuter* win = GetWindow();
6662   if (win && win->IsDesktopModeViewport() && !IsAboutPage()) {
6663     CSSCoord viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
6664     CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth);
6665     float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
6666     CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
6667     ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
6668     return nsViewportInfo(fakeDesktopSize, scaleToFit,
6669                           /*allowZoom*/ true);
6670   }
6671 
6672   if (!gfxPrefs::MetaViewportEnabled()) {
6673     return nsViewportInfo(aDisplaySize, defaultScale,
6674                           /*allowZoom*/ false);
6675   }
6676 
6677   // In cases where the width of the CSS viewport is less than or equal to the
6678   // width of the display (i.e. width <= device-width) then we disable
6679   // double-tap-to-zoom behaviour. See bug 941995 for details.
6680 
6681   switch (mViewportType) {
6682     case DisplayWidthHeight:
6683       return nsViewportInfo(aDisplaySize, defaultScale,
6684                             /*allowZoom*/ true);
6685     case Unknown: {
6686       nsAutoString viewport;
6687       GetHeaderData(nsGkAtoms::viewport, viewport);
6688       if (viewport.IsEmpty()) {
6689         // If the docType specifies that we are on a site optimized for mobile,
6690         // then we want to return specially crafted defaults for the viewport
6691         // info.
6692         RefPtr<DocumentType> docType = GetDoctype();
6693         if (docType) {
6694           nsAutoString docId;
6695           nsresult rv = docType->GetPublicId(docId);
6696           if (NS_SUCCEEDED(rv)) {
6697             if ((docId.Find("WAP") != -1) || (docId.Find("Mobile") != -1) ||
6698                 (docId.Find("WML") != -1)) {
6699               // We're making an assumption that the docType can't change here
6700               mViewportType = DisplayWidthHeight;
6701               return nsViewportInfo(aDisplaySize, defaultScale,
6702                                     /*allowZoom*/ true);
6703             }
6704           }
6705         }
6706 
6707         nsAutoString handheldFriendly;
6708         GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
6709         if (handheldFriendly.EqualsLiteral("true")) {
6710           mViewportType = DisplayWidthHeight;
6711           return nsViewportInfo(aDisplaySize, defaultScale,
6712                                 /*allowZoom*/ true);
6713         }
6714       }
6715 
6716       nsAutoString minScaleStr;
6717       GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
6718 
6719       nsresult errorCode;
6720       mScaleMinFloat =
6721           LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
6722 
6723       if (NS_FAILED(errorCode)) {
6724         mScaleMinFloat = kViewportMinScale;
6725       }
6726 
6727       mScaleMinFloat = mozilla::clamped(mScaleMinFloat, kViewportMinScale,
6728                                         kViewportMaxScale);
6729 
6730       nsAutoString maxScaleStr;
6731       GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
6732 
6733       // We define a special error code variable for the scale and max scale,
6734       // because they are used later (see the width calculations).
6735       nsresult scaleMaxErrorCode;
6736       mScaleMaxFloat =
6737           LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
6738 
6739       if (NS_FAILED(scaleMaxErrorCode)) {
6740         mScaleMaxFloat = kViewportMaxScale;
6741       }
6742 
6743       mScaleMaxFloat = mozilla::clamped(mScaleMaxFloat, kViewportMinScale,
6744                                         kViewportMaxScale);
6745 
6746       nsAutoString scaleStr;
6747       GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
6748 
6749       nsresult scaleErrorCode;
6750       mScaleFloat =
6751           LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
6752 
6753       nsAutoString widthStr, heightStr;
6754 
6755       GetHeaderData(nsGkAtoms::viewport_height, heightStr);
6756       GetHeaderData(nsGkAtoms::viewport_width, widthStr);
6757 
6758       mAutoSize = false;
6759 
6760       if (widthStr.EqualsLiteral("device-width")) {
6761         mAutoSize = true;
6762       }
6763 
6764       if (widthStr.IsEmpty() && (heightStr.EqualsLiteral("device-height") ||
6765                                  (mScaleFloat.scale == 1.0))) {
6766         mAutoSize = true;
6767       }
6768 
6769       nsresult widthErrorCode, heightErrorCode;
6770       mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
6771       mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
6772 
6773       // If width or height has not been set to a valid number by this point,
6774       // fall back to a default value.
6775       mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) &&
6776                      mViewportSize.width > 0);
6777       mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) &&
6778                       mViewportSize.height > 0);
6779 
6780       mAllowZoom = true;
6781       nsAutoString userScalable;
6782       GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
6783 
6784       if ((userScalable.EqualsLiteral("0")) ||
6785           (userScalable.EqualsLiteral("no")) ||
6786           (userScalable.EqualsLiteral("false"))) {
6787         mAllowZoom = false;
6788       }
6789 
6790       mScaleStrEmpty = scaleStr.IsEmpty();
6791       mWidthStrEmpty = widthStr.IsEmpty();
6792       mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
6793       mValidMaxScale =
6794           !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
6795 
6796       mViewportType = Specified;
6797       MOZ_FALLTHROUGH;
6798     }
6799     case Specified:
6800     default:
6801       LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
6802       LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
6803       bool effectiveValidMaxScale = mValidMaxScale;
6804       bool effectiveAllowZoom = mAllowZoom;
6805       if (gfxPrefs::ForceUserScalable()) {
6806         // If the pref to force user-scalable is enabled, we ignore the values
6807         // from the meta-viewport tag for these properties and just assume they
6808         // allow the page to be scalable. Note in particular that this code is
6809         // in the "Specified" branch of the enclosing switch statement, so that
6810         // calls to GetViewportInfo always use the latest value of the
6811         // ForceUserScalable pref. Other codepaths that return nsViewportInfo
6812         // instances are all consistent with ForceUserScalable() already.
6813         effectiveMinScale = kViewportMinScale;
6814         effectiveMaxScale = kViewportMaxScale;
6815         effectiveValidMaxScale = true;
6816         effectiveAllowZoom = true;
6817       }
6818 
6819       CSSSize size = mViewportSize;
6820 
6821       if (!mValidWidth) {
6822         if (mValidHeight && !aDisplaySize.IsEmpty()) {
6823           size.width = size.height * aDisplaySize.width / aDisplaySize.height;
6824         } else {
6825           // Stretch CSS pixel size of viewport to keep device pixel size
6826           // unchanged after full zoom applied.
6827           // See bug 974242.
6828           size.width = gfxPrefs::DesktopViewportWidth() / fullZoom;
6829         }
6830       }
6831 
6832       if (!mValidHeight) {
6833         if (!aDisplaySize.IsEmpty()) {
6834           size.height = size.width * aDisplaySize.height / aDisplaySize.width;
6835         } else {
6836           size.height = size.width;
6837         }
6838       }
6839 
6840       CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
6841       CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
6842       CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
6843 
6844       if (mAutoSize) {
6845         // aDisplaySize is in screen pixels; convert them to CSS pixels for the
6846         // viewport size.
6847         CSSToScreenScale defaultPixelScale =
6848             layoutDeviceScale * LayoutDeviceToScreenScale(1.0f);
6849         size = ScreenSize(aDisplaySize) / defaultPixelScale;
6850       }
6851 
6852       size.width = clamped(size.width, float(kViewportMinSize.width),
6853                            float(kViewportMaxSize.width));
6854 
6855       // Also recalculate the default zoom, if it wasn't specified in the
6856       // metadata, and the width is specified.
6857       if (mScaleStrEmpty && !mWidthStrEmpty) {
6858         CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width);
6859         scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
6860       }
6861 
6862       size.height = clamped(size.height, float(kViewportMinSize.height),
6863                             float(kViewportMaxSize.height));
6864 
6865       // We need to perform a conversion, but only if the initial or maximum
6866       // scale were set explicitly by the user.
6867       if (mValidScaleFloat && scaleFloat >= scaleMinFloat &&
6868           scaleFloat <= scaleMaxFloat) {
6869         CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
6870         size.width = std::max(size.width, displaySize.width);
6871         size.height = std::max(size.height, displaySize.height);
6872       } else if (effectiveValidMaxScale) {
6873         CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
6874         size.width = std::max(size.width, displaySize.width);
6875         size.height = std::max(size.height, displaySize.height);
6876       }
6877 
6878       return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
6879                             mAutoSize, effectiveAllowZoom);
6880   }
6881 }
6882 
GetOrCreateListenerManager()6883 EventListenerManager* nsDocument::GetOrCreateListenerManager() {
6884   if (!mListenerManager) {
6885     mListenerManager =
6886         new EventListenerManager(static_cast<EventTarget*>(this));
6887     SetFlags(NODE_HAS_LISTENERMANAGER);
6888   }
6889 
6890   return mListenerManager;
6891 }
6892 
GetExistingListenerManager() const6893 EventListenerManager* nsDocument::GetExistingListenerManager() const {
6894   return mListenerManager;
6895 }
6896 
GetEventTargetParent(EventChainPreVisitor & aVisitor)6897 nsresult nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
6898   if (mDocGroup && aVisitor.mEvent->mMessage != eVoidEvent &&
6899       !mIgnoreDocGroupMismatches) {
6900     mDocGroup->ValidateAccess();
6901   }
6902 
6903   aVisitor.mCanHandle = true;
6904   // FIXME! This is a hack to make middle mouse paste working also in Editor.
6905   // Bug 329119
6906   aVisitor.mForceContentDispatch = true;
6907 
6908   // Load events must not propagate to |window| object, see bug 335251.
6909   if (aVisitor.mEvent->mMessage != eLoad) {
6910     nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetWindow());
6911     aVisitor.SetParentTarget(
6912         window ? window->GetTargetForEventTargetChain() : nullptr, false);
6913   }
6914   return NS_OK;
6915 }
6916 
CreateEvent(const nsAString & aEventType,CallerType aCallerType,ErrorResult & rv) const6917 already_AddRefed<Event> nsIDocument::CreateEvent(const nsAString& aEventType,
6918                                                  CallerType aCallerType,
6919                                                  ErrorResult& rv) const {
6920   nsPresContext* presContext = GetPresContext();
6921 
6922   // Create event even without presContext.
6923   RefPtr<Event> ev =
6924       EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), presContext,
6925                                    nullptr, aEventType, aCallerType);
6926   if (!ev) {
6927     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6928     return nullptr;
6929   }
6930   WidgetEvent* e = ev->WidgetEventPtr();
6931   e->mFlags.mBubbles = false;
6932   e->mFlags.mCancelable = false;
6933   return ev.forget();
6934 }
6935 
FlushPendingNotifications(FlushType aType)6936 void nsIDocument::FlushPendingNotifications(FlushType aType) {
6937   mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
6938   FlushPendingNotifications(flush);
6939 }
6940 
FlushPendingNotifications(mozilla::ChangesToFlush aFlush)6941 void nsIDocument::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
6942   FlushType flushType = aFlush.mFlushType;
6943 
6944   nsDocumentOnStack dos(this);
6945 
6946   // We need to flush the sink for non-HTML documents (because the XML
6947   // parser still does insertion with deferred notifications).  We
6948   // also need to flush the sink if this is a layout-related flush, to
6949   // make sure that layout is started as needed.  But we can skip that
6950   // part if we have no presshell or if it's already done an initial
6951   // reflow.
6952   if ((!IsHTMLDocument() || (flushType > FlushType::ContentAndNotify &&
6953                              mPresShell && !mPresShell->DidInitialize())) &&
6954       (mParser || mWeakSink)) {
6955     nsCOMPtr<nsIContentSink> sink;
6956     if (mParser) {
6957       sink = mParser->GetContentSink();
6958     } else {
6959       sink = do_QueryReferent(mWeakSink);
6960       if (!sink) {
6961         mWeakSink = nullptr;
6962       }
6963     }
6964     // Determine if it is safe to flush the sink notifications
6965     // by determining if it safe to flush all the presshells.
6966     if (sink && (flushType == FlushType::Content || IsSafeToFlush())) {
6967       sink->FlushPendingNotifications(flushType);
6968     }
6969   }
6970 
6971   // Should we be flushing pending binding constructors in here?
6972 
6973   if (flushType <= FlushType::ContentAndNotify) {
6974     // Nothing to do here
6975     return;
6976   }
6977 
6978   // If we have a parent we must flush the parent too to ensure that our
6979   // container is reflowed if its size was changed.  But if it's not safe to
6980   // flush ourselves, then don't flush the parent, since that can cause things
6981   // like resizes of our frame's widget, which we can't handle while flushing
6982   // is unsafe.
6983   // Since media queries mean that a size change of our container can
6984   // affect style, we need to promote a style flush on ourself to a
6985   // layout flush on our parent, since we need our container to be the
6986   // correct size to determine the correct style.
6987   if (mParentDocument && IsSafeToFlush()) {
6988     mozilla::ChangesToFlush parentFlush = aFlush;
6989     if (flushType >= FlushType::Style) {
6990       parentFlush.mFlushType = std::max(FlushType::Layout, flushType);
6991     }
6992     mParentDocument->FlushPendingNotifications(parentFlush);
6993   }
6994 
6995   if (nsIPresShell* shell = GetShell()) {
6996     shell->FlushPendingNotifications(aFlush);
6997   }
6998 }
6999 
Copy(nsIDocument * aDocument,void * aData)7000 static bool Copy(nsIDocument* aDocument, void* aData) {
7001   nsTArray<nsCOMPtr<nsIDocument>>* resources =
7002       static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData);
7003   resources->AppendElement(aDocument);
7004   return true;
7005 }
7006 
FlushExternalResources(FlushType aType)7007 void nsDocument::FlushExternalResources(FlushType aType) {
7008   NS_ASSERTION(
7009       aType >= FlushType::Style,
7010       "should only need to flush for style or higher in external resources");
7011   if (GetDisplayDocument()) {
7012     return;
7013   }
7014   nsTArray<nsCOMPtr<nsIDocument>> resources;
7015   EnumerateExternalResources(Copy, &resources);
7016 
7017   for (uint32_t i = 0; i < resources.Length(); i++) {
7018     resources[i]->FlushPendingNotifications(aType);
7019   }
7020 }
7021 
SetXMLDeclaration(const char16_t * aVersion,const char16_t * aEncoding,const int32_t aStandalone)7022 void nsDocument::SetXMLDeclaration(const char16_t* aVersion,
7023                                    const char16_t* aEncoding,
7024                                    const int32_t aStandalone) {
7025   if (!aVersion || *aVersion == '\0') {
7026     mXMLDeclarationBits = 0;
7027     return;
7028   }
7029 
7030   mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
7031 
7032   if (aEncoding && *aEncoding != '\0') {
7033     mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
7034   }
7035 
7036   if (aStandalone == 1) {
7037     mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
7038                            XML_DECLARATION_BITS_STANDALONE_YES;
7039   } else if (aStandalone == 0) {
7040     mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
7041   }
7042 }
7043 
GetXMLDeclaration(nsAString & aVersion,nsAString & aEncoding,nsAString & aStandalone)7044 void nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
7045                                    nsAString& aStandalone) {
7046   aVersion.Truncate();
7047   aEncoding.Truncate();
7048   aStandalone.Truncate();
7049 
7050   if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
7051     return;
7052   }
7053 
7054   // always until we start supporting 1.1 etc.
7055   aVersion.AssignLiteral("1.0");
7056 
7057   if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
7058     // This is what we have stored, not necessarily what was written
7059     // in the original
7060     GetCharacterSet(aEncoding);
7061   }
7062 
7063   if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
7064     if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
7065       aStandalone.AssignLiteral("yes");
7066     } else {
7067       aStandalone.AssignLiteral("no");
7068     }
7069   }
7070 }
7071 
IsScriptEnabled()7072 bool nsDocument::IsScriptEnabled() {
7073   // If this document is sandboxed without 'allow-scripts'
7074   // script is not enabled
7075   if (HasScriptsBlockedBySandbox()) {
7076     return false;
7077   }
7078 
7079   nsCOMPtr<nsIScriptGlobalObject> globalObject =
7080       do_QueryInterface(GetInnerWindow());
7081   if (!globalObject || !globalObject->GetGlobalJSObject()) {
7082     return false;
7083   }
7084 
7085   return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
7086 }
7087 
GetRadioGroup(const nsAString & aName) const7088 nsRadioGroupStruct* nsDocument::GetRadioGroup(const nsAString& aName) const {
7089   nsRadioGroupStruct* radioGroup = nullptr;
7090   mRadioGroups.Get(aName, &radioGroup);
7091   return radioGroup;
7092 }
7093 
GetOrCreateRadioGroup(const nsAString & aName)7094 nsRadioGroupStruct* nsDocument::GetOrCreateRadioGroup(const nsAString& aName) {
7095   return mRadioGroups.LookupForAdd(aName).OrInsert(
7096       []() { return new nsRadioGroupStruct(); });
7097 }
7098 
SetCurrentRadioButton(const nsAString & aName,HTMLInputElement * aRadio)7099 void nsDocument::SetCurrentRadioButton(const nsAString& aName,
7100                                        HTMLInputElement* aRadio) {
7101   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7102   radioGroup->mSelectedRadioButton = aRadio;
7103 }
7104 
GetCurrentRadioButton(const nsAString & aName)7105 HTMLInputElement* nsDocument::GetCurrentRadioButton(const nsAString& aName) {
7106   return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
7107 }
7108 
7109 NS_IMETHODIMP
GetNextRadioButton(const nsAString & aName,const bool aPrevious,HTMLInputElement * aFocusedRadio,HTMLInputElement ** aRadioOut)7110 nsDocument::GetNextRadioButton(const nsAString& aName, const bool aPrevious,
7111                                HTMLInputElement* aFocusedRadio,
7112                                HTMLInputElement** aRadioOut) {
7113   // XXX Can we combine the HTML radio button method impls of
7114   //     nsDocument and nsHTMLFormControl?
7115   // XXX Why is HTML radio button stuff in nsDocument, as
7116   //     opposed to nsHTMLDocument?
7117   *aRadioOut = nullptr;
7118 
7119   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7120 
7121   // Return the radio button relative to the focused radio button.
7122   // If no radio is focused, get the radio relative to the selected one.
7123   RefPtr<HTMLInputElement> currentRadio;
7124   if (aFocusedRadio) {
7125     currentRadio = aFocusedRadio;
7126   } else {
7127     currentRadio = radioGroup->mSelectedRadioButton;
7128     if (!currentRadio) {
7129       return NS_ERROR_FAILURE;
7130     }
7131   }
7132   int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
7133   if (index < 0) {
7134     return NS_ERROR_FAILURE;
7135   }
7136 
7137   int32_t numRadios = radioGroup->mRadioButtons.Count();
7138   RefPtr<HTMLInputElement> radio;
7139   do {
7140     if (aPrevious) {
7141       if (--index < 0) {
7142         index = numRadios - 1;
7143       }
7144     } else if (++index >= numRadios) {
7145       index = 0;
7146     }
7147     NS_ASSERTION(
7148         static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])
7149             ->IsHTMLElement(nsGkAtoms::input),
7150         "mRadioButtons holding a non-radio button");
7151     radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
7152   } while (radio->Disabled() && radio != currentRadio);
7153 
7154   radio.forget(aRadioOut);
7155   return NS_OK;
7156 }
7157 
AddToRadioGroup(const nsAString & aName,HTMLInputElement * aRadio)7158 void nsDocument::AddToRadioGroup(const nsAString& aName,
7159                                  HTMLInputElement* aRadio) {
7160   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7161   radioGroup->mRadioButtons.AppendObject(aRadio);
7162 
7163   if (aRadio->IsRequired()) {
7164     radioGroup->mRequiredRadioCount++;
7165   }
7166 }
7167 
RemoveFromRadioGroup(const nsAString & aName,HTMLInputElement * aRadio)7168 void nsDocument::RemoveFromRadioGroup(const nsAString& aName,
7169                                       HTMLInputElement* aRadio) {
7170   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7171   radioGroup->mRadioButtons.RemoveObject(aRadio);
7172 
7173   if (aRadio->IsRequired()) {
7174     NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
7175                  "mRequiredRadioCount about to wrap below 0!");
7176     radioGroup->mRequiredRadioCount--;
7177   }
7178 }
7179 
7180 NS_IMETHODIMP
WalkRadioGroup(const nsAString & aName,nsIRadioVisitor * aVisitor,bool aFlushContent)7181 nsDocument::WalkRadioGroup(const nsAString& aName, nsIRadioVisitor* aVisitor,
7182                            bool aFlushContent) {
7183   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7184 
7185   for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
7186     if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
7187       return NS_OK;
7188     }
7189   }
7190 
7191   return NS_OK;
7192 }
7193 
GetRequiredRadioCount(const nsAString & aName) const7194 uint32_t nsDocument::GetRequiredRadioCount(const nsAString& aName) const {
7195   nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
7196   return radioGroup ? radioGroup->mRequiredRadioCount : 0;
7197 }
7198 
RadioRequiredWillChange(const nsAString & aName,bool aRequiredAdded)7199 void nsDocument::RadioRequiredWillChange(const nsAString& aName,
7200                                          bool aRequiredAdded) {
7201   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7202 
7203   if (aRequiredAdded) {
7204     radioGroup->mRequiredRadioCount++;
7205   } else {
7206     NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
7207                  "mRequiredRadioCount about to wrap below 0!");
7208     radioGroup->mRequiredRadioCount--;
7209   }
7210 }
7211 
GetValueMissingState(const nsAString & aName) const7212 bool nsDocument::GetValueMissingState(const nsAString& aName) const {
7213   nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
7214   return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
7215 }
7216 
SetValueMissingState(const nsAString & aName,bool aValue)7217 void nsDocument::SetValueMissingState(const nsAString& aName, bool aValue) {
7218   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7219   radioGroup->mGroupSuffersFromValueMissing = aValue;
7220 }
7221 
RetrieveRelevantHeaders(nsIChannel * aChannel)7222 void nsDocument::RetrieveRelevantHeaders(nsIChannel* aChannel) {
7223   PRTime modDate = 0;
7224   nsresult rv;
7225 
7226   nsCOMPtr<nsIHttpChannel> httpChannel;
7227   rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
7228   if (NS_WARN_IF(NS_FAILED(rv))) {
7229     return;
7230   }
7231 
7232   if (httpChannel) {
7233     nsAutoCString tmp;
7234     rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
7235                                         tmp);
7236 
7237     if (NS_SUCCEEDED(rv)) {
7238       PRTime time;
7239       PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
7240       if (st == PR_SUCCESS) {
7241         modDate = time;
7242       }
7243     }
7244 
7245     // The misspelled key 'referer' is as per the HTTP spec
7246     rv =
7247         httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"), mReferrer);
7248 
7249     static const char* const headers[] = {
7250         "default-style", "content-style-type", "content-language",
7251         "content-disposition", "refresh", "x-dns-prefetch-control",
7252         "x-frame-options", "referrer-policy",
7253         // add more http headers if you need
7254         // XXXbz don't add content-location support without reading bug
7255         // 238654 and its dependencies/dups first.
7256         0};
7257 
7258     nsAutoCString headerVal;
7259     const char* const* name = headers;
7260     while (*name) {
7261       rv = httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
7262       if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
7263         RefPtr<nsAtom> key = NS_Atomize(*name);
7264         SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
7265       }
7266       ++name;
7267     }
7268   } else {
7269     nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
7270     if (fileChannel) {
7271       nsCOMPtr<nsIFile> file;
7272       fileChannel->GetFile(getter_AddRefs(file));
7273       if (file) {
7274         PRTime msecs;
7275         rv = file->GetLastModifiedTime(&msecs);
7276 
7277         if (NS_SUCCEEDED(rv)) {
7278           modDate = msecs * int64_t(PR_USEC_PER_MSEC);
7279         }
7280       }
7281     } else {
7282       nsAutoCString contentDisp;
7283       rv = aChannel->GetContentDispositionHeader(contentDisp);
7284       if (NS_SUCCEEDED(rv)) {
7285         SetHeaderData(nsGkAtoms::headerContentDisposition,
7286                       NS_ConvertASCIItoUTF16(contentDisp));
7287       }
7288     }
7289   }
7290 
7291   mLastModified.Truncate();
7292   if (modDate != 0) {
7293     GetFormattedTimeString(modDate, mLastModified);
7294   }
7295 }
7296 
CreateElem(const nsAString & aName,nsAtom * aPrefix,int32_t aNamespaceID,const nsAString * aIs)7297 already_AddRefed<Element> nsDocument::CreateElem(const nsAString& aName,
7298                                                  nsAtom* aPrefix,
7299                                                  int32_t aNamespaceID,
7300                                                  const nsAString* aIs) {
7301 #ifdef DEBUG
7302   nsAutoString qName;
7303   if (aPrefix) {
7304     aPrefix->ToString(qName);
7305     qName.Append(':');
7306   }
7307   qName.Append(aName);
7308 
7309   // Note: "a:b:c" is a valid name in non-namespaces XML, and
7310   // nsDocument::CreateElement can call us with such a name and no prefix,
7311   // which would cause an error if we just used true here.
7312   bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
7313   NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
7314                "Don't pass invalid prefixes to nsDocument::CreateElem, "
7315                "check caller.");
7316 #endif
7317 
7318   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
7319   mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID, ELEMENT_NODE,
7320                                 getter_AddRefs(nodeInfo));
7321   NS_ENSURE_TRUE(nodeInfo, nullptr);
7322 
7323   nsCOMPtr<Element> element;
7324   nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
7325                               NOT_FROM_PARSER, aIs);
7326   return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
7327 }
7328 
IsSafeToFlush() const7329 bool nsIDocument::IsSafeToFlush() const {
7330   nsIPresShell* shell = GetShell();
7331   if (!shell) return true;
7332 
7333   return shell->IsSafeToFlush();
7334 }
7335 
Sanitize()7336 void nsDocument::Sanitize() {
7337   // Sanitize the document by resetting all password fields and any form
7338   // fields with autocomplete=off to their default values.  We do this now,
7339   // instead of when the presentation is restored, to offer some protection
7340   // in case there is ever an exploit that allows a cached document to be
7341   // accessed from a different document.
7342 
7343   // First locate all input elements, regardless of whether they are
7344   // in a form, and reset the password and autocomplete=off elements.
7345 
7346   RefPtr<nsContentList> nodes =
7347       GetElementsByTagName(NS_LITERAL_STRING("input"));
7348 
7349   nsAutoString value;
7350 
7351   uint32_t length = nodes->Length(true);
7352   for (uint32_t i = 0; i < length; ++i) {
7353     NS_ASSERTION(nodes->Item(i), "null item in node list!");
7354 
7355     RefPtr<HTMLInputElement> input =
7356         HTMLInputElement::FromContentOrNull(nodes->Item(i));
7357     if (!input) continue;
7358 
7359     bool resetValue = false;
7360 
7361     input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
7362     if (value.LowerCaseEqualsLiteral("off")) {
7363       resetValue = true;
7364     } else {
7365       input->GetType(value);
7366       if (value.LowerCaseEqualsLiteral("password")) resetValue = true;
7367     }
7368 
7369     if (resetValue) {
7370       input->Reset();
7371     }
7372   }
7373 
7374   // Now locate all _form_ elements that have autocomplete=off and reset them
7375   nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
7376 
7377   length = nodes->Length(true);
7378   for (uint32_t i = 0; i < length; ++i) {
7379     NS_ASSERTION(nodes->Item(i), "null item in nodelist");
7380 
7381     HTMLFormElement* form = HTMLFormElement::FromContent(nodes->Item(i));
7382     if (!form) continue;
7383 
7384     form->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, value);
7385     if (value.LowerCaseEqualsLiteral("off")) form->Reset();
7386   }
7387 }
7388 
EnumerateSubDocuments(nsSubDocEnumFunc aCallback,void * aData)7389 void nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback,
7390                                        void* aData) {
7391   if (!mSubDocuments) {
7392     return;
7393   }
7394 
7395   // PLDHashTable::Iterator can't handle modifications while iterating so we
7396   // copy all entries to an array first before calling any callbacks.
7397   AutoTArray<nsCOMPtr<nsIDocument>, 8> subdocs;
7398   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7399     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7400     nsIDocument* subdoc = entry->mSubDocument;
7401     if (subdoc) {
7402       subdocs.AppendElement(subdoc);
7403     }
7404   }
7405   for (auto subdoc : subdocs) {
7406     if (!aCallback(subdoc, aData)) {
7407       break;
7408     }
7409   }
7410 }
7411 
CollectDescendantDocuments(nsTArray<nsCOMPtr<nsIDocument>> & aDescendants,nsDocTestFunc aCallback) const7412 void nsDocument::CollectDescendantDocuments(
7413     nsTArray<nsCOMPtr<nsIDocument>>& aDescendants,
7414     nsDocTestFunc aCallback) const {
7415   if (!mSubDocuments) {
7416     return;
7417   }
7418 
7419   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7420     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7421     const nsIDocument* subdoc = entry->mSubDocument;
7422     if (subdoc) {
7423       if (aCallback(subdoc)) {
7424         aDescendants.AppendElement(entry->mSubDocument);
7425       }
7426       subdoc->CollectDescendantDocuments(aDescendants, aCallback);
7427     }
7428   }
7429 }
7430 
7431 #ifdef DEBUG_bryner
7432 #define DEBUG_PAGE_CACHE
7433 #endif
7434 
CanSavePresentation(nsIRequest * aNewRequest)7435 bool nsDocument::CanSavePresentation(nsIRequest* aNewRequest) {
7436   if (EventHandlingSuppressed()) {
7437     return false;
7438   }
7439 
7440   // Do not allow suspended windows to be placed in the
7441   // bfcache.  This method is also used to verify a document
7442   // coming out of the bfcache is ok to restore, though.  So
7443   // we only want to block suspend windows that aren't also
7444   // frozen.
7445   nsPIDOMWindowInner* win = GetInnerWindow();
7446   if (win && win->IsSuspended() && !win->IsFrozen()) {
7447     return false;
7448   }
7449 
7450   // Check our event listener manager for unload/beforeunload listeners.
7451   nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
7452   if (piTarget) {
7453     EventListenerManager* manager = piTarget->GetExistingListenerManager();
7454     if (manager && manager->HasUnloadListeners()) {
7455       return false;
7456     }
7457   }
7458 
7459   // Check if we have pending network requests
7460   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7461   if (loadGroup) {
7462     nsCOMPtr<nsISimpleEnumerator> requests;
7463     loadGroup->GetRequests(getter_AddRefs(requests));
7464 
7465     bool hasMore = false;
7466 
7467     // We want to bail out if we have any requests other than aNewRequest (or
7468     // in the case when aNewRequest is a part of a multipart response the base
7469     // channel the multipart response is coming in on).
7470     nsCOMPtr<nsIChannel> baseChannel;
7471     nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
7472     if (part) {
7473       part->GetBaseChannel(getter_AddRefs(baseChannel));
7474     }
7475 
7476     while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
7477       nsCOMPtr<nsISupports> elem;
7478       requests->GetNext(getter_AddRefs(elem));
7479 
7480       nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
7481       if (request && request != aNewRequest && request != baseChannel) {
7482 #ifdef DEBUG_PAGE_CACHE
7483         nsAutoCString requestName, docSpec;
7484         request->GetName(requestName);
7485         if (mDocumentURI) mDocumentURI->GetSpec(docSpec);
7486 
7487         printf("document %s has request %s\n", docSpec.get(),
7488                requestName.get());
7489 #endif
7490         return false;
7491       }
7492     }
7493   }
7494 
7495   // Check if we have active GetUserMedia use
7496   if (MediaManager::Exists() && win &&
7497       MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
7498     return false;
7499   }
7500 
7501 #ifdef MOZ_WEBRTC
7502   // Check if we have active PeerConnections
7503   if (win && win->HasActivePeerConnections()) {
7504     return false;
7505   }
7506 #endif  // MOZ_WEBRTC
7507 
7508   // Don't save presentations for documents containing EME content, so that
7509   // CDMs reliably shutdown upon user navigation.
7510   if (ContainsEMEContent()) {
7511     return false;
7512   }
7513 
7514   // Don't save presentations for documents containing MSE content, to
7515   // reduce memory usage.
7516   if (ContainsMSEContent()) {
7517     return false;
7518   }
7519 
7520   if (mSubDocuments) {
7521     for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7522       auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7523       nsIDocument* subdoc = entry->mSubDocument;
7524 
7525       // The aIgnoreRequest we were passed is only for us, so don't pass it on.
7526       bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
7527       if (!canCache) {
7528         return false;
7529       }
7530     }
7531   }
7532 
7533   if (win) {
7534     auto* globalWindow = nsGlobalWindowInner::Cast(win);
7535 #ifdef MOZ_WEBSPEECH
7536     if (globalWindow->HasActiveSpeechSynthesis()) {
7537       return false;
7538     }
7539 #endif
7540     if (globalWindow->HasUsedVR()) {
7541       return false;
7542     }
7543   }
7544 
7545   return true;
7546 }
7547 
Destroy()7548 void nsDocument::Destroy() {
7549   // The ContentViewer wants to release the document now.  So, tell our content
7550   // to drop any references to the document so that it can be destroyed.
7551   if (mIsGoingAway) return;
7552 
7553   mIsGoingAway = true;
7554 
7555   ScriptLoader()->Destroy();
7556   SetScriptGlobalObject(nullptr);
7557   RemovedFromDocShell();
7558 
7559   bool oldVal = mInUnlinkOrDeletion;
7560   mInUnlinkOrDeletion = true;
7561   uint32_t i, count = mChildren.ChildCount();
7562   for (i = 0; i < count; ++i) {
7563     mChildren.ChildAt(i)->DestroyContent();
7564   }
7565   mInUnlinkOrDeletion = oldVal;
7566 
7567   mLayoutHistoryState = nullptr;
7568 
7569   // Shut down our external resource map.  We might not need this for
7570   // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
7571   // tearing down all those frame trees right now is the right thing to do.
7572   mExternalResourceMap.Shutdown();
7573 }
7574 
RemovedFromDocShell()7575 void nsDocument::RemovedFromDocShell() {
7576   if (mRemovedFromDocShell) return;
7577 
7578   mRemovedFromDocShell = true;
7579   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
7580 
7581   uint32_t i, count = mChildren.ChildCount();
7582   for (i = 0; i < count; ++i) {
7583     mChildren.ChildAt(i)->SaveSubtreeState();
7584   }
7585 }
7586 
GetLayoutHistoryState() const7587 already_AddRefed<nsILayoutHistoryState> nsDocument::GetLayoutHistoryState()
7588     const {
7589   nsCOMPtr<nsILayoutHistoryState> state;
7590   if (!mScriptGlobalObject) {
7591     state = mLayoutHistoryState;
7592   } else {
7593     nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
7594     if (docShell) {
7595       docShell->GetLayoutHistoryState(getter_AddRefs(state));
7596     }
7597   }
7598 
7599   return state.forget();
7600 }
7601 
EnsureOnloadBlocker()7602 void nsDocument::EnsureOnloadBlocker() {
7603   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
7604   // -- it's not ours.
7605   if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
7606     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7607     if (loadGroup) {
7608       // Check first to see if mOnloadBlocker is in the loadgroup.
7609       nsCOMPtr<nsISimpleEnumerator> requests;
7610       loadGroup->GetRequests(getter_AddRefs(requests));
7611 
7612       bool hasMore = false;
7613       while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
7614         nsCOMPtr<nsISupports> elem;
7615         requests->GetNext(getter_AddRefs(elem));
7616         nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
7617         if (request && request == mOnloadBlocker) {
7618           return;
7619         }
7620       }
7621 
7622       // Not in the loadgroup, so add it.
7623       loadGroup->AddRequest(mOnloadBlocker, nullptr);
7624     }
7625   }
7626 }
7627 
AsyncBlockOnload()7628 void nsDocument::AsyncBlockOnload() {
7629   while (mAsyncOnloadBlockCount) {
7630     --mAsyncOnloadBlockCount;
7631     BlockOnload();
7632   }
7633 }
7634 
BlockOnload()7635 void nsDocument::BlockOnload() {
7636   if (mDisplayDocument) {
7637     mDisplayDocument->BlockOnload();
7638     return;
7639   }
7640 
7641   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
7642   // -- it's not ours.
7643   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
7644     if (!nsContentUtils::IsSafeToRunScript()) {
7645       // Because AddRequest may lead to OnStateChange calls in chrome,
7646       // block onload only when there are no script blockers.
7647       ++mAsyncOnloadBlockCount;
7648       if (mAsyncOnloadBlockCount == 1) {
7649         nsContentUtils::AddScriptRunner(
7650             NewRunnableMethod("nsDocument::AsyncBlockOnload", this,
7651                               &nsDocument::AsyncBlockOnload));
7652       }
7653       return;
7654     }
7655     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7656     if (loadGroup) {
7657       loadGroup->AddRequest(mOnloadBlocker, nullptr);
7658     }
7659   }
7660   ++mOnloadBlockCount;
7661 }
7662 
UnblockOnload(bool aFireSync)7663 void nsDocument::UnblockOnload(bool aFireSync) {
7664   if (mDisplayDocument) {
7665     mDisplayDocument->UnblockOnload(aFireSync);
7666     return;
7667   }
7668 
7669   if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
7670     NS_NOTREACHED(
7671         "More UnblockOnload() calls than BlockOnload() calls; dropping call");
7672     return;
7673   }
7674 
7675   --mOnloadBlockCount;
7676 
7677   if (mOnloadBlockCount == 0) {
7678     if (mScriptGlobalObject) {
7679       // Only manipulate the loadgroup in this case, because if
7680       // mScriptGlobalObject is null, it's not ours.
7681       if (aFireSync && mAsyncOnloadBlockCount == 0) {
7682         // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
7683         ++mOnloadBlockCount;
7684         DoUnblockOnload();
7685       } else {
7686         PostUnblockOnloadEvent();
7687       }
7688     } else if (mIsBeingUsedAsImage) {
7689       // To correctly unblock onload for a document that contains an SVG
7690       // image, we need to know when all of the SVG document's resources are
7691       // done loading, in a way comparable to |window.onload|. We fire this
7692       // event to indicate that the SVG should be considered fully loaded.
7693       // Because scripting is disabled on SVG-as-image documents, this event
7694       // is not accessible to content authors. (See bug 837315.)
7695       RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
7696           this, NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), false, false);
7697       asyncDispatcher->PostDOMEvent();
7698     }
7699   }
7700 }
7701 
7702 class nsUnblockOnloadEvent : public Runnable {
7703  public:
nsUnblockOnloadEvent(nsDocument * aDoc)7704   explicit nsUnblockOnloadEvent(nsDocument* aDoc)
7705       : mozilla::Runnable("nsUnblockOnloadEvent"), mDoc(aDoc) {}
Run()7706   NS_IMETHOD Run() override {
7707     mDoc->DoUnblockOnload();
7708     return NS_OK;
7709   }
7710 
7711  private:
7712   RefPtr<nsDocument> mDoc;
7713 };
7714 
PostUnblockOnloadEvent()7715 void nsDocument::PostUnblockOnloadEvent() {
7716   MOZ_RELEASE_ASSERT(NS_IsMainThread());
7717   nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
7718   nsresult rv = Dispatch(TaskCategory::Other, evt.forget());
7719   if (NS_SUCCEEDED(rv)) {
7720     // Stabilize block count so we don't post more events while this one is up
7721     ++mOnloadBlockCount;
7722   } else {
7723     NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
7724   }
7725 }
7726 
DoUnblockOnload()7727 void nsDocument::DoUnblockOnload() {
7728   NS_PRECONDITION(!mDisplayDocument,
7729                   "Shouldn't get here for resource document");
7730   NS_PRECONDITION(mOnloadBlockCount != 0,
7731                   "Shouldn't have a count of zero here, since we stabilized in "
7732                   "PostUnblockOnloadEvent");
7733 
7734   --mOnloadBlockCount;
7735 
7736   if (mOnloadBlockCount != 0) {
7737     // We blocked again after the last unblock.  Nothing to do here.  We'll
7738     // post a new event when we unblock again.
7739     return;
7740   }
7741 
7742   if (mAsyncOnloadBlockCount != 0) {
7743     // We need to wait until the async onload block has been handled.
7744     PostUnblockOnloadEvent();
7745   }
7746 
7747   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
7748   // -- it's not ours.
7749   if (mScriptGlobalObject) {
7750     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7751     if (loadGroup) {
7752       loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
7753     }
7754   }
7755 }
7756 
GetContentInThisDocument(nsIFrame * aFrame) const7757 nsIContent* nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const {
7758   for (nsIFrame* f = aFrame; f;
7759        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
7760     nsIContent* content = f->GetContent();
7761     if (!content || content->IsInAnonymousSubtree()) continue;
7762 
7763     if (content->OwnerDoc() == this) {
7764       return content;
7765     }
7766     // We must be in a subdocument so jump directly to the root frame.
7767     // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
7768     // the containing document.
7769     f = f->PresContext()->GetPresShell()->GetRootFrame();
7770   }
7771 
7772   return nullptr;
7773 }
7774 
DispatchPageTransition(EventTarget * aDispatchTarget,const nsAString & aType,bool aPersisted)7775 void nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
7776                                         const nsAString& aType,
7777                                         bool aPersisted) {
7778   if (!aDispatchTarget) {
7779     return;
7780   }
7781 
7782   PageTransitionEventInit init;
7783   init.mBubbles = true;
7784   init.mCancelable = true;
7785   init.mPersisted = aPersisted;
7786 
7787   nsDocShell* docShell = mDocumentContainer.get();
7788   init.mInFrameSwap = docShell && docShell->InFrameSwap();
7789 
7790   RefPtr<PageTransitionEvent> event =
7791       PageTransitionEvent::Constructor(this, aType, init);
7792 
7793   event->SetTrusted(true);
7794   event->SetTarget(this);
7795   EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, nullptr,
7796                                     nullptr);
7797 }
7798 
NotifyPageShow(nsIDocument * aDocument,void * aData)7799 static bool NotifyPageShow(nsIDocument* aDocument, void* aData) {
7800   const bool* aPersistedPtr = static_cast<const bool*>(aData);
7801   aDocument->OnPageShow(*aPersistedPtr, nullptr);
7802   return true;
7803 }
7804 
OnPageShow(bool aPersisted,EventTarget * aDispatchStartTarget)7805 void nsDocument::OnPageShow(bool aPersisted,
7806                             EventTarget* aDispatchStartTarget) {
7807   mVisible = true;
7808 
7809   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
7810   EnumerateExternalResources(NotifyPageShow, &aPersisted);
7811 
7812   Element* root = GetRootElement();
7813   if (aPersisted && root) {
7814     // Send out notifications that our <link> elements are attached.
7815     RefPtr<nsContentList> links =
7816         NS_GetContentList(root, kNameSpaceID_XHTML, NS_LITERAL_STRING("link"));
7817 
7818     uint32_t linkCount = links->Length(true);
7819     for (uint32_t i = 0; i < linkCount; ++i) {
7820       static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
7821     }
7822   }
7823 
7824   // See nsIDocument
7825   if (!aDispatchStartTarget) {
7826     // Set mIsShowing before firing events, in case those event handlers
7827     // move us around.
7828     mIsShowing = true;
7829   }
7830 
7831   if (mAnimationController) {
7832     mAnimationController->OnPageShow();
7833   }
7834 
7835   if (aPersisted) {
7836     ImageTracker()->SetAnimatingState(true);
7837   }
7838 
7839   UpdateVisibilityState();
7840 
7841   if (!mIsBeingUsedAsImage) {
7842     // Dispatch observer notification to notify observers page is shown.
7843     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
7844     if (os) {
7845       nsIPrincipal* principal = GetPrincipal();
7846       os->NotifyObservers(static_cast<nsIDocument*>(this),
7847                           nsContentUtils::IsSystemPrincipal(principal)
7848                               ? "chrome-page-shown"
7849                               : "content-page-shown",
7850                           nullptr);
7851     }
7852 
7853     nsCOMPtr<EventTarget> target = aDispatchStartTarget;
7854     if (!target) {
7855       target = do_QueryInterface(GetWindow());
7856     }
7857     DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
7858   }
7859 }
7860 
NotifyPageHide(nsIDocument * aDocument,void * aData)7861 static bool NotifyPageHide(nsIDocument* aDocument, void* aData) {
7862   const bool* aPersistedPtr = static_cast<const bool*>(aData);
7863   aDocument->OnPageHide(*aPersistedPtr, nullptr);
7864   return true;
7865 }
7866 
DispatchCustomEventWithFlush(nsINode * aTarget,const nsAString & aEventType,bool aBubbles,bool aOnlyChromeDispatch)7867 static void DispatchCustomEventWithFlush(nsINode* aTarget,
7868                                          const nsAString& aEventType,
7869                                          bool aBubbles,
7870                                          bool aOnlyChromeDispatch) {
7871   RefPtr<Event> event = NS_NewDOMEvent(aTarget, nullptr, nullptr);
7872   event->InitEvent(aEventType, aBubbles, false);
7873   event->SetTrusted(true);
7874   if (aOnlyChromeDispatch) {
7875     event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
7876   }
7877   if (nsPresContext* presContext = aTarget->OwnerDoc()->GetPresContext()) {
7878     presContext->RefreshDriver()->ScheduleEventDispatch(aTarget, event);
7879   }
7880 }
7881 
DispatchFullScreenChange(nsIDocument * aTarget)7882 static void DispatchFullScreenChange(nsIDocument* aTarget) {
7883   DispatchCustomEventWithFlush(aTarget, NS_LITERAL_STRING("fullscreenchange"),
7884                                /* Bubbles */ true, /* OnlyChrome */ false);
7885 }
7886 
7887 static void ClearPendingFullscreenRequests(nsIDocument* aDoc);
7888 
OnPageHide(bool aPersisted,EventTarget * aDispatchStartTarget)7889 void nsDocument::OnPageHide(bool aPersisted,
7890                             EventTarget* aDispatchStartTarget) {
7891   // Send out notifications that our <link> elements are detached,
7892   // but only if this is not a full unload.
7893   Element* root = GetRootElement();
7894   if (aPersisted && root) {
7895     RefPtr<nsContentList> links =
7896         NS_GetContentList(root, kNameSpaceID_XHTML, NS_LITERAL_STRING("link"));
7897 
7898     uint32_t linkCount = links->Length(true);
7899     for (uint32_t i = 0; i < linkCount; ++i) {
7900       static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
7901     }
7902   }
7903 
7904   // See nsIDocument
7905   if (!aDispatchStartTarget) {
7906     // Set mIsShowing before firing events, in case those event handlers
7907     // move us around.
7908     mIsShowing = false;
7909   }
7910 
7911   if (mAnimationController) {
7912     mAnimationController->OnPageHide();
7913   }
7914 
7915   // We do not stop the animations (bug 1024343)
7916   // when the page is refreshing while being dragged out
7917   nsDocShell* docShell = mDocumentContainer.get();
7918   if (aPersisted && !(docShell && docShell->InFrameSwap())) {
7919     ImageTracker()->SetAnimatingState(false);
7920   }
7921 
7922   ExitPointerLock();
7923 
7924   if (!mIsBeingUsedAsImage) {
7925     // Dispatch observer notification to notify observers page is hidden.
7926     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
7927     if (os) {
7928       nsIPrincipal* principal = GetPrincipal();
7929       os->NotifyObservers(static_cast<nsIDocument*>(this),
7930                           nsContentUtils::IsSystemPrincipal(principal)
7931                               ? "chrome-page-hidden"
7932                               : "content-page-hidden",
7933                           nullptr);
7934     }
7935 
7936     // Now send out a PageHide event.
7937     nsCOMPtr<EventTarget> target = aDispatchStartTarget;
7938     if (!target) {
7939       target = do_QueryInterface(GetWindow());
7940     }
7941     {
7942       PageUnloadingEventTimeStamp timeStamp(this);
7943       DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
7944     }
7945   }
7946 
7947   mVisible = false;
7948 
7949   UpdateVisibilityState();
7950 
7951   EnumerateExternalResources(NotifyPageHide, &aPersisted);
7952   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
7953 
7954   ClearPendingFullscreenRequests(this);
7955   if (FullScreenStackTop()) {
7956     // If this document was fullscreen, we should exit fullscreen in this
7957     // doctree branch. This ensures that if the user navigates while in
7958     // fullscreen mode we don't leave its still visible ancestor documents
7959     // in fullscreen mode. So exit fullscreen in the document's fullscreen
7960     // root document, as this will exit fullscreen in all the root's
7961     // descendant documents. Note that documents are removed from the
7962     // doctree by the time OnPageHide() is called, so we must store a
7963     // reference to the root (in nsDocument::mFullscreenRoot) since we can't
7964     // just traverse the doctree to get the root.
7965     nsIDocument::ExitFullscreenInDocTree(this);
7966 
7967     // Since the document is removed from the doctree before OnPageHide() is
7968     // called, ExitFullscreen() can't traverse from the root down to *this*
7969     // document, so we must manually call CleanupFullscreenState() below too.
7970     // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
7971     // so we *must* call it after ExitFullscreen(), not before.
7972     // OnPageHide() is called in every hidden (i.e. descendant) document,
7973     // so calling CleanupFullscreenState() here will ensure all hidden
7974     // documents have their fullscreen state reset.
7975     CleanupFullscreenState();
7976 
7977     // If anyone was listening to this document's state, advertizing the state
7978     // change would be the least of the politeness.
7979     DispatchFullScreenChange(this);
7980   }
7981 }
7982 
WillDispatchMutationEvent(nsINode * aTarget)7983 void nsDocument::WillDispatchMutationEvent(nsINode* aTarget) {
7984   NS_ASSERTION(
7985       mSubtreeModifiedDepth != 0 || mSubtreeModifiedTargets.Count() == 0,
7986       "mSubtreeModifiedTargets not cleared after dispatching?");
7987   ++mSubtreeModifiedDepth;
7988   if (aTarget) {
7989     // MayDispatchMutationEvent is often called just before this method,
7990     // so it has already appended the node to mSubtreeModifiedTargets.
7991     int32_t count = mSubtreeModifiedTargets.Count();
7992     if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
7993       mSubtreeModifiedTargets.AppendObject(aTarget);
7994     }
7995   }
7996 }
7997 
MutationEventDispatched(nsINode * aTarget)7998 void nsDocument::MutationEventDispatched(nsINode* aTarget) {
7999   --mSubtreeModifiedDepth;
8000   if (mSubtreeModifiedDepth == 0) {
8001     int32_t count = mSubtreeModifiedTargets.Count();
8002     if (!count) {
8003       return;
8004     }
8005 
8006     nsPIDOMWindowInner* window = GetInnerWindow();
8007     if (window &&
8008         !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
8009       mSubtreeModifiedTargets.Clear();
8010       return;
8011     }
8012 
8013     nsCOMArray<nsINode> realTargets;
8014     for (int32_t i = 0; i < count; ++i) {
8015       nsINode* possibleTarget = mSubtreeModifiedTargets[i];
8016       nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
8017       if (content && content->ChromeOnlyAccess()) {
8018         continue;
8019       }
8020 
8021       nsINode* commonAncestor = nullptr;
8022       int32_t realTargetCount = realTargets.Count();
8023       for (int32_t j = 0; j < realTargetCount; ++j) {
8024         commonAncestor =
8025             nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
8026         if (commonAncestor) {
8027           realTargets.ReplaceObjectAt(commonAncestor, j);
8028           break;
8029         }
8030       }
8031       if (!commonAncestor) {
8032         realTargets.AppendObject(possibleTarget);
8033       }
8034     }
8035 
8036     mSubtreeModifiedTargets.Clear();
8037 
8038     int32_t realTargetCount = realTargets.Count();
8039     for (int32_t k = 0; k < realTargetCount; ++k) {
8040       InternalMutationEvent mutation(true, eLegacySubtreeModified);
8041       (new AsyncEventDispatcher(realTargets[k], mutation))
8042           ->RunDOMEventWhenSafe();
8043     }
8044   }
8045 }
8046 
AddStyleRelevantLink(Link * aLink)8047 void nsDocument::AddStyleRelevantLink(Link* aLink) {
8048   NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
8049 #ifdef DEBUG
8050   nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
8051   NS_ASSERTION(!entry, "Document already knows about this Link!");
8052   mStyledLinksCleared = false;
8053 #endif
8054   (void)mStyledLinks.PutEntry(aLink);
8055 }
8056 
ForgetLink(Link * aLink)8057 void nsDocument::ForgetLink(Link* aLink) {
8058   NS_ASSERTION(aLink, "Passing in a null link.  Expect crashes RSN!");
8059 #ifdef DEBUG
8060   nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
8061   NS_ASSERTION(entry || mStyledLinksCleared,
8062                "Document knows nothing about this Link!");
8063 #endif
8064   mStyledLinks.RemoveEntry(aLink);
8065 }
8066 
DestroyElementMaps()8067 void nsDocument::DestroyElementMaps() {
8068 #ifdef DEBUG
8069   mStyledLinksCleared = true;
8070 #endif
8071   mStyledLinks.Clear();
8072   mIdentifierMap.Clear();
8073   ++mExpandoAndGeneration.generation;
8074 }
8075 
RefreshLinkHrefs()8076 void nsDocument::RefreshLinkHrefs() {
8077   // Get a list of all links we know about.  We will reset them, which will
8078   // remove them from the document, so we need a copy of what is in the
8079   // hashtable.
8080   LinkArray linksToNotify(mStyledLinks.Count());
8081   for (auto iter = mStyledLinks.ConstIter(); !iter.Done(); iter.Next()) {
8082     linksToNotify.AppendElement(iter.Get()->GetKey());
8083   }
8084 
8085   // Reset all of our styled links.
8086   nsAutoScriptBlocker scriptBlocker;
8087   for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
8088     linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
8089   }
8090 }
8091 
CloneDocHelper(nsDocument * clone,bool aPreallocateChildren) const8092 nsresult nsDocument::CloneDocHelper(nsDocument* clone,
8093                                     bool aPreallocateChildren) const {
8094   clone->mIsStaticDocument = mCreatingStaticClone;
8095 
8096   // Init document
8097   nsresult rv = clone->Init();
8098   NS_ENSURE_SUCCESS(rv, rv);
8099 
8100   if (mCreatingStaticClone) {
8101     nsCOMPtr<nsILoadGroup> loadGroup;
8102 
8103     // |mDocumentContainer| is the container of the document that is being
8104     // created and not the original container. See CreateStaticClone function().
8105     nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
8106     if (docLoader) {
8107       docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
8108     }
8109     nsCOMPtr<nsIChannel> channel = GetChannel();
8110     nsCOMPtr<nsIURI> uri;
8111     if (channel) {
8112       NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
8113     } else {
8114       uri = nsIDocument::GetDocumentURI();
8115     }
8116     clone->mChannel = channel;
8117     if (uri) {
8118       clone->ResetToURI(uri, loadGroup, NodePrincipal());
8119     }
8120 
8121     clone->SetContainer(mDocumentContainer);
8122   }
8123 
8124   // Now ensure that our clone has the same URI, base URI, and principal as us.
8125   // We do this after the mCreatingStaticClone block above, because that block
8126   // can set the base URI to an incorrect value in cases when base URI
8127   // information came from the channel.  So we override explicitly, and do it
8128   // for all these properties, in case ResetToURI messes with any of the rest of
8129   // them.
8130   clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
8131   clone->SetChromeXHRDocURI(mChromeXHRDocURI);
8132   clone->SetPrincipal(NodePrincipal());
8133   clone->mDocumentBaseURI = mDocumentBaseURI;
8134   clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
8135 
8136   bool hasHadScriptObject = true;
8137   nsIScriptGlobalObject* scriptObject =
8138       GetScriptHandlingObject(hasHadScriptObject);
8139   NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
8140   if (mCreatingStaticClone) {
8141     // If we're doing a static clone (print, print preview), then we're going to
8142     // be setting a scope object after the clone. It's better to set it only
8143     // once, so we don't do that here. However, we do want to act as if there is
8144     // a script handling object. So we set mHasHadScriptHandlingObject.
8145     clone->mHasHadScriptHandlingObject = true;
8146   } else if (scriptObject) {
8147     clone->SetScriptHandlingObject(scriptObject);
8148   } else {
8149     clone->SetScopeObject(GetScopeObject());
8150   }
8151   // Make the clone a data document
8152   clone->SetLoadedAsData(true);
8153 
8154   // Misc state
8155 
8156   // State from nsIDocument
8157   clone->mCharacterSet = mCharacterSet;
8158   clone->mCharacterSetSource = mCharacterSetSource;
8159   clone->mCompatMode = mCompatMode;
8160   clone->mBidiOptions = mBidiOptions;
8161   clone->mContentLanguage = mContentLanguage;
8162   clone->SetContentTypeInternal(GetContentTypeInternal());
8163   clone->mSecurityInfo = mSecurityInfo;
8164   clone->mStyleBackendType = mStyleBackendType;
8165 
8166   // State from nsDocument
8167   clone->mType = mType;
8168   clone->mXMLDeclarationBits = mXMLDeclarationBits;
8169   clone->mBaseTarget = mBaseTarget;
8170 
8171   // Preallocate attributes and child arrays
8172   rv = clone->mChildren.EnsureCapacityToClone(mChildren, aPreallocateChildren);
8173   NS_ENSURE_SUCCESS(rv, rv);
8174 
8175   return NS_OK;
8176 }
8177 
SetReadyStateInternal(ReadyState rs)8178 void nsDocument::SetReadyStateInternal(ReadyState rs) {
8179   mReadyState = rs;
8180   if (rs == READYSTATE_UNINITIALIZED) {
8181     // Transition back to uninitialized happens only to keep assertions happy
8182     // right before readyState transitions to something else. Make this
8183     // transition undetectable by Web content.
8184     return;
8185   }
8186   if (mTiming) {
8187     switch (rs) {
8188       case READYSTATE_LOADING:
8189         mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
8190         break;
8191       case READYSTATE_INTERACTIVE:
8192         mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
8193         break;
8194       case READYSTATE_COMPLETE:
8195         mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
8196         break;
8197       default:
8198         NS_WARNING("Unexpected ReadyState value");
8199         break;
8200     }
8201   }
8202   // At the time of loading start, we don't have timing object, record time.
8203   if (READYSTATE_LOADING == rs) {
8204     mLoadingTimeStamp = mozilla::TimeStamp::Now();
8205   }
8206 
8207   RecordNavigationTiming(rs);
8208 
8209   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
8210       this, NS_LITERAL_STRING("readystatechange"), false, false);
8211   asyncDispatcher->RunDOMEventWhenSafe();
8212 }
8213 
GetReadyState(nsAString & aReadyState) const8214 void nsIDocument::GetReadyState(nsAString& aReadyState) const {
8215   switch (mReadyState) {
8216     case READYSTATE_LOADING:
8217       aReadyState.AssignLiteral(u"loading");
8218       break;
8219     case READYSTATE_INTERACTIVE:
8220       aReadyState.AssignLiteral(u"interactive");
8221       break;
8222     case READYSTATE_COMPLETE:
8223       aReadyState.AssignLiteral(u"complete");
8224       break;
8225     default:
8226       aReadyState.AssignLiteral(u"uninitialized");
8227   }
8228 }
8229 
SuppressEventHandlingInDocument(nsIDocument * aDocument,void * aData)8230 static bool SuppressEventHandlingInDocument(nsIDocument* aDocument,
8231                                             void* aData) {
8232   aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData));
8233 
8234   return true;
8235 }
8236 
SuppressEventHandling(uint32_t aIncrease)8237 void nsDocument::SuppressEventHandling(uint32_t aIncrease) {
8238   mEventsSuppressed += aIncrease;
8239   UpdateFrameRequestCallbackSchedulingState();
8240   for (uint32_t i = 0; i < aIncrease; ++i) {
8241     ScriptLoader()->AddExecuteBlocker();
8242   }
8243 
8244   EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
8245 }
8246 
FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument>> & aDocuments,bool aFireEvents)8247 static void FireOrClearDelayedEvents(
8248     nsTArray<nsCOMPtr<nsIDocument>>& aDocuments, bool aFireEvents) {
8249   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
8250   if (!fm) return;
8251 
8252   for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
8253     // NB: Don't bother trying to fire delayed events on documents that were
8254     // closed before this event ran.
8255     if (!aDocuments[i]->EventHandlingSuppressed()) {
8256       fm->FireDelayedEvents(aDocuments[i]);
8257       nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
8258       if (shell) {
8259         // Only fire events for active documents.
8260         bool fire = aFireEvents && aDocuments[i]->GetInnerWindow() &&
8261                     aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
8262         shell->FireOrClearDelayedEvents(fire);
8263       }
8264     }
8265   }
8266 }
8267 
PreloadPictureOpened()8268 void nsDocument::PreloadPictureOpened() { mPreloadPictureDepth++; }
8269 
PreloadPictureClosed()8270 void nsDocument::PreloadPictureClosed() {
8271   mPreloadPictureDepth--;
8272   if (mPreloadPictureDepth == 0) {
8273     mPreloadPictureFoundSource.SetIsVoid(true);
8274   } else {
8275     MOZ_ASSERT(mPreloadPictureDepth >= 0);
8276   }
8277 }
8278 
PreloadPictureImageSource(const nsAString & aSrcsetAttr,const nsAString & aSizesAttr,const nsAString & aTypeAttr,const nsAString & aMediaAttr)8279 void nsDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr,
8280                                            const nsAString& aSizesAttr,
8281                                            const nsAString& aTypeAttr,
8282                                            const nsAString& aMediaAttr) {
8283   // Nested pictures are not valid syntax, so while we'll eventually load them,
8284   // it's not worth tracking sources mixed between nesting levels to preload
8285   // them effectively.
8286   if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) {
8287     // <picture> selects the first matching source, so if this returns a URI we
8288     // needn't consider new sources until a new <picture> is encountered.
8289     bool found = HTMLImageElement::SelectSourceForTagWithAttrs(
8290         this, true, VoidString(), aSrcsetAttr, aSizesAttr, aTypeAttr,
8291         aMediaAttr, mPreloadPictureFoundSource);
8292     if (found && mPreloadPictureFoundSource.IsVoid()) {
8293       // Found an empty source, which counts
8294       mPreloadPictureFoundSource.SetIsVoid(false);
8295     }
8296   }
8297 }
8298 
ResolvePreloadImage(nsIURI * aBaseURI,const nsAString & aSrcAttr,const nsAString & aSrcsetAttr,const nsAString & aSizesAttr,bool * aIsImgSet)8299 already_AddRefed<nsIURI> nsDocument::ResolvePreloadImage(
8300     nsIURI* aBaseURI, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr,
8301     const nsAString& aSizesAttr, bool* aIsImgSet) {
8302   nsString sourceURL;
8303   bool isImgSet;
8304   if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
8305     // We're in a <picture> element and found a URI from a source previous to
8306     // this image, use it.
8307     sourceURL = mPreloadPictureFoundSource;
8308     isImgSet = true;
8309   } else {
8310     // Otherwise try to use this <img> as a source
8311     HTMLImageElement::SelectSourceForTagWithAttrs(
8312         this, false, aSrcAttr, aSrcsetAttr, aSizesAttr, VoidString(),
8313         VoidString(), sourceURL);
8314     isImgSet = !aSrcsetAttr.IsEmpty();
8315   }
8316 
8317   // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
8318   if (sourceURL.IsEmpty()) {
8319     return nullptr;
8320   }
8321 
8322   // Construct into URI using passed baseURI (the parser may know of base URI
8323   // changes that have not reached us)
8324   nsresult rv;
8325   nsCOMPtr<nsIURI> uri;
8326   rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL,
8327                                                  this, aBaseURI);
8328   if (NS_FAILED(rv)) {
8329     return nullptr;
8330   }
8331 
8332   *aIsImgSet = isImgSet;
8333 
8334   // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
8335   // this this <picture> share the same <sources> (though this is not valid per
8336   // spec)
8337   return uri.forget();
8338 }
8339 
MaybePreLoadImage(nsIURI * uri,const nsAString & aCrossOriginAttr,ReferrerPolicy aReferrerPolicy,bool aIsImgSet)8340 void nsDocument::MaybePreLoadImage(nsIURI* uri,
8341                                    const nsAString& aCrossOriginAttr,
8342                                    ReferrerPolicy aReferrerPolicy,
8343                                    bool aIsImgSet) {
8344   // Early exit if the img is already present in the img-cache
8345   // which indicates that the "real" load has already started and
8346   // that we shouldn't preload it.
8347   if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument*>(this))) {
8348     return;
8349   }
8350 
8351   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
8352                           nsContentUtils::CORSModeToLoadImageFlags(
8353                               Element::StringToCORSMode(aCrossOriginAttr));
8354 
8355   nsContentPolicyType policyType =
8356       aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET
8357                 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
8358 
8359   // Image not in cache - trigger preload
8360   RefPtr<imgRequestProxy> request;
8361   nsresult rv = nsContentUtils::LoadImage(
8362       uri, static_cast<nsINode*>(this), this, NodePrincipal(), 0,
8363       mDocumentURI,  // uri of document used as referrer
8364       aReferrerPolicy,
8365       nullptr,  // no observer
8366       loadFlags, NS_LITERAL_STRING("img"), getter_AddRefs(request), policyType);
8367 
8368   // Pin image-reference to avoid evicting it from the img-cache before
8369   // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
8370   // unlink
8371   if (NS_SUCCEEDED(rv)) {
8372     mPreloadingImages.Put(uri, request.forget());
8373   }
8374 }
8375 
MaybePreconnect(nsIURI * aOrigURI,mozilla::CORSMode aCORSMode)8376 void nsDocument::MaybePreconnect(nsIURI* aOrigURI,
8377                                  mozilla::CORSMode aCORSMode) {
8378   NS_MutateURI mutator(aOrigURI);
8379   if (NS_FAILED(mutator.GetStatus())) {
8380     return;
8381   }
8382 
8383   // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
8384   // which ignores the path and uses only the origin. The other is for the
8385   // document mPreloadedPreconnects de-duplication hash. Anonymous vs
8386   // non-Anonymous preconnects create different connections on the wire and
8387   // therefore should not be considred duplicates of each other and we
8388   // normalize the path before putting it in the hash to accomplish that.
8389 
8390   if (aCORSMode == CORS_ANONYMOUS) {
8391     mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/anonymous"));
8392   } else {
8393     mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/"));
8394   }
8395 
8396   nsCOMPtr<nsIURI> uri;
8397   nsresult rv = mutator.Finalize(uri);
8398   if (NS_FAILED(rv)) {
8399     return;
8400   }
8401 
8402   auto entry = mPreloadedPreconnects.LookupForAdd(uri);
8403   if (entry) {
8404     return;  // we found an existing entry
8405   }
8406   entry.OrInsert([]() { return true; });
8407 
8408   nsCOMPtr<nsISpeculativeConnect> speculator(
8409       do_QueryInterface(nsContentUtils::GetIOService()));
8410   if (!speculator) {
8411     return;
8412   }
8413 
8414   if (aCORSMode == CORS_ANONYMOUS) {
8415     speculator->SpeculativeAnonymousConnect2(uri, NodePrincipal(), nullptr);
8416   } else {
8417     speculator->SpeculativeConnect2(uri, NodePrincipal(), nullptr);
8418   }
8419 }
8420 
ForgetImagePreload(nsIURI * aURI)8421 void nsDocument::ForgetImagePreload(nsIURI* aURI) {
8422   // Checking count is faster than hashing the URI in the common
8423   // case of empty table.
8424   if (mPreloadingImages.Count() != 0) {
8425     nsCOMPtr<imgIRequest> req;
8426     mPreloadingImages.Remove(aURI, getter_AddRefs(req));
8427     if (req) {
8428       // Make sure to cancel the request so imagelib knows it's gone.
8429       req->CancelAndForgetObserver(NS_BINDING_ABORTED);
8430     }
8431   }
8432 }
8433 
UpdateDocumentStates(EventStates aChangedStates)8434 void nsIDocument::UpdateDocumentStates(EventStates aChangedStates) {
8435   if (aChangedStates.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
8436     if (IsDocumentRightToLeft()) {
8437       mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
8438     } else {
8439       mDocumentState &= ~NS_DOCUMENT_STATE_RTL_LOCALE;
8440     }
8441   }
8442 
8443   if (aChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
8444     if (IsTopLevelWindowInactive()) {
8445       mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
8446     } else {
8447       mDocumentState &= ~NS_DOCUMENT_STATE_WINDOW_INACTIVE;
8448     }
8449   }
8450 }
8451 
8452 namespace {
8453 
8454 /**
8455  * Stub for LoadSheet(), since all we want is to get the sheet into
8456  * the CSSLoader's style cache
8457  */
8458 class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
~StubCSSLoaderObserver()8459   ~StubCSSLoaderObserver() {}
8460 
8461  public:
8462   NS_IMETHOD
StyleSheetLoaded(StyleSheet *,bool,nsresult)8463   StyleSheetLoaded(StyleSheet*, bool, nsresult) override { return NS_OK; }
8464   NS_DECL_ISUPPORTS
8465 };
8466 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
8467 
8468 }  // namespace
8469 
PreloadStyle(nsIURI * uri,const Encoding * aEncoding,const nsAString & aCrossOriginAttr,const enum ReferrerPolicy aReferrerPolicy,const nsAString & aIntegrity)8470 void nsIDocument::PreloadStyle(nsIURI* uri, const Encoding* aEncoding,
8471                                const nsAString& aCrossOriginAttr,
8472                                const enum ReferrerPolicy aReferrerPolicy,
8473                                const nsAString& aIntegrity) {
8474   // The CSSLoader will retain this object after we return.
8475   nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
8476 
8477   // Charset names are always ASCII.
8478   CSSLoader()->LoadSheet(uri, true, NodePrincipal(), aEncoding, obs,
8479                          Element::StringToCORSMode(aCrossOriginAttr),
8480                          aReferrerPolicy, aIntegrity);
8481 }
8482 
LoadChromeSheetSync(nsIURI * uri,bool isAgentSheet,RefPtr<mozilla::StyleSheet> * aSheet)8483 nsresult nsIDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
8484                                           RefPtr<mozilla::StyleSheet>* aSheet) {
8485   css::SheetParsingMode mode =
8486       isAgentSheet ? css::eAgentSheetFeatures : css::eAuthorSheetFeatures;
8487   return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, aSheet);
8488 }
8489 
8490 class nsDelayedEventDispatcher : public Runnable {
8491  public:
nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>> & aDocuments)8492   explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
8493       : mozilla::Runnable("nsDelayedEventDispatcher") {
8494     mDocuments.SwapElements(aDocuments);
8495   }
~nsDelayedEventDispatcher()8496   virtual ~nsDelayedEventDispatcher() {}
8497 
Run()8498   NS_IMETHOD Run() override {
8499     FireOrClearDelayedEvents(mDocuments, true);
8500     return NS_OK;
8501   }
8502 
8503  private:
8504   nsTArray<nsCOMPtr<nsIDocument>> mDocuments;
8505 };
8506 
GetAndUnsuppressSubDocuments(nsIDocument * aDocument,void * aData)8507 static bool GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData) {
8508   if (aDocument->EventHandlingSuppressed() > 0) {
8509     static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
8510     aDocument->ScriptLoader()->RemoveExecuteBlocker();
8511   }
8512 
8513   nsTArray<nsCOMPtr<nsIDocument>>* docs =
8514       static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData);
8515 
8516   docs->AppendElement(aDocument);
8517   aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
8518   return true;
8519 }
8520 
UnsuppressEventHandlingAndFireEvents(bool aFireEvents)8521 void nsDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) {
8522   nsTArray<nsCOMPtr<nsIDocument>> documents;
8523   GetAndUnsuppressSubDocuments(this, &documents);
8524 
8525   if (aFireEvents) {
8526     MOZ_RELEASE_ASSERT(NS_IsMainThread());
8527     nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
8528     Dispatch(TaskCategory::Other, ded.forget());
8529   } else {
8530     FireOrClearDelayedEvents(documents, false);
8531   }
8532 }
8533 
GetCurrentContentSink()8534 nsISupports* nsDocument::GetCurrentContentSink() {
8535   return mParser ? mParser->GetContentSink() : nullptr;
8536 }
8537 
GetTemplateContentsOwner()8538 nsIDocument* nsDocument::GetTemplateContentsOwner() {
8539   if (!mTemplateContentsOwner) {
8540     bool hasHadScriptObject = true;
8541     nsIScriptGlobalObject* scriptObject =
8542         GetScriptHandlingObject(hasHadScriptObject);
8543 
8544     nsCOMPtr<nsIDOMDocument> domDocument;
8545     nsresult rv =
8546         NS_NewDOMDocument(getter_AddRefs(domDocument),
8547                           EmptyString(),  // aNamespaceURI
8548                           EmptyString(),  // aQualifiedName
8549                           nullptr,        // aDoctype
8550                           nsIDocument::GetDocumentURI(),
8551                           nsIDocument::GetDocBaseURI(), NodePrincipal(),
8552                           true,          // aLoadedAsData
8553                           scriptObject,  // aEventObject
8554                           DocumentFlavorHTML);
8555     NS_ENSURE_SUCCESS(rv, nullptr);
8556 
8557     mTemplateContentsOwner = do_QueryInterface(domDocument);
8558     NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
8559 
8560     nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
8561 
8562     if (!scriptObject) {
8563       mTemplateContentsOwner->SetScopeObject(GetScopeObject());
8564     }
8565 
8566     doc->mHasHadScriptHandlingObject = hasHadScriptObject;
8567 
8568     // Set |doc| as the template contents owner of itself so that
8569     // |doc| is the template contents owner of template elements created
8570     // by |doc|.
8571     doc->mTemplateContentsOwner = doc;
8572   }
8573 
8574   return mTemplateContentsOwner;
8575 }
8576 
FindTopWindowForElement(Element * element)8577 static already_AddRefed<nsPIDOMWindowOuter> FindTopWindowForElement(
8578     Element* element) {
8579   nsIDocument* document = element->OwnerDoc();
8580   if (!document) {
8581     return nullptr;
8582   }
8583 
8584   nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
8585   if (!window) {
8586     return nullptr;
8587   }
8588 
8589   // Trying to find the top window (equivalent to window.top).
8590   if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetTop()) {
8591     window = top.forget();
8592   }
8593   return window.forget();
8594 }
8595 
8596 /**
8597  * nsAutoFocusEvent is used to dispatch a focus event for an
8598  * nsGenericHTMLFormElement with the autofocus attribute enabled.
8599  */
8600 class nsAutoFocusEvent : public Runnable {
8601  public:
nsAutoFocusEvent(already_AddRefed<Element> && aElement,already_AddRefed<nsPIDOMWindowOuter> && aTopWindow)8602   explicit nsAutoFocusEvent(already_AddRefed<Element>&& aElement,
8603                             already_AddRefed<nsPIDOMWindowOuter>&& aTopWindow)
8604       : mozilla::Runnable("nsAutoFocusEvent"),
8605         mElement(aElement),
8606         mTopWindow(aTopWindow) {}
8607 
Run()8608   NS_IMETHOD Run() override {
8609     nsCOMPtr<nsPIDOMWindowOuter> currentTopWindow =
8610         FindTopWindowForElement(mElement);
8611     if (currentTopWindow != mTopWindow) {
8612       // The element's top window changed from when the event was queued.
8613       // Don't take away focus from an unrelated window.
8614       return NS_OK;
8615     }
8616 
8617     // Don't steal focus from the user.
8618     if (mTopWindow->GetFocusedNode()) {
8619       return NS_OK;
8620     }
8621 
8622     mozilla::ErrorResult rv;
8623     mElement->Focus(rv);
8624     return rv.StealNSResult();
8625   }
8626 
8627  private:
8628   nsCOMPtr<Element> mElement;
8629   nsCOMPtr<nsPIDOMWindowOuter> mTopWindow;
8630 };
8631 
SetAutoFocusElement(Element * aAutoFocusElement)8632 void nsDocument::SetAutoFocusElement(Element* aAutoFocusElement) {
8633   if (mAutoFocusFired) {
8634     // Too late.
8635     return;
8636   }
8637 
8638   if (mAutoFocusElement) {
8639     // The spec disallows multiple autofocus elements, so we consider only the
8640     // first one to preserve the old behavior.
8641     return;
8642   }
8643 
8644   mAutoFocusElement = do_GetWeakReference(aAutoFocusElement);
8645   TriggerAutoFocus();
8646 }
8647 
TriggerAutoFocus()8648 void nsDocument::TriggerAutoFocus() {
8649   if (mAutoFocusFired) {
8650     return;
8651   }
8652 
8653   if (!mPresShell || !mPresShell->DidInitialize()) {
8654     // Delay autofocus until frames are constructed so that we don't thrash
8655     // style and layout calculations.
8656     return;
8657   }
8658 
8659   nsCOMPtr<Element> autoFocusElement = do_QueryReferent(mAutoFocusElement);
8660   if (autoFocusElement && autoFocusElement->OwnerDoc() == this) {
8661     mAutoFocusFired = true;
8662 
8663     nsCOMPtr<nsPIDOMWindowOuter> topWindow =
8664         FindTopWindowForElement(autoFocusElement);
8665     if (!topWindow) {
8666       return;
8667     }
8668 
8669     // NOTE: This may be removed in the future since the spec technically
8670     // allows autofocus after load.
8671     nsCOMPtr<nsIDocument> topDoc = topWindow->GetExtantDoc();
8672     if (topDoc &&
8673         topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) {
8674       return;
8675     }
8676 
8677     nsCOMPtr<nsIRunnable> event =
8678         new nsAutoFocusEvent(autoFocusElement.forget(), topWindow.forget());
8679     nsresult rv = NS_DispatchToCurrentThread(event.forget());
8680     NS_ENSURE_SUCCESS_VOID(rv);
8681   }
8682 }
8683 
SetScrollToRef(nsIURI * aDocumentURI)8684 void nsDocument::SetScrollToRef(nsIURI* aDocumentURI) {
8685   if (!aDocumentURI) {
8686     return;
8687   }
8688 
8689   nsAutoCString ref;
8690 
8691   // Since all URI's that pass through here aren't URL's we can't
8692   // rely on the nsIURI implementation for providing a way for
8693   // finding the 'ref' part of the URI, we'll haveto revert to
8694   // string routines for finding the data past '#'
8695 
8696   nsresult rv = aDocumentURI->GetSpec(ref);
8697   if (NS_FAILED(rv)) {
8698     Unused << aDocumentURI->GetRef(mScrollToRef);
8699     return;
8700   }
8701 
8702   nsReadingIterator<char> start, end;
8703 
8704   ref.BeginReading(start);
8705   ref.EndReading(end);
8706 
8707   if (FindCharInReadable('#', start, end)) {
8708     ++start;  // Skip over the '#'
8709 
8710     mScrollToRef = Substring(start, end);
8711   }
8712 }
8713 
ScrollToRef()8714 void nsDocument::ScrollToRef() {
8715   if (mScrolledToRefAlready) {
8716     nsCOMPtr<nsIPresShell> shell = GetShell();
8717     if (shell) {
8718       shell->ScrollToAnchor();
8719     }
8720     return;
8721   }
8722 
8723   if (mScrollToRef.IsEmpty()) {
8724     return;
8725   }
8726 
8727   nsCOMPtr<nsIPresShell> shell = GetShell();
8728   if (shell) {
8729     nsresult rv = NS_ERROR_FAILURE;
8730     // We assume that the bytes are in UTF-8, as it says in the spec:
8731     // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
8732     NS_ConvertUTF8toUTF16 ref(mScrollToRef);
8733     // Check an empty string which might be caused by the UTF-8 conversion
8734     if (!ref.IsEmpty()) {
8735       // Note that GoToAnchor will handle flushing layout as needed.
8736       rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
8737     } else {
8738       rv = NS_ERROR_FAILURE;
8739     }
8740 
8741     if (NS_FAILED(rv)) {
8742       char* tmpstr = ToNewCString(mScrollToRef);
8743       if (!tmpstr) {
8744         return;
8745       }
8746       nsUnescape(tmpstr);
8747       nsAutoCString unescapedRef;
8748       unescapedRef.Assign(tmpstr);
8749       free(tmpstr);
8750 
8751       NS_ConvertUTF8toUTF16 utf16Str(unescapedRef);
8752       if (!utf16Str.IsEmpty()) {
8753         rv = shell->GoToAnchor(utf16Str, mChangeScrollPosWhenScrollingToRef);
8754       }
8755 
8756       // If UTF-8 URI failed then try to assume the string as a
8757       // document's charset.
8758       if (NS_FAILED(rv)) {
8759         const Encoding* encoding = GetDocumentCharacterSet();
8760         rv = encoding->DecodeWithoutBOMHandling(unescapedRef, ref);
8761         if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
8762           rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
8763         }
8764       }
8765     }
8766     if (NS_SUCCEEDED(rv)) {
8767       mScrolledToRefAlready = true;
8768     }
8769   }
8770 }
8771 
ResetScrolledToRefAlready()8772 void nsDocument::ResetScrolledToRefAlready() { mScrolledToRefAlready = false; }
8773 
SetChangeScrollPosWhenScrollingToRef(bool aValue)8774 void nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue) {
8775   mChangeScrollPosWhenScrollingToRef = aValue;
8776 }
8777 
RegisterActivityObserver(nsISupports * aSupports)8778 void nsIDocument::RegisterActivityObserver(nsISupports* aSupports) {
8779   if (!mActivityObservers) {
8780     mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports>>();
8781   }
8782   mActivityObservers->PutEntry(aSupports);
8783 }
8784 
UnregisterActivityObserver(nsISupports * aSupports)8785 bool nsIDocument::UnregisterActivityObserver(nsISupports* aSupports) {
8786   if (!mActivityObservers) {
8787     return false;
8788   }
8789   nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports);
8790   if (!entry) {
8791     return false;
8792   }
8793   mActivityObservers->RemoveEntry(entry);
8794   return true;
8795 }
8796 
EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,void * aData)8797 void nsIDocument::EnumerateActivityObservers(
8798     ActivityObserverEnumerator aEnumerator, void* aData) {
8799   if (!mActivityObservers) return;
8800 
8801   for (auto iter = mActivityObservers->ConstIter(); !iter.Done(); iter.Next()) {
8802     aEnumerator(iter.Get()->GetKey(), aData);
8803   }
8804 }
8805 
RegisterPendingLinkUpdate(Link * aLink)8806 void nsIDocument::RegisterPendingLinkUpdate(Link* aLink) {
8807   if (aLink->HasPendingLinkUpdate()) {
8808     return;
8809   }
8810 
8811   aLink->SetHasPendingLinkUpdate();
8812 
8813   if (!mHasLinksToUpdateRunnable && !mFlushingPendingLinkUpdates) {
8814     nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
8815         "nsIDocument::FlushPendingLinkUpdatesFromRunnable", this,
8816         &nsIDocument::FlushPendingLinkUpdatesFromRunnable);
8817     // Do this work in a second in the worst case.
8818     nsresult rv = NS_IdleDispatchToCurrentThread(event.forget(), 1000);
8819     if (NS_FAILED(rv)) {
8820       // If during shutdown posting a runnable doesn't succeed, we probably
8821       // don't need to update link states.
8822       return;
8823     }
8824     mHasLinksToUpdateRunnable = true;
8825   }
8826 
8827   mLinksToUpdate.InfallibleAppend(aLink);
8828 }
8829 
FlushPendingLinkUpdatesFromRunnable()8830 void nsIDocument::FlushPendingLinkUpdatesFromRunnable() {
8831   MOZ_ASSERT(mHasLinksToUpdateRunnable);
8832   mHasLinksToUpdateRunnable = false;
8833   FlushPendingLinkUpdates();
8834 }
8835 
FlushPendingLinkUpdates()8836 void nsIDocument::FlushPendingLinkUpdates() {
8837   if (mFlushingPendingLinkUpdates) {
8838     return;
8839   }
8840 
8841   auto restore = MakeScopeExit([&] { mFlushingPendingLinkUpdates = false; });
8842   mFlushingPendingLinkUpdates = true;
8843 
8844   while (!mLinksToUpdate.IsEmpty()) {
8845     LinksToUpdateList links(Move(mLinksToUpdate));
8846     for (auto iter = links.Iter(); !iter.Done(); iter.Next()) {
8847       Link* link = iter.Get();
8848       Element* element = link->GetElement();
8849       if (element->OwnerDoc() == this) {
8850         link->ClearHasPendingLinkUpdate();
8851         if (element->IsInComposedDoc()) {
8852           element->UpdateLinkState(link->LinkState());
8853         }
8854       }
8855     }
8856   }
8857 }
8858 
CreateStaticClone(nsIDocShell * aCloneContainer)8859 already_AddRefed<nsIDocument> nsIDocument::CreateStaticClone(
8860     nsIDocShell* aCloneContainer) {
8861   nsDocument* thisAsDoc = static_cast<nsDocument*>(this);
8862   mCreatingStaticClone = true;
8863 
8864   // Make document use different container during cloning.
8865   RefPtr<nsDocShell> originalShell = mDocumentContainer.get();
8866   SetContainer(static_cast<nsDocShell*>(aCloneContainer));
8867   ErrorResult rv;
8868   nsCOMPtr<nsINode> clonedNode = thisAsDoc->CloneNode(true, rv);
8869   SetContainer(originalShell);
8870 
8871   RefPtr<nsDocument> clonedDoc;
8872   if (rv.Failed()) {
8873     // Don't return yet; we need to reset mCreatingStaticClone
8874     rv.SuppressException();
8875   } else {
8876     nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode);
8877     if (tmp) {
8878       clonedDoc = static_cast<nsDocument*>(tmp.get());
8879       if (IsStaticDocument()) {
8880         clonedDoc->mOriginalDocument = mOriginalDocument;
8881       } else {
8882         clonedDoc->mOriginalDocument = this;
8883       }
8884 
8885       clonedDoc->mOriginalDocument->mStaticCloneCount++;
8886 
8887       MOZ_ASSERT(GetStyleBackendType() == clonedDoc->GetStyleBackendType());
8888       size_t sheetsCount = SheetCount();
8889       for (size_t i = 0; i < sheetsCount; ++i) {
8890         RefPtr<StyleSheet> sheet = SheetAt(i);
8891         if (sheet) {
8892           if (sheet->IsApplicable()) {
8893             RefPtr<StyleSheet> clonedSheet =
8894                 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
8895             NS_WARNING_ASSERTION(clonedSheet,
8896                                  "Cloning a stylesheet didn't work!");
8897             if (clonedSheet) {
8898               clonedDoc->AddStyleSheet(clonedSheet);
8899             }
8900           }
8901         }
8902       }
8903 
8904       // Iterate backwards to maintain order
8905       for (StyleSheet* sheet : Reversed(thisAsDoc->mOnDemandBuiltInUASheets)) {
8906         if (sheet) {
8907           if (sheet->IsApplicable()) {
8908             RefPtr<StyleSheet> clonedSheet =
8909                 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
8910             NS_WARNING_ASSERTION(clonedSheet,
8911                                  "Cloning a stylesheet didn't work!");
8912             if (clonedSheet) {
8913               clonedDoc->AddOnDemandBuiltInUASheet(clonedSheet);
8914             }
8915           }
8916         }
8917       }
8918     }
8919   }
8920   mCreatingStaticClone = false;
8921   return clonedDoc.forget();
8922 }
8923 
UnlinkOriginalDocumentIfStatic()8924 void nsIDocument::UnlinkOriginalDocumentIfStatic() {
8925   if (IsStaticDocument() && mOriginalDocument) {
8926     MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0);
8927     mOriginalDocument->mStaticCloneCount--;
8928     mOriginalDocument = nullptr;
8929   }
8930   MOZ_ASSERT(!mOriginalDocument);
8931 }
8932 
ScheduleFrameRequestCallback(FrameRequestCallback & aCallback,int32_t * aHandle)8933 nsresult nsIDocument::ScheduleFrameRequestCallback(
8934     FrameRequestCallback& aCallback, int32_t* aHandle) {
8935   if (mFrameRequestCallbackCounter == INT32_MAX) {
8936     // Can't increment without overflowing; bail out
8937     return NS_ERROR_NOT_AVAILABLE;
8938   }
8939   int32_t newHandle = ++mFrameRequestCallbackCounter;
8940 
8941   DebugOnly<FrameRequest*> request =
8942       mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
8943   NS_ASSERTION(request, "This is supposed to be infallible!");
8944   UpdateFrameRequestCallbackSchedulingState();
8945 
8946   *aHandle = newHandle;
8947   return NS_OK;
8948 }
8949 
CancelFrameRequestCallback(int32_t aHandle)8950 void nsIDocument::CancelFrameRequestCallback(int32_t aHandle) {
8951   // mFrameRequestCallbacks is stored sorted by handle
8952   if (mFrameRequestCallbacks.RemoveElementSorted(aHandle)) {
8953     UpdateFrameRequestCallbackSchedulingState();
8954   }
8955 }
8956 
GetStateObject(nsIVariant ** aState)8957 nsresult nsDocument::GetStateObject(nsIVariant** aState) {
8958   // Get the document's current state object. This is the object backing both
8959   // history.state and popStateEvent.state.
8960   //
8961   // mStateObjectContainer may be null; this just means that there's no
8962   // current state object.
8963 
8964   if (!mStateObjectCached && mStateObjectContainer) {
8965     AutoJSContext cx;
8966     nsIGlobalObject* sgo = GetScopeObject();
8967     NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
8968     JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
8969     NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
8970     JSAutoCompartment ac(cx, global);
8971 
8972     mStateObjectContainer->DeserializeToVariant(
8973         cx, getter_AddRefs(mStateObjectCached));
8974   }
8975 
8976   NS_IF_ADDREF(*aState = mStateObjectCached);
8977 
8978   return NS_OK;
8979 }
8980 
GetNavigationTiming() const8981 nsDOMNavigationTiming* nsDocument::GetNavigationTiming() const {
8982   return mTiming;
8983 }
8984 
SetNavigationTiming(nsDOMNavigationTiming * aTiming)8985 nsresult nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming) {
8986   mTiming = aTiming;
8987   if (!mLoadingTimeStamp.IsNull() && mTiming) {
8988     mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(),
8989                                     mLoadingTimeStamp);
8990   }
8991   return NS_OK;
8992 }
8993 
FindImageMap(const nsAString & aUseMapValue)8994 Element* nsDocument::FindImageMap(const nsAString& aUseMapValue) {
8995   if (aUseMapValue.IsEmpty()) {
8996     return nullptr;
8997   }
8998 
8999   nsAString::const_iterator start, end;
9000   aUseMapValue.BeginReading(start);
9001   aUseMapValue.EndReading(end);
9002 
9003   int32_t hash = aUseMapValue.FindChar('#');
9004   if (hash < 0) {
9005     return nullptr;
9006   }
9007   // aUsemap contains a '#', set start to point right after the '#'
9008   start.advance(hash + 1);
9009 
9010   if (start == end) {
9011     return nullptr;  // aUsemap == "#"
9012   }
9013 
9014   const nsAString& mapName = Substring(start, end);
9015 
9016   if (!mImageMaps) {
9017     mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map,
9018                                    nsGkAtoms::map);
9019   }
9020 
9021   uint32_t i, n = mImageMaps->Length(true);
9022   nsString name;
9023   for (i = 0; i < n; ++i) {
9024     nsIContent* map = mImageMaps->Item(i);
9025     if (map->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
9026                                       eCaseMatters) ||
9027         map->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
9028                                       mapName, eCaseMatters)) {
9029       return map->AsElement();
9030     }
9031   }
9032 
9033   return nullptr;
9034 }
9035 
9036 #define DEPRECATED_OPERATION(_op) #_op "Warning",
9037 static const char* kDeprecationWarnings[] = {
9038 #include "nsDeprecatedOperationList.h"
9039     nullptr};
9040 #undef DEPRECATED_OPERATION
9041 
9042 #define DOCUMENT_WARNING(_op) #_op "Warning",
9043 static const char* kDocumentWarnings[] = {
9044 #include "nsDocumentWarningList.h"
9045     nullptr};
9046 #undef DOCUMENT_WARNING
9047 
OperationToUseCounter(nsIDocument::DeprecatedOperations aOperation)9048 static UseCounter OperationToUseCounter(
9049     nsIDocument::DeprecatedOperations aOperation) {
9050   switch (aOperation) {
9051 #define DEPRECATED_OPERATION(_op) \
9052   case nsIDocument::e##_op:       \
9053     return eUseCounter_##_op;
9054 #include "nsDeprecatedOperationList.h"
9055 #undef DEPRECATED_OPERATION
9056     default:
9057       MOZ_CRASH();
9058   }
9059 }
9060 
HasWarnedAbout(DeprecatedOperations aOperation) const9061 bool nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const {
9062   return mDeprecationWarnedAbout[aOperation];
9063 }
9064 
WarnOnceAbout(DeprecatedOperations aOperation,bool asError) const9065 void nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
9066                                 bool asError /* = false */) const {
9067   MOZ_ASSERT(NS_IsMainThread());
9068   if (HasWarnedAbout(aOperation)) {
9069     return;
9070   }
9071   mDeprecationWarnedAbout[aOperation] = true;
9072   // Don't count deprecated operations for about pages since those pages
9073   // are almost in our control, and we always need to remove uses there
9074   // before we remove the operation itself anyway.
9075   if (!static_cast<const nsDocument*>(this)->IsAboutPage()) {
9076     const_cast<nsIDocument*>(this)->SetDocumentAndPageUseCounter(
9077         OperationToUseCounter(aOperation));
9078   }
9079   uint32_t flags =
9080       asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag;
9081   nsContentUtils::ReportToConsole(flags, NS_LITERAL_CSTRING("DOM Core"), this,
9082                                   nsContentUtils::eDOM_PROPERTIES,
9083                                   kDeprecationWarnings[aOperation]);
9084 }
9085 
HasWarnedAbout(DocumentWarnings aWarning) const9086 bool nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const {
9087   return mDocWarningWarnedAbout[aWarning];
9088 }
9089 
WarnOnceAbout(DocumentWarnings aWarning,bool asError,const char16_t ** aParams,uint32_t aParamsLength) const9090 void nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
9091                                 bool asError /* = false */,
9092                                 const char16_t** aParams /* = nullptr */,
9093                                 uint32_t aParamsLength /* = 0 */) const {
9094   MOZ_ASSERT(NS_IsMainThread());
9095   if (HasWarnedAbout(aWarning)) {
9096     return;
9097   }
9098   mDocWarningWarnedAbout[aWarning] = true;
9099   uint32_t flags =
9100       asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag;
9101   nsContentUtils::ReportToConsole(flags, NS_LITERAL_CSTRING("DOM Core"), this,
9102                                   nsContentUtils::eDOM_PROPERTIES,
9103                                   kDocumentWarnings[aWarning], aParams,
9104                                   aParamsLength);
9105 }
9106 
ImageTracker()9107 mozilla::dom::ImageTracker* nsIDocument::ImageTracker() {
9108   if (!mImageTracker) {
9109     mImageTracker = new mozilla::dom::ImageTracker;
9110   }
9111   return mImageTracker;
9112 }
9113 
AddPlugin(nsIObjectLoadingContent * aPlugin)9114 nsresult nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin) {
9115   MOZ_ASSERT(aPlugin);
9116   if (!mPlugins.PutEntry(aPlugin)) {
9117     return NS_ERROR_OUT_OF_MEMORY;
9118   }
9119   return NS_OK;
9120 }
9121 
RemovePlugin(nsIObjectLoadingContent * aPlugin)9122 void nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin) {
9123   MOZ_ASSERT(aPlugin);
9124   mPlugins.RemoveEntry(aPlugin);
9125 }
9126 
AllSubDocumentPluginEnum(nsIDocument * aDocument,void * userArg)9127 static bool AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg) {
9128   nsTArray<nsIObjectLoadingContent*>* plugins =
9129       reinterpret_cast<nsTArray<nsIObjectLoadingContent*>*>(userArg);
9130   MOZ_ASSERT(plugins);
9131   aDocument->GetPlugins(*plugins);
9132   return true;
9133 }
9134 
GetPlugins(nsTArray<nsIObjectLoadingContent * > & aPlugins)9135 void nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) {
9136   aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
9137   for (auto iter = mPlugins.ConstIter(); !iter.Done(); iter.Next()) {
9138     aPlugins.AppendElement(iter.Get()->GetKey());
9139   }
9140   EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
9141 }
9142 
AddResponsiveContent(nsIContent * aContent)9143 nsresult nsDocument::AddResponsiveContent(nsIContent* aContent) {
9144   MOZ_ASSERT(aContent);
9145   MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::img));
9146   mResponsiveContent.PutEntry(aContent);
9147   return NS_OK;
9148 }
9149 
RemoveResponsiveContent(nsIContent * aContent)9150 void nsDocument::RemoveResponsiveContent(nsIContent* aContent) {
9151   MOZ_ASSERT(aContent);
9152   mResponsiveContent.RemoveEntry(aContent);
9153 }
9154 
NotifyMediaFeatureValuesChanged()9155 void nsDocument::NotifyMediaFeatureValuesChanged() {
9156   for (auto iter = mResponsiveContent.ConstIter(); !iter.Done(); iter.Next()) {
9157     nsCOMPtr<nsIContent> content = iter.Get()->GetKey();
9158     if (content->IsHTMLElement(nsGkAtoms::img)) {
9159       auto* imageElement = static_cast<HTMLImageElement*>(content.get());
9160       imageElement->MediaFeatureValuesChanged();
9161     }
9162   }
9163 }
9164 
CreateTouch(nsGlobalWindowInner * aView,EventTarget * aTarget,int32_t aIdentifier,int32_t aPageX,int32_t aPageY,int32_t aScreenX,int32_t aScreenY,int32_t aClientX,int32_t aClientY,int32_t aRadiusX,int32_t aRadiusY,float aRotationAngle,float aForce)9165 already_AddRefed<Touch> nsIDocument::CreateTouch(
9166     nsGlobalWindowInner* aView, EventTarget* aTarget, int32_t aIdentifier,
9167     int32_t aPageX, int32_t aPageY, int32_t aScreenX, int32_t aScreenY,
9168     int32_t aClientX, int32_t aClientY, int32_t aRadiusX, int32_t aRadiusY,
9169     float aRotationAngle, float aForce) {
9170   RefPtr<Touch> touch =
9171       new Touch(aTarget, aIdentifier, aPageX, aPageY, aScreenX, aScreenY,
9172                 aClientX, aClientY, aRadiusX, aRadiusY, aRotationAngle, aForce);
9173   return touch.forget();
9174 }
9175 
CreateTouchList()9176 already_AddRefed<TouchList> nsIDocument::CreateTouchList() {
9177   RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9178   return retval.forget();
9179 }
9180 
CreateTouchList(Touch & aTouch,const Sequence<OwningNonNull<Touch>> & aTouches)9181 already_AddRefed<TouchList> nsIDocument::CreateTouchList(
9182     Touch& aTouch, const Sequence<OwningNonNull<Touch>>& aTouches) {
9183   RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9184   retval->Append(&aTouch);
9185   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
9186     retval->Append(aTouches[i].get());
9187   }
9188   return retval.forget();
9189 }
9190 
CreateTouchList(const Sequence<OwningNonNull<Touch>> & aTouches)9191 already_AddRefed<TouchList> nsIDocument::CreateTouchList(
9192     const Sequence<OwningNonNull<Touch>>& aTouches) {
9193   RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9194   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
9195     retval->Append(aTouches[i].get());
9196   }
9197   return retval.forget();
9198 }
9199 
CaretPositionFromPoint(float aX,float aY)9200 already_AddRefed<nsDOMCaretPosition> nsIDocument::CaretPositionFromPoint(
9201     float aX, float aY) {
9202   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
9203   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
9204   nsPoint pt(x, y);
9205 
9206   FlushPendingNotifications(FlushType::Layout);
9207 
9208   nsIPresShell* ps = GetShell();
9209   if (!ps) {
9210     return nullptr;
9211   }
9212 
9213   nsIFrame* rootFrame = ps->GetRootFrame();
9214 
9215   // XUL docs, unlike HTML, have no frame tree until everything's done loading
9216   if (!rootFrame) {
9217     return nullptr;
9218   }
9219 
9220   nsIFrame* ptFrame =
9221       nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
9222                                       nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
9223                                           nsLayoutUtils::IGNORE_CROSS_DOC);
9224   if (!ptFrame) {
9225     return nullptr;
9226   }
9227 
9228   // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
9229   // to adjust to frame-relative coordinates before we can perform this call.
9230   // It should also not take into account the padding of the frame.
9231   nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
9232 
9233   nsFrame::ContentOffsets offsets =
9234       ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
9235 
9236   nsCOMPtr<nsIContent> node = offsets.content;
9237   uint32_t offset = offsets.offset;
9238   nsCOMPtr<nsIContent> anonNode = node;
9239   bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
9240   if (nodeIsAnonymous) {
9241     node = ptFrame->GetContent();
9242     nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
9243     HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromContent(nonanon);
9244     nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
9245     nsNumberControlFrame* numberFrame =
9246         do_QueryFrame(nonanon->GetPrimaryFrame());
9247     if (textFrame || numberFrame) {
9248       // If the anonymous content node has a child, then we need to make sure
9249       // that we get the appropriate child, as otherwise the offset may not be
9250       // correct when we construct a range for it.
9251       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
9252       if (firstChild) {
9253         anonNode = firstChild;
9254       }
9255 
9256       if (textArea) {
9257         offset =
9258             nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
9259       }
9260 
9261       node = nonanon;
9262     } else {
9263       node = nullptr;
9264       offset = 0;
9265     }
9266   }
9267 
9268   RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
9269   if (nodeIsAnonymous) {
9270     aCaretPos->SetAnonymousContentNode(anonNode);
9271   }
9272   return aCaretPos.forget();
9273 }
9274 
IsPotentiallyScrollable(HTMLBodyElement * aBody)9275 bool nsIDocument::IsPotentiallyScrollable(HTMLBodyElement* aBody) {
9276   // We rely on correct frame information here, so need to flush frames.
9277   FlushPendingNotifications(FlushType::Frames);
9278 
9279   // An element is potentially scrollable if all of the following conditions are
9280   // true:
9281 
9282   // The element has an associated CSS layout box.
9283   nsIFrame* bodyFrame = aBody->GetPrimaryFrame();
9284   if (!bodyFrame) {
9285     return false;
9286   }
9287 
9288   // The element is not the HTML body element, or it is and the root element's
9289   // used value of the overflow-x or overflow-y properties is not visible.
9290   MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement());
9291   nsIFrame* parentFrame = aBody->GetParent()->GetPrimaryFrame();
9292   if (parentFrame &&
9293       parentFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
9294       parentFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
9295     return false;
9296   }
9297 
9298   // The element's used value of the overflow-x or overflow-y properties is not
9299   // visible.
9300   if (bodyFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
9301       bodyFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
9302     return false;
9303   }
9304 
9305   return true;
9306 }
9307 
GetScrollingElement()9308 Element* nsIDocument::GetScrollingElement() {
9309   // Keep this in sync with IsScrollingElement.
9310   if (GetCompatibilityMode() == eCompatibility_NavQuirks) {
9311     RefPtr<HTMLBodyElement> body = GetBodyElement();
9312     if (body && !IsPotentiallyScrollable(body)) {
9313       return body;
9314     }
9315 
9316     return nullptr;
9317   }
9318 
9319   return GetRootElement();
9320 }
9321 
IsScrollingElement(Element * aElement)9322 bool nsIDocument::IsScrollingElement(Element* aElement) {
9323   // Keep this in sync with GetScrollingElement.
9324   MOZ_ASSERT(aElement);
9325 
9326   if (GetCompatibilityMode() != eCompatibility_NavQuirks) {
9327     return aElement == GetRootElement();
9328   }
9329 
9330   // In the common case when aElement != body, avoid refcounting.
9331   HTMLBodyElement* body = GetBodyElement();
9332   if (aElement != body) {
9333     return false;
9334   }
9335 
9336   // Now we know body is non-null, since aElement is not null.  It's the
9337   // scrolling element for the document if it itself is not potentially
9338   // scrollable.
9339   RefPtr<HTMLBodyElement> strongBody(body);
9340   return !IsPotentiallyScrollable(strongBody);
9341 }
9342 
ObsoleteSheet(nsIURI * aSheetURI,ErrorResult & rv)9343 void nsIDocument::ObsoleteSheet(nsIURI* aSheetURI, ErrorResult& rv) {
9344   nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
9345   if (NS_FAILED(res)) {
9346     rv.Throw(res);
9347   }
9348 }
9349 
ObsoleteSheet(const nsAString & aSheetURI,ErrorResult & rv)9350 void nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv) {
9351   nsCOMPtr<nsIURI> uri;
9352   nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
9353   if (NS_FAILED(res)) {
9354     rv.Throw(res);
9355     return;
9356   }
9357   res = CSSLoader()->ObsoleteSheet(uri);
9358   if (NS_FAILED(res)) {
9359     rv.Throw(res);
9360   }
9361 }
9362 
9363 class UnblockParsingPromiseHandler final : public PromiseNativeHandler {
9364  public:
9365   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)9366   NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
9367 
9368   explicit UnblockParsingPromiseHandler(nsIDocument* aDocument,
9369                                         Promise* aPromise,
9370                                         const BlockParsingOptions& aOptions)
9371       : mPromise(aPromise) {
9372     nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
9373     if (parser &&
9374         (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) {
9375       parser->BlockParser();
9376       mParser = do_GetWeakReference(parser);
9377       mDocument = aDocument;
9378       mDocument->BlockOnload();
9379     }
9380   }
9381 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)9382   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
9383     MaybeUnblockParser();
9384 
9385     mPromise->MaybeResolve(aCx, aValue);
9386   }
9387 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)9388   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
9389     MaybeUnblockParser();
9390 
9391     mPromise->MaybeReject(aCx, aValue);
9392   }
9393 
9394  protected:
~UnblockParsingPromiseHandler()9395   virtual ~UnblockParsingPromiseHandler() {
9396     // If we're being cleaned up by the cycle collector, our mDocument reference
9397     // may have been unlinked while our mParser weak reference is still alive.
9398     if (mDocument) {
9399       MaybeUnblockParser();
9400     }
9401   }
9402 
9403  private:
MaybeUnblockParser()9404   void MaybeUnblockParser() {
9405     nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
9406     if (parser) {
9407       MOZ_DIAGNOSTIC_ASSERT(mDocument);
9408       nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
9409       if (parser == docParser) {
9410         parser->UnblockParser();
9411         parser->ContinueInterruptedParsingAsync();
9412         mDocument->UnblockOnload(false);
9413       }
9414     }
9415     mParser = nullptr;
9416     mDocument = nullptr;
9417   }
9418 
9419   nsWeakPtr mParser;
9420   RefPtr<Promise> mPromise;
9421   RefPtr<nsIDocument> mDocument;
9422 };
9423 
NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler,mDocument,mPromise)9424 NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
9425 
9426 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
9427   NS_INTERFACE_MAP_ENTRY(nsISupports)
9428 NS_INTERFACE_MAP_END
9429 
9430 NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler)
9431 NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler)
9432 
9433 already_AddRefed<Promise> nsIDocument::BlockParsing(
9434     Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv) {
9435   RefPtr<Promise> resultPromise =
9436       Promise::Create(aPromise.GetParentObject(), aRv);
9437   if (aRv.Failed()) {
9438     return nullptr;
9439   }
9440 
9441   RefPtr<PromiseNativeHandler> promiseHandler =
9442       new UnblockParsingPromiseHandler(this, resultPromise, aOptions);
9443   aPromise.AppendNativeHandler(promiseHandler);
9444 
9445   return resultPromise.forget();
9446 }
9447 
GetMozDocumentURIIfNotForErrorPages()9448 already_AddRefed<nsIURI> nsIDocument::GetMozDocumentURIIfNotForErrorPages() {
9449   if (mFailedChannel) {
9450     nsCOMPtr<nsIURI> failedURI;
9451     if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
9452       return failedURI.forget();
9453     }
9454   }
9455 
9456   nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
9457   if (!uri) {
9458     return nullptr;
9459   }
9460 
9461   return uri.forget();
9462 }
9463 
GetDocumentReadyForIdle(ErrorResult & aRv)9464 Promise* nsIDocument::GetDocumentReadyForIdle(ErrorResult& aRv) {
9465   if (!mReadyForIdle) {
9466     nsIGlobalObject* global = GetScopeObject();
9467     if (!global) {
9468       aRv.Throw(NS_ERROR_NOT_AVAILABLE);
9469       return nullptr;
9470     }
9471 
9472     mReadyForIdle = Promise::Create(global, aRv);
9473     if (aRv.Failed()) {
9474       return nullptr;
9475     }
9476   }
9477 
9478   return mReadyForIdle;
9479 }
9480 
MaybeResolveReadyForIdle()9481 void nsIDocument::MaybeResolveReadyForIdle() {
9482   IgnoredErrorResult rv;
9483   Promise* readyPromise = GetDocumentReadyForIdle(rv);
9484   if (readyPromise) {
9485     readyPromise->MaybeResolve(this);
9486   }
9487 }
9488 
Children()9489 nsIHTMLCollection* nsIDocument::Children() {
9490   if (!mChildrenCollection) {
9491     mChildrenCollection =
9492         new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
9493                           nsGkAtoms::_asterisk, false);
9494   }
9495 
9496   return mChildrenCollection;
9497 }
9498 
ChildElementCount()9499 uint32_t nsIDocument::ChildElementCount() { return Children()->Length(); }
9500 
9501 namespace mozilla {
9502 
9503 // Singleton class to manage the list of fullscreen documents which are the
9504 // root of a branch which contains fullscreen documents. We maintain this list
9505 // so that we can easily exit all windows from fullscreen when the user
9506 // presses the escape key.
9507 class FullscreenRoots {
9508  public:
9509   // Adds the root of given document to the manager. Calling this method
9510   // with a document whose root is already contained has no effect.
9511   static void Add(nsIDocument* aDoc);
9512 
9513   // Iterates over every root in the root list, and calls aFunction, passing
9514   // each root once to aFunction. It is safe to call Add() and Remove() while
9515   // iterating over the list (i.e. in aFunction). Documents that are removed
9516   // from the manager during traversal are not traversed, and documents that
9517   // are added to the manager during traversal are also not traversed.
9518   static void ForEach(void (*aFunction)(nsIDocument* aDoc));
9519 
9520   // Removes the root of a specific document from the manager.
9521   static void Remove(nsIDocument* aDoc);
9522 
9523   // Returns true if all roots added to the list have been removed.
9524   static bool IsEmpty();
9525 
9526  private:
FullscreenRoots()9527   FullscreenRoots() { MOZ_COUNT_CTOR(FullscreenRoots); }
~FullscreenRoots()9528   ~FullscreenRoots() { MOZ_COUNT_DTOR(FullscreenRoots); }
9529 
9530   enum { NotFound = uint32_t(-1) };
9531   // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
9532   static uint32_t Find(nsIDocument* aRoot);
9533 
9534   // Returns true if aRoot is in the list of fullscreen roots.
9535   static bool Contains(nsIDocument* aRoot);
9536 
9537   // Singleton instance of the FullscreenRoots. This is instantiated when a
9538   // root is added, and it is deleted when the last root is removed.
9539   static FullscreenRoots* sInstance;
9540 
9541   // List of weak pointers to roots.
9542   nsTArray<nsWeakPtr> mRoots;
9543 };
9544 
9545 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
9546 
9547 /* static */
ForEach(void (* aFunction)(nsIDocument * aDoc))9548 void FullscreenRoots::ForEach(void (*aFunction)(nsIDocument* aDoc)) {
9549   if (!sInstance) {
9550     return;
9551   }
9552   // Create a copy of the roots array, and iterate over the copy. This is so
9553   // that if an element is removed from mRoots we don't mess up our iteration.
9554   nsTArray<nsWeakPtr> roots(sInstance->mRoots);
9555   // Call aFunction on all entries.
9556   for (uint32_t i = 0; i < roots.Length(); i++) {
9557     nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
9558     // Check that the root isn't in the manager. This is so that new additions
9559     // while we were running don't get traversed.
9560     if (root && FullscreenRoots::Contains(root)) {
9561       aFunction(root);
9562     }
9563   }
9564 }
9565 
9566 /* static */
Contains(nsIDocument * aRoot)9567 bool FullscreenRoots::Contains(nsIDocument* aRoot) {
9568   return FullscreenRoots::Find(aRoot) != NotFound;
9569 }
9570 
9571 /* static */
Add(nsIDocument * aDoc)9572 void FullscreenRoots::Add(nsIDocument* aDoc) {
9573   nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
9574   if (!FullscreenRoots::Contains(root)) {
9575     if (!sInstance) {
9576       sInstance = new FullscreenRoots();
9577     }
9578     sInstance->mRoots.AppendElement(do_GetWeakReference(root));
9579   }
9580 }
9581 
9582 /* static */
Find(nsIDocument * aRoot)9583 uint32_t FullscreenRoots::Find(nsIDocument* aRoot) {
9584   if (!sInstance) {
9585     return NotFound;
9586   }
9587   nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
9588   for (uint32_t i = 0; i < roots.Length(); i++) {
9589     nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
9590     if (otherRoot == aRoot) {
9591       return i;
9592     }
9593   }
9594   return NotFound;
9595 }
9596 
9597 /* static */
Remove(nsIDocument * aDoc)9598 void FullscreenRoots::Remove(nsIDocument* aDoc) {
9599   nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
9600   uint32_t index = Find(root);
9601   NS_ASSERTION(index != NotFound,
9602                "Should only try to remove roots which are still added!");
9603   if (index == NotFound || !sInstance) {
9604     return;
9605   }
9606   sInstance->mRoots.RemoveElementAt(index);
9607   if (sInstance->mRoots.IsEmpty()) {
9608     delete sInstance;
9609     sInstance = nullptr;
9610   }
9611 }
9612 
9613 /* static */
IsEmpty()9614 bool FullscreenRoots::IsEmpty() { return !sInstance; }
9615 
9616 }  // end namespace mozilla.
9617 using mozilla::FullscreenRoots;
9618 
GetFullscreenRoot()9619 nsIDocument* nsDocument::GetFullscreenRoot() {
9620   nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
9621   return root;
9622 }
9623 
SetFullscreenRoot(nsIDocument * aRoot)9624 void nsDocument::SetFullscreenRoot(nsIDocument* aRoot) {
9625   mFullscreenRoot = do_GetWeakReference(aRoot);
9626 }
9627 
ExitFullscreen()9628 void nsIDocument::ExitFullscreen() { RestorePreviousFullScreenState(); }
9629 
AskWindowToExitFullscreen(nsIDocument * aDoc)9630 static void AskWindowToExitFullscreen(nsIDocument* aDoc) {
9631   if (XRE_GetProcessType() == GeckoProcessType_Content) {
9632     nsContentUtils::DispatchEventOnlyToChrome(
9633         aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
9634         /* Bubbles */ true, /* Cancelable */ false,
9635         /* DefaultAction */ nullptr);
9636   } else {
9637     if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
9638       win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false);
9639     }
9640   }
9641 }
9642 
9643 class nsCallExitFullscreen : public Runnable {
9644  public:
nsCallExitFullscreen(nsIDocument * aDoc)9645   explicit nsCallExitFullscreen(nsIDocument* aDoc)
9646       : mozilla::Runnable("nsCallExitFullscreen"), mDoc(aDoc) {}
9647 
Run()9648   NS_IMETHOD Run() final {
9649     if (!mDoc) {
9650       FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
9651     } else {
9652       AskWindowToExitFullscreen(mDoc);
9653     }
9654     return NS_OK;
9655   }
9656 
9657  private:
9658   nsCOMPtr<nsIDocument> mDoc;
9659 };
9660 
AsyncExitFullscreen(nsIDocument * aDoc)9661 /* static */ void nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc) {
9662   MOZ_RELEASE_ASSERT(NS_IsMainThread());
9663   nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
9664   if (aDoc) {
9665     aDoc->Dispatch(TaskCategory::Other, exit.forget());
9666   } else {
9667     NS_DispatchToCurrentThread(exit.forget());
9668   }
9669 }
9670 
CountFullscreenSubDocuments(nsIDocument * aDoc,void * aData)9671 static bool CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData) {
9672   if (aDoc->FullScreenStackTop()) {
9673     uint32_t* count = static_cast<uint32_t*>(aData);
9674     (*count)++;
9675   }
9676   return true;
9677 }
9678 
CountFullscreenSubDocuments(nsIDocument * aDoc)9679 static uint32_t CountFullscreenSubDocuments(nsIDocument* aDoc) {
9680   uint32_t count = 0;
9681   aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
9682   return count;
9683 }
9684 
IsFullscreenLeaf()9685 bool nsDocument::IsFullscreenLeaf() {
9686   // A fullscreen leaf document is fullscreen, and has no fullscreen
9687   // subdocuments.
9688   if (!FullScreenStackTop()) {
9689     return false;
9690   }
9691   return CountFullscreenSubDocuments(this) == 0;
9692 }
9693 
ResetFullScreen(nsIDocument * aDocument,void * aData)9694 static bool ResetFullScreen(nsIDocument* aDocument, void* aData) {
9695   if (aDocument->FullScreenStackTop()) {
9696     NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
9697                  "Should have at most 1 fullscreen subdocument.");
9698     static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
9699     NS_ASSERTION(!aDocument->FullScreenStackTop(), "Should reset full-screen");
9700     auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
9701     changed->AppendElement(aDocument);
9702     aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
9703   }
9704   return true;
9705 }
9706 
9707 // Since nsIDocument::ExitFullscreenInDocTree() could be called from
9708 // Element::UnbindFromTree() where it is not safe to synchronously run
9709 // script. This runnable is the script part of that function.
9710 class ExitFullscreenScriptRunnable : public Runnable {
9711  public:
ExitFullscreenScriptRunnable(nsCOMArray<nsIDocument> && aDocuments)9712   explicit ExitFullscreenScriptRunnable(nsCOMArray<nsIDocument>&& aDocuments)
9713       : mozilla::Runnable("ExitFullscreenScriptRunnable"),
9714         mDocuments(Move(aDocuments)) {}
9715 
Run()9716   NS_IMETHOD Run() override {
9717     // Dispatch MozDOMFullscreen:Exited to the last document in
9718     // the list since we want this event to follow the same path
9719     // MozDOMFullscreen:Entered dispatched.
9720     nsIDocument* lastDocument = mDocuments[mDocuments.Length() - 1];
9721     nsContentUtils::DispatchEventOnlyToChrome(
9722         lastDocument, ToSupports(lastDocument),
9723         NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
9724         /* Bubbles */ true, /* Cancelable */ false,
9725         /* DefaultAction */ nullptr);
9726     // Ensure the window exits fullscreen.
9727     if (nsPIDOMWindowOuter* win = mDocuments[0]->GetWindow()) {
9728       win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen,
9729                                  false);
9730     }
9731     return NS_OK;
9732   }
9733 
9734  private:
9735   nsCOMArray<nsIDocument> mDocuments;
9736 };
9737 
ExitFullscreenInDocTree(nsIDocument * aMaybeNotARootDoc)9738 /* static */ void nsIDocument::ExitFullscreenInDocTree(
9739     nsIDocument* aMaybeNotARootDoc) {
9740   MOZ_ASSERT(aMaybeNotARootDoc);
9741 
9742   // Unlock the pointer
9743   UnlockPointer();
9744 
9745   nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
9746   if (!root || !root->FullScreenStackTop()) {
9747     // If a document was detached before exiting from fullscreen, it is
9748     // possible that the root had left fullscreen state. In this case,
9749     // we would not get anything from the ResetFullScreen() call. Root's
9750     // not being a fullscreen doc also means the widget should have
9751     // exited fullscreen state. It means even if we do not return here,
9752     // we would actually do nothing below except crashing ourselves via
9753     // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
9754     // document.
9755     return;
9756   }
9757 
9758   // Stores a list of documents to which we must dispatch "fullscreenchange".
9759   // We're required by the spec to dispatch the events in leaf-to-root
9760   // order when exiting fullscreen, but we traverse the doctree in a
9761   // root-to-leaf order, so we save references to the documents we must
9762   // dispatch to so that we dispatch in the specified order.
9763   nsCOMArray<nsIDocument> changed;
9764 
9765   // Walk the tree of fullscreen documents, and reset their fullscreen state.
9766   ResetFullScreen(root, static_cast<void*>(&changed));
9767 
9768   // Dispatch "fullscreenchange" events. Note this loop is in reverse
9769   // order so that the events for the leaf document arrives before the root
9770   // document, as required by the spec.
9771   for (uint32_t i = 0; i < changed.Length(); ++i) {
9772     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
9773   }
9774 
9775   NS_ASSERTION(!root->FullScreenStackTop(),
9776                "Fullscreen root should no longer be a fullscreen doc...");
9777 
9778   // Move the top-level window out of fullscreen mode.
9779   FullscreenRoots::Remove(root);
9780 
9781   nsContentUtils::AddScriptRunner(
9782       new ExitFullscreenScriptRunnable(Move(changed)));
9783 }
9784 
GetFullscreenLeaf(nsIDocument * aDoc,void * aData)9785 bool GetFullscreenLeaf(nsIDocument* aDoc, void* aData) {
9786   if (aDoc->IsFullscreenLeaf()) {
9787     nsIDocument** result = static_cast<nsIDocument**>(aData);
9788     *result = aDoc;
9789     return false;
9790   } else if (aDoc->FullScreenStackTop()) {
9791     aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
9792   }
9793   return true;
9794 }
9795 
GetFullscreenLeaf(nsIDocument * aDoc)9796 static nsIDocument* GetFullscreenLeaf(nsIDocument* aDoc) {
9797   nsIDocument* leaf = nullptr;
9798   GetFullscreenLeaf(aDoc, &leaf);
9799   if (leaf) {
9800     return leaf;
9801   }
9802   // Otherwise we could be either in a non-fullscreen doc tree, or we're
9803   // below the fullscreen doc. Start the search from the root.
9804   nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
9805   // Check that the root is actually fullscreen so we don't waste time walking
9806   // around its descendants.
9807   if (!root->FullScreenStackTop()) {
9808     return nullptr;
9809   }
9810   GetFullscreenLeaf(root, &leaf);
9811   return leaf;
9812 }
9813 
RestorePreviousFullScreenState()9814 void nsDocument::RestorePreviousFullScreenState() {
9815   NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
9816                "Should have at least 1 fullscreen root when fullscreen!");
9817 
9818   if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
9819     return;
9820   }
9821 
9822   nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
9823   AutoTArray<nsDocument*, 8> exitDocs;
9824 
9825   nsIDocument* doc = fullScreenDoc;
9826   // Collect all subdocuments.
9827   for (; doc != this; doc = doc->GetParentDocument()) {
9828     exitDocs.AppendElement(static_cast<nsDocument*>(doc));
9829   }
9830   MOZ_ASSERT(doc == this, "Must have reached this doc");
9831   // Collect all ancestor documents which we are going to change.
9832   for (; doc; doc = doc->GetParentDocument()) {
9833     nsDocument* theDoc = static_cast<nsDocument*>(doc);
9834     MOZ_ASSERT(!theDoc->mFullScreenStack.IsEmpty(),
9835                "Ancestor of fullscreen document must also be in fullscreen");
9836     if (doc != this) {
9837       Element* top = theDoc->FullScreenStackTop();
9838       if (top->IsHTMLElement(nsGkAtoms::iframe)) {
9839         if (static_cast<HTMLIFrameElement*>(top)->FullscreenFlag()) {
9840           // If this is an iframe, and it explicitly requested
9841           // fullscreen, don't rollback it automatically.
9842           break;
9843         }
9844       }
9845     }
9846     exitDocs.AppendElement(theDoc);
9847     if (theDoc->mFullScreenStack.Length() > 1) {
9848       break;
9849     }
9850   }
9851 
9852   nsDocument* lastDoc = exitDocs.LastElement();
9853   if (!lastDoc->GetParentDocument() &&
9854       lastDoc->mFullScreenStack.Length() == 1) {
9855     // If we are fully exiting fullscreen, don't touch anything here,
9856     // just wait for the window to get out from fullscreen first.
9857     AskWindowToExitFullscreen(this);
9858     return;
9859   }
9860 
9861   // If fullscreen mode is updated the pointer should be unlocked
9862   UnlockPointer();
9863   // All documents listed in the array except the last one are going to
9864   // completely exit from the fullscreen state.
9865   for (auto i : IntegerRange(exitDocs.Length() - 1)) {
9866     exitDocs[i]->CleanupFullscreenState();
9867   }
9868   // The last document will either rollback one fullscreen element, or
9869   // completely exit from the fullscreen state as well.
9870   nsIDocument* newFullscreenDoc;
9871   if (lastDoc->mFullScreenStack.Length() > 1) {
9872     lastDoc->FullScreenStackPop();
9873     newFullscreenDoc = lastDoc;
9874   } else {
9875     lastDoc->CleanupFullscreenState();
9876     newFullscreenDoc = lastDoc->GetParentDocument();
9877   }
9878   // Dispatch the fullscreenchange event to all document listed.
9879   for (nsDocument* d : exitDocs) {
9880     DispatchFullScreenChange(d);
9881   }
9882 
9883   MOZ_ASSERT(newFullscreenDoc,
9884              "If we were going to exit from fullscreen on "
9885              "all documents in this doctree, we should've asked the window to "
9886              "exit first instead of reaching here.");
9887   if (fullScreenDoc != newFullscreenDoc &&
9888       !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) {
9889     // We've popped so enough off the stack that we've rolled back to
9890     // a fullscreen element in a parent document. If this document is
9891     // cross origin, dispatch an event to chrome so it knows to show
9892     // the warning UI.
9893     DispatchCustomEventWithFlush(
9894         newFullscreenDoc, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
9895         /* Bubbles */ true, /* ChromeOnly */ true);
9896   }
9897 }
9898 
9899 class nsCallRequestFullScreen : public Runnable {
9900  public:
nsCallRequestFullScreen(UniquePtr<FullscreenRequest> && aRequest)9901   explicit nsCallRequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
9902       : mozilla::Runnable("nsCallRequestFullScreen"),
9903         mRequest(Move(aRequest)) {}
9904 
Run()9905   NS_IMETHOD Run() override {
9906     mRequest->GetDocument()->RequestFullScreen(Move(mRequest));
9907     return NS_OK;
9908   }
9909 
9910   UniquePtr<FullscreenRequest> mRequest;
9911 };
9912 
AsyncRequestFullScreen(UniquePtr<FullscreenRequest> && aRequest)9913 void nsDocument::AsyncRequestFullScreen(
9914     UniquePtr<FullscreenRequest>&& aRequest) {
9915   if (!aRequest->GetElement()) {
9916     MOZ_ASSERT_UNREACHABLE(
9917         "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
9918     return;
9919   }
9920 
9921   // Request full-screen asynchronously.
9922   MOZ_RELEASE_ASSERT(NS_IsMainThread());
9923   nsCOMPtr<nsIRunnable> event = new nsCallRequestFullScreen(Move(aRequest));
9924   Dispatch(TaskCategory::Other, event.forget());
9925 }
9926 
DispatchFullscreenError(const char * aMessage)9927 void nsIDocument::DispatchFullscreenError(const char* aMessage) {
9928   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
9929       this, NS_LITERAL_STRING("fullscreenerror"), true, false);
9930   asyncDispatcher->PostDOMEvent();
9931   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
9932                                   NS_LITERAL_CSTRING("DOM"), this,
9933                                   nsContentUtils::eDOM_PROPERTIES, aMessage);
9934 }
9935 
UpdateViewportScrollbarOverrideForFullscreen(nsIDocument * aDoc)9936 static void UpdateViewportScrollbarOverrideForFullscreen(nsIDocument* aDoc) {
9937   if (nsPresContext* presContext = aDoc->GetPresContext()) {
9938     presContext->UpdateViewportScrollbarStylesOverride();
9939   }
9940 }
9941 
ClearFullscreenStateOnElement(Element * aElement)9942 static void ClearFullscreenStateOnElement(Element* aElement) {
9943   // Remove styles from existing top element.
9944   EventStateManager::SetFullScreenState(aElement, false);
9945   // Reset iframe fullscreen flag.
9946   if (aElement->IsHTMLElement(nsGkAtoms::iframe)) {
9947     static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false);
9948   }
9949 }
9950 
CleanupFullscreenState()9951 void nsDocument::CleanupFullscreenState() {
9952   // Iterate the fullscreen stack and clear the fullscreen states.
9953   // Since we also need to clear the fullscreen-ancestor state, and
9954   // currently fullscreen elements can only be placed in hierarchy
9955   // order in the stack, reversely iterating the stack could be more
9956   // efficient. NOTE that fullscreen-ancestor state would be removed
9957   // in bug 1199529, and the elements may not in hierarchy order
9958   // after bug 1195213.
9959   for (nsWeakPtr& weakPtr : Reversed(mFullScreenStack)) {
9960     if (nsCOMPtr<Element> element = do_QueryReferent(weakPtr)) {
9961       ClearFullscreenStateOnElement(element);
9962     }
9963   }
9964   mFullScreenStack.Clear();
9965   mFullscreenRoot = nullptr;
9966   UpdateViewportScrollbarOverrideForFullscreen(this);
9967 }
9968 
FullScreenStackPush(Element * aElement)9969 bool nsDocument::FullScreenStackPush(Element* aElement) {
9970   NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
9971   Element* top = FullScreenStackTop();
9972   if (top == aElement || !aElement) {
9973     return false;
9974   }
9975   EventStateManager::SetFullScreenState(aElement, true);
9976   mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
9977   NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
9978   UpdateViewportScrollbarOverrideForFullscreen(this);
9979   return true;
9980 }
9981 
FullScreenStackPop()9982 void nsDocument::FullScreenStackPop() {
9983   if (mFullScreenStack.IsEmpty()) {
9984     return;
9985   }
9986 
9987   ClearFullscreenStateOnElement(FullScreenStackTop());
9988 
9989   // Remove top element. Note the remaining top element in the stack
9990   // will not have full-screen style bits set, so we will need to restore
9991   // them on the new top element before returning.
9992   uint32_t last = mFullScreenStack.Length() - 1;
9993   mFullScreenStack.RemoveElementAt(last);
9994 
9995   // Pop from the stack null elements (references to elements which have
9996   // been GC'd since they were added to the stack) and elements which are
9997   // no longer in this document.
9998   while (!mFullScreenStack.IsEmpty()) {
9999     Element* element = FullScreenStackTop();
10000     if (!element || !element->IsInUncomposedDoc() ||
10001         element->OwnerDoc() != this) {
10002       NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
10003                    "Should have already removed full-screen styles");
10004       uint32_t last = mFullScreenStack.Length() - 1;
10005       mFullScreenStack.RemoveElementAt(last);
10006     } else {
10007       // The top element of the stack is now an in-doc element. Return here.
10008       break;
10009     }
10010   }
10011 
10012   UpdateViewportScrollbarOverrideForFullscreen(this);
10013 }
10014 
FullScreenStackTop()10015 Element* nsDocument::FullScreenStackTop() {
10016   if (mFullScreenStack.IsEmpty()) {
10017     return nullptr;
10018   }
10019   uint32_t last = mFullScreenStack.Length() - 1;
10020   nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
10021   NS_ASSERTION(element, "Should have full-screen element!");
10022   NS_ASSERTION(element->IsInComposedDoc(),
10023                "Full-screen element should be in doc");
10024   NS_ASSERTION(element->OwnerDoc() == this,
10025                "Full-screen element should be in this doc");
10026   return element;
10027 }
10028 
GetFullscreenStack() const10029 /* virtual */ nsTArray<Element*> nsDocument::GetFullscreenStack() const {
10030   nsTArray<Element*> elements;
10031   for (const nsWeakPtr& ptr : mFullScreenStack) {
10032     if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
10033       MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN));
10034       elements.AppendElement(elem);
10035     }
10036   }
10037   return elements;
10038 }
10039 
10040 // Returns true if aDoc is in the focused tab in the active window.
IsInActiveTab(nsIDocument * aDoc)10041 static bool IsInActiveTab(nsIDocument* aDoc) {
10042   nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
10043   if (!docshell) {
10044     return false;
10045   }
10046 
10047   bool isActive = false;
10048   docshell->GetIsActive(&isActive);
10049   if (!isActive) {
10050     return false;
10051   }
10052 
10053   nsCOMPtr<nsIDocShellTreeItem> rootItem;
10054   docshell->GetRootTreeItem(getter_AddRefs(rootItem));
10055   if (!rootItem) {
10056     return false;
10057   }
10058   nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
10059   if (!rootWin) {
10060     return false;
10061   }
10062 
10063   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
10064   if (!fm) {
10065     return false;
10066   }
10067 
10068   nsCOMPtr<mozIDOMWindowProxy> activeWindow;
10069   fm->GetActiveWindow(getter_AddRefs(activeWindow));
10070   if (!activeWindow) {
10071     return false;
10072   }
10073 
10074   return activeWindow == rootWin;
10075 }
10076 
RemoteFrameFullscreenChanged(nsIDOMElement * aFrameElement)10077 nsresult nsDocument::RemoteFrameFullscreenChanged(
10078     nsIDOMElement* aFrameElement) {
10079   // Ensure the frame element is the fullscreen element in this document.
10080   // If the frame element is already the fullscreen element in this document,
10081   // this has no effect.
10082   nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
10083   auto request = MakeUnique<FullscreenRequest>(content->AsElement());
10084   request->mIsCallerChrome = false;
10085   request->mShouldNotifyNewOrigin = false;
10086   RequestFullScreen(Move(request));
10087 
10088   return NS_OK;
10089 }
10090 
RemoteFrameFullscreenReverted()10091 nsresult nsDocument::RemoteFrameFullscreenReverted() {
10092   RestorePreviousFullScreenState();
10093   return NS_OK;
10094 }
10095 
IsUnprefixedFullscreenEnabled(JSContext * aCx,JSObject * aObject)10096 /* static */ bool nsIDocument::IsUnprefixedFullscreenEnabled(
10097     JSContext* aCx, JSObject* aObject) {
10098   MOZ_ASSERT(NS_IsMainThread());
10099   return nsContentUtils::IsSystemCaller(aCx) ||
10100          nsContentUtils::IsUnprefixedFullscreenApiEnabled();
10101 }
10102 
HasFullScreenSubDocument(nsIDocument * aDoc)10103 static bool HasFullScreenSubDocument(nsIDocument* aDoc) {
10104   uint32_t count = CountFullscreenSubDocuments(aDoc);
10105   NS_ASSERTION(count <= 1,
10106                "Fullscreen docs should have at most 1 fullscreen child!");
10107   return count >= 1;
10108 }
10109 
10110 // Returns nullptr if a request for Fullscreen API is currently enabled
10111 // in the given document. Returns a static string indicates the reason
10112 // why it is not enabled otherwise.
GetFullscreenError(nsIDocument * aDoc,bool aCallerIsChrome)10113 static const char* GetFullscreenError(nsIDocument* aDoc, bool aCallerIsChrome) {
10114   bool apiEnabled = nsContentUtils::IsFullScreenApiEnabled();
10115   if (apiEnabled && aCallerIsChrome) {
10116     // Chrome code can always use the full-screen API, provided it's not
10117     // explicitly disabled.
10118     return nullptr;
10119   }
10120 
10121   if (!apiEnabled) {
10122     return "FullscreenDeniedDisabled";
10123   }
10124 
10125   // Ensure that all containing elements are <iframe> and have
10126   // allowfullscreen attribute set.
10127   nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell());
10128   if (!docShell || !docShell->GetFullscreenAllowed()) {
10129     return "FullscreenDeniedContainerNotAllowed";
10130   }
10131   return nullptr;
10132 }
10133 
FullscreenElementReadyCheck(Element * aElement,bool aWasCallerChrome)10134 bool nsDocument::FullscreenElementReadyCheck(Element* aElement,
10135                                              bool aWasCallerChrome) {
10136   NS_ASSERTION(aElement,
10137                "Must pass non-null element to nsDocument::RequestFullScreen");
10138   if (!aElement || aElement == FullScreenStackTop()) {
10139     return false;
10140   }
10141   if (!aElement->IsInComposedDoc()) {
10142     DispatchFullscreenError("FullscreenDeniedNotInDocument");
10143     return false;
10144   }
10145   if (aElement->OwnerDoc() != this) {
10146     DispatchFullscreenError("FullscreenDeniedMovedDocument");
10147     return false;
10148   }
10149   if (!GetWindow()) {
10150     DispatchFullscreenError("FullscreenDeniedLostWindow");
10151     return false;
10152   }
10153   if (const char* msg = GetFullscreenError(this, aWasCallerChrome)) {
10154     DispatchFullscreenError(msg);
10155     return false;
10156   }
10157   if (!IsVisible()) {
10158     DispatchFullscreenError("FullscreenDeniedHidden");
10159     return false;
10160   }
10161   if (HasFullScreenSubDocument(this)) {
10162     DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
10163     return false;
10164   }
10165   // XXXsmaug Note, we don't follow the latest fullscreen spec here.
10166   //         This whole check could be probably removed.
10167   if (FullScreenStackTop() &&
10168       !nsContentUtils::ContentIsHostIncludingDescendantOf(
10169           aElement, FullScreenStackTop())) {
10170     // If this document is full-screen, only grant full-screen requests from
10171     // a descendant of the current full-screen element.
10172     DispatchFullscreenError("FullscreenDeniedNotDescendant");
10173     return false;
10174   }
10175   if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
10176     DispatchFullscreenError("FullscreenDeniedNotFocusedTab");
10177     return false;
10178   }
10179   // Deny requests when a windowed plugin is focused.
10180   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
10181   if (!fm) {
10182     NS_WARNING("Failed to retrieve focus manager in full-screen request.");
10183     return false;
10184   }
10185   nsCOMPtr<nsIDOMElement> focusedElement;
10186   fm->GetFocusedElement(getter_AddRefs(focusedElement));
10187   if (focusedElement) {
10188     nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
10189     if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
10190       DispatchFullscreenError("FullscreenDeniedFocusedPlugin");
10191       return false;
10192     }
10193   }
10194   return true;
10195 }
10196 
FullscreenRequest(Element * aElement)10197 FullscreenRequest::FullscreenRequest(Element* aElement)
10198     : mElement(aElement),
10199       mDocument(static_cast<nsDocument*>(aElement->OwnerDoc())) {
10200   MOZ_COUNT_CTOR(FullscreenRequest);
10201 }
10202 
~FullscreenRequest()10203 FullscreenRequest::~FullscreenRequest() { MOZ_COUNT_DTOR(FullscreenRequest); }
10204 
10205 // Any fullscreen request waiting for the widget to finish being full-
10206 // screen is queued here. This is declared static instead of a member
10207 // of nsDocument because in the majority of time, there would be at most
10208 // one document requesting fullscreen. We shouldn't waste the space to
10209 // hold for it in every document.
10210 class PendingFullscreenRequestList {
10211  public:
Add(UniquePtr<FullscreenRequest> && aRequest)10212   static void Add(UniquePtr<FullscreenRequest>&& aRequest) {
10213     sList.insertBack(aRequest.release());
10214   }
10215 
GetLast()10216   static const FullscreenRequest* GetLast() { return sList.getLast(); }
10217 
10218   enum IteratorOption {
10219     // When we are committing fullscreen changes or preparing for
10220     // that, we generally want to iterate all requests in the same
10221     // window with eDocumentsWithSameRoot option.
10222     eDocumentsWithSameRoot,
10223     // If we are removing a document from the tree, we would only
10224     // want to remove the requests from the given document and its
10225     // descendants. For that case, use eInclusiveDescendants.
10226     eInclusiveDescendants
10227   };
10228 
10229   class Iterator {
10230    public:
Iterator(nsIDocument * aDoc,IteratorOption aOption)10231     explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
10232         : mCurrent(PendingFullscreenRequestList::sList.getFirst()),
10233           mRootShellForIteration(aDoc->GetDocShell()) {
10234       if (mCurrent) {
10235         if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
10236           mRootShellForIteration->GetRootTreeItem(
10237               getter_AddRefs(mRootShellForIteration));
10238         }
10239         SkipToNextMatch();
10240       }
10241     }
10242 
DeleteAndNext()10243     void DeleteAndNext() {
10244       DeleteAndNextInternal();
10245       SkipToNextMatch();
10246     }
AtEnd() const10247     bool AtEnd() const { return mCurrent == nullptr; }
Get() const10248     const FullscreenRequest& Get() const { return *mCurrent; }
10249 
10250    private:
DeleteAndNextInternal()10251     void DeleteAndNextInternal() {
10252       FullscreenRequest* thisRequest = mCurrent;
10253       mCurrent = mCurrent->getNext();
10254       delete thisRequest;
10255     }
SkipToNextMatch()10256     void SkipToNextMatch() {
10257       while (mCurrent) {
10258         nsCOMPtr<nsIDocShellTreeItem> docShell =
10259             mCurrent->GetDocument()->GetDocShell();
10260         if (!docShell) {
10261           // Always automatically drop documents which has been
10262           // detached from the doc shell.
10263           DeleteAndNextInternal();
10264         } else {
10265           while (docShell && docShell != mRootShellForIteration) {
10266             docShell->GetParent(getter_AddRefs(docShell));
10267           }
10268           if (!docShell) {
10269             // We've gone over the root, but haven't find the target
10270             // ancestor, so skip this item.
10271             mCurrent = mCurrent->getNext();
10272           } else {
10273             break;
10274           }
10275         }
10276       }
10277     }
10278 
10279     FullscreenRequest* mCurrent;
10280     nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
10281   };
10282 
10283  private:
10284   PendingFullscreenRequestList() = delete;
10285 
10286   static LinkedList<FullscreenRequest> sList;
10287 };
10288 
10289 /* static */ LinkedList<FullscreenRequest> PendingFullscreenRequestList::sList;
10290 
GetRootWindow(nsIDocument * aDoc)10291 static nsCOMPtr<nsPIDOMWindowOuter> GetRootWindow(nsIDocument* aDoc) {
10292   nsIDocShell* docShell = aDoc->GetDocShell();
10293   if (!docShell) {
10294     return nullptr;
10295   }
10296   nsCOMPtr<nsIDocShellTreeItem> rootItem;
10297   docShell->GetRootTreeItem(getter_AddRefs(rootItem));
10298   return rootItem ? rootItem->GetWindow() : nullptr;
10299 }
10300 
ShouldApplyFullscreenDirectly(nsIDocument * aDoc,nsPIDOMWindowOuter * aRootWin)10301 static bool ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
10302                                           nsPIDOMWindowOuter* aRootWin) {
10303   if (XRE_GetProcessType() == GeckoProcessType_Content) {
10304     // If we are in the content process, we can apply the fullscreen
10305     // state directly only if we have been in DOM fullscreen, because
10306     // otherwise we always need to notify the chrome.
10307     return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement();
10308   } else {
10309     // If we are in the chrome process, and the window has not been in
10310     // fullscreen, we certainly need to make that fullscreen first.
10311     if (!aRootWin->GetFullScreen()) {
10312       return false;
10313     }
10314     // The iterator not being at end indicates there is still some
10315     // pending fullscreen request relates to this document. We have to
10316     // push the request to the pending queue so requests are handled
10317     // in the correct order.
10318     PendingFullscreenRequestList::Iterator iter(
10319         aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
10320     if (!iter.AtEnd()) {
10321       return false;
10322     }
10323     // We have to apply the fullscreen state directly in this case,
10324     // because nsGlobalWindow::SetFullscreenInternal() will do nothing
10325     // if it is already in fullscreen. If we do not apply the state but
10326     // instead add it to the queue and wait for the window as normal,
10327     // we would get stuck.
10328     return true;
10329   }
10330 }
10331 
RequestFullScreen(UniquePtr<FullscreenRequest> && aRequest)10332 void nsDocument::RequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest) {
10333   nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
10334   if (!rootWin) {
10335     return;
10336   }
10337 
10338   if (ShouldApplyFullscreenDirectly(this, rootWin)) {
10339     ApplyFullscreen(*aRequest);
10340     return;
10341   }
10342 
10343   // Per spec only HTML, <svg>, and <math> should be allowed, but
10344   // we also need to allow XUL elements right now.
10345   Element* elem = aRequest->GetElement();
10346   if (!elem->IsHTMLElement() && !elem->IsXULElement() &&
10347       !elem->IsSVGElement(nsGkAtoms::svg) &&
10348       !elem->IsMathMLElement(nsGkAtoms::math)) {
10349     DispatchFullscreenError("FullscreenDeniedNotHTMLSVGOrMathML");
10350     return;
10351   }
10352 
10353   // We don't need to check element ready before this point, because
10354   // if we called ApplyFullscreen, it would check that for us.
10355   if (!FullscreenElementReadyCheck(elem, aRequest->mIsCallerChrome)) {
10356     return;
10357   }
10358 
10359   PendingFullscreenRequestList::Add(Move(aRequest));
10360   if (XRE_GetProcessType() == GeckoProcessType_Content) {
10361     // If we are not the top level process, dispatch an event to make
10362     // our parent process go fullscreen first.
10363     nsContentUtils::DispatchEventOnlyToChrome(
10364         this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Request"),
10365         /* Bubbles */ true, /* Cancelable */ false,
10366         /* DefaultAction */ nullptr);
10367   } else {
10368     // Make the window fullscreen.
10369     rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
10370   }
10371 }
10372 
HandlePendingFullscreenRequests(nsIDocument * aDoc)10373 /* static */ bool nsIDocument::HandlePendingFullscreenRequests(
10374     nsIDocument* aDoc) {
10375   bool handled = false;
10376   PendingFullscreenRequestList::Iterator iter(
10377       aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
10378   while (!iter.AtEnd()) {
10379     const FullscreenRequest& request = iter.Get();
10380     if (request.GetDocument()->ApplyFullscreen(request)) {
10381       handled = true;
10382     }
10383     iter.DeleteAndNext();
10384   }
10385   return handled;
10386 }
10387 
ClearPendingFullscreenRequests(nsIDocument * aDoc)10388 static void ClearPendingFullscreenRequests(nsIDocument* aDoc) {
10389   PendingFullscreenRequestList::Iterator iter(
10390       aDoc, PendingFullscreenRequestList::eInclusiveDescendants);
10391   while (!iter.AtEnd()) {
10392     iter.DeleteAndNext();
10393   }
10394 }
10395 
ApplyFullscreen(const FullscreenRequest & aRequest)10396 bool nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest) {
10397   Element* elem = aRequest.GetElement();
10398   if (!FullscreenElementReadyCheck(elem, aRequest.mIsCallerChrome)) {
10399     return false;
10400   }
10401 
10402   // Stash a reference to any existing fullscreen doc, we'll use this later
10403   // to detect if the origin which is fullscreen has changed.
10404   nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
10405 
10406   // Stores a list of documents which we must dispatch "fullscreenchange"
10407   // too. We're required by the spec to dispatch the events in root-to-leaf
10408   // order, but we traverse the doctree in a leaf-to-root order, so we save
10409   // references to the documents we must dispatch to so that we get the order
10410   // as specified.
10411   AutoTArray<nsIDocument*, 8> changed;
10412 
10413   // Remember the root document, so that if a full-screen document is hidden
10414   // we can reset full-screen state in the remaining visible full-screen
10415   // documents.
10416   nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
10417 
10418   // If a document is already in fullscreen, then unlock the mouse pointer
10419   // before setting a new document to fullscreen
10420   UnlockPointer();
10421 
10422   // Set the full-screen element. This sets the full-screen style on the
10423   // element, and the full-screen-ancestor styles on ancestors of the element
10424   // in this document.
10425   DebugOnly<bool> x = FullScreenStackPush(elem);
10426   NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
10427   // Set the iframe fullscreen flag.
10428   if (elem->IsHTMLElement(nsGkAtoms::iframe)) {
10429     static_cast<HTMLIFrameElement*>(elem)->SetFullscreenFlag(true);
10430   }
10431   changed.AppendElement(this);
10432 
10433   // Propagate up the document hierarchy, setting the full-screen element as
10434   // the element's container in ancestor documents. This also sets the
10435   // appropriate css styles as well. Note we don't propagate down the
10436   // document hierarchy, the full-screen element (or its container) is not
10437   // visible there. Stop when we reach the root document.
10438   nsIDocument* child = this;
10439   while (true) {
10440     child->SetFullscreenRoot(fullScreenRootDoc);
10441     NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
10442                  "Fullscreen root should be set!");
10443     if (child == fullScreenRootDoc) {
10444       break;
10445     }
10446     nsIDocument* parent = child->GetParentDocument();
10447     Element* element = parent->FindContentForSubDocument(child)->AsElement();
10448     if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
10449       changed.AppendElement(parent);
10450       child = parent;
10451     } else {
10452       // We've reached either the root, or a point in the doctree where the
10453       // new full-screen element container is the same as the previous
10454       // full-screen element's container. No more changes need to be made
10455       // to the full-screen stacks of documents further up the tree.
10456       break;
10457     }
10458   }
10459 
10460   FullscreenRoots::Add(this);
10461 
10462   // If it is the first entry of the fullscreen, trigger an event so
10463   // that the UI can response to this change, e.g. hide chrome, or
10464   // notifying parent process to enter fullscreen. Note that chrome
10465   // code may also want to listen to MozDOMFullscreen:NewOrigin event
10466   // to pop up warning UI.
10467   if (!previousFullscreenDoc) {
10468     nsContentUtils::DispatchEventOnlyToChrome(
10469         this, ToSupports(elem), NS_LITERAL_STRING("MozDOMFullscreen:Entered"),
10470         /* Bubbles */ true, /* Cancelable */ false,
10471         /* DefaultAction */ nullptr);
10472   }
10473 
10474   // The origin which is fullscreen gets changed. Trigger an event so
10475   // that the chrome knows to pop up a warning UI. Note that
10476   // previousFullscreenDoc == nullptr upon first entry, so we always
10477   // take this path on the first entry. Also note that, in a multi-
10478   // process browser, the code in content process is responsible for
10479   // sending message with the origin to its parent, and the parent
10480   // shouldn't rely on this event itself.
10481   if (aRequest.mShouldNotifyNewOrigin &&
10482       !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
10483     DispatchCustomEventWithFlush(
10484         this, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
10485         /* Bubbles */ true, /* ChromeOnly */ true);
10486   }
10487 
10488   // Dispatch "fullscreenchange" events. Note this loop is in reverse
10489   // order so that the events for the root document arrives before the leaf
10490   // document, as required by the spec.
10491   for (uint32_t i = 0; i < changed.Length(); ++i) {
10492     DispatchFullScreenChange(changed[changed.Length() - i - 1]);
10493   }
10494   return true;
10495 }
10496 
FullscreenEnabled(CallerType aCallerType)10497 bool nsDocument::FullscreenEnabled(CallerType aCallerType) {
10498   return !GetFullscreenError(this, aCallerType == CallerType::System);
10499 }
10500 
CurrentOrientationAngle() const10501 uint16_t nsDocument::CurrentOrientationAngle() const {
10502   return mCurrentOrientationAngle;
10503 }
10504 
CurrentOrientationType() const10505 OrientationType nsDocument::CurrentOrientationType() const {
10506   return mCurrentOrientationType;
10507 }
10508 
SetCurrentOrientation(mozilla::dom::OrientationType aType,uint16_t aAngle)10509 void nsDocument::SetCurrentOrientation(mozilla::dom::OrientationType aType,
10510                                        uint16_t aAngle) {
10511   mCurrentOrientationType = aType;
10512   mCurrentOrientationAngle = aAngle;
10513 }
10514 
GetOrientationPendingPromise() const10515 Promise* nsDocument::GetOrientationPendingPromise() const {
10516   return mOrientationPendingPromise;
10517 }
10518 
SetOrientationPendingPromise(Promise * aPromise)10519 void nsDocument::SetOrientationPendingPromise(Promise* aPromise) {
10520   mOrientationPendingPromise = aPromise;
10521 }
10522 
DispatchPointerLockChange(nsIDocument * aTarget)10523 static void DispatchPointerLockChange(nsIDocument* aTarget) {
10524   if (!aTarget) {
10525     return;
10526   }
10527 
10528   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
10529       aTarget, NS_LITERAL_STRING("pointerlockchange"), true, false);
10530   asyncDispatcher->PostDOMEvent();
10531 }
10532 
DispatchPointerLockError(nsIDocument * aTarget,const char * aMessage)10533 static void DispatchPointerLockError(nsIDocument* aTarget,
10534                                      const char* aMessage) {
10535   if (!aTarget) {
10536     return;
10537   }
10538 
10539   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
10540       aTarget, NS_LITERAL_STRING("pointerlockerror"), true, false);
10541   asyncDispatcher->PostDOMEvent();
10542   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
10543                                   NS_LITERAL_CSTRING("DOM"), aTarget,
10544                                   nsContentUtils::eDOM_PROPERTIES, aMessage);
10545 }
10546 
10547 class PointerLockRequest final : public Runnable {
10548  public:
PointerLockRequest(Element * aElement,bool aUserInputOrChromeCaller)10549   PointerLockRequest(Element* aElement, bool aUserInputOrChromeCaller)
10550       : mozilla::Runnable("PointerLockRequest"),
10551         mElement(do_GetWeakReference(aElement)),
10552         mDocument(do_GetWeakReference(aElement->OwnerDoc())),
10553         mUserInputOrChromeCaller(aUserInputOrChromeCaller) {}
10554 
10555   NS_IMETHOD Run() final;
10556 
10557  private:
10558   nsWeakPtr mElement;
10559   nsWeakPtr mDocument;
10560   bool mUserInputOrChromeCaller;
10561 };
10562 
GetPointerLockError(Element * aElement,Element * aCurrentLock,bool aNoFocusCheck=false)10563 static const char* GetPointerLockError(Element* aElement, Element* aCurrentLock,
10564                                        bool aNoFocusCheck = false) {
10565   // Check if pointer lock pref is enabled
10566   if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
10567     return "PointerLockDeniedDisabled";
10568   }
10569 
10570   nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
10571   if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) {
10572     return "PointerLockDeniedInUse";
10573   }
10574 
10575   if (!aElement->IsInComposedDoc()) {
10576     return "PointerLockDeniedNotInDocument";
10577   }
10578 
10579   if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) {
10580     return "PointerLockDeniedSandboxed";
10581   }
10582 
10583   // Check if the element is in a document with a docshell.
10584   if (!ownerDoc->GetContainer()) {
10585     return "PointerLockDeniedHidden";
10586   }
10587   nsCOMPtr<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow();
10588   if (!ownerWindow) {
10589     return "PointerLockDeniedHidden";
10590   }
10591   nsCOMPtr<nsPIDOMWindowInner> ownerInnerWindow = ownerDoc->GetInnerWindow();
10592   if (!ownerInnerWindow) {
10593     return "PointerLockDeniedHidden";
10594   }
10595   if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
10596     return "PointerLockDeniedHidden";
10597   }
10598 
10599   nsCOMPtr<nsPIDOMWindowOuter> top = ownerWindow->GetScriptableTop();
10600   if (!top || !top->GetExtantDoc() || top->GetExtantDoc()->Hidden()) {
10601     return "PointerLockDeniedHidden";
10602   }
10603 
10604   if (!aNoFocusCheck) {
10605     mozilla::ErrorResult rv;
10606     if (!top->GetExtantDoc()->HasFocus(rv)) {
10607       return "PointerLockDeniedNotFocused";
10608     }
10609   }
10610 
10611   return nullptr;
10612 }
10613 
ChangePointerLockedElement(Element * aElement,nsIDocument * aDocument,Element * aPointerLockedElement)10614 static void ChangePointerLockedElement(Element* aElement,
10615                                        nsIDocument* aDocument,
10616                                        Element* aPointerLockedElement) {
10617   // aDocument here is not really necessary, as it is the uncomposed
10618   // document of both aElement and aPointerLockedElement as far as one
10619   // is not nullptr, and they wouldn't both be nullptr in any case.
10620   // But since the caller of this function should have known what the
10621   // document is, we just don't try to figure out what it should be.
10622   MOZ_ASSERT(aDocument);
10623   MOZ_ASSERT(aElement != aPointerLockedElement);
10624   if (aPointerLockedElement) {
10625     MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument);
10626     aPointerLockedElement->ClearPointerLock();
10627   }
10628   if (aElement) {
10629     MOZ_ASSERT(aElement->GetComposedDoc() == aDocument);
10630     aElement->SetPointerLock();
10631     EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
10632     EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
10633     NS_ASSERTION(EventStateManager::sPointerLockedElement &&
10634                      EventStateManager::sPointerLockedDoc,
10635                  "aElement and this should support weak references!");
10636   } else {
10637     EventStateManager::sPointerLockedElement = nullptr;
10638     EventStateManager::sPointerLockedDoc = nullptr;
10639   }
10640   // Retarget all events to aElement via capture or
10641   // stop retargeting if aElement is nullptr.
10642   nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
10643   DispatchPointerLockChange(aDocument);
10644 }
10645 
10646 NS_IMETHODIMP
Run()10647 PointerLockRequest::Run() {
10648   nsCOMPtr<Element> e = do_QueryReferent(mElement);
10649   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
10650   nsDocument* d = static_cast<nsDocument*>(doc.get());
10651   const char* error = nullptr;
10652   if (!e || !d || !e->GetComposedDoc()) {
10653     error = "PointerLockDeniedNotInDocument";
10654   } else if (e->GetComposedDoc() != d) {
10655     error = "PointerLockDeniedMovedDocument";
10656   }
10657   if (!error) {
10658     nsCOMPtr<Element> pointerLockedElement =
10659         do_QueryReferent(EventStateManager::sPointerLockedElement);
10660     if (e == pointerLockedElement) {
10661       DispatchPointerLockChange(d);
10662       return NS_OK;
10663     }
10664     // Note, we must bypass focus change, so pass true as the last parameter!
10665     error = GetPointerLockError(e, pointerLockedElement, true);
10666     // Another element in the same document is requesting pointer lock,
10667     // just grant it without user input check.
10668     if (!error && pointerLockedElement) {
10669       ChangePointerLockedElement(e, d, pointerLockedElement);
10670       return NS_OK;
10671     }
10672   }
10673   // If it is neither user input initiated, nor requested in fullscreen,
10674   // it should be rejected.
10675   if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) {
10676     error = "PointerLockDeniedNotInputDriven";
10677   }
10678   if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
10679     error = "PointerLockDeniedFailedToLock";
10680   }
10681   if (error) {
10682     DispatchPointerLockError(d, error);
10683     return NS_OK;
10684   }
10685 
10686   ChangePointerLockedElement(e, d, nullptr);
10687   nsContentUtils::DispatchEventOnlyToChrome(
10688       doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"),
10689       /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
10690   return NS_OK;
10691 }
10692 
RequestPointerLock(Element * aElement,CallerType aCallerType)10693 void nsDocument::RequestPointerLock(Element* aElement, CallerType aCallerType) {
10694   NS_ASSERTION(aElement,
10695                "Must pass non-null element to nsDocument::RequestPointerLock");
10696 
10697   nsCOMPtr<Element> pointerLockedElement =
10698       do_QueryReferent(EventStateManager::sPointerLockedElement);
10699   if (aElement == pointerLockedElement) {
10700     DispatchPointerLockChange(this);
10701     return;
10702   }
10703 
10704   if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) {
10705     DispatchPointerLockError(this, msg);
10706     return;
10707   }
10708 
10709   bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() ||
10710                                  aCallerType == CallerType::System;
10711   nsCOMPtr<nsIRunnable> request =
10712       new PointerLockRequest(aElement, userInputOrSystemCaller);
10713   Dispatch(TaskCategory::Other, request.forget());
10714 }
10715 
SetPointerLock(Element * aElement,int aCursorStyle)10716 bool nsDocument::SetPointerLock(Element* aElement, int aCursorStyle) {
10717   MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
10718              "We should be either unlocking pointer (aElement is nullptr), "
10719              "or locking pointer to an element in this document");
10720 #ifdef DEBUG
10721   if (!aElement) {
10722     nsCOMPtr<nsIDocument> pointerLockedDoc =
10723         do_QueryReferent(EventStateManager::sPointerLockedDoc);
10724     MOZ_ASSERT(pointerLockedDoc == this);
10725   }
10726 #endif
10727 
10728   nsIPresShell* shell = GetShell();
10729   if (!shell) {
10730     NS_WARNING("SetPointerLock(): No PresShell");
10731     if (!aElement) {
10732       // If we are unlocking pointer lock, but for some reason the doc
10733       // has already detached from the presshell, just ask the event
10734       // state manager to release the pointer.
10735       EventStateManager::SetPointerLock(nullptr, nullptr);
10736       return true;
10737     }
10738     return false;
10739   }
10740   nsPresContext* presContext = shell->GetPresContext();
10741   if (!presContext) {
10742     NS_WARNING("SetPointerLock(): Unable to get PresContext");
10743     return false;
10744   }
10745 
10746   nsCOMPtr<nsIWidget> widget;
10747   nsIFrame* rootFrame = shell->GetRootFrame();
10748   if (!NS_WARN_IF(!rootFrame)) {
10749     widget = rootFrame->GetNearestWidget();
10750     NS_WARNING_ASSERTION(widget,
10751                          "SetPointerLock(): Unable to find widget in "
10752                          "shell->GetRootFrame()->GetNearestWidget();");
10753     if (aElement && !widget) {
10754       return false;
10755     }
10756   }
10757 
10758   // Hide the cursor and set pointer lock for future mouse events
10759   RefPtr<EventStateManager> esm = presContext->EventStateManager();
10760   esm->SetCursor(aCursorStyle, nullptr, false, 0.0f, 0.0f, widget, true);
10761   EventStateManager::SetPointerLock(widget, aElement);
10762 
10763   return true;
10764 }
10765 
UnlockPointer(nsIDocument * aDoc)10766 void nsDocument::UnlockPointer(nsIDocument* aDoc) {
10767   if (!EventStateManager::sIsPointerLocked) {
10768     return;
10769   }
10770 
10771   nsCOMPtr<nsIDocument> pointerLockedDoc =
10772       do_QueryReferent(EventStateManager::sPointerLockedDoc);
10773   if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
10774     return;
10775   }
10776   nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
10777   if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
10778     return;
10779   }
10780 
10781   nsCOMPtr<Element> pointerLockedElement =
10782       do_QueryReferent(EventStateManager::sPointerLockedElement);
10783   ChangePointerLockedElement(nullptr, doc, pointerLockedElement);
10784 
10785   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
10786       pointerLockedElement, NS_LITERAL_STRING("MozDOMPointerLock:Exited"), true,
10787       true);
10788   asyncDispatcher->RunDOMEventWhenSafe();
10789 }
10790 
UnlockPointer(nsIDocument * aDoc)10791 void nsIDocument::UnlockPointer(nsIDocument* aDoc) {
10792   nsDocument::UnlockPointer(aDoc);
10793 }
10794 
UpdateVisibilityState()10795 void nsDocument::UpdateVisibilityState() {
10796   dom::VisibilityState oldState = mVisibilityState;
10797   mVisibilityState = GetVisibilityState();
10798   if (oldState != mVisibilityState) {
10799     nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
10800                                          NS_LITERAL_STRING("visibilitychange"),
10801                                          /* bubbles = */ true,
10802                                          /* cancelable = */ false);
10803     EnumerateActivityObservers(NotifyActivityChanged, nullptr);
10804   }
10805 
10806   if (mVisibilityState == dom::VisibilityState::Visible) {
10807     MaybeActiveMediaComponents();
10808   }
10809 }
10810 
GetVisibilityState() const10811 VisibilityState nsDocument::GetVisibilityState() const {
10812   // We have to check a few pieces of information here:
10813   // 1)  Are we in bfcache (!IsVisible())?  If so, nothing else matters.
10814   // 2)  Do we have an outer window?  If not, we're hidden.  Note that we don't
10815   //     want to use GetWindow here because it does weird groveling for windows
10816   //     in some cases.
10817   // 3)  Is our outer window background?  If so, we're hidden.
10818   // Otherwise, we're visible.
10819   if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
10820       mWindow->GetOuterWindow()->IsBackground()) {
10821     return dom::VisibilityState::Hidden;
10822   }
10823 
10824   return dom::VisibilityState::Visible;
10825 }
10826 
PostVisibilityUpdateEvent()10827 /* virtual */ void nsDocument::PostVisibilityUpdateEvent() {
10828   nsCOMPtr<nsIRunnable> event =
10829       NewRunnableMethod("nsDocument::UpdateVisibilityState", this,
10830                         &nsDocument::UpdateVisibilityState);
10831   Dispatch(TaskCategory::Other, event.forget());
10832 }
10833 
MaybeActiveMediaComponents()10834 void nsDocument::MaybeActiveMediaComponents() {
10835   if (!mWindow) {
10836     return;
10837   }
10838 
10839   GetWindow()->MaybeActiveMediaComponents();
10840 }
10841 
DocAddSizeOfExcludingThis(nsWindowSizes & aSizes) const10842 /* virtual */ void nsIDocument::DocAddSizeOfExcludingThis(
10843     nsWindowSizes& aSizes) const {
10844   nsINode::AddSizeOfExcludingThis(aSizes, &aSizes.mDOMOtherSize);
10845 
10846   if (mPresShell) {
10847     mPresShell->AddSizeOfIncludingThis(aSizes);
10848   }
10849 
10850   aSizes.mPropertyTablesSize +=
10851       mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
10852   for (uint32_t i = 0, count = mExtraPropertyTables.Length(); i < count; ++i) {
10853     aSizes.mPropertyTablesSize += mExtraPropertyTables[i]->SizeOfIncludingThis(
10854         aSizes.mState.mMallocSizeOf);
10855   }
10856 
10857   if (EventListenerManager* elm = GetExistingListenerManager()) {
10858     aSizes.mDOMEventListenersCount += elm->ListenerCount();
10859   }
10860 
10861   if (mNodeInfoManager) {
10862     mNodeInfoManager->AddSizeOfIncludingThis(aSizes);
10863   }
10864 
10865   // Measurement of the following members may be added later if DMD finds it
10866   // is worthwhile:
10867   // - many!
10868 }
10869 
DocAddSizeOfIncludingThis(nsWindowSizes & aWindowSizes) const10870 void nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const {
10871   aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
10872   DocAddSizeOfExcludingThis(aWindowSizes);
10873 }
10874 
SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>> & aSheets,MallocSizeOf aMallocSizeOf)10875 static size_t SizeOfOwnedSheetArrayExcludingThis(
10876     const nsTArray<RefPtr<StyleSheet>>& aSheets, MallocSizeOf aMallocSizeOf) {
10877   size_t n = 0;
10878   n += aSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
10879   for (StyleSheet* sheet : aSheets) {
10880     if (!sheet->GetAssociatedDocument()) {
10881       // Avoid over-reporting shared sheets.
10882       continue;
10883     }
10884     n += sheet->SizeOfIncludingThis(aMallocSizeOf);
10885   }
10886   return n;
10887 }
10888 
AddSizeOfExcludingThis(nsWindowSizes & aSizes,size_t * aNodeSize) const10889 void nsDocument::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
10890                                         size_t* aNodeSize) const {
10891   // This AddSizeOfExcludingThis() overrides the one from nsINode.  But
10892   // nsDocuments can only appear at the top of the DOM tree, and we use the
10893   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
10894   // be called.
10895   MOZ_CRASH();
10896 }
10897 
AddSizeOfNodeTree(nsIContent * aNode,nsWindowSizes & aWindowSizes)10898 static void AddSizeOfNodeTree(nsIContent* aNode, nsWindowSizes& aWindowSizes) {
10899   size_t nodeSize = 0;
10900   aNode->AddSizeOfIncludingThis(aWindowSizes, &nodeSize);
10901 
10902   // This is where we transfer the nodeSize obtained from
10903   // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
10904   switch (aNode->NodeType()) {
10905     case nsINode::ELEMENT_NODE:
10906       aWindowSizes.mDOMElementNodesSize += nodeSize;
10907       break;
10908     case nsINode::TEXT_NODE:
10909       aWindowSizes.mDOMTextNodesSize += nodeSize;
10910       break;
10911     case nsINode::CDATA_SECTION_NODE:
10912       aWindowSizes.mDOMCDATANodesSize += nodeSize;
10913       break;
10914     case nsINode::COMMENT_NODE:
10915       aWindowSizes.mDOMCommentNodesSize += nodeSize;
10916       break;
10917     default:
10918       aWindowSizes.mDOMOtherSize += nodeSize;
10919       break;
10920   }
10921 
10922   if (EventListenerManager* elm = aNode->GetExistingListenerManager()) {
10923     aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
10924   }
10925 
10926   AllChildrenIterator iter(aNode, nsIContent::eAllChildren);
10927   for (nsIContent* n = iter.GetNextChild(); n; n = iter.GetNextChild()) {
10928     AddSizeOfNodeTree(n, aWindowSizes);
10929   }
10930 }
10931 
DocAddSizeOfExcludingThis(nsWindowSizes & aWindowSizes) const10932 void nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const {
10933   // We use AllChildrenIterator to iterate over DOM nodes in
10934   // AddSizeOfNodeTree(). The obvious place to start is at the document's root
10935   // element, using GetRootElement(). However, that will miss comment nodes
10936   // that are siblings of the root element. Instead we use
10937   // GetFirstChild()/GetNextSibling() to traverse the document's immediate
10938   // child nodes, calling AddSizeOfNodeTree() on each to measure them and then
10939   // all their descendants. (The comment nodes won't have any descendants).
10940   for (nsIContent* node = nsINode::GetFirstChild(); node;
10941        node = node->GetNextSibling()) {
10942     AddSizeOfNodeTree(node, aWindowSizes);
10943   }
10944 
10945   // IMPORTANT: for our ComputedValues measurements, we want to measure
10946   // ComputedValues accessible from DOM elements before ComputedValues not
10947   // accessible from DOM elements (i.e. accessible only from the frame tree).
10948   //
10949   // Therefore, the measurement of the nsIDocument superclass must happen after
10950   // the measurement of DOM nodes (above), because nsIDocument contains the
10951   // PresShell, which contains the frame tree.
10952   nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
10953 
10954   aWindowSizes.mLayoutStyleSheetsSize += SizeOfOwnedSheetArrayExcludingThis(
10955       mStyleSheets, aWindowSizes.mState.mMallocSizeOf);
10956   // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
10957   // (the nsLayoutStyleSheetCache singleton does).
10958   aWindowSizes.mLayoutStyleSheetsSize +=
10959       mOnDemandBuiltInUASheets.ShallowSizeOfExcludingThis(
10960           aWindowSizes.mState.mMallocSizeOf);
10961   for (auto& sheetArray : mAdditionalSheets) {
10962     aWindowSizes.mLayoutStyleSheetsSize += SizeOfOwnedSheetArrayExcludingThis(
10963         sheetArray, aWindowSizes.mState.mMallocSizeOf);
10964   }
10965   // Lumping in the loader with the style-sheets size is not ideal,
10966   // but most of the things in there are in fact stylesheets, so it
10967   // doesn't seem worthwhile to separate it out.
10968   aWindowSizes.mLayoutStyleSheetsSize +=
10969       CSSLoader()->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
10970 
10971   aWindowSizes.mDOMOtherSize += mAttrStyleSheet
10972                                     ? mAttrStyleSheet->DOMSizeOfIncludingThis(
10973                                           aWindowSizes.mState.mMallocSizeOf)
10974                                     : 0;
10975 
10976   aWindowSizes.mDOMOtherSize += mStyledLinks.ShallowSizeOfExcludingThis(
10977       aWindowSizes.mState.mMallocSizeOf);
10978 
10979   aWindowSizes.mDOMOtherSize +=
10980       mIdentifierMap.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
10981 
10982   // Measurement of the following members may be added later if DMD finds it
10983   // is worthwhile:
10984   // - many!
10985 }
10986 
Constructor(const GlobalObject & aGlobal,ErrorResult & rv)10987 already_AddRefed<nsIDocument> nsIDocument::Constructor(
10988     const GlobalObject& aGlobal, ErrorResult& rv) {
10989   nsCOMPtr<nsIScriptGlobalObject> global =
10990       do_QueryInterface(aGlobal.GetAsSupports());
10991   if (!global) {
10992     rv.Throw(NS_ERROR_UNEXPECTED);
10993     return nullptr;
10994   }
10995 
10996   nsCOMPtr<nsIScriptObjectPrincipal> prin =
10997       do_QueryInterface(aGlobal.GetAsSupports());
10998   if (!prin) {
10999     rv.Throw(NS_ERROR_UNEXPECTED);
11000     return nullptr;
11001   }
11002 
11003   nsCOMPtr<nsIURI> uri;
11004   NS_NewURI(getter_AddRefs(uri), "about:blank");
11005   if (!uri) {
11006     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
11007     return nullptr;
11008   }
11009 
11010   nsCOMPtr<nsIDOMDocument> document;
11011   nsresult res = NS_NewDOMDocument(
11012       getter_AddRefs(document), VoidString(), EmptyString(), nullptr, uri, uri,
11013       prin->GetPrincipal(), true, global, DocumentFlavorPlain);
11014   if (NS_FAILED(res)) {
11015     rv.Throw(res);
11016     return nullptr;
11017   }
11018 
11019   nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
11020   doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
11021 
11022   return doc.forget();
11023 }
11024 
CreateExpression(const nsAString & aExpression,XPathNSResolver * aResolver,ErrorResult & rv)11025 XPathExpression* nsIDocument::CreateExpression(const nsAString& aExpression,
11026                                                XPathNSResolver* aResolver,
11027                                                ErrorResult& rv) {
11028   return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
11029 }
11030 
CreateNSResolver(nsINode & aNodeResolver)11031 nsINode* nsIDocument::CreateNSResolver(nsINode& aNodeResolver) {
11032   return XPathEvaluator()->CreateNSResolver(aNodeResolver);
11033 }
11034 
Evaluate(JSContext * aCx,const nsAString & aExpression,nsINode & aContextNode,XPathNSResolver * aResolver,uint16_t aType,JS::Handle<JSObject * > aResult,ErrorResult & rv)11035 already_AddRefed<XPathResult> nsIDocument::Evaluate(
11036     JSContext* aCx, const nsAString& aExpression, nsINode& aContextNode,
11037     XPathNSResolver* aResolver, uint16_t aType, JS::Handle<JSObject*> aResult,
11038     ErrorResult& rv) {
11039   return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
11040                                     aType, aResult, rv);
11041 }
11042 
GetTopLevelContentDocument()11043 nsIDocument* nsIDocument::GetTopLevelContentDocument() {
11044   nsIDocument* parent;
11045 
11046   if (!mLoadedAsData) {
11047     parent = this;
11048   } else {
11049     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
11050     if (!window) {
11051       return nullptr;
11052     }
11053 
11054     parent = window->GetExtantDoc();
11055     if (!parent) {
11056       return nullptr;
11057     }
11058   }
11059 
11060   do {
11061     if (parent->IsTopLevelContentDocument()) {
11062       break;
11063     }
11064 
11065     // If we ever have a non-content parent before we hit a toplevel content
11066     // parent, then we're never going to find one.  Just bail.
11067     if (!parent->IsContentDocument()) {
11068       return nullptr;
11069     }
11070 
11071     nsIDocument* candidate = parent->GetParentDocument();
11072     parent = static_cast<nsDocument*>(candidate);
11073   } while (parent);
11074 
11075   return parent;
11076 }
11077 
PropagateUseCounters(nsIDocument * aParentDocument)11078 void nsIDocument::PropagateUseCounters(nsIDocument* aParentDocument) {
11079   MOZ_ASSERT(this != aParentDocument);
11080 
11081   // What really matters here is that our use counters get propagated as
11082   // high up in the content document hierarchy as possible.  So,
11083   // starting with aParentDocument, we need to find the toplevel content
11084   // document, and propagate our use counters into its
11085   // mChildDocumentUseCounters.
11086   nsIDocument* contentParent = aParentDocument->GetTopLevelContentDocument();
11087 
11088   if (!contentParent) {
11089     return;
11090   }
11091 
11092   contentParent->mChildDocumentUseCounters |= mUseCounters;
11093   contentParent->mChildDocumentUseCounters |= mChildDocumentUseCounters;
11094 }
11095 
SetPageUseCounter(UseCounter aUseCounter)11096 void nsIDocument::SetPageUseCounter(UseCounter aUseCounter) {
11097   // We want to set the use counter on the "page" that owns us; the definition
11098   // of "page" depends on what kind of document we are.  See the comments below
11099   // for details.  In any event, checking all the conditions below is
11100   // reasonably expensive, so we cache whether we've notified our owning page.
11101   if (mNotifiedPageForUseCounter[aUseCounter]) {
11102     return;
11103   }
11104   mNotifiedPageForUseCounter[aUseCounter] = true;
11105 
11106   if (mDisplayDocument) {
11107     // If we are a resource document, we won't have a docshell and so we won't
11108     // record any page use counters on this document.  Instead, we should
11109     // forward it up to the document that loaded us.
11110     MOZ_ASSERT(!mDocumentContainer);
11111     mDisplayDocument->SetChildDocumentUseCounter(aUseCounter);
11112     return;
11113   }
11114 
11115   if (IsBeingUsedAsImage()) {
11116     // If this is an SVG image document, we also won't have a docshell.
11117     MOZ_ASSERT(!mDocumentContainer);
11118     return;
11119   }
11120 
11121   // We only care about use counters in content.  If we're already a toplevel
11122   // content document, then we should have already set the use counter on
11123   // ourselves, and we are done.
11124   nsIDocument* contentParent = GetTopLevelContentDocument();
11125   if (!contentParent) {
11126     return;
11127   }
11128 
11129   if (this == contentParent) {
11130     MOZ_ASSERT(GetUseCounter(aUseCounter));
11131     return;
11132   }
11133 
11134   contentParent->SetChildDocumentUseCounter(aUseCounter);
11135 }
11136 
HasScriptsBlockedBySandbox()11137 bool nsIDocument::HasScriptsBlockedBySandbox() {
11138   return mSandboxFlags & SANDBOXED_SCRIPTS;
11139 }
11140 
InlineScriptAllowedByCSP()11141 bool nsIDocument::InlineScriptAllowedByCSP() {
11142   // this function assumes the inline script is parser created
11143   //  (e.g., before setting attribute(!) event handlers)
11144   nsCOMPtr<nsIContentSecurityPolicy> csp;
11145   nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
11146   NS_ENSURE_SUCCESS(rv, true);
11147   bool allowsInlineScript = true;
11148   if (csp) {
11149     nsresult rv =
11150         csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
11151                              EmptyString(),  // aNonce
11152                              true,           // aParserCreated
11153                              nullptr,  // FIXME get script sample (bug 1314567)
11154                              0,        // aLineNumber
11155                              &allowsInlineScript);
11156     NS_ENSURE_SUCCESS(rv, true);
11157   }
11158   return allowsInlineScript;
11159 }
11160 
MightBeAboutOrChromeScheme(nsIURI * aURI)11161 static bool MightBeAboutOrChromeScheme(nsIURI* aURI) {
11162   MOZ_ASSERT(aURI);
11163   bool isAbout = true;
11164   bool isChrome = true;
11165   aURI->SchemeIs("about", &isAbout);
11166   aURI->SchemeIs("chrome", &isChrome);
11167   return isAbout || isChrome;
11168 }
11169 
ReportExternalResourceUseCounters(nsIDocument * aDocument,void * aData)11170 static bool ReportExternalResourceUseCounters(nsIDocument* aDocument,
11171                                               void* aData) {
11172   const auto reportKind =
11173       nsDocument::UseCounterReportKind::eIncludeExternalResources;
11174   static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind);
11175   return true;
11176 }
11177 
ReportUseCounters(UseCounterReportKind aKind)11178 void nsDocument::ReportUseCounters(UseCounterReportKind aKind) {
11179   static const bool sDebugUseCounters = false;
11180   if (mReportedUseCounters) {
11181     return;
11182   }
11183 
11184   mReportedUseCounters = true;
11185 
11186   if (aKind == UseCounterReportKind::eIncludeExternalResources) {
11187     EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr);
11188   }
11189 
11190   if (Telemetry::HistogramUseCounterCount > 0 &&
11191       (IsContentDocument() || IsResourceDoc())) {
11192     nsCOMPtr<nsIURI> uri;
11193     NodePrincipal()->GetURI(getter_AddRefs(uri));
11194     if (!uri || MightBeAboutOrChromeScheme(uri)) {
11195       return;
11196     }
11197 
11198     if (sDebugUseCounters) {
11199       nsCString spec = uri->GetSpecOrDefault();
11200 
11201       // URIs can be rather long for data documents, so truncate them to
11202       // some reasonable length.
11203       spec.Truncate(std::min(128U, spec.Length()));
11204       printf("-- Use counters for %s --\n", spec.get());
11205     }
11206 
11207     // We keep separate counts for individual documents and top-level
11208     // pages to more accurately track how many web pages might break if
11209     // certain features were removed.  Consider the case of a single
11210     // HTML document with several SVG images and/or iframes with
11211     // sub-documents of their own.  If we maintained a single set of use
11212     // counters and all the sub-documents use a particular feature, then
11213     // telemetry would indicate that we would be breaking N documents if
11214     // that feature were removed.  Whereas with a document/top-level
11215     // page split, we can see that N documents would be affected, but
11216     // only a single web page would be affected.
11217 
11218     // The difference between the values of these two histograms and the
11219     // related use counters below tell us how many pages did *not* use
11220     // the feature in question.  For instance, if we see that a given
11221     // session has destroyed 30 content documents, but a particular use
11222     // counter shows only a count of 5, we can infer that the use
11223     // counter was *not* used in 25 of those 30 documents.
11224     //
11225     // We do things this way, rather than accumulating a boolean flag
11226     // for each use counter, to avoid sending histograms for features
11227     // that don't get widely used.  Doing things in this fashion means
11228     // smaller telemetry payloads and faster processing on the server
11229     // side.
11230     Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1);
11231     if (IsTopLevelContentDocument()) {
11232       Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED,
11233                             1);
11234     }
11235 
11236     for (int32_t c = 0; c < eUseCounter_Count; ++c) {
11237       UseCounter uc = static_cast<UseCounter>(c);
11238 
11239       Telemetry::HistogramID id = static_cast<Telemetry::HistogramID>(
11240           Telemetry::HistogramFirstUseCounter + uc * 2);
11241       bool value = GetUseCounter(uc);
11242 
11243       if (value) {
11244         if (sDebugUseCounters) {
11245           const char* name = Telemetry::GetHistogramName(id);
11246           if (name) {
11247             printf("  %s", name);
11248           } else {
11249             printf("  #%d", id);
11250           }
11251           printf(": %d\n", value);
11252         }
11253 
11254         Telemetry::Accumulate(id, 1);
11255       }
11256 
11257       if (IsTopLevelContentDocument()) {
11258         id = static_cast<Telemetry::HistogramID>(
11259             Telemetry::HistogramFirstUseCounter + uc * 2 + 1);
11260         value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
11261 
11262         if (value) {
11263           if (sDebugUseCounters) {
11264             const char* name = Telemetry::GetHistogramName(id);
11265             if (name) {
11266               printf("  %s", name);
11267             } else {
11268               printf("  #%d", id);
11269             }
11270             printf(": %d\n", value);
11271           }
11272 
11273           Telemetry::Accumulate(id, 1);
11274         }
11275       }
11276     }
11277   }
11278 
11279   if (IsContentDocument() || IsResourceDoc()) {
11280     uint16_t num = mIncCounters[eIncCounter_ScriptTag];
11281     Telemetry::Accumulate(Telemetry::DOM_SCRIPT_EVAL_PER_DOCUMENT, num);
11282   }
11283 }
11284 
AddIntersectionObserver(DOMIntersectionObserver * aObserver)11285 void nsDocument::AddIntersectionObserver(DOMIntersectionObserver* aObserver) {
11286   MOZ_ASSERT(!mIntersectionObservers.Contains(aObserver),
11287              "Intersection observer already in the list");
11288   mIntersectionObservers.PutEntry(aObserver);
11289 }
11290 
RemoveIntersectionObserver(DOMIntersectionObserver * aObserver)11291 void nsDocument::RemoveIntersectionObserver(
11292     DOMIntersectionObserver* aObserver) {
11293   mIntersectionObservers.RemoveEntry(aObserver);
11294 }
11295 
UpdateIntersectionObservations()11296 void nsDocument::UpdateIntersectionObservations() {
11297   if (mIntersectionObservers.IsEmpty()) {
11298     return;
11299   }
11300 
11301   DOMHighResTimeStamp time = 0;
11302   if (nsPIDOMWindowInner* window = GetInnerWindow()) {
11303     Performance* perf = window->GetPerformance();
11304     if (perf) {
11305       time = perf->Now();
11306     }
11307   }
11308   nsTArray<RefPtr<DOMIntersectionObserver>> observers(
11309       mIntersectionObservers.Count());
11310   for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
11311     DOMIntersectionObserver* observer = iter.Get()->GetKey();
11312     observers.AppendElement(observer);
11313   }
11314   for (const auto& observer : observers) {
11315     if (observer) {
11316       observer->Update(this, time);
11317     }
11318   }
11319 }
11320 
ScheduleIntersectionObserverNotification()11321 void nsDocument::ScheduleIntersectionObserverNotification() {
11322   if (mIntersectionObservers.IsEmpty()) {
11323     return;
11324   }
11325   MOZ_RELEASE_ASSERT(NS_IsMainThread());
11326   nsCOMPtr<nsIRunnable> notification =
11327       NewRunnableMethod("nsDocument::NotifyIntersectionObservers", this,
11328                         &nsDocument::NotifyIntersectionObservers);
11329   Dispatch(TaskCategory::Other, notification.forget());
11330 }
11331 
NotifyIntersectionObservers()11332 void nsDocument::NotifyIntersectionObservers() {
11333   nsTArray<RefPtr<DOMIntersectionObserver>> observers(
11334       mIntersectionObservers.Count());
11335   for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
11336     DOMIntersectionObserver* observer = iter.Get()->GetKey();
11337     observers.AppendElement(observer);
11338   }
11339   for (const auto& observer : observers) {
11340     if (observer) {
11341       observer->Notify();
11342     }
11343   }
11344 }
11345 
NotifyLayerManagerRecreatedCallback(nsIDocument * aDocument,void * aData)11346 static bool NotifyLayerManagerRecreatedCallback(nsIDocument* aDocument,
11347                                                 void* aData) {
11348   aDocument->NotifyLayerManagerRecreated();
11349   return true;
11350 }
11351 
NotifyLayerManagerRecreated()11352 void nsDocument::NotifyLayerManagerRecreated() {
11353   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
11354   EnumerateSubDocuments(NotifyLayerManagerRecreatedCallback, nullptr);
11355 }
11356 
XPathEvaluator()11357 XPathEvaluator* nsIDocument::XPathEvaluator() {
11358   if (!mXPathEvaluator) {
11359     mXPathEvaluator.reset(new dom::XPathEvaluator(this));
11360   }
11361   return mXPathEvaluator.get();
11362 }
11363 
GetCachedEncoder()11364 already_AddRefed<nsIDocumentEncoder> nsIDocument::GetCachedEncoder() {
11365   return mCachedEncoder.forget();
11366 }
11367 
SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)11368 void nsIDocument::SetCachedEncoder(
11369     already_AddRefed<nsIDocumentEncoder> aEncoder) {
11370   mCachedEncoder = aEncoder;
11371 }
11372 
SetContentTypeInternal(const nsACString & aType)11373 void nsIDocument::SetContentTypeInternal(const nsACString& aType) {
11374   if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
11375       aType.EqualsLiteral("application/xhtml+xml")) {
11376     mDefaultElementType = kNameSpaceID_XHTML;
11377   }
11378 
11379   mCachedEncoder = nullptr;
11380   mContentType = aType;
11381 }
11382 
GetLoadContext() const11383 nsILoadContext* nsIDocument::GetLoadContext() const {
11384   return mDocumentContainer;
11385 }
11386 
GetDocShell() const11387 nsIDocShell* nsIDocument::GetDocShell() const { return mDocumentContainer; }
11388 
SetStateObject(nsIStructuredCloneContainer * scContainer)11389 void nsIDocument::SetStateObject(nsIStructuredCloneContainer* scContainer) {
11390   mStateObjectContainer = scContainer;
11391   mStateObjectCached = nullptr;
11392 }
11393 
CreateHTMLElement(nsAtom * aTag)11394 already_AddRefed<Element> nsIDocument::CreateHTMLElement(nsAtom* aTag) {
11395   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
11396   nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
11397                                            ELEMENT_NODE);
11398   MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
11399 
11400   nsCOMPtr<Element> element;
11401   DebugOnly<nsresult> rv =
11402       NS_NewHTMLElement(getter_AddRefs(element), nodeInfo.forget(),
11403                         mozilla::dom::NOT_FROM_PARSER);
11404 
11405   MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
11406   return element.forget();
11407 }
11408 
MarkDocumentTreeToBeInSyncOperation(nsIDocument * aDoc,void * aData)11409 bool MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData) {
11410   nsCOMArray<nsIDocument>* documents =
11411       static_cast<nsCOMArray<nsIDocument>*>(aData);
11412   if (aDoc) {
11413     aDoc->SetIsInSyncOperation(true);
11414     if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc->GetInnerWindow()) {
11415       window->TimeoutManager().BeginSyncOperation();
11416     }
11417     documents->AppendObject(aDoc);
11418     aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
11419   }
11420   return true;
11421 }
11422 
nsAutoSyncOperation(nsIDocument * aDoc)11423 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc) {
11424   mMicroTaskLevel = 0;
11425   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
11426   if (ccjs) {
11427     mMicroTaskLevel = ccjs->MicroTaskLevel();
11428     ccjs->SetMicroTaskLevel(0);
11429   }
11430   if (aDoc) {
11431     if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
11432       if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
11433         nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
11434         MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
11435       }
11436     }
11437   }
11438 }
11439 
~nsAutoSyncOperation()11440 nsAutoSyncOperation::~nsAutoSyncOperation() {
11441   for (int32_t i = 0; i < mDocuments.Count(); ++i) {
11442     if (nsCOMPtr<nsPIDOMWindowInner> window = mDocuments[i]->GetInnerWindow()) {
11443       window->TimeoutManager().EndSyncOperation();
11444     }
11445     mDocuments[i]->SetIsInSyncOperation(false);
11446   }
11447   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
11448   if (ccjs) {
11449     ccjs->SetMicroTaskLevel(mMicroTaskLevel);
11450   }
11451 }
11452 
GetUserFontSet(bool aFlushUserFontSet)11453 gfxUserFontSet* nsIDocument::GetUserFontSet(bool aFlushUserFontSet) {
11454 // We want to initialize the user font set lazily the first time the
11455 // user asks for it, rather than building it too early and forcing
11456 // rule cascade creation.  Thus we try to enforce the invariant that
11457 // we *never* build the user font set until the first call to
11458 // GetUserFontSet.  However, once it's been requested, we can't wait
11459 // for somebody to call GetUserFontSet in order to rebuild it (see
11460 // comments below in MarkUserFontSetDirty for why).
11461 #ifdef DEBUG
11462   bool userFontSetGottenBefore = mGetUserFontSetCalled;
11463 #endif
11464   // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
11465   // flush.
11466   mGetUserFontSetCalled = true;
11467   if (mFontFaceSetDirty && aFlushUserFontSet) {
11468     // If this assertion fails, and there have actually been changes to
11469     // @font-face rules, then we will call StyleChangeReflow in
11470     // FlushUserFontSet.  If we're in the middle of reflow,
11471     // that's a bad thing to do, and the caller was responsible for
11472     // flushing first.  If we're not (e.g., in frame construction), it's
11473     // ok.
11474     NS_ASSERTION(!userFontSetGottenBefore || !GetShell() ||
11475                      !GetShell()->IsReflowLocked(),
11476                  "FlushUserFontSet should have been called first");
11477     FlushUserFontSet();
11478   }
11479 
11480   if (!mFontFaceSet) {
11481     return nullptr;
11482   }
11483 
11484   return mFontFaceSet->GetUserFontSet();
11485 }
11486 
FlushUserFontSet()11487 void nsIDocument::FlushUserFontSet() {
11488   if (!mGetUserFontSetCalled) {
11489     return;  // No one cares about this font set yet, but we want to be careful
11490              // to not unset our mFontFaceSetDirty bit, so when someone really
11491              // does we'll create it.
11492   }
11493 
11494   if (!mFontFaceSetDirty) {
11495     return;
11496   }
11497 
11498   mFontFaceSetDirty = false;
11499 
11500   if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
11501     nsTArray<nsFontFaceRuleContainer> rules;
11502     nsIPresShell* shell = GetShell();
11503     if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) {
11504       return;
11505     }
11506 
11507     if (!mFontFaceSet && !rules.IsEmpty()) {
11508       nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
11509       mFontFaceSet = new FontFaceSet(window, this);
11510     }
11511 
11512     bool changed = false;
11513     if (mFontFaceSet) {
11514       changed = mFontFaceSet->UpdateRules(rules);
11515     }
11516 
11517     // We need to enqueue a style change reflow (for later) to
11518     // reflect that we're modifying @font-face rules.  (However,
11519     // without a reflow, nothing will happen to start any downloads
11520     // that are needed.)
11521     if (changed && shell) {
11522       if (nsPresContext* presContext = shell->GetPresContext()) {
11523         presContext->UserFontSetUpdated();
11524       }
11525     }
11526   }
11527 }
11528 
MarkUserFontSetDirty()11529 void nsIDocument::MarkUserFontSetDirty() {
11530   if (!mGetUserFontSetCalled) {
11531     // We want to lazily build the user font set the first time it's
11532     // requested (so we don't force creation of rule cascades too
11533     // early), so don't do anything now.
11534     return;
11535   }
11536 
11537   mFontFaceSetDirty = true;
11538 }
11539 
Fonts()11540 FontFaceSet* nsIDocument::Fonts() {
11541   if (!mFontFaceSet) {
11542     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
11543     mFontFaceSet = new FontFaceSet(window, this);
11544     GetUserFontSet();  // this will cause the user font set to be
11545                        // created/updated
11546   }
11547   return mFontFaceSet;
11548 }
11549 
ReportHasScrollLinkedEffect()11550 void nsIDocument::ReportHasScrollLinkedEffect() {
11551   if (mHasScrollLinkedEffect) {
11552     // We already did this once for this document, don't do it again.
11553     return;
11554   }
11555   mHasScrollLinkedEffect = true;
11556   nsContentUtils::ReportToConsole(
11557       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Async Pan/Zoom"), this,
11558       nsContentUtils::eLAYOUT_PROPERTIES, "ScrollLinkedEffectFound2");
11559 }
11560 
UpdateStyleBackendType()11561 void nsIDocument::UpdateStyleBackendType() {
11562   MOZ_ASSERT(mStyleBackendType == StyleBackendType::None,
11563              "no need to call UpdateStyleBackendType now");
11564 
11565   // Assume Gecko by default.
11566   mStyleBackendType = StyleBackendType::Gecko;
11567 
11568 #ifdef MOZ_STYLO
11569   if (nsLayoutUtils::StyloEnabled() &&
11570       nsLayoutUtils::ShouldUseStylo(NodePrincipal())) {
11571     mStyleBackendType = StyleBackendType::Servo;
11572   }
11573 #endif
11574 }
11575 
SetUserHasInteracted(bool aUserHasInteracted)11576 void nsIDocument::SetUserHasInteracted(bool aUserHasInteracted) {
11577   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
11578           ("Document %p has been interacted by user.", this));
11579   mUserHasInteracted = aUserHasInteracted;
11580 }
11581 
NotifyUserActivation()11582 void nsIDocument::NotifyUserActivation() {
11583   ActivateByUserGesture();
11584   // Activate parent document which has same principle on the parent chain.
11585   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
11586   nsCOMPtr<nsIDocument> parent = GetSameTypeParentDocument();
11587   while (parent) {
11588     parent->MaybeActivateByUserGesture(principal);
11589     parent = parent->GetSameTypeParentDocument();
11590   }
11591 }
11592 
MaybeActivateByUserGesture(nsIPrincipal * aPrincipal)11593 void nsIDocument::MaybeActivateByUserGesture(nsIPrincipal* aPrincipal) {
11594   bool isEqual = false;
11595   nsresult rv = aPrincipal->Equals(NodePrincipal(), &isEqual);
11596   if (NS_WARN_IF(NS_FAILED(rv))) {
11597     return;
11598   }
11599 
11600   // If a child frame is actived, it would always activate the top frame and its
11601   // parent frames which has same priciple.
11602   if (isEqual || IsTopLevelContentDocument()) {
11603     ActivateByUserGesture();
11604   }
11605 }
11606 
ActivateByUserGesture()11607 void nsIDocument::ActivateByUserGesture() {
11608   if (mUserHasActivatedInteraction) {
11609     return;
11610   }
11611 
11612   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
11613           ("Document %p has been activated by user.", this));
11614   mUserHasActivatedInteraction = true;
11615 }
11616 
HasBeenUserActivated()11617 bool nsIDocument::HasBeenUserActivated() {
11618   if (!mUserHasActivatedInteraction) {
11619     // If one of its parent on the parent chain has been activated and has same
11620     // principal, then this child would also be treated as activated.
11621     nsIDocument* parent =
11622         GetFirstParentDocumentWithSamePrincipal(NodePrincipal());
11623     if (parent) {
11624       mUserHasActivatedInteraction = parent->HasBeenUserActivated();
11625     }
11626   }
11627 
11628   return mUserHasActivatedInteraction;
11629 }
11630 
GetFirstParentDocumentWithSamePrincipal(nsIPrincipal * aPrincipal)11631 nsIDocument* nsIDocument::GetFirstParentDocumentWithSamePrincipal(
11632     nsIPrincipal* aPrincipal) {
11633   MOZ_ASSERT(aPrincipal);
11634   nsIDocument* parent = GetSameTypeParentDocument();
11635   while (parent) {
11636     bool isEqual = false;
11637     nsresult rv = aPrincipal->Equals(parent->NodePrincipal(), &isEqual);
11638     if (NS_WARN_IF(NS_FAILED(rv))) {
11639       return nullptr;
11640     }
11641 
11642     if (isEqual) {
11643       return parent;
11644     }
11645     parent = parent->GetSameTypeParentDocument();
11646   }
11647   MOZ_ASSERT(!parent);
11648   return nullptr;
11649 }
11650 
GetSameTypeParentDocument()11651 nsIDocument* nsIDocument::GetSameTypeParentDocument() {
11652   nsCOMPtr<nsIDocShellTreeItem> current = GetDocShell();
11653   if (!current) {
11654     return nullptr;
11655   }
11656 
11657   nsCOMPtr<nsIDocShellTreeItem> parent;
11658   current->GetSameTypeParent(getter_AddRefs(parent));
11659   if (!parent) {
11660     return nullptr;
11661   }
11662 
11663   return parent->GetDocument();
11664 }
11665 
11666 /**
11667  * Retrieves the classification of the Flash plugins in the document based on
11668  * the classification lists. We perform AsyncInitFlashClassification on
11669  * StartDocumentLoad() and the result may not be initialized when this function
11670  * gets called. In that case, We can only unfortunately have a blocking wait.
11671  *
11672  * For more information, see
11673  * toolkit/components/url-classifier/flash-block-lists.rst
11674  */
PrincipalFlashClassification()11675 FlashClassification nsDocument::PrincipalFlashClassification() {
11676   MOZ_ASSERT(mPrincipalFlashClassifier);
11677   return mPrincipalFlashClassifier->ClassifyMaybeSync(GetPrincipal(),
11678                                                       IsThirdParty());
11679 }
11680 
11681 /**
11682  * Helper function for |nsDocument::PrincipalFlashClassification|
11683  *
11684  * Adds a table name string to a table list (a comma separated string). The
11685  * table will not be added if the name is an empty string.
11686  */
MaybeAddTableToTableList(const nsACString & aTableNames,nsACString & aTableList)11687 static void MaybeAddTableToTableList(const nsACString& aTableNames,
11688                                      nsACString& aTableList) {
11689   if (aTableNames.IsEmpty()) {
11690     return;
11691   }
11692   if (!aTableList.IsEmpty()) {
11693     aTableList.AppendLiteral(",");
11694   }
11695   aTableList.Append(aTableNames);
11696 }
11697 
11698 /**
11699  * Helper function for |nsDocument::PrincipalFlashClassification|
11700  *
11701  * Takes an array of table names and a comma separated list of table names
11702  * Returns |true| if any table name in the array matches a table name in the
11703  * comma separated list.
11704  */
ArrayContainsTable(const nsTArray<nsCString> & aTableArray,const nsACString & aTableNames)11705 static bool ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
11706                                const nsACString& aTableNames) {
11707   for (const nsCString& table : aTableArray) {
11708     // This check is sufficient because table names cannot contain commas and
11709     // cannot contain another existing table name.
11710     if (FindInReadable(table, aTableNames)) {
11711       return true;
11712     }
11713   }
11714   return false;
11715 }
11716 
11717 namespace {
11718 
11719 // An object to store all preferences we need for flash blocking feature.
11720 struct PrefStore {
PrefStore__anon937f70ae0911::PrefStore11721   PrefStore() {
11722     Preferences::AddBoolVarCache(&mFlashBlockEnabled,
11723                                  "plugins.flashBlock.enabled");
11724     Preferences::AddBoolVarCache(&mPluginsHttpOnly, "plugins.http_https_only");
11725 
11726     // We only need to register string-typed preferences.
11727     Preferences::RegisterCallback(UpdateStringPrefs,
11728                                   "urlclassifier.flashAllowTable", this);
11729     Preferences::RegisterCallback(UpdateStringPrefs,
11730                                   "urlclassifier.flashAllowExceptTable", this);
11731     Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashTable",
11732                                   this);
11733     Preferences::RegisterCallback(UpdateStringPrefs,
11734                                   "urlclassifier.flashExceptTable", this);
11735     Preferences::RegisterCallback(UpdateStringPrefs,
11736                                   "urlclassifier.flashSubDocTable", this);
11737     Preferences::RegisterCallback(UpdateStringPrefs,
11738                                   "urlclassifier.flashSubDocExceptTable", this);
11739 
11740     UpdateStringPrefs();
11741   }
11742 
~PrefStore__anon937f70ae0911::PrefStore11743   ~PrefStore() {
11744     Preferences::UnregisterCallback(UpdateStringPrefs,
11745                                     "urlclassifier.flashAllowTable", this);
11746     Preferences::UnregisterCallback(
11747         UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
11748     Preferences::UnregisterCallback(UpdateStringPrefs,
11749                                     "urlclassifier.flashTable", this);
11750     Preferences::UnregisterCallback(UpdateStringPrefs,
11751                                     "urlclassifier.flashExceptTable", this);
11752     Preferences::UnregisterCallback(UpdateStringPrefs,
11753                                     "urlclassifier.flashSubDocTable", this);
11754     Preferences::UnregisterCallback(
11755         UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
11756   }
11757 
UpdateStringPrefs__anon937f70ae0911::PrefStore11758   void UpdateStringPrefs() {
11759     Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
11760     Preferences::GetCString("urlclassifier.flashAllowExceptTable",
11761                             mAllowExceptionsTables);
11762     Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
11763     Preferences::GetCString("urlclassifier.flashExceptTable",
11764                             mDenyExceptionsTables);
11765     Preferences::GetCString("urlclassifier.flashSubDocTable",
11766                             mSubDocDenyTables);
11767     Preferences::GetCString("urlclassifier.flashSubDocExceptTable",
11768                             mSubDocDenyExceptionsTables);
11769   }
11770 
UpdateStringPrefs__anon937f70ae0911::PrefStore11771   static void UpdateStringPrefs(const char*, void* aClosure) {
11772     static_cast<PrefStore*>(aClosure)->UpdateStringPrefs();
11773   }
11774 
11775   bool mFlashBlockEnabled;
11776   bool mPluginsHttpOnly;
11777 
11778   nsCString mAllowTables;
11779   nsCString mAllowExceptionsTables;
11780   nsCString mDenyTables;
11781   nsCString mDenyExceptionsTables;
11782   nsCString mSubDocDenyTables;
11783   nsCString mSubDocDenyExceptionsTables;
11784 };
11785 
GetPrefStore()11786 static const PrefStore& GetPrefStore() {
11787   static UniquePtr<PrefStore> sPrefStore;
11788   if (!sPrefStore) {
11789     sPrefStore.reset(new PrefStore());
11790     ClearOnShutdown(&sPrefStore);
11791   }
11792   return *sPrefStore;
11793 }
11794 
11795 }  // end of unnamed namespace.
11796 
11797 ////////////////////////////////////////////////////////////////////
11798 // PrincipalFlashClassifier implementation.
11799 
NS_IMPL_ISUPPORTS(PrincipalFlashClassifier,nsIURIClassifierCallback)11800 NS_IMPL_ISUPPORTS(PrincipalFlashClassifier, nsIURIClassifierCallback)
11801 
11802 PrincipalFlashClassifier::PrincipalFlashClassifier() { Reset(); }
11803 
Reset()11804 void PrincipalFlashClassifier::Reset() {
11805   mAsyncClassified = false;
11806   mMatchedTables.Clear();
11807   mResult = FlashClassification::Unclassified;
11808 }
11809 
GetClassificationTables(bool aIsThirdParty,nsACString & aTables)11810 void PrincipalFlashClassifier::GetClassificationTables(bool aIsThirdParty,
11811                                                        nsACString& aTables) {
11812   aTables.Truncate();
11813   auto& prefs = GetPrefStore();
11814 
11815   MaybeAddTableToTableList(prefs.mAllowTables, aTables);
11816   MaybeAddTableToTableList(prefs.mAllowExceptionsTables, aTables);
11817   MaybeAddTableToTableList(prefs.mDenyTables, aTables);
11818   MaybeAddTableToTableList(prefs.mDenyExceptionsTables, aTables);
11819 
11820   if (aIsThirdParty) {
11821     MaybeAddTableToTableList(prefs.mSubDocDenyTables, aTables);
11822     MaybeAddTableToTableList(prefs.mSubDocDenyExceptionsTables, aTables);
11823   }
11824 }
11825 
EnsureUriClassifier()11826 bool PrincipalFlashClassifier::EnsureUriClassifier() {
11827   if (!mUriClassifier) {
11828     mUriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
11829   }
11830 
11831   return !!mUriClassifier;
11832 }
11833 
ClassifyMaybeSync(nsIPrincipal * aPrincipal,bool aIsThirdParty)11834 FlashClassification PrincipalFlashClassifier::ClassifyMaybeSync(
11835     nsIPrincipal* aPrincipal, bool aIsThirdParty) {
11836   if (FlashClassification::Unclassified != mResult) {
11837     // We already have the result. Just return it.
11838     return mResult;
11839   }
11840 
11841   // TODO: Bug 1342333 - Entirely remove the use of the sync API
11842   // (ClassifyLocalWithTables).
11843   if (!mAsyncClassified) {
11844     //
11845     // We may
11846     //   1) have called AsyncClassifyLocalWithTables but OnClassifyComplete
11847     //      hasn't been called.
11848     //   2) haven't even called AsyncClassifyLocalWithTables.
11849     //
11850     // In both cases we need to do the synchronous classification as the
11851     // fallback.
11852     //
11853 
11854     if (!EnsureUriClassifier()) {
11855       return FlashClassification::Denied;
11856     }
11857     mResult = CheckIfClassifyNeeded(aPrincipal);
11858     if (FlashClassification::Unclassified != mResult) {
11859       return mResult;
11860     }
11861 
11862     nsresult rv;
11863     nsAutoCString classificationTables;
11864     GetClassificationTables(aIsThirdParty, classificationTables);
11865 
11866     if (!mClassificationURI) {
11867       rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
11868       if (NS_FAILED(rv) || !mClassificationURI) {
11869         mResult = FlashClassification::Denied;
11870         return mResult;
11871       }
11872     }
11873 
11874     rv = mUriClassifier->ClassifyLocalWithTables(
11875         mClassificationURI, classificationTables, mMatchedTables);
11876     if (NS_WARN_IF(NS_FAILED(rv))) {
11877       if (rv == NS_ERROR_MALFORMED_URI) {
11878         // This means that the URI had no hostname (ex: file://doc.html). In
11879         // this case, we allow the default (Unknown plugin) behavior.
11880         mResult = FlashClassification::Unknown;
11881       } else {
11882         mResult = FlashClassification::Denied;
11883       }
11884       return mResult;
11885     }
11886   }
11887 
11888   // Resolve the result based on mMatchedTables and aIsThirdParty.
11889   mResult = Resolve(aIsThirdParty);
11890   MOZ_ASSERT(FlashClassification::Unclassified != mResult);
11891 
11892   // The subsequent call of Result() will return the resolved result
11893   // and never reach here until Reset() is called.
11894   return mResult;
11895 }
11896 
OnClassifyComplete(nsresult,const nsACString & aLists,const nsACString &,const nsACString &)11897 /*virtual*/ nsresult PrincipalFlashClassifier::OnClassifyComplete(
11898     nsresult /*aErrorCode*/,
11899     const nsACString& aLists,  // Only this matters.
11900     const nsACString& /*aProvider*/, const nsACString& /*aPrefix*/) {
11901   mAsyncClassified = true;
11902 
11903   if (FlashClassification::Unclassified != mResult) {
11904     // Result() has been called prior to this callback.
11905     return NS_OK;
11906   }
11907 
11908   // TODO: Bug 1364804 - We should use a callback type which notifies
11909   // the result as a string array rather than a formatted string.
11910 
11911   // We only populate the matched list without resolving the classification
11912   // result because we are not sure if the parent doc has been properly set.
11913   // We also parse the comma-separated tables to array. (the code is copied
11914   // from Classifier::SplitTables.)
11915   nsACString::const_iterator begin, iter, end;
11916   aLists.BeginReading(begin);
11917   aLists.EndReading(end);
11918   while (begin != end) {
11919     iter = begin;
11920     FindCharInReadable(',', iter, end);
11921     nsDependentCSubstring table = Substring(begin, iter);
11922     if (!table.IsEmpty()) {
11923       mMatchedTables.AppendElement(Substring(begin, iter));
11924     }
11925     begin = iter;
11926     if (begin != end) {
11927       begin++;
11928     }
11929   }
11930 
11931   return NS_OK;
11932 }
11933 
11934 // We resolve the classification result based on aIsThirdParty
11935 // and the matched tables we got ealier on (via either sync or async API).
Resolve(bool aIsThirdParty)11936 FlashClassification PrincipalFlashClassifier::Resolve(bool aIsThirdParty) {
11937   MOZ_ASSERT(FlashClassification::Unclassified == mResult,
11938              "We already have resolved classification result.");
11939 
11940   if (mMatchedTables.IsEmpty()) {
11941     return FlashClassification::Unknown;
11942   }
11943 
11944   auto& prefs = GetPrefStore();
11945   if (ArrayContainsTable(mMatchedTables, prefs.mDenyTables) &&
11946       !ArrayContainsTable(mMatchedTables, prefs.mDenyExceptionsTables)) {
11947     return FlashClassification::Denied;
11948   } else if (ArrayContainsTable(mMatchedTables, prefs.mAllowTables) &&
11949              !ArrayContainsTable(mMatchedTables,
11950                                  prefs.mAllowExceptionsTables)) {
11951     return FlashClassification::Allowed;
11952   }
11953 
11954   if (aIsThirdParty &&
11955       ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyTables) &&
11956       !ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyExceptionsTables)) {
11957     return FlashClassification::Denied;
11958   }
11959 
11960   return FlashClassification::Unknown;
11961 }
11962 
AsyncClassify(nsIPrincipal * aPrincipal)11963 void PrincipalFlashClassifier::AsyncClassify(nsIPrincipal* aPrincipal) {
11964   MOZ_ASSERT(FlashClassification::Unclassified == mResult,
11965              "The old classification result should be reset first.");
11966   Reset();
11967   mResult = AsyncClassifyInternal(aPrincipal);
11968 }
11969 
CheckIfClassifyNeeded(nsIPrincipal * aPrincipal)11970 FlashClassification PrincipalFlashClassifier::CheckIfClassifyNeeded(
11971     nsIPrincipal* aPrincipal) {
11972   nsresult rv;
11973 
11974   auto& prefs = GetPrefStore();
11975 
11976   // If neither pref is on, skip the null-principal and principal URI checks.
11977   if (prefs.mPluginsHttpOnly && !prefs.mFlashBlockEnabled) {
11978     return FlashClassification::Unknown;
11979   }
11980 
11981   nsCOMPtr<nsIPrincipal> principal = aPrincipal;
11982   if (principal->GetIsNullPrincipal()) {
11983     return FlashClassification::Denied;
11984   }
11985 
11986   nsCOMPtr<nsIURI> classificationURI;
11987   rv = principal->GetURI(getter_AddRefs(classificationURI));
11988   if (NS_FAILED(rv) || !classificationURI) {
11989     return FlashClassification::Denied;
11990   }
11991 
11992   if (prefs.mPluginsHttpOnly) {
11993     // Only allow plugins for documents from an HTTP/HTTPS origin. This should
11994     // allow dependent data: URIs to load plugins, but not:
11995     // * chrome documents
11996     // * "bare" data: loads
11997     // * FTP/gopher/file
11998     nsAutoCString scheme;
11999     rv = classificationURI->GetScheme(scheme);
12000     if (NS_WARN_IF(NS_FAILED(rv)) ||
12001         !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) {
12002       return FlashClassification::Denied;
12003     }
12004   }
12005 
12006   // If flash blocking is disabled, it is equivalent to all sites being
12007   // on neither list.
12008   if (!prefs.mFlashBlockEnabled) {
12009     return FlashClassification::Unknown;
12010   }
12011 
12012   return FlashClassification::Unclassified;
12013 }
12014 
12015 // Using nsIURIClassifier.asyncClassifyLocalWithTables to do classification
12016 // against the flash related tables based on the given principal.
AsyncClassifyInternal(nsIPrincipal * aPrincipal)12017 FlashClassification PrincipalFlashClassifier::AsyncClassifyInternal(
12018     nsIPrincipal* aPrincipal) {
12019   auto result = CheckIfClassifyNeeded(aPrincipal);
12020   if (FlashClassification::Unclassified != result) {
12021     return result;
12022   }
12023 
12024   // We haven't been able to decide if it's a third party document
12025   // since determining if a document is third-party may depend on its
12026   // parent document. At the time we call AsyncClassifyInternal
12027   // (i.e. StartDocumentLoad) the parent document may not have been
12028   // set. As a result, we wait until Resolve() to be called to
12029   // take "is third party" into account. At this point, we just assume
12030   // it's third-party to include every list.
12031   nsAutoCString tables;
12032   GetClassificationTables(true, tables);
12033 
12034   if (tables.IsEmpty()) {
12035     return FlashClassification::Unknown;
12036   }
12037 
12038   if (!EnsureUriClassifier()) {
12039     return FlashClassification::Denied;
12040   }
12041 
12042   nsresult rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
12043   if (NS_FAILED(rv) || !mClassificationURI) {
12044     return FlashClassification::Denied;
12045   }
12046 
12047   rv = mUriClassifier->AsyncClassifyLocalWithTables(mClassificationURI, tables,
12048                                                     this);
12049 
12050   if (NS_FAILED(rv)) {
12051     if (rv == NS_ERROR_MALFORMED_URI) {
12052       // This means that the URI had no hostname (ex: file://doc.html). In this
12053       // case, we allow the default (Unknown plugin) behavior.
12054       return FlashClassification::Unknown;
12055     } else {
12056       return FlashClassification::Denied;
12057     }
12058   }
12059 
12060   return FlashClassification::Unclassified;
12061 }
12062 
ComputeFlashClassification()12063 FlashClassification nsDocument::ComputeFlashClassification() {
12064   nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
12065   if (!current) {
12066     return FlashClassification::Denied;
12067   }
12068   nsCOMPtr<nsIDocShellTreeItem> parent;
12069   DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
12070   MOZ_ASSERT(NS_SUCCEEDED(rv),
12071              "nsIDocShellTreeItem::GetSameTypeParent should never fail");
12072 
12073   bool isTopLevel = !parent;
12074   FlashClassification classification;
12075   if (isTopLevel) {
12076     classification = PrincipalFlashClassification();
12077   } else {
12078     nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
12079     if (!parentDocument) {
12080       return FlashClassification::Denied;
12081     }
12082     FlashClassification parentClassification =
12083         parentDocument->DocumentFlashClassification();
12084 
12085     if (parentClassification == FlashClassification::Denied) {
12086       classification = FlashClassification::Denied;
12087     } else {
12088       classification = PrincipalFlashClassification();
12089 
12090       // Allow unknown children to inherit allowed status from parent, but
12091       // do not allow denied children to do so.
12092       if (classification == FlashClassification::Unknown &&
12093           parentClassification == FlashClassification::Allowed) {
12094         classification = FlashClassification::Allowed;
12095       }
12096     }
12097   }
12098 
12099   return classification;
12100 }
12101 
12102 /**
12103  * Retrieves the classification of plugins in this document. This is dependent
12104  * on the classification of this document and all parent documents.
12105  * This function is infallible - It must return some classification that
12106  * callers can act on.
12107  *
12108  * This function will NOT return FlashClassification::Unclassified
12109  */
DocumentFlashClassification()12110 FlashClassification nsDocument::DocumentFlashClassification() {
12111   if (mFlashClassification == FlashClassification::Unclassified) {
12112     FlashClassification result = ComputeFlashClassification();
12113     mFlashClassification = result;
12114     MOZ_ASSERT(
12115         result != FlashClassification::Unclassified,
12116         "nsDocument::GetPluginClassification should never return Unclassified");
12117   }
12118 
12119   return mFlashClassification;
12120 }
12121 
12122 /**
12123  * Initializes |mIsThirdParty| if necessary and returns its value. The value
12124  * returned represents whether this document should be considered Third-Party.
12125  *
12126  * A top-level document cannot be a considered Third-Party; only subdocuments
12127  * may. For a subdocument to be considered Third-Party, it must meet ANY ONE
12128  * of the following requirements:
12129  *  - The document's parent is Third-Party
12130  *  - The document has a different scheme (http/https) than its parent document
12131  *  - The document's domain and subdomain do not match those of its parent
12132  *    document.
12133  *
12134  * If there is an error in determining whether the document is Third-Party,
12135  * it will be assumed to be Third-Party for security reasons.
12136  */
IsThirdParty()12137 bool nsDocument::IsThirdParty() {
12138   if (mIsThirdParty.isSome()) {
12139     return mIsThirdParty.value();
12140   }
12141 
12142   nsCOMPtr<nsIDocShellTreeItem> docshell = this->GetDocShell();
12143   if (!docshell) {
12144     mIsThirdParty.emplace(true);
12145     return mIsThirdParty.value();
12146   }
12147 
12148   nsCOMPtr<nsIDocShellTreeItem> parent;
12149   nsresult rv = docshell->GetSameTypeParent(getter_AddRefs(parent));
12150   MOZ_ASSERT(NS_SUCCEEDED(rv),
12151              "nsIDocShellTreeItem::GetSameTypeParent should never fail");
12152   bool isTopLevel = !parent;
12153 
12154   if (isTopLevel) {
12155     mIsThirdParty.emplace(false);
12156     return mIsThirdParty.value();
12157   }
12158 
12159   nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
12160   if (!parentDocument) {
12161     // Failure
12162     mIsThirdParty.emplace(true);
12163     return mIsThirdParty.value();
12164   }
12165 
12166   if (parentDocument->IsThirdParty()) {
12167     mIsThirdParty.emplace(true);
12168     return mIsThirdParty.value();
12169   }
12170 
12171   nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
12172   nsCOMPtr<nsIScriptObjectPrincipal> sop =
12173       do_QueryInterface(parentDocument, &rv);
12174   if (NS_WARN_IF(NS_FAILED(rv) || !sop)) {
12175     // Failure
12176     mIsThirdParty.emplace(true);
12177     return mIsThirdParty.value();
12178   }
12179   nsCOMPtr<nsIPrincipal> parentPrincipal = sop->GetPrincipal();
12180 
12181   bool principalsMatch = false;
12182   rv = principal->Equals(parentPrincipal, &principalsMatch);
12183 
12184   if (NS_WARN_IF(NS_FAILED(rv))) {
12185     // Failure
12186     mIsThirdParty.emplace(true);
12187     return mIsThirdParty.value();
12188   }
12189 
12190   if (!principalsMatch) {
12191     mIsThirdParty.emplace(true);
12192     return mIsThirdParty.value();
12193   }
12194 
12195   // Fall-through. Document is not a Third-Party Document.
12196   mIsThirdParty.emplace(false);
12197   return mIsThirdParty.value();
12198 }
12199 
IsScopedStyleEnabled()12200 bool nsIDocument::IsScopedStyleEnabled() {
12201   if (mIsScopedStyleEnabled == eScopedStyle_Unknown) {
12202     mIsScopedStyleEnabled = nsContentUtils::IsChromeDoc(this) ||
12203                                     nsContentUtils::IsScopedStylePrefEnabled()
12204                                 ? eScopedStyle_Enabled
12205                                 : eScopedStyle_Disabled;
12206   }
12207   return mIsScopedStyleEnabled == eScopedStyle_Enabled;
12208 }
12209 
ClearStaleServoData()12210 void nsIDocument::ClearStaleServoData() {
12211   DocumentStyleRootIterator iter(this);
12212   while (Element* root = iter.GetNextStyleRoot()) {
12213     ServoRestyleManager::ClearServoDataFromSubtree(root);
12214   }
12215 }
12216 
GetSelection(ErrorResult & aRv)12217 Selection* nsIDocument::GetSelection(ErrorResult& aRv) {
12218   nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
12219   if (!window) {
12220     return nullptr;
12221   }
12222 
12223   if (!window->IsCurrentInnerWindow()) {
12224     return nullptr;
12225   }
12226 
12227   return nsGlobalWindowInner::Cast(window)->GetSelection(aRv);
12228 }
12229 
RecordNavigationTiming(ReadyState aReadyState)12230 void nsDocument::RecordNavigationTiming(ReadyState aReadyState) {
12231   if (!XRE_IsContentProcess()) {
12232     return;
12233   }
12234   if (!IsTopLevelContentDocument()) {
12235     return;
12236   }
12237   // If we dont have the timing yet (mostly because the doc is still loading),
12238   // get it from docshell.
12239   RefPtr<nsDOMNavigationTiming> timing = mTiming;
12240   if (!timing) {
12241     if (!mDocumentContainer) {
12242       return;
12243     }
12244     timing = mDocumentContainer->GetNavigationTiming();
12245     if (!timing) {
12246       return;
12247     }
12248   }
12249   TimeStamp startTime = timing->GetNavigationStartTimeStamp();
12250   switch (aReadyState) {
12251     case READYSTATE_LOADING:
12252       if (!mDOMLoadingSet) {
12253         Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS,
12254                                        startTime);
12255         mDOMLoadingSet = true;
12256       }
12257       break;
12258     case READYSTATE_INTERACTIVE:
12259       if (!mDOMInteractiveSet) {
12260         Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS,
12261                                        startTime);
12262         mDOMInteractiveSet = true;
12263       }
12264       break;
12265     case READYSTATE_COMPLETE:
12266       if (!mDOMCompleteSet) {
12267         Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS,
12268                                        startTime);
12269         mDOMCompleteSet = true;
12270       }
12271       break;
12272     default:
12273       NS_WARNING("Unexpected ReadyState value");
12274       break;
12275   }
12276 }
12277 
ModuleScriptsEnabled()12278 bool nsIDocument::ModuleScriptsEnabled() {
12279   static bool sEnabledForContent = false;
12280   static bool sCachedPref = false;
12281   if (!sCachedPref) {
12282     sCachedPref = true;
12283     Preferences::AddBoolVarCache(&sEnabledForContent,
12284                                  "dom.moduleScripts.enabled", false);
12285   }
12286 
12287   return nsContentUtils::IsChromeDoc(this) || sEnabledForContent;
12288 }
12289