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