Line data Source code
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/CSSEnabledState.h"
19 : #include "mozilla/DebugOnly.h"
20 : #include "mozilla/EffectSet.h"
21 : #include "mozilla/EnumSet.h"
22 : #include "mozilla/IntegerRange.h"
23 : #include "mozilla/MemoryReporting.h"
24 : #include "mozilla/Likely.h"
25 : #include "mozilla/PresShell.h"
26 : #include "mozilla/URLExtraData.h"
27 : #include <algorithm>
28 :
29 : #include "mozilla/Logging.h"
30 : #include "plstr.h"
31 : #include "mozilla/Sprintf.h"
32 :
33 : #include "mozilla/Telemetry.h"
34 : #include "nsIInterfaceRequestor.h"
35 : #include "nsIInterfaceRequestorUtils.h"
36 : #include "nsILoadContext.h"
37 : #include "nsITextControlFrame.h"
38 : #include "nsNumberControlFrame.h"
39 : #include "nsUnicharUtils.h"
40 : #include "nsContentList.h"
41 : #include "nsCSSPseudoElements.h"
42 : #include "nsIObserver.h"
43 : #include "nsIBaseWindow.h"
44 : #include "mozilla/css/Loader.h"
45 : #include "mozilla/css/ImageLoader.h"
46 : #include "nsDocShell.h"
47 : #include "nsDocShellLoadTypes.h"
48 : #include "nsIDocShellTreeItem.h"
49 : #include "nsCOMArray.h"
50 : #include "nsQueryObject.h"
51 : #include "mozilla/Services.h"
52 : #include "nsScreen.h"
53 : #include "ChildIterator.h"
54 :
55 : #include "mozilla/AsyncEventDispatcher.h"
56 : #include "mozilla/BasicEvents.h"
57 : #include "mozilla/EventListenerManager.h"
58 : #include "mozilla/EventStateManager.h"
59 :
60 : #include "mozilla/dom/Attr.h"
61 : #include "mozilla/dom/BindingDeclarations.h"
62 : #include "mozilla/dom/Element.h"
63 : #include "mozilla/dom/Event.h"
64 : #include "mozilla/dom/FramingChecker.h"
65 : #include "mozilla/dom/HTMLSharedElement.h"
66 : #include "nsGenericHTMLElement.h"
67 : #include "mozilla/dom/CDATASection.h"
68 : #include "mozilla/dom/ProcessingInstruction.h"
69 : #include "nsDOMString.h"
70 : #include "nsNodeUtils.h"
71 : #include "nsLayoutUtils.h" // for GetFrameForPoint
72 : #include "nsIFrame.h"
73 : #include "nsITabChild.h"
74 :
75 : #include "nsRange.h"
76 : #include "mozilla/dom/DocumentType.h"
77 : #include "mozilla/dom/NodeIterator.h"
78 : #include "mozilla/dom/Promise.h"
79 : #include "mozilla/dom/PromiseNativeHandler.h"
80 : #include "mozilla/dom/TreeWalker.h"
81 :
82 : #include "nsIServiceManager.h"
83 : #include "mozilla/dom/ServiceWorkerManager.h"
84 : #include "imgLoader.h"
85 :
86 : #include "nsAboutProtocolUtils.h"
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 "nsFocusManager.h"
116 :
117 : // for radio group stuff
118 : #include "nsIRadioVisitor.h"
119 : #include "nsIFormControl.h"
120 :
121 : #include "nsBidiUtils.h"
122 :
123 : #include "nsContentCreatorFunctions.h"
124 :
125 : #include "nsIScriptContext.h"
126 : #include "nsBindingManager.h"
127 : #include "nsHTMLDocument.h"
128 : #include "nsIRequest.h"
129 : #include "mozilla/dom/BlobURLProtocolHandler.h"
130 :
131 : #include "nsCharsetSource.h"
132 : #include "nsIParser.h"
133 : #include "nsIContentSink.h"
134 :
135 : #include "mozilla/EventDispatcher.h"
136 : #include "mozilla/EventStates.h"
137 : #include "mozilla/InternalMutationEvent.h"
138 : #include "nsDOMCID.h"
139 :
140 : #include "jsapi.h"
141 : #include "nsIXPConnect.h"
142 : #include "xpcpublic.h"
143 : #include "nsCCUncollectableMarker.h"
144 : #include "nsIContentPolicy.h"
145 : #include "nsContentPolicyUtils.h"
146 : #include "nsICategoryManager.h"
147 : #include "nsIDocumentLoaderFactory.h"
148 : #include "nsIDocumentLoader.h"
149 : #include "nsIContentViewer.h"
150 : #include "nsIXMLContentSink.h"
151 : #include "nsIPrompt.h"
152 : #include "nsIPropertyBag2.h"
153 : #include "mozilla/dom/PageTransitionEvent.h"
154 : #include "mozilla/dom/StyleRuleChangeEvent.h"
155 : #include "mozilla/dom/StyleSheetChangeEvent.h"
156 : #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
157 : #include "nsJSUtils.h"
158 : #include "nsFrameLoader.h"
159 : #include "nsEscape.h"
160 : #include "nsObjectLoadingContent.h"
161 : #include "nsHtml5TreeOpExecutor.h"
162 : #include "mozilla/dom/HTMLFormElement.h"
163 : #include "mozilla/dom/HTMLLinkElement.h"
164 : #include "mozilla/dom/HTMLMediaElement.h"
165 : #include "mozilla/dom/HTMLIFrameElement.h"
166 : #include "mozilla/dom/HTMLImageElement.h"
167 : #include "mozilla/dom/HTMLTextAreaElement.h"
168 : #include "mozilla/dom/MediaSource.h"
169 :
170 : #include "mozAutoDocUpdate.h"
171 : #include "nsGlobalWindow.h"
172 : #include "mozilla/Encoding.h"
173 : #include "nsDOMNavigationTiming.h"
174 :
175 : #include "nsSMILAnimationController.h"
176 : #include "imgIContainer.h"
177 : #include "nsSVGUtils.h"
178 :
179 : #include "nsRefreshDriver.h"
180 :
181 : // FOR CSP (autogenerated by xpidl)
182 : #include "nsIContentSecurityPolicy.h"
183 : #include "mozilla/dom/nsCSPContext.h"
184 : #include "mozilla/dom/nsCSPService.h"
185 : #include "mozilla/dom/nsCSPUtils.h"
186 : #include "nsHTMLStyleSheet.h"
187 : #include "nsHTMLCSSStyleSheet.h"
188 : #include "mozilla/dom/DOMImplementation.h"
189 : #include "mozilla/dom/ShadowRoot.h"
190 : #include "mozilla/dom/Comment.h"
191 : #include "nsTextNode.h"
192 : #include "mozilla/dom/Link.h"
193 : #include "mozilla/dom/HTMLCollectionBinding.h"
194 : #include "mozilla/dom/HTMLElementBinding.h"
195 : #include "nsXULAppAPI.h"
196 : #include "mozilla/dom/Touch.h"
197 : #include "mozilla/dom/TouchEvent.h"
198 :
199 : #include "mozilla/Preferences.h"
200 :
201 : #include "imgILoader.h"
202 : #include "imgRequestProxy.h"
203 : #include "nsWrapperCacheInlines.h"
204 : #include "nsSandboxFlags.h"
205 : #include "mozilla/dom/AnimatableBinding.h"
206 : #include "mozilla/dom/AnonymousContent.h"
207 : #include "mozilla/dom/BindingUtils.h"
208 : #include "mozilla/dom/ClientInfo.h"
209 : #include "mozilla/dom/ClientState.h"
210 : #include "mozilla/dom/DocumentFragment.h"
211 : #include "mozilla/dom/DocumentTimeline.h"
212 : #include "mozilla/dom/Event.h"
213 : #include "mozilla/dom/HTMLBodyElement.h"
214 : #include "mozilla/dom/HTMLInputElement.h"
215 : #include "mozilla/dom/ImageTracker.h"
216 : #include "mozilla/dom/MediaQueryList.h"
217 : #include "mozilla/dom/NodeFilterBinding.h"
218 : #include "mozilla/OwningNonNull.h"
219 : #include "mozilla/dom/TabChild.h"
220 : #include "mozilla/dom/WebComponentsBinding.h"
221 : #include "mozilla/dom/CustomElementRegistryBinding.h"
222 : #include "mozilla/dom/CustomElementRegistry.h"
223 : #include "mozilla/dom/ServiceWorkerDescriptor.h"
224 : #include "mozilla/dom/TimeoutManager.h"
225 : #include "mozilla/ExtensionPolicyService.h"
226 : #include "nsFrame.h"
227 : #include "nsDOMCaretPosition.h"
228 : #include "nsViewportInfo.h"
229 : #include "mozilla/StaticPtr.h"
230 : #include "nsITextControlElement.h"
231 : #include "nsIEditor.h"
232 : #include "nsIHttpChannelInternal.h"
233 : #include "nsISecurityConsoleMessage.h"
234 : #include "nsCharSeparatedTokenizer.h"
235 : #include "mozilla/dom/XPathEvaluator.h"
236 : #include "mozilla/dom/XPathNSResolverBinding.h"
237 : #include "mozilla/dom/XPathResult.h"
238 : #include "nsIDocumentEncoder.h"
239 : #include "nsIDocumentActivity.h"
240 : #include "nsIStructuredCloneContainer.h"
241 : #include "nsIMutableArray.h"
242 : #include "mozilla/dom/DOMStringList.h"
243 : #include "nsWindowSizes.h"
244 : #include "mozilla/dom/Location.h"
245 : #include "mozilla/dom/FontFaceSet.h"
246 : #include "gfxPrefs.h"
247 : #include "nsISupportsPrimitives.h"
248 : #include "mozilla/ServoStyleSet.h"
249 : #include "mozilla/StyleSheet.h"
250 : #include "mozilla/StyleSheetInlines.h"
251 : #include "mozilla/dom/SVGDocument.h"
252 : #include "mozilla/dom/SVGSVGElement.h"
253 : #include "mozilla/dom/DocGroup.h"
254 : #include "mozilla/dom/TabGroup.h"
255 : #ifdef MOZ_XUL
256 : #include "mozilla/dom/ListBoxObject.h"
257 : #include "mozilla/dom/MenuBoxObject.h"
258 : #include "mozilla/dom/ScrollBoxObject.h"
259 : #include "mozilla/dom/TreeBoxObject.h"
260 : #include "nsIXULWindow.h"
261 : #include "nsIDocShellTreeOwner.h"
262 : #endif
263 : #include "nsIPresShellInlines.h"
264 :
265 : #include "mozilla/DocLoadingTimelineMarker.h"
266 :
267 : #include "nsISpeculativeConnect.h"
268 :
269 : #include "mozilla/MediaManager.h"
270 :
271 : #include "nsIURIClassifier.h"
272 : #include "nsIURIMutator.h"
273 : #include "mozilla/DocumentStyleRootIterator.h"
274 : #include "mozilla/RestyleManager.h"
275 : #include "mozilla/ClearOnShutdown.h"
276 : #include "nsHTMLTags.h"
277 :
278 : using namespace mozilla;
279 : using namespace mozilla::dom;
280 :
281 : typedef nsTArray<Link*> LinkArray;
282 :
283 : static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
284 : static LazyLogModule gCspPRLog("CSP");
285 : static LazyLogModule gUserInteractionPRLog("UserInteraction");
286 :
287 89 : static nsresult
288 : GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel)
289 178 : {
290 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
291 0 : if (httpChannel) {
292 0 : httpChannel.forget(aHttpChannel);
293 : return NS_OK;
294 : }
295 178 :
296 0 : nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
297 0 : if (!multipart) {
298 0 : *aHttpChannel = nullptr;
299 : return NS_OK;
300 : }
301 0 :
302 0 : nsCOMPtr<nsIChannel> baseChannel;
303 0 : nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
304 : if (NS_WARN_IF(NS_FAILED(rv))) {
305 : return rv;
306 : }
307 0 :
308 0 : httpChannel = do_QueryInterface(baseChannel);
309 : httpChannel.forget(aHttpChannel);
310 0 :
311 : return NS_OK;
312 : }
313 :
314 : ////////////////////////////////////////////////////////////////////
315 : // PrincipalFlashClassifier
316 :
317 : // Classify the flash based on the document principal.
318 : // The usage of this class is as follows:
319 : //
320 : // 1) Call AsyncClassify() as early as possible to asynchronously do
321 : // classification against all the flash blocking related tables
322 : // via nsIURIClassifier.asyncClassifyLocalWithTables.
323 : //
324 : // 2) At any time you need the classification result, call Result()
325 : // and it is guaranteed to give you the result. Note that you have
326 : // to specify "aIsThirdParty" to the function so please make sure
327 : // you can already correctly decide if the document is third-party.
328 : //
329 : // Behind the scenes, the sync classification API
330 : // (nsIURIClassifier.classifyLocalWithTable) may be called as a fallback to
331 : // synchronously get the result if the asyncClassifyLocalWithTables hasn't
332 : // been done yet.
333 : //
334 : // 3) You can call Result() as many times as you want and only the first time
335 : // it may unfortunately call the blocking sync API. The subsequent call
336 : // will just return the result that came out at the first time.
337 : //
338 : class PrincipalFlashClassifier final : public nsIURIClassifierCallback
339 : {
340 : public:
341 : NS_DECL_THREADSAFE_ISUPPORTS
342 : NS_DECL_NSIURICLASSIFIERCALLBACK
343 :
344 : PrincipalFlashClassifier();
345 :
346 : // Fire async classification based on the given principal.
347 : void AsyncClassify(nsIPrincipal* aPrincipal);
348 :
349 : // Would block if the result hasn't come out.
350 : mozilla::dom::FlashClassification ClassifyMaybeSync(nsIPrincipal* aPrincipal,
351 : bool aIsThirdParty);
352 :
353 0 : private:
354 : ~PrincipalFlashClassifier() = default;
355 :
356 : void Reset();
357 : bool EnsureUriClassifier();
358 : mozilla::dom::FlashClassification CheckIfClassifyNeeded(nsIPrincipal* aPrincipal);
359 : mozilla::dom::FlashClassification Resolve(bool aIsThirdParty);
360 : mozilla::dom::FlashClassification AsyncClassifyInternal(nsIPrincipal* aPrincipal);
361 : void GetClassificationTables(bool aIsThirdParty, nsACString& aTables);
362 :
363 : // For the fallback sync classification.
364 : nsCOMPtr<nsIURI> mClassificationURI;
365 :
366 : nsCOMPtr<nsIURIClassifier> mUriClassifier;
367 : bool mAsyncClassified;
368 : nsTArray<nsCString> mMatchedTables;
369 : mozilla::dom::FlashClassification mResult;
370 : };
371 :
372 :
373 : #define NAME_NOT_VALID ((nsSimpleContentList*)1)
374 0 :
375 0 : nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString& aKey)
376 0 : : mKey(aKey)
377 : {}
378 1645 :
379 0 : nsIdentifierMapEntry::nsIdentifierMapEntry(const nsIdentifierMapEntry::AtomOrString* aKey)
380 0 : : mKey(aKey ? *aKey : nullptr)
381 : {}
382 4806 :
383 0 : nsIdentifierMapEntry::~nsIdentifierMapEntry()
384 : {}
385 2292 :
386 0 : nsIdentifierMapEntry::nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther)
387 0 : : mKey(std::move(aOther.mKey))
388 0 : , mIdContentList(std::move(aOther.mIdContentList))
389 0 : , mNameContentList(std::move(aOther.mNameContentList))
390 0 : , mChangeCallbacks(std::move(aOther.mChangeCallbacks))
391 0 : , mImageElement(std::move(aOther.mImageElement))
392 : {}
393 :
394 15 : void
395 : nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
396 : {
397 15 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
398 0 : "mIdentifierMap mNameContentList");
399 : aCallback->NoteXPCOMChild(static_cast<nsINodeList*>(mNameContentList));
400 30 :
401 : if (mImageElement) {
402 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
403 0 : "mIdentifierMap mImageElement element");
404 0 : nsIContent* imageElement = mImageElement;
405 : aCallback->NoteXPCOMChild(imageElement);
406 15 : }
407 : }
408 :
409 111 : bool
410 : nsIdentifierMapEntry::IsEmpty()
411 555 : {
412 0 : return mIdContentList.IsEmpty() && !mNameContentList &&
413 : !mChangeCallbacks && !mImageElement;
414 : }
415 :
416 0 : bool
417 : nsIdentifierMapEntry::HasNameElement() const
418 0 : {
419 : return mNameContentList && mNameContentList->Length() != 0;
420 : }
421 :
422 248 : Element*
423 : nsIdentifierMapEntry::GetIdElement()
424 248 : {
425 : return mIdContentList.SafeElementAt(0);
426 : }
427 :
428 0 : Element*
429 : nsIdentifierMapEntry::GetImageIdElement()
430 0 : {
431 : return mImageElement ? mImageElement.get() : GetIdElement();
432 : }
433 :
434 0 : void
435 : nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
436 : void* aData, bool aForImage)
437 0 : {
438 0 : if (!mChangeCallbacks) {
439 : mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
440 : }
441 0 :
442 0 : ChangeCallback cc = { aCallback, aData, aForImage };
443 0 : mChangeCallbacks->PutEntry(cc);
444 : }
445 :
446 0 : void
447 : nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
448 : void* aData, bool aForImage)
449 0 : {
450 : if (!mChangeCallbacks)
451 0 : return;
452 0 : ChangeCallback cc = { aCallback, aData, aForImage };
453 0 : mChangeCallbacks->RemoveEntry(cc);
454 0 : if (mChangeCallbacks->Count() == 0) {
455 : mChangeCallbacks = nullptr;
456 : }
457 : }
458 :
459 1756 : void
460 : nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
461 : Element* aNewElement,
462 : bool aImageOnly)
463 3512 : {
464 : if (!mChangeCallbacks)
465 : return;
466 0 :
467 0 : for (auto iter = mChangeCallbacks->ConstIter(); !iter.Done(); iter.Next()) {
468 : nsIdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
469 : // Don't fire image changes for non-image observers, and don't fire element
470 0 : // changes for image observers when an image override is active.
471 : if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
472 : continue;
473 : }
474 0 :
475 0 : if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
476 : iter.Remove();
477 : }
478 : }
479 : }
480 :
481 : namespace {
482 :
483 : struct PositionComparator
484 : {
485 1047 : Element* const mElement;
486 : explicit PositionComparator(Element* const aElement) : mElement(aElement) {}
487 1109 :
488 0 : int operator()(void* aElement) const {
489 0 : Element* curElement = static_cast<Element*>(aElement);
490 : if (mElement == curElement) {
491 : return 0;
492 90 : }
493 : if (nsContentUtils::PositionIsBefore(mElement, curElement)) {
494 : return -1;
495 90 : }
496 : return 1;
497 : }
498 : };
499 :
500 : } // namespace
501 :
502 2692 : bool
503 : nsIdentifierMapEntry::AddIdElement(Element* aElement)
504 2692 : {
505 0 : MOZ_ASSERT(aElement, "Must have element");
506 : MOZ_ASSERT(!mIdContentList.Contains(nullptr),
507 : "Why is null in our list?");
508 :
509 2692 : #ifdef DEBUG
510 : Element* currentElement = mIdContentList.SafeElementAt(0);
511 : #endif
512 :
513 5384 : // Common case
514 0 : if (mIdContentList.IsEmpty()) {
515 : if (!mIdContentList.AppendElement(aElement))
516 1645 : return false;
517 0 : NS_ASSERTION(currentElement == nullptr, "How did that happen?");
518 0 : FireChangeCallbacks(nullptr, aElement);
519 : return true;
520 : }
521 :
522 : // We seem to have multiple content nodes for the same id, or XUL is messing
523 : // with us. Search for the right place to insert the content.
524 :
525 3141 : size_t idx;
526 0 : if (BinarySearchIf(mIdContentList, 0, mIdContentList.Length(),
527 : PositionComparator(aElement), &idx)) {
528 : // Already in the list, so already in the right spot. Get out of here.
529 : // XXXbz this only happens because XUL does all sorts of random
530 : // UpdateIdTableEntry calls. Hate, hate, hate!
531 : return true;
532 : }
533 28 :
534 : if (!mIdContentList.InsertElementAt(idx, aElement)) {
535 : return false;
536 : }
537 28 :
538 0 : if (idx == 0) {
539 0 : Element* oldElement = mIdContentList.SafeElementAt(1);
540 0 : NS_ASSERTION(currentElement == oldElement, "How did that happen?");
541 : FireChangeCallbacks(oldElement, aElement);
542 : }
543 : return true;
544 : }
545 :
546 111 : void
547 : nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
548 111 : {
549 : MOZ_ASSERT(aElement, "Missing element");
550 :
551 : // This should only be called while the document is in an update.
552 : // Assertions near the call to this method guarantee this.
553 :
554 : // This could fire in OOM situations
555 : // Only assert this in HTML documents for now as XUL does all sorts of weird
556 111 : // crap.
557 : NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
558 : mIdContentList.Contains(aElement),
559 : "Removing id entry that doesn't exist");
560 :
561 : // XXXbz should this ever Compact() I guess when all the content is gone
562 111 : // we'll just get cleaned up in the natural order of things...
563 0 : Element* currentElement = mIdContentList.SafeElementAt(0);
564 0 : mIdContentList.RemoveElement(aElement);
565 0 : if (currentElement == aElement) {
566 : FireChangeCallbacks(currentElement, mIdContentList.SafeElementAt(0));
567 111 : }
568 : }
569 :
570 0 : void
571 : nsIdentifierMapEntry::SetImageElement(Element* aElement)
572 0 : {
573 0 : Element* oldElement = GetImageIdElement();
574 0 : mImageElement = aElement;
575 0 : Element* newElement = GetImageIdElement();
576 0 : if (oldElement != newElement) {
577 : FireChangeCallbacks(oldElement, newElement, true);
578 0 : }
579 : }
580 :
581 : namespace mozilla {
582 : namespace dom {
583 : class SimpleHTMLCollection final : public nsSimpleContentList
584 : , public nsIHTMLCollection
585 : {
586 0 : public:
587 : explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
588 :
589 : NS_DECL_ISUPPORTS_INHERITED
590 0 :
591 : virtual nsINode* GetParentObject() override
592 0 : {
593 : return nsSimpleContentList::GetParentObject();
594 0 : }
595 : virtual uint32_t Length() override
596 0 : {
597 : return nsSimpleContentList::Length();
598 0 : }
599 : virtual Element* GetElementAt(uint32_t aIndex) override
600 0 : {
601 : return mElements.SafeElementAt(aIndex)->AsElement();
602 : }
603 0 :
604 : virtual Element* GetFirstNamedElement(const nsAString& aName,
605 : bool& aFound) override
606 0 : {
607 0 : aFound = false;
608 0 : RefPtr<nsAtom> name = NS_Atomize(aName);
609 0 : for (uint32_t i = 0; i < mElements.Length(); i++) {
610 0 : MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
611 0 : Element* element = mElements[i]->AsElement();
612 0 : if (element->GetID() == name ||
613 0 : (element->HasName() &&
614 0 : element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
615 0 : aFound = true;
616 : return element;
617 : }
618 : }
619 : return nullptr;
620 : }
621 0 :
622 : virtual void GetSupportedNames(nsTArray<nsString>& aNames) override
623 0 : {
624 0 : AutoTArray<nsAtom*, 8> atoms;
625 0 : for (uint32_t i = 0; i < mElements.Length(); i++) {
626 0 : MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
627 : Element* element = mElements[i]->AsElement();
628 0 :
629 0 : nsAtom* id = element->GetID();
630 0 : MOZ_ASSERT(id != nsGkAtoms::_empty);
631 0 : if (id && !atoms.Contains(id)) {
632 : atoms.AppendElement(id);
633 : }
634 0 :
635 0 : if (element->HasName()) {
636 0 : nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
637 0 : MOZ_ASSERT(name && name != nsGkAtoms::_empty);
638 0 : if (name && !atoms.Contains(name)) {
639 : atoms.AppendElement(name);
640 : }
641 : }
642 : }
643 0 :
644 0 : nsString* names = aNames.AppendElements(atoms.Length());
645 0 : for (uint32_t i = 0; i < atoms.Length(); i++) {
646 : atoms[i]->ToString(names[i]);
647 0 : }
648 : }
649 0 :
650 : virtual JSObject* GetWrapperPreserveColorInternal() override
651 0 : {
652 : return nsWrapperCache::GetWrapperPreserveColor();
653 0 : }
654 : virtual void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override
655 0 : {
656 0 : nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
657 0 : }
658 : virtual JSObject* WrapObject(JSContext *aCx,
659 : JS::Handle<JSObject*> aGivenProto) override
660 0 : {
661 : return HTMLCollectionBinding::Wrap(aCx, this, aGivenProto);
662 : }
663 :
664 : using nsBaseContentList::Item;
665 :
666 0 : private:
667 : virtual ~SimpleHTMLCollection() {}
668 : };
669 0 :
670 : NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
671 : nsIHTMLCollection)
672 :
673 : } // namespace dom
674 : } // namespace mozilla
675 :
676 0 : void
677 : nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
678 0 : {
679 0 : if (!mNameContentList) {
680 : mNameContentList = new SimpleHTMLCollection(aNode);
681 : }
682 0 :
683 0 : mNameContentList->AppendElement(aElement);
684 : }
685 :
686 0 : void
687 : nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
688 0 : {
689 0 : if (mNameContentList) {
690 : mNameContentList->RemoveElement(aElement);
691 0 : }
692 : }
693 :
694 0 : bool
695 : nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
696 0 : {
697 0 : Element* idElement = GetIdElement();
698 0 : return idElement &&
699 : nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
700 : }
701 :
702 0 : size_t
703 : nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
704 0 : {
705 : return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
706 : }
707 :
708 : // Helper structs for the content->subdoc map
709 :
710 : class SubDocMapEntry : public PLDHashEntryHdr
711 : {
712 : public:
713 : // Both of these are strong references
714 : Element *mKey; // must be first, to look like PLDHashEntryStub
715 : nsIDocument *mSubDocument;
716 : };
717 :
718 :
719 : /**
720 : * A struct that holds all the information about a radio group.
721 0 : */
722 : struct nsRadioGroupStruct
723 0 : {
724 0 : nsRadioGroupStruct()
725 0 : : mRequiredRadioCount(0)
726 0 : , mGroupSuffersFromValueMissing(false)
727 : {}
728 :
729 : /**
730 : * A strong pointer to the currently selected radio button.
731 : */
732 : RefPtr<HTMLInputElement> mSelectedRadioButton;
733 : nsCOMArray<nsIFormControl> mRadioButtons;
734 : uint32_t mRequiredRadioCount;
735 : bool mGroupSuffersFromValueMissing;
736 : };
737 :
738 1875 : // nsOnloadBlocker implementation
739 : NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
740 :
741 132 : NS_IMETHODIMP
742 : nsOnloadBlocker::GetName(nsACString &aResult)
743 132 : {
744 0 : aResult.AssignLiteral("about:document-onload-blocker");
745 : return NS_OK;
746 : }
747 :
748 0 : NS_IMETHODIMP
749 : nsOnloadBlocker::IsPending(bool *_retval)
750 0 : {
751 0 : *_retval = true;
752 : return NS_OK;
753 : }
754 :
755 0 : NS_IMETHODIMP
756 : nsOnloadBlocker::GetStatus(nsresult *status)
757 0 : {
758 0 : *status = NS_OK;
759 : return NS_OK;
760 : }
761 :
762 2 : NS_IMETHODIMP
763 : nsOnloadBlocker::Cancel(nsresult status)
764 2 : {
765 : return NS_OK;
766 : }
767 0 : NS_IMETHODIMP
768 : nsOnloadBlocker::Suspend(void)
769 0 : {
770 : return NS_OK;
771 : }
772 0 : NS_IMETHODIMP
773 : nsOnloadBlocker::Resume(void)
774 0 : {
775 : return NS_OK;
776 : }
777 :
778 0 : NS_IMETHODIMP
779 : nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
780 0 : {
781 0 : *aLoadGroup = nullptr;
782 : return NS_OK;
783 : }
784 :
785 0 : NS_IMETHODIMP
786 : nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
787 0 : {
788 : return NS_OK;
789 : }
790 :
791 94 : NS_IMETHODIMP
792 : nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
793 94 : {
794 0 : *aLoadFlags = nsIRequest::LOAD_NORMAL;
795 : return NS_OK;
796 : }
797 :
798 0 : NS_IMETHODIMP
799 : nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
800 0 : {
801 : return NS_OK;
802 : }
803 :
804 : // ==================================================================
805 60 :
806 0 : nsExternalResourceMap::nsExternalResourceMap()
807 : : mHaveShutDown(false)
808 60 : {
809 : }
810 :
811 0 : nsIDocument*
812 : nsExternalResourceMap::RequestResource(nsIURI* aURI,
813 : nsINode* aRequestingNode,
814 : nsIDocument* aDisplayDocument,
815 : ExternalResourceLoad** aPendingLoad)
816 : {
817 : // If we ever start allowing non-same-origin loads here, we might need to do
818 : // something interesting with aRequestingPrincipal even for the hashtable
819 0 : // gets.
820 0 : MOZ_ASSERT(aURI, "Must have a URI");
821 0 : MOZ_ASSERT(aRequestingNode, "Must have a node");
822 0 : *aPendingLoad = nullptr;
823 : if (mHaveShutDown) {
824 : return nullptr;
825 : }
826 :
827 0 : // First, make sure we strip the ref from aURI.
828 0 : nsCOMPtr<nsIURI> clone;
829 0 : nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
830 : if (NS_FAILED(rv) || !clone) {
831 : return nullptr;
832 : }
833 :
834 0 : ExternalResource* resource;
835 0 : mMap.Get(clone, &resource);
836 0 : if (resource) {
837 : return resource->mDocument;
838 : }
839 0 :
840 0 : RefPtr<PendingLoad>& loadEntry = mPendingLoads.GetOrInsert(clone);
841 0 : if (loadEntry) {
842 0 : RefPtr<PendingLoad> load(loadEntry);
843 : load.forget(aPendingLoad);
844 : return nullptr;
845 : }
846 0 :
847 0 : RefPtr<PendingLoad> load(new PendingLoad(aDisplayDocument));
848 : loadEntry = load;
849 0 :
850 : if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
851 : // Make sure we don't thrash things by trying this load again, since
852 0 : // chances are it failed for good reasons (security check, etc).
853 : AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
854 0 : } else {
855 : load.forget(aPendingLoad);
856 : }
857 :
858 : return nullptr;
859 : }
860 :
861 160 : void
862 : nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
863 : void* aData)
864 480 : {
865 0 : for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
866 0 : nsExternalResourceMap::ExternalResource* resource = iter.UserData();
867 : if (resource->mDocument && !aCallback(resource->mDocument, aData)) {
868 : break;
869 : }
870 160 : }
871 : }
872 :
873 41 : void
874 : nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
875 : {
876 : // mPendingLoads will get cleared out as the requests complete, so
877 123 : // no need to worry about those here.
878 0 : for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
879 : nsExternalResourceMap::ExternalResource* resource = iter.UserData();
880 :
881 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
882 0 : "mExternalResourceMap.mMap entry"
883 0 : "->mDocument");
884 : aCallback->NoteXPCOMChild(resource->mDocument);
885 :
886 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
887 0 : "mExternalResourceMap.mMap entry"
888 0 : "->mViewer");
889 : aCallback->NoteXPCOMChild(resource->mViewer);
890 :
891 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
892 0 : "mExternalResourceMap.mMap entry"
893 0 : "->mLoadGroup");
894 : aCallback->NoteXPCOMChild(resource->mLoadGroup);
895 41 : }
896 : }
897 :
898 8 : void
899 : nsExternalResourceMap::HideViewers()
900 24 : {
901 0 : for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
902 0 : nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
903 0 : if (viewer) {
904 : viewer->Hide();
905 : }
906 8 : }
907 : }
908 :
909 27 : void
910 : nsExternalResourceMap::ShowViewers()
911 81 : {
912 0 : for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
913 0 : nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
914 0 : if (viewer) {
915 : viewer->Show();
916 : }
917 27 : }
918 : }
919 :
920 0 : void
921 : TransferZoomLevels(nsIDocument* aFromDoc,
922 : nsIDocument* aToDoc)
923 0 : {
924 : MOZ_ASSERT(aFromDoc && aToDoc,
925 : "transferring zoom levels from/to null doc");
926 0 :
927 0 : nsPresContext* fromCtxt = aFromDoc->GetPresContext();
928 : if (!fromCtxt)
929 : return;
930 0 :
931 0 : nsPresContext* toCtxt = aToDoc->GetPresContext();
932 : if (!toCtxt)
933 : return;
934 0 :
935 0 : toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
936 0 : toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
937 0 : toCtxt->SetTextZoom(fromCtxt->TextZoom());
938 : toCtxt->SetOverrideDPPX(fromCtxt->GetOverrideDPPX());
939 : }
940 :
941 0 : void
942 : TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
943 0 : {
944 : MOZ_ASSERT(aFromDoc && aToDoc,
945 : "transferring showing state from/to null doc");
946 0 :
947 0 : if (aFromDoc->IsShowing()) {
948 : aToDoc->OnPageShow(true, nullptr);
949 0 : }
950 : }
951 :
952 0 : nsresult
953 : nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
954 : nsIContentViewer* aViewer,
955 : nsILoadGroup* aLoadGroup,
956 : nsIDocument* aDisplayDocument)
957 0 : {
958 0 : MOZ_ASSERT(aURI, "Unexpected call");
959 : MOZ_ASSERT((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
960 : "Must have both or neither");
961 0 :
962 0 : RefPtr<PendingLoad> load;
963 : mPendingLoads.Remove(aURI, getter_AddRefs(load));
964 0 :
965 : nsresult rv = NS_OK;
966 0 :
967 0 : nsCOMPtr<nsIDocument> doc;
968 0 : if (aViewer) {
969 0 : doc = aViewer->GetDocument();
970 : NS_ASSERTION(doc, "Must have a document");
971 0 :
972 : if (doc->IsXULDocument()) {
973 : // We don't handle XUL stuff here yet.
974 : rv = NS_ERROR_NOT_AVAILABLE;
975 0 : } else {
976 : doc->SetDisplayDocument(aDisplayDocument);
977 :
978 0 : // Make sure that hiding our viewer will tear down its presentation.
979 : aViewer->SetSticky(false);
980 0 :
981 0 : rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
982 0 : if (NS_SUCCEEDED(rv)) {
983 : rv = aViewer->Open(nullptr, nullptr);
984 : }
985 : }
986 0 :
987 0 : if (NS_FAILED(rv)) {
988 0 : doc = nullptr;
989 0 : aViewer = nullptr;
990 : aLoadGroup = nullptr;
991 : }
992 : }
993 0 :
994 0 : ExternalResource* newResource = new ExternalResource();
995 : mMap.Put(aURI, newResource);
996 0 :
997 0 : newResource->mDocument = doc;
998 0 : newResource->mViewer = aViewer;
999 0 : newResource->mLoadGroup = aLoadGroup;
1000 0 : if (doc) {
1001 0 : TransferZoomLevels(aDisplayDocument, doc);
1002 : TransferShowingState(aDisplayDocument, doc);
1003 : }
1004 0 :
1005 0 : const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
1006 0 : for (uint32_t i = 0; i < obs.Length(); ++i) {
1007 : obs[i]->Observe(doc, "external-resource-document-created", nullptr);
1008 : }
1009 0 :
1010 : return rv;
1011 : }
1012 0 :
1013 : NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
1014 : nsIStreamListener,
1015 : nsIRequestObserver)
1016 :
1017 0 : NS_IMETHODIMP
1018 : nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
1019 : nsISupports *aContext)
1020 0 : {
1021 0 : nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
1022 : if (map.HaveShutDown()) {
1023 : return NS_BINDING_ABORTED;
1024 : }
1025 0 :
1026 0 : nsCOMPtr<nsIContentViewer> viewer;
1027 0 : nsCOMPtr<nsILoadGroup> loadGroup;
1028 0 : nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
1029 : getter_AddRefs(loadGroup));
1030 :
1031 0 : // Make sure to do this no matter what
1032 0 : nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
1033 0 : mDisplayDocument);
1034 : if (NS_FAILED(rv)) {
1035 : return rv;
1036 0 : }
1037 0 : if (NS_FAILED(rv2)) {
1038 0 : mTargetListener = nullptr;
1039 : return rv2;
1040 : }
1041 0 :
1042 : return mTargetListener->OnStartRequest(aRequest, aContext);
1043 : }
1044 :
1045 0 : nsresult
1046 : nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
1047 : nsIContentViewer** aViewer,
1048 : nsILoadGroup** aLoadGroup)
1049 0 : {
1050 0 : MOZ_ASSERT(!mTargetListener, "Unexpected call to OnStartRequest");
1051 0 : *aViewer = nullptr;
1052 : *aLoadGroup = nullptr;
1053 0 :
1054 0 : nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1055 : NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
1056 0 :
1057 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
1058 : if (httpChannel) {
1059 0 : bool requestSucceeded;
1060 0 : if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
1061 : !requestSucceeded) {
1062 0 : // Bail out on this load, since it looks like we have an HTTP error page
1063 : return NS_BINDING_ABORTED;
1064 : }
1065 : }
1066 0 :
1067 0 : nsAutoCString type;
1068 : chan->GetContentType(type);
1069 0 :
1070 0 : nsCOMPtr<nsILoadGroup> loadGroup;
1071 : chan->GetLoadGroup(getter_AddRefs(loadGroup));
1072 :
1073 : // Give this document its own loadgroup
1074 0 : nsCOMPtr<nsILoadGroup> newLoadGroup =
1075 0 : do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1076 0 : NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
1077 : newLoadGroup->SetLoadGroup(loadGroup);
1078 0 :
1079 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
1080 : loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1081 :
1082 0 : nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1083 0 : new LoadgroupCallbacks(callbacks);
1084 : newLoadGroup->SetNotificationCallbacks(newCallbacks);
1085 :
1086 : // This is some serious hackery cribbed from docshell
1087 0 : nsCOMPtr<nsICategoryManager> catMan =
1088 0 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1089 0 : NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1090 0 : nsCString contractId;
1091 0 : nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1092 0 : getter_Copies(contractId));
1093 : NS_ENSURE_SUCCESS(rv, rv);
1094 0 : nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1095 0 : do_GetService(contractId.get());
1096 : NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1097 0 :
1098 0 : nsCOMPtr<nsIContentViewer> viewer;
1099 0 : nsCOMPtr<nsIStreamListener> listener;
1100 : rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1101 0 : type, nullptr, nullptr,
1102 0 : getter_AddRefs(listener),
1103 0 : getter_AddRefs(viewer));
1104 0 : NS_ENSURE_SUCCESS(rv, rv);
1105 : NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1106 0 :
1107 0 : nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1108 : if (!parser) {
1109 : /// We don't want to deal with the various fake documents yet
1110 : return NS_ERROR_NOT_IMPLEMENTED;
1111 : }
1112 :
1113 0 : // We can't handle HTML and other weird things here yet.
1114 0 : nsIContentSink* sink = parser->GetContentSink();
1115 0 : nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1116 : if (!xmlSink) {
1117 : return NS_ERROR_NOT_IMPLEMENTED;
1118 : }
1119 0 :
1120 0 : listener.swap(mTargetListener);
1121 0 : viewer.forget(aViewer);
1122 0 : newLoadGroup.forget(aLoadGroup);
1123 : return NS_OK;
1124 : }
1125 :
1126 0 : NS_IMETHODIMP
1127 : nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1128 : nsISupports* aContext,
1129 : nsIInputStream* aStream,
1130 : uint64_t aOffset,
1131 : uint32_t aCount)
1132 : {
1133 0 : // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1134 0 : NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
1135 : if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1136 : return NS_BINDING_ABORTED;
1137 0 : }
1138 0 : return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1139 : aCount);
1140 : }
1141 :
1142 0 : NS_IMETHODIMP
1143 : nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1144 : nsISupports* aContext,
1145 : nsresult aStatus)
1146 : {
1147 0 : // mTargetListener might be null if SetupViewer or AddExternalResource failed
1148 0 : if (mTargetListener) {
1149 0 : nsCOMPtr<nsIStreamListener> listener;
1150 0 : mTargetListener.swap(listener);
1151 : return listener->OnStopRequest(aRequest, aContext, aStatus);
1152 : }
1153 :
1154 : return NS_OK;
1155 : }
1156 :
1157 0 : nsresult
1158 : nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1159 : nsINode* aRequestingNode)
1160 0 : {
1161 0 : MOZ_ASSERT(aURI, "Must have a URI");
1162 : MOZ_ASSERT(aRequestingNode, "Must have a node");
1163 :
1164 0 : nsCOMPtr<nsILoadGroup> loadGroup =
1165 : aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
1166 0 :
1167 0 : nsresult rv = NS_OK;
1168 0 : nsCOMPtr<nsIChannel> channel;
1169 : rv = NS_NewChannel(getter_AddRefs(channel),
1170 : aURI,
1171 : aRequestingNode,
1172 : nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
1173 : nsIContentPolicy::TYPE_OTHER,
1174 0 : nullptr, // aPerformanceStorage
1175 0 : loadGroup);
1176 : NS_ENSURE_SUCCESS(rv, rv);
1177 0 :
1178 : mURI = aURI;
1179 0 :
1180 : return channel->AsyncOpen2(this);
1181 : }
1182 0 :
1183 : NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1184 : nsIInterfaceRequestor)
1185 :
1186 : #define IMPL_SHIM(_i) \
1187 : NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1188 0 :
1189 0 : IMPL_SHIM(nsILoadContext)
1190 0 : IMPL_SHIM(nsIProgressEventSink)
1191 0 : IMPL_SHIM(nsIChannelEventSink)
1192 0 : IMPL_SHIM(nsISecurityEventSink)
1193 : IMPL_SHIM(nsIApplicationCacheContainer)
1194 :
1195 : #undef IMPL_SHIM
1196 :
1197 : #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1198 :
1199 : #define TRY_SHIM(_i) \
1200 : PR_BEGIN_MACRO \
1201 : if (IID_IS(_i)) { \
1202 : nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1203 : if (!real) { \
1204 : return NS_NOINTERFACE; \
1205 : } \
1206 : nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1207 : shim.forget(aSink); \
1208 : return NS_OK; \
1209 : } \
1210 : PR_END_MACRO
1211 :
1212 0 : NS_IMETHODIMP
1213 : nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1214 : void **aSink)
1215 0 : {
1216 0 : if (mCallbacks &&
1217 0 : (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
1218 0 : IID_IS(nsITabChild))) {
1219 : return mCallbacks->GetInterface(aIID, aSink);
1220 : }
1221 0 :
1222 : *aSink = nullptr;
1223 0 :
1224 0 : TRY_SHIM(nsILoadContext);
1225 0 : TRY_SHIM(nsIProgressEventSink);
1226 0 : TRY_SHIM(nsIChannelEventSink);
1227 0 : TRY_SHIM(nsISecurityEventSink);
1228 : TRY_SHIM(nsIApplicationCacheContainer);
1229 :
1230 : return NS_NOINTERFACE;
1231 : }
1232 :
1233 : #undef TRY_SHIM
1234 : #undef IID_IS
1235 0 :
1236 : nsExternalResourceMap::ExternalResource::~ExternalResource()
1237 0 : {
1238 0 : if (mViewer) {
1239 0 : mViewer->Close(nullptr);
1240 : mViewer->Destroy();
1241 0 : }
1242 : }
1243 :
1244 : // ==================================================================
1245 : // =
1246 : // ==================================================================
1247 :
1248 : // If we ever have an nsIDocumentObserver notification for stylesheet title
1249 : // changes we should update the list from that instead of overriding
1250 0 : // EnsureFresh.
1251 : class nsDOMStyleSheetSetList final : public DOMStringList
1252 : {
1253 : public:
1254 : explicit nsDOMStyleSheetSetList(nsIDocument* aDocument);
1255 :
1256 : void Disconnect()
1257 0 : {
1258 : mDocument = nullptr;
1259 : }
1260 :
1261 : virtual void EnsureFresh() override;
1262 :
1263 : protected:
1264 : nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it
1265 : // dies.
1266 : };
1267 0 :
1268 0 : nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1269 : : mDocument(aDocument)
1270 0 : {
1271 0 : NS_ASSERTION(mDocument, "Must have document!");
1272 : }
1273 :
1274 0 : void
1275 : nsDOMStyleSheetSetList::EnsureFresh()
1276 0 : {
1277 : MOZ_ASSERT(NS_IsMainThread());
1278 0 :
1279 : mNames.Clear();
1280 0 :
1281 0 : if (!mDocument) {
1282 : return; // Spec says "no exceptions", and we have no style sets if we have
1283 : // no document, for sure
1284 : }
1285 0 :
1286 0 : size_t count = mDocument->SheetCount();
1287 0 : nsAutoString title;
1288 0 : for (size_t index = 0; index < count; index++) {
1289 0 : StyleSheet* sheet = mDocument->SheetAt(index);
1290 0 : NS_ASSERTION(sheet, "Null sheet in sheet list!");
1291 0 : sheet->GetTitle(title);
1292 0 : if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1293 : return;
1294 : }
1295 : }
1296 : }
1297 :
1298 1 : // ==================================================================
1299 : nsIDocument::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget)
1300 2 : : nsExpirationTracker<SelectorCacheKey, 4>(
1301 0 : 1000, "nsIDocument::SelectorCache", aEventTarget)
1302 : { }
1303 0 :
1304 : nsIDocument::SelectorCache::~SelectorCache()
1305 0 : {
1306 0 : AgeAllGenerations();
1307 : }
1308 0 :
1309 : void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
1310 0 : {
1311 0 : MOZ_ASSERT(NS_IsMainThread());
1312 : MOZ_ASSERT(aSelector);
1313 :
1314 : // There is no guarantee that this method won't be re-entered when selector
1315 : // matching is ongoing because "memory-pressure" could be notified immediately
1316 : // when OOM happens according to the design of nsExpirationTracker.
1317 : // The perfect solution is to delete the |aSelector| and its
1318 : // RawServoSelectorList in mTable asynchronously.
1319 : // We remove these objects synchronously for now because NotifyExpired() will
1320 : // never be triggered by "memory-pressure" which is not implemented yet in
1321 : // the stage 2 of mozalloc_handle_oom().
1322 : // Once these objects are removed asynchronously, we should update the warning
1323 0 : // added in mozalloc_handle_oom() as well.
1324 0 : RemoveObject(aSelector);
1325 0 : mTable.Remove(aSelector->mKey);
1326 0 : delete aSelector;
1327 : }
1328 8 :
1329 : struct nsIDocument::FrameRequest
1330 : {
1331 2 : FrameRequest(FrameRequestCallback& aCallback,
1332 : int32_t aHandle) :
1333 2 : mCallback(&aCallback),
1334 : mHandle(aHandle)
1335 : {}
1336 :
1337 : // Conversion operator so that we can append these to a
1338 : // FrameRequestCallbackList
1339 2 : operator const RefPtr<FrameRequestCallback>& () const {
1340 : return mCallback;
1341 : }
1342 :
1343 : // Comparator operators to allow RemoveElementSorted with an
1344 : // integer argument on arrays of FrameRequest
1345 : bool operator==(int32_t aHandle) const {
1346 : return mHandle == aHandle;
1347 : }
1348 : bool operator<(int32_t aHandle) const {
1349 : return mHandle < aHandle;
1350 : }
1351 :
1352 : RefPtr<FrameRequestCallback> mCallback;
1353 : int32_t mHandle;
1354 : };
1355 1 :
1356 : static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
1357 :
1358 : // ==================================================================
1359 : // =
1360 60 : // ==================================================================
1361 : nsIDocument::nsIDocument()
1362 : : nsINode(nullNodeInfo),
1363 : DocumentOrShadowRoot(*this),
1364 : mReferrerPolicySet(false),
1365 : mReferrerPolicy(mozilla::net::RP_Unset),
1366 : mBlockAllMixedContent(false),
1367 : mBlockAllMixedContentPreloads(false),
1368 : mUpgradeInsecureRequests(false),
1369 : mUpgradeInsecurePreloads(false),
1370 : mCharacterSet(WINDOWS_1252_ENCODING),
1371 : mCharacterSetSource(0),
1372 : mParentDocument(nullptr),
1373 : mCachedRootElement(nullptr),
1374 : mNodeInfoManager(nullptr),
1375 : #ifdef DEBUG
1376 : mStyledLinksCleared(false),
1377 : #endif
1378 : mBidiEnabled(false),
1379 : mMathMLEnabled(false),
1380 : mIsInitialDocumentInWindow(false),
1381 : mIgnoreDocGroupMismatches(false),
1382 : mLoadedAsData(false),
1383 : mLoadedAsInteractiveData(false),
1384 : mMayStartLayout(true),
1385 : mHaveFiredTitleChange(false),
1386 : mIsShowing(false),
1387 : mVisible(true),
1388 : mRemovedFromDocShell(false),
1389 : // mAllowDNSPrefetch starts true, so that we can always reliably && it
1390 : // with various values that might disable it. Since we never prefetch
1391 : // unless we get a window, and in that case the docshell value will get
1392 : // &&-ed in, this is safe.
1393 : mAllowDNSPrefetch(true),
1394 : mIsStaticDocument(false),
1395 : mCreatingStaticClone(false),
1396 : mInUnlinkOrDeletion(false),
1397 : mHasHadScriptHandlingObject(false),
1398 : mIsBeingUsedAsImage(false),
1399 : mIsSyntheticDocument(false),
1400 : mHasLinksToUpdateRunnable(false),
1401 : mFlushingPendingLinkUpdates(false),
1402 : mMayHaveDOMMutationObservers(false),
1403 : mMayHaveAnimationObservers(false),
1404 : mHasMixedActiveContentLoaded(false),
1405 : mHasMixedActiveContentBlocked(false),
1406 : mHasMixedDisplayContentLoaded(false),
1407 : mHasMixedDisplayContentBlocked(false),
1408 : mHasMixedContentObjectSubrequest(false),
1409 : mHasCSP(false),
1410 : mHasUnsafeEvalCSP(false),
1411 : mHasUnsafeInlineCSP(false),
1412 : mHasTrackingContentBlocked(false),
1413 : mHasTrackingContentLoaded(false),
1414 : mBFCacheDisallowed(false),
1415 : mHasHadDefaultView(false),
1416 : mStyleSheetChangeEventsEnabled(false),
1417 : mIsSrcdocDocument(false),
1418 : mDidDocumentOpen(false),
1419 : mHasDisplayDocument(false),
1420 : mFontFaceSetDirty(true),
1421 : mGetUserFontSetCalled(false),
1422 : mDidFireDOMContentLoaded(true),
1423 : mHasScrollLinkedEffect(false),
1424 : mFrameRequestCallbacksScheduled(false),
1425 : mIsTopLevelContentDocument(false),
1426 : mIsContentDocument(false),
1427 : mDidCallBeginLoad(false),
1428 : mAllowPaymentRequest(false),
1429 : mEncodingMenuDisabled(false),
1430 : mIsShadowDOMEnabled(false),
1431 : mIsSVGGlyphsDocument(false),
1432 : mInDestructor(false),
1433 : mIsGoingAway(false),
1434 : mInXBLUpdate(false),
1435 : mNeedsReleaseAfterStackRefCntRelease(false),
1436 : mStyleSetFilled(false),
1437 : mSSApplicableStateNotificationPending(false),
1438 : mMayHaveTitleElement(false),
1439 : mDOMLoadingSet(false),
1440 : mDOMInteractiveSet(false),
1441 : mDOMCompleteSet(false),
1442 : mAutoFocusFired(false),
1443 : mScrolledToRefAlready(false),
1444 : mChangeScrollPosWhenScrollingToRef(false),
1445 : mHasWarnedAboutBoxObjects(false),
1446 : mDelayFrameLoaderInitialization(false),
1447 : mSynchronousDOMContentLoaded(false),
1448 : mMaybeServiceWorkerControlled(false),
1449 : mValidWidth(false),
1450 : mValidHeight(false),
1451 : mAutoSize(false),
1452 : mAllowZoom(false),
1453 : mAllowDoubleTapZoom(false),
1454 : mValidScaleFloat(false),
1455 : mValidMaxScale(false),
1456 : mScaleStrEmpty(false),
1457 : mWidthStrEmpty(false),
1458 : mParserAborted(false),
1459 : mReportedUseCounters(false),
1460 : mHasReportedShadowDOMUsage(false),
1461 : #ifdef DEBUG
1462 : mWillReparent(false),
1463 : #endif
1464 : mPendingFullscreenRequests(0),
1465 : mXMLDeclarationBits(0),
1466 : mOnloadBlockCount(0),
1467 : mAsyncOnloadBlockCount(0),
1468 : mCompatMode(eCompatibility_FullStandards),
1469 : mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
1470 : #ifdef MOZILLA_INTERNAL_API
1471 : mVisibilityState(dom::VisibilityState::Hidden),
1472 : #else
1473 : mDummy(0),
1474 : #endif
1475 : mType(eUnknown),
1476 : mDefaultElementType(0),
1477 : mAllowXULXBL(eTriUnset),
1478 : mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
1479 : mSandboxFlags(0),
1480 : mPartID(0),
1481 : mMarkedCCGeneration(0),
1482 : mPresShell(nullptr),
1483 : mSubtreeModifiedDepth(0),
1484 : mPreloadPictureDepth(0),
1485 : mEventsSuppressed(0),
1486 : mIgnoreDestructiveWritesCounter(0),
1487 : mFrameRequestCallbackCounter(0),
1488 : mStaticCloneCount(0),
1489 : mWindow(nullptr),
1490 : mBFCacheEntry(nullptr),
1491 : mInSyncOperationCount(0),
1492 : mBlockDOMContentLoaded(0),
1493 : mUseCounters(0),
1494 : mChildDocumentUseCounters(0),
1495 : mNotifiedPageForUseCounter(0),
1496 : mUserHasInteracted(false),
1497 : mUserHasActivatedInteraction(false),
1498 : mStackRefCnt(0),
1499 : mUpdateNestLevel(0),
1500 : mViewportType(Unknown),
1501 : mViewportOverflowType(ViewportOverflowType::NoOverflow),
1502 : mSubDocuments(nullptr),
1503 : mHeaderData(nullptr),
1504 : mFlashClassification(FlashClassification::Unclassified),
1505 : mBoxObjectTable(nullptr),
1506 : mCurrentOrientationAngle(0),
1507 : mCurrentOrientationType(OrientationType::Portrait_primary),
1508 : mServoRestyleRootDirtyBits(0),
1509 0 : mThrowOnDynamicMarkupInsertionCounter(0),
1510 : mIgnoreOpensDuringUnloadCounter(0)
1511 0 : {
1512 0 : SetIsInDocument();
1513 : }
1514 0 :
1515 0 : nsDocument::nsDocument(const char* aContentType)
1516 : : nsIDocument()
1517 0 : {
1518 : SetContentTypeInternal(nsDependentCString(aContentType));
1519 0 :
1520 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
1521 :
1522 0 : // Start out mLastStyleSheetSet as null, per spec
1523 : SetDOMStringToNull(mLastStyleSheetSet);
1524 :
1525 0 : // void state used to differentiate an empty source from an unselected source
1526 : mPreloadPictureFoundSource.SetIsVoid(true);
1527 : // For determining if this is a flash document which should be
1528 0 : // blocked based on its principal.
1529 0 : mPrincipalFlashClassifier = new PrincipalFlashClassifier();
1530 : }
1531 :
1532 0 : void
1533 : nsIDocument::ClearAllBoxObjects()
1534 0 : {
1535 0 : if (mBoxObjectTable) {
1536 0 : for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1537 0 : nsPIBoxObject* boxObject = iter.UserData();
1538 0 : if (boxObject) {
1539 : boxObject->Clear();
1540 : }
1541 0 : }
1542 0 : delete mBoxObjectTable;
1543 : mBoxObjectTable = nullptr;
1544 0 : }
1545 : }
1546 0 :
1547 : nsIDocument::~nsIDocument()
1548 0 : {
1549 : MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
1550 : "must not have media query lists left");
1551 0 :
1552 0 : if (mNodeInfoManager) {
1553 : mNodeInfoManager->DropDocumentReference();
1554 : }
1555 0 :
1556 0 : if (mDocGroup) {
1557 : mDocGroup->RemoveDocument(this);
1558 : }
1559 0 :
1560 0 : UnlinkOriginalDocumentIfStatic();
1561 : }
1562 :
1563 0 : bool
1564 : nsIDocument::IsAboutPage() const
1565 0 : {
1566 0 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
1567 0 : nsCOMPtr<nsIURI> uri;
1568 0 : principal->GetURI(getter_AddRefs(uri));
1569 0 : bool isAboutScheme = true;
1570 0 : if (uri) {
1571 : uri->SchemeIs("about", &isAboutScheme);
1572 0 : }
1573 : return isAboutScheme;
1574 : }
1575 0 :
1576 : nsDocument::~nsDocument()
1577 0 : {
1578 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
1579 0 :
1580 : NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1581 0 :
1582 : if (IsTopLevelContentDocument()) {
1583 0 : //don't report for about: pages
1584 : if (!IsAboutPage()) {
1585 0 : // Record the page load
1586 0 : uint32_t pageLoaded = 1;
1587 : Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1588 : // Record the mixed content status of the docshell in Telemetry
1589 : enum {
1590 : NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1591 : MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
1592 : MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
1593 : MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
1594 : };
1595 0 :
1596 0 : bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1597 : bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1598 0 :
1599 0 : bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1600 : bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1601 0 :
1602 0 : bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1603 : bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1604 0 :
1605 0 : uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1606 : if (hasMixedDisplay && hasMixedActive) {
1607 0 : mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1608 : } else if (hasMixedActive){
1609 0 : mixedContentLevel = MIXED_ACTIVE_CONTENT;
1610 0 : } else if (hasMixedDisplay) {
1611 : mixedContentLevel = MIXED_DISPLAY_CONTENT;
1612 0 : }
1613 : Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1614 :
1615 0 : // record mixed object subrequest telemetry
1616 : if (mHasMixedContentObjectSubrequest) {
1617 0 : /* mixed object subrequest loaded on page*/
1618 : Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 1);
1619 : } else {
1620 0 : /* no mixed object subrequests loaded on page*/
1621 : Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 0);
1622 : }
1623 :
1624 0 : // record CSP telemetry on this document
1625 0 : if (mHasCSP) {
1626 : Accumulate(Telemetry::CSP_DOCUMENTS_COUNT, 1);
1627 0 : }
1628 0 : if (mHasUnsafeInlineCSP) {
1629 : Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT, 1);
1630 0 : }
1631 0 : if (mHasUnsafeEvalCSP) {
1632 : Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT, 1);
1633 : }
1634 0 :
1635 0 : if (MOZ_UNLIKELY(GetMathMLEnabled())) {
1636 : ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT, 1);
1637 : }
1638 : }
1639 : }
1640 0 :
1641 : ReportUseCounters();
1642 0 :
1643 0 : mInDestructor = true;
1644 : mInUnlinkOrDeletion = true;
1645 0 :
1646 : mozilla::DropJSObjects(this);
1647 :
1648 0 : // Clear mObservers to keep it in sync with the mutationobserver list
1649 : mObservers.Clear();
1650 0 :
1651 : mIntersectionObservers.Clear();
1652 0 :
1653 0 : if (mStyleSheetSetList) {
1654 : mStyleSheetSetList->Disconnect();
1655 : }
1656 0 :
1657 0 : if (mAnimationController) {
1658 : mAnimationController->Disconnect();
1659 : }
1660 0 :
1661 : MOZ_ASSERT(mTimelines.isEmpty());
1662 0 :
1663 : mParentDocument = nullptr;
1664 :
1665 : // Kill the subdocument map, doing this will release its strong
1666 0 : // references, if any.
1667 0 : delete mSubDocuments;
1668 : mSubDocuments = nullptr;
1669 :
1670 : // Destroy link map now so we don't waste time removing
1671 0 : // links one by one
1672 : DestroyElementMaps();
1673 0 :
1674 : nsAutoScriptBlocker scriptBlocker;
1675 :
1676 0 : // Invalidate cached array of child nodes
1677 : InvalidateChildNodes();
1678 0 :
1679 0 : for (uint32_t indx = mChildren.ChildCount(); indx-- != 0; ) {
1680 0 : mChildren.ChildAt(indx)->UnbindFromTree();
1681 : mChildren.RemoveChildAt(indx);
1682 0 : }
1683 0 : mFirstChild = nullptr;
1684 : mCachedRootElement = nullptr;
1685 :
1686 0 : // Let the stylesheets know we're going away
1687 0 : for (StyleSheet* sheet : mStyleSheets) {
1688 : sheet->ClearAssociatedDocumentOrShadowRoot();
1689 0 : }
1690 0 : for (auto& sheets : mAdditionalSheets) {
1691 0 : for (StyleSheet* sheet : sheets) {
1692 : sheet->ClearAssociatedDocumentOrShadowRoot();
1693 : }
1694 0 : }
1695 0 : if (mAttrStyleSheet) {
1696 : mAttrStyleSheet->SetOwningDocument(nullptr);
1697 : }
1698 : // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
1699 0 :
1700 0 : if (mListenerManager) {
1701 0 : mListenerManager->Disconnect();
1702 : UnsetFlags(NODE_HAS_LISTENERMANAGER);
1703 : }
1704 0 :
1705 0 : if (mScriptLoader) {
1706 : mScriptLoader->DropDocumentReference();
1707 : }
1708 0 :
1709 : if (mCSSLoader) {
1710 0 : // Could be null here if Init() failed or if we have been unlinked.
1711 : mCSSLoader->DropDocumentReference();
1712 : }
1713 0 :
1714 0 : if (mStyleImageLoader) {
1715 : mStyleImageLoader->DropDocumentReference();
1716 : }
1717 0 :
1718 : delete mHeaderData;
1719 0 :
1720 : ClearAllBoxObjects();
1721 0 :
1722 : mPendingTitleChangeEvent.Revoke();
1723 0 :
1724 0 : mPlugins.Clear();
1725 : }
1726 0 :
1727 0 : NS_INTERFACE_TABLE_HEAD(nsDocument)
1728 : NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
1729 : NS_INTERFACE_TABLE_BEGIN
1730 : NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1731 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1732 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1733 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1734 : NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1735 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1736 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1737 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1738 0 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1739 0 : NS_INTERFACE_TABLE_END
1740 0 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1741 : NS_INTERFACE_MAP_END
1742 :
1743 0 :
1744 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1745 0 : NS_IMETHODIMP_(MozExternalRefCountType)
1746 : nsDocument::Release()
1747 0 : {
1748 0 : MOZ_ASSERT(0 != mRefCnt, "dup release");
1749 0 : NS_ASSERT_OWNINGTHREAD(nsDocument);
1750 0 : nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1751 0 : bool shouldDelete = false;
1752 0 : nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1753 0 : NS_LOG_RELEASE(this, count, "nsDocument");
1754 0 : if (count == 0) {
1755 0 : if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1756 0 : mNeedsReleaseAfterStackRefCntRelease = true;
1757 0 : NS_ADDREF_THIS();
1758 : return mRefCnt.get();
1759 0 : }
1760 0 : mRefCnt.incr(base);
1761 0 : nsNodeUtils::LastRelease(this);
1762 0 : mRefCnt.decr(base);
1763 0 : if (shouldDelete) {
1764 0 : mRefCnt.stabilizeForDeletion();
1765 : DeleteCycleCollectable();
1766 : }
1767 0 : }
1768 : return count;
1769 : }
1770 :
1771 0 : NS_IMETHODIMP_(void)
1772 : nsDocument::DeleteCycleCollectable()
1773 0 : {
1774 0 : delete this;
1775 : }
1776 0 :
1777 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1778 0 : if (Element::CanSkip(tmp, aRemovingAllowed)) {
1779 0 : EventListenerManager* elm = tmp->GetExistingListenerManager();
1780 0 : if (elm) {
1781 : elm->MarkForCC();
1782 : }
1783 : return true;
1784 : }
1785 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1786 0 :
1787 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1788 : return Element::CanSkipInCC(tmp);
1789 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1790 0 :
1791 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1792 : return Element::CanSkipThis(tmp);
1793 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1794 :
1795 : static const char* kNSURIs[] = {
1796 : "([none])",
1797 : "(xmlns)",
1798 : "(xml)",
1799 : "(xhtml)",
1800 : "(XLink)",
1801 : "(XSLT)",
1802 : "(XBL)",
1803 : "(MathML)",
1804 : "(RDF)",
1805 : "(XUL)"
1806 : };
1807 0 :
1808 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1809 : if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1810 0 : char name[512];
1811 0 : nsAutoCString loadedAsData;
1812 0 : if (tmp->IsLoadedAsData()) {
1813 : loadedAsData.AssignLiteral("data");
1814 0 : } else {
1815 : loadedAsData.AssignLiteral("normal");
1816 0 : }
1817 0 : uint32_t nsid = tmp->GetDefaultNamespaceID();
1818 0 : nsAutoCString uri;
1819 0 : if (tmp->mDocumentURI)
1820 0 : uri = tmp->mDocumentURI->GetSpecOrDefault();
1821 0 : if (nsid < ArrayLength(kNSURIs)) {
1822 0 : SprintfLiteral(name, "nsDocument %s %s %s",
1823 : loadedAsData.get(), kNSURIs[nsid], uri.get());
1824 : }
1825 0 : else {
1826 0 : SprintfLiteral(name, "nsDocument %s %s",
1827 : loadedAsData.get(), uri.get());
1828 0 : }
1829 : cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1830 : }
1831 0 : else {
1832 : NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1833 : }
1834 0 :
1835 : if (!nsINode::Traverse(tmp, cb)) {
1836 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1837 : }
1838 0 :
1839 : if (tmp->mMaybeEndOutermostXBLUpdateRunner) {
1840 : // The cached runnable keeps a reference to the document object..
1841 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1842 0 : "mMaybeEndOutermostXBLUpdateRunner.mObj");
1843 : cb.NoteXPCOMChild(ToSupports(tmp));
1844 : }
1845 0 :
1846 0 : for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
1847 0 : iter.Next()) {
1848 : iter.Get()->Traverse(&cb);
1849 : }
1850 0 :
1851 : tmp->mExternalResourceMap.Traverse(&cb);
1852 :
1853 0 : // Traverse the mChildren nsAttrAndChildArray.
1854 0 : for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1855 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1856 : cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1857 : }
1858 :
1859 0 : // Traverse all nsIDocument pointer members.
1860 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1861 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1862 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
1863 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
1864 :
1865 41 : // Traverse all nsDocument nsCOMPtrs.
1866 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1867 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1868 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1869 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1870 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1871 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1872 123 :
1873 0 : for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
1874 : nsRadioGroupStruct* radioGroup = iter.UserData();
1875 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1876 0 : cb, "mRadioGroups entry->mSelectedRadioButton");
1877 : cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
1878 0 :
1879 0 : uint32_t i, count = radioGroup->mRadioButtons.Count();
1880 : for (i = 0; i < count; ++i) {
1881 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1882 0 : cb, "mRadioGroups entry->mRadioButtons[i]");
1883 : cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
1884 : }
1885 : }
1886 :
1887 : // The boxobject for an element will only exist as long as it's in the
1888 41 : // document, so we'll traverse the table here instead of from the element.
1889 0 : if (tmp->mBoxObjectTable) {
1890 0 : for (auto iter = tmp->mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1891 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mBoxObjectTable entry");
1892 : cb.NoteXPCOMChild(iter.UserData());
1893 : }
1894 : }
1895 41 :
1896 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
1897 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
1898 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
1899 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
1900 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
1901 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
1902 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
1903 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
1904 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
1905 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
1906 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
1907 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
1908 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
1909 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
1910 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
1911 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
1912 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
1913 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
1914 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
1915 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
1916 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
1917 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
1918 :
1919 41 : // Traverse all our nsCOMArrays.
1920 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
1921 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
1922 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
1923 82 :
1924 0 : for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
1925 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
1926 : cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
1927 : }
1928 :
1929 82 : // Traverse animation components
1930 0 : if (tmp->mAnimationController) {
1931 : tmp->mAnimationController->Traverse(&cb);
1932 : }
1933 41 :
1934 0 : if (tmp->mSubDocuments) {
1935 0 : for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
1936 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
1937 :
1938 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1939 0 : "mSubDocuments entry->mKey");
1940 : cb.NoteXPCOMChild(entry->mKey);
1941 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1942 0 : "mSubDocuments entry->mSubDocument");
1943 : cb.NoteXPCOMChild(entry->mSubDocument);
1944 : }
1945 : }
1946 41 :
1947 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
1948 :
1949 : // We own only the items in mDOMMediaQueryLists that have listeners;
1950 : // this reference is managed by their AddListener and RemoveListener
1951 41 : // methods.
1952 0 : for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;
1953 0 : mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
1954 0 : if (mql->HasListeners()) {
1955 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
1956 : cb.NoteXPCOMChild(mql);
1957 : }
1958 : }
1959 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1960 :
1961 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
1962 164 :
1963 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDocument)
1964 0 :
1965 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
1966 : tmp->mInUnlinkOrDeletion = true;
1967 :
1968 0 : // Clear out our external resources
1969 : tmp->mExternalResourceMap.Shutdown();
1970 0 :
1971 : nsAutoScriptBlocker scriptBlocker;
1972 0 :
1973 : nsINode::Unlink(tmp);
1974 :
1975 0 : // Unlink the mChildren nsAttrAndChildArray.
1976 0 : uint32_t childCount = tmp->mChildren.ChildCount();
1977 0 : if (childCount) {
1978 : while (childCount-- > 0) {
1979 : // Hold a strong ref to the node when we remove it, because we may be
1980 : // the last reference to it. We need to call TakeChildAt() and
1981 : // update mFirstChild before calling UnbindFromTree, since this last
1982 : // can notify various observers and they should really see consistent
1983 : // tree state.
1984 : // If this code changes, change the corresponding code in
1985 0 : // FragmentOrElement's unlink impl and ContentUnbinder::UnbindSubtree.
1986 0 : nsCOMPtr<nsIContent> child = tmp->mChildren.TakeChildAt(childCount);
1987 0 : if (childCount == 0) {
1988 : tmp->mFirstChild = nullptr;
1989 0 : }
1990 : child->UnbindFromTree();
1991 : }
1992 0 : }
1993 : tmp->mFirstChild = nullptr;
1994 0 :
1995 : tmp->UnlinkOriginalDocumentIfStatic();
1996 0 :
1997 0 : tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
1998 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
1999 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
2000 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaybeEndOutermostXBLUpdateRunner)
2001 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
2002 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
2003 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
2004 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
2005 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
2006 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
2007 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
2008 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
2009 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds);
2010 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
2011 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
2012 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
2013 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
2014 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
2015 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
2016 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
2017 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle);
2018 0 :
2019 : tmp->mParentDocument = nullptr;
2020 0 :
2021 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
2022 0 :
2023 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
2024 0 :
2025 : tmp->ClearAllBoxObjects();
2026 0 :
2027 0 : if (tmp->mListenerManager) {
2028 0 : tmp->mListenerManager->Disconnect();
2029 0 : tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
2030 : tmp->mListenerManager = nullptr;
2031 : }
2032 0 :
2033 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
2034 0 :
2035 0 : if (tmp->mStyleSheetSetList) {
2036 0 : tmp->mStyleSheetSetList->Disconnect();
2037 : tmp->mStyleSheetSetList = nullptr;
2038 : }
2039 0 :
2040 0 : delete tmp->mSubDocuments;
2041 : tmp->mSubDocuments = nullptr;
2042 0 :
2043 0 : tmp->mFrameRequestCallbacks.Clear();
2044 : MOZ_RELEASE_ASSERT(!tmp->mFrameRequestCallbacksScheduled,
2045 : "How did we get here without our presshell going away "
2046 : "first?");
2047 0 :
2048 : tmp->mRadioGroups.Clear();
2049 :
2050 : // nsDocument has a pretty complex destructor, so we're going to
2051 : // assume that *most* cycles you actually want to break somewhere
2052 : // else, and not unlink an awful lot here.
2053 0 :
2054 0 : tmp->mIdentifierMap.Clear();
2055 : tmp->mExpandoAndGeneration.OwnerUnlinked();
2056 0 :
2057 0 : if (tmp->mAnimationController) {
2058 : tmp->mAnimationController->Unlink();
2059 : }
2060 0 :
2061 : tmp->mPendingTitleChangeEvent.Revoke();
2062 0 :
2063 0 : if (tmp->mCSSLoader) {
2064 0 : tmp->mCSSLoader->DropDocumentReference();
2065 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
2066 : }
2067 :
2068 : // We own only the items in mDOMMediaQueryLists that have listeners;
2069 : // this reference is managed by their AddListener and RemoveListener
2070 0 : // methods.
2071 : for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
2072 0 : MediaQueryList* next =
2073 0 : static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext();
2074 0 : mql->Disconnect();
2075 : mql = next;
2076 : }
2077 0 :
2078 0 : tmp->mInUnlinkOrDeletion = false;
2079 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2080 :
2081 60 : nsresult
2082 : nsDocument::Init()
2083 0 : {
2084 : if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
2085 : return NS_ERROR_ALREADY_INITIALIZED;
2086 : }
2087 :
2088 60 : // Force initialization.
2089 : nsINode::nsSlots* slots = Slots();
2090 :
2091 : // Prepend self as mutation-observer whether we need it or not (some
2092 : // subclasses currently do, other don't). This is because the code in
2093 : // nsNodeUtils always notifies the first observer first, expecting the
2094 60 : // first observer to be the document.
2095 : NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
2096 : NS_ERROR_OUT_OF_MEMORY);
2097 :
2098 60 :
2099 120 : mOnloadBlocker = new nsOnloadBlocker();
2100 : mCSSLoader = new mozilla::css::Loader(this);
2101 0 : // Assume we're not quirky, until we know otherwise
2102 : mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2103 0 :
2104 : mStyleImageLoader = new mozilla::css::ImageLoader(this);
2105 0 :
2106 60 : mNodeInfoManager = new nsNodeInfoManager();
2107 0 : nsresult rv = mNodeInfoManager->Init(this);
2108 : NS_ENSURE_SUCCESS(rv, rv);
2109 :
2110 60 : // mNodeInfo keeps NodeInfoManager alive!
2111 120 : mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2112 0 : NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2113 : MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
2114 : "Bad NodeType in aNodeInfo");
2115 60 :
2116 : NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2117 :
2118 : // Set this when document is initialized and value stays the same for the
2119 60 : // lifetime of the document.
2120 : mIsShadowDOMEnabled = nsContentUtils::IsShadowDOMEnabled();
2121 :
2122 : // If after creation the owner js global is not set for a document
2123 : // we use the default compartment for this document, instead of creating
2124 : // wrapper in some random compartment when the document is exposed to js
2125 120 : // via some events.
2126 60 : nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2127 0 : NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2128 0 : mScopeObject = do_GetWeakReference(global);
2129 : MOZ_ASSERT(mScopeObject);
2130 0 :
2131 : mScriptLoader = new dom::ScriptLoader(this);
2132 0 :
2133 : mozilla::HoldJSObjects(this);
2134 0 :
2135 : return NS_OK;
2136 : }
2137 :
2138 0 : void
2139 : nsIDocument::DeleteAllProperties()
2140 0 : {
2141 0 : PropertyTable().DeleteAllProperties();
2142 : }
2143 :
2144 0 : void
2145 : nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
2146 0 : {
2147 0 : PropertyTable().DeleteAllPropertiesFor(aNode);
2148 : }
2149 :
2150 0 : bool
2151 : nsIDocument::IsVisibleConsideringAncestors() const
2152 0 : {
2153 0 : const nsIDocument *parent = this;
2154 0 : do {
2155 : if (!parent->IsVisible()) {
2156 : return false;
2157 : }
2158 : } while ((parent = parent->GetParentDocument()));
2159 :
2160 : return true;
2161 : }
2162 :
2163 38 : void
2164 : nsIDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
2165 0 : {
2166 76 : nsCOMPtr<nsIURI> uri;
2167 0 : nsCOMPtr<nsIPrincipal> principal;
2168 : if (aChannel) {
2169 : // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2170 : // nsScriptSecurityManager::GetChannelResultPrincipal.
2171 38 : // Note: this should match nsDocShell::OnLoadingSite
2172 : NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2173 0 :
2174 38 : bool isWyciwyg = false;
2175 0 : uri->SchemeIs("wyciwyg", &isWyciwyg);
2176 0 : if (isWyciwyg) {
2177 : nsCOMPtr<nsIURI> cleanURI;
2178 0 : nsresult rv =
2179 0 : nsContentUtils::RemoveWyciwygScheme(uri, getter_AddRefs(cleanURI));
2180 0 : if (NS_SUCCEEDED(rv)) {
2181 : uri = cleanURI;
2182 : }
2183 : }
2184 :
2185 38 : nsIScriptSecurityManager *securityManager =
2186 38 : nsContentUtils::GetSecurityManager();
2187 0 : if (securityManager) {
2188 0 : securityManager->GetChannelResultPrincipal(aChannel,
2189 : getter_AddRefs(principal));
2190 : }
2191 : }
2192 38 :
2193 : principal = MaybeDowngradePrincipal(principal);
2194 0 :
2195 : ResetToURI(uri, aLoadGroup, principal);
2196 :
2197 : // Note that, since mTiming does not change during a reset, the
2198 : // navigationStart time remains unchanged and therefore any future new
2199 38 : // timeline will have the same global clock time as the old one.
2200 : mDocumentTimeline = nullptr;
2201 0 :
2202 38 : nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2203 0 : if (bag) {
2204 0 : nsCOMPtr<nsIURI> baseURI;
2205 0 : bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2206 0 : NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2207 0 : if (baseURI) {
2208 0 : mDocumentBaseURI = baseURI;
2209 : mChromeXHRDocBaseURI = nullptr;
2210 : }
2211 : }
2212 38 :
2213 38 : mChannel = aChannel;
2214 : }
2215 :
2216 46 : void
2217 : nsIDocument::ResetToURI(nsIURI* aURI,
2218 : nsILoadGroup* aLoadGroup,
2219 : nsIPrincipal* aPrincipal)
2220 46 : {
2221 : MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
2222 0 :
2223 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2224 : ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
2225 46 :
2226 : mSecurityInfo = nullptr;
2227 0 :
2228 : mDocumentLoadGroup = nullptr;
2229 :
2230 : // Delete references to sub-documents and kill the subdocument map,
2231 46 : // if any. It holds strong references
2232 46 : delete mSubDocuments;
2233 : mSubDocuments = nullptr;
2234 :
2235 : // Destroy link map now so we don't waste time removing
2236 46 : // links one by one
2237 : DestroyElementMaps();
2238 0 :
2239 46 : bool oldVal = mInUnlinkOrDeletion;
2240 0 : mInUnlinkOrDeletion = true;
2241 : uint32_t count = mChildren.ChildCount();
2242 0 : { // Scope for update
2243 : MOZ_AUTO_DOC_UPDATE(this, true);
2244 :
2245 46 : // Invalidate cached array of child nodes
2246 : InvalidateChildNodes();
2247 0 :
2248 0 : for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
2249 : nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
2250 0 :
2251 : nsIContent* previousSibling = content->GetPreviousSibling();
2252 0 :
2253 0 : if (nsINode::GetFirstChild() == content) {
2254 : mFirstChild = content->GetNextSibling();
2255 0 : }
2256 0 : mChildren.RemoveChildAt(i);
2257 : if (content == mCachedRootElement) {
2258 : // Immediately clear mCachedRootElement, now that it's been removed
2259 : // from mChildren, so that GetRootElement() will stop returning this
2260 0 : // now-stale value.
2261 : mCachedRootElement = nullptr;
2262 0 : }
2263 0 : nsNodeUtils::ContentRemoved(this, content, previousSibling);
2264 : content->UnbindFromTree();
2265 0 : }
2266 : MOZ_ASSERT(!mCachedRootElement,
2267 : "After removing all children, there should be no root elem");
2268 46 : }
2269 : mInUnlinkOrDeletion = oldVal;
2270 :
2271 46 : // Reset our stylesheets
2272 : ResetStylesheetsToURI(aURI);
2273 :
2274 92 : // Release the listener manager
2275 0 : if (mListenerManager) {
2276 0 : mListenerManager->Disconnect();
2277 : mListenerManager = nullptr;
2278 : }
2279 :
2280 46 : // Release the stylesheets list.
2281 : mDOMStyleSheets = nullptr;
2282 :
2283 : // Release our principal after tearing down the document, rather than before.
2284 : // This ensures that, during teardown, the document and the dying window (which
2285 : // already nulled out its document pointer and cached the principal) have
2286 46 : // matching principals.
2287 : SetPrincipal(nullptr);
2288 :
2289 46 : // Clear the original URI so SetDocumentURI sets it.
2290 : mOriginalURI = nullptr;
2291 0 :
2292 46 : SetDocumentURI(aURI);
2293 : mChromeXHRDocURI = nullptr;
2294 : // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2295 46 : // mDocumentURI.
2296 46 : mDocumentBaseURI = nullptr;
2297 : mChromeXHRDocBaseURI = nullptr;
2298 0 :
2299 45 : if (aLoadGroup) {
2300 : mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2301 : // there was an assertion here that aLoadGroup was not null. This
2302 : // is no longer valid: nsDocShell::SetDocument does not create a
2303 : // load group, and it works just fine
2304 :
2305 : // XXXbz what does "just fine" mean exactly? And given that there
2306 : // is no nsDocShell::SetDocument, what is this talking about?
2307 45 :
2308 : if (IsContentDocument()) {
2309 : // Inform the associated request context about this load start so
2310 : // any of its internal load progress flags gets reset.
2311 4 : nsCOMPtr<nsIRequestContextService> rcsvc =
2312 2 : do_GetService("@mozilla.org/network/request-context-service;1");
2313 0 : if (rcsvc) {
2314 0 : nsCOMPtr<nsIRequestContext> rc;
2315 0 : rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
2316 0 : if (rc) {
2317 : rc->BeginLoad();
2318 : }
2319 : }
2320 : }
2321 : }
2322 46 :
2323 : mLastModified.Truncate();
2324 : // XXXbz I guess we're assuming that the caller will either pass in
2325 46 : // a channel with a useful type or call SetContentType?
2326 46 : SetContentTypeInternal(EmptyCString());
2327 0 : mContentLanguage.Truncate();
2328 0 : mBaseTarget.Truncate();
2329 : mReferrer.Truncate();
2330 0 :
2331 : mXMLDeclarationBits = 0;
2332 :
2333 46 : // Now get our new principal
2334 42 : if (aPrincipal) {
2335 : SetPrincipal(aPrincipal);
2336 : } else {
2337 4 : nsIScriptSecurityManager *securityManager =
2338 4 : nsContentUtils::GetSecurityManager();
2339 0 : if (securityManager) {
2340 : nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2341 0 :
2342 8 : if (!loadContext && aLoadGroup) {
2343 0 : nsCOMPtr<nsIInterfaceRequestor> cbs;
2344 0 : aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2345 : loadContext = do_GetInterface(cbs);
2346 : }
2347 4 :
2348 : MOZ_ASSERT(loadContext,
2349 : "must have a load context or pass in an explicit principal");
2350 8 :
2351 : nsCOMPtr<nsIPrincipal> principal;
2352 0 : nsresult rv = securityManager->
2353 12 : GetLoadContextCodebasePrincipal(mDocumentURI, loadContext,
2354 0 : getter_AddRefs(principal));
2355 0 : if (NS_SUCCEEDED(rv)) {
2356 : SetPrincipal(principal);
2357 : }
2358 : }
2359 : }
2360 92 :
2361 0 : if (mFontFaceSet) {
2362 : mFontFaceSet->RefreshStandardFontLoadPrincipal();
2363 : }
2364 :
2365 46 : // Refresh the principal on the realm.
2366 0 : if (nsPIDOMWindowInner* win = GetInnerWindow()) {
2367 : nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal();
2368 0 : }
2369 : }
2370 :
2371 41 : already_AddRefed<nsIPrincipal>
2372 : nsIDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal)
2373 0 : {
2374 : if (!aPrincipal) {
2375 : return nullptr;
2376 : }
2377 :
2378 : // We can't load a document with an expanded principal. If we're given one,
2379 : // automatically downgrade it to the last principal it subsumes (which is the
2380 41 : // extension principal, in the case of extension content scripts).
2381 41 : auto* basePrin = BasePrincipal::Cast(aPrincipal);
2382 0 : if (basePrin->Is<ExpandedPrincipal>()) {
2383 : MOZ_DIAGNOSTIC_ASSERT(false, "Should never try to create a document with "
2384 : "an expanded principal");
2385 :
2386 : auto* expanded = basePrin->As<ExpandedPrincipal>();
2387 : return do_AddRef(expanded->WhiteList().LastElement());
2388 : }
2389 41 :
2390 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
2391 : // We basically want the parent document here, but because this is very
2392 : // early in the load, GetParentDocument() returns null, so we use the
2393 24 : // docshell hierarchy to get this information instead.
2394 8 : if (mDocumentContainer) {
2395 0 : nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
2396 0 : mDocumentContainer->GetParent(getter_AddRefs(parentDocShellItem));
2397 0 : nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentDocShellItem);
2398 0 : if (parentDocShell) {
2399 0 : nsCOMPtr<nsIDocument> parentDoc;
2400 0 : parentDoc = parentDocShell->GetDocument();
2401 0 : if (!parentDoc ||
2402 : !nsContentUtils::IsSystemPrincipal(parentDoc->NodePrincipal())) {
2403 0 : nsCOMPtr<nsIPrincipal> nullPrincipal =
2404 0 : do_CreateInstance("@mozilla.org/nullprincipal;1");
2405 : return nullPrincipal.forget();
2406 : }
2407 : }
2408 : }
2409 82 : }
2410 41 : nsCOMPtr<nsIPrincipal> principal(aPrincipal);
2411 : return principal.forget();
2412 : }
2413 :
2414 0 : void
2415 : nsIDocument::RemoveDocStyleSheetsFromStyleSets()
2416 : {
2417 0 : // The stylesheets should forget us
2418 0 : for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2419 : sheet->ClearAssociatedDocumentOrShadowRoot();
2420 0 :
2421 0 : if (sheet->IsApplicable()) {
2422 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
2423 0 : if (shell) {
2424 : shell->StyleSet()->RemoveDocStyleSheet(sheet);
2425 : }
2426 : }
2427 : // XXX Tell observers?
2428 0 : }
2429 : }
2430 :
2431 0 : void
2432 : nsIDocument::RemoveStyleSheetsFromStyleSets(
2433 : const nsTArray<RefPtr<StyleSheet>>& aSheets,
2434 : SheetType aType)
2435 : {
2436 0 : // The stylesheets should forget us
2437 0 : for (StyleSheet* sheet : Reversed(aSheets)) {
2438 : sheet->ClearAssociatedDocumentOrShadowRoot();
2439 0 :
2440 0 : if (sheet->IsApplicable()) {
2441 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
2442 0 : if (shell) {
2443 : shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2444 : }
2445 : }
2446 : // XXX Tell observers?
2447 0 : }
2448 : }
2449 :
2450 49 : void
2451 : nsIDocument::ResetStylesheetsToURI(nsIURI* aURI)
2452 0 : {
2453 : MOZ_ASSERT(aURI);
2454 0 :
2455 : if (mStyleSetFilled) {
2456 : // Skip removing style sheets from the style set if we know we haven't
2457 : // filled the style set. (This allows us to avoid calling
2458 0 : // GetStyleBackendType() too early.)
2459 0 : RemoveDocStyleSheetsFromStyleSets();
2460 0 : RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, SheetType::Agent);
2461 0 : RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent);
2462 0 : RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User);
2463 : RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc);
2464 0 :
2465 : if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2466 0 : RemoveStyleSheetsFromStyleSets(
2467 : *sheetService->AuthorStyleSheets(), SheetType::Doc);
2468 : }
2469 0 :
2470 : mStyleSetFilled = false;
2471 : }
2472 :
2473 49 : // Release all the sheets
2474 49 : mStyleSheets.Clear();
2475 0 : mOnDemandBuiltInUASheets.Clear();
2476 0 : for (auto& sheets : mAdditionalSheets) {
2477 : sheets.Clear();
2478 : }
2479 :
2480 : // NOTE: We don't release the catalog sheets. It doesn't really matter
2481 : // now, but it could in the future -- in which case not releasing them
2482 : // is probably the right thing to do.
2483 :
2484 98 : // Now reset our inline style and attribute sheets.
2485 0 : if (mAttrStyleSheet) {
2486 0 : mAttrStyleSheet->Reset();
2487 : mAttrStyleSheet->SetOwningDocument(this);
2488 0 : } else {
2489 : mAttrStyleSheet = new nsHTMLStyleSheet(this);
2490 : }
2491 98 :
2492 49 : if (!mStyleAttrStyleSheet) {
2493 : mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2494 : }
2495 :
2496 49 : // Now set up our style sets
2497 0 : if (nsIPresShell* shell = GetShell()) {
2498 0 : FillStyleSet(shell->StyleSet());
2499 0 : if (shell->StyleSet()->StyleSheetsHaveChanged()) {
2500 : shell->ApplicableStylesChanged();
2501 : }
2502 49 : }
2503 : }
2504 :
2505 81 : static void
2506 : AppendSheetsToStyleSet(ServoStyleSet* aStyleSet,
2507 : const nsTArray<RefPtr<StyleSheet>>& aSheets,
2508 : SheetType aType)
2509 243 : {
2510 0 : for (StyleSheet* sheet : Reversed(aSheets)) {
2511 : aStyleSet->AppendStyleSheet(aType, sheet);
2512 0 : }
2513 : }
2514 :
2515 :
2516 27 : void
2517 : nsIDocument::FillStyleSet(ServoStyleSet* aStyleSet)
2518 0 : {
2519 27 : MOZ_ASSERT(aStyleSet, "Must have a style set");
2520 : MOZ_ASSERT(aStyleSet->SheetCount(SheetType::Doc) == 0,
2521 : "Style set already has document sheets?");
2522 27 :
2523 : MOZ_ASSERT(!mStyleSetFilled);
2524 0 :
2525 0 : for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2526 0 : if (sheet->IsApplicable()) {
2527 : aStyleSet->AddDocStyleSheet(sheet, this);
2528 : }
2529 : }
2530 27 :
2531 : if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2532 0 : nsTArray<RefPtr<StyleSheet>>& sheets =
2533 81 : *sheetService->AuthorStyleSheets();
2534 0 : for (StyleSheet* sheet : sheets) {
2535 : aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
2536 : }
2537 : }
2538 :
2539 108 : // Iterate backwards to maintain order
2540 0 : for (StyleSheet* sheet : Reversed(mOnDemandBuiltInUASheets)) {
2541 0 : if (sheet->IsApplicable()) {
2542 : aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
2543 : }
2544 : }
2545 27 :
2546 27 : AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2547 0 : SheetType::Agent);
2548 0 : AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2549 0 : SheetType::User);
2550 0 : AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2551 : SheetType::Doc);
2552 0 :
2553 27 : mStyleSetFilled = true;
2554 : }
2555 :
2556 4 : static void
2557 : WarnIfSandboxIneffective(nsIDocShell* aDocShell,
2558 : uint32_t aSandboxFlags,
2559 : nsIChannel* aChannel)
2560 : {
2561 : // If the document is sandboxed (via the HTML5 iframe sandbox
2562 : // attribute) and both the allow-scripts and allow-same-origin
2563 : // keywords are supplied, the sandboxed document can call into its
2564 : // parent document and remove its sandboxing entirely - we print a
2565 4 : // warning to the web console in this case.
2566 4 : if (aSandboxFlags & SANDBOXED_NAVIGATION &&
2567 : !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
2568 0 : !(aSandboxFlags & SANDBOXED_ORIGIN)) {
2569 0 : nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2570 0 : aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2571 0 : nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentAsItem);
2572 0 : if (!parentDocShell) {
2573 : return;
2574 : }
2575 :
2576 0 : // Don't warn if our parent is not the top-level document.
2577 0 : nsCOMPtr<nsIDocShellTreeItem> grandParentAsItem;
2578 0 : parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem));
2579 0 : if (grandParentAsItem) {
2580 : return;
2581 : }
2582 0 :
2583 0 : nsCOMPtr<nsIChannel> parentChannel;
2584 0 : parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
2585 0 : if (!parentChannel) {
2586 : return;
2587 0 : }
2588 0 : nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
2589 : if (NS_FAILED(rv)) {
2590 : return;
2591 : }
2592 0 :
2593 0 : nsCOMPtr<nsIDocument> parentDocument = parentDocShell->GetDocument();
2594 0 : nsCOMPtr<nsIURI> iframeUri;
2595 0 : parentChannel->GetURI(getter_AddRefs(iframeUri));
2596 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2597 : NS_LITERAL_CSTRING("Iframe Sandbox"),
2598 : parentDocument,
2599 : nsContentUtils::eSECURITY_PROPERTIES,
2600 0 : "BothAllowScriptsAndSameOriginPresent",
2601 : nullptr, 0, iframeUri);
2602 : }
2603 : }
2604 :
2605 4 : bool
2606 16 : nsIDocument::IsSynthesized() {
2607 0 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->GetLoadInfo() : nullptr;
2608 0 : return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized();
2609 0 : }
2610 0 :
2611 : bool
2612 0 : nsDocument::IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject)
2613 : {
2614 : JS::Rooted<JSObject*> obj(aCx, aObject);
2615 :
2616 24 : JSAutoRealm ar(aCx, obj);
2617 : JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
2618 0 : nsCOMPtr<nsPIDOMWindowInner> window =
2619 : do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
2620 0 :
2621 48 : nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
2622 : if (!doc) {
2623 0 : return false;
2624 : }
2625 0 :
2626 24 : return doc->IsShadowDOMEnabled();
2627 : }
2628 :
2629 : bool
2630 13 : nsDocument::IsShadowDOMEnabled(const nsINode* aNode)
2631 : {
2632 : return aNode->OwnerDoc()->IsShadowDOMEnabled();
2633 : }
2634 116477 :
2635 : nsresult
2636 0 : nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2637 : nsILoadGroup* aLoadGroup,
2638 : nsISupports* aContainer,
2639 : nsIStreamListener **aDocListener,
2640 49 : bool aReset, nsIContentSink* aSink)
2641 : {
2642 : if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
2643 : nsCOMPtr<nsIURI> uri;
2644 : aChannel->GetURI(getter_AddRefs(uri));
2645 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2646 49 : ("DOCUMENT %p StartDocumentLoad %s",
2647 0 : this, uri ? uri->GetSpecOrDefault().get() : ""));
2648 0 : }
2649 0 :
2650 : MOZ_ASSERT(NodePrincipal()->GetAppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2651 : "Document should never have UNKNOWN_APP_ID");
2652 :
2653 : MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2654 49 : "Bad readyState");
2655 : SetReadyStateInternal(READYSTATE_LOADING);
2656 :
2657 49 : if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2658 : mLoadedAsData = true;
2659 0 : // We need to disable script & style loading in this case.
2660 : // We leave them disabled even in EndLoad(), and let anyone
2661 0 : // who puts the document on display to worry about enabling.
2662 12 :
2663 : // Do not load/process scripts when loading as data
2664 : ScriptLoader()->SetEnabled(false);
2665 :
2666 : // styles
2667 : CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
2668 24 : } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2669 : // Allow CSS, but not scripts
2670 : ScriptLoader()->SetEnabled(false);
2671 24 : }
2672 37 :
2673 : mMayStartLayout = false;
2674 0 : MOZ_ASSERT(!mReadyForIdle, "We should never hit DOMContentLoaded before this point");
2675 :
2676 : if (aReset) {
2677 49 : Reset(aChannel, aLoadGroup);
2678 98 : }
2679 :
2680 0 : nsAutoCString contentType;
2681 38 : nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2682 : if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2683 : NS_LITERAL_STRING("contentType"), contentType))) ||
2684 98 : NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2685 98 : // XXX this is only necessary for viewsource:
2686 0 : nsACString::const_iterator start, end, semicolon;
2687 0 : contentType.BeginReading(start);
2688 0 : contentType.EndReading(end);
2689 : semicolon = start;
2690 0 : FindCharInReadable(';', semicolon, end);
2691 49 : SetContentTypeInternal(Substring(start, semicolon));
2692 0 : }
2693 0 :
2694 0 : RetrieveRelevantHeaders(aChannel);
2695 0 :
2696 : mChannel = aChannel;
2697 : nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2698 49 : if (inStrmChan) {
2699 : bool isSrcdocChannel;
2700 0 : inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2701 196 : if (isSrcdocChannel) {
2702 0 : mIsSrcdocDocument = true;
2703 : }
2704 0 : }
2705 12 :
2706 0 : if (mChannel) {
2707 : nsLoadFlags loadFlags;
2708 : mChannel->GetLoadFlags(&loadFlags);
2709 : bool isDocument = false;
2710 98 : mChannel->GetIsDocument(&isDocument);
2711 : if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
2712 0 : isDocument &&
2713 49 : IsSynthesized() &&
2714 0 : XRE_IsContentProcess()) {
2715 0 : ContentChild::UpdateCookieStatus(mChannel);
2716 0 : }
2717 0 : }
2718 0 :
2719 0 : // If this document is being loaded by a docshell, copy its sandbox flags
2720 : // to the document, and store the fullscreen enabled flag. These are
2721 : // immutable after being set here.
2722 : nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2723 :
2724 : // If this is an error page, don't inherit sandbox flags from docshell
2725 : nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2726 98 : if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
2727 : nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2728 : NS_ENSURE_SUCCESS(rv, rv);
2729 98 : WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
2730 53 : }
2731 0 :
2732 0 : // The CSP directive upgrade-insecure-requests not only applies to the
2733 0 : // toplevel document, but also to nested documents. Let's propagate that
2734 : // flag from the parent to the nested document.
2735 : nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
2736 : if (treeItem) {
2737 : nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
2738 : treeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
2739 98 : if (sameTypeParent) {
2740 49 : nsIDocument* doc = sameTypeParent->GetDocument();
2741 0 : mBlockAllMixedContent = doc->GetBlockAllMixedContent(false);
2742 0 : // if the parent document makes use of block-all-mixed-content
2743 0 : // then subdocument preloads should always be blocked.
2744 0 : mBlockAllMixedContentPreloads =
2745 0 : mBlockAllMixedContent || doc->GetBlockAllMixedContent(true);
2746 :
2747 : mUpgradeInsecureRequests = doc->GetUpgradeInsecureRequests(false);
2748 1 : // if the parent document makes use of upgrade-insecure-requests
2749 2 : // then subdocument preloads should always be upgraded.
2750 : mUpgradeInsecurePreloads =
2751 0 : mUpgradeInsecureRequests || doc->GetUpgradeInsecureRequests(true);
2752 : }
2753 : }
2754 1 :
2755 2 : // If this is not a data document, set CSP.
2756 : if (!mLoadedAsData) {
2757 : nsresult rv = InitCSP(aChannel);
2758 : NS_ENSURE_SUCCESS(rv, rv);
2759 : }
2760 49 :
2761 37 : // XFO needs to be checked after CSP because it is ignored if
2762 0 : // the CSP defines frame-ancestors.
2763 : if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
2764 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2765 : ("XFO doesn't like frame's ancestry, not loading."));
2766 : // stop! ERROR page!
2767 98 : aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2768 0 : }
2769 :
2770 : // Perform a async flash classification based on the doc principal
2771 0 : // in an early stage to reduce the blocking time.
2772 : mFlashClassification = FlashClassification::Unclassified;
2773 : mPrincipalFlashClassifier->AsyncClassify(GetPrincipal());
2774 :
2775 : return NS_OK;
2776 49 : }
2777 49 :
2778 : void
2779 0 : nsIDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
2780 : {
2781 : for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2782 : nsAutoString messageTag;
2783 0 : aMessages[i]->GetTag(messageTag);
2784 :
2785 0 : nsAutoString category;
2786 0 : aMessages[i]->GetCategory(category);
2787 0 :
2788 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2789 0 : NS_ConvertUTF16toUTF8(category),
2790 0 : this, nsContentUtils::eSECURITY_PROPERTIES,
2791 : NS_ConvertUTF16toUTF8(messageTag).get());
2792 0 : }
2793 0 : }
2794 :
2795 0 : void
2796 : nsIDocument::ApplySettingsFromCSP(bool aSpeculative)
2797 0 : {
2798 : nsresult rv = NS_OK;
2799 : if (!aSpeculative) {
2800 2 : // 1) apply settings from regular CSP
2801 : nsCOMPtr<nsIContentSecurityPolicy> csp;
2802 0 : rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
2803 2 : NS_ENSURE_SUCCESS_VOID(rv);
2804 : if (csp) {
2805 0 : // Set up 'block-all-mixed-content' if not already inherited
2806 4 : // from the parent context or set by any other CSP.
2807 0 : if (!mBlockAllMixedContent) {
2808 0 : rv = csp->GetBlockAllMixedContent(&mBlockAllMixedContent);
2809 : NS_ENSURE_SUCCESS_VOID(rv);
2810 : }
2811 2 : if (!mBlockAllMixedContentPreloads) {
2812 2 : mBlockAllMixedContentPreloads = mBlockAllMixedContent;
2813 0 : }
2814 :
2815 0 : // Set up 'upgrade-insecure-requests' if not already inherited
2816 2 : // from the parent context or set by any other CSP.
2817 : if (!mUpgradeInsecureRequests) {
2818 : rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
2819 : NS_ENSURE_SUCCESS_VOID(rv);
2820 : }
2821 2 : if (!mUpgradeInsecurePreloads) {
2822 2 : mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
2823 0 : }
2824 : }
2825 0 : return;
2826 2 : }
2827 :
2828 : // 2) apply settings from speculative csp
2829 : nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
2830 : rv = NodePrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
2831 : NS_ENSURE_SUCCESS_VOID(rv);
2832 : if (preloadCsp) {
2833 0 : if (!mBlockAllMixedContentPreloads) {
2834 0 : rv = preloadCsp->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads);
2835 0 : NS_ENSURE_SUCCESS_VOID(rv);
2836 0 : }
2837 0 : if (!mUpgradeInsecurePreloads) {
2838 0 : rv = preloadCsp->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads);
2839 0 : NS_ENSURE_SUCCESS_VOID(rv);
2840 : }
2841 0 : }
2842 0 : }
2843 0 :
2844 : nsresult
2845 : nsIDocument::InitCSP(nsIChannel* aChannel)
2846 : {
2847 : MOZ_ASSERT(!mScriptGlobalObject,
2848 : "CSP must be initialized before mScriptGlobalObject is set!");
2849 37 : if (!CSPService::sCSPEnabled) {
2850 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2851 0 : ("CSP is disabled, skipping CSP init for document %p", this));
2852 : return NS_OK;
2853 0 : }
2854 0 :
2855 : nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2856 :
2857 : nsCOMPtr<nsIHttpChannel> httpChannel;
2858 : nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
2859 74 : if (NS_WARN_IF(NS_FAILED(rv))) {
2860 : return rv;
2861 0 : }
2862 37 :
2863 0 : if (httpChannel) {
2864 : Unused << httpChannel->GetResponseHeader(
2865 : NS_LITERAL_CSTRING("content-security-policy"),
2866 : tCspHeaderValue);
2867 37 :
2868 0 : Unused << httpChannel->GetResponseHeader(
2869 0 : NS_LITERAL_CSTRING("content-security-policy-report-only"),
2870 0 : tCspROHeaderValue);
2871 : }
2872 0 : NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2873 0 : NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2874 0 :
2875 : // Check if this is a document from a WebExtension.
2876 0 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
2877 74 : auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
2878 :
2879 : // Check if this is a signed content to apply default CSP.
2880 74 : bool applySignedContentCSP = false;
2881 37 : nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2882 : if (loadInfo && loadInfo->GetVerifySignedContent()) {
2883 : applySignedContentCSP = true;
2884 37 : }
2885 74 :
2886 0 : // If there's no CSP to apply, go ahead and return early
2887 0 : if (!addonPolicy &&
2888 : !applySignedContentCSP &&
2889 : cspHeaderValue.IsEmpty() &&
2890 : cspROHeaderValue.IsEmpty()) {
2891 111 : if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
2892 72 : nsCOMPtr<nsIURI> chanURI;
2893 0 : aChannel->GetURI(getter_AddRefs(chanURI));
2894 0 : nsAutoCString aspec;
2895 0 : chanURI->GetAsciiSpec(aspec);
2896 0 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2897 0 : ("no CSP for document, %s",
2898 0 : aspec.get()));
2899 0 : }
2900 0 :
2901 : return NS_OK;
2902 : }
2903 :
2904 : MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an add-on or CSP header specified %p", this));
2905 :
2906 : nsCOMPtr<nsIContentSecurityPolicy> csp;
2907 : rv = principal->EnsureCSP(static_cast<nsDocument*>(this), getter_AddRefs(csp));
2908 2 : NS_ENSURE_SUCCESS(rv, rv);
2909 :
2910 0 : // ----- if the doc is an addon, apply its CSP.
2911 4 : if (addonPolicy) {
2912 0 : nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
2913 :
2914 : nsAutoString addonCSP;
2915 2 : Unused << ExtensionPolicyService::GetSingleton().GetBaseCSP(addonCSP);
2916 4 : csp->AppendPolicy(addonCSP, false, false);
2917 :
2918 0 : csp->AppendPolicy(addonPolicy->ContentSecurityPolicy(), false, false);
2919 2 : }
2920 0 :
2921 : // ----- if the doc is a signed content, apply the default CSP.
2922 0 : // Note that when the content signing becomes a standard, we might have
2923 : // to restrict this enforcement to "remote content" only.
2924 : if (applySignedContentCSP) {
2925 : nsAutoString signedContentCSP;
2926 : Preferences::GetString("security.signed_content.CSP.default",
2927 : signedContentCSP);
2928 2 : csp->AppendPolicy(signedContentCSP, false, false);
2929 0 : }
2930 :
2931 0 : // ----- if there's a full-strength CSP header, apply it.
2932 0 : if (!cspHeaderValue.IsEmpty()) {
2933 : rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
2934 : NS_ENSURE_SUCCESS(rv, rv);
2935 : }
2936 2 :
2937 0 : // ----- if there's a report-only CSP header, apply it.
2938 0 : if (!cspROHeaderValue.IsEmpty()) {
2939 : rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
2940 : NS_ENSURE_SUCCESS(rv, rv);
2941 : }
2942 2 :
2943 0 : // ----- Enforce sandbox policy if supplied in CSP header
2944 0 : // The document may already have some sandbox flags set (e.g. if the document
2945 : // is an iframe with the sandbox attribute set). If we have a CSP sandbox
2946 : // directive, intersect the CSP sandbox flags with the existing flags. This
2947 : // corresponds to the _least_ permissive policy.
2948 : uint32_t cspSandboxFlags = SANDBOXED_NONE;
2949 : rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
2950 : NS_ENSURE_SUCCESS(rv, rv);
2951 :
2952 2 : // Probably the iframe sandbox attribute already caused the creation of a
2953 2 : // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
2954 0 : // and no one has been created yet.
2955 : bool needNewNullPrincipal =
2956 : (cspSandboxFlags & SANDBOXED_ORIGIN) && !(mSandboxFlags & SANDBOXED_ORIGIN);
2957 :
2958 : mSandboxFlags |= cspSandboxFlags;
2959 :
2960 2 : if (needNewNullPrincipal) {
2961 : principal = NullPrincipal::CreateWithInheritedAttributes(principal);
2962 0 : principal->SetCsp(csp);
2963 : SetPrincipal(principal);
2964 0 : }
2965 0 :
2966 0 : // ----- Enforce frame-ancestor policy on any applied policies
2967 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2968 : if (docShell) {
2969 : bool safeAncestry = false;
2970 :
2971 4 : // PermitsAncestry sends violation reports when necessary
2972 2 : rv = csp->PermitsAncestry(docShell, &safeAncestry);
2973 0 :
2974 : if (NS_FAILED(rv) || !safeAncestry) {
2975 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2976 4 : ("CSP doesn't like frame's ancestry, not loading."));
2977 : // stop! ERROR page!
2978 0 : aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2979 0 : }
2980 : }
2981 : ApplySettingsFromCSP(false);
2982 0 : return NS_OK;
2983 : }
2984 :
2985 2 : void
2986 : nsDocument::StopDocumentLoad()
2987 : {
2988 : if (mParser) {
2989 : mParserAborted = true;
2990 9 : mParser->Terminate();
2991 : }
2992 0 : }
2993 0 :
2994 0 : void
2995 : nsIDocument::SetDocumentURI(nsIURI* aURI)
2996 0 : {
2997 : nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
2998 : mDocumentURI = aURI;
2999 58 : nsIURI* newBase = GetDocBaseURI();
3000 :
3001 0 : bool equalBases = false;
3002 58 : // Changing just the ref of a URI does not change how relative URIs would
3003 0 : // resolve wrt to it, so we can treat the bases as equal as long as they're
3004 : // equal ignoring the ref.
3005 0 : if (oldBase && newBase) {
3006 : oldBase->EqualsExceptRef(newBase, &equalBases);
3007 : }
3008 : else {
3009 58 : equalBases = !oldBase && !newBase;
3010 1 : }
3011 :
3012 : // If this is the first time we're setting the document's URI, set the
3013 57 : // document's original URI.
3014 : if (!mOriginalURI)
3015 : mOriginalURI = mDocumentURI;
3016 :
3017 : // If changing the document's URI changed the base URI of the document, we
3018 116 : // need to refresh the hrefs of all the links on the page.
3019 58 : if (!equalBases) {
3020 : RefreshLinkHrefs();
3021 : }
3022 : }
3023 58 :
3024 57 : static void
3025 : GetFormattedTimeString(PRTime aTime, nsAString& aFormattedTimeString)
3026 0 : {
3027 : PRExplodedTime prtime;
3028 : PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime);
3029 0 : // "MM/DD/YYYY hh:mm:ss"
3030 : char formatedTime[24];
3031 : if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
3032 0 : prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
3033 : prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
3034 : CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
3035 0 : } else {
3036 0 : // If we for whatever reason failed to find the last modified time
3037 : // (or even the current time), fall back to what NS4.x returned.
3038 0 : aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
3039 : }
3040 : }
3041 :
3042 0 : void
3043 : nsIDocument::GetLastModified(nsAString& aLastModified) const
3044 0 : {
3045 : if (!mLastModified.IsEmpty()) {
3046 : aLastModified.Assign(mLastModified);
3047 0 : } else {
3048 : GetFormattedTimeString(PR_Now(), aLastModified);
3049 0 : }
3050 0 : }
3051 :
3052 0 : static void
3053 : IncrementExpandoGeneration(nsIDocument& aDoc)
3054 0 : {
3055 : ++static_cast<nsDocument&>(aDoc).mExpandoAndGeneration.generation;
3056 : }
3057 :
3058 : void
3059 46 : nsIDocument::AddToNameTable(Element* aElement, nsAtom* aName)
3060 : {
3061 : MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
3062 : "Only put elements that need to be exposed as document['name'] in "
3063 0 : "the named table.");
3064 :
3065 0 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
3066 :
3067 : // Null for out-of-memory
3068 : if (entry) {
3069 0 : if (!entry->HasNameElement() &&
3070 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3071 : IncrementExpandoGeneration(*this);
3072 0 : }
3073 0 : entry->AddNameElement(this, aElement);
3074 0 : }
3075 : }
3076 :
3077 0 : void
3078 : nsIDocument::RemoveFromNameTable(Element* aElement, nsAtom* aName)
3079 0 : {
3080 : // Speed up document teardown
3081 : if (mIdentifierMap.Count() == 0)
3082 0 : return;
3083 :
3084 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
3085 0 : if (!entry) // Could be false if the element was anonymous, hence never added
3086 : return;
3087 :
3088 0 : entry->RemoveNameElement(aElement);
3089 0 : if (!entry->HasNameElement() &&
3090 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3091 : IncrementExpandoGeneration(*this);
3092 0 : }
3093 0 : }
3094 0 :
3095 : void
3096 : nsIDocument::AddToIdTable(Element* aElement, nsAtom* aId)
3097 : {
3098 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
3099 :
3100 2692 : if (entry) { /* True except on OOM */
3101 : if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3102 0 : !entry->HasNameElement() &&
3103 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3104 0 : IncrementExpandoGeneration(*this);
3105 5384 : }
3106 0 : entry->AddIdElement(aElement);
3107 0 : }
3108 : }
3109 :
3110 2692 : void
3111 : nsIDocument::RemoveFromIdTable(Element* aElement, nsAtom* aId)
3112 0 : {
3113 : NS_ASSERTION(aId, "huhwhatnow?");
3114 :
3115 138 : // Speed up document teardown
3116 : if (mIdentifierMap.Count() == 0) {
3117 0 : return;
3118 : }
3119 :
3120 276 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
3121 : if (!entry) // Can be null for XML elements with changing ids.
3122 : return;
3123 :
3124 276 : entry->RemoveIdElement(aElement);
3125 138 : if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3126 : !entry->HasNameElement() &&
3127 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3128 111 : IncrementExpandoGeneration(*this);
3129 222 : }
3130 0 : if (entry->IsEmpty()) {
3131 0 : mIdentifierMap.RemoveEntry(entry);
3132 : }
3133 : }
3134 111 :
3135 111 : nsIPrincipal*
3136 : nsDocument::GetPrincipal()
3137 : {
3138 : return NodePrincipal();
3139 : }
3140 49 :
3141 : extern bool sDisablePrefetchHTTPSPref;
3142 0 :
3143 : void
3144 : nsIDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
3145 : {
3146 : if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3147 : nsCOMPtr<nsIURI> uri;
3148 107 : aNewPrincipal->GetURI(getter_AddRefs(uri));
3149 : bool isHTTPS;
3150 0 : if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
3151 122 : isHTTPS) {
3152 0 : mAllowDNSPrefetch = false;
3153 : }
3154 0 : }
3155 : mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3156 0 :
3157 : #ifdef DEBUG
3158 : // Validate that the docgroup is set correctly by calling its getter and
3159 107 : // triggering its sanity check.
3160 : //
3161 : // If we're setting the principal to null, we don't want to perform the check,
3162 : // as the document is entering an intermediate state where it does not have a
3163 : // principal. It will be given another real principal shortly which we will
3164 : // check. It's not unsafe to have a document which has a null principal in the
3165 : // same docgroup as another document, so this should not be a problem.
3166 : if (aNewPrincipal) {
3167 : GetDocGroup();
3168 : }
3169 : #endif
3170 107 : }
3171 61 :
3172 : mozilla::dom::DocGroup*
3173 : nsIDocument::GetDocGroup() const
3174 107 : {
3175 : #ifdef DEBUG
3176 : // Sanity check that we have an up-to-date and accurate docgroup
3177 897 : if (mDocGroup) {
3178 : nsAutoCString docGroupKey;
3179 :
3180 : // GetKey() can fail, e.g. after the TLD service has shut down.
3181 1794 : nsresult rv = mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
3182 1670 : if (NS_SUCCEEDED(rv)) {
3183 : MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
3184 : }
3185 835 : // XXX: Check that the TabGroup is correct as well!
3186 835 : }
3187 0 : #endif
3188 :
3189 : return mDocGroup;
3190 : }
3191 :
3192 : nsresult
3193 1794 : nsIDocument::Dispatch(TaskCategory aCategory,
3194 : already_AddRefed<nsIRunnable>&& aRunnable)
3195 : {
3196 : // Note that this method may be called off the main thread.
3197 149 : if (mDocGroup) {
3198 : return mDocGroup->Dispatch(aCategory, std::move(aRunnable));
3199 : }
3200 : return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
3201 298 : }
3202 64 :
3203 : nsISerialEventTarget*
3204 0 : nsIDocument::EventTargetFor(TaskCategory aCategory) const
3205 : {
3206 : if (mDocGroup) {
3207 : return mDocGroup->EventTargetFor(aCategory);
3208 53 : }
3209 : return DispatcherTrait::EventTargetFor(aCategory);
3210 0 : }
3211 38 :
3212 : AbstractThread*
3213 0 : nsIDocument::AbstractMainThreadFor(mozilla::TaskCategory aCategory)
3214 : {
3215 : MOZ_ASSERT(NS_IsMainThread());
3216 : if (mDocGroup) {
3217 2 : return mDocGroup->AbstractMainThreadFor(aCategory);
3218 : }
3219 0 : return DispatcherTrait::AbstractMainThreadFor(aCategory);
3220 4 : }
3221 0 :
3222 : void
3223 0 : nsIDocument::NoteScriptTrackingStatus(const nsACString& aURL, bool aIsTracking)
3224 : {
3225 : if (aIsTracking) {
3226 : mTrackingScripts.PutEntry(aURL);
3227 5 : } else {
3228 : MOZ_ASSERT(!mTrackingScripts.Contains(aURL));
3229 0 : }
3230 0 : }
3231 :
3232 0 : bool
3233 : nsIDocument::IsScriptTracking(const nsACString& aURL) const
3234 0 : {
3235 : return mTrackingScripts.Contains(aURL);
3236 : }
3237 1 :
3238 : NS_IMETHODIMP
3239 0 : nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
3240 : {
3241 : NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3242 :
3243 0 : return NS_OK;
3244 : }
3245 0 :
3246 : NS_IMETHODIMP
3247 0 : nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
3248 : {
3249 : mApplicationCache = aApplicationCache;
3250 :
3251 0 : return NS_OK;
3252 : }
3253 0 :
3254 : void
3255 0 : nsIDocument::GetContentType(nsAString& aContentType)
3256 : {
3257 : CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3258 : }
3259 23 :
3260 : void
3261 0 : nsIDocument::SetContentType(const nsAString& aContentType)
3262 23 : {
3263 : SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3264 : }
3265 0 :
3266 : bool
3267 0 : nsIDocument::GetAllowPlugins()
3268 0 : {
3269 : // First, we ask our docshell if it allows plugins.
3270 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3271 0 :
3272 : if (docShell) {
3273 : bool allowPlugins = false;
3274 0 : docShell->GetAllowPlugins(&allowPlugins);
3275 : if (!allowPlugins) {
3276 0 : return false;
3277 0 : }
3278 0 :
3279 0 : // If the docshell allows plugins, we check whether
3280 0 : // we are sandboxed and plugins should not be allowed.
3281 : if (mSandboxFlags & SANDBOXED_PLUGINS) {
3282 : return false;
3283 : }
3284 : }
3285 0 :
3286 : FlashClassification classification = DocumentFlashClassification();
3287 : if (classification == FlashClassification::Denied) {
3288 : return false;
3289 : }
3290 0 :
3291 0 : return true;
3292 : }
3293 :
3294 : bool
3295 0 : nsDocument::IsElementAnimateEnabled(JSContext* aCx, JSObject* /*unused*/)
3296 : {
3297 : MOZ_ASSERT(NS_IsMainThread());
3298 :
3299 8 : return nsContentUtils::IsSystemCaller(aCx) ||
3300 : nsContentUtils::AnimationsAPICoreEnabled() ||
3301 0 : nsContentUtils::AnimationsAPIElementAnimateEnabled();
3302 : }
3303 10 :
3304 8 : bool
3305 8 : nsDocument::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/)
3306 : {
3307 : MOZ_ASSERT(NS_IsMainThread());
3308 :
3309 40 : return nsContentUtils::IsSystemCaller(aCx) ||
3310 : nsContentUtils::AnimationsAPICoreEnabled();
3311 0 : }
3312 :
3313 56 : bool
3314 40 : nsDocument::IsWebAnimationsEnabled(CallerType aCallerType)
3315 : {
3316 : MOZ_ASSERT(NS_IsMainThread());
3317 :
3318 0 : return aCallerType == dom::CallerType::System ||
3319 : nsContentUtils::AnimationsAPICoreEnabled();
3320 0 : }
3321 :
3322 0 : DocumentTimeline*
3323 0 : nsIDocument::Timeline()
3324 : {
3325 : if (!mDocumentTimeline) {
3326 : mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0));
3327 15 : }
3328 :
3329 0 : return mDocumentTimeline;
3330 0 : }
3331 :
3332 : void
3333 30 : nsIDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
3334 : {
3335 : // Hold a strong ref for the root element since Element::GetAnimations() calls
3336 : // FlushPendingNotifications() which may destroy the element.
3337 0 : RefPtr<Element> root = GetRootElement();
3338 : if (!root) {
3339 : return;
3340 : }
3341 0 : AnimationFilter filter;
3342 0 : filter.mSubtree = true;
3343 0 : root->GetAnimations(filter, aAnimations);
3344 : }
3345 0 :
3346 0 : SVGSVGElement*
3347 0 : nsIDocument::GetSVGRootElement() const
3348 : {
3349 : Element* root = GetRootElement();
3350 : if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
3351 0 : return nullptr;
3352 : }
3353 0 : return static_cast<SVGSVGElement*>(root);
3354 0 : }
3355 :
3356 : /* Return true if the document is in the focused top-level window, and is an
3357 0 : * ancestor of the focused DOMWindow. */
3358 : bool
3359 : nsIDocument::HasFocus(ErrorResult& rv) const
3360 : {
3361 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3362 : if (!fm) {
3363 0 : rv.Throw(NS_ERROR_NOT_AVAILABLE);
3364 : return false;
3365 15 : }
3366 15 :
3367 0 : // Is there a focused DOMWindow?
3368 0 : nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3369 : fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3370 : if (!focusedWindow) {
3371 : return false;
3372 0 : }
3373 30 :
3374 15 : nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(focusedWindow);
3375 :
3376 : // Are we an ancestor of the focused DOMWindow?
3377 : for (nsIDocument* currentDoc = piWindow->GetDoc(); currentDoc;
3378 0 : currentDoc = currentDoc->GetParentDocument()) {
3379 : if (currentDoc == this) {
3380 : // Yes, we are an ancestor
3381 0 : return true;
3382 : }
3383 5 : }
3384 :
3385 : return false;
3386 : }
3387 :
3388 : TimeStamp
3389 : nsIDocument::LastFocusTime() const
3390 : {
3391 : return mLastFocusTime;
3392 : }
3393 0 :
3394 : void
3395 0 : nsIDocument::SetLastFocusTime(const TimeStamp& aFocusTime)
3396 : {
3397 : MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
3398 : MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
3399 0 : aFocusTime >= mLastFocusTime);
3400 : mLastFocusTime = aFocusTime;
3401 1 : }
3402 2 :
3403 : void
3404 0 : nsIDocument::GetReferrer(nsAString& aReferrer) const
3405 0 : {
3406 : if (mIsSrcdocDocument && mParentDocument)
3407 : mParentDocument->GetReferrer(aReferrer);
3408 2 : else
3409 : CopyUTF8toUTF16(mReferrer, aReferrer);
3410 2 : }
3411 :
3412 : nsresult
3413 2 : nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
3414 2 : {
3415 : if (mIsSrcdocDocument) {
3416 : nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3417 0 : if (inStrmChan) {
3418 : return inStrmChan->GetSrcdocData(aSrcdocData);
3419 0 : }
3420 0 : }
3421 0 : aSrcdocData = VoidString();
3422 0 : return NS_OK;
3423 : }
3424 :
3425 0 : Element*
3426 0 : nsIDocument::GetActiveElement()
3427 : {
3428 : // Get the focused element.
3429 : Element* focusedElement = GetRetargetedFocusedElement();
3430 0 : if (focusedElement) {
3431 : return focusedElement;
3432 : }
3433 1 :
3434 1 : // No focused element anywhere in this document. Try to get the BODY.
3435 : if (IsHTMLOrXHTML()) {
3436 : // Because of IE compatibility, return null when html document doesn't have
3437 : // a body.
3438 : return AsHTMLDocument()->GetBody();
3439 0 : }
3440 :
3441 : // If we couldn't get a BODY, return the root element.
3442 0 : return GetDocumentElement();
3443 : }
3444 :
3445 : Element*
3446 1 : nsIDocument::GetCurrentScript()
3447 : {
3448 : nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3449 : return el;
3450 0 : }
3451 :
3452 0 : nsresult
3453 0 : nsIDocument::NodesFromRectHelper(float aX, float aY,
3454 : float aTopSize, float aRightSize,
3455 : float aBottomSize, float aLeftSize,
3456 : bool aIgnoreRootScrollFrame,
3457 0 : bool aFlushLayout,
3458 : nsINodeList** aReturn)
3459 : {
3460 : NS_ENSURE_ARG_POINTER(aReturn);
3461 :
3462 : nsSimpleContentList* elements = new nsSimpleContentList(this);
3463 : NS_ADDREF(elements);
3464 0 : *aReturn = elements;
3465 :
3466 0 : // Following the same behavior of elementFromPoint,
3467 0 : // we don't return anything if either coord is negative
3468 0 : if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
3469 : return NS_OK;
3470 :
3471 : nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3472 0 : nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3473 : nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3474 : nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3475 0 :
3476 0 : nsRect rect(x, y, w, h);
3477 0 :
3478 0 : // Make sure the layout information we get is up-to-date, and
3479 : // ensure we get a root frame (for everything but XUL)
3480 0 : if (aFlushLayout) {
3481 : FlushPendingNotifications(FlushType::Layout);
3482 : }
3483 :
3484 0 : nsIPresShell *ps = GetShell();
3485 0 : NS_ENSURE_STATE(ps);
3486 : nsIFrame *rootFrame = ps->GetRootFrame();
3487 :
3488 0 : // XUL docs, unlike HTML, have no frame tree until everything's done loading
3489 0 : if (!rootFrame)
3490 0 : return NS_OK; // return nothing to premature XUL callers as a reminder to wait
3491 :
3492 : AutoTArray<nsIFrame*,8> outFrames;
3493 0 : nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
3494 : nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3495 : (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3496 0 :
3497 0 : // Used to filter out repeated elements in sequence.
3498 : nsIContent* lastAdded = nullptr;
3499 0 :
3500 : for (uint32_t i = 0; i < outFrames.Length(); i++) {
3501 : nsIContent* node = GetContentInThisDocument(outFrames[i]);
3502 0 :
3503 : if (node && !node->IsElement() && !node->IsText()) {
3504 0 : // We have a node that isn't an element or a text node,
3505 0 : // use its parent content instead.
3506 : node = node->GetParent();
3507 0 : }
3508 : if (node && node != lastAdded) {
3509 : elements->AppendElement(node);
3510 0 : lastAdded = node;
3511 : }
3512 0 : }
3513 0 :
3514 0 : return NS_OK;
3515 : }
3516 :
3517 : void
3518 0 : nsIDocument::ReleaseCapture() const
3519 : {
3520 : // only release the capture if the caller can access it. This prevents a
3521 : // page from stopping a scrollbar grab for example.
3522 0 : nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3523 : if (node && nsContentUtils::CanCallerAccess(node)) {
3524 : nsIPresShell::SetCapturingContent(nullptr, 0);
3525 : }
3526 0 : }
3527 0 :
3528 0 : already_AddRefed<nsIURI>
3529 : nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
3530 0 : {
3531 : nsCOMPtr<nsIURI> uri;
3532 : if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3533 0 : uri = mChromeXHRDocBaseURI;
3534 : } else {
3535 0 : uri = GetDocBaseURI();
3536 0 : }
3537 0 :
3538 : return uri.forget();
3539 51 : }
3540 :
3541 : void
3542 0 : nsIDocument::SetBaseURI(nsIURI* aURI)
3543 : {
3544 : if (!aURI && !mDocumentBaseURI) {
3545 : return;
3546 0 : }
3547 :
3548 0 : // Don't do anything if the URI wasn't actually changed.
3549 : if (aURI && mDocumentBaseURI) {
3550 : bool equalBases = false;
3551 : mDocumentBaseURI->Equals(aURI, &equalBases);
3552 : if (equalBases) {
3553 2 : return;
3554 0 : }
3555 0 : }
3556 0 :
3557 0 : mDocumentBaseURI = aURI;
3558 : RefreshLinkHrefs();
3559 : }
3560 :
3561 1 : URLExtraData*
3562 0 : nsIDocument::DefaultStyleAttrURLData()
3563 : {
3564 : MOZ_ASSERT(NS_IsMainThread());
3565 : nsIURI* baseURI = GetDocBaseURI();
3566 1 : nsIURI* docURI = GetDocumentURI();
3567 : nsIPrincipal* principal = NodePrincipal();
3568 1 : if (!mCachedURLData ||
3569 1 : mCachedURLData->BaseURI() != baseURI ||
3570 0 : mCachedURLData->GetReferrer() != docURI ||
3571 0 : mCachedURLData->GetPrincipal() != principal) {
3572 0 : mCachedURLData = new URLExtraData(baseURI, docURI, principal);
3573 0 : }
3574 1 : return mCachedURLData;
3575 0 : }
3576 0 :
3577 : void
3578 2 : nsIDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding)
3579 : {
3580 : if (mCharacterSet != aEncoding) {
3581 : mCharacterSet = aEncoding;
3582 119 : mEncodingMenuDisabled = aEncoding == UTF_8_ENCODING;
3583 :
3584 0 : if (nsPresContext* context = GetPresContext()) {
3585 0 : context->DispatchCharSetChange(aEncoding);
3586 0 : }
3587 : }
3588 0 : }
3589 3 :
3590 : void
3591 : nsIDocument::GetSandboxFlagsAsString(nsAString& aFlags)
3592 119 : {
3593 : nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags);
3594 : }
3595 0 :
3596 : void
3597 0 : nsIDocument::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const
3598 0 : {
3599 : aData.Truncate();
3600 : const nsDocHeaderData* data = mHeaderData;
3601 0 : while (data) {
3602 : if (data->mField == aHeaderField) {
3603 0 : aData = data->mData;
3604 0 :
3605 0 : break;
3606 0 : }
3607 0 : data = data->mNext;
3608 : }
3609 : }
3610 :
3611 0 : void
3612 : nsIDocument::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData)
3613 0 : {
3614 : if (!aHeaderField) {
3615 : NS_ERROR("null headerField");
3616 0 : return;
3617 : }
3618 0 :
3619 0 : if (!mHeaderData) {
3620 : if (!aData.IsEmpty()) { // don't bother storing empty string
3621 : mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3622 : }
3623 0 : }
3624 0 : else {
3625 0 : nsDocHeaderData* data = mHeaderData;
3626 : nsDocHeaderData** lastPtr = &mHeaderData;
3627 : bool found = false;
3628 : do { // look for existing and replace
3629 0 : if (data->mField == aHeaderField) {
3630 0 : if (!aData.IsEmpty()) {
3631 0 : data->mData.Assign(aData);
3632 : }
3633 0 : else { // don't store empty string
3634 0 : *lastPtr = data->mNext;
3635 0 : data->mNext = nullptr;
3636 : delete data;
3637 : }
3638 0 : found = true;
3639 0 :
3640 0 : break;
3641 : }
3642 : lastPtr = &(data->mNext);
3643 : data = *lastPtr;
3644 : } while (data);
3645 :
3646 0 : if (!aData.IsEmpty() && !found) {
3647 0 : // didn't find, append
3648 0 : *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3649 : }
3650 0 : }
3651 :
3652 0 : if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3653 : CopyUTF16toUTF8(aData, mContentLanguage);
3654 : }
3655 :
3656 0 : if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3657 0 : SetPreferredStyleSheetSet(aData);
3658 : }
3659 :
3660 0 : if (aHeaderField == nsGkAtoms::refresh) {
3661 0 : // We get into this code before we have a script global yet, so get to
3662 : // our container via mDocumentContainer.
3663 : nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3664 0 : if (refresher) {
3665 : // Note: using mDocumentURI instead of mBaseURI here, for consistency
3666 : // (used to just use the current URI of our webnavigation, but that
3667 0 : // should really be the same thing). Note that this code can run
3668 0 : // before the current URI of the webnavigation has been updated, so we
3669 : // can't assert equality here.
3670 : refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3671 : NS_ConvertUTF16toUTF8(aData));
3672 : }
3673 : }
3674 0 :
3675 0 : if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3676 : mAllowDNSPrefetch) {
3677 : // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3678 : mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3679 0 : }
3680 0 :
3681 : if (aHeaderField == nsGkAtoms::viewport ||
3682 0 : aHeaderField == nsGkAtoms::handheldFriendly ||
3683 : aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3684 : aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3685 0 : aHeaderField == nsGkAtoms::viewport_initial_scale ||
3686 0 : aHeaderField == nsGkAtoms::viewport_height ||
3687 0 : aHeaderField == nsGkAtoms::viewport_width ||
3688 0 : aHeaderField == nsGkAtoms::viewport_user_scalable) {
3689 0 : mViewportType = Unknown;
3690 0 : mViewportOverflowType = ViewportOverflowType::NoOverflow;
3691 0 : }
3692 0 :
3693 0 : // Referrer policy spec says to ignore any empty referrer policies.
3694 0 : if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
3695 : enum mozilla::net::ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
3696 : // If policy is not the empty string, then set element's node document's
3697 : // referrer policy to policy
3698 0 : if (policy != mozilla::net::RP_Unset) {
3699 0 : // Referrer policy spec (section 6.1) says that we always use the newest
3700 : // referrer policy we find
3701 : mReferrerPolicy = policy;
3702 0 : mReferrerPolicySet = true;
3703 : }
3704 : }
3705 0 :
3706 0 : if (aHeaderField == nsGkAtoms::headerReferrerPolicy && !aData.IsEmpty()) {
3707 : enum mozilla::net::ReferrerPolicy policy = nsContentUtils::GetReferrerPolicyFromHeader(aData);
3708 : if (policy != mozilla::net::RP_Unset) {
3709 : mReferrerPolicy = policy;
3710 0 : mReferrerPolicySet = true;
3711 0 : }
3712 0 : }
3713 0 :
3714 0 : }
3715 : void
3716 : nsDocument::TryChannelCharset(nsIChannel *aChannel,
3717 : int32_t& aCharsetSource,
3718 : NotNull<const Encoding*>& aEncoding,
3719 : nsHtml5TreeOpExecutor* aExecutor)
3720 49 : {
3721 : if (aChannel) {
3722 : nsAutoCString charsetVal;
3723 : nsresult rv = aChannel->GetContentCharset(charsetVal);
3724 : if (NS_SUCCEEDED(rv)) {
3725 0 : const Encoding* preferred = Encoding::ForLabel(charsetVal);
3726 0 : if (preferred) {
3727 49 : aEncoding = WrapNotNull(preferred);
3728 49 : aCharsetSource = kCharsetFromChannel;
3729 98 : return;
3730 49 : } else if (aExecutor && !charsetVal.IsEmpty()) {
3731 46 : aExecutor->ComplainAboutBogusProtocolCharset(this);
3732 0 : }
3733 0 : }
3734 3 : }
3735 0 : }
3736 :
3737 : static inline void
3738 : AssertNoStaleServoDataIn(const nsINode& aSubtreeRoot)
3739 : {
3740 : #ifdef DEBUG
3741 : for (const nsINode* node = &aSubtreeRoot;
3742 35 : node;
3743 : node = node->GetNextNode(&aSubtreeRoot)) {
3744 : const Element* element = Element::FromNode(node);
3745 0 : if (!element) {
3746 0 : continue;
3747 : }
3748 0 : MOZ_ASSERT(!element->HasServoData());
3749 0 : if (auto* shadow = element->GetShadowRoot()) {
3750 : AssertNoStaleServoDataIn(*shadow);
3751 : }
3752 48 : if (nsXBLBinding* binding = element->GetXBLBinding()) {
3753 48 : if (nsXBLBinding* bindingWithContent = binding->GetBindingWithContent()) {
3754 0 : nsIContent* content = bindingWithContent->GetAnonymousContent();
3755 : MOZ_ASSERT(!content->AsElement()->HasServoData());
3756 0 : for (nsINode* child = content->GetFirstChild();
3757 0 : child;
3758 0 : child = child->GetNextSibling()) {
3759 0 : AssertNoStaleServoDataIn(*child);
3760 0 : }
3761 0 : }
3762 : }
3763 0 : }
3764 : #endif
3765 : }
3766 :
3767 : already_AddRefed<nsIPresShell>
3768 : nsIDocument::CreateShell(nsPresContext* aContext,
3769 0 : nsViewManager* aViewManager,
3770 : UniquePtr<ServoStyleSet> aStyleSet)
3771 : {
3772 27 : NS_ASSERTION(!mPresShell, "We have a presshell already!");
3773 :
3774 : NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3775 :
3776 27 : FillStyleSet(aStyleSet.get());
3777 : AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
3778 27 :
3779 : RefPtr<PresShell> shell = new PresShell;
3780 27 : // Note: we don't hold a ref to the shell (it holds a ref to us)
3781 27 : mPresShell = shell;
3782 : shell->Init(this, aContext, aViewManager, std::move(aStyleSet));
3783 0 :
3784 : // Make sure to never paint if we belong to an invisible DocShell.
3785 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3786 0 : if (docShell && docShell->IsInvisible())
3787 : shell->SetNeverPainting(true);
3788 :
3789 0 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p with PressShell %p and DocShell %p",
3790 0 : this, shell.get(), docShell.get()));
3791 0 :
3792 : mExternalResourceMap.ShowViewers();
3793 27 :
3794 : UpdateFrameRequestCallbackSchedulingState();
3795 :
3796 27 : // Now that we have a shell, we might have @font-face rules (the presence of a
3797 : // shell may change which rules apply to us). We don't need to do anything
3798 27 : // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
3799 : // is ready to update we'll flush the font set.
3800 : MarkUserFontSetDirty();
3801 :
3802 : return shell.forget();
3803 : }
3804 27 :
3805 : void
3806 0 : nsIDocument::UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell)
3807 : {
3808 : // If the condition for shouldBeScheduled changes to depend on some other
3809 : // variable, add UpdateFrameRequestCallbackSchedulingState() calls to the
3810 0 : // places where that variable can change.
3811 : bool shouldBeScheduled =
3812 : mPresShell && IsEventHandlingEnabled() && !mFrameRequestCallbacks.IsEmpty();
3813 : if (shouldBeScheduled == mFrameRequestCallbacksScheduled) {
3814 : // nothing to do
3815 : return;
3816 0 : }
3817 0 :
3818 : nsIPresShell* presShell = aOldShell ? aOldShell : mPresShell;
3819 : MOZ_RELEASE_ASSERT(presShell);
3820 :
3821 : nsRefreshDriver* rd = presShell->GetPresContext()->RefreshDriver();
3822 2 : if (shouldBeScheduled) {
3823 2 : rd->ScheduleFrameRequestCallbacks(this);
3824 : } else {
3825 6 : rd->RevokeFrameRequestCallbacks(this);
3826 0 : }
3827 2 :
3828 : mFrameRequestCallbacksScheduled = shouldBeScheduled;
3829 0 : }
3830 :
3831 : void
3832 2 : nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
3833 : {
3834 : aCallbacks.AppendElements(mFrameRequestCallbacks);
3835 : mFrameRequestCallbacks.Clear();
3836 2 : // No need to manually remove ourselves from the refresh driver; it will
3837 : // handle that part. But we do have to update our state.
3838 0 : mFrameRequestCallbacksScheduled = false;
3839 2 : }
3840 :
3841 : bool
3842 0 : nsIDocument::ShouldThrottleFrameRequests()
3843 0 : {
3844 : if (mStaticCloneCount > 0) {
3845 : // Even if we're not visible, a static clone may be, so run at full speed.
3846 0 : return false;
3847 : }
3848 0 :
3849 : if (Hidden()) {
3850 : // We're not visible (probably in a background tab or the bf cache).
3851 : return true;
3852 : }
3853 0 :
3854 : if (!mPresShell) {
3855 : return false; // Can't do anything smarter.
3856 : }
3857 :
3858 13 : nsIFrame* frame = mPresShell->GetRootFrame();
3859 : if (!frame) {
3860 : return false; // Can't do anything smarter.
3861 : }
3862 26 :
3863 0 : nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame);
3864 : if (!displayRootFrame) {
3865 : return false; // Can't do anything smarter.
3866 : }
3867 0 :
3868 13 : if (!displayRootFrame->DidPaintPresShell(mPresShell)) {
3869 : // We didn't get painted during the last paint, so we're not visible.
3870 : // Throttle. Note that because we have to paint this document at least
3871 : // once to unthrottle it, we will drop one requestAnimationFrame frame
3872 13 : // when a document that previously wasn't visible scrolls into view. This
3873 : // is acceptable since it would happen outside the viewport on APZ
3874 : // platforms and is unlikely to be human-perceivable on non-APZ platforms.
3875 : return true;
3876 : }
3877 :
3878 : // We got painted during the last paint, so run at full speed.
3879 : return false;
3880 : }
3881 :
3882 : void
3883 0 : nsIDocument::DeleteShell()
3884 : {
3885 : mExternalResourceMap.HideViewers();
3886 : if (nsPresContext* presContext = mPresShell->GetPresContext()) {
3887 8 : presContext->RefreshDriver()->CancelPendingEvents(this);
3888 : }
3889 0 :
3890 16 : // When our shell goes away, request that all our images be immediately
3891 8 : // discarded, so we don't carry around decoded image data for a document we
3892 : // no longer intend to paint.
3893 : ImageTracker()->RequestDiscardAll();
3894 :
3895 : // Now that we no longer have a shell, we need to forget about any FontFace
3896 : // objects for @font-face rules that came from the style set. There's no need
3897 8 : // to call EnsureStyleFlush either, the shell is going away anyway, so there's
3898 : // no point on it.
3899 : MarkUserFontSetDirty();
3900 :
3901 : nsIPresShell* oldShell = mPresShell;
3902 : mPresShell = nullptr;
3903 0 : UpdateFrameRequestCallbackSchedulingState(oldShell);
3904 : mStyleSetFilled = false;
3905 0 :
3906 8 : ClearStaleServoData();
3907 8 : AssertNoStaleServoDataIn(static_cast<nsINode&>(*this));
3908 8 : }
3909 :
3910 0 : void
3911 8 : nsIDocument::SetBFCacheEntry(nsIBFCacheEntry* aEntry)
3912 8 : {
3913 : MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
3914 :
3915 0 : if (mPresShell) {
3916 : if (aEntry) {
3917 0 : mPresShell->StopObservingRefreshDriver();
3918 : } else if (mBFCacheEntry) {
3919 0 : mPresShell->StartObservingRefreshDriver();
3920 0 : }
3921 0 : }
3922 0 : mBFCacheEntry = aEntry;
3923 0 : }
3924 :
3925 : static void
3926 0 : SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
3927 0 : {
3928 : SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
3929 :
3930 1 : NS_RELEASE(e->mKey);
3931 : if (e->mSubDocument) {
3932 1 : e->mSubDocument->SetParentDocument(nullptr);
3933 : NS_RELEASE(e->mSubDocument);
3934 1 : }
3935 1 : }
3936 2 :
3937 1 : static void
3938 : SubDocInitEntry(PLDHashEntryHdr *entry, const void *key)
3939 1 : {
3940 : SubDocMapEntry *e =
3941 : const_cast<SubDocMapEntry *>
3942 4 : (static_cast<const SubDocMapEntry *>(entry));
3943 :
3944 : e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
3945 : NS_ADDREF(e->mKey);
3946 0 :
3947 : e->mSubDocument = nullptr;
3948 0 : }
3949 4 :
3950 : nsresult
3951 4 : nsIDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
3952 4 : {
3953 : NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
3954 :
3955 7 : if (!aSubDoc) {
3956 : // aSubDoc is nullptr, remove the mapping
3957 7 :
3958 : if (mSubDocuments) {
3959 7 : nsIDocument* subDoc = GetSubDocumentFor(aElement);
3960 : if (subDoc) {
3961 : subDoc->SetAllowPaymentRequest(false);
3962 0 : }
3963 0 : mSubDocuments->Remove(aElement);
3964 0 : }
3965 : } else {
3966 : if (!mSubDocuments) {
3967 0 : // Create a new hashtable
3968 :
3969 : static const PLDHashTableOps hash_table_ops =
3970 6 : {
3971 : PLDHashTable::HashVoidPtrKeyStub,
3972 : PLDHashTable::MatchEntryStub,
3973 : PLDHashTable::MoveEntryStub,
3974 : SubDocClearEntry,
3975 : SubDocInitEntry
3976 : };
3977 :
3978 : mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
3979 : }
3980 :
3981 : // Add a mapping to the hash table
3982 3 : auto entry =
3983 : static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible));
3984 :
3985 : if (!entry) {
3986 : return NS_ERROR_OUT_OF_MEMORY;
3987 0 : }
3988 :
3989 0 : if (entry->mSubDocument) {
3990 : entry->mSubDocument->SetAllowPaymentRequest(false);
3991 : entry->mSubDocument->SetParentDocument(nullptr);
3992 :
3993 0 : // Release the old sub document
3994 0 : NS_RELEASE(entry->mSubDocument);
3995 4 : }
3996 :
3997 : entry->mSubDocument = aSubDoc;
3998 2 : NS_ADDREF(entry->mSubDocument);
3999 :
4000 : // set allowpaymentrequest for the binding subdocument
4001 6 : if (!mAllowPaymentRequest) {
4002 6 : aSubDoc->SetAllowPaymentRequest(false);
4003 : } else {
4004 : nsresult rv = nsContentUtils::CheckSameOrigin(aElement, aSubDoc);
4005 0 : if (NS_SUCCEEDED(rv)) {
4006 : aSubDoc->SetAllowPaymentRequest(true);
4007 : } else {
4008 0 : if (aElement->IsHTMLElement(nsGkAtoms::iframe) &&
4009 0 : aElement->GetBoolAttr(nsGkAtoms::allowpaymentrequest)) {
4010 : aSubDoc->SetAllowPaymentRequest(true);
4011 : } else {
4012 0 : aSubDoc->SetAllowPaymentRequest(false);
4013 0 : }
4014 : }
4015 : }
4016 :
4017 : aSubDoc->SetParentDocument(this);
4018 : }
4019 :
4020 : return NS_OK;
4021 0 : }
4022 :
4023 : nsIDocument*
4024 : nsIDocument::GetSubDocumentFor(nsIContent *aContent) const
4025 : {
4026 : if (mSubDocuments && aContent->IsElement()) {
4027 : auto entry = static_cast<SubDocMapEntry*>
4028 29 : (mSubDocuments->Search(aContent->AsElement()));
4029 :
4030 55 : if (entry) {
4031 : return entry->mSubDocument;
4032 26 : }
4033 : }
4034 26 :
4035 3 : return nullptr;
4036 : }
4037 :
4038 : Element*
4039 : nsIDocument::FindContentForSubDocument(nsIDocument *aDocument) const
4040 : {
4041 : NS_ENSURE_TRUE(aDocument, nullptr);
4042 :
4043 3 : if (!mSubDocuments) {
4044 : return nullptr;
4045 3 : }
4046 :
4047 3 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
4048 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
4049 : if (entry->mSubDocument == aDocument) {
4050 : return entry->mKey;
4051 0 : }
4052 0 : }
4053 3 : return nullptr;
4054 3 : }
4055 :
4056 : bool
4057 0 : nsIDocument::IsNodeOfType(uint32_t aFlags) const
4058 : {
4059 : return false;
4060 : }
4061 0 :
4062 : Element*
4063 0 : nsIDocument::GetRootElement() const
4064 : {
4065 : return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
4066 : mCachedRootElement : GetRootElementInternal();
4067 10990 : }
4068 :
4069 0 : nsIContent*
4070 0 : nsIDocument::GetUnfocusedKeyEventTarget()
4071 : {
4072 : return GetRootElement();
4073 : }
4074 0 :
4075 : Element*
4076 0 : nsIDocument::GetRootElementInternal() const
4077 : {
4078 : // We invoke GetRootElement() immediately before the servo traversal, so we
4079 : // should always have a cache hit from Servo.
4080 168 : MOZ_ASSERT(NS_IsMainThread());
4081 :
4082 : // Loop backwards because any non-elements, such as doctypes and PIs
4083 : // are likely to appear before the root element.
4084 168 : for (nsIContent* child = GetLastChild(); child;
4085 : child = child->GetPreviousSibling()) {
4086 : if (Element* element = Element::FromNode(child)) {
4087 : const_cast<nsIDocument*>(this)->mCachedRootElement = element;
4088 : return element;
4089 0 : }
4090 522 : }
4091 0 :
4092 0 : const_cast<nsIDocument*>(this)->mCachedRootElement = nullptr;
4093 : return nullptr;
4094 : }
4095 :
4096 108 : nsresult
4097 108 : nsIDocument::InsertChildBefore(nsIContent* aKid,
4098 : nsIContent* aBeforeThis,
4099 : bool aNotify)
4100 : {
4101 13 : if (aKid->IsElement() && GetRootElement()) {
4102 : NS_WARNING("Inserting root element when we already have one");
4103 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
4104 : }
4105 26 :
4106 0 : int32_t index = aBeforeThis ? ComputeIndexOf(aBeforeThis) : GetChildCount();
4107 0 : MOZ_ASSERT(index >= 0);
4108 :
4109 : return doInsertChildAt(aKid, index, aNotify, mChildren);
4110 0 : }
4111 0 :
4112 : nsresult
4113 13 : nsIDocument::InsertChildAt_Deprecated(nsIContent* aKid,
4114 : uint32_t aIndex,
4115 : bool aNotify)
4116 : {
4117 118 : if (aKid->IsElement() && GetRootElement()) {
4118 : NS_WARNING("Inserting root element when we already have one");
4119 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
4120 : }
4121 236 :
4122 0 : return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
4123 0 : }
4124 :
4125 : void
4126 0 : nsIDocument::RemoveChildAt_Deprecated(uint32_t aIndex, bool aNotify)
4127 : {
4128 : nsCOMPtr<nsIContent> oldKid = GetChildAt_Deprecated(aIndex);
4129 : if (!oldKid) {
4130 0 : return;
4131 : }
4132 0 :
4133 0 : if (oldKid->IsElement()) {
4134 0 : // Destroy the link map up front before we mess with the child list.
4135 : DestroyElementMaps();
4136 : }
4137 0 :
4138 : // Preemptively clear mCachedRootElement, since we may be about to remove it
4139 0 : // from our child list, and we don't want to return this maybe-obsolete value
4140 : // from any GetRootElement() calls that happen inside of doRemoveChildAt().
4141 : // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
4142 : // GetRootElement() calls until after it's removed the child from mChildren.
4143 : // Any call before that point would restore this soon-to-be-obsolete cached
4144 : // answer, and our clearing here would be fruitless.)
4145 : mCachedRootElement = nullptr;
4146 : doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
4147 : MOZ_ASSERT(mCachedRootElement != oldKid,
4148 : "Stale pointer in mCachedRootElement, after we tried to clear it "
4149 0 : "(maybe somebody called GetRootElement() too early?)");
4150 0 : }
4151 0 :
4152 : void
4153 : nsIDocument::RemoveChildNode(nsIContent* aKid, bool aNotify)
4154 : {
4155 : if (aKid->IsElement()) {
4156 : // Destroy the link map up front before we mess with the child list.
4157 0 : DestroyElementMaps();
4158 : }
4159 0 :
4160 : // Preemptively clear mCachedRootElement, since we may be about to remove it
4161 0 : // from our child list, and we don't want to return this maybe-obsolete value
4162 : // from any GetRootElement() calls that happen inside of doRemoveChildAt().
4163 : // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
4164 : // GetRootElement() calls until after it's removed the child from mChildren.
4165 : // Any call before that point would restore this soon-to-be-obsolete cached
4166 : // answer, and our clearing here would be fruitless.)
4167 : mCachedRootElement = nullptr;
4168 : doRemoveChildAt(ComputeIndexOf(aKid), aNotify, aKid, mChildren);
4169 : MOZ_ASSERT(mCachedRootElement != aKid,
4170 : "Stale pointer in mCachedRootElement, after we tried to clear it "
4171 0 : "(maybe somebody called GetRootElement() too early?)");
4172 0 : }
4173 0 :
4174 : void
4175 : nsIDocument::EnsureOnDemandBuiltInUASheet(StyleSheet* aSheet)
4176 0 : {
4177 : if (mOnDemandBuiltInUASheets.Contains(aSheet)) {
4178 : return;
4179 1 : }
4180 : AddOnDemandBuiltInUASheet(aSheet);
4181 13 : }
4182 :
4183 : void
4184 13 : nsIDocument::AddOnDemandBuiltInUASheet(StyleSheet* aSheet)
4185 : {
4186 : MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(aSheet));
4187 :
4188 13 : // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in
4189 : // the same order that they should end up in the style set.
4190 0 : mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet);
4191 :
4192 : if (aSheet->IsApplicable()) {
4193 : // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
4194 0 : if (nsIPresShell* shell = GetShell()) {
4195 : // Note that prepending here is necessary to make sure that html.css etc.
4196 0 : // does not override Firefox OS/Mobile's content.css sheet.
4197 : //
4198 13 : // Maybe we should have an insertion point to match the order of
4199 : // nsDocumentViewer::CreateStyleSet though?
4200 : //
4201 : // FIXME(emilio): We probably should, randomly prepending stuff here is
4202 : // very prone to subtle bugs, behavior differences...
4203 26 : shell->StyleSet()->PrependStyleSheet(SheetType::Agent, aSheet);
4204 13 : shell->ApplicableStylesChanged();
4205 : }
4206 : }
4207 :
4208 0 : NotifyStyleSheetAdded(aSheet, false);
4209 13 : }
4210 :
4211 : void
4212 12 : nsIDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet)
4213 : {
4214 0 : if (nsIPresShell* shell = GetShell()) {
4215 12 : shell->StyleSet()->AddDocStyleSheet(aSheet, this);
4216 0 : shell->ApplicableStylesChanged();
4217 : }
4218 0 : }
4219 :
4220 : #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \
4221 : do { \
4222 : className##Init init; \
4223 : init.mBubbles = true; \
4224 : init.mCancelable = true; \
4225 : init.mStylesheet = aSheet; \
4226 : init.memberName = argName; \
4227 : \
4228 : RefPtr<className> event = \
4229 : className::Constructor(this, NS_LITERAL_STRING(type), init); \
4230 : event->SetTrusted(true); \
4231 : event->SetTarget(this); \
4232 : RefPtr<AsyncEventDispatcher> asyncDispatcher = \
4233 : new AsyncEventDispatcher(this, event); \
4234 : asyncDispatcher->mOnlyChromeDispatch = true; \
4235 : asyncDispatcher->PostDOMEvent(); \
4236 : } while (0);
4237 :
4238 : void
4239 27 : nsIDocument::NotifyStyleSheetAdded(StyleSheet* aSheet, bool aDocumentSheet)
4240 : {
4241 0 : if (StyleSheetChangeEventsEnabled()) {
4242 0 : DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4243 : "StyleSheetAdded",
4244 : mDocumentSheet,
4245 : aDocumentSheet);
4246 : }
4247 0 : }
4248 :
4249 : void
4250 0 : nsIDocument::NotifyStyleSheetRemoved(StyleSheet* aSheet, bool aDocumentSheet)
4251 : {
4252 0 : if (StyleSheetChangeEventsEnabled()) {
4253 0 : DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4254 : "StyleSheetRemoved",
4255 : mDocumentSheet,
4256 : aDocumentSheet);
4257 : }
4258 0 : }
4259 :
4260 : void
4261 0 : nsIDocument::AddStyleSheet(StyleSheet* aSheet)
4262 : {
4263 0 : MOZ_ASSERT(aSheet);
4264 0 : DocumentOrShadowRoot::AppendSheet(*aSheet);
4265 :
4266 0 : if (aSheet->IsApplicable()) {
4267 0 : AddStyleSheetToStyleSets(aSheet);
4268 : }
4269 :
4270 0 : NotifyStyleSheetAdded(aSheet, true);
4271 0 : }
4272 :
4273 : void
4274 0 : nsIDocument::RemoveStyleSheetFromStyleSets(StyleSheet* aSheet)
4275 : {
4276 0 : if (nsIPresShell* shell = GetShell()) {
4277 0 : shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4278 0 : shell->ApplicableStylesChanged();
4279 : }
4280 0 : }
4281 :
4282 : void
4283 0 : nsIDocument::RemoveStyleSheet(StyleSheet* aSheet)
4284 : {
4285 0 : MOZ_ASSERT(aSheet);
4286 0 : RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
4287 :
4288 0 : if (!sheet) {
4289 0 : NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4290 0 : return;
4291 : }
4292 :
4293 0 : if (!mIsGoingAway) {
4294 0 : if (sheet->IsApplicable()) {
4295 0 : RemoveStyleSheetFromStyleSets(sheet);
4296 : }
4297 :
4298 0 : NotifyStyleSheetRemoved(sheet, true);
4299 : }
4300 :
4301 0 : sheet->ClearAssociatedDocumentOrShadowRoot();
4302 : }
4303 :
4304 : void
4305 0 : nsIDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets,
4306 : nsTArray<RefPtr<StyleSheet>>& aNewSheets)
4307 : {
4308 : // XXX Need to set the sheet on the ownernode, if any
4309 0 : MOZ_ASSERT(aOldSheets.Length() == aNewSheets.Length(),
4310 : "The lists must be the same length!");
4311 0 : int32_t count = aOldSheets.Length();
4312 :
4313 0 : RefPtr<StyleSheet> oldSheet;
4314 : int32_t i;
4315 0 : for (i = 0; i < count; ++i) {
4316 0 : oldSheet = aOldSheets[i];
4317 :
4318 : // First remove the old sheet.
4319 0 : NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4320 0 : int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4321 0 : RemoveStyleSheet(oldSheet); // This does the right notifications
4322 :
4323 : // Now put the new one in its place. If it's null, just ignore it.
4324 0 : StyleSheet* newSheet = aNewSheets[i];
4325 0 : if (newSheet) {
4326 0 : DocumentOrShadowRoot::InsertSheetAt(oldIndex, *newSheet);
4327 0 : if (newSheet->IsApplicable()) {
4328 0 : AddStyleSheetToStyleSets(newSheet);
4329 : }
4330 :
4331 0 : NotifyStyleSheetAdded(newSheet, true);
4332 : }
4333 : }
4334 0 : }
4335 :
4336 : void
4337 0 : nsIDocument::InsertStyleSheetAt(StyleSheet* aSheet, size_t aIndex)
4338 : {
4339 13 : MOZ_ASSERT(aSheet);
4340 :
4341 13 : DocumentOrShadowRoot::InsertSheetAt(aIndex, *aSheet);
4342 :
4343 0 : if (aSheet->IsApplicable()) {
4344 0 : AddStyleSheetToStyleSets(aSheet);
4345 : }
4346 :
4347 1 : NotifyStyleSheetAdded(aSheet, true);
4348 13 : }
4349 :
4350 :
4351 : void
4352 0 : nsIDocument::SetStyleSheetApplicableState(StyleSheet* aSheet, bool aApplicable)
4353 : {
4354 14 : MOZ_ASSERT(aSheet, "null arg");
4355 :
4356 : // If we're actually in the document style sheet list
4357 28 : if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) {
4358 0 : if (aApplicable) {
4359 12 : AddStyleSheetToStyleSets(aSheet);
4360 : } else {
4361 0 : RemoveStyleSheetFromStyleSets(aSheet);
4362 : }
4363 : }
4364 :
4365 14 : if (StyleSheetChangeEventsEnabled()) {
4366 0 : DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
4367 : "StyleSheetApplicableStateChanged",
4368 : mApplicable,
4369 : aApplicable);
4370 : }
4371 :
4372 0 : if (!mSSApplicableStateNotificationPending) {
4373 1 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
4374 : nsCOMPtr<nsIRunnable> notification =
4375 4 : NewRunnableMethod("nsIDocument::NotifyStyleSheetApplicableStateChanged",
4376 : this,
4377 1 : &nsIDocument::NotifyStyleSheetApplicableStateChanged);
4378 1 : mSSApplicableStateNotificationPending =
4379 4 : NS_SUCCEEDED(
4380 : Dispatch(TaskCategory::Other, notification.forget()));
4381 : }
4382 1 : }
4383 :
4384 : void
4385 1 : nsIDocument::NotifyStyleSheetApplicableStateChanged()
4386 : {
4387 2 : mSSApplicableStateNotificationPending = false;
4388 : nsCOMPtr<nsIObserverService> observerService =
4389 4 : mozilla::services::GetObserverService();
4390 2 : if (observerService) {
4391 0 : observerService->NotifyObservers(this,
4392 : "style-sheet-applicable-state-changed",
4393 4 : nullptr);
4394 : }
4395 2 : }
4396 :
4397 : static SheetType
4398 0 : ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
4399 : {
4400 0 : switch(aType) {
4401 : case nsIDocument::eAgentSheet:
4402 : return SheetType::Agent;
4403 : case nsIDocument::eUserSheet:
4404 0 : return SheetType::User;
4405 : case nsIDocument::eAuthorSheet:
4406 1 : return SheetType::Doc;
4407 : default:
4408 0 : MOZ_ASSERT(false, "wrong type");
4409 : // we must return something although this should never happen
4410 : return SheetType::Count;
4411 : }
4412 : }
4413 :
4414 : static int32_t
4415 0 : FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets, nsIURI* aSheetURI)
4416 : {
4417 0 : for (int32_t i = aSheets.Length() - 1; i >= 0; i-- ) {
4418 : bool bEqual;
4419 0 : nsIURI* uri = aSheets[i]->GetSheetURI();
4420 :
4421 0 : if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4422 0 : return i;
4423 : }
4424 :
4425 : return -1;
4426 : }
4427 :
4428 : nsresult
4429 0 : nsIDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
4430 : nsIURI* aSheetURI)
4431 : {
4432 0 : MOZ_ASSERT(aSheetURI, "null arg");
4433 :
4434 : // Checking if we have loaded this one already.
4435 0 : if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4436 : return NS_ERROR_INVALID_ARG;
4437 :
4438 : // Loading the sheet sync.
4439 0 : RefPtr<css::Loader> loader = new css::Loader(GetDocGroup());
4440 :
4441 : css::SheetParsingMode parsingMode;
4442 0 : switch (aType) {
4443 : case nsIDocument::eAgentSheet:
4444 : parsingMode = css::eAgentSheetFeatures;
4445 : break;
4446 :
4447 : case nsIDocument::eUserSheet:
4448 0 : parsingMode = css::eUserSheetFeatures;
4449 0 : break;
4450 :
4451 : case nsIDocument::eAuthorSheet:
4452 0 : parsingMode = css::eAuthorSheetFeatures;
4453 0 : break;
4454 :
4455 : default:
4456 0 : MOZ_CRASH("impossible value for aType");
4457 : }
4458 :
4459 0 : RefPtr<StyleSheet> sheet;
4460 0 : nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet);
4461 0 : NS_ENSURE_SUCCESS(rv, rv);
4462 :
4463 0 : sheet->SetAssociatedDocumentOrShadowRoot(
4464 0 : this, StyleSheet::OwnedByDocumentOrShadowRoot);
4465 0 : MOZ_ASSERT(sheet->IsApplicable());
4466 :
4467 0 : return AddAdditionalStyleSheet(aType, sheet);
4468 : }
4469 :
4470 : nsresult
4471 1 : nsIDocument::AddAdditionalStyleSheet(additionalSheetType aType, StyleSheet* aSheet)
4472 : {
4473 1 : if (mAdditionalSheets[aType].Contains(aSheet))
4474 : return NS_ERROR_INVALID_ARG;
4475 :
4476 0 : if (!aSheet->IsApplicable())
4477 : return NS_ERROR_INVALID_ARG;
4478 :
4479 0 : mAdditionalSheets[aType].AppendElement(aSheet);
4480 :
4481 1 : if (nsIPresShell* shell = GetShell()) {
4482 1 : SheetType type = ConvertAdditionalSheetType(aType);
4483 2 : shell->StyleSet()->AppendStyleSheet(type, aSheet);
4484 1 : shell->ApplicableStylesChanged();
4485 : }
4486 :
4487 : // Passing false, so documet.styleSheets.length will not be affected by
4488 : // these additional sheets.
4489 0 : NotifyStyleSheetAdded(aSheet, false);
4490 1 : return NS_OK;
4491 : }
4492 :
4493 : void
4494 0 : nsIDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4495 : {
4496 0 : MOZ_ASSERT(aSheetURI);
4497 :
4498 0 : nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType];
4499 :
4500 0 : int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4501 0 : if (i >= 0) {
4502 0 : RefPtr<StyleSheet> sheetRef = sheets[i];
4503 0 : sheets.RemoveElementAt(i);
4504 :
4505 0 : if (!mIsGoingAway) {
4506 0 : MOZ_ASSERT(sheetRef->IsApplicable());
4507 0 : if (nsIPresShell* shell = GetShell()) {
4508 0 : SheetType type = ConvertAdditionalSheetType(aType);
4509 0 : shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4510 0 : shell->ApplicableStylesChanged();
4511 : }
4512 : }
4513 :
4514 : // Passing false, so documet.styleSheets.length will not be affected by
4515 : // these additional sheets.
4516 0 : NotifyStyleSheetRemoved(sheetRef, false);
4517 0 : sheetRef->ClearAssociatedDocumentOrShadowRoot();
4518 : }
4519 0 : }
4520 :
4521 : nsIGlobalObject*
4522 0 : nsIDocument::GetScopeObject() const
4523 : {
4524 0 : nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4525 4306 : return scope;
4526 : }
4527 :
4528 : void
4529 16 : nsIDocument::SetScopeObject(nsIGlobalObject* aGlobal)
4530 : {
4531 16 : mScopeObject = do_GetWeakReference(aGlobal);
4532 16 : if (aGlobal) {
4533 0 : mHasHadScriptHandlingObject = true;
4534 :
4535 32 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
4536 0 : if (window) {
4537 : // We want to get the tabgroup unconditionally, such that we can make
4538 : // certain that it is cached in the inner window early enough.
4539 0 : mozilla::dom::TabGroup* tabgroup = window->TabGroup();
4540 : // We should already have the principal, and now that we have been added to a
4541 : // window, we should be able to join a DocGroup!
4542 30 : nsAutoCString docGroupKey;
4543 : nsresult rv =
4544 15 : mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
4545 30 : if (mDocGroup) {
4546 0 : if (NS_SUCCEEDED(rv)) {
4547 0 : MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
4548 : }
4549 0 : MOZ_RELEASE_ASSERT(mDocGroup->GetTabGroup() == tabgroup);
4550 : } else {
4551 0 : mDocGroup = tabgroup->AddDocument(docGroupKey, this);
4552 30 : MOZ_ASSERT(mDocGroup);
4553 : }
4554 : }
4555 : }
4556 16 : }
4557 :
4558 : static void
4559 0 : CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
4560 : {
4561 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4562 0 : if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
4563 0 : bool* contains = static_cast<bool*>(aContainsEME);
4564 0 : if (mediaElem->GetMediaKeys()) {
4565 0 : *contains = true;
4566 : }
4567 : }
4568 0 : }
4569 :
4570 : bool
4571 0 : nsIDocument::ContainsEMEContent()
4572 : {
4573 0 : bool containsEME = false;
4574 : EnumerateActivityObservers(CheckIfContainsEMEContent,
4575 0 : static_cast<void*>(&containsEME));
4576 0 : return containsEME;
4577 : }
4578 :
4579 : static void
4580 0 : CheckIfContainsMSEContent(nsISupports* aSupports, void* aContainsMSE)
4581 : {
4582 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4583 0 : if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
4584 0 : bool* contains = static_cast<bool*>(aContainsMSE);
4585 0 : RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
4586 0 : if (ms) {
4587 0 : *contains = true;
4588 : }
4589 : }
4590 0 : }
4591 :
4592 : bool
4593 0 : nsIDocument::ContainsMSEContent()
4594 : {
4595 0 : bool containsMSE = false;
4596 : EnumerateActivityObservers(CheckIfContainsMSEContent,
4597 0 : static_cast<void*>(&containsMSE));
4598 0 : return containsMSE;
4599 : }
4600 :
4601 : static void
4602 0 : NotifyActivityChanged(nsISupports *aSupports, void *aUnused)
4603 : {
4604 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
4605 1 : if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
4606 0 : mediaElem->NotifyOwnerDocumentActivityChanged();
4607 : }
4608 0 : nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aSupports));
4609 0 : if (objectLoadingContent) {
4610 0 : nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4611 0 : olc->NotifyOwnerDocumentActivityChanged();
4612 : }
4613 0 : nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(do_QueryInterface(aSupports));
4614 1 : if (objectDocumentActivity) {
4615 0 : objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
4616 : }
4617 1 : }
4618 :
4619 : bool
4620 0 : nsIDocument::IsTopLevelWindowInactive() const
4621 : {
4622 1 : nsCOMPtr<nsIDocShellTreeItem> treeItem = GetDocShell();
4623 43 : if (!treeItem) {
4624 : return false;
4625 : }
4626 :
4627 34 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
4628 0 : treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
4629 17 : if (!rootItem) {
4630 : return false;
4631 : }
4632 :
4633 0 : nsCOMPtr<nsPIDOMWindowOuter> domWindow = rootItem->GetWindow();
4634 17 : return domWindow && !domWindow->IsActive();
4635 : }
4636 :
4637 : void
4638 41 : nsIDocument::SetContainer(nsDocShell* aContainer)
4639 : {
4640 0 : if (aContainer) {
4641 0 : mDocumentContainer = aContainer;
4642 : } else {
4643 0 : mDocumentContainer = WeakPtr<nsDocShell>();
4644 : }
4645 :
4646 41 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4647 :
4648 : // IsTopLevelWindowInactive depends on the docshell, so
4649 : // update the cached value now that it's available.
4650 0 : UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
4651 41 : if (!aContainer) {
4652 : return;
4653 : }
4654 :
4655 : // Get the Docshell
4656 15 : if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4657 : // check if same type root
4658 10 : nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4659 0 : aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4660 5 : NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
4661 :
4662 0 : if (sameTypeRoot == aContainer) {
4663 0 : static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4664 : }
4665 :
4666 0 : static_cast<nsDocument*>(this)->SetIsContentDocument(true);
4667 : }
4668 :
4669 30 : mAncestorPrincipals = aContainer->AncestorPrincipals();
4670 0 : mAncestorOuterWindowIDs = aContainer->AncestorOuterWindowIDs();
4671 : }
4672 :
4673 : nsISupports*
4674 0 : nsIDocument::GetContainer() const
4675 : {
4676 24 : return static_cast<nsIDocShell*>(mDocumentContainer);
4677 : }
4678 :
4679 : void
4680 0 : nsIDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
4681 : {
4682 47 : MOZ_ASSERT(aScriptGlobalObject || !mAnimationController ||
4683 : mAnimationController->IsPausedByType(
4684 : nsSMILTimeContainer::PAUSE_PAGEHIDE |
4685 : nsSMILTimeContainer::PAUSE_BEGIN),
4686 : "Clearing window pointer while animations are unpaused");
4687 :
4688 62 : if (mScriptGlobalObject && !aScriptGlobalObject) {
4689 : // We're detaching from the window. We need to grab a pointer to
4690 : // our layout history state now.
4691 0 : mLayoutHistoryState = GetLayoutHistoryState();
4692 :
4693 : // Also make sure to remove our onload blocker now if we haven't done it yet
4694 8 : if (mOnloadBlockCount != 0) {
4695 0 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4696 8 : if (loadGroup) {
4697 0 : loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4698 : }
4699 : }
4700 :
4701 16 : ErrorResult error;
4702 16 : if (GetController().isSome()) {
4703 0 : imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
4704 0 : if (loader) {
4705 0 : loader->ClearCacheForControlledDocument(this);
4706 : }
4707 :
4708 : // We may become controlled again if this document comes back out
4709 : // of bfcache. Clear our state to allow that to happen. Only
4710 : // clear this flag if we are actually controlled, though, so pages
4711 : // that were force reloaded don't become controlled when they
4712 : // come out of bfcache.
4713 0 : mMaybeServiceWorkerControlled = false;
4714 : }
4715 : }
4716 :
4717 : // BlockOnload() might be called before mScriptGlobalObject is set.
4718 : // We may need to add the blocker once mScriptGlobalObject is set.
4719 0 : bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
4720 :
4721 31 : mScriptGlobalObject = aScriptGlobalObject;
4722 :
4723 0 : if (needOnloadBlocker) {
4724 15 : EnsureOnloadBlocker();
4725 : }
4726 :
4727 0 : UpdateFrameRequestCallbackSchedulingState();
4728 :
4729 31 : if (aScriptGlobalObject) {
4730 : // Go back to using the docshell for the layout history state
4731 0 : mLayoutHistoryState = nullptr;
4732 15 : SetScopeObject(aScriptGlobalObject);
4733 0 : mHasHadDefaultView = true;
4734 : #ifdef DEBUG
4735 15 : if (!mWillReparent) {
4736 : // We really shouldn't have a wrapper here but if we do we need to make sure
4737 : // it has the correct parent.
4738 15 : JSObject *obj = GetWrapperPreserveColor();
4739 0 : if (obj) {
4740 0 : JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
4741 0 : NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
4742 : "Wrong scope, this is really bad!");
4743 : }
4744 : }
4745 : #endif
4746 :
4747 15 : if (mAllowDNSPrefetch) {
4748 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4749 7 : if (docShell) {
4750 : #ifdef DEBUG
4751 : nsCOMPtr<nsIWebNavigation> webNav =
4752 0 : do_GetInterface(aScriptGlobalObject);
4753 0 : NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4754 : "Unexpected container or script global?");
4755 : #endif
4756 : bool allowDNSPrefetch;
4757 7 : docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4758 0 : mAllowDNSPrefetch = allowDNSPrefetch;
4759 : }
4760 : }
4761 :
4762 : // If we are set in a window that is already focused we should remember this
4763 : // as the time the document gained focus.
4764 30 : IgnoredErrorResult ignored;
4765 15 : bool focused = HasFocus(ignored);
4766 15 : if (focused) {
4767 0 : SetLastFocusTime(TimeStamp::Now());
4768 : }
4769 : }
4770 :
4771 : // Remember the pointer to our window (or lack there of), to avoid
4772 : // having to QI every time it's asked for.
4773 124 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
4774 31 : mWindow = window;
4775 :
4776 : // Now that we know what our window is, we can flush the CSP errors to the
4777 : // Web Console. We are flushing all messages that occured and were stored
4778 : // in the queue prior to this point.
4779 62 : nsCOMPtr<nsIContentSecurityPolicy> csp;
4780 0 : NodePrincipal()->GetCsp(getter_AddRefs(csp));
4781 0 : if (csp) {
4782 2 : static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages();
4783 : }
4784 :
4785 : nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4786 0 : do_QueryInterface(GetChannel());
4787 31 : if (internalChannel) {
4788 0 : nsCOMArray<nsISecurityConsoleMessage> messages;
4789 0 : DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
4790 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
4791 0 : SendToConsole(messages);
4792 : }
4793 :
4794 : // Set our visibility state, but do not fire the event. This is correct
4795 : // because either we're coming out of bfcache (in which case IsVisible() will
4796 : // still test false at this point and no state change will happen) or we're
4797 : // doing the initial document load and don't want to fire the event for this
4798 : // change.
4799 31 : dom::VisibilityState oldState = mVisibilityState;
4800 31 : mVisibilityState = ComputeVisibilityState();
4801 : // When the visibility is changed, notify it to observers.
4802 : // Some observers need the notification, for example HTMLMediaElement uses
4803 : // it to update internal media resource allocation.
4804 : // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
4805 : // creation are already done before nsDocument::SetScriptGlobalObject() call.
4806 : // MediaDecoder decides whether starting decoding is decided based on
4807 : // document's visibility. When the MediaDecoder is created,
4808 : // nsDocument::SetScriptGlobalObject() is not yet called and document is
4809 : // hidden state. Therefore the MediaDecoder decides that decoding is
4810 : // not yet necessary. But soon after nsDocument::SetScriptGlobalObject()
4811 : // call, the document becomes not hidden. At the time, MediaDecoder needs
4812 : // to know it and needs to start updating decoding.
4813 31 : if (oldState != mVisibilityState) {
4814 0 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4815 : }
4816 :
4817 : // The global in the template contents owner document should be the same.
4818 62 : if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
4819 0 : mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
4820 : }
4821 :
4822 0 : if (!mMaybeServiceWorkerControlled && mDocumentContainer && mScriptGlobalObject && GetChannel()) {
4823 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4824 : uint32_t loadType;
4825 7 : docShell->GetLoadType(&loadType);
4826 :
4827 : // If we are shift-reloaded, don't associate with a ServiceWorker.
4828 7 : if (IsForceReloadType(loadType)) {
4829 0 : NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
4830 0 : return;
4831 : }
4832 :
4833 7 : mMaybeServiceWorkerControlled = true;
4834 : }
4835 : }
4836 :
4837 : nsIScriptGlobalObject*
4838 0 : nsIDocument::GetScriptHandlingObjectInternal() const
4839 : {
4840 10886 : MOZ_ASSERT(!mScriptGlobalObject,
4841 : "Do not call this when mScriptGlobalObject is set!");
4842 5443 : if (mHasHadDefaultView) {
4843 : return nullptr;
4844 : }
4845 :
4846 : nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4847 0 : do_QueryReferent(mScopeObject);
4848 0 : nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject);
4849 5441 : if (win) {
4850 0 : nsPIDOMWindowOuter* outer = win->GetOuterWindow();
4851 0 : if (!outer || outer->GetCurrentInnerWindow() != win) {
4852 0 : NS_WARNING("Wrong inner/outer window combination!");
4853 0 : return nullptr;
4854 : }
4855 : }
4856 0 : return scriptHandlingObject;
4857 : }
4858 : void
4859 0 : nsIDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
4860 : {
4861 0 : NS_ASSERTION(!mScriptGlobalObject ||
4862 : mScriptGlobalObject == aScriptObject,
4863 : "Wrong script object!");
4864 0 : if (aScriptObject) {
4865 0 : SetScopeObject(aScriptObject);
4866 0 : mHasHadDefaultView = false;
4867 : }
4868 0 : }
4869 :
4870 : nsPIDOMWindowOuter*
4871 0 : nsIDocument::GetWindowInternal() const
4872 : {
4873 444 : MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4874 : // Let's use mScriptGlobalObject. Even if the document is already removed from
4875 : // the docshell, the outer window might be still obtainable from the it.
4876 0 : nsCOMPtr<nsPIDOMWindowOuter> win;
4877 444 : if (mRemovedFromDocShell) {
4878 : // The docshell returns the outer window we are done.
4879 0 : nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer);
4880 0 : if (kungFuDeathGrip) {
4881 0 : win = kungFuDeathGrip->GetWindow();
4882 : }
4883 : } else {
4884 2220 : if (nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(mScriptGlobalObject)) {
4885 : // mScriptGlobalObject is always the inner window, let's get the outer.
4886 0 : win = inner->GetOuterWindow();
4887 : }
4888 : }
4889 :
4890 0 : return win;
4891 : }
4892 :
4893 : bool
4894 11 : nsIDocument::InternalAllowXULXBL()
4895 : {
4896 11 : if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
4897 0 : mAllowXULXBL = eTriTrue;
4898 4 : return true;
4899 : }
4900 :
4901 7 : mAllowXULXBL = eTriFalse;
4902 7 : return false;
4903 : }
4904 :
4905 : // Note: We don't hold a reference to the document observer; we assume
4906 : // that it has a live reference to the document.
4907 : void
4908 0 : nsIDocument::AddObserver(nsIDocumentObserver* aObserver)
4909 : {
4910 0 : NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
4911 : "Observer already in the list");
4912 63 : mObservers.AppendElement(aObserver);
4913 0 : AddMutationObserver(aObserver);
4914 63 : }
4915 :
4916 : bool
4917 62 : nsIDocument::RemoveObserver(nsIDocumentObserver* aObserver)
4918 : {
4919 : // If we're in the process of destroying the document (and we're
4920 : // informing the observers of the destruction), don't remove the
4921 : // observers from the list. This is not a big deal, since we
4922 : // don't hold a live reference to the observers.
4923 0 : if (!mInDestructor) {
4924 62 : RemoveMutationObserver(aObserver);
4925 0 : return mObservers.RemoveElement(aObserver);
4926 : }
4927 :
4928 0 : return mObservers.Contains(aObserver);
4929 : }
4930 :
4931 : void
4932 783 : nsIDocument::MaybeEndOutermostXBLUpdate()
4933 : {
4934 : // Only call BindingManager()->EndOutermostUpdate() when
4935 : // we're not in an update and it is safe to run scripts.
4936 0 : if (mUpdateNestLevel == 0 && mInXBLUpdate) {
4937 0 : if (nsContentUtils::IsSafeToRunScript()) {
4938 0 : mInXBLUpdate = false;
4939 670 : BindingManager()->EndOutermostUpdate();
4940 89 : } else if (!mInDestructor) {
4941 0 : if (!mMaybeEndOutermostXBLUpdateRunner) {
4942 : mMaybeEndOutermostXBLUpdateRunner =
4943 1 : NewRunnableMethod("nsDocument::MaybeEndOutermostXBLUpdate",
4944 : this,
4945 18 : &nsDocument::MaybeEndOutermostXBLUpdate);
4946 : }
4947 0 : nsContentUtils::AddScriptRunner(mMaybeEndOutermostXBLUpdateRunner);
4948 : }
4949 : }
4950 783 : }
4951 :
4952 : void
4953 0 : nsIDocument::BeginUpdate()
4954 : {
4955 : // If the document is going away, then it's probably okay to do things to it
4956 : // in the wrong DocGroup. We're unlikely to run JS or do anything else
4957 : // observable at this point. We reach this point when cycle collecting a
4958 : // <link> element and the unlink code removes a style sheet.
4959 : //
4960 : // TODO(emilio): Style updates are gone, can this happen now?
4961 1388 : if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion && !mIgnoreDocGroupMismatches) {
4962 465 : mDocGroup->ValidateAccess();
4963 : }
4964 :
4965 0 : if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
4966 335 : mInXBLUpdate = true;
4967 0 : BindingManager()->BeginOutermostUpdate();
4968 : }
4969 :
4970 0 : ++mUpdateNestLevel;
4971 0 : nsContentUtils::AddScriptBlocker();
4972 3642 : NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this));
4973 694 : }
4974 :
4975 : void
4976 694 : nsDocument::EndUpdate()
4977 : {
4978 3642 : NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this));
4979 :
4980 0 : nsContentUtils::RemoveScriptBlocker();
4981 :
4982 0 : --mUpdateNestLevel;
4983 :
4984 : // This set of updates may have created XBL bindings. Let the
4985 : // binding manager know we're done.
4986 694 : MaybeEndOutermostXBLUpdate();
4987 :
4988 694 : MaybeInitializeFinalizeFrameLoaders();
4989 0 : }
4990 :
4991 : void
4992 49 : nsDocument::BeginLoad()
4993 : {
4994 0 : MOZ_ASSERT(!mDidCallBeginLoad);
4995 0 : mDidCallBeginLoad = true;
4996 :
4997 : // Block onload here to prevent having to deal with blocking and
4998 : // unblocking it while we know the document is loading.
4999 49 : BlockOnload();
5000 0 : mDidFireDOMContentLoaded = false;
5001 49 : BlockDOMContentLoaded();
5002 :
5003 98 : if (mScriptLoader) {
5004 0 : mScriptLoader->BeginDeferringScripts();
5005 : }
5006 :
5007 0 : NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
5008 49 : }
5009 :
5010 : void
5011 0 : nsIDocument::MozSetImageElement(const nsAString& aImageElementId,
5012 : Element* aElement)
5013 : {
5014 0 : if (aImageElementId.IsEmpty())
5015 0 : return;
5016 :
5017 : // Hold a script blocker while calling SetImageElement since that can call
5018 : // out to id-observers
5019 0 : nsAutoScriptBlocker scriptBlocker;
5020 :
5021 0 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aImageElementId);
5022 0 : if (entry) {
5023 0 : entry->SetImageElement(aElement);
5024 0 : if (entry->IsEmpty()) {
5025 0 : mIdentifierMap.RemoveEntry(entry);
5026 : }
5027 : }
5028 : }
5029 :
5030 : Element*
5031 0 : nsIDocument::LookupImageElement(const nsAString& aId)
5032 : {
5033 0 : if (aId.IsEmpty())
5034 : return nullptr;
5035 :
5036 0 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
5037 0 : return entry ? entry->GetImageIdElement() : nullptr;
5038 : }
5039 :
5040 : void
5041 52 : nsIDocument::DispatchContentLoadedEvents()
5042 : {
5043 : // If you add early returns from this method, make sure you're
5044 : // calling UnblockOnload properly.
5045 :
5046 : // Unpin references to preloaded images
5047 104 : mPreloadingImages.Clear();
5048 :
5049 : // DOM manipulation after content loaded should not care if the element
5050 : // came from the preloader.
5051 0 : mPreloadedPreconnects.Clear();
5052 :
5053 104 : if (mTiming) {
5054 20 : mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
5055 : }
5056 :
5057 : // Dispatch observer notification to notify observers document is interactive.
5058 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
5059 52 : if (os) {
5060 0 : nsIPrincipal* principal = NodePrincipal();
5061 0 : os->NotifyObservers(this,
5062 52 : nsContentUtils::IsSystemPrincipal(principal) ?
5063 : "chrome-document-interactive" :
5064 : "content-document-interactive",
5065 0 : nullptr);
5066 : }
5067 :
5068 : // Fire a DOM event notifying listeners that this document has been
5069 : // loaded (excluding images and other loads initiated by this
5070 : // document).
5071 0 : nsContentUtils::DispatchTrustedEvent(this, this,
5072 0 : NS_LITERAL_STRING("DOMContentLoaded"),
5073 156 : true, false);
5074 :
5075 52 : if (MayStartLayout()) {
5076 0 : MaybeResolveReadyForIdle();
5077 : }
5078 :
5079 0 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
5080 0 : nsIDocShell* docShell = this->GetDocShell();
5081 :
5082 0 : if (timelines && timelines->HasConsumer(docShell)) {
5083 0 : timelines->AddMarkerForDocShell(docShell,
5084 0 : MakeUnique<DocLoadingTimelineMarker>("document::DOMContentLoaded"));
5085 : }
5086 :
5087 104 : if (mTiming) {
5088 1 : mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
5089 : }
5090 :
5091 : // If this document is a [i]frame, fire a DOMFrameContentLoaded
5092 : // event on all parent documents notifying that the HTML (excluding
5093 : // other external files such as images and stylesheets) in a frame
5094 : // has finished loading.
5095 :
5096 : // target_frame is the [i]frame element that will be used as the
5097 : // target for the event. It's the [i]frame whose content is done
5098 : // loading.
5099 104 : nsCOMPtr<EventTarget> target_frame;
5100 :
5101 52 : if (mParentDocument) {
5102 3 : target_frame = mParentDocument->FindContentForSubDocument(this);
5103 : }
5104 :
5105 52 : if (target_frame) {
5106 6 : nsCOMPtr<nsIDocument> parent = mParentDocument;
5107 3 : do {
5108 0 : RefPtr<Event> event;
5109 3 : if (parent) {
5110 0 : IgnoredErrorResult ignored;
5111 0 : event = parent->CreateEvent(NS_LITERAL_STRING("Events"),
5112 9 : CallerType::System, ignored);
5113 :
5114 : }
5115 :
5116 0 : if (event) {
5117 0 : event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
5118 0 : true);
5119 :
5120 3 : event->SetTarget(target_frame);
5121 3 : event->SetTrusted(true);
5122 :
5123 : // To dispatch this event we must manually call
5124 : // EventDispatcher::Dispatch() on the ancestor document since the
5125 : // target is not in the same document, so the event would never reach
5126 : // the ancestor document if we used the normal event
5127 : // dispatching code.
5128 :
5129 0 : WidgetEvent* innerEvent = event->WidgetEventPtr();
5130 0 : if (innerEvent) {
5131 3 : nsEventStatus status = nsEventStatus_eIgnore;
5132 :
5133 0 : RefPtr<nsPresContext> context = parent->GetPresContext();
5134 :
5135 3 : if (context) {
5136 0 : EventDispatcher::Dispatch(parent, context, innerEvent, event,
5137 0 : &status);
5138 : }
5139 : }
5140 : }
5141 :
5142 3 : parent = parent->GetParentDocument();
5143 : } while (parent);
5144 : }
5145 :
5146 : // If the document has a manifest attribute, fire a MozApplicationManifest
5147 : // event.
5148 52 : Element* root = GetRootElement();
5149 52 : if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
5150 : nsContentUtils::DispatchChromeEvent(this, this,
5151 0 : NS_LITERAL_STRING("MozApplicationManifest"),
5152 0 : true, true);
5153 : }
5154 :
5155 52 : if (mMaybeServiceWorkerControlled) {
5156 : using mozilla::dom::ServiceWorkerManager;
5157 21 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
5158 0 : if (swm) {
5159 0 : Maybe<ClientInfo> clientInfo = GetClientInfo();
5160 7 : if (clientInfo.isSome()) {
5161 7 : swm->MaybeCheckNavigationUpdate(clientInfo.ref());
5162 : }
5163 : }
5164 : }
5165 :
5166 0 : UnblockOnload(true);
5167 0 : }
5168 :
5169 : #if defined(DEBUG) && !defined(ANDROID)
5170 : // We want to get to a point where all about: pages ship with a CSP. This
5171 : // assertion ensures that we can not deploy new about: pages without a CSP.
5172 : // Initially we will whitelist legacy about: pages which not yet have a CSP
5173 : // attached, but ultimately that whitelist should disappear.
5174 : // Please note that any about: page should not use inline JS or inline CSS,
5175 : // and instead should load JS and CSS from an external file (*.js, *.css)
5176 : // which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally,
5177 : // the CSP allows precisely the resources that need to be loaded; but it
5178 : // should at least be as strong as:
5179 : // <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/>
5180 : static void
5181 49 : AssertContentPrivilegedAboutPageHasCSP(nsIURI* aDocumentURI, nsIPrincipal* aPrincipal)
5182 : {
5183 : // Curently we can't serialize the CSP, hence we only assert if
5184 : // running in the content process.
5185 49 : if (!XRE_IsContentProcess()) {
5186 0 : return;
5187 : }
5188 :
5189 : // Check if we are loading an about: URI at all
5190 : bool isAboutURI =
5191 0 : (NS_SUCCEEDED(aDocumentURI->SchemeIs("about", &isAboutURI)) && isAboutURI);
5192 :
5193 0 : if (!isAboutURI) {
5194 : return;
5195 : }
5196 :
5197 : // Check if we are loading a content-privileged about: URI
5198 0 : nsCOMPtr<nsIAboutModule> aboutModule;
5199 0 : nsresult rv = NS_GetAboutModule(aDocumentURI, getter_AddRefs(aboutModule));
5200 0 : NS_ENSURE_SUCCESS_VOID(rv);
5201 :
5202 0 : uint32_t aboutModuleFlags = 0;
5203 0 : rv = aboutModule->GetURIFlags(aDocumentURI, &aboutModuleFlags);
5204 0 : NS_ENSURE_SUCCESS_VOID(rv);
5205 :
5206 0 : if (!(aboutModuleFlags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT)) {
5207 : return;
5208 : }
5209 :
5210 : // Potentially init the legacy whitelist of about URIs without a CSP.
5211 0 : static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP;
5212 0 : if (!sLegacyAboutPagesWithNoCSP) {
5213 0 : sLegacyAboutPagesWithNoCSP = new nsTArray<nsCString>();
5214 0 : nsAutoCString legacyAboutPages;
5215 : Preferences::GetCString("csp.content_privileged_about_uris_without_csp",
5216 0 : legacyAboutPages);
5217 0 : for (const nsACString& hostString : legacyAboutPages.Split(',')) {
5218 : // please note that for the actual whitelist we only store the path of
5219 : // about: URI. Let's reassemble the full about URI here so we don't
5220 : // have to remove query arguments later.
5221 0 : nsCString aboutURI;
5222 0 : aboutURI.AppendLiteral("about:");
5223 0 : aboutURI.Append(hostString);
5224 0 : sLegacyAboutPagesWithNoCSP->AppendElement(aboutURI);
5225 : }
5226 0 : ClearOnShutdown(&sLegacyAboutPagesWithNoCSP);
5227 : }
5228 :
5229 : // Check if the about URI is whitelisted
5230 0 : nsAutoCString aboutSpec;
5231 0 : aDocumentURI->GetSpec(aboutSpec);
5232 0 : for (auto& legacyPageEntry : *sLegacyAboutPagesWithNoCSP) {
5233 : // please note that we perform a substring match here on purpose,
5234 : // so we don't have to deal and parse out all the query arguments
5235 : // the various about pages rely on.
5236 0 : if (aboutSpec.Find(legacyPageEntry) == 0) {
5237 0 : return;
5238 : }
5239 : }
5240 :
5241 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
5242 0 : aPrincipal->GetCsp(getter_AddRefs(csp));
5243 0 : nsAutoString parsedPolicyStr;
5244 0 : if (csp) {
5245 0 : uint32_t policyCount = 0;
5246 0 : csp->GetPolicyCount(&policyCount);
5247 0 : if (policyCount > 0) {
5248 0 : csp->GetPolicyString(0, parsedPolicyStr);
5249 : }
5250 : }
5251 0 : MOZ_ASSERT(parsedPolicyStr.Find("default-src") >= 0,
5252 : "about: page must contain a CSP including default-src");
5253 : }
5254 : #endif
5255 :
5256 : void
5257 0 : nsDocument::EndLoad()
5258 : {
5259 : #if defined(DEBUG) && !defined(ANDROID)
5260 0 : AssertContentPrivilegedAboutPageHasCSP(mDocumentURI, NodePrincipal());
5261 : #endif
5262 :
5263 : // EndLoad may have been called without a matching call to BeginLoad, in the
5264 : // case of a failed parse (for example, due to timeout). In such a case, we
5265 : // still want to execute part of this code to do appropriate cleanup, but we
5266 : // gate part of it because it is intended to match 1-for-1 with calls to
5267 : // BeginLoad. We have an explicit flag bit for this purpose, since it's
5268 : // complicated and error prone to derive this condition from other related
5269 : // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
5270 :
5271 : // Part 1: Code that always executes to cleanup end of parsing, whether
5272 : // that parsing was successful or not.
5273 :
5274 : // Drop the ref to our parser, if any, but keep hold of the sink so that we
5275 : // can flush it from FlushPendingNotifications as needed. We might have to
5276 : // do that to get a StartLayout() to happen.
5277 98 : if (mParser) {
5278 0 : mWeakSink = do_GetWeakReference(mParser->GetContentSink());
5279 0 : mParser = nullptr;
5280 : }
5281 :
5282 222 : NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
5283 :
5284 : // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
5285 :
5286 49 : if (!mDidCallBeginLoad) {
5287 : return;
5288 : }
5289 0 : mDidCallBeginLoad = false;
5290 :
5291 49 : UnblockDOMContentLoaded();
5292 : }
5293 :
5294 : void
5295 49 : nsIDocument::UnblockDOMContentLoaded()
5296 : {
5297 49 : MOZ_ASSERT(mBlockDOMContentLoaded);
5298 0 : if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
5299 : return;
5300 : }
5301 :
5302 0 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p UnblockDOMContentLoaded", this));
5303 :
5304 0 : mDidFireDOMContentLoaded = true;
5305 0 : if (nsIPresShell* shell = GetShell()) {
5306 16 : shell->GetRefreshDriver()->NotifyDOMContentLoaded();
5307 : }
5308 :
5309 :
5310 49 : MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
5311 49 : if (!mSynchronousDOMContentLoaded) {
5312 18 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
5313 : nsCOMPtr<nsIRunnable> ev =
5314 0 : NewRunnableMethod("nsIDocument::DispatchContentLoadedEvents",
5315 : this,
5316 36 : &nsIDocument::DispatchContentLoadedEvents);
5317 0 : Dispatch(TaskCategory::Other, ev.forget());
5318 : } else {
5319 31 : DispatchContentLoadedEvents();
5320 : }
5321 : }
5322 :
5323 : void
5324 10 : nsIDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
5325 : {
5326 10 : MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
5327 : "Someone forgot a scriptblocker");
5328 40 : NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
5329 : (this, aContent, aStateMask));
5330 10 : }
5331 :
5332 : void
5333 5 : nsIDocument::DocumentStatesChanged(EventStates aStateMask)
5334 : {
5335 0 : UpdateDocumentStates(aStateMask);
5336 0 : NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
5337 5 : }
5338 :
5339 : void
5340 0 : nsIDocument::StyleRuleChanged(StyleSheet* aSheet, css::Rule* aStyleRule)
5341 : {
5342 0 : if (nsIPresShell* shell = GetShell()) {
5343 0 : shell->ApplicableStylesChanged();
5344 : }
5345 :
5346 0 : if (!StyleSheetChangeEventsEnabled()) {
5347 : return;
5348 : }
5349 :
5350 0 : DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5351 : "StyleRuleChanged",
5352 : mRule,
5353 : aStyleRule);
5354 : }
5355 :
5356 : void
5357 0 : nsIDocument::StyleRuleAdded(StyleSheet* aSheet, css::Rule* aStyleRule)
5358 : {
5359 0 : if (nsIPresShell* shell = GetShell()) {
5360 0 : shell->ApplicableStylesChanged();
5361 : }
5362 :
5363 0 : if (!StyleSheetChangeEventsEnabled()) {
5364 : return;
5365 : }
5366 :
5367 0 : DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5368 : "StyleRuleAdded",
5369 : mRule,
5370 : aStyleRule);
5371 : }
5372 :
5373 : void
5374 0 : nsIDocument::StyleRuleRemoved(StyleSheet* aSheet, css::Rule* aStyleRule)
5375 : {
5376 0 : if (nsIPresShell* shell = GetShell()) {
5377 0 : shell->ApplicableStylesChanged();
5378 : }
5379 :
5380 0 : if (!StyleSheetChangeEventsEnabled()) {
5381 : return;
5382 : }
5383 :
5384 0 : DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5385 : "StyleRuleRemoved",
5386 : mRule,
5387 : aStyleRule);
5388 : }
5389 :
5390 : #undef DO_STYLESHEET_NOTIFICATION
5391 :
5392 : already_AddRefed<AnonymousContent>
5393 0 : nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
5394 : {
5395 0 : nsIPresShell* shell = GetShell();
5396 0 : if (!shell || !shell->GetCanvasFrame()) {
5397 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5398 : return nullptr;
5399 : }
5400 :
5401 0 : // We're about to insert random content here that will be rendered. We're
5402 0 : // going to need more than svg.css here...
5403 0 : if (IsSVGDocument()) {
5404 0 : AsSVGDocument()->EnsureNonSVGUserAgentStyleSheetsLoaded();
5405 0 : }
5406 :
5407 : nsAutoScriptBlocker scriptBlocker;
5408 : nsCOMPtr<Element> container = shell->GetCanvasFrame()
5409 : ->GetCustomContentContainer();
5410 0 : if (!container) {
5411 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5412 : return nullptr;
5413 : }
5414 :
5415 : // Clone the node to avoid returning a direct reference
5416 : nsCOMPtr<nsINode> clonedElement = aElement.CloneNode(true, aRv);
5417 0 : if (aRv.Failed()) {
5418 0 : return nullptr;
5419 : }
5420 :
5421 : // Insert the element into the container
5422 : nsresult rv;
5423 0 : rv = container->AppendChildTo(clonedElement->AsContent(), true);
5424 0 : if (NS_FAILED(rv)) {
5425 : return nullptr;
5426 0 : }
5427 :
5428 0 : RefPtr<AnonymousContent> anonymousContent =
5429 : new AnonymousContent(clonedElement->AsElement());
5430 : mAnonymousContents.AppendElement(anonymousContent);
5431 :
5432 0 : shell->GetCanvasFrame()->ShowCustomContentContainer();
5433 :
5434 : return anonymousContent.forget();
5435 0 : }
5436 0 :
5437 0 : void
5438 0 : nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
5439 : ErrorResult& aRv)
5440 : {
5441 0 : nsIPresShell* shell = GetShell();
5442 0 : if (!shell || !shell->GetCanvasFrame()) {
5443 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5444 0 : return;
5445 0 : }
5446 0 :
5447 : nsAutoScriptBlocker scriptBlocker;
5448 : nsCOMPtr<Element> container = shell->GetCanvasFrame()
5449 : ->GetCustomContentContainer();
5450 0 : if (!container) {
5451 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5452 : return;
5453 0 : }
5454 :
5455 : // Iterate over mAnonymousContents to find and remove the given node.
5456 0 : for (size_t i = 0, len = mAnonymousContents.Length(); i < len; ++i) {
5457 : if (mAnonymousContents[i] == &aContent) {
5458 : // Get the node from the customContent
5459 0 : nsCOMPtr<Element> node = aContent.GetContentNode();
5460 0 :
5461 0 : // Remove the entry in mAnonymousContents
5462 : mAnonymousContents.RemoveElementAt(i);
5463 :
5464 0 : // Remove the node from its container
5465 : container->RemoveChild(*node, aRv);
5466 : if (aRv.Failed()) {
5467 0 : return;
5468 0 : }
5469 :
5470 : break;
5471 : }
5472 : }
5473 0 : if (mAnonymousContents.IsEmpty()) {
5474 : shell->GetCanvasFrame()->HideCustomContentContainer();
5475 0 : }
5476 : }
5477 :
5478 : Element*
5479 0 : nsIDocument::GetAnonRootIfInAnonymousContentContainer(nsINode* aNode) const
5480 0 : {
5481 : if (!aNode->IsInNativeAnonymousSubtree()) {
5482 : return nullptr;
5483 : }
5484 0 :
5485 0 : nsIPresShell* shell = GetShell();
5486 0 : if (!shell || !shell->GetCanvasFrame()) {
5487 0 : return nullptr;
5488 : }
5489 :
5490 : nsAutoScriptBlocker scriptBlocker;
5491 : nsCOMPtr<Element> customContainer = shell->GetCanvasFrame()
5492 : ->GetCustomContentContainer();
5493 : if (!customContainer) {
5494 0 : return nullptr;
5495 0 : }
5496 0 :
5497 0 : // An arbitrary number of elements can be inserted as children of the custom
5498 0 : // container frame. We want the one that was added that contains aNode, so
5499 : // we need to keep track of the last child separately using |child| here.
5500 0 : nsINode* child = aNode;
5501 0 : nsINode* parent = aNode->GetParentNode();
5502 : while (parent && parent->IsInNativeAnonymousSubtree()) {
5503 : if (parent == customContainer) {
5504 : return Element::FromNode(child);
5505 : }
5506 : child = parent;
5507 0 : parent = child->GetParentNode();
5508 : }
5509 82 : return nullptr;
5510 0 : }
5511 82 :
5512 : Maybe<ClientInfo>
5513 : nsIDocument::GetClientInfo() const
5514 : {
5515 : nsPIDOMWindowInner* inner = GetInnerWindow();
5516 : if (inner) {
5517 0 : return inner->GetClientInfo();
5518 : }
5519 0 : return Maybe<ClientInfo>();
5520 0 : }
5521 0 :
5522 : Maybe<ClientState>
5523 : nsIDocument::GetClientState() const
5524 : {
5525 : nsPIDOMWindowInner* inner = GetInnerWindow();
5526 : if (inner) {
5527 103 : return inner->GetClientState();
5528 : }
5529 103 : return Maybe<ClientState>();
5530 0 : }
5531 103 :
5532 : Maybe<ServiceWorkerDescriptor>
5533 : nsIDocument::GetController() const
5534 : {
5535 : nsPIDOMWindowInner* inner = GetInnerWindow();
5536 : if (inner) {
5537 : return inner->GetController();
5538 : }
5539 : return Maybe<ServiceWorkerDescriptor>();
5540 0 : }
5541 :
5542 0 : //
5543 0 : // nsIDocument interface
5544 0 : //
5545 0 : DocumentType*
5546 : nsIDocument::GetDoctype() const
5547 : {
5548 : for (nsIContent* child = GetFirstChild();
5549 : child;
5550 : child = child->GetNextSibling()) {
5551 : if (child->NodeType() == DOCUMENT_TYPE_NODE) {
5552 : return static_cast<DocumentType*>(child);
5553 0 : }
5554 : }
5555 0 : return nullptr;
5556 0 : }
5557 0 :
5558 0 : DOMImplementation*
5559 0 : nsIDocument::GetImplementation(ErrorResult& rv)
5560 0 : {
5561 : if (!mDOMImplementation) {
5562 0 : nsCOMPtr<nsIURI> uri;
5563 : NS_NewURI(getter_AddRefs(uri), "about:blank");
5564 0 : if (!uri) {
5565 0 : rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5566 0 : return nullptr;
5567 0 : }
5568 : bool hasHadScriptObject = true;
5569 : nsIScriptGlobalObject* scriptObject =
5570 0 : GetScriptHandlingObject(hasHadScriptObject);
5571 : if (!scriptObject && hasHadScriptObject) {
5572 : rv.Throw(NS_ERROR_UNEXPECTED);
5573 0 : return nullptr;
5574 : }
5575 : mDOMImplementation = new DOMImplementation(this,
5576 0 : scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5577 : }
5578 0 :
5579 0 : return mDOMImplementation;
5580 0 : }
5581 0 :
5582 : bool IsLowercaseASCII(const nsAString& aValue)
5583 : {
5584 : int32_t len = aValue.Length();
5585 : for (int32_t i = 0; i < len; ++i) {
5586 : char16_t c = aValue[i];
5587 : if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5588 : return false;
5589 : }
5590 0 : }
5591 : return true;
5592 0 : }
5593 0 :
5594 0 : // We only support pseudo-elements with two colons in this function.
5595 0 : static CSSPseudoElementType
5596 : GetPseudoElementType(const nsString& aString, ErrorResult& aRv)
5597 0 : {
5598 0 : MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null");
5599 0 : if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
5600 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5601 : return CSSPseudoElementType::NotPseudo;
5602 : }
5603 53 : RefPtr<nsAtom> pseudo = NS_Atomize(Substring(aString, 1));
5604 : return nsCSSPseudoElements::GetPseudoType(pseudo,
5605 : CSSEnabledState::eInUASheets);
5606 : }
5607 106 :
5608 106 : already_AddRefed<Element>
5609 : nsIDocument::CreateElement(const nsAString& aTagName,
5610 : const ElementCreationOptionsOrString& aOptions,
5611 : ErrorResult& rv)
5612 0 : {
5613 0 : rv = nsContentUtils::CheckQName(aTagName, false);
5614 0 : if (rv.Failed()) {
5615 0 : return nullptr;
5616 : }
5617 :
5618 53 : bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
5619 0 : nsAutoString lcTagName;
5620 53 : if (needsLowercase) {
5621 : nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5622 0 : }
5623 :
5624 1 : const nsString* is = nullptr;
5625 106 : CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
5626 0 : if (aOptions.IsElementCreationOptions()) {
5627 : const ElementCreationOptions& options =
5628 : aOptions.GetAsElementCreationOptions();
5629 :
5630 : if (CustomElementRegistry::IsCustomElementEnabled(this) &&
5631 106 : options.mIs.WasPassed()) {
5632 0 : is = &options.mIs.Value();
5633 0 : }
5634 0 :
5635 0 : // Check 'pseudo' and throw an exception if it's not one allowed
5636 0 : // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
5637 : if (options.mPseudo.WasPassed()) {
5638 : pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
5639 : if (rv.Failed() ||
5640 : pseudoType == CSSPseudoElementType::NotPseudo ||
5641 : !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) {
5642 106 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5643 159 : return nullptr;
5644 : }
5645 53 : }
5646 0 : }
5647 :
5648 : RefPtr<Element> elem = CreateElem(
5649 0 : needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
5650 0 :
5651 : if (pseudoType != CSSPseudoElementType::NotPseudo) {
5652 : elem->SetPseudoElementType(pseudoType);
5653 53 : }
5654 :
5655 : if (is) {
5656 : elem->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
5657 6 : }
5658 :
5659 : return elem.forget();
5660 : }
5661 :
5662 12 : already_AddRefed<Element>
5663 6 : nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
5664 : const nsAString& aQualifiedName,
5665 : const ElementCreationOptionsOrString& aOptions,
5666 : ErrorResult& rv)
5667 12 : {
5668 12 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5669 : rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5670 : aQualifiedName,
5671 : mNodeInfoManager,
5672 0 : ELEMENT_NODE,
5673 12 : getter_AddRefs(nodeInfo));
5674 6 : if (rv.Failed()) {
5675 0 : return nullptr;
5676 0 : }
5677 0 :
5678 : const nsString* is = nullptr;
5679 : if (CustomElementRegistry::IsCustomElementEnabled(this) &&
5680 : aOptions.IsElementCreationOptions()) {
5681 0 : const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions();
5682 0 : if (options.mIs.WasPassed()) {
5683 0 : is = &options.mIs.Value();
5684 12 : }
5685 : }
5686 :
5687 : nsCOMPtr<Element> element;
5688 0 : rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5689 0 : NOT_FROM_PARSER, is);
5690 : if (rv.Failed()) {
5691 : return nullptr;
5692 0 : }
5693 :
5694 : if (is) {
5695 : element->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *is, true);
5696 0 : }
5697 :
5698 0 : return element.forget();
5699 0 : }
5700 :
5701 : already_AddRefed<nsTextNode>
5702 : nsIDocument::CreateEmptyTextNode() const
5703 0 : {
5704 : RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5705 0 : return text.forget();
5706 : }
5707 0 :
5708 0 : already_AddRefed<nsTextNode>
5709 : nsIDocument::CreateTextNode(const nsAString& aData) const
5710 : {
5711 : RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5712 0 : // Don't notify; this node is still being created.
5713 : text->SetText(aData, false);
5714 0 : return text.forget();
5715 0 : }
5716 :
5717 : already_AddRefed<DocumentFragment>
5718 : nsIDocument::CreateDocumentFragment() const
5719 : {
5720 0 : RefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5721 : return frag.forget();
5722 0 : }
5723 :
5724 : // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
5725 0 : already_AddRefed<dom::Comment>
5726 0 : nsIDocument::CreateComment(const nsAString& aData) const
5727 : {
5728 : RefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5729 :
5730 0 : // Don't notify; this node is still being created.
5731 : comment->SetText(aData, false);
5732 : return comment.forget();
5733 0 : }
5734 0 :
5735 : already_AddRefed<CDATASection>
5736 : nsIDocument::CreateCDATASection(const nsAString& aData,
5737 : ErrorResult& rv)
5738 0 : {
5739 0 : if (IsHTMLDocument()) {
5740 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5741 : return nullptr;
5742 : }
5743 0 :
5744 : if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5745 : rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5746 0 : return nullptr;
5747 : }
5748 0 :
5749 : RefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5750 :
5751 : // Don't notify; this node is still being created.
5752 0 : cdata->SetText(aData, false);
5753 :
5754 : return cdata.forget();
5755 : }
5756 0 :
5757 0 : already_AddRefed<ProcessingInstruction>
5758 0 : nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5759 : const nsAString& aData,
5760 : ErrorResult& rv) const
5761 : {
5762 0 : nsresult res = nsContentUtils::CheckQName(aTarget, false);
5763 0 : if (NS_FAILED(res)) {
5764 : rv.Throw(res);
5765 : return nullptr;
5766 : }
5767 :
5768 0 : if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5769 : rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5770 0 : return nullptr;
5771 : }
5772 :
5773 : RefPtr<ProcessingInstruction> pi =
5774 0 : NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5775 :
5776 0 : return pi.forget();
5777 0 : }
5778 :
5779 : already_AddRefed<Attr>
5780 : nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
5781 0 : {
5782 0 : if (!mNodeInfoManager) {
5783 0 : rv.Throw(NS_ERROR_NOT_INITIALIZED);
5784 : return nullptr;
5785 : }
5786 :
5787 0 : nsresult res = nsContentUtils::CheckQName(aName, false);
5788 0 : if (NS_FAILED(res)) {
5789 0 : rv.Throw(res);
5790 : return nullptr;
5791 : }
5792 :
5793 : nsAutoString name;
5794 0 : if (IsHTMLDocument()) {
5795 0 : nsContentUtils::ASCIIToLower(aName, name);
5796 0 : } else {
5797 0 : name = aName;
5798 0 : }
5799 :
5800 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5801 : res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None,
5802 0 : ATTRIBUTE_NODE, getter_AddRefs(nodeInfo));
5803 0 : if (NS_FAILED(res)) {
5804 0 : rv.Throw(res);
5805 : return nullptr;
5806 : }
5807 :
5808 0 : RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5809 : EmptyString());
5810 : return attribute.forget();
5811 : }
5812 0 :
5813 0 : already_AddRefed<Attr>
5814 : nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
5815 : const nsAString& aQualifiedName,
5816 : ErrorResult& rv)
5817 0 : {
5818 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5819 : rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5820 : aQualifiedName,
5821 : mNodeInfoManager,
5822 0 : ATTRIBUTE_NODE,
5823 0 : getter_AddRefs(nodeInfo));
5824 0 : if (rv.Failed()) {
5825 : return nullptr;
5826 : }
5827 :
5828 543 : RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5829 : EmptyString());
5830 1679 : return attribute.forget();
5831 0 : }
5832 25 :
5833 : void
5834 1 : nsIDocument::ResolveScheduledSVGPresAttrs()
5835 543 : {
5836 : for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
5837 : nsSVGElement* svg = iter.Get()->GetKey();
5838 0 : svg->UpdateContentDeclarationBlock();
5839 : }
5840 0 : mLazySVGPresElements.Clear();
5841 : }
5842 0 :
5843 0 : already_AddRefed<nsSimpleContentList>
5844 : nsIDocument::BlockedTrackingNodes() const
5845 0 : {
5846 0 : RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
5847 0 :
5848 : nsTArray<nsWeakPtr> blockedTrackingNodes;
5849 : blockedTrackingNodes = mBlockedTrackingNodes;
5850 :
5851 0 : for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
5852 0 : nsWeakPtr weakNode = blockedTrackingNodes[i];
5853 : nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
5854 : // Consider only nodes to which we have managed to get strong references.
5855 : // Coping with nullptrs since it's expected for nodes to disappear when
5856 0 : // nobody else is referring to them.
5857 : if (node) {
5858 : list->AppendElement(node);
5859 : }
5860 0 : }
5861 :
5862 0 : return list.forget();
5863 : }
5864 :
5865 0 : void
5866 0 : nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
5867 0 : {
5868 0 : aSheetSet.Truncate();
5869 0 :
5870 : // Look through our sheets, find the selected set title
5871 0 : size_t count = SheetCount();
5872 : nsAutoString title;
5873 : for (size_t index = 0; index < count; index++) {
5874 : StyleSheet* sheet = SheetAt(index);
5875 : NS_ASSERTION(sheet, "Null sheet in sheet list!");
5876 0 :
5877 : if (sheet->Disabled()) {
5878 0 : // Disabled sheets don't affect the currently selected set
5879 : continue;
5880 0 : }
5881 :
5882 0 : sheet->GetTitle(title);
5883 0 :
5884 : if (aSheetSet.IsEmpty()) {
5885 : aSheetSet = title;
5886 : } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
5887 : // Sheets from multiple sets enabled; return null string, per spec.
5888 : SetDOMStringToNull(aSheetSet);
5889 0 : return;
5890 : }
5891 0 : }
5892 : }
5893 :
5894 : void
5895 : nsIDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
5896 : {
5897 0 : if (DOMStringIsNull(aSheetSet)) {
5898 0 : return;
5899 : }
5900 :
5901 : // Must update mLastStyleSheetSet before doing anything else with stylesheets
5902 0 : // or CSSLoaders.
5903 : mLastStyleSheetSet = aSheetSet;
5904 0 : EnableStyleSheetsForSetInternal(aSheetSet, true);
5905 : }
5906 :
5907 0 : void
5908 : nsIDocument::SetPreferredStyleSheetSet(const nsAString& aSheetSet)
5909 : {
5910 : mPreferredStyleSheetSet = aSheetSet;
5911 : // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
5912 : // spec.
5913 0 : if (DOMStringIsNull(mLastStyleSheetSet)) {
5914 : // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
5915 0 : // per spec. The idea here is that we're changing our preferred set and
5916 : // that shouldn't change the value of lastStyleSheetSet. Also, we're
5917 : // using the Internal version so we can update the CSSLoader and not have
5918 0 : // to worry about null strings.
5919 : EnableStyleSheetsForSetInternal(aSheetSet, true);
5920 0 : }
5921 0 : }
5922 :
5923 0 : DOMStringList*
5924 : nsIDocument::StyleSheetSets()
5925 : {
5926 : if (!mStyleSheetSetList) {
5927 0 : mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
5928 : }
5929 : return mStyleSheetSetList;
5930 0 : }
5931 :
5932 : void
5933 : nsIDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
5934 : {
5935 0 : // Per spec, passing in null is a no-op.
5936 : if (!DOMStringIsNull(aSheetSet)) {
5937 0 : // Note: must make sure to not change the CSSLoader's preferred sheet --
5938 : // that value should be equal to either our lastStyleSheetSet (if that's
5939 : // non-null) or to our preferredStyleSheetSet. And this method doesn't
5940 0 : // change either of those.
5941 : EnableStyleSheetsForSetInternal(aSheetSet, false);
5942 : }
5943 0 : }
5944 0 :
5945 0 : void
5946 0 : nsIDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
5947 0 : bool aUpdateCSSLoader)
5948 : {
5949 0 : size_t count = SheetCount();
5950 0 : nsAutoString title;
5951 0 : for (size_t index = 0; index < count; index++) {
5952 : StyleSheet* sheet = SheetAt(index);
5953 : NS_ASSERTION(sheet, "Null sheet in sheet list!");
5954 0 :
5955 0 : sheet->GetTitle(title);
5956 : if (!title.IsEmpty()) {
5957 0 : sheet->SetEnabled(title.Equals(aSheetSet));
5958 0 : }
5959 0 : }
5960 : if (aUpdateCSSLoader) {
5961 : CSSLoader()->DocumentStyleSheetSetChanged();
5962 0 : }
5963 : if (nsIPresShell* shell = GetShell()) {
5964 : if (shell->StyleSet()->StyleSheetsHaveChanged()) {
5965 0 : shell->ApplicableStylesChanged();
5966 : }
5967 0 : }
5968 0 : }
5969 0 :
5970 0 : void
5971 : nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
5972 : {
5973 0 : nsAutoCString charset;
5974 : GetDocumentCharacterSet()->Name(charset);
5975 0 : CopyASCIItoUTF16(charset, aCharacterSet);
5976 : }
5977 0 :
5978 : already_AddRefed<nsINode>
5979 : nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
5980 : {
5981 : nsINode* imported = &aNode;
5982 :
5983 : switch (imported->NodeType()) {
5984 : case DOCUMENT_NODE:
5985 : {
5986 : break;
5987 : }
5988 : case DOCUMENT_FRAGMENT_NODE:
5989 : case ATTRIBUTE_NODE:
5990 : case ELEMENT_NODE:
5991 0 : case PROCESSING_INSTRUCTION_NODE:
5992 : case TEXT_NODE:
5993 : case CDATA_SECTION_NODE:
5994 : case COMMENT_NODE:
5995 0 : case DOCUMENT_TYPE_NODE:
5996 : {
5997 : return nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, nullptr, rv);
5998 : }
5999 0 : default:
6000 : {
6001 : NS_WARNING("Don't know how to clone this nodetype for importNode.");
6002 : }
6003 : }
6004 0 :
6005 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6006 : return nullptr;
6007 : }
6008 0 :
6009 0 : void
6010 0 : nsIDocument::LoadBindingDocument(const nsAString& aURI,
6011 0 : nsIPrincipal& aSubjectPrincipal,
6012 : ErrorResult& rv)
6013 : {
6014 0 : nsCOMPtr<nsIURI> uri;
6015 : rv = NS_NewURI(getter_AddRefs(uri), aURI, mCharacterSet, GetDocBaseURI());
6016 : if (rv.Failed()) {
6017 : return;
6018 0 : }
6019 :
6020 0 : BindingManager()->LoadBindingDocument(this, uri, &aSubjectPrincipal);
6021 0 : }
6022 :
6023 : Element*
6024 0 : nsIDocument::GetBindingParent(nsINode& aNode)
6025 0 : {
6026 : nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
6027 : if (!content)
6028 : return nullptr;
6029 16 :
6030 : nsIContent* bindingParent = content->GetBindingParent();
6031 : return bindingParent ? bindingParent->AsElement() : nullptr;
6032 0 : }
6033 :
6034 : static Element*
6035 : GetElementByAttribute(Element* aElement, nsAtom* aAttrName,
6036 : const nsAString& aAttrValue, bool aUniversalMatch)
6037 : {
6038 20 : if (aUniversalMatch ? aElement->HasAttr(kNameSpaceID_None, aAttrName) :
6039 20 : aElement->AttrValueIs(kNameSpaceID_None, aAttrName,
6040 8 : aAttrValue, eCaseMatters)) {
6041 24 : return aElement;
6042 : }
6043 :
6044 : for (nsIContent* child = aElement->GetFirstChild();
6045 : child;
6046 10 : child = child->GetNextSibling()) {
6047 10 : if (!child->IsElement()) {
6048 0 : continue;
6049 : }
6050 :
6051 : Element* matchedElement =
6052 : GetElementByAttribute(child->AsElement(), aAttrName, aAttrValue,
6053 : aUniversalMatch);
6054 : if (matchedElement)
6055 : return matchedElement;
6056 0 : }
6057 :
6058 : return nullptr;
6059 : }
6060 8 :
6061 1 : Element*
6062 : nsIDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
6063 : nsAtom* aAttrName,
6064 4 : const nsAString& aAttrValue) const
6065 : {
6066 1 : nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
6067 : if (!nodeList)
6068 1 : return nullptr;
6069 12 :
6070 6 : uint32_t length = nodeList->Length();
6071 :
6072 : bool universalMatch = aAttrValue.EqualsLiteral("*");
6073 :
6074 : for (uint32_t i = 0; i < length; ++i) {
6075 0 : Element* current = Element::FromNode(nodeList->Item(i));
6076 6 : if (!current) {
6077 : continue;
6078 : }
6079 :
6080 : Element* matchedElm =
6081 : GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
6082 : if (matchedElm)
6083 : return matchedElm;
6084 4 : }
6085 :
6086 : return nullptr;
6087 : }
6088 12 :
6089 : Element*
6090 8 : nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
6091 : const nsAString& aAttrName,
6092 : const nsAString& aAttrValue)
6093 : {
6094 0 : RefPtr<nsAtom> attribute = NS_Atomize(aAttrName);
6095 :
6096 0 : return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
6097 : }
6098 :
6099 : nsINodeList*
6100 0 : nsIDocument::GetAnonymousNodes(Element& aElement)
6101 : {
6102 0 : return BindingManager()->GetAnonymousNodesFor(&aElement);
6103 0 : }
6104 0 :
6105 0 : already_AddRefed<nsRange>
6106 : nsIDocument::CreateRange(ErrorResult& rv)
6107 : {
6108 : RefPtr<nsRange> range = new nsRange(this);
6109 : nsresult res = range->CollapseTo(this, 0);
6110 : if (NS_FAILED(res)) {
6111 : rv.Throw(res);
6112 : return nullptr;
6113 0 : }
6114 :
6115 : return range.forget();
6116 : }
6117 :
6118 0 : already_AddRefed<NodeIterator>
6119 0 : nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6120 : NodeFilter* aFilter,
6121 : ErrorResult& rv) const
6122 : {
6123 0 : RefPtr<NodeIterator> iterator = new NodeIterator(&aRoot, aWhatToShow,
6124 : aFilter);
6125 : return iterator.forget();
6126 : }
6127 0 :
6128 0 : already_AddRefed<TreeWalker>
6129 : nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6130 : NodeFilter* aFilter,
6131 : ErrorResult& rv) const
6132 : {
6133 0 : RefPtr<TreeWalker> walker = new TreeWalker(&aRoot, aWhatToShow, aFilter);
6134 : return walker.forget();
6135 8 : }
6136 :
6137 2 :
6138 : already_AddRefed<Location>
6139 : nsIDocument::GetLocation() const
6140 : {
6141 0 : nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
6142 4 :
6143 2 : if (!w) {
6144 : return nullptr;
6145 : }
6146 :
6147 0 : nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(w);
6148 : RefPtr<Location> loc = window->GetLocation();
6149 65 : return loc.forget();
6150 65 : }
6151 :
6152 29 : Element*
6153 : nsIDocument::GetHtmlElement() const
6154 : {
6155 : Element* rootElement = GetRootElement();
6156 65 : if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html))
6157 : return rootElement;
6158 65 : return nullptr;
6159 0 : }
6160 :
6161 : Element*
6162 : nsIDocument::GetHtmlChildElement(nsAtom* aTag)
6163 : {
6164 97 : Element* html = GetHtmlElement();
6165 97 : if (!html)
6166 61 : return nullptr;
6167 97 :
6168 36 : // Look for the element with aTag inside html. This needs to run
6169 : // forwards to find the first such element.
6170 : for (nsIContent* child = html->GetFirstChild();
6171 : child;
6172 : child = child->GetNextSibling()) {
6173 : if (child->IsHTMLElement(aTag))
6174 0 : return child->AsElement();
6175 : }
6176 0 : return nullptr;
6177 0 : }
6178 :
6179 : nsGenericHTMLElement*
6180 : nsIDocument::GetBody()
6181 0 : {
6182 0 : Element* html = GetHtmlElement();
6183 0 : if (!html) {
6184 0 : return nullptr;
6185 0 : }
6186 :
6187 : for (nsIContent* child = html->GetFirstChild();
6188 : child;
6189 : child = child->GetNextSibling()) {
6190 : if (child->IsHTMLElement(nsGkAtoms::body) ||
6191 : child->IsHTMLElement(nsGkAtoms::frameset)) {
6192 : return static_cast<nsGenericHTMLElement*>(child);
6193 : }
6194 0 : }
6195 :
6196 0 : return nullptr;
6197 : }
6198 :
6199 : void
6200 0 : nsIDocument::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv)
6201 0 : {
6202 0 : nsCOMPtr<Element> root = GetRootElement();
6203 0 :
6204 0 : // The body element must be either a body tag or a frameset tag. And we must
6205 : // have a root element to be able to add kids to it.
6206 : if (!newBody ||
6207 : !newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset) ||
6208 0 : !root) {
6209 0 : rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
6210 0 : return;
6211 : }
6212 0 :
6213 : // Use DOM methods so that we pass through the appropriate security checks.
6214 : nsCOMPtr<Element> currentBody = GetBody();
6215 : if (currentBody) {
6216 : root->ReplaceChild(*newBody, *currentBody, rv);
6217 0 : } else {
6218 : root->AppendChild(*newBody, rv);
6219 0 : }
6220 : }
6221 :
6222 : HTMLSharedElement*
6223 0 : nsIDocument::GetHead()
6224 : {
6225 : return static_cast<HTMLSharedElement*>(GetHeadElement());
6226 : }
6227 :
6228 : Element*
6229 : nsIDocument::GetTitleElement()
6230 51 : {
6231 : // mMayHaveTitleElement will have been set to true if any HTML or SVG
6232 : // <title> element has been bound to this document. So if it's false,
6233 0 : // we know there is nothing to do here. This avoids us having to search
6234 0 : // the whole DOM if someone calls document.title on a large document
6235 : // without a title.
6236 0 : if (!mMayHaveTitleElement)
6237 0 : return nullptr;
6238 0 :
6239 0 : Element* root = GetRootElement();
6240 : if (root && root->IsSVGElement(nsGkAtoms::svg)) {
6241 : // In SVG, the document's title must be a child
6242 : for (nsIContent* child = root->GetFirstChild();
6243 : child; child = child->GetNextSibling()) {
6244 : if (child->IsSVGElement(nsGkAtoms::title)) {
6245 : return child->AsElement();
6246 : }
6247 : }
6248 : return nullptr;
6249 : }
6250 :
6251 : // We check the HTML namespace even for non-HTML documents, except SVG. This
6252 4 : // matches the spec and the behavior of all tested browsers.
6253 : // We avoid creating a live nsContentList since we don't need to watch for DOM
6254 2 : // tree mutations.
6255 : RefPtr<nsContentList> list = new nsContentList(this, kNameSpaceID_XHTML,
6256 2 : nsGkAtoms::title, nsGkAtoms::title,
6257 : /* aDeep = */ true,
6258 : /* aLiveList = */ false);
6259 :
6260 0 : nsIContent* first = list->Item(0, false);
6261 :
6262 52 : return first ? first->AsElement() : nullptr;
6263 : }
6264 52 :
6265 0 : void
6266 0 : nsIDocument::GetTitle(nsAString& aTitle)
6267 : {
6268 : aTitle.Truncate();
6269 0 :
6270 : Element* rootElement = GetRootElement();
6271 : if (!rootElement) {
6272 52 : return;
6273 1 : }
6274 :
6275 : nsAutoString tmp;
6276 :
6277 51 : #ifdef MOZ_XUL
6278 51 : if (rootElement->IsXULElement()) {
6279 : rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
6280 : } else
6281 2 : #endif
6282 : {
6283 : Element* title = GetTitleElement();
6284 3 : if (!title) {
6285 3 : return;
6286 : }
6287 : nsContentUtils::GetNodeTextContent(title, false, tmp);
6288 : }
6289 0 :
6290 : tmp.CompressWhitespace();
6291 0 : aTitle = tmp;
6292 0 : }
6293 0 :
6294 : void
6295 : nsIDocument::SetTitle(const nsAString& aTitle, ErrorResult& aRv)
6296 : {
6297 0 : Element* rootElement = GetRootElement();
6298 0 : if (!rootElement) {
6299 : return;
6300 : }
6301 :
6302 : #ifdef MOZ_XUL
6303 : if (rootElement->IsXULElement()) {
6304 : aRv = rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
6305 : aTitle, true);
6306 0 : return;
6307 : }
6308 0 : #endif
6309 0 :
6310 0 : // Batch updates so that mutation events don't change "the title
6311 : // element" under us
6312 0 : mozAutoDocUpdate updateBatch(this, true);
6313 :
6314 0 : nsCOMPtr<Element> title = GetTitleElement();
6315 0 : if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
6316 0 : if (!title) {
6317 0 : RefPtr<mozilla::dom::NodeInfo> titleInfo =
6318 0 : mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6319 : kNameSpaceID_SVG,
6320 0 : ELEMENT_NODE);
6321 : NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
6322 0 : NOT_FROM_PARSER);
6323 0 : if (!title) {
6324 0 : return;
6325 0 : }
6326 0 : rootElement->InsertChildBefore(title, rootElement->GetFirstChild(), true);
6327 : }
6328 : } else if (rootElement->IsHTMLElement()) {
6329 0 : if (!title) {
6330 0 : Element* head = GetHeadElement();
6331 0 : if (!head) {
6332 0 : return;
6333 0 : }
6334 0 :
6335 : RefPtr<mozilla::dom::NodeInfo> titleInfo;
6336 : titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6337 0 : kNameSpaceID_XHTML, ELEMENT_NODE);
6338 : title = NS_NewHTMLTitleElement(titleInfo.forget());
6339 : if (!title) {
6340 : return;
6341 : }
6342 :
6343 0 : head->AppendChildTo(title, true);
6344 : }
6345 : } else {
6346 : return;
6347 55 : }
6348 :
6349 0 : aRv = nsContentUtils::SetNodeTextContent(title, aTitle, false);
6350 : }
6351 55 :
6352 3 : void
6353 : nsIDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
6354 : {
6355 0 : NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
6356 2 : "Setting a title while unlinking or destroying the element?");
6357 : if (mInUnlinkOrDeletion) {
6358 110 : return;
6359 : }
6360 :
6361 52 : if (aBoundTitleElement) {
6362 : mMayHaveTitleElement = true;
6363 0 : }
6364 : if (mPendingTitleChangeEvent.IsPending())
6365 0 : return;
6366 0 :
6367 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
6368 52 : RefPtr<nsRunnableMethod<nsIDocument, void, false>> event =
6369 : NewNonOwningRunnableMethod("nsIDocument::DoNotifyPossibleTitleChange",
6370 : this,
6371 : &nsIDocument::DoNotifyPossibleTitleChange);
6372 : nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(event));
6373 0 : if (NS_SUCCEEDED(rv)) {
6374 : mPendingTitleChangeEvent = std::move(event);
6375 1 : }
6376 52 : }
6377 :
6378 104 : void
6379 0 : nsIDocument::DoNotifyPossibleTitleChange()
6380 : {
6381 0 : mPendingTitleChangeEvent.Forget();
6382 0 : mHaveFiredTitleChange = true;
6383 :
6384 57 : nsAutoString title;
6385 19 : GetTitle(title);
6386 0 :
6387 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
6388 0 : if (shell) {
6389 : nsCOMPtr<nsISupports> container =
6390 : shell->GetPresContext()->GetContainerWeak();
6391 : if (container) {
6392 : nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
6393 : if (docShellWin) {
6394 0 : docShellWin->SetTitle(title);
6395 52 : }
6396 156 : }
6397 52 : }
6398 :
6399 : // Fire a DOM event for the title change.
6400 0 : nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
6401 : NS_LITERAL_STRING("DOMTitleChanged"),
6402 2 : true, true);
6403 0 : }
6404 :
6405 : already_AddRefed<BoxObject>
6406 : nsIDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
6407 2 : {
6408 0 : if (!aElement) {
6409 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
6410 : return nullptr;
6411 : }
6412 :
6413 0 : nsIDocument* doc = aElement->OwnerDoc();
6414 0 : if (doc != this) {
6415 0 : aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
6416 0 : return nullptr;
6417 : }
6418 0 :
6419 : if (!mHasWarnedAboutBoxObjects && !aElement->IsXULElement()) {
6420 : mHasWarnedAboutBoxObjects = true;
6421 2 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
6422 0 : NS_LITERAL_CSTRING("BoxObjects"), this,
6423 : nsContentUtils::eDOM_PROPERTIES,
6424 : "UseOfGetBoxObjectForWarning");
6425 0 : }
6426 4 :
6427 2 : if (!mBoxObjectTable) {
6428 0 : mBoxObjectTable = new nsRefPtrHashtable<nsPtrHashKey<nsIContent>, BoxObject>(6);
6429 : }
6430 :
6431 : RefPtr<BoxObject> boxObject;
6432 : auto entry = mBoxObjectTable->LookupForAdd(aElement);
6433 0 : if (entry) {
6434 : boxObject = entry.Data();
6435 0 : return boxObject.forget();
6436 0 : }
6437 1 :
6438 0 : int32_t namespaceID;
6439 0 : RefPtr<nsAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
6440 2 : #ifdef MOZ_XUL
6441 0 : if (namespaceID == kNameSpaceID_XUL) {
6442 0 : if (tag == nsGkAtoms::menu) {
6443 0 : boxObject = new MenuBoxObject();
6444 : } else if (tag == nsGkAtoms::tree) {
6445 0 : boxObject = new TreeBoxObject();
6446 : } else if (tag == nsGkAtoms::listbox) {
6447 : boxObject = new ListBoxObject();
6448 : } else if (tag == nsGkAtoms::scrollbox) {
6449 : boxObject = new ScrollBoxObject();
6450 0 : } else {
6451 : boxObject = new BoxObject();
6452 : }
6453 0 : } else
6454 0 : #endif // MOZ_XUL
6455 : {
6456 2 : boxObject = new BoxObject();
6457 : }
6458 :
6459 : boxObject->Init(aElement);
6460 1 : entry.OrInsert([&boxObject]() { return boxObject; });
6461 :
6462 93 : return boxObject.forget();
6463 64 : }
6464 0 :
6465 0 : void
6466 0 : nsIDocument::ClearBoxObjectFor(nsIContent* aContent)
6467 : {
6468 : if (mBoxObjectTable) {
6469 93 : if (auto entry = mBoxObjectTable->Lookup(aContent)) {
6470 : nsPIBoxObject* boxObject = entry.Data();
6471 : boxObject->Clear();
6472 0 : entry.Remove();
6473 : }
6474 : }
6475 : }
6476 6 :
6477 : already_AddRefed<MediaQueryList>
6478 0 : nsIDocument::MatchMedia(const nsAString& aMediaQueryList,
6479 : CallerType aCallerType)
6480 6 : {
6481 : RefPtr<MediaQueryList> result =
6482 : new MediaQueryList(this, aMediaQueryList, aCallerType);
6483 :
6484 0 : mDOMMediaQueryLists.insertBack(result);
6485 :
6486 0 : return result.forget();
6487 0 : }
6488 :
6489 : void
6490 0 : nsIDocument::FlushSkinBindings()
6491 : {
6492 0 : BindingManager()->FlushSkinBindings();
6493 0 : }
6494 :
6495 : void
6496 : nsIDocument::SetMayStartLayout(bool aMayStartLayout)
6497 0 : {
6498 : mMayStartLayout = aMayStartLayout;
6499 0 : if (MayStartLayout()) {
6500 : // Before starting layout, check whether we're a toplevel chrome
6501 49 : // window. If we are, setup some state so that we don't have to restyle
6502 0 : // the whole tree after StartLayout.
6503 : if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
6504 20 : // We're the chrome document!
6505 : win->BeforeStartLayout();
6506 : }
6507 0 : ReadyState state = GetReadyStateEnum();
6508 : if (state >= READYSTATE_INTERACTIVE) {
6509 : // DOMContentLoaded has fired already.
6510 0 : MaybeResolveReadyForIdle();
6511 : }
6512 5 : }
6513 : }
6514 5 :
6515 : nsresult
6516 0 : nsIDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
6517 0 : {
6518 : mInitializableFrameLoaders.RemoveElement(aLoader);
6519 : // Don't even try to initialize.
6520 0 : if (mInDestructor) {
6521 0 : NS_WARNING("Trying to initialize a frame loader while"
6522 : "document is being deleted");
6523 0 : return NS_ERROR_FAILURE;
6524 : }
6525 5 :
6526 0 : mInitializableFrameLoaders.AppendElement(aLoader);
6527 10 : if (!mFrameLoaderRunner) {
6528 : mFrameLoaderRunner =
6529 : NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders",
6530 : this,
6531 : &nsIDocument::MaybeInitializeFinalizeFrameLoaders);
6532 : NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6533 0 : nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6534 : }
6535 0 : return NS_OK;
6536 1 : }
6537 :
6538 : nsresult
6539 : nsIDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer)
6540 1 : {
6541 0 : mInitializableFrameLoaders.RemoveElement(aLoader);
6542 : if (mInDestructor) {
6543 0 : return NS_ERROR_FAILURE;
6544 : }
6545 1 :
6546 2 : mFrameLoaderFinalizers.AppendElement(aFinalizer);
6547 0 : if (!mFrameLoaderRunner) {
6548 : mFrameLoaderRunner =
6549 : NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders",
6550 : this,
6551 : &nsIDocument::MaybeInitializeFinalizeFrameLoaders);
6552 : NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6553 703 : nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6554 : }
6555 703 : return NS_OK;
6556 : }
6557 :
6558 0 : void
6559 0 : nsIDocument::MaybeInitializeFinalizeFrameLoaders()
6560 : {
6561 : if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
6562 : // This method will be recalled when mUpdateNestLevel drops to 0,
6563 : // or when !mDelayFrameLoaderInitialization.
6564 0 : mFrameLoaderRunner = nullptr;
6565 356 : return;
6566 267 : }
6567 0 :
6568 : // We're not in an update, but it is not safe to run scripts, so
6569 0 : // postpone frameloader initialization and finalization.
6570 : if (!nsContentUtils::IsSafeToRunScript()) {
6571 0 : if (!mInDestructor && !mFrameLoaderRunner &&
6572 0 : (mInitializableFrameLoaders.Length() ||
6573 : mFrameLoaderFinalizers.Length())) {
6574 : mFrameLoaderRunner =
6575 : NewRunnableMethod("nsIDocument::MaybeInitializeFinalizeFrameLoaders",
6576 248 : this,
6577 : &nsIDocument::MaybeInitializeFinalizeFrameLoaders);
6578 : nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6579 : }
6580 : return;
6581 511 : }
6582 0 : mFrameLoaderRunner = nullptr;
6583 0 :
6584 0 : // Don't use a temporary array for mInitializableFrameLoaders, because
6585 5 : // loading a frame may cause some other frameloader to be removed from the
6586 : // array. But be careful to keep the loader alive when starting the load!
6587 : while (mInitializableFrameLoaders.Length()) {
6588 496 : RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
6589 248 : mInitializableFrameLoaders.RemoveElementAt(0);
6590 0 : NS_ASSERTION(loader, "null frameloader in the array?");
6591 1 : loader->ReallyStartLoading();
6592 0 : }
6593 0 :
6594 : uint32_t length = mFrameLoaderFinalizers.Length();
6595 : if (length > 0) {
6596 : nsTArray<nsCOMPtr<nsIRunnable> > finalizers;
6597 : mFrameLoaderFinalizers.SwapElements(finalizers);
6598 : for (uint32_t i = 0; i < length; ++i) {
6599 6 : finalizers[i]->Run();
6600 : }
6601 12 : }
6602 0 : }
6603 0 :
6604 0 : void
6605 0 : nsIDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
6606 : {
6607 : uint32_t length = mInitializableFrameLoaders.Length();
6608 : for (uint32_t i = 0; i < length; ++i) {
6609 : if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
6610 : mInitializableFrameLoaders.RemoveElementAt(i);
6611 0 : return;
6612 : }
6613 : }
6614 : }
6615 0 :
6616 0 : nsIDocument*
6617 0 : nsIDocument::RequestExternalResource(nsIURI* aURI,
6618 0 : nsINode* aRequestingNode,
6619 : ExternalResourceLoad** aPendingLoad)
6620 0 : {
6621 : MOZ_ASSERT(aURI, "Must have a URI");
6622 : MOZ_ASSERT(aRequestingNode, "Must have a node");
6623 0 : if (mDisplayDocument) {
6624 0 : return mDisplayDocument->RequestExternalResource(aURI,
6625 : aRequestingNode,
6626 : aPendingLoad);
6627 : }
6628 1 :
6629 : return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
6630 160 : this, aPendingLoad);
6631 53 : }
6632 :
6633 : void
6634 168 : nsIDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
6635 : {
6636 : mExternalResourceMap.EnumerateResources(aCallback, aData);
6637 : }
6638 0 :
6639 : nsSMILAnimationController*
6640 : nsIDocument::GetAnimationController()
6641 0 : {
6642 : // We create the animation controller lazily because most documents won't want
6643 : // one and only SVG documents and the like will call this
6644 26 : if (mAnimationController)
6645 : return mAnimationController;
6646 : // Refuse to create an Animation Controller for data documents.
6647 : if (mLoadedAsData || mLoadedAsInteractiveData)
6648 0 : return nullptr;
6649 0 :
6650 0 : mAnimationController = new nsSMILAnimationController(this);
6651 0 :
6652 : // If there's a presContext then check the animation mode and pause if
6653 : // necessary.
6654 : nsPresContext* context = GetPresContext();
6655 : if (mAnimationController && context &&
6656 : context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
6657 13 : mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
6658 0 : }
6659 :
6660 : // If we're hidden (or being hidden), notify the newly-created animation
6661 0 : // controller. (Skip this check for SVG-as-an-image documents, though,
6662 : // because they don't get OnPageShow / OnPageHide calls).
6663 : if (!mIsShowing && !mIsBeingUsedAsImage) {
6664 : mAnimationController->OnPageHide();
6665 2 : }
6666 :
6667 4 : return mAnimationController;
6668 0 : }
6669 :
6670 : PendingAnimationTracker*
6671 4 : nsIDocument::GetOrCreatePendingAnimationTracker()
6672 : {
6673 : if (!mPendingAnimationTracker) {
6674 : mPendingAnimationTracker = new PendingAnimationTracker(this);
6675 : }
6676 :
6677 : return mPendingAnimationTracker;
6678 : }
6679 :
6680 0 : /**
6681 : * Retrieve the "direction" property of the document.
6682 0 : *
6683 0 : * @lina 01/09/2001
6684 0 : */
6685 0 : void
6686 : nsIDocument::GetDir(nsAString& aDirection) const
6687 0 : {
6688 : aDirection.Truncate();
6689 : Element* rootElement = GetHtmlElement();
6690 : if (rootElement) {
6691 : static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
6692 : }
6693 : }
6694 :
6695 0 : /**
6696 : * Set the "direction" property of the document.
6697 0 : *
6698 0 : * @lina 01/09/2001
6699 0 : */
6700 0 : void
6701 : nsIDocument::SetDir(const nsAString& aDirection)
6702 0 : {
6703 : Element* rootElement = GetHtmlElement();
6704 : if (rootElement) {
6705 0 : rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
6706 : aDirection, true);
6707 0 : }
6708 0 : }
6709 :
6710 0 : nsIHTMLCollection*
6711 : nsIDocument::Images()
6712 : {
6713 : if (!mImages) {
6714 0 : mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img, nsGkAtoms::img);
6715 : }
6716 0 : return mImages;
6717 0 : }
6718 :
6719 0 : nsIHTMLCollection*
6720 : nsIDocument::Embeds()
6721 : {
6722 : if (!mEmbeds) {
6723 0 : mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed, nsGkAtoms::embed);
6724 : }
6725 : return mEmbeds;
6726 0 : }
6727 0 :
6728 : static bool
6729 : MatchLinks(Element* aElement, int32_t aNamespaceID,
6730 : nsAtom* aAtom, void* aData)
6731 0 : {
6732 : return aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
6733 0 : aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
6734 0 : }
6735 :
6736 0 : nsIHTMLCollection*
6737 : nsIDocument::Links()
6738 : {
6739 : if (!mLinks) {
6740 0 : mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr);
6741 : }
6742 0 : return mLinks;
6743 : }
6744 0 :
6745 : nsIHTMLCollection*
6746 : nsIDocument::Forms()
6747 0 : {
6748 : if (!mForms) {
6749 : // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
6750 : mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form, nsGkAtoms::form);
6751 0 : }
6752 :
6753 0 : return mForms;
6754 0 : }
6755 :
6756 0 : nsIHTMLCollection*
6757 : nsIDocument::Scripts()
6758 : {
6759 : if (!mScripts) {
6760 0 : mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script, nsGkAtoms::script);
6761 : }
6762 0 : return mScripts;
6763 0 : }
6764 :
6765 0 : nsIHTMLCollection*
6766 : nsIDocument::Applets()
6767 : {
6768 : if (!mApplets) {
6769 0 : mApplets = new nsEmptyContentList(this);
6770 : }
6771 : return mApplets;
6772 0 : }
6773 0 :
6774 : static bool
6775 : MatchAnchors(Element* aElement, int32_t aNamespaceID,
6776 : nsAtom* aAtom, void* aData)
6777 0 : {
6778 : return aElement->IsHTMLElement(nsGkAtoms::a) &&
6779 0 : aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
6780 0 : }
6781 :
6782 0 : nsIHTMLCollection*
6783 : nsIDocument::Anchors()
6784 : {
6785 : if (!mAnchors) {
6786 : mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
6787 0 : }
6788 : return mAnchors;
6789 : }
6790 0 :
6791 : /* static */
6792 0 : bool
6793 : nsIDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
6794 : nsAtom* aAtom, void* aData)
6795 : {
6796 0 : MOZ_ASSERT(aElement, "Must have element to work with!");
6797 :
6798 0 : if (!aElement->HasName()) {
6799 0 : return false;
6800 : }
6801 :
6802 : nsString* elementName = static_cast<nsString*>(aData);
6803 : return
6804 : aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
6805 0 : aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
6806 : *elementName, eCaseMatters);
6807 0 : }
6808 :
6809 : /* static */
6810 : void*
6811 0 : nsIDocument::UseExistingNameString(nsINode* aRootNode, const nsString* aName)
6812 : {
6813 0 : return const_cast<nsString*>(aName);
6814 36 : }
6815 18 :
6816 18 : nsresult
6817 : nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
6818 18 : {
6819 : if (mDocumentURI) {
6820 0 : nsAutoCString uri;
6821 : nsresult rv = mDocumentURI->GetSpec(uri);
6822 : NS_ENSURE_SUCCESS(rv, rv);
6823 :
6824 : CopyUTF8toUTF16(uri, aDocumentURI);
6825 : } else {
6826 : aDocumentURI.Truncate();
6827 : }
6828 0 :
6829 : return NS_OK;
6830 0 : }
6831 :
6832 : // Alias of above
6833 : nsresult
6834 0 : nsIDocument::GetURL(nsString& aURL) const
6835 : {
6836 : return GetDocumentURI(aURL);
6837 0 : }
6838 18 :
6839 0 : void
6840 : nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI, CallerType aCallerType,
6841 : ErrorResult& aRv) const
6842 0 : {
6843 0 : if (!mChromeXHRDocURI || aCallerType != CallerType::System) {
6844 0 : aRv = GetDocumentURI(aDocumentURI);
6845 0 : return;
6846 : }
6847 :
6848 0 : nsAutoCString uri;
6849 : nsresult res = mChromeXHRDocURI->GetSpec(uri);
6850 : if (NS_FAILED(res)) {
6851 : aRv.Throw(res);
6852 4 : return;
6853 : }
6854 8 : CopyUTF8toUTF16(uri, aDocumentURI);
6855 0 : }
6856 :
6857 : nsIURI*
6858 : nsIDocument::GetDocumentURIObject() const
6859 : {
6860 : if (!mChromeXHRDocURI) {
6861 : return GetDocumentURI();
6862 0 : }
6863 :
6864 0 : return mChromeXHRDocURI;
6865 : }
6866 :
6867 : void
6868 : nsIDocument::GetCompatMode(nsString& aCompatMode) const
6869 0 : {
6870 0 : NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
6871 : mCompatMode == eCompatibility_AlmostStandards ||
6872 0 : mCompatMode == eCompatibility_FullStandards,
6873 : "mCompatMode is neither quirks nor strict for this document");
6874 0 :
6875 : if (mCompatMode == eCompatibility_NavQuirks) {
6876 : aCompatMode.AssignLiteral("BackCompat");
6877 0 : } else {
6878 : aCompatMode.AssignLiteral("CSS1Compat");
6879 0 : }
6880 0 : }
6881 :
6882 0 : void
6883 : nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode)
6884 : {
6885 : if (Element* element = Element::FromNode(aNode)) {
6886 : if (const nsDOMAttributeMap* map = element->GetAttributeMap()) {
6887 : while (true) {
6888 0 : nsCOMPtr<nsIAttribute> attr;
6889 0 : {
6890 : // Use an iterator to get an arbitrary attribute from the
6891 : // cache. The iterator must be destroyed before any other
6892 0 : // operations on mAttributeCache, to avoid hash table
6893 : // assertions.
6894 0 : auto iter = map->mAttributeCache.ConstIter();
6895 : if (iter.Done()) {
6896 : break;
6897 0 : }
6898 : attr = iter.UserData();
6899 : }
6900 0 : NS_ASSERTION(attr.get(),
6901 : "non-nsIAttribute somehow made it into the hashmap?!");
6902 0 :
6903 : BlastSubtreeToPieces(attr);
6904 :
6905 0 : DebugOnly<nsresult> rv =
6906 : element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
6907 : attr->NodeInfo()->NameAtom(),
6908 : false);
6909 :
6910 0 : // XXX Should we abort here?
6911 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
6912 0 : }
6913 0 : }
6914 : }
6915 0 :
6916 : while (aNode->HasChildren()) {
6917 : nsIContent* node = aNode->GetFirstChild();
6918 0 : BlastSubtreeToPieces(node);
6919 : aNode->RemoveChildNode(node, false);
6920 0 : }
6921 : }
6922 :
6923 : nsINode*
6924 : nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
6925 0 : {
6926 0 : nsINode* adoptedNode = &aAdoptedNode;
6927 0 :
6928 : // Scope firing mutation events so that we don't carry any state that
6929 : // might be stale
6930 : {
6931 0 : nsINode* parent = adoptedNode->GetParentNode();
6932 : if (parent) {
6933 0 : nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent);
6934 : }
6935 : }
6936 :
6937 0 : nsAutoScriptBlocker scriptBlocker;
6938 :
6939 0 : switch (adoptedNode->NodeType()) {
6940 0 : case ATTRIBUTE_NODE:
6941 0 : {
6942 : // Remove from ownerElement.
6943 : RefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
6944 0 :
6945 : nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
6946 0 : if (rv.Failed()) {
6947 0 : return nullptr;
6948 0 : }
6949 :
6950 : if (ownerElement) {
6951 0 : RefPtr<Attr> newAttr =
6952 : ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
6953 : if (rv.Failed()) {
6954 0 : return nullptr;
6955 : }
6956 :
6957 : newAttr.swap(adoptedAttr);
6958 0 : }
6959 0 :
6960 0 : break;
6961 : }
6962 : case DOCUMENT_FRAGMENT_NODE:
6963 : {
6964 : if (adoptedNode->IsShadowRoot()) {
6965 : rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
6966 : return nullptr;
6967 : }
6968 : MOZ_FALLTHROUGH;
6969 : }
6970 : case ELEMENT_NODE:
6971 : case PROCESSING_INSTRUCTION_NODE:
6972 0 : case TEXT_NODE:
6973 0 : case CDATA_SECTION_NODE:
6974 0 : case COMMENT_NODE:
6975 : case DOCUMENT_TYPE_NODE:
6976 : {
6977 : // Don't allow adopting a node's anonymous subtree out from under it.
6978 : if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
6979 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6980 : return nullptr;
6981 : }
6982 0 :
6983 0 : // We don't want to adopt an element into its own contentDocument or into
6984 0 : // a descendant contentDocument, so we check if the frameElement of this
6985 0 : // document or any of its parents is the adopted node or one of its
6986 0 : // descendants.
6987 0 : nsIDocument *doc = this;
6988 0 : do {
6989 : if (nsPIDOMWindowOuter *win = doc->GetWindow()) {
6990 : nsCOMPtr<nsINode> node = win->GetFrameElementInternal();
6991 : if (node &&
6992 : nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
6993 : rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
6994 0 : return nullptr;
6995 0 : }
6996 0 : }
6997 : } while ((doc = doc->GetParentDocument()));
6998 0 :
6999 : // Remove from parent.
7000 : nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
7001 : if (parent) {
7002 : parent->RemoveChildNode(adoptedNode->AsContent(), true);
7003 : } else {
7004 0 : MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc());
7005 0 :
7006 : // If we're adopting a node that's not in a document, it might still
7007 : // have a binding applied. Remove the binding from the element now
7008 : // that it's getting adopted into a new document.
7009 : // TODO Fully tear down the binding.
7010 : if (Element* element = Element::FromNode(adoptedNode)) {
7011 : element->SetXBLBinding(nullptr);
7012 : }
7013 0 : }
7014 0 :
7015 : break;
7016 : }
7017 : case DOCUMENT_NODE:
7018 0 : {
7019 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7020 0 : return nullptr;
7021 0 : }
7022 : default:
7023 : {
7024 : NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
7025 0 :
7026 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7027 : return nullptr;
7028 0 : }
7029 0 : }
7030 0 :
7031 0 : nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
7032 0 : bool sameDocument = oldDocument == this;
7033 :
7034 : AutoJSContext cx;
7035 : JS::Rooted<JSObject*> newScope(cx, nullptr);
7036 : if (!sameDocument) {
7037 0 : newScope = GetWrapper();
7038 0 : if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
7039 0 : // Make sure cx is in a semi-sane compartment before we call WrapNative.
7040 0 : // It's kind of irrelevant, given that we're passing aAllowWrapping =
7041 0 : // false, and documents should always insist on being wrapped in an
7042 0 : // canonical scope. But we try to pass something sane anyway.
7043 0 : JSAutoRealm ar(cx, GetScopeObject()->GetGlobalJSObject());
7044 : JS::Rooted<JS::Value> v(cx);
7045 : rv = nsContentUtils::WrapNative(cx, this, this, &v,
7046 : /* aAllowWrapping = */ false);
7047 0 : if (rv.Failed())
7048 0 : return nullptr;
7049 0 : newScope = &v.toObject();
7050 0 : }
7051 : }
7052 :
7053 0 : nsCOMArray<nsINode> nodesWithProperties;
7054 : nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
7055 0 : newScope, nodesWithProperties, rv);
7056 0 : if (rv.Failed()) {
7057 : // Disconnect all nodes from their parents, since some have the old document
7058 0 : // as their ownerDocument and some have this as their ownerDocument.
7059 : nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
7060 :
7061 : if (!sameDocument && oldDocument) {
7062 : for (nsINode* node : nodesWithProperties) {
7063 : // Remove all properties.
7064 : oldDocument->PropertyTable().DeleteAllPropertiesFor(node);
7065 0 : }
7066 0 : }
7067 0 :
7068 0 : return nullptr;
7069 0 : }
7070 :
7071 : if (!sameDocument && oldDocument) {
7072 0 : nsPropertyTable& oldTable = oldDocument->PropertyTable();
7073 : nsPropertyTable& newTable = PropertyTable();
7074 0 : for (nsINode* node : nodesWithProperties) {
7075 : rv = oldTable.TransferOrDeleteAllPropertiesFor(node, newTable);
7076 0 : }
7077 :
7078 : if (rv.Failed()) {
7079 : // Disconnect all nodes from their parents.
7080 0 : nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
7081 :
7082 : return nullptr;
7083 : }
7084 : }
7085 :
7086 : NS_ASSERTION(adoptedNode->OwnerDoc() == this,
7087 0 : "Should still be in the document we just got adopted into");
7088 :
7089 0 : return adoptedNode;
7090 : }
7091 :
7092 : nsViewportInfo
7093 0 : nsIDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
7094 : {
7095 : MOZ_ASSERT(mPresShell);
7096 :
7097 : // Compute the CSS-to-LayoutDevice pixel scale as the product of the
7098 0 : // widget scale and the full zoom.
7099 0 : nsPresContext* context = mPresShell->GetPresContext();
7100 0 : // When querying the full zoom, get it from the device context rather than
7101 : // directly from the pres context, because the device context's value can
7102 : // include an adjustment necessay to keep the number of app units per device
7103 0 : // pixel an integer, and we want the adjusted value.
7104 : float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
7105 : fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
7106 0 : CSSToLayoutDeviceScale layoutDeviceScale = context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
7107 0 :
7108 0 : CSSToScreenScale defaultScale = layoutDeviceScale
7109 0 : * LayoutDeviceToScreenScale(1.0);
7110 0 :
7111 0 : // Special behaviour for desktop mode, provided we are not on an about: page
7112 0 : nsPIDOMWindowOuter* win = GetWindow();
7113 : if (win && win->IsDesktopModeViewport() && !IsAboutPage()) {
7114 : CSSCoord viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
7115 0 : CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth);
7116 : float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
7117 : CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
7118 0 : ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
7119 : return nsViewportInfo(fakeDesktopSize,
7120 : scaleToFit,
7121 0 : /*allowZoom*/ true);
7122 : }
7123 :
7124 : if (!gfxPrefs::MetaViewportEnabled()) {
7125 : return nsViewportInfo(aDisplaySize,
7126 : defaultScale,
7127 : /*allowZoom*/ false);
7128 0 : }
7129 :
7130 : // In cases where the width of the CSS viewport is less than or equal to the width
7131 : // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
7132 0 : // behaviour. See bug 941995 for details.
7133 :
7134 : switch (mViewportType) {
7135 0 : case DisplayWidthHeight:
7136 0 : return nsViewportInfo(aDisplaySize,
7137 0 : defaultScale,
7138 : /*allowZoom*/ true);
7139 : case Unknown:
7140 0 : {
7141 0 : nsAutoString viewport;
7142 0 : GetHeaderData(nsGkAtoms::viewport, viewport);
7143 0 : if (viewport.IsEmpty()) {
7144 0 : // If the docType specifies that we are on a site optimized for mobile,
7145 0 : // then we want to return specially crafted defaults for the viewport info.
7146 0 : RefPtr<DocumentType> docType = GetDoctype();
7147 : if (docType) {
7148 : nsAutoString docId;
7149 0 : docType->GetPublicId(docId);
7150 : if ((docId.Find("WAP") != -1) ||
7151 : (docId.Find("Mobile") != -1) ||
7152 0 : (docId.Find("WML") != -1))
7153 : {
7154 : // We're making an assumption that the docType can't change here
7155 : mViewportType = DisplayWidthHeight;
7156 0 : return nsViewportInfo(aDisplaySize,
7157 0 : defaultScale,
7158 0 : /*allowZoom*/true);
7159 0 : }
7160 : }
7161 0 :
7162 : nsAutoString handheldFriendly;
7163 : GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
7164 : if (handheldFriendly.EqualsLiteral("true")) {
7165 0 : mViewportType = DisplayWidthHeight;
7166 0 : return nsViewportInfo(aDisplaySize, defaultScale,
7167 : /*allowZoom*/true);
7168 : }
7169 0 : }
7170 :
7171 0 : nsAutoString minScaleStr;
7172 0 : GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
7173 :
7174 : nsresult errorCode;
7175 0 : mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
7176 0 :
7177 : if (NS_FAILED(errorCode)) {
7178 0 : mScaleMinFloat = kViewportMinScale;
7179 0 : }
7180 :
7181 : mScaleMinFloat = mozilla::clamped(
7182 : mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
7183 :
7184 0 : nsAutoString maxScaleStr;
7185 : GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
7186 0 :
7187 0 : // We define a special error code variable for the scale and max scale,
7188 : // because they are used later (see the width calculations).
7189 : nsresult scaleMaxErrorCode;
7190 0 : mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
7191 0 :
7192 : if (NS_FAILED(scaleMaxErrorCode)) {
7193 0 : mScaleMaxFloat = kViewportMaxScale;
7194 0 : }
7195 :
7196 : mScaleMaxFloat = mozilla::clamped(
7197 0 : mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
7198 :
7199 0 : nsAutoString scaleStr;
7200 : GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
7201 0 :
7202 0 : nsresult scaleErrorCode;
7203 : mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
7204 0 :
7205 : nsAutoString widthStr, heightStr;
7206 0 :
7207 0 : GetHeaderData(nsGkAtoms::viewport_height, heightStr);
7208 : GetHeaderData(nsGkAtoms::viewport_width, widthStr);
7209 :
7210 0 : mAutoSize = false;
7211 0 :
7212 0 : if (widthStr.EqualsLiteral("device-width")) {
7213 : mAutoSize = true;
7214 0 : }
7215 :
7216 : if (widthStr.IsEmpty() &&
7217 : (heightStr.EqualsLiteral("device-height") ||
7218 0 : (mScaleFloat.scale == 1.0)))
7219 0 : {
7220 : mAutoSize = true;
7221 : }
7222 :
7223 0 : nsresult widthErrorCode, heightErrorCode;
7224 0 : mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
7225 : mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
7226 0 :
7227 0 : // If width or height has not been set to a valid number by this point,
7228 0 : // fall back to a default value.
7229 : mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
7230 0 : mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
7231 0 :
7232 0 : mAllowZoom = true;
7233 0 : nsAutoString userScalable;
7234 : GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
7235 :
7236 0 : if ((userScalable.EqualsLiteral("0")) ||
7237 0 : (userScalable.EqualsLiteral("no")) ||
7238 0 : (userScalable.EqualsLiteral("false"))) {
7239 0 : mAllowZoom = false;
7240 : }
7241 0 :
7242 0 : mScaleStrEmpty = scaleStr.IsEmpty();
7243 : mWidthStrEmpty = widthStr.IsEmpty();
7244 : mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
7245 : mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
7246 :
7247 0 : mViewportType = Specified;
7248 0 : mViewportOverflowType = ViewportOverflowType::NoOverflow;
7249 0 : MOZ_FALLTHROUGH;
7250 0 : }
7251 0 : case Specified:
7252 : default:
7253 : LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
7254 : LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
7255 : bool effectiveValidMaxScale = mValidMaxScale;
7256 : bool effectiveAllowZoom = mAllowZoom;
7257 : if (gfxPrefs::ForceUserScalable()) {
7258 : // If the pref to force user-scalable is enabled, we ignore the values
7259 0 : // from the meta-viewport tag for these properties and just assume they
7260 0 : // allow the page to be scalable. Note in particular that this code is
7261 0 : // in the "Specified" branch of the enclosing switch statement, so that
7262 0 : // calls to GetViewportInfo always use the latest value of the
7263 : // ForceUserScalable pref. Other codepaths that return nsViewportInfo
7264 : // instances are all consistent with ForceUserScalable() already.
7265 0 : effectiveMinScale = kViewportMinScale;
7266 : effectiveMaxScale = kViewportMaxScale;
7267 0 : effectiveValidMaxScale = true;
7268 0 : effectiveAllowZoom = true;
7269 0 : }
7270 :
7271 : CSSSize size = mViewportSize;
7272 :
7273 : if (!mValidWidth) {
7274 0 : if (mValidHeight && !aDisplaySize.IsEmpty()) {
7275 : size.width = size.height * aDisplaySize.width / aDisplaySize.height;
7276 : } else {
7277 : // Stretch CSS pixel size of viewport to keep device pixel size
7278 0 : // unchanged after full zoom applied.
7279 0 : // See bug 974242.
7280 0 : size.width = gfxPrefs::DesktopViewportWidth() / fullZoom;
7281 : }
7282 0 : }
7283 :
7284 : if (!mValidHeight) {
7285 : if (!aDisplaySize.IsEmpty()) {
7286 0 : size.height = size.width * aDisplaySize.height / aDisplaySize.width;
7287 0 : } else {
7288 0 : size.height = size.width;
7289 : }
7290 0 : }
7291 :
7292 0 : CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
7293 0 : CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
7294 : CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
7295 :
7296 0 : if (mAutoSize) {
7297 : // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
7298 : CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f);
7299 : size = ScreenSize(aDisplaySize) / defaultPixelScale;
7300 0 : }
7301 0 :
7302 0 : size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width));
7303 :
7304 : // Also recalculate the default zoom, if it wasn't specified in the metadata,
7305 0 : // and the width is specified.
7306 : if (mScaleStrEmpty && !mWidthStrEmpty) {
7307 : CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width);
7308 : scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
7309 0 : }
7310 0 :
7311 0 : size.height = clamped(size.height, float(kViewportMinSize.height), float(kViewportMaxSize.height));
7312 0 :
7313 0 : // We need to perform a conversion, but only if the initial or maximum
7314 0 : // scale were set explicitly by the user.
7315 0 : if (mValidScaleFloat && scaleFloat >= scaleMinFloat && scaleFloat <= scaleMaxFloat) {
7316 0 : CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
7317 : size.width = std::max(size.width, displaySize.width);
7318 : size.height = std::max(size.height, displaySize.height);
7319 : } else if (effectiveValidMaxScale) {
7320 0 : CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
7321 : size.width = std::max(size.width, displaySize.width);
7322 : size.height = std::max(size.height, displaySize.height);
7323 : }
7324 :
7325 0 : return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
7326 : mAutoSize, effectiveAllowZoom);
7327 : }
7328 : }
7329 0 :
7330 0 : void
7331 0 : nsIDocument::UpdateViewportOverflowType(nscoord aScrolledWidth,
7332 : nscoord aScrollportWidth)
7333 : {
7334 0 : #ifdef DEBUG
7335 : MOZ_ASSERT(mPresShell);
7336 0 : nsPresContext* pc = GetPresContext();
7337 : MOZ_ASSERT(pc->GetViewportScrollbarStylesOverride().mHorizontal ==
7338 : NS_STYLE_OVERFLOW_HIDDEN,
7339 : "Should only be called when viewport has overflow-x: hidden");
7340 0 : MOZ_ASSERT(aScrolledWidth > aScrollportWidth,
7341 0 : "Should only be called when viewport is overflowed");
7342 0 : MOZ_ASSERT(IsTopLevelContentDocument(),
7343 0 : "Should only be called for top-level content document");
7344 : #endif // DEBUG
7345 :
7346 0 : if (!gfxPrefs::MetaViewportEnabled() ||
7347 : (GetWindow() && GetWindow()->IsDesktopModeViewport())) {
7348 : mViewportOverflowType = ViewportOverflowType::Desktop;
7349 : return;
7350 : }
7351 :
7352 : if (mViewportType == Unknown) {
7353 : // The viewport info hasn't been initialized yet. Suppose we would
7354 0 : // get here again at some point after it's initialized.
7355 0 : return;
7356 : }
7357 :
7358 0 : static const LayoutDeviceToScreenScale
7359 : kBlinkDefaultMinScale = LayoutDeviceToScreenScale(0.25f);
7360 : LayoutDeviceToScreenScale minScale;
7361 0 : if (mViewportType == DisplayWidthHeight) {
7362 : minScale = kBlinkDefaultMinScale;
7363 : } else {
7364 : if (mScaleMinFloat == kViewportMinScale) {
7365 : minScale = kBlinkDefaultMinScale;
7366 : } else {
7367 0 : minScale = mScaleMinFloat;
7368 0 : }
7369 0 : }
7370 :
7371 0 : // If the content has overflowed with minimum scale applied, don't
7372 : // change it, otherwise update the overflow type.
7373 : if (mViewportOverflowType != ViewportOverflowType::MinScaleSize) {
7374 : if (aScrolledWidth * minScale.scale < aScrollportWidth) {
7375 : mViewportOverflowType = ViewportOverflowType::ButNotMinScaleSize;
7376 : } else {
7377 133 : mViewportOverflowType = ViewportOverflowType::MinScaleSize;
7378 : }
7379 266 : }
7380 : }
7381 0 :
7382 28 : EventListenerManager*
7383 : nsDocument::GetOrCreateListenerManager()
7384 : {
7385 0 : if (!mListenerManager) {
7386 : mListenerManager =
7387 : new EventListenerManager(static_cast<EventTarget*>(this));
7388 : SetFlags(NODE_HAS_LISTENERMANAGER);
7389 0 : }
7390 :
7391 1324 : return mListenerManager;
7392 : }
7393 :
7394 : EventListenerManager*
7395 0 : nsDocument::GetExistingListenerManager() const
7396 : {
7397 955 : return mListenerManager;
7398 143 : }
7399 0 :
7400 : void
7401 : nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor)
7402 0 : {
7403 : if (mDocGroup && aVisitor.mEvent->mMessage != eVoidEvent &&
7404 : !mIgnoreDocGroupMismatches) {
7405 406 : mDocGroup->ValidateAccess();
7406 : }
7407 :
7408 406 : aVisitor.mCanHandle = true;
7409 0 : // FIXME! This is a hack to make middle mouse paste working also in Editor.
7410 336 : // Bug 329119
7411 : aVisitor.mForceContentDispatch = true;
7412 :
7413 0 : // Load events must not propagate to |window| object, see bug 335251.
7414 : if (aVisitor.mEvent->mMessage != eLoad) {
7415 : nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetWindow());
7416 203 : aVisitor.SetParentTarget(
7417 : window ? window->GetTargetForEventTargetChain() : nullptr, false);
7418 : }
7419 203 : }
7420 :
7421 : already_AddRefed<Event>
7422 : nsIDocument::CreateEvent(const nsAString& aEventType, CallerType aCallerType,
7423 0 : ErrorResult& rv) const
7424 406 : {
7425 203 : nsPresContext* presContext = GetPresContext();
7426 0 :
7427 : // Create event even without presContext.
7428 : RefPtr<Event> ev =
7429 0 : EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), presContext,
7430 0 : nullptr, aEventType, aCallerType);
7431 203 : if (!ev) {
7432 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7433 : return nullptr;
7434 : }
7435 : WidgetEvent* e = ev->WidgetEventPtr();
7436 498 : e->mFlags.mBubbles = false;
7437 : e->mFlags.mCancelable = false;
7438 996 : return ev.forget();
7439 498 : }
7440 0 :
7441 : void
7442 : nsIDocument::FlushPendingNotifications(FlushType aType)
7443 0 : {
7444 : mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
7445 503 : FlushPendingNotifications(flush);
7446 : }
7447 0 :
7448 : void
7449 : nsIDocument::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
7450 : {
7451 : FlushType flushType = aFlush.mFlushType;
7452 :
7453 : nsDocumentOnStack dos(this);
7454 :
7455 1043 : // We need to flush the sink for non-HTML documents (because the XML
7456 12 : // parser still does insertion with deferred notifications). We
7457 981 : // also need to flush the sink if this is a layout-related flush, to
7458 1872 : // make sure that layout is started as needed. But we can skip that
7459 0 : // part if we have no presshell or if it's already done an initial
7460 0 : // reflow.
7461 0 : if ((!IsHTMLDocument() ||
7462 : (flushType > FlushType::ContentAndNotify && mPresShell &&
7463 0 : !mPresShell->DidInitialize())) &&
7464 0 : (mParser || mWeakSink)) {
7465 0 : nsCOMPtr<nsIContentSink> sink;
7466 : if (mParser) {
7467 : sink = mParser->GetContentSink();
7468 : } else {
7469 : sink = do_QueryReferent(mWeakSink);
7470 70 : if (!sink) {
7471 70 : mWeakSink = nullptr;
7472 : }
7473 : }
7474 : // Determine if it is safe to flush the sink notifications
7475 : // by determining if it safe to flush all the presshells.
7476 : if (sink && (flushType == FlushType::Content || IsSafeToFlush())) {
7477 503 : sink->FlushPendingNotifications(flushType);
7478 : }
7479 434 : }
7480 :
7481 : // Should we be flushing pending binding constructors in here?
7482 :
7483 : if (flushType <= FlushType::ContentAndNotify) {
7484 : // Nothing to do here
7485 : return;
7486 : }
7487 :
7488 : // If we have a parent we must flush the parent too to ensure that our
7489 : // container is reflowed if its size was changed. But if it's not safe to
7490 : // flush ourselves, then don't flush the parent, since that can cause things
7491 69 : // like resizes of our frame's widget, which we can't handle while flushing
7492 5 : // is unsafe.
7493 5 : // Since media queries mean that a size change of our container can
7494 10 : // affect style, we need to promote a style flush on ourself to a
7495 : // layout flush on our parent, since we need our container to be the
7496 0 : // correct size to determine the correct style.
7497 : if (mParentDocument && IsSafeToFlush()) {
7498 : mozilla::ChangesToFlush parentFlush = aFlush;
7499 69 : if (flushType >= FlushType::Style) {
7500 0 : parentFlush.mFlushType = std::max(FlushType::Layout, flushType);
7501 : }
7502 : mParentDocument->FlushPendingNotifications(parentFlush);
7503 : }
7504 :
7505 0 : if (nsIPresShell* shell = GetShell()) {
7506 : shell->FlushPendingNotifications(aFlush);
7507 : }
7508 0 : }
7509 0 :
7510 0 : static bool
7511 : Copy(nsIDocument* aDocument, void* aData)
7512 : {
7513 : nsTArray<nsCOMPtr<nsIDocument> >* resources =
7514 0 : static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
7515 : resources->AppendElement(aDocument);
7516 90 : return true;
7517 : }
7518 0 :
7519 0 : void
7520 : nsIDocument::FlushExternalResources(FlushType aType)
7521 180 : {
7522 0 : NS_ASSERTION(aType >= FlushType::Style,
7523 : "should only need to flush for style or higher in external resources");
7524 180 : if (GetDisplayDocument()) {
7525 0 : return;
7526 : }
7527 : nsTArray<nsCOMPtr<nsIDocument> > resources;
7528 : EnumerateExternalResources(Copy, &resources);
7529 :
7530 31 : for (uint32_t i = 0; i < resources.Length(); i++) {
7531 : resources[i]->FlushPendingNotifications(aType);
7532 : }
7533 : }
7534 0 :
7535 0 : void
7536 0 : nsIDocument::SetXMLDeclaration(const char16_t* aVersion,
7537 : const char16_t* aEncoding,
7538 : const int32_t aStandalone)
7539 1 : {
7540 : if (!aVersion || *aVersion == '\0') {
7541 31 : mXMLDeclarationBits = 0;
7542 4 : return;
7543 : }
7544 :
7545 0 : mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
7546 0 :
7547 0 : if (aEncoding && *aEncoding != '\0') {
7548 : mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
7549 0 : }
7550 0 :
7551 : if (aStandalone == 1) {
7552 : mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
7553 : XML_DECLARATION_BITS_STANDALONE_YES;
7554 : }
7555 0 : else if (aStandalone == 0) {
7556 : mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
7557 : }
7558 : }
7559 0 :
7560 0 : void
7561 0 : nsIDocument::GetXMLDeclaration(nsAString& aVersion,
7562 : nsAString& aEncoding,
7563 0 : nsAString& aStandalone)
7564 : {
7565 : aVersion.Truncate();
7566 : aEncoding.Truncate();
7567 : aStandalone.Truncate();
7568 0 :
7569 : if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
7570 0 : return;
7571 : }
7572 :
7573 0 : // always until we start supporting 1.1 etc.
7574 : aVersion.AssignLiteral("1.0");
7575 :
7576 0 : if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
7577 0 : // This is what we have stored, not necessarily what was written
7578 0 : // in the original
7579 : GetCharacterSet(aEncoding);
7580 0 : }
7581 :
7582 : if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
7583 : if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
7584 : aStandalone.AssignLiteral("yes");
7585 : } else {
7586 23 : aStandalone.AssignLiteral("no");
7587 : }
7588 : }
7589 : }
7590 0 :
7591 : bool
7592 : nsIDocument::IsScriptEnabled()
7593 : {
7594 0 : // If this document is sandboxed without 'allow-scripts'
7595 23 : // script is not enabled
7596 : if (HasScriptsBlockedBySandbox()) {
7597 : return false;
7598 : }
7599 0 :
7600 : nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
7601 : if (!globalObject || !globalObject->GetGlobalJSObject()) {
7602 : return false;
7603 0 : }
7604 :
7605 0 : return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
7606 0 : }
7607 0 :
7608 : nsRadioGroupStruct*
7609 : nsDocument::GetRadioGroup(const nsAString& aName) const
7610 : {
7611 0 : nsRadioGroupStruct* radioGroup = nullptr;
7612 : mRadioGroups.Get(aName, &radioGroup);
7613 0 : return radioGroup;
7614 0 : }
7615 :
7616 : nsRadioGroupStruct*
7617 : nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
7618 0 : {
7619 : return mRadioGroups.LookupForAdd(aName).OrInsert(
7620 : [] () { return new nsRadioGroupStruct(); });
7621 0 : }
7622 0 :
7623 0 : void
7624 : nsDocument::SetCurrentRadioButton(const nsAString& aName,
7625 : HTMLInputElement* aRadio)
7626 0 : {
7627 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7628 0 : radioGroup->mSelectedRadioButton = aRadio;
7629 : }
7630 :
7631 : HTMLInputElement*
7632 0 : nsDocument::GetCurrentRadioButton(const nsAString& aName)
7633 : {
7634 : return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
7635 : }
7636 :
7637 : NS_IMETHODIMP
7638 : nsDocument::GetNextRadioButton(const nsAString& aName,
7639 : const bool aPrevious,
7640 : HTMLInputElement* aFocusedRadio,
7641 0 : HTMLInputElement** aRadioOut)
7642 : {
7643 0 : // XXX Can we combine the HTML radio button method impls of
7644 : // nsDocument and nsHTMLFormControl?
7645 : // XXX Why is HTML radio button stuff in nsDocument, as
7646 : // opposed to nsHTMLDocument?
7647 0 : *aRadioOut = nullptr;
7648 0 :
7649 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7650 :
7651 : // Return the radio button relative to the focused radio button.
7652 0 : // If no radio is focused, get the radio relative to the selected one.
7653 0 : RefPtr<HTMLInputElement> currentRadio;
7654 : if (aFocusedRadio) {
7655 : currentRadio = aFocusedRadio;
7656 : }
7657 0 : else {
7658 0 : currentRadio = radioGroup->mSelectedRadioButton;
7659 : if (!currentRadio) {
7660 : return NS_ERROR_FAILURE;
7661 : }
7662 0 : }
7663 0 : int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
7664 0 : if (index < 0) {
7665 0 : return NS_ERROR_FAILURE;
7666 0 : }
7667 0 :
7668 : int32_t numRadios = radioGroup->mRadioButtons.Count();
7669 : RefPtr<HTMLInputElement> radio;
7670 0 : do {
7671 0 : if (aPrevious) {
7672 : if (--index < 0) {
7673 0 : index = numRadios -1;
7674 : }
7675 0 : }
7676 0 : else if (++index >= numRadios) {
7677 : index = 0;
7678 0 : }
7679 : NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTMLElement(nsGkAtoms::input),
7680 : "mRadioButtons holding a non-radio button");
7681 : radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
7682 : } while (radio->Disabled() && radio != currentRadio);
7683 0 :
7684 : radio.forget(aRadioOut);
7685 : return NS_OK;
7686 0 : }
7687 0 :
7688 : void
7689 0 : nsDocument::AddToRadioGroup(const nsAString& aName,
7690 0 : HTMLInputElement* aRadio)
7691 : {
7692 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7693 : radioGroup->mRadioButtons.AppendObject(aRadio);
7694 :
7695 0 : if (aRadio->IsRequired()) {
7696 : radioGroup->mRequiredRadioCount++;
7697 : }
7698 0 : }
7699 0 :
7700 : void
7701 0 : nsDocument::RemoveFromRadioGroup(const nsAString& aName,
7702 0 : HTMLInputElement* aRadio)
7703 : {
7704 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7705 : radioGroup->mRadioButtons.RemoveObject(aRadio);
7706 0 :
7707 : if (aRadio->IsRequired()) {
7708 : NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
7709 0 : "mRequiredRadioCount about to wrap below 0!");
7710 : radioGroup->mRequiredRadioCount--;
7711 : }
7712 : }
7713 0 :
7714 : NS_IMETHODIMP
7715 0 : nsDocument::WalkRadioGroup(const nsAString& aName,
7716 0 : nsIRadioVisitor* aVisitor,
7717 : bool aFlushContent)
7718 : {
7719 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7720 :
7721 : for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
7722 : if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
7723 : return NS_OK;
7724 : }
7725 0 : }
7726 :
7727 0 : return NS_OK;
7728 0 : }
7729 :
7730 : uint32_t
7731 : nsDocument::GetRequiredRadioCount(const nsAString& aName) const
7732 0 : {
7733 : nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
7734 0 : return radioGroup ? radioGroup->mRequiredRadioCount : 0;
7735 : }
7736 0 :
7737 0 : void
7738 : nsDocument::RadioRequiredWillChange(const nsAString& aName, bool aRequiredAdded)
7739 0 : {
7740 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7741 0 :
7742 : if (aRequiredAdded) {
7743 0 : radioGroup->mRequiredRadioCount++;
7744 : } else {
7745 : NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
7746 0 : "mRequiredRadioCount about to wrap below 0!");
7747 : radioGroup->mRequiredRadioCount--;
7748 0 : }
7749 0 : }
7750 :
7751 : bool
7752 : nsDocument::GetValueMissingState(const nsAString& aName) const
7753 0 : {
7754 : nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
7755 0 : return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
7756 0 : }
7757 0 :
7758 : void
7759 : nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
7760 0 : {
7761 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
7762 52 : radioGroup->mGroupSuffersFromValueMissing = aValue;
7763 : }
7764 :
7765 104 : void
7766 0 : nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
7767 52 : {
7768 0 : PRTime modDate = 0;
7769 : nsresult rv;
7770 :
7771 0 : nsCOMPtr<nsIHttpChannel> httpChannel;
7772 0 : rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
7773 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7774 0 : return;
7775 : }
7776 0 :
7777 : if (httpChannel) {
7778 0 : nsAutoCString tmp;
7779 0 : rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
7780 0 : tmp);
7781 :
7782 : if (NS_SUCCEEDED(rv)) {
7783 : PRTime time;
7784 : PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
7785 0 : if (st == PR_SUCCESS) {
7786 0 : modDate = time;
7787 : }
7788 : }
7789 :
7790 : // The misspelled key 'referer' is as per the HTTP spec
7791 : rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
7792 : mReferrer);
7793 :
7794 : static const char *const headers[] = {
7795 : "default-style",
7796 : "content-style-type",
7797 : "content-language",
7798 : "content-disposition",
7799 : "refresh",
7800 : "x-dns-prefetch-control",
7801 : "x-frame-options",
7802 : "referrer-policy",
7803 0 : // add more http headers if you need
7804 0 : // XXXbz don't add content-location support without reading bug
7805 0 : // 238654 and its dependencies/dups first.
7806 : 0
7807 0 : };
7808 0 :
7809 0 : nsAutoCString headerVal;
7810 0 : const char *const *name = headers;
7811 : while (*name) {
7812 0 : rv =
7813 : httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
7814 : if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
7815 104 : RefPtr<nsAtom> key = NS_Atomize(*name);
7816 0 : SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
7817 0 : }
7818 0 : ++name;
7819 0 : }
7820 : } else {
7821 0 : nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
7822 : if (fileChannel) {
7823 0 : nsCOMPtr<nsIFile> file;
7824 0 : fileChannel->GetFile(getter_AddRefs(file));
7825 : if (file) {
7826 : PRTime msecs;
7827 : rv = file->GetLastModifiedTime(&msecs);
7828 0 :
7829 52 : if (NS_SUCCEEDED(rv)) {
7830 52 : modDate = msecs * int64_t(PR_USEC_PER_MSEC);
7831 0 : }
7832 0 : }
7833 : } else {
7834 : nsAutoCString contentDisp;
7835 : rv = aChannel->GetContentDispositionHeader(contentDisp);
7836 : if (NS_SUCCEEDED(rv)) {
7837 52 : SetHeaderData(nsGkAtoms::headerContentDisposition,
7838 52 : NS_ConvertASCIItoUTF16(contentDisp));
7839 0 : }
7840 : }
7841 : }
7842 :
7843 : mLastModified.Truncate();
7844 54 : if (modDate != 0) {
7845 : GetFormattedTimeString(modDate, mLastModified);
7846 : }
7847 : }
7848 0 :
7849 54 : already_AddRefed<Element>
7850 0 : nsIDocument::CreateElem(const nsAString& aName, nsAtom *aPrefix,
7851 0 : int32_t aNamespaceID, const nsAString* aIs)
7852 : {
7853 0 : #ifdef DEBUG
7854 : nsAutoString qName;
7855 : if (aPrefix) {
7856 : aPrefix->ToString(qName);
7857 : qName.Append(':');
7858 108 : }
7859 54 : qName.Append(aName);
7860 :
7861 : // Note: "a:b:c" is a valid name in non-namespaces XML, and
7862 : // nsIDocument::CreateElement can call us with such a name and no prefix,
7863 : // which would cause an error if we just used true here.
7864 108 : bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
7865 54 : NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
7866 108 : "Don't pass invalid prefixes to nsDocument::CreateElem, "
7867 54 : "check caller.");
7868 : #endif
7869 0 :
7870 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
7871 0 : mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
7872 54 : ELEMENT_NODE, getter_AddRefs(nodeInfo));
7873 : NS_ENSURE_TRUE(nodeInfo, nullptr);
7874 :
7875 : nsCOMPtr<Element> element;
7876 0 : nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
7877 : NOT_FROM_PARSER, aIs);
7878 75 : return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
7879 75 : }
7880 :
7881 : bool
7882 0 : nsIDocument::IsSafeToFlush() const
7883 : {
7884 : nsIPresShell* shell = GetShell();
7885 : if (!shell)
7886 0 : return true;
7887 :
7888 : return shell->IsSafeToFlush();
7889 : }
7890 :
7891 : void
7892 : nsIDocument::Sanitize()
7893 : {
7894 : // Sanitize the document by resetting all password fields and any form
7895 : // fields with autocomplete=off to their default values. We do this now,
7896 : // instead of when the presentation is restored, to offer some protection
7897 0 : // in case there is ever an exploit that allows a cached document to be
7898 : // accessed from a different document.
7899 0 :
7900 : // First locate all input elements, regardless of whether they are
7901 0 : // in a form, and reset the password and autocomplete=off elements.
7902 0 :
7903 0 : RefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
7904 :
7905 0 : nsAutoString value;
7906 0 :
7907 0 : uint32_t length = nodes->Length(true);
7908 : for (uint32_t i = 0; i < length; ++i) {
7909 0 : NS_ASSERTION(nodes->Item(i), "null item in node list!");
7910 :
7911 0 : RefPtr<HTMLInputElement> input = HTMLInputElement::FromNodeOrNull(nodes->Item(i));
7912 0 : if (!input)
7913 : continue;
7914 :
7915 0 : bool resetValue = false;
7916 0 :
7917 0 : input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
7918 : if (value.LowerCaseEqualsLiteral("off")) {
7919 : resetValue = true;
7920 0 : } else {
7921 0 : input->GetType(value);
7922 : if (value.LowerCaseEqualsLiteral("password"))
7923 : resetValue = true;
7924 : }
7925 :
7926 0 : if (resetValue) {
7927 : input->Reset();
7928 0 : }
7929 0 : }
7930 0 :
7931 : // Now locate all _form_ elements that have autocomplete=off and reset them
7932 0 : nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
7933 0 :
7934 : length = nodes->Length(true);
7935 : for (uint32_t i = 0; i < length; ++i) {
7936 0 : NS_ASSERTION(nodes->Item(i), "null item in nodelist");
7937 0 :
7938 0 : HTMLFormElement* form = HTMLFormElement::FromNode(nodes->Item(i));
7939 : if (!form)
7940 0 : continue;
7941 :
7942 : form->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, value);
7943 33 : if (value.LowerCaseEqualsLiteral("off"))
7944 : form->Reset();
7945 33 : }
7946 20 : }
7947 :
7948 : void
7949 : nsIDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
7950 : {
7951 26 : if (!mSubDocuments) {
7952 52 : return;
7953 13 : }
7954 13 :
7955 0 : // PLDHashTable::Iterator can't handle modifications while iterating so we
7956 0 : // copy all entries to an array first before calling any callbacks.
7957 : AutoTArray<nsCOMPtr<nsIDocument>, 8> subdocs;
7958 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7959 0 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7960 0 : nsIDocument* subdoc = entry->mSubDocument;
7961 : if (subdoc) {
7962 : subdocs.AppendElement(subdoc);
7963 : }
7964 : }
7965 : for (auto subdoc : subdocs) {
7966 : if (!aCallback(subdoc, aData)) {
7967 72 : break;
7968 : }
7969 : }
7970 : }
7971 0 :
7972 : void
7973 : nsIDocument::CollectDescendantDocuments(
7974 : nsTArray<nsCOMPtr<nsIDocument>>& aDescendants,
7975 0 : nsDocTestFunc aCallback) const
7976 21 : {
7977 21 : if (!mSubDocuments) {
7978 21 : return;
7979 0 : }
7980 0 :
7981 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
7982 0 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
7983 : const nsIDocument* subdoc = entry->mSubDocument;
7984 : if (subdoc) {
7985 : if (aCallback(subdoc)) {
7986 : aDescendants.AppendElement(entry->mSubDocument);
7987 : }
7988 : subdoc->CollectDescendantDocuments(aDescendants, aCallback);
7989 : }
7990 : }
7991 : }
7992 0 :
7993 : #ifdef DEBUG_bryner
7994 0 : #define DEBUG_PAGE_CACHE
7995 : #endif
7996 :
7997 : bool
7998 0 : nsIDocument::CanSavePresentation(nsIRequest *aNewRequest)
7999 : {
8000 : if (!IsBFCachingAllowed()) {
8001 : return false;
8002 : }
8003 :
8004 : if (EventHandlingSuppressed()) {
8005 : return false;
8006 : }
8007 0 :
8008 0 : // Do not allow suspended windows to be placed in the
8009 : // bfcache. This method is also used to verify a document
8010 : // coming out of the bfcache is ok to restore, though. So
8011 : // we only want to block suspend windows that aren't also
8012 : // frozen.
8013 0 : nsPIDOMWindowInner* win = GetInnerWindow();
8014 0 : if (win && win->IsSuspended() && !win->IsFrozen()) {
8015 0 : return false;
8016 0 : }
8017 :
8018 : // Check our event listener manager for unload/beforeunload listeners.
8019 : nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
8020 : if (piTarget) {
8021 : EventListenerManager* manager = piTarget->GetExistingListenerManager();
8022 0 : if (manager && manager->HasUnloadListeners()) {
8023 0 : return false;
8024 0 : }
8025 0 : }
8026 :
8027 0 : // Check if we have pending network requests
8028 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8029 : if (loadGroup) {
8030 : nsCOMPtr<nsISimpleEnumerator> requests;
8031 : loadGroup->GetRequests(getter_AddRefs(requests));
8032 0 :
8033 0 : bool hasMore = false;
8034 0 :
8035 0 : // We want to bail out if we have any requests other than aNewRequest (or
8036 : // in the case when aNewRequest is a part of a multipart response the base
8037 : // channel the multipart response is coming in on).
8038 0 : nsCOMPtr<nsIChannel> baseChannel;
8039 0 : nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
8040 0 : if (part) {
8041 : part->GetBaseChannel(getter_AddRefs(baseChannel));
8042 0 : }
8043 0 :
8044 : while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8045 : nsCOMPtr<nsISupports> elem;
8046 : requests->GetNext(getter_AddRefs(elem));
8047 :
8048 : nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8049 : if (request && request != aNewRequest && request != baseChannel) {
8050 : #ifdef DEBUG_PAGE_CACHE
8051 : nsAutoCString requestName, docSpec;
8052 : request->GetName(requestName);
8053 0 : if (mDocumentURI)
8054 : mDocumentURI->GetSpec(docSpec);
8055 :
8056 : printf("document %s has request %s\n",
8057 : docSpec.get(), requestName.get());
8058 : #endif
8059 0 : return false;
8060 0 : }
8061 : }
8062 : }
8063 :
8064 : // Check if we have active GetUserMedia use
8065 : if (MediaManager::Exists() && win &&
8066 0 : MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
8067 : return false;
8068 : }
8069 :
8070 : #ifdef MOZ_WEBRTC
8071 : // Check if we have active PeerConnections
8072 : if (win && win->HasActivePeerConnections()) {
8073 0 : return false;
8074 : }
8075 : #endif // MOZ_WEBRTC
8076 :
8077 : // Don't save presentations for documents containing EME content, so that
8078 : // CDMs reliably shutdown upon user navigation.
8079 0 : if (ContainsEMEContent()) {
8080 : return false;
8081 : }
8082 :
8083 0 : // Don't save presentations for documents containing MSE content, to
8084 0 : // reduce memory usage.
8085 0 : if (ContainsMSEContent()) {
8086 0 : return false;
8087 : }
8088 :
8089 0 : if (mSubDocuments) {
8090 0 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
8091 0 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
8092 : nsIDocument* subdoc = entry->mSubDocument;
8093 :
8094 : // The aIgnoreRequest we were passed is only for us, so don't pass it on.
8095 : bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
8096 : if (!canCache) {
8097 0 : return false;
8098 0 : }
8099 : }
8100 0 : }
8101 :
8102 :
8103 : if (win) {
8104 0 : auto* globalWindow = nsGlobalWindowInner::Cast(win);
8105 : #ifdef MOZ_WEBSPEECH
8106 : if (globalWindow->HasActiveSpeechSynthesis()) {
8107 : return false;
8108 : }
8109 : #endif
8110 : if (globalWindow->HasUsedVR()) {
8111 : return false;
8112 : }
8113 8 : }
8114 :
8115 : return true;
8116 : }
8117 0 :
8118 : void
8119 : nsDocument::Destroy()
8120 8 : {
8121 : // The ContentViewer wants to release the document now. So, tell our content
8122 24 : // to drop any references to the document so that it can be destroyed.
8123 8 : if (mIsGoingAway)
8124 0 : return;
8125 :
8126 0 : mIsGoingAway = true;
8127 0 :
8128 0 : ScriptLoader()->Destroy();
8129 16 : SetScriptGlobalObject(nullptr);
8130 0 : RemovedFromDocShell();
8131 :
8132 0 : bool oldVal = mInUnlinkOrDeletion;
8133 : mInUnlinkOrDeletion = true;
8134 0 :
8135 : #ifdef DEBUG
8136 : uint32_t oldChildCount = GetChildCount();
8137 : #endif
8138 :
8139 8 : for (nsIContent* child = GetFirstChild(); child;
8140 : child = child->GetNextSibling()) {
8141 : child->DestroyContent();
8142 : MOZ_ASSERT(child->GetParentNode() == this);
8143 0 : }
8144 : MOZ_ASSERT(oldChildCount == GetChildCount());
8145 16 :
8146 : mInUnlinkOrDeletion = oldVal;
8147 :
8148 8 : mLayoutHistoryState = nullptr;
8149 0 :
8150 : // Shut down our external resource map. We might not need this for
8151 8 : // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
8152 0 : // tearing down all those frame trees right now is the right thing to do.
8153 0 : mExternalResourceMap.Shutdown();
8154 : }
8155 :
8156 : void
8157 : nsDocument::RemovedFromDocShell()
8158 58 : {
8159 : if (mRemovedFromDocShell)
8160 116 : return;
8161 116 :
8162 0 : mRemovedFromDocShell = true;
8163 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8164 0 :
8165 0 : for (nsIContent* child = GetFirstChild(); child;
8166 0 : child = child->GetNextSibling()) {
8167 : child->SaveSubtreeState();
8168 : }
8169 : }
8170 0 :
8171 : already_AddRefed<nsILayoutHistoryState>
8172 : nsIDocument::GetLayoutHistoryState() const
8173 : {
8174 0 : nsCOMPtr<nsILayoutHistoryState> state;
8175 : if (!mScriptGlobalObject) {
8176 : state = mLayoutHistoryState;
8177 : } else {
8178 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
8179 0 : if (docShell) {
8180 0 : docShell->GetLayoutHistoryState(getter_AddRefs(state));
8181 : }
8182 0 : }
8183 0 :
8184 : return state.forget();
8185 0 : }
8186 0 :
8187 0 : void
8188 0 : nsIDocument::EnsureOnloadBlocker()
8189 0 : {
8190 0 : // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8191 0 : // -- it's not ours.
8192 : if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
8193 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8194 : if (loadGroup) {
8195 : // Check first to see if mOnloadBlocker is in the loadgroup.
8196 0 : nsCOMPtr<nsISimpleEnumerator> requests;
8197 : loadGroup->GetRequests(getter_AddRefs(requests));
8198 :
8199 : bool hasMore = false;
8200 : while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8201 : nsCOMPtr<nsISupports> elem;
8202 3 : requests->GetNext(getter_AddRefs(elem));
8203 : nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8204 9 : if (request && request == mOnloadBlocker) {
8205 3 : return;
8206 0 : }
8207 : }
8208 0 :
8209 : // Not in the loadgroup, so add it.
8210 : loadGroup->AddRequest(mOnloadBlocker, nullptr);
8211 212 : }
8212 : }
8213 424 : }
8214 0 :
8215 0 : void
8216 : nsDocument::AsyncBlockOnload()
8217 : {
8218 : while (mAsyncOnloadBlockCount) {
8219 : --mAsyncOnloadBlockCount;
8220 280 : BlockOnload();
8221 23 : }
8222 : }
8223 :
8224 0 : void
8225 0 : nsDocument::BlockOnload()
8226 9 : {
8227 3 : if (mDisplayDocument) {
8228 : mDisplayDocument->BlockOnload();
8229 0 : return;
8230 : }
8231 0 :
8232 20 : // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8233 0 : // -- it's not ours.
8234 : if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
8235 : if (!nsContentUtils::IsSafeToRunScript()) {
8236 0 : // Because AddRequest may lead to OnStateChange calls in chrome,
8237 : // block onload only when there are no script blockers.
8238 : ++mAsyncOnloadBlockCount;
8239 : if (mAsyncOnloadBlockCount == 1) {
8240 0 : nsContentUtils::AddScriptRunner(NewRunnableMethod(
8241 : "nsDocument::AsyncBlockOnload", this, &nsDocument::AsyncBlockOnload));
8242 418 : }
8243 0 : return;
8244 0 : }
8245 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8246 : if (loadGroup) {
8247 0 : loadGroup->AddRequest(mOnloadBlocker, nullptr);
8248 0 : }
8249 : }
8250 : ++mOnloadBlockCount;
8251 : }
8252 1 :
8253 : void
8254 209 : nsDocument::UnblockOnload(bool aFireSync)
8255 130 : {
8256 : if (mDisplayDocument) {
8257 : mDisplayDocument->UnblockOnload(aFireSync);
8258 0 : return;
8259 : }
8260 10 :
8261 10 : if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
8262 : NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
8263 10 : return;
8264 : }
8265 0 :
8266 : --mOnloadBlockCount;
8267 :
8268 : if (mOnloadBlockCount == 0) {
8269 : if (mScriptGlobalObject) {
8270 : // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
8271 : // is null, it's not ours.
8272 : if (aFireSync && mAsyncOnloadBlockCount == 0) {
8273 : // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
8274 13 : ++mOnloadBlockCount;
8275 : DoUnblockOnload();
8276 52 : } else {
8277 13 : PostUnblockOnloadEvent();
8278 : }
8279 : } else if (mIsBeingUsedAsImage) {
8280 : // To correctly unblock onload for a document that contains an SVG
8281 : // image, we need to know when all of the SVG document's resources are
8282 30 : // done loading, in a way comparable to |window.onload|. We fire this
8283 : // event to indicate that the SVG should be considered fully loaded.
8284 10 : // Because scripting is disabled on SVG-as-image documents, this event
8285 10 : // is not accessible to content authors. (See bug 837315.)
8286 0 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
8287 : new AsyncEventDispatcher(this,
8288 0 : NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
8289 0 : false,
8290 0 : false);
8291 10 : asyncDispatcher->PostDOMEvent();
8292 : }
8293 : }
8294 : }
8295 :
8296 : class nsUnblockOnloadEvent : public Runnable {
8297 : public:
8298 10 : explicit nsUnblockOnloadEvent(nsIDocument* aDoc)
8299 : : mozilla::Runnable("nsUnblockOnloadEvent")
8300 10 : , mDoc(aDoc)
8301 20 : {
8302 : }
8303 10 : NS_IMETHOD Run() override {
8304 0 : mDoc->DoUnblockOnload();
8305 : return NS_OK;
8306 10 : }
8307 : private:
8308 0 : RefPtr<nsIDocument> mDoc;
8309 : };
8310 0 :
8311 : void
8312 : nsIDocument::PostUnblockOnloadEvent()
8313 20 : {
8314 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
8315 40 : nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
8316 : nsresult rv =
8317 0 : Dispatch(TaskCategory::Other, evt.forget());
8318 : if (NS_SUCCEEDED(rv)) {
8319 : // Stabilize block count so we don't post more events while this one is up
8320 : ++mOnloadBlockCount;
8321 0 : } else {
8322 : NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
8323 20 : }
8324 : }
8325 :
8326 : void
8327 : nsIDocument::DoUnblockOnload()
8328 : {
8329 20 : MOZ_ASSERT(!mDisplayDocument,
8330 : "Shouldn't get here for resource document");
8331 0 : MOZ_ASSERT(mOnloadBlockCount != 0,
8332 : "Shouldn't have a count of zero here, since we stabilized in "
8333 : "PostUnblockOnloadEvent");
8334 :
8335 : --mOnloadBlockCount;
8336 40 :
8337 24 : if (mOnloadBlockCount != 0) {
8338 12 : // We blocked again after the last unblock. Nothing to do here. We'll
8339 24 : // post a new event when we unblock again.
8340 : return;
8341 : }
8342 :
8343 : if (mAsyncOnloadBlockCount != 0) {
8344 : // We need to wait until the async onload block has been handled.
8345 0 : PostUnblockOnloadEvent();
8346 : }
8347 0 :
8348 : // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8349 0 : // -- it's not ours.
8350 0 : if (mScriptGlobalObject) {
8351 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8352 : if (loadGroup) {
8353 0 : loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
8354 : }
8355 : }
8356 : }
8357 :
8358 : nsIContent*
8359 0 : nsIDocument::GetContentInThisDocument(nsIFrame* aFrame) const
8360 : {
8361 : for (nsIFrame* f = aFrame; f;
8362 : f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
8363 : nsIContent* content = f->GetContent();
8364 : if (!content || content->IsInAnonymousSubtree())
8365 : continue;
8366 17 :
8367 : if (content->OwnerDoc() == this) {
8368 : return content;
8369 : }
8370 0 : // We must be in a subdocument so jump directly to the root frame.
8371 0 : // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
8372 : // the containing document.
8373 : f = f->PresContext()->GetPresShell()->GetRootFrame();
8374 0 : }
8375 0 :
8376 17 : return nullptr;
8377 17 : }
8378 :
8379 0 : void
8380 0 : nsIDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
8381 : const nsAString& aType,
8382 : bool aPersisted)
8383 0 : {
8384 : if (!aDispatchTarget) {
8385 17 : return;
8386 17 : }
8387 :
8388 17 : PageTransitionEventInit init;
8389 : init.mBubbles = true;
8390 : init.mCancelable = true;
8391 : init.mPersisted = aPersisted;
8392 0 :
8393 : nsDocShell* docShell = mDocumentContainer.get();
8394 0 : init.mInFrameSwap = docShell && docShell->InFrameSwap();
8395 0 :
8396 0 : RefPtr<PageTransitionEvent> event =
8397 : PageTransitionEvent::Constructor(this, aType, init);
8398 :
8399 : event->SetTrusted(true);
8400 0 : event->SetTarget(this);
8401 : EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
8402 9 : nullptr, nullptr);
8403 : }
8404 0 :
8405 9 : static bool
8406 : NotifyPageShow(nsIDocument* aDocument, void* aData)
8407 9 : {
8408 0 : const bool* aPersistedPtr = static_cast<const bool*>(aData);
8409 : aDocument->OnPageShow(*aPersistedPtr, nullptr);
8410 0 : return true;
8411 : }
8412 0 :
8413 : void
8414 0 : nsIDocument::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget)
8415 0 : {
8416 0 : mVisible = true;
8417 :
8418 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8419 : EnumerateExternalResources(NotifyPageShow, &aPersisted);
8420 :
8421 9 : Element* root = GetRootElement();
8422 : if (aPersisted && root) {
8423 : // Send out notifications that our <link> elements are attached.
8424 9 : RefPtr<nsContentList> links = NS_GetContentList(root,
8425 : kNameSpaceID_XHTML,
8426 : NS_LITERAL_STRING("link"));
8427 18 :
8428 0 : uint32_t linkCount = links->Length(true);
8429 : for (uint32_t i = 0; i < linkCount; ++i) {
8430 : static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
8431 0 : }
8432 0 : }
8433 :
8434 : // See nsIDocument
8435 0 : if (!aDispatchStartTarget) {
8436 : // Set mIsShowing before firing events, in case those event handlers
8437 9 : // move us around.
8438 : mIsShowing = true;
8439 0 : }
8440 9 :
8441 0 : if (mAnimationController) {
8442 18 : mAnimationController->OnPageShow();
8443 0 : }
8444 :
8445 : if (aPersisted) {
8446 0 : ImageTracker()->SetAnimatingState(true);
8447 : }
8448 :
8449 18 : UpdateVisibilityState();
8450 0 :
8451 18 : if (!mIsBeingUsedAsImage) {
8452 : // Dispatch observer notification to notify observers page is shown.
8453 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8454 : if (os) {
8455 0 : nsIPrincipal* principal = NodePrincipal();
8456 : os->NotifyObservers(this,
8457 : nsContentUtils::IsSystemPrincipal(principal) ?
8458 0 : "chrome-page-shown" :
8459 : "content-page-shown",
8460 0 : nullptr);
8461 0 : }
8462 0 :
8463 : nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8464 : if (!target) {
8465 : target = do_QueryInterface(GetWindow());
8466 0 : }
8467 : DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
8468 : }
8469 0 : }
8470 0 :
8471 0 : static bool
8472 0 : NotifyPageHide(nsIDocument* aDocument, void* aData)
8473 0 : {
8474 : const bool* aPersistedPtr = static_cast<const bool*>(aData);
8475 0 : aDocument->OnPageHide(*aPersistedPtr, nullptr);
8476 0 : return true;
8477 : }
8478 0 :
8479 : static void
8480 : DispatchCustomEventWithFlush(nsINode* aTarget, const nsAString& aEventType,
8481 0 : bool aBubbles, bool aOnlyChromeDispatch)
8482 : {
8483 : RefPtr<Event> event = NS_NewDOMEvent(aTarget, nullptr, nullptr);
8484 0 : event->InitEvent(aEventType, aBubbles, false);
8485 0 : event->SetTrusted(true);
8486 0 : if (aOnlyChromeDispatch) {
8487 : event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
8488 : }
8489 : if (nsPresContext* presContext = aTarget->OwnerDoc()->GetPresContext()) {
8490 : presContext->RefreshDriver()->ScheduleEventDispatch(aTarget, event);
8491 8 : }
8492 : }
8493 16 :
8494 0 : static void
8495 0 : DispatchFullScreenChange(nsIDocument* aTarget)
8496 : {
8497 0 : DispatchCustomEventWithFlush(
8498 0 : aTarget, NS_LITERAL_STRING("fullscreenchange"),
8499 0 : /* Bubbles */ true, /* OnlyChrome */ false);
8500 0 : }
8501 0 :
8502 : static void ClearPendingFullscreenRequests(nsIDocument* aDoc);
8503 :
8504 : void
8505 : nsIDocument::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget)
8506 : {
8507 8 : if (mDocGroup && Telemetry::CanRecordExtended() &&
8508 8 : IsTopLevelContentDocument()) {
8509 0 : TabGroup* tabGroup = mDocGroup->GetTabGroup();
8510 :
8511 0 : if (tabGroup) {
8512 : Telemetry::Accumulate(Telemetry::ACTIVE_DOCGROUPS_PER_TABGROUP,
8513 0 : tabGroup->Count(true /* aActiveOnly */));
8514 0 : Telemetry::Accumulate(Telemetry::TOTAL_DOCGROUPS_PER_TABGROUP,
8515 0 : tabGroup->Count());
8516 : }
8517 : }
8518 :
8519 : // Send out notifications that our <link> elements are detached,
8520 8 : // but only if this is not a full unload.
8521 : Element* root = GetRootElement();
8522 : if (aPersisted && root) {
8523 8 : RefPtr<nsContentList> links = NS_GetContentList(root,
8524 : kNameSpaceID_XHTML,
8525 : NS_LITERAL_STRING("link"));
8526 16 :
8527 0 : uint32_t linkCount = links->Length(true);
8528 : for (uint32_t i = 0; i < linkCount; ++i) {
8529 : static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
8530 : }
8531 : }
8532 8 :
8533 8 : // See nsIDocument
8534 0 : if (!aDispatchStartTarget) {
8535 : // Set mIsShowing before firing events, in case those event handlers
8536 : // move us around.
8537 0 : mIsShowing = false;
8538 : }
8539 8 :
8540 : if (mAnimationController) {
8541 0 : mAnimationController->OnPageHide();
8542 8 : }
8543 0 :
8544 16 : // We do not stop the animations (bug 1024343)
8545 0 : // when the page is refreshing while being dragged out
8546 : nsDocShell* docShell = mDocumentContainer.get();
8547 : if (aPersisted && !(docShell && docShell->InFrameSwap())) {
8548 0 : ImageTracker()->SetAnimatingState(false);
8549 : }
8550 :
8551 : ExitPointerLock();
8552 0 :
8553 8 : if (!mIsBeingUsedAsImage) {
8554 16 : // Dispatch observer notification to notify observers page is hidden.
8555 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8556 : if (os) {
8557 0 : nsIPrincipal* principal = NodePrincipal();
8558 0 : os->NotifyObservers(this,
8559 : nsContentUtils::IsSystemPrincipal(principal) ?
8560 : "chrome-page-hidden" :
8561 : "content-page-hidden",
8562 0 : nullptr);
8563 : }
8564 8 :
8565 : // Now send out a PageHide event.
8566 0 : nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8567 8 : if (!target) {
8568 : target = do_QueryInterface(GetWindow());
8569 8 : }
8570 0 : {
8571 : PageUnloadingEventTimeStamp timeStamp(this);
8572 : DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
8573 : }
8574 : }
8575 :
8576 : mVisible = false;
8577 :
8578 : UpdateVisibilityState();
8579 :
8580 0 : EnumerateExternalResources(NotifyPageHide, &aPersisted);
8581 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8582 :
8583 : ClearPendingFullscreenRequests(this);
8584 : if (FullScreenStackTop()) {
8585 : // If this document was fullscreen, we should exit fullscreen in this
8586 : // doctree branch. This ensures that if the user navigates while in
8587 : // fullscreen mode we don't leave its still visible ancestor documents
8588 : // in fullscreen mode. So exit fullscreen in the document's fullscreen
8589 : // root document, as this will exit fullscreen in all the root's
8590 0 : // descendant documents. Note that documents are removed from the
8591 : // doctree by the time OnPageHide() is called, so we must store a
8592 : // reference to the root (in nsDocument::mFullscreenRoot) since we can't
8593 : // just traverse the doctree to get the root.
8594 0 : nsIDocument::ExitFullscreenInDocTree(this);
8595 :
8596 8 : // Since the document is removed from the doctree before OnPageHide() is
8597 : // called, ExitFullscreen() can't traverse from the root down to *this*
8598 : // document, so we must manually call CleanupFullscreenState() below too.
8599 0 : // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
8600 : // so we *must* call it after ExitFullscreen(), not before.
8601 0 : // OnPageHide() is called in every hidden (i.e. descendant) document,
8602 : // so calling CleanupFullscreenState() here will ensure all hidden
8603 : // documents have their fullscreen state reset.
8604 0 : CleanupFullscreenState();
8605 0 :
8606 : // If anyone was listening to this document's state, advertizing the state
8607 : // change would be the least of the politeness.
8608 0 : DispatchFullScreenChange(this);
8609 0 : }
8610 0 : }
8611 :
8612 : void
8613 0 : nsIDocument::WillDispatchMutationEvent(nsINode* aTarget)
8614 : {
8615 : NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
8616 0 : mSubtreeModifiedTargets.Count() == 0,
8617 : "mSubtreeModifiedTargets not cleared after dispatching?");
8618 0 : ++mSubtreeModifiedDepth;
8619 0 : if (aTarget) {
8620 0 : // MayDispatchMutationEvent is often called just before this method,
8621 0 : // so it has already appended the node to mSubtreeModifiedTargets.
8622 0 : int32_t count = mSubtreeModifiedTargets.Count();
8623 : if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
8624 : mSubtreeModifiedTargets.AppendObject(aTarget);
8625 0 : }
8626 0 : }
8627 0 : }
8628 0 :
8629 0 : void
8630 : nsIDocument::MutationEventDispatched(nsINode* aTarget)
8631 : {
8632 0 : --mSubtreeModifiedDepth;
8633 0 : if (mSubtreeModifiedDepth == 0) {
8634 0 : int32_t count = mSubtreeModifiedTargets.Count();
8635 0 : if (!count) {
8636 0 : return;
8637 0 : }
8638 :
8639 : nsPIDOMWindowInner* window = GetInnerWindow();
8640 0 : if (window &&
8641 0 : !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
8642 0 : mSubtreeModifiedTargets.Clear();
8643 : return;
8644 0 : }
8645 0 :
8646 : nsCOMArray<nsINode> realTargets;
8647 : for (int32_t i = 0; i < count; ++i) {
8648 : nsINode* possibleTarget = mSubtreeModifiedTargets[i];
8649 : nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
8650 0 : if (content && content->ChromeOnlyAccess()) {
8651 0 : continue;
8652 : }
8653 :
8654 : nsINode* commonAncestor = nullptr;
8655 0 : int32_t realTargetCount = realTargets.Count();
8656 : for (int32_t j = 0; j < realTargetCount; ++j) {
8657 0 : commonAncestor =
8658 0 : nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
8659 0 : if (commonAncestor) {
8660 0 : realTargets.ReplaceObjectAt(commonAncestor, j);
8661 0 : break;
8662 : }
8663 : }
8664 : if (!commonAncestor) {
8665 : realTargets.AppendObject(possibleTarget);
8666 : }
8667 46 : }
8668 :
8669 : mSubtreeModifiedTargets.Clear();
8670 46 :
8671 : int32_t realTargetCount = realTargets.Count();
8672 92 : for (int32_t k = 0; k < realTargetCount; ++k) {
8673 92 : InternalMutationEvent mutation(true, eLegacySubtreeModified);
8674 0 : (new AsyncEventDispatcher(realTargets[k], mutation))->
8675 92 : RunDOMEventWhenSafe();
8676 0 : }
8677 0 : }
8678 : }
8679 :
8680 0 : void
8681 : nsIDocument::DestroyElementMaps()
8682 : {
8683 : #ifdef DEBUG
8684 : mStyledLinksCleared = true;
8685 232 : #endif
8686 174 : mStyledLinks.Clear();
8687 0 : mIdentifierMap.Clear();
8688 : mComposedShadowRoots.Clear();
8689 : mResponsiveContent.Clear();
8690 : IncrementExpandoGeneration(*this);
8691 0 : }
8692 116 :
8693 0 : void
8694 : nsIDocument::RefreshLinkHrefs()
8695 0 : {
8696 : // Get a list of all links we know about. We will reset them, which will
8697 : // remove them from the document, so we need a copy of what is in the
8698 0 : // hashtable.
8699 : LinkArray linksToNotify(mStyledLinks.Count());
8700 0 : for (auto iter = mStyledLinks.ConstIter(); !iter.Done(); iter.Next()) {
8701 : linksToNotify.AppendElement(iter.Get()->GetKey());
8702 : }
8703 0 :
8704 0 : // Reset all of our styled links.
8705 : nsAutoScriptBlocker scriptBlocker;
8706 0 : for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
8707 0 : linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
8708 : }
8709 : }
8710 :
8711 0 : nsresult
8712 0 : nsDocument::CloneDocHelper(nsDocument* clone, bool aPreallocateChildren) const
8713 0 : {
8714 : clone->mIsStaticDocument = mCreatingStaticClone;
8715 0 :
8716 0 : // Init document
8717 0 : nsresult rv = clone->Init();
8718 0 : NS_ENSURE_SUCCESS(rv, rv);
8719 :
8720 0 : if (mCreatingStaticClone) {
8721 : nsCOMPtr<nsILoadGroup> loadGroup;
8722 0 :
8723 0 : // |mDocumentContainer| is the container of the document that is being
8724 0 : // created and not the original container. See CreateStaticClone function().
8725 : nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
8726 : if (docLoader) {
8727 0 : docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
8728 : }
8729 : nsCOMPtr<nsIChannel> channel = GetChannel();
8730 : nsCOMPtr<nsIURI> uri;
8731 : if (channel) {
8732 : NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
8733 : } else {
8734 : uri = nsIDocument::GetDocumentURI();
8735 : }
8736 0 : clone->mChannel = channel;
8737 0 : if (uri) {
8738 0 : clone->ResetToURI(uri, loadGroup, NodePrincipal());
8739 0 : }
8740 0 :
8741 : clone->SetContainer(mDocumentContainer);
8742 0 : }
8743 :
8744 0 : // Now ensure that our clone has the same URI, base URI, and principal as us.
8745 0 : // We do this after the mCreatingStaticClone block above, because that block
8746 0 : // can set the base URI to an incorrect value in cases when base URI
8747 : // information came from the channel. So we override explicitly, and do it
8748 : // for all these properties, in case ResetToURI messes with any of the rest of
8749 : // them.
8750 : clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
8751 0 : clone->SetChromeXHRDocURI(mChromeXHRDocURI);
8752 0 : clone->SetPrincipal(NodePrincipal());
8753 0 : clone->mDocumentBaseURI = mDocumentBaseURI;
8754 : clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
8755 0 :
8756 : bool hasHadScriptObject = true;
8757 : nsIScriptGlobalObject* scriptObject =
8758 0 : GetScriptHandlingObject(hasHadScriptObject);
8759 : NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
8760 : if (mCreatingStaticClone) {
8761 : // If we're doing a static clone (print, print preview), then we're going to
8762 : // be setting a scope object after the clone. It's better to set it only
8763 0 : // once, so we don't do that here. However, we do want to act as if there is
8764 0 : // a script handling object. So we set mHasHadScriptHandlingObject.
8765 0 : clone->mHasHadScriptHandlingObject = true;
8766 0 : } else if (scriptObject) {
8767 0 : clone->SetScriptHandlingObject(scriptObject);
8768 0 : } else {
8769 0 : clone->SetScopeObject(GetScopeObject());
8770 : }
8771 : // Make the clone a data document
8772 0 : clone->SetLoadedAsData(true);
8773 0 :
8774 0 : // Misc state
8775 :
8776 : // State from nsIDocument
8777 0 : clone->mCharacterSet = mCharacterSet;
8778 0 : clone->mCharacterSetSource = mCharacterSetSource;
8779 : clone->mCompatMode = mCompatMode;
8780 : clone->mBidiOptions = mBidiOptions;
8781 : clone->mContentLanguage = mContentLanguage;
8782 : clone->SetContentTypeInternal(GetContentTypeInternal());
8783 : clone->mSecurityInfo = mSecurityInfo;
8784 136 :
8785 : // State from nsDocument
8786 136 : clone->mType = mType;
8787 136 : clone->mXMLDeclarationBits = mXMLDeclarationBits;
8788 : clone->mBaseTarget = mBaseTarget;
8789 :
8790 : // Preallocate attributes and child arrays
8791 0 : rv = clone->mChildren.EnsureCapacityToClone(mChildren, aPreallocateChildren);
8792 : NS_ENSURE_SUCCESS(rv, rv);
8793 272 :
8794 24 : return NS_OK;
8795 : }
8796 0 :
8797 0 : void
8798 : nsIDocument::SetReadyStateInternal(ReadyState rs)
8799 17 : {
8800 0 : mReadyState = rs;
8801 : if (rs == READYSTATE_UNINITIALIZED) {
8802 7 : // Transition back to uninitialized happens only to keep assertions happy
8803 0 : // right before readyState transitions to something else. Make this
8804 : // transition undetectable by Web content.
8805 0 : return;
8806 0 : }
8807 : if (mTiming) {
8808 : switch (rs) {
8809 : case READYSTATE_LOADING:
8810 1 : mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
8811 49 : break;
8812 : case READYSTATE_INTERACTIVE:
8813 : mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
8814 0 : break;
8815 : case READYSTATE_COMPLETE:
8816 : mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
8817 136 : break;
8818 0 : default:
8819 136 : NS_WARNING("Unexpected ReadyState value");
8820 : break;
8821 : }
8822 : }
8823 0 : // At the time of loading start, we don't have timing object, record time.
8824 : if (READYSTATE_LOADING == rs) {
8825 5 : mLoadingTimeStamp = mozilla::TimeStamp::Now();
8826 : }
8827 0 :
8828 0 : RecordNavigationTiming(rs);
8829 :
8830 0 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
8831 0 : new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
8832 : false, false);
8833 3 : asyncDispatcher->RunDOMEventWhenSafe();
8834 0 : }
8835 :
8836 2 : void
8837 : nsIDocument::GetReadyState(nsAString& aReadyState) const
8838 0 : {
8839 : switch(mReadyState) {
8840 : case READYSTATE_LOADING :
8841 0 : aReadyState.AssignLiteral(u"loading");
8842 : break;
8843 0 : case READYSTATE_INTERACTIVE :
8844 : aReadyState.AssignLiteral(u"interactive");
8845 0 : break;
8846 : case READYSTATE_COMPLETE :
8847 : aReadyState.AssignLiteral(u"complete");
8848 : break;
8849 0 : default:
8850 : aReadyState.AssignLiteral(u"uninitialized");
8851 0 : }
8852 0 : }
8853 0 :
8854 0 : static bool
8855 : SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
8856 : {
8857 0 : aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData));
8858 0 :
8859 : return true;
8860 : }
8861 0 :
8862 : void
8863 : nsIDocument::SuppressEventHandling(uint32_t aIncrease)
8864 0 : {
8865 0 : mEventsSuppressed += aIncrease;
8866 : UpdateFrameRequestCallbackSchedulingState();
8867 : for (uint32_t i = 0; i < aIncrease; ++i) {
8868 0 : ScriptLoader()->AddExecuteBlocker();
8869 : }
8870 :
8871 0 : EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
8872 0 : }
8873 0 :
8874 0 : static void
8875 : FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments,
8876 0 : bool aFireEvents)
8877 0 : {
8878 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
8879 0 : if (!fm)
8880 : return;
8881 :
8882 : for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
8883 : // NB: Don't bother trying to fire delayed events on documents that were
8884 : // closed before this event ran.
8885 : if (!aDocuments[i]->EventHandlingSuppressed()) {
8886 0 : fm->FireDelayedEvents(aDocuments[i]);
8887 : nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
8888 0 : if (shell) {
8889 0 : // Only fire events for active documents.
8890 0 : bool fire = aFireEvents &&
8891 0 : aDocuments[i]->GetInnerWindow() &&
8892 : aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
8893 0 : shell->FireOrClearDelayedEvents(fire);
8894 : }
8895 : }
8896 0 : }
8897 : }
8898 :
8899 : void
8900 : nsIDocument::PreloadPictureClosed()
8901 : {
8902 : MOZ_ASSERT(mPreloadPictureDepth > 0);
8903 : mPreloadPictureDepth--;
8904 0 : if (mPreloadPictureDepth == 0) {
8905 : mPreloadPictureFoundSource.SetIsVoid(true);
8906 : }
8907 : }
8908 0 :
8909 : void
8910 : nsIDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr,
8911 0 : const nsAString& aSizesAttr,
8912 0 : const nsAString& aTypeAttr,
8913 : const nsAString& aMediaAttr)
8914 0 : {
8915 : // Nested pictures are not valid syntax, so while we'll eventually load them,
8916 : // it's not worth tracking sources mixed between nesting levels to preload
8917 0 : // them effectively.
8918 : if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) {
8919 : // <picture> selects the first matching source, so if this returns a URI we
8920 0 : // needn't consider new sources until a new <picture> is encountered.
8921 : bool found =
8922 : HTMLImageElement::SelectSourceForTagWithAttrs(this, true, VoidString(),
8923 : aSrcsetAttr, aSizesAttr,
8924 : aTypeAttr, aMediaAttr,
8925 : mPreloadPictureFoundSource);
8926 0 : if (found && mPreloadPictureFoundSource.IsVoid()) {
8927 : // Found an empty source, which counts
8928 0 : mPreloadPictureFoundSource.SetIsVoid(false);
8929 : }
8930 : }
8931 0 : }
8932 0 :
8933 : already_AddRefed<nsIURI>
8934 : nsIDocument::ResolvePreloadImage(nsIURI *aBaseURI,
8935 0 : const nsAString& aSrcAttr,
8936 : const nsAString& aSrcsetAttr,
8937 0 : const nsAString& aSizesAttr,
8938 0 : bool *aIsImgSet)
8939 0 : {
8940 : nsString sourceURL;
8941 : bool isImgSet;
8942 : if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
8943 0 : // We're in a <picture> element and found a URI from a source previous to
8944 : // this image, use it.
8945 : sourceURL = mPreloadPictureFoundSource;
8946 : isImgSet = true;
8947 : } else {
8948 : // Otherwise try to use this <img> as a source
8949 : HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr,
8950 0 : aSrcsetAttr, aSizesAttr,
8951 0 : VoidString(), VoidString(),
8952 0 : sourceURL);
8953 0 : isImgSet = !aSrcsetAttr.IsEmpty();
8954 : }
8955 :
8956 : // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
8957 0 : if (sourceURL.IsEmpty()) {
8958 : return nullptr;
8959 : }
8960 :
8961 : // Construct into URI using passed baseURI (the parser may know of base URI
8962 0 : // changes that have not reached us)
8963 : nsresult rv;
8964 : nsCOMPtr<nsIURI> uri;
8965 : rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL,
8966 0 : this, aBaseURI);
8967 : if (NS_FAILED(rv)) {
8968 : return nullptr;
8969 : }
8970 :
8971 : *aIsImgSet = isImgSet;
8972 :
8973 : // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
8974 0 : // this this <picture> share the same <sources> (though this is not valid per
8975 0 : // spec)
8976 : return uri.forget();
8977 : }
8978 :
8979 0 : void
8980 0 : nsIDocument::MaybePreLoadImage(nsIURI* uri,
8981 : const nsAString &aCrossOriginAttr,
8982 : enum mozilla::net::ReferrerPolicy aReferrerPolicy,
8983 0 : bool aIsImgSet)
8984 0 : {
8985 : // Early exit if the img is already present in the img-cache
8986 : // which indicates that the "real" load has already started and
8987 0 : // that we shouldn't preload it.
8988 : if (nsContentUtils::IsImageInCache(uri, this)) {
8989 0 : return;
8990 : }
8991 :
8992 : nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
8993 : nsContentUtils::CORSModeToLoadImageFlags(
8994 : Element::StringToCORSMode(aCrossOriginAttr));
8995 :
8996 : nsContentPolicyType policyType =
8997 : aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET :
8998 0 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
8999 0 :
9000 0 : // Image not in cache - trigger preload
9001 : RefPtr<imgRequestProxy> request;
9002 : nsresult rv =
9003 : nsContentUtils::LoadImage(uri,
9004 : static_cast<nsINode*>(this),
9005 0 : this,
9006 0 : NodePrincipal(),
9007 : 0,
9008 : mDocumentURI, // uri of document used as referrer
9009 : aReferrerPolicy,
9010 : nullptr, // no observer
9011 0 : loadFlags,
9012 : NS_LITERAL_STRING("img"),
9013 0 : getter_AddRefs(request),
9014 0 : policyType);
9015 0 :
9016 : // Pin image-reference to avoid evicting it from the img-cache before
9017 : // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
9018 : // unlink
9019 : if (NS_SUCCEEDED(rv)) {
9020 : mPreloadingImages.Put(uri, request.forget());
9021 : }
9022 : }
9023 :
9024 : void
9025 0 : nsIDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode)
9026 0 : {
9027 : NS_MutateURI mutator(aOrigURI);
9028 0 : if (NS_FAILED(mutator.GetStatus())) {
9029 : return;
9030 : }
9031 0 :
9032 0 : // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
9033 0 : // which ignores the path and uses only the origin. The other is for the
9034 0 : // document mPreloadedPreconnects de-duplication hash. Anonymous vs
9035 : // non-Anonymous preconnects create different connections on the wire and
9036 : // therefore should not be considred duplicates of each other and we
9037 0 : // normalize the path before putting it in the hash to accomplish that.
9038 0 :
9039 0 : if (aCORSMode == CORS_ANONYMOUS) {
9040 : mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/anonymous"));
9041 0 : } else {
9042 : mutator.SetPathQueryRef(NS_LITERAL_CSTRING("/"));
9043 : }
9044 0 :
9045 0 : nsCOMPtr<nsIURI> uri;
9046 0 : nsresult rv = mutator.Finalize(uri);
9047 : if (NS_FAILED(rv)) {
9048 : return;
9049 0 : }
9050 0 :
9051 : auto entry = mPreloadedPreconnects.LookupForAdd(uri);
9052 0 : if (entry) {
9053 : return; // we found an existing entry
9054 : }
9055 : entry.OrInsert([] () { return true; });
9056 :
9057 0 : nsCOMPtr<nsISpeculativeConnect>
9058 : speculator(do_QueryInterface(nsContentUtils::GetIOService()));
9059 : if (!speculator) {
9060 : return;
9061 0 : }
9062 0 :
9063 0 : if (aCORSMode == CORS_ANONYMOUS) {
9064 0 : speculator->SpeculativeAnonymousConnect2(uri, NodePrincipal(), nullptr);
9065 : } else {
9066 0 : speculator->SpeculativeConnect2(uri, NodePrincipal(), nullptr);
9067 : }
9068 : }
9069 0 :
9070 : void
9071 : nsIDocument::ForgetImagePreload(nsIURI* aURI)
9072 46 : {
9073 : // Checking count is faster than hashing the URI in the common
9074 46 : // case of empty table.
9075 3 : if (mPreloadingImages.Count() != 0) {
9076 0 : nsCOMPtr<imgIRequest> req;
9077 : mPreloadingImages.Remove(aURI, getter_AddRefs(req));
9078 0 : if (req) {
9079 : // Make sure to cancel the request so imagelib knows it's gone.
9080 : req->CancelAndForgetObserver(NS_BINDING_ABORTED);
9081 : }
9082 0 : }
9083 43 : }
9084 30 :
9085 : void
9086 0 : nsIDocument::UpdateDocumentStates(EventStates aChangedStates)
9087 : {
9088 : if (aChangedStates.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
9089 46 : if (IsDocumentRightToLeft()) {
9090 : mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9091 : } else {
9092 : mDocumentState &= ~NS_DOCUMENT_STATE_RTL_LOCALE;
9093 : }
9094 : }
9095 :
9096 : if (aChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
9097 0 : if (IsTopLevelWindowInactive()) {
9098 0 : mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9099 : } else {
9100 : mDocumentState &= ~NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9101 0 : }
9102 : }
9103 0 : }
9104 :
9105 : namespace {
9106 :
9107 0 : /**
9108 : * Stub for LoadSheet(), since all we want is to get the sheet into
9109 : * the CSSLoader's style cache
9110 : */
9111 : class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
9112 0 : ~StubCSSLoaderObserver() {}
9113 : public:
9114 : NS_IMETHOD
9115 : StyleSheetLoaded(StyleSheet*, bool, nsresult) override
9116 : {
9117 : return NS_OK;
9118 : }
9119 0 : NS_DECL_ISUPPORTS
9120 : };
9121 : NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
9122 0 :
9123 : } // namespace
9124 :
9125 : void
9126 : nsIDocument::PreloadStyle(nsIURI* uri,
9127 0 : const Encoding* aEncoding,
9128 : const nsAString& aCrossOriginAttr,
9129 0 : const enum mozilla::net::ReferrerPolicy aReferrerPolicy,
9130 0 : const nsAString& aIntegrity)
9131 : {
9132 : // The CSSLoader will retain this object after we return.
9133 0 : nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
9134 :
9135 : // Charset names are always ASCII.
9136 : CSSLoader()->LoadSheet(uri,
9137 0 : true,
9138 0 : NodePrincipal(),
9139 0 : aEncoding,
9140 : obs,
9141 : Element::StringToCORSMode(aCrossOriginAttr),
9142 : aReferrerPolicy,
9143 : aIntegrity);
9144 : }
9145 0 :
9146 0 : nsresult
9147 : nsIDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
9148 0 : RefPtr<mozilla::StyleSheet>* aSheet)
9149 0 : {
9150 0 : css::SheetParsingMode mode =
9151 : isAgentSheet ? css::eAgentSheetFeatures
9152 0 : : css::eAuthorSheetFeatures;
9153 : return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, aSheet);
9154 0 : }
9155 0 :
9156 : class nsDelayedEventDispatcher : public Runnable
9157 : {
9158 : public:
9159 : explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
9160 : : mozilla::Runnable("nsDelayedEventDispatcher")
9161 : {
9162 : mDocuments.SwapElements(aDocuments);
9163 0 : }
9164 : virtual ~nsDelayedEventDispatcher() {}
9165 0 :
9166 0 : NS_IMETHOD Run() override
9167 0 : {
9168 : FireOrClearDelayedEvents(mDocuments, true);
9169 : return NS_OK;
9170 0 : }
9171 :
9172 0 : private:
9173 0 : nsTArray<nsCOMPtr<nsIDocument>> mDocuments;
9174 0 : };
9175 :
9176 : static bool
9177 : GetAndUnsuppressSubDocuments(nsIDocument* aDocument, void* aData)
9178 0 : {
9179 : if (aDocument->EventHandlingSuppressed() > 0) {
9180 0 : aDocument->DecreaseEventSuppression();
9181 0 : aDocument->ScriptLoader()->RemoveExecuteBlocker();
9182 : }
9183 0 :
9184 0 : auto* docs = static_cast<nsTArray<nsCOMPtr<nsIDocument>>*>(aData);
9185 0 :
9186 0 : docs->AppendElement(aDocument);
9187 : aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
9188 0 : return true;
9189 : }
9190 0 :
9191 : void
9192 : nsIDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents)
9193 0 : {
9194 : nsTArray<nsCOMPtr<nsIDocument>> documents;
9195 0 : GetAndUnsuppressSubDocuments(this, &documents);
9196 :
9197 : if (aFireEvents) {
9198 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
9199 0 : nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
9200 : Dispatch(TaskCategory::Other, ded.forget());
9201 0 : } else {
9202 0 : FireOrClearDelayedEvents(documents, false);
9203 : }
9204 0 : }
9205 :
9206 0 : nsISupports*
9207 0 : nsIDocument::GetCurrentContentSink()
9208 0 : {
9209 0 : return mParser ? mParser->GetContentSink() : nullptr;
9210 : }
9211 :
9212 : nsIDocument*
9213 : nsIDocument::GetTemplateContentsOwner()
9214 : {
9215 : if (!mTemplateContentsOwner) {
9216 0 : bool hasHadScriptObject = true;
9217 0 : nsIScriptGlobalObject* scriptObject =
9218 : GetScriptHandlingObject(hasHadScriptObject);
9219 0 :
9220 0 : nsCOMPtr<nsIDocument> document;
9221 : nsresult rv = NS_NewDOMDocument(getter_AddRefs(document),
9222 0 : EmptyString(), // aNamespaceURI
9223 : EmptyString(), // aQualifiedName
9224 0 : nullptr, // aDoctype
9225 0 : nsIDocument::GetDocumentURI(),
9226 : nsIDocument::GetDocBaseURI(),
9227 : NodePrincipal(),
9228 0 : true, // aLoadedAsData
9229 : scriptObject, // aEventObject
9230 : DocumentFlavorHTML);
9231 : NS_ENSURE_SUCCESS(rv, nullptr);
9232 :
9233 0 : mTemplateContentsOwner = document;
9234 : NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
9235 :
9236 0 : nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
9237 :
9238 : if (!scriptObject) {
9239 : mTemplateContentsOwner->SetScopeObject(GetScopeObject());
9240 0 : }
9241 :
9242 0 : doc->mHasHadScriptHandlingObject = hasHadScriptObject;
9243 0 :
9244 : // Set |doc| as the template contents owner of itself so that
9245 : // |doc| is the template contents owner of template elements created
9246 : // by |doc|.
9247 0 : doc->mTemplateContentsOwner = doc;
9248 0 : }
9249 :
9250 : return mTemplateContentsOwner;
9251 : }
9252 :
9253 0 : static already_AddRefed<nsPIDOMWindowOuter>
9254 0 : FindTopWindowForElement(Element* element)
9255 : {
9256 0 : nsIDocument* document = element->OwnerDoc();
9257 : if (!document) {
9258 : return nullptr;
9259 : }
9260 :
9261 : nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
9262 : if (!window) {
9263 0 : return nullptr;
9264 : }
9265 :
9266 0 : // Trying to find the top window (equivalent to window.top).
9267 : if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetTop()) {
9268 0 : window = top.forget();
9269 : }
9270 0 : return window.forget();
9271 : }
9272 0 :
9273 : /**
9274 0 : * nsAutoFocusEvent is used to dispatch a focus event for an
9275 : * nsGenericHTMLFormElement with the autofocus attribute enabled.
9276 : */
9277 0 : class nsAutoFocusEvent : public Runnable
9278 0 : {
9279 : public:
9280 : explicit nsAutoFocusEvent(already_AddRefed<Element>&& aElement,
9281 : already_AddRefed<nsPIDOMWindowOuter>&& aTopWindow)
9282 : : mozilla::Runnable("nsAutoFocusEvent")
9283 : , mElement(aElement)
9284 : , mTopWindow(aTopWindow)
9285 0 : {
9286 : }
9287 :
9288 : NS_IMETHOD Run() override
9289 0 : {
9290 0 : nsCOMPtr<nsPIDOMWindowOuter> currentTopWindow =
9291 0 : FindTopWindowForElement(mElement);
9292 : if (currentTopWindow != mTopWindow) {
9293 : // The element's top window changed from when the event was queued.
9294 : // Don't take away focus from an unrelated window.
9295 : return NS_OK;
9296 : }
9297 :
9298 : // Don't steal focus from the user.
9299 0 : if (mTopWindow->GetFocusedElement()) {
9300 : return NS_OK;
9301 0 : }
9302 :
9303 : mozilla::ErrorResult rv;
9304 : mElement->Focus(rv);
9305 : return rv.StealNSResult();
9306 0 : }
9307 : private:
9308 : nsCOMPtr<Element> mElement;
9309 : nsCOMPtr<nsPIDOMWindowOuter> mTopWindow;
9310 : };
9311 :
9312 0 : void
9313 0 : nsIDocument::SetAutoFocusElement(Element* aAutoFocusElement)
9314 : {
9315 : if (mAutoFocusFired) {
9316 : // Too late.
9317 0 : return;
9318 : }
9319 19 :
9320 0 : if (mAutoFocusElement) {
9321 : // The spec disallows multiple autofocus elements, so we consider only the
9322 : // first one to preserve the old behavior.
9323 0 : return;
9324 : }
9325 :
9326 : mAutoFocusElement = do_GetWeakReference(aAutoFocusElement);
9327 : TriggerAutoFocus();
9328 : }
9329 76 :
9330 19 : void
9331 0 : nsIDocument::TriggerAutoFocus()
9332 : {
9333 : if (mAutoFocusFired) {
9334 0 : return;
9335 0 : }
9336 0 :
9337 : if (!mPresShell || !mPresShell->DidInitialize()) {
9338 : // Delay autofocus until frames are constructed so that we don't thrash
9339 : // style and layout calculations.
9340 : return;
9341 0 : }
9342 0 :
9343 0 : nsCOMPtr<Element> autoFocusElement = do_QueryReferent(mAutoFocusElement);
9344 : if (autoFocusElement && autoFocusElement->OwnerDoc() == this) {
9345 : mAutoFocusFired = true;
9346 :
9347 0 : nsCOMPtr<nsPIDOMWindowOuter> topWindow =
9348 0 : FindTopWindowForElement(autoFocusElement);
9349 0 : if (!topWindow) {
9350 : return;
9351 : }
9352 :
9353 : // NOTE: This may be removed in the future since the spec technically
9354 49 : // allows autofocus after load.
9355 : nsCOMPtr<nsIDocument> topDoc = topWindow->GetExtantDoc();
9356 49 : if (topDoc && topDoc->GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE) {
9357 0 : return;
9358 : }
9359 :
9360 0 : nsCOMPtr<nsIRunnable> event =
9361 : new nsAutoFocusEvent(autoFocusElement.forget(), topWindow.forget());
9362 : nsresult rv = NS_DispatchToCurrentThread(event.forget());
9363 : NS_ENSURE_SUCCESS_VOID(rv);
9364 : }
9365 : }
9366 :
9367 49 : void
9368 49 : nsIDocument::SetScrollToRef(nsIURI* aDocumentURI)
9369 0 : {
9370 : if (!aDocumentURI) {
9371 : return;
9372 : }
9373 1 :
9374 : nsAutoCString ref;
9375 49 :
9376 49 : // Since all URI's that pass through here aren't URL's we can't
9377 : // rely on the nsIURI implementation for providing a way for
9378 49 : // finding the 'ref' part of the URI, we'll haveto revert to
9379 0 : // string routines for finding the data past '#'
9380 :
9381 0 : nsresult rv = aDocumentURI->GetSpec(ref);
9382 : if (NS_FAILED(rv)) {
9383 : Unused << aDocumentURI->GetRef(mScrollToRef);
9384 : return;
9385 : }
9386 58 :
9387 : nsReadingIterator<char> start, end;
9388 58 :
9389 0 : ref.BeginReading(start);
9390 0 : ref.EndReading(end);
9391 0 :
9392 : if (FindCharInReadable('#', start, end)) {
9393 : ++start; // Skip over the '#'
9394 :
9395 : mScrollToRef = Substring(start, end);
9396 58 : }
9397 : }
9398 :
9399 : void
9400 0 : nsIDocument::ScrollToRef()
9401 0 : {
9402 0 : if (mScrolledToRefAlready) {
9403 : nsCOMPtr<nsIPresShell> shell = GetShell();
9404 : if (shell) {
9405 0 : shell->ScrollToAnchor();
9406 : }
9407 0 : return;
9408 : }
9409 0 :
9410 : if (mScrollToRef.IsEmpty()) {
9411 : return;
9412 : }
9413 :
9414 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
9415 0 : if (shell) {
9416 0 : nsresult rv = NS_ERROR_FAILURE;
9417 0 : // We assume that the bytes are in UTF-8, as it says in the spec:
9418 : // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9419 0 : NS_ConvertUTF8toUTF16 ref(mScrollToRef);
9420 0 : // Check an empty string which might be caused by the UTF-8 conversion
9421 0 : if (!ref.IsEmpty()) {
9422 0 : // Note that GoToAnchor will handle flushing layout as needed.
9423 : rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9424 0 : } else {
9425 0 : rv = NS_ERROR_FAILURE;
9426 0 : }
9427 :
9428 : if (NS_FAILED(rv)) {
9429 : char* tmpstr = ToNewCString(mScrollToRef);
9430 : if (!tmpstr) {
9431 0 : return;
9432 0 : }
9433 0 : nsUnescape(tmpstr);
9434 0 : nsAutoCString unescapedRef;
9435 0 : unescapedRef.Assign(tmpstr);
9436 : free(tmpstr);
9437 :
9438 : NS_ConvertUTF8toUTF16 utf16Str(unescapedRef);
9439 0 : if (!utf16Str.IsEmpty()) {
9440 0 : rv = shell->GoToAnchor(utf16Str, mChangeScrollPosWhenScrollingToRef);
9441 : }
9442 :
9443 : // If UTF-8 URI failed then try to assume the string as a
9444 : // document's charset.
9445 : if (NS_FAILED(rv)) {
9446 1 : const Encoding* encoding = GetDocumentCharacterSet();
9447 : rv = encoding->DecodeWithoutBOMHandling(unescapedRef, ref);
9448 2 : if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
9449 2 : rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9450 : }
9451 1 : }
9452 0 : }
9453 : if (NS_SUCCEEDED(rv)) {
9454 : mScrolledToRefAlready = true;
9455 0 : }
9456 : }
9457 0 : }
9458 :
9459 : void
9460 0 : nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
9461 0 : {
9462 : if (!mActivityObservers) {
9463 : mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports> >();
9464 0 : }
9465 0 : mActivityObservers->PutEntry(aSupports);
9466 : }
9467 :
9468 : bool
9469 0 : nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
9470 : {
9471 : if (!mActivityObservers) {
9472 222 : return false;
9473 : }
9474 : nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports);
9475 5 : if (!entry) {
9476 0 : return false;
9477 1 : }
9478 : mActivityObservers->RemoveEntry(entry);
9479 : return true;
9480 : }
9481 :
9482 8 : void
9483 : nsIDocument::EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
9484 16 : void* aData)
9485 : {
9486 : if (!mActivityObservers)
9487 : return;
9488 0 :
9489 : for (auto iter = mActivityObservers->ConstIter(); !iter.Done();
9490 8 : iter.Next()) {
9491 : aEnumerator(iter.Get()->GetKey(), aData);
9492 0 : }
9493 : }
9494 0 :
9495 : void
9496 : nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
9497 1 : {
9498 0 : if (aLink->HasPendingLinkUpdate()) {
9499 : return;
9500 : }
9501 0 :
9502 : aLink->SetHasPendingLinkUpdate();
9503 1 :
9504 : if (!mHasLinksToUpdateRunnable && !mFlushingPendingLinkUpdates) {
9505 : nsCOMPtr<nsIRunnable> event =
9506 8 : NewRunnableMethod("nsIDocument::FlushPendingLinkUpdatesFromRunnable",
9507 : this,
9508 : &nsIDocument::FlushPendingLinkUpdatesFromRunnable);
9509 : // Do this work in a second in the worst case.
9510 0 : nsresult rv =
9511 : NS_IdleDispatchToCurrentThread(event.forget(), 1000);
9512 1 : if (NS_FAILED(rv)) {
9513 1 : // If during shutdown posting a runnable doesn't succeed, we probably
9514 0 : // don't need to update link states.
9515 1 : return;
9516 : }
9517 : mHasLinksToUpdateRunnable = true;
9518 0 : }
9519 :
9520 1 : mLinksToUpdate.InfallibleAppend(aLink);
9521 0 : }
9522 :
9523 : void
9524 0 : nsIDocument::FlushPendingLinkUpdatesFromRunnable()
9525 1 : {
9526 : MOZ_ASSERT(mHasLinksToUpdateRunnable);
9527 3 : mHasLinksToUpdateRunnable = false;
9528 0 : FlushPendingLinkUpdates();
9529 0 : }
9530 16 :
9531 0 : void
9532 0 : nsIDocument::FlushPendingLinkUpdates()
9533 0 : {
9534 0 : if (mFlushingPendingLinkUpdates) {
9535 0 : return;
9536 : }
9537 :
9538 : auto restore = MakeScopeExit([&] { mFlushingPendingLinkUpdates = false; });
9539 : mFlushingPendingLinkUpdates = true;
9540 :
9541 : while (!mLinksToUpdate.IsEmpty()) {
9542 : LinksToUpdateList links(std::move(mLinksToUpdate));
9543 0 : for (auto iter = links.Iter(); !iter.Done(); iter.Next()) {
9544 : Link* link = iter.Get();
9545 0 : Element* element = link->GetElement();
9546 0 : if (element->OwnerDoc() == this) {
9547 : link->ClearHasPendingLinkUpdate();
9548 : if (element->IsInComposedDoc()) {
9549 0 : element->UpdateLinkState(link->LinkState());
9550 0 : }
9551 0 : }
9552 0 : }
9553 0 : }
9554 : }
9555 0 :
9556 0 : already_AddRefed<nsIDocument>
9557 : nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
9558 0 : {
9559 : nsDocument* thisAsDoc = static_cast<nsDocument*>(this);
9560 0 : mCreatingStaticClone = true;
9561 0 :
9562 0 : // Make document use different container during cloning.
9563 0 : RefPtr<nsDocShell> originalShell = mDocumentContainer.get();
9564 0 : SetContainer(static_cast<nsDocShell*>(aCloneContainer));
9565 : ErrorResult rv;
9566 0 : nsCOMPtr<nsINode> clonedNode = thisAsDoc->CloneNode(true, rv);
9567 : SetContainer(originalShell);
9568 :
9569 0 : RefPtr<nsDocument> clonedDoc;
9570 : if (rv.Failed()) {
9571 0 : // Don't return yet; we need to reset mCreatingStaticClone
9572 0 : rv.SuppressException();
9573 0 : } else {
9574 0 : nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode);
9575 0 : if (tmp) {
9576 : clonedDoc = static_cast<nsDocument*>(tmp.get());
9577 0 : if (IsStaticDocument()) {
9578 0 : clonedDoc->mOriginalDocument = mOriginalDocument;
9579 : } else {
9580 0 : clonedDoc->mOriginalDocument = this;
9581 0 : }
9582 :
9583 : clonedDoc->mOriginalDocument->mStaticCloneCount++;
9584 :
9585 : size_t sheetsCount = SheetCount();
9586 : for (size_t i = 0; i < sheetsCount; ++i) {
9587 : RefPtr<StyleSheet> sheet = SheetAt(i);
9588 0 : if (sheet) {
9589 0 : if (sheet->IsApplicable()) {
9590 0 : RefPtr<StyleSheet> clonedSheet =
9591 : sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9592 0 : NS_WARNING_ASSERTION(clonedSheet,
9593 0 : "Cloning a stylesheet didn't work!");
9594 : if (clonedSheet) {
9595 0 : clonedDoc->AddStyleSheet(clonedSheet);
9596 0 : }
9597 : }
9598 : }
9599 : }
9600 :
9601 : // Iterate backwards to maintain order
9602 : for (StyleSheet* sheet : Reversed(thisAsDoc->mOnDemandBuiltInUASheets)) {
9603 0 : if (sheet) {
9604 0 : if (sheet->IsApplicable()) {
9605 : RefPtr<StyleSheet> clonedSheet =
9606 : sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9607 : NS_WARNING_ASSERTION(clonedSheet,
9608 0 : "Cloning a stylesheet didn't work!");
9609 : if (clonedSheet) {
9610 0 : clonedDoc->AddOnDemandBuiltInUASheet(clonedSheet);
9611 0 : }
9612 0 : }
9613 0 : }
9614 : }
9615 0 : }
9616 0 : }
9617 : mCreatingStaticClone = false;
9618 : return clonedDoc.forget();
9619 0 : }
9620 :
9621 : void
9622 2 : nsIDocument::UnlinkOriginalDocumentIfStatic()
9623 : {
9624 : if (IsStaticDocument() && mOriginalDocument) {
9625 : MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0);
9626 0 : mOriginalDocument->mStaticCloneCount--;
9627 : mOriginalDocument = nullptr;
9628 : }
9629 6 : MOZ_ASSERT(!mOriginalDocument);
9630 0 : }
9631 2 :
9632 : nsresult
9633 0 : nsIDocument::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
9634 0 : int32_t *aHandle)
9635 : {
9636 : if (mFrameRequestCallbackCounter == INT32_MAX) {
9637 : // Can't increment without overflowing; bail out
9638 0 : return NS_ERROR_NOT_AVAILABLE;
9639 : }
9640 : int32_t newHandle = ++mFrameRequestCallbackCounter;
9641 0 :
9642 0 : DebugOnly<FrameRequest*> request =
9643 : mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
9644 0 : NS_ASSERTION(request, "This is supposed to be infallible!");
9645 : UpdateFrameRequestCallbackSchedulingState();
9646 :
9647 0 : *aHandle = newHandle;
9648 : return NS_OK;
9649 : }
9650 :
9651 : void
9652 : nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
9653 : {
9654 : // mFrameRequestCallbacks is stored sorted by handle
9655 0 : if (mFrameRequestCallbacks.RemoveElementSorted(aHandle)) {
9656 0 : UpdateFrameRequestCallbackSchedulingState();
9657 0 : }
9658 0 : }
9659 0 :
9660 0 : nsresult
9661 0 : nsIDocument::GetStateObject(nsIVariant** aState)
9662 : {
9663 0 : // Get the document's current state object. This is the object backing both
9664 0 : // history.state and popStateEvent.state.
9665 : //
9666 : // mStateObjectContainer may be null; this just means that there's no
9667 0 : // current state object.
9668 0 :
9669 : if (!mStateObjectCached && mStateObjectContainer) {
9670 : AutoJSContext cx;
9671 : nsIGlobalObject* sgo = GetScopeObject();
9672 0 : NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
9673 : JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
9674 56 : NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
9675 73 : JSAutoRealm ar(cx, global);
9676 0 :
9677 : mStateObjectContainer->
9678 0 : DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
9679 : }
9680 :
9681 0 : NS_IF_ADDREF(*aState = mStateObjectCached);
9682 : return NS_OK;
9683 0 : }
9684 :
9685 : void
9686 : nsIDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
9687 0 : {
9688 0 : mTiming = aTiming;
9689 0 : if (!mLoadingTimeStamp.IsNull() && mTiming) {
9690 : mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(), mLoadingTimeStamp);
9691 0 : }
9692 0 : }
9693 :
9694 : Element*
9695 : nsIDocument::FindImageMap(const nsAString& aUseMapValue)
9696 0 : {
9697 : if (aUseMapValue.IsEmpty()) {
9698 0 : return nullptr;
9699 : }
9700 :
9701 : nsAString::const_iterator start, end;
9702 0 : aUseMapValue.BeginReading(start);
9703 : aUseMapValue.EndReading(end);
9704 0 :
9705 0 : int32_t hash = aUseMapValue.FindChar('#');
9706 : if (hash < 0) {
9707 : return nullptr;
9708 0 : }
9709 0 : // aUsemap contains a '#', set start to point right after the '#'
9710 0 : start.advance(hash + 1);
9711 0 :
9712 0 : if (start == end) {
9713 0 : return nullptr; // aUsemap == "#"
9714 0 : }
9715 :
9716 0 : const nsAString& mapName = Substring(start, end);
9717 :
9718 : if (!mImageMaps) {
9719 : mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
9720 : }
9721 :
9722 : uint32_t i, n = mImageMaps->Length(true);
9723 : nsString name;
9724 : for (i = 0; i < n; ++i) {
9725 : nsIContent* map = mImageMaps->Item(i);
9726 : if (map->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
9727 : eCaseMatters) ||
9728 : map->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, mapName,
9729 : eCaseMatters)) {
9730 : return map->AsElement();
9731 : }
9732 : }
9733 :
9734 : return nullptr;
9735 : }
9736 :
9737 : #define DEPRECATED_OPERATION(_op) #_op "Warning",
9738 0 : static const char* kDeprecationWarnings[] = {
9739 : #include "nsDeprecatedOperationList.h"
9740 0 : nullptr
9741 : };
9742 : #undef DEPRECATED_OPERATION
9743 :
9744 : #define DOCUMENT_WARNING(_op) #_op "Warning",
9745 : static const char* kDocumentWarnings[] = {
9746 0 : #include "nsDocumentWarningList.h"
9747 : nullptr
9748 : };
9749 : #undef DOCUMENT_WARNING
9750 :
9751 0 : static UseCounter
9752 : OperationToUseCounter(nsIDocument::DeprecatedOperations aOperation)
9753 0 : {
9754 : switch(aOperation) {
9755 : #define DEPRECATED_OPERATION(_op) \
9756 : case nsIDocument::e##_op: return eUseCounter_##_op;
9757 0 : #include "nsDeprecatedOperationList.h"
9758 : #undef DEPRECATED_OPERATION
9759 : default:
9760 0 : MOZ_CRASH();
9761 0 : }
9762 : }
9763 :
9764 0 : bool
9765 : nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const
9766 : {
9767 : return mDeprecationWarnedAbout[aOperation];
9768 0 : }
9769 :
9770 0 : void
9771 : nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
9772 0 : bool asError /* = false */) const
9773 0 : {
9774 0 : MOZ_ASSERT(NS_IsMainThread());
9775 0 : if (HasWarnedAbout(aOperation)) {
9776 : return;
9777 0 : }
9778 : mDeprecationWarnedAbout[aOperation] = true;
9779 : // Don't count deprecated operations for about pages since those pages
9780 : // are almost in our control, and we always need to remove uses there
9781 0 : // before we remove the operation itself anyway.
9782 : if (!IsAboutPage()) {
9783 0 : const_cast<nsIDocument*>(this)->
9784 : SetDocumentAndPageUseCounter(OperationToUseCounter(aOperation));
9785 : }
9786 : uint32_t flags = asError ? nsIScriptError::errorFlag
9787 0 : : nsIScriptError::warningFlag;
9788 : nsContentUtils::ReportToConsole(flags,
9789 : NS_LITERAL_CSTRING("DOM Core"), this,
9790 : nsContentUtils::eDOM_PROPERTIES,
9791 : kDeprecationWarnings[aOperation]);
9792 0 : }
9793 0 :
9794 : bool
9795 : nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const
9796 0 : {
9797 0 : return mDocWarningWarnedAbout[aWarning];
9798 0 : }
9799 0 :
9800 0 : void
9801 : nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
9802 : bool asError /* = false */,
9803 : const char16_t **aParams /* = nullptr */,
9804 0 : uint32_t aParamsLength /* = 0 */) const
9805 : {
9806 : MOZ_ASSERT(NS_IsMainThread());
9807 : if (HasWarnedAbout(aWarning)) {
9808 0 : return;
9809 : }
9810 88 : mDocWarningWarnedAbout[aWarning] = true;
9811 27 : uint32_t flags = asError ? nsIScriptError::errorFlag
9812 : : nsIScriptError::warningFlag;
9813 88 : nsContentUtils::ReportToConsole(flags,
9814 : NS_LITERAL_CSTRING("DOM Core"), this,
9815 : nsContentUtils::eDOM_PROPERTIES,
9816 : kDocumentWarnings[aWarning],
9817 0 : aParams,
9818 : aParamsLength);
9819 : }
9820 0 :
9821 0 : mozilla::dom::ImageTracker*
9822 0 : nsIDocument::ImageTracker()
9823 0 : {
9824 : if (!mImageTracker) {
9825 : mImageTracker = new mozilla::dom::ImageTracker;
9826 : }
9827 0 : return mImageTracker;
9828 : }
9829 0 :
9830 0 : static bool
9831 0 : AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
9832 : {
9833 0 : nsTArray<nsIObjectLoadingContent*>* plugins =
9834 0 : reinterpret_cast<nsTArray<nsIObjectLoadingContent*>*>(userArg);
9835 : MOZ_ASSERT(plugins);
9836 : aDocument->GetPlugins(*plugins);
9837 0 : return true;
9838 : }
9839 3 :
9840 0 : void
9841 0 : nsIDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
9842 0 : {
9843 : aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
9844 0 : for (auto iter = mPlugins.ConstIter(); !iter.Done(); iter.Next()) {
9845 : aPlugins.AppendElement(iter.Get()->GetKey());
9846 : }
9847 0 : EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
9848 : }
9849 :
9850 : void
9851 : nsIDocument::NotifyMediaFeatureValuesChanged()
9852 : {
9853 : for (auto iter = mResponsiveContent.ConstIter(); !iter.Done();
9854 : iter.Next()) {
9855 : RefPtr<HTMLImageElement> imageElement = iter.Get()->GetKey();
9856 : imageElement->MediaFeatureValuesChanged();
9857 : }
9858 : }
9859 :
9860 : already_AddRefed<Touch>
9861 : nsIDocument::CreateTouch(nsGlobalWindowInner* aView,
9862 : EventTarget* aTarget,
9863 : int32_t aIdentifier,
9864 0 : int32_t aPageX, int32_t aPageY,
9865 0 : int32_t aScreenX, int32_t aScreenY,
9866 : int32_t aClientX, int32_t aClientY,
9867 : int32_t aRadiusX, int32_t aRadiusY,
9868 : float aRotationAngle,
9869 0 : float aForce)
9870 : {
9871 0 : RefPtr<Touch> touch = new Touch(aTarget,
9872 0 : aIdentifier,
9873 : aPageX, aPageY,
9874 : aScreenX, aScreenY,
9875 : aClientX, aClientY,
9876 0 : aRadiusX, aRadiusY,
9877 : aRotationAngle,
9878 : aForce);
9879 0 : return touch.forget();
9880 0 : }
9881 0 :
9882 0 : already_AddRefed<TouchList>
9883 : nsIDocument::CreateTouchList()
9884 0 : {
9885 : RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9886 : return retval.forget();
9887 : }
9888 0 :
9889 : already_AddRefed<TouchList>
9890 0 : nsIDocument::CreateTouchList(Touch& aTouch,
9891 0 : const Sequence<OwningNonNull<Touch> >& aTouches)
9892 0 : {
9893 : RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9894 0 : retval->Append(&aTouch);
9895 : for (uint32_t i = 0; i < aTouches.Length(); ++i) {
9896 : retval->Append(aTouches[i].get());
9897 : }
9898 0 : return retval.forget();
9899 : }
9900 0 :
9901 0 : already_AddRefed<TouchList>
9902 0 : nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
9903 : {
9904 0 : RefPtr<TouchList> retval = new TouchList(ToSupports(this));
9905 : for (uint32_t i = 0; i < aTouches.Length(); ++i) {
9906 0 : retval->Append(aTouches[i].get());
9907 0 : }
9908 : return retval.forget();
9909 : }
9910 :
9911 0 : already_AddRefed<nsDOMCaretPosition>
9912 : nsIDocument::CaretPositionFromPoint(float aX, float aY)
9913 : {
9914 0 : nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
9915 : nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
9916 : nsPoint pt(x, y);
9917 :
9918 0 : FlushPendingNotifications(FlushType::Layout);
9919 0 :
9920 0 : nsIPresShell *ps = GetShell();
9921 : if (!ps) {
9922 : return nullptr;
9923 : }
9924 :
9925 0 : nsIFrame *rootFrame = ps->GetRootFrame();
9926 0 :
9927 : // XUL docs, unlike HTML, have no frame tree until everything's done loading
9928 0 : if (!rootFrame) {
9929 : return nullptr;
9930 0 : }
9931 :
9932 : nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
9933 0 : nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
9934 : if (!ptFrame) {
9935 0 : return nullptr;
9936 0 : }
9937 0 :
9938 0 : // We require frame-relative coordinates for GetContentOffsetsFromPoint.
9939 0 : nsPoint aOffset;
9940 0 : nsCOMPtr<nsIWidget> widget = nsContentUtils::GetWidget(ps, &aOffset);
9941 0 : LayoutDeviceIntPoint refPoint =
9942 0 : nsContentUtils::ToWidgetPoint(CSSPoint(aX, aY), aOffset, GetPresContext());
9943 0 : nsPoint adjustedPoint =
9944 0 : nsLayoutUtils::GetEventCoordinatesRelativeTo(widget, refPoint, ptFrame);
9945 0 :
9946 : nsFrame::ContentOffsets offsets =
9947 : ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
9948 :
9949 0 : nsCOMPtr<nsIContent> node = offsets.content;
9950 0 : uint32_t offset = offsets.offset;
9951 0 : nsCOMPtr<nsIContent> anonNode = node;
9952 : bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
9953 : if (nodeIsAnonymous) {
9954 0 : node = ptFrame->GetContent();
9955 0 : nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
9956 : HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(nonanon);
9957 : nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
9958 0 : nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
9959 : if (textFrame || numberFrame) {
9960 0 : // If the anonymous content node has a child, then we need to make sure
9961 0 : // that we get the appropriate child, as otherwise the offset may not be
9962 : // correct when we construct a range for it.
9963 : nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
9964 : if (firstChild) {
9965 0 : anonNode = firstChild;
9966 0 : }
9967 0 :
9968 : if (textArea) {
9969 0 : offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
9970 : }
9971 :
9972 : node = nonanon;
9973 0 : } else {
9974 : node = nullptr;
9975 : offset = 0;
9976 0 : }
9977 : }
9978 :
9979 : RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
9980 : if (nodeIsAnonymous) {
9981 : aCaretPos->SetAnonymousContentNode(anonNode);
9982 0 : }
9983 0 : return aCaretPos.forget();
9984 : }
9985 :
9986 : bool
9987 : nsIDocument::IsPotentiallyScrollable(HTMLBodyElement* aBody)
9988 : {
9989 0 : // We rely on correct frame information here, so need to flush frames.
9990 0 : FlushPendingNotifications(FlushType::Frames);
9991 0 :
9992 0 : // An element is potentially scrollable if all of the following conditions are
9993 0 : // true:
9994 :
9995 : // The element has an associated CSS layout box.
9996 : nsIFrame* bodyFrame = aBody->GetPrimaryFrame();
9997 : if (!bodyFrame) {
9998 : return false;
9999 0 : }
10000 0 :
10001 : // The element is not the HTML body element, or it is and the root element's
10002 : // used value of the overflow-x or overflow-y properties is not visible.
10003 : MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement());
10004 0 : nsIFrame* parentFrame = aBody->GetParent()->GetPrimaryFrame();
10005 : if (parentFrame &&
10006 : parentFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
10007 : parentFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
10008 0 : return false;
10009 : }
10010 :
10011 0 : // The element's used value of the overflow-x or overflow-y properties is not
10012 0 : // visible.
10013 0 : if (bodyFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
10014 0 : bodyFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
10015 : return false;
10016 : }
10017 :
10018 : return true;
10019 : }
10020 0 :
10021 : Element*
10022 : nsIDocument::GetScrollingElement()
10023 : {
10024 0 : // Keep this in sync with IsScrollingElement.
10025 : if (GetCompatibilityMode() == eCompatibility_NavQuirks) {
10026 : RefPtr<HTMLBodyElement> body = GetBodyElement();
10027 0 : if (body && !IsPotentiallyScrollable(body)) {
10028 : return body;
10029 0 : }
10030 0 :
10031 : return nullptr;
10032 : }
10033 :
10034 0 : return GetRootElement();
10035 0 : }
10036 :
10037 : bool
10038 : nsIDocument::IsScrollingElement(Element* aElement)
10039 : {
10040 : // Keep this in sync with GetScrollingElement.
10041 : MOZ_ASSERT(aElement);
10042 0 :
10043 0 : if (GetCompatibilityMode() != eCompatibility_NavQuirks) {
10044 : return aElement == GetRootElement();
10045 : }
10046 :
10047 0 : // In the common case when aElement != body, avoid refcounting.
10048 : HTMLBodyElement* body = GetBodyElement();
10049 0 : if (aElement != body) {
10050 0 : return false;
10051 0 : }
10052 :
10053 0 : // Now we know body is non-null, since aElement is not null. It's the
10054 : // scrolling element for the document if it itself is not potentially
10055 : // scrollable.
10056 0 : RefPtr<HTMLBodyElement> strongBody(body);
10057 : return !IsPotentiallyScrollable(strongBody);
10058 0 : }
10059 0 :
10060 0 : void
10061 0 : nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
10062 0 : {
10063 : nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
10064 0 : if (NS_FAILED(res)) {
10065 0 : rv.Throw(res);
10066 0 : }
10067 : }
10068 :
10069 : void
10070 : nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
10071 : {
10072 : nsCOMPtr<nsIURI> uri;
10073 : nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
10074 0 : if (NS_FAILED(res)) {
10075 : rv.Throw(res);
10076 0 : return;
10077 : }
10078 0 : res = CSSLoader()->ObsoleteSheet(uri);
10079 : if (NS_FAILED(res)) {
10080 0 : rv.Throw(res);
10081 0 : }
10082 0 : }
10083 0 :
10084 0 : class UnblockParsingPromiseHandler final : public PromiseNativeHandler
10085 : {
10086 0 : public:
10087 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
10088 : NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
10089 0 :
10090 : explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise,
10091 0 : const BlockParsingOptions& aOptions)
10092 : : mPromise(aPromise)
10093 0 : {
10094 0 : nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
10095 : if (parser && (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) {
10096 : parser->BlockParser();
10097 0 : mParser = do_GetWeakReference(parser);
10098 : mDocument = aDocument;
10099 0 : }
10100 : }
10101 0 :
10102 0 : void
10103 : ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
10104 : {
10105 0 : MaybeUnblockParser();
10106 0 :
10107 : mPromise->MaybeResolve(aCx, aValue);
10108 : }
10109 0 :
10110 0 : void
10111 : RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
10112 0 : {
10113 : MaybeUnblockParser();
10114 :
10115 0 : mPromise->MaybeReject(aCx, aValue);
10116 0 : }
10117 0 :
10118 0 : protected:
10119 0 : virtual ~UnblockParsingPromiseHandler()
10120 0 : {
10121 0 : // If we're being cleaned up by the cycle collector, our mDocument reference
10122 0 : // may have been unlinked while our mParser weak reference is still alive.
10123 : if (mDocument) {
10124 : MaybeUnblockParser();
10125 0 : }
10126 0 : }
10127 0 :
10128 : private:
10129 : void MaybeUnblockParser() {
10130 : nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
10131 : if (parser) {
10132 : MOZ_DIAGNOSTIC_ASSERT(mDocument);
10133 : nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
10134 0 : if (parser == docParser) {
10135 : parser->UnblockParser();
10136 0 : parser->ContinueInterruptedParsingAsync();
10137 0 : }
10138 0 : }
10139 : mParser = nullptr;
10140 0 : mDocument = nullptr;
10141 0 : }
10142 :
10143 : nsWeakPtr mParser;
10144 0 : RefPtr<Promise> mPromise;
10145 : RefPtr<nsIDocument> mDocument;
10146 0 : };
10147 0 :
10148 : NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
10149 :
10150 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
10151 : NS_INTERFACE_MAP_ENTRY(nsISupports)
10152 0 : NS_INTERFACE_MAP_END
10153 0 :
10154 : NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler)
10155 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler)
10156 :
10157 : already_AddRefed<Promise>
10158 : nsIDocument::BlockParsing(Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv)
10159 0 : {
10160 : RefPtr<Promise> resultPromise = Promise::Create(aPromise.GetParentObject(), aRv);
10161 0 : if (aRv.Failed()) {
10162 0 : return nullptr;
10163 0 : }
10164 0 :
10165 : RefPtr<PromiseNativeHandler> promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise,
10166 : aOptions);
10167 : aPromise.AppendNativeHandler(promiseHandler);
10168 0 :
10169 0 : return resultPromise.forget();
10170 : }
10171 :
10172 : already_AddRefed<nsIURI>
10173 0 : nsIDocument::GetMozDocumentURIIfNotForErrorPages()
10174 : {
10175 : if (mFailedChannel) {
10176 : nsCOMPtr<nsIURI> failedURI;
10177 0 : if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
10178 : return failedURI.forget();
10179 144 : }
10180 52 : }
10181 0 :
10182 0 : nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
10183 0 : if (!uri) {
10184 : return nullptr;
10185 : }
10186 1 :
10187 1 : return uri.forget();
10188 : }
10189 :
10190 : Promise*
10191 : nsIDocument::GetDocumentReadyForIdle(ErrorResult& aRv)
10192 144 : {
10193 : if (!mReadyForIdle) {
10194 : nsIGlobalObject* global = GetScopeObject();
10195 : if (!global) {
10196 0 : aRv.Throw(NS_ERROR_NOT_AVAILABLE);
10197 : return nullptr;
10198 144 : }
10199 72 :
10200 0 : mReadyForIdle = Promise::Create(global, aRv);
10201 72 : if (aRv.Failed()) {
10202 : return nullptr;
10203 0 : }
10204 : }
10205 :
10206 0 : return mReadyForIdle;
10207 : }
10208 0 :
10209 : void
10210 : nsIDocument::MaybeResolveReadyForIdle()
10211 : {
10212 0 : IgnoredErrorResult rv;
10213 : Promise* readyPromise = GetDocumentReadyForIdle(rv);
10214 : if (readyPromise) {
10215 0 : readyPromise->MaybeResolve(this);
10216 : }
10217 : }
10218 :
10219 0 : nsIHTMLCollection*
10220 : nsIDocument::Children()
10221 0 : {
10222 : if (!mChildrenCollection) {
10223 : mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
10224 : nsGkAtoms::_asterisk,
10225 : nsGkAtoms::_asterisk,
10226 : false);
10227 : }
10228 :
10229 : return mChildrenCollection;
10230 : }
10231 :
10232 : uint32_t
10233 : nsIDocument::ChildElementCount()
10234 : {
10235 : return Children()->Length();
10236 : }
10237 :
10238 : namespace mozilla {
10239 :
10240 : // Singleton class to manage the list of fullscreen documents which are the
10241 : // root of a branch which contains fullscreen documents. We maintain this list
10242 : // so that we can easily exit all windows from fullscreen when the user
10243 : // presses the escape key.
10244 : class FullscreenRoots {
10245 : public:
10246 : // Adds the root of given document to the manager. Calling this method
10247 : // with a document whose root is already contained has no effect.
10248 : static void Add(nsIDocument* aDoc);
10249 :
10250 : // Iterates over every root in the root list, and calls aFunction, passing
10251 0 : // each root once to aFunction. It is safe to call Add() and Remove() while
10252 0 : // iterating over the list (i.e. in aFunction). Documents that are removed
10253 0 : // from the manager during traversal are not traversed, and documents that
10254 0 : // are added to the manager during traversal are also not traversed.
10255 0 : static void ForEach(void(*aFunction)(nsIDocument* aDoc));
10256 0 :
10257 : // Removes the root of a specific document from the manager.
10258 : static void Remove(nsIDocument* aDoc);
10259 :
10260 : // Returns true if all roots added to the list have been removed.
10261 : static bool IsEmpty();
10262 :
10263 : private:
10264 :
10265 : FullscreenRoots() {
10266 : MOZ_COUNT_CTOR(FullscreenRoots);
10267 : }
10268 : ~FullscreenRoots() {
10269 : MOZ_COUNT_DTOR(FullscreenRoots);
10270 : }
10271 :
10272 : enum {
10273 : NotFound = uint32_t(-1)
10274 : };
10275 : // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
10276 : static uint32_t Find(nsIDocument* aRoot);
10277 :
10278 : // Returns true if aRoot is in the list of fullscreen roots.
10279 0 : static bool Contains(nsIDocument* aRoot);
10280 :
10281 0 : // Singleton instance of the FullscreenRoots. This is instantiated when a
10282 0 : // root is added, and it is deleted when the last root is removed.
10283 : static FullscreenRoots* sInstance;
10284 :
10285 : // List of weak pointers to roots.
10286 0 : nsTArray<nsWeakPtr> mRoots;
10287 : };
10288 0 :
10289 0 : FullscreenRoots* FullscreenRoots::sInstance = nullptr;
10290 :
10291 : /* static */
10292 0 : void
10293 0 : FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
10294 : {
10295 : if (!sInstance) {
10296 : return;
10297 : }
10298 : // Create a copy of the roots array, and iterate over the copy. This is so
10299 : // that if an element is removed from mRoots we don't mess up our iteration.
10300 0 : nsTArray<nsWeakPtr> roots(sInstance->mRoots);
10301 : // Call aFunction on all entries.
10302 0 : for (uint32_t i = 0; i < roots.Length(); i++) {
10303 : nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
10304 : // Check that the root isn't in the manager. This is so that new additions
10305 : // while we were running don't get traversed.
10306 : if (root && FullscreenRoots::Contains(root)) {
10307 0 : aFunction(root);
10308 : }
10309 0 : }
10310 0 : }
10311 0 :
10312 0 : /* static */
10313 : bool
10314 0 : FullscreenRoots::Contains(nsIDocument* aRoot)
10315 : {
10316 0 : return FullscreenRoots::Find(aRoot) != NotFound;
10317 : }
10318 :
10319 : /* static */
10320 0 : void
10321 : FullscreenRoots::Add(nsIDocument* aDoc)
10322 0 : {
10323 : nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
10324 : if (!FullscreenRoots::Contains(root)) {
10325 : if (!sInstance) {
10326 0 : sInstance = new FullscreenRoots();
10327 0 : }
10328 0 : sInstance->mRoots.AppendElement(do_GetWeakReference(root));
10329 0 : }
10330 : }
10331 :
10332 : /* static */
10333 : uint32_t
10334 : FullscreenRoots::Find(nsIDocument* aRoot)
10335 : {
10336 : if (!sInstance) {
10337 0 : return NotFound;
10338 : }
10339 0 : nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
10340 0 : for (uint32_t i = 0; i < roots.Length(); i++) {
10341 0 : nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
10342 : if (otherRoot == aRoot) {
10343 0 : return i;
10344 0 : }
10345 : }
10346 0 : return NotFound;
10347 0 : }
10348 0 :
10349 0 : /* static */
10350 : void
10351 : FullscreenRoots::Remove(nsIDocument* aDoc)
10352 : {
10353 : nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
10354 : uint32_t index = Find(root);
10355 0 : NS_ASSERTION(index != NotFound,
10356 : "Should only try to remove roots which are still added!");
10357 0 : if (index == NotFound || !sInstance) {
10358 : return;
10359 : }
10360 : sInstance->mRoots.RemoveElementAt(index);
10361 : if (sInstance->mRoots.IsEmpty()) {
10362 : delete sInstance;
10363 : sInstance = nullptr;
10364 0 : }
10365 : }
10366 0 :
10367 0 : /* static */
10368 : bool
10369 : FullscreenRoots::IsEmpty()
10370 : {
10371 0 : return !sInstance;
10372 : }
10373 0 :
10374 0 : } // end namespace mozilla.
10375 : using mozilla::FullscreenRoots;
10376 :
10377 0 : nsIDocument*
10378 : nsIDocument::GetFullscreenRoot()
10379 0 : {
10380 0 : nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
10381 : return root;
10382 : }
10383 0 :
10384 : void
10385 0 : nsIDocument::SetFullscreenRoot(nsIDocument* aRoot)
10386 0 : {
10387 0 : mFullscreenRoot = do_GetWeakReference(aRoot);
10388 : }
10389 0 :
10390 : void
10391 0 : nsIDocument::ExitFullscreen()
10392 0 : {
10393 : RestorePreviousFullScreenState();
10394 : }
10395 0 :
10396 : static void
10397 0 : AskWindowToExitFullscreen(nsIDocument* aDoc)
10398 : {
10399 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
10400 0 : nsContentUtils::DispatchEventOnlyToChrome(
10401 0 : aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
10402 0 : /* Bubbles */ true, /* Cancelable */ false,
10403 : /* DefaultAction */ nullptr);
10404 0 : } else {
10405 : if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
10406 0 : win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false);
10407 : }
10408 0 : }
10409 0 : }
10410 :
10411 0 : class nsCallExitFullscreen : public Runnable
10412 : {
10413 0 : public:
10414 : explicit nsCallExitFullscreen(nsIDocument* aDoc)
10415 : : mozilla::Runnable("nsCallExitFullscreen")
10416 : , mDoc(aDoc)
10417 : {
10418 : }
10419 :
10420 : NS_IMETHOD Run() final
10421 0 : {
10422 : if (!mDoc) {
10423 0 : FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
10424 0 : } else {
10425 0 : AskWindowToExitFullscreen(mDoc);
10426 0 : }
10427 : return NS_OK;
10428 0 : }
10429 :
10430 0 : private:
10431 : nsCOMPtr<nsIDocument> mDoc;
10432 : };
10433 0 :
10434 : /* static */ void
10435 0 : nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
10436 0 : {
10437 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
10438 : nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
10439 0 : if (aDoc) {
10440 : aDoc->Dispatch(TaskCategory::Other, exit.forget());
10441 : } else {
10442 : NS_DispatchToCurrentThread(exit.forget());
10443 0 : }
10444 : }
10445 0 :
10446 0 : static bool
10447 0 : CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
10448 : {
10449 : if (aDoc->FullScreenStackTop()) {
10450 : uint32_t* count = static_cast<uint32_t*>(aData);
10451 0 : (*count)++;
10452 : }
10453 : return true;
10454 : }
10455 0 :
10456 : static uint32_t
10457 : CountFullscreenSubDocuments(nsIDocument* aDoc)
10458 0 : {
10459 : uint32_t count = 0;
10460 : aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
10461 : return count;
10462 0 : }
10463 :
10464 0 : bool
10465 0 : nsIDocument::IsFullscreenLeaf()
10466 : {
10467 0 : // A fullscreen leaf document is fullscreen, and has no fullscreen
10468 0 : // subdocuments.
10469 : if (!FullScreenStackTop()) {
10470 0 : return false;
10471 0 : }
10472 0 : return CountFullscreenSubDocuments(this) == 0;
10473 : }
10474 0 :
10475 : static bool
10476 : ResetFullScreen(nsIDocument* aDocument, void* aData)
10477 : {
10478 : if (aDocument->FullScreenStackTop()) {
10479 : NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
10480 0 : "Should have at most 1 fullscreen subdocument.");
10481 : aDocument->CleanupFullscreenState();
10482 : NS_ASSERTION(!aDocument->FullScreenStackTop(),
10483 0 : "Should reset full-screen");
10484 0 : auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
10485 0 : changed->AppendElement(aDocument);
10486 : aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
10487 0 : }
10488 : return true;
10489 0 : }
10490 :
10491 : // Since nsIDocument::ExitFullscreenInDocTree() could be called from
10492 : // Element::UnbindFromTree() where it is not safe to synchronously run
10493 : // script. This runnable is the script part of that function.
10494 0 : class ExitFullscreenScriptRunnable : public Runnable
10495 0 : {
10496 : public:
10497 0 : explicit ExitFullscreenScriptRunnable(nsCOMArray<nsIDocument>&& aDocuments)
10498 0 : : mozilla::Runnable("ExitFullscreenScriptRunnable")
10499 : , mDocuments(std::move(aDocuments))
10500 0 : {
10501 0 : }
10502 :
10503 0 : NS_IMETHOD Run() override
10504 : {
10505 : // Dispatch MozDOMFullscreen:Exited to the last document in
10506 : // the list since we want this event to follow the same path
10507 : // MozDOMFullscreen:Entered dispatched.
10508 : nsIDocument* lastDocument = mDocuments[mDocuments.Length() - 1];
10509 : nsContentUtils::DispatchEventOnlyToChrome(
10510 : lastDocument, ToSupports(lastDocument),
10511 0 : NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
10512 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
10513 0 : // Ensure the window exits fullscreen.
10514 : if (nsPIDOMWindowOuter* win = mDocuments[0]->GetWindow()) {
10515 : win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false);
10516 0 : }
10517 : return NS_OK;
10518 0 : }
10519 0 :
10520 : private:
10521 : nsCOMArray<nsIDocument> mDocuments;
10522 : };
10523 :
10524 : /* static */ void
10525 : nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
10526 : {
10527 : MOZ_ASSERT(aMaybeNotARootDoc);
10528 0 :
10529 : // Unlock the pointer
10530 : UnlockPointer();
10531 :
10532 : nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
10533 : if (!root || !root->FullScreenStackTop()) {
10534 : // If a document was detached before exiting from fullscreen, it is
10535 : // possible that the root had left fullscreen state. In this case,
10536 0 : // we would not get anything from the ResetFullScreen() call. Root's
10537 : // not being a fullscreen doc also means the widget should have
10538 : // exited fullscreen state. It means even if we do not return here,
10539 0 : // we would actually do nothing below except crashing ourselves via
10540 : // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
10541 : // document.
10542 : return;
10543 : }
10544 0 :
10545 0 : // Stores a list of documents to which we must dispatch "fullscreenchange".
10546 : // We're required by the spec to dispatch the events in leaf-to-root
10547 : // order when exiting fullscreen, but we traverse the doctree in a
10548 0 : // root-to-leaf order, so we save references to the documents we must
10549 : // dispatch to so that we dispatch in the specified order.
10550 : nsCOMArray<nsIDocument> changed;
10551 :
10552 0 : // Walk the tree of fullscreen documents, and reset their fullscreen state.
10553 : ResetFullScreen(root, static_cast<void*>(&changed));
10554 0 :
10555 0 : // Dispatch "fullscreenchange" events. Note this loop is in reverse
10556 : // order so that the events for the leaf document arrives before the root
10557 : // document, as required by the spec.
10558 : for (uint32_t i = 0; i < changed.Length(); ++i) {
10559 0 : DispatchFullScreenChange(changed[changed.Length() - i - 1]);
10560 : }
10561 0 :
10562 0 : NS_ASSERTION(!root->FullScreenStackTop(),
10563 0 : "Fullscreen root should no longer be a fullscreen doc...");
10564 0 :
10565 0 : // Move the top-level window out of fullscreen mode.
10566 0 : FullscreenRoots::Remove(root);
10567 :
10568 : nsContentUtils::AddScriptRunner(
10569 : new ExitFullscreenScriptRunnable(std::move(changed)));
10570 : }
10571 :
10572 0 : bool
10573 : GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
10574 0 : {
10575 0 : if (aDoc->IsFullscreenLeaf()) {
10576 0 : nsIDocument** result = static_cast<nsIDocument**>(aData);
10577 : *result = aDoc;
10578 : return false;
10579 : } else if (aDoc->FullScreenStackTop()) {
10580 : aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
10581 0 : }
10582 : return true;
10583 : }
10584 0 :
10585 : static nsIDocument*
10586 : GetFullscreenLeaf(nsIDocument* aDoc)
10587 0 : {
10588 0 : nsIDocument* leaf = nullptr;
10589 : GetFullscreenLeaf(aDoc, &leaf);
10590 : if (leaf) {
10591 : return leaf;
10592 0 : }
10593 : // Otherwise we could be either in a non-fullscreen doc tree, or we're
10594 0 : // below the fullscreen doc. Start the search from the root.
10595 : nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
10596 : // Check that the root is actually fullscreen so we don't waste time walking
10597 0 : // around its descendants.
10598 0 : if (!root->FullScreenStackTop()) {
10599 : return nullptr;
10600 : }
10601 0 : GetFullscreenLeaf(root, &leaf);
10602 0 : return leaf;
10603 : }
10604 0 :
10605 : void
10606 0 : nsIDocument::RestorePreviousFullScreenState()
10607 0 : {
10608 : NS_ASSERTION(!FullScreenStackTop() || !FullscreenRoots::IsEmpty(),
10609 : "Should have at least 1 fullscreen root when fullscreen!");
10610 :
10611 0 : if (!FullScreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
10612 0 : return;
10613 : }
10614 0 :
10615 0 : nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
10616 0 : AutoTArray<nsIDocument*, 8> exitDocs;
10617 0 :
10618 : nsIDocument* doc = fullScreenDoc;
10619 : // Collect all subdocuments.
10620 : for (; doc != this; doc = doc->GetParentDocument()) {
10621 : exitDocs.AppendElement(doc);
10622 : }
10623 : MOZ_ASSERT(doc == this, "Must have reached this doc");
10624 0 : // Collect all ancestor documents which we are going to change.
10625 0 : for (; doc; doc = doc->GetParentDocument()) {
10626 : MOZ_ASSERT(!doc->mFullScreenStack.IsEmpty(),
10627 : "Ancestor of fullscreen document must also be in fullscreen");
10628 : if (doc != this) {
10629 : Element* top = doc->FullScreenStackTop();
10630 0 : if (top->IsHTMLElement(nsGkAtoms::iframe)) {
10631 0 : if (static_cast<HTMLIFrameElement*>(top)->FullscreenFlag()) {
10632 0 : // If this is an iframe, and it explicitly requested
10633 : // fullscreen, don't rollback it automatically.
10634 : break;
10635 0 : }
10636 0 : }
10637 : }
10638 : exitDocs.AppendElement(doc);
10639 : if (doc->mFullScreenStack.Length() > 1) {
10640 0 : break;
10641 : }
10642 : }
10643 0 :
10644 0 : nsIDocument* lastDoc = exitDocs.LastElement();
10645 : if (!lastDoc->GetParentDocument() &&
10646 : lastDoc->mFullScreenStack.Length() == 1) {
10647 : // If we are fully exiting fullscreen, don't touch anything here,
10648 : // just wait for the window to get out from fullscreen first.
10649 0 : AskWindowToExitFullscreen(this);
10650 0 : return;
10651 0 : }
10652 :
10653 0 : // If fullscreen mode is updated the pointer should be unlocked
10654 0 : UnlockPointer();
10655 : // All documents listed in the array except the last one are going to
10656 : // completely exit from the fullscreen state.
10657 0 : for (auto i : IntegerRange(exitDocs.Length() - 1)) {
10658 0 : exitDocs[i]->CleanupFullscreenState();
10659 : }
10660 : // The last document will either rollback one fullscreen element, or
10661 0 : // completely exit from the fullscreen state as well.
10662 : nsIDocument* newFullscreenDoc;
10663 : if (lastDoc->mFullScreenStack.Length() > 1) {
10664 0 : lastDoc->FullScreenStackPop();
10665 0 : newFullscreenDoc = lastDoc;
10666 : } else {
10667 : lastDoc->CleanupFullscreenState();
10668 : newFullscreenDoc = lastDoc->GetParentDocument();
10669 : }
10670 : // Dispatch the fullscreenchange event to all document listed.
10671 0 : for (nsIDocument* d : exitDocs) {
10672 0 : DispatchFullScreenChange(d);
10673 : }
10674 :
10675 : MOZ_ASSERT(newFullscreenDoc, "If we were going to exit from fullscreen on "
10676 0 : "all documents in this doctree, we should've asked the window to "
10677 : "exit first instead of reaching here.");
10678 : if (fullScreenDoc != newFullscreenDoc &&
10679 0 : !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) {
10680 0 : // We've popped so enough off the stack that we've rolled back to
10681 0 : // a fullscreen element in a parent document. If this document is
10682 : // cross origin, dispatch an event to chrome so it knows to show
10683 0 : // the warning UI.
10684 : DispatchCustomEventWithFlush(
10685 0 : newFullscreenDoc, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
10686 : /* Bubbles */ true, /* ChromeOnly */ true);
10687 0 : }
10688 0 : }
10689 :
10690 : class nsCallRequestFullScreen : public Runnable
10691 : {
10692 : public:
10693 : explicit nsCallRequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
10694 : : mozilla::Runnable("nsCallRequestFullScreen")
10695 0 : , mRequest(std::move(aRequest))
10696 : {
10697 0 : }
10698 0 :
10699 : NS_IMETHOD Run() override
10700 : {
10701 : mRequest->GetDocument()->RequestFullScreen(std::move(mRequest));
10702 : return NS_OK;
10703 : }
10704 0 :
10705 0 : UniquePtr<FullscreenRequest> mRequest;
10706 0 : };
10707 0 :
10708 : void
10709 : nsIDocument::AsyncRequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
10710 0 : {
10711 : if (!aRequest->GetElement()) {
10712 : MOZ_ASSERT_UNREACHABLE(
10713 : "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
10714 0 : return;
10715 : }
10716 0 :
10717 0 : // Request full-screen asynchronously.
10718 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
10719 0 : nsCOMPtr<nsIRunnable> event = new nsCallRequestFullScreen(std::move(aRequest));
10720 : Dispatch(TaskCategory::Other, event.forget());
10721 0 : }
10722 0 :
10723 : void
10724 : nsIDocument::DispatchFullscreenError(const char* aMessage)
10725 0 : {
10726 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
10727 0 : new AsyncEventDispatcher(this,
10728 0 : NS_LITERAL_STRING("fullscreenerror"),
10729 : true,
10730 0 : false);
10731 : asyncDispatcher->PostDOMEvent();
10732 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
10733 0 : NS_LITERAL_CSTRING("DOM"), this,
10734 : nsContentUtils::eDOM_PROPERTIES,
10735 : aMessage);
10736 0 : }
10737 :
10738 0 : static void
10739 : UpdateViewportScrollbarOverrideForFullscreen(nsIDocument* aDoc)
10740 : {
10741 0 : if (nsPresContext* presContext = aDoc->GetPresContext()) {
10742 : presContext->UpdateViewportScrollbarStylesOverride();
10743 : }
10744 0 : }
10745 :
10746 : static void
10747 : ClearFullscreenStateOnElement(Element* aElement)
10748 : {
10749 : // Remove styles from existing top element.
10750 : EventStateManager::SetFullScreenState(aElement, false);
10751 : // Reset iframe fullscreen flag.
10752 : if (aElement->IsHTMLElement(nsGkAtoms::iframe)) {
10753 0 : static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false);
10754 0 : }
10755 0 : }
10756 :
10757 : void
10758 0 : nsIDocument::CleanupFullscreenState()
10759 0 : {
10760 0 : // Iterate the fullscreen stack and clear the fullscreen states.
10761 0 : // Since we also need to clear the fullscreen-ancestor state, and
10762 : // currently fullscreen elements can only be placed in hierarchy
10763 : // order in the stack, reversely iterating the stack could be more
10764 0 : // efficient. NOTE that fullscreen-ancestor state would be removed
10765 : // in bug 1199529, and the elements may not in hierarchy order
10766 0 : // after bug 1195213.
10767 0 : for (nsWeakPtr& weakPtr : Reversed(mFullScreenStack)) {
10768 0 : if (nsCOMPtr<Element> element = do_QueryReferent(weakPtr)) {
10769 : ClearFullscreenStateOnElement(element);
10770 : }
10771 0 : }
10772 0 : mFullScreenStack.Clear();
10773 0 : mFullscreenRoot = nullptr;
10774 0 : UpdateViewportScrollbarOverrideForFullscreen(this);
10775 0 : }
10776 :
10777 : bool
10778 : nsIDocument::FullScreenStackPush(Element* aElement)
10779 0 : {
10780 : NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
10781 0 : Element* top = FullScreenStackTop();
10782 : if (top == aElement || !aElement) {
10783 : return false;
10784 : }
10785 0 : EventStateManager::SetFullScreenState(aElement, true);
10786 : mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
10787 : NS_ASSERTION(FullScreenStackTop() == aElement, "Should match");
10788 : UpdateViewportScrollbarOverrideForFullscreen(this);
10789 : return true;
10790 0 : }
10791 0 :
10792 : void
10793 : nsIDocument::FullScreenStackPop()
10794 : {
10795 : if (mFullScreenStack.IsEmpty()) {
10796 0 : return;
10797 0 : }
10798 0 :
10799 0 : ClearFullscreenStateOnElement(FullScreenStackTop());
10800 :
10801 0 : // Remove top element. Note the remaining top element in the stack
10802 0 : // will not have full-screen style bits set, so we will need to restore
10803 : // them on the new top element before returning.
10804 : uint32_t last = mFullScreenStack.Length() - 1;
10805 : mFullScreenStack.RemoveElementAt(last);
10806 :
10807 : // Pop from the stack null elements (references to elements which have
10808 : // been GC'd since they were added to the stack) and elements which are
10809 0 : // no longer in this document.
10810 : while (!mFullScreenStack.IsEmpty()) {
10811 : Element* element = FullScreenStackTop();
10812 : if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) {
10813 0 : NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
10814 : "Should have already removed full-screen styles");
10815 62 : uint32_t last = mFullScreenStack.Length() - 1;
10816 : mFullScreenStack.RemoveElementAt(last);
10817 : } else {
10818 0 : // The top element of the stack is now an in-doc element. Return here.
10819 0 : break;
10820 0 : }
10821 0 : }
10822 0 :
10823 0 : UpdateViewportScrollbarOverrideForFullscreen(this);
10824 : }
10825 :
10826 : Element*
10827 0 : nsIDocument::FullScreenStackTop()
10828 : {
10829 18 : if (mFullScreenStack.IsEmpty()) {
10830 54 : return nullptr;
10831 0 : }
10832 0 : uint32_t last = mFullScreenStack.Length() - 1;
10833 0 : nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
10834 : NS_ASSERTION(element, "Should have full-screen element!");
10835 : NS_ASSERTION(element->IsInComposedDoc(), "Full-screen element should be in doc");
10836 0 : NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
10837 : return element;
10838 : }
10839 :
10840 : nsTArray<Element*>
10841 0 : nsIDocument::GetFullscreenStack() const
10842 : {
10843 0 : nsTArray<Element*> elements;
10844 0 : for (const nsWeakPtr& ptr : mFullScreenStack) {
10845 : if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
10846 : MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN));
10847 : elements.AppendElement(elem);
10848 0 : }
10849 0 : }
10850 0 : return elements;
10851 : }
10852 :
10853 : // Returns true if aDoc is in the focused tab in the active window.
10854 0 : static bool
10855 0 : IsInActiveTab(nsIDocument* aDoc)
10856 0 : {
10857 : nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
10858 : if (!docshell) {
10859 0 : return false;
10860 0 : }
10861 :
10862 : bool isActive = false;
10863 : docshell->GetIsActive(&isActive);
10864 0 : if (!isActive) {
10865 0 : return false;
10866 : }
10867 :
10868 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
10869 0 : docshell->GetRootTreeItem(getter_AddRefs(rootItem));
10870 0 : if (!rootItem) {
10871 0 : return false;
10872 : }
10873 : nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
10874 : if (!rootWin) {
10875 0 : return false;
10876 : }
10877 :
10878 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
10879 : if (!fm) {
10880 : return false;
10881 : }
10882 :
10883 0 : nsCOMPtr<mozIDOMWindowProxy> activeWindow;
10884 0 : fm->GetActiveWindow(getter_AddRefs(activeWindow));
10885 0 : if (!activeWindow) {
10886 0 : return false;
10887 : }
10888 0 :
10889 : return activeWindow == rootWin;
10890 : }
10891 0 :
10892 : nsresult nsIDocument::RemoteFrameFullscreenChanged(Element* aFrameElement)
10893 0 : {
10894 0 : // Ensure the frame element is the fullscreen element in this document.
10895 : // If the frame element is already the fullscreen element in this document,
10896 : // this has no effect.
10897 : auto request = MakeUnique<FullscreenRequest>(aFrameElement);
10898 0 : request->mIsCallerChrome = false;
10899 : request->mShouldNotifyNewOrigin = false;
10900 88 : RequestFullScreen(std::move(request));
10901 125 :
10902 0 : return NS_OK;
10903 : }
10904 :
10905 : nsresult nsIDocument::RemoteFrameFullscreenReverted()
10906 0 : {
10907 : RestorePreviousFullScreenState();
10908 0 : return NS_OK;
10909 0 : }
10910 0 :
10911 : /* static */ bool
10912 : nsIDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
10913 : {
10914 : MOZ_ASSERT(NS_IsMainThread());
10915 : return nsContentUtils::IsSystemCaller(aCx) ||
10916 : nsContentUtils::IsUnprefixedFullscreenApiEnabled();
10917 0 : }
10918 :
10919 0 : static bool
10920 0 : HasFullScreenSubDocument(nsIDocument* aDoc)
10921 : {
10922 : uint32_t count = CountFullscreenSubDocuments(aDoc);
10923 : NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
10924 : return count >= 1;
10925 : }
10926 0 :
10927 : // Returns nullptr if a request for Fullscreen API is currently enabled
10928 : // in the given document. Returns a static string indicates the reason
10929 : // why it is not enabled otherwise.
10930 : static const char*
10931 : GetFullscreenError(nsIDocument* aDoc, bool aCallerIsChrome)
10932 0 : {
10933 0 : bool apiEnabled = nsContentUtils::IsFullScreenApiEnabled();
10934 : if (apiEnabled && aCallerIsChrome) {
10935 : // Chrome code can always use the full-screen API, provided it's not
10936 0 : // explicitly disabled.
10937 : return nullptr;
10938 : }
10939 :
10940 0 : if (!apiEnabled) {
10941 : return "FullscreenDeniedDisabled";
10942 : }
10943 0 :
10944 : // Ensure that all containing elements are <iframe> and have
10945 0 : // allowfullscreen attribute set.
10946 : nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell());
10947 : if (!docShell || !docShell->GetFullscreenAllowed()) {
10948 0 : return "FullscreenDeniedContainerNotAllowed";
10949 0 : }
10950 0 : return nullptr;
10951 : }
10952 0 :
10953 0 : bool
10954 0 : nsIDocument::FullscreenElementReadyCheck(Element* aElement,
10955 : bool aWasCallerChrome)
10956 0 : {
10957 0 : NS_ASSERTION(aElement,
10958 0 : "Must pass non-null element to nsDocument::RequestFullScreen");
10959 : if (!aElement || aElement == FullScreenStackTop()) {
10960 0 : return false;
10961 0 : }
10962 0 : if (!aElement->IsInComposedDoc()) {
10963 : DispatchFullscreenError("FullscreenDeniedNotInDocument");
10964 0 : return false;
10965 0 : }
10966 0 : if (aElement->OwnerDoc() != this) {
10967 : DispatchFullscreenError("FullscreenDeniedMovedDocument");
10968 0 : return false;
10969 0 : }
10970 0 : if (!GetWindow()) {
10971 : DispatchFullscreenError("FullscreenDeniedLostWindow");
10972 : return false;
10973 : }
10974 0 : if (const char* msg = GetFullscreenError(this, aWasCallerChrome)) {
10975 0 : DispatchFullscreenError(msg);
10976 0 : return false;
10977 : }
10978 : if (!IsVisible()) {
10979 0 : DispatchFullscreenError("FullscreenDeniedHidden");
10980 0 : return false;
10981 : }
10982 0 : if (HasFullScreenSubDocument(this)) {
10983 0 : DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
10984 0 : return false;
10985 : }
10986 : //XXXsmaug Note, we don't follow the latest fullscreen spec here.
10987 0 : // This whole check could be probably removed.
10988 0 : if (FullScreenStackTop() &&
10989 0 : !nsContentUtils::ContentIsHostIncludingDescendantOf(aElement,
10990 0 : FullScreenStackTop())) {
10991 : // If this document is full-screen, only grant full-screen requests from
10992 0 : // a descendant of the current full-screen element.
10993 0 : DispatchFullscreenError("FullscreenDeniedNotDescendant");
10994 0 : return false;
10995 : }
10996 : if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
10997 : DispatchFullscreenError("FullscreenDeniedNotFocusedTab");
10998 : return false;
10999 0 : }
11000 : // Deny requests when a windowed plugin is focused.
11001 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
11002 : if (!fm) {
11003 0 : NS_WARNING("Failed to retrieve focus manager in full-screen request.");
11004 0 : return false;
11005 : }
11006 0 : if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(fm->GetFocusedElement())) {
11007 : DispatchFullscreenError("FullscreenDeniedFocusedPlugin");
11008 0 : return false;
11009 0 : }
11010 : return true;
11011 : }
11012 :
11013 : FullscreenRequest::FullscreenRequest(Element* aElement)
11014 : : mElement(aElement)
11015 : , mDocument(static_cast<nsDocument*>(aElement->OwnerDoc()))
11016 : {
11017 : MOZ_COUNT_CTOR(FullscreenRequest);
11018 : }
11019 0 :
11020 : FullscreenRequest::~FullscreenRequest()
11021 0 : {
11022 0 : MOZ_COUNT_DTOR(FullscreenRequest);
11023 : }
11024 :
11025 : // Any fullscreen request waiting for the widget to finish being full-
11026 : // screen is queued here. This is declared static instead of a member
11027 : // of nsDocument because in the majority of time, there would be at most
11028 : // one document requesting fullscreen. We shouldn't waste the space to
11029 : // hold for it in every document.
11030 : class PendingFullscreenRequestList
11031 : {
11032 : public:
11033 : static void Add(UniquePtr<FullscreenRequest>&& aRequest)
11034 : {
11035 : sList.insertBack(aRequest.release());
11036 : }
11037 :
11038 : static const FullscreenRequest* GetLast()
11039 : {
11040 : return sList.getLast();
11041 8 : }
11042 :
11043 : enum IteratorOption
11044 8 : {
11045 0 : // When we are committing fullscreen changes or preparing for
11046 8 : // that, we generally want to iterate all requests in the same
11047 : // window with eDocumentsWithSameRoot option.
11048 0 : eDocumentsWithSameRoot,
11049 0 : // If we are removing a document from the tree, we would only
11050 0 : // want to remove the requests from the given document and its
11051 0 : // descendants. For that case, use eInclusiveDescendants.
11052 : eInclusiveDescendants
11053 0 : };
11054 :
11055 0 : class Iterator
11056 : {
11057 0 : public:
11058 : explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
11059 0 : : mCurrent(PendingFullscreenRequestList::sList.getFirst())
11060 0 : , mRootShellForIteration(aDoc->GetDocShell())
11061 0 : {
11062 : if (mCurrent) {
11063 : if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
11064 : mRootShellForIteration->
11065 : GetRootTreeItem(getter_AddRefs(mRootShellForIteration));
11066 0 : }
11067 : SkipToNextMatch();
11068 0 : }
11069 0 : }
11070 0 :
11071 0 : void DeleteAndNext()
11072 0 : {
11073 : DeleteAndNextInternal();
11074 0 : SkipToNextMatch();
11075 : }
11076 0 : bool AtEnd() const { return mCurrent == nullptr; }
11077 0 : const FullscreenRequest& Get() const { return *mCurrent; }
11078 :
11079 : private:
11080 0 : void DeleteAndNextInternal()
11081 : {
11082 0 : FullscreenRequest* thisRequest = mCurrent;
11083 0 : mCurrent = mCurrent->getNext();
11084 : delete thisRequest;
11085 0 : }
11086 : void SkipToNextMatch()
11087 : {
11088 0 : while (mCurrent) {
11089 : nsCOMPtr<nsIDocShellTreeItem>
11090 : docShell = mCurrent->GetDocument()->GetDocShell();
11091 : if (!docShell) {
11092 : // Always automatically drop documents which has been
11093 : // detached from the doc shell.
11094 0 : DeleteAndNextInternal();
11095 : } else {
11096 : while (docShell && docShell != mRootShellForIteration) {
11097 : docShell->GetParent(getter_AddRefs(docShell));
11098 : }
11099 : if (!docShell) {
11100 : // We've gone over the root, but haven't find the target
11101 : // ancestor, so skip this item.
11102 : mCurrent = mCurrent->getNext();
11103 : } else {
11104 : break;
11105 : }
11106 1 : }
11107 : }
11108 : }
11109 0 :
11110 : FullscreenRequest* mCurrent;
11111 0 : nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
11112 0 : };
11113 :
11114 : private:
11115 0 : PendingFullscreenRequestList() = delete;
11116 0 :
11117 0 : static LinkedList<FullscreenRequest> sList;
11118 : };
11119 :
11120 : /* static */ LinkedList<FullscreenRequest> PendingFullscreenRequestList::sList;
11121 0 :
11122 : static nsCOMPtr<nsPIDOMWindowOuter>
11123 : GetRootWindow(nsIDocument* aDoc)
11124 0 : {
11125 : nsIDocShell* docShell = aDoc->GetDocShell();
11126 : if (!docShell) {
11127 : return nullptr;
11128 0 : }
11129 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
11130 : docShell->GetRootTreeItem(getter_AddRefs(rootItem));
11131 : return rootItem ? rootItem->GetWindow() : nullptr;
11132 0 : }
11133 :
11134 : static bool
11135 : ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
11136 : nsPIDOMWindowOuter* aRootWin)
11137 : {
11138 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
11139 : // If we are in the content process, we can apply the fullscreen
11140 0 : // state directly only if we have been in DOM fullscreen, because
11141 0 : // otherwise we always need to notify the chrome.
11142 : return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement();
11143 : } else {
11144 : // If we are in the chrome process, and the window has not been in
11145 : // fullscreen, we certainly need to make that fullscreen first.
11146 : if (!aRootWin->GetFullScreen()) {
11147 : return false;
11148 : }
11149 0 : // The iterator not being at end indicates there is still some
11150 : // pending fullscreen request relates to this document. We have to
11151 : // push the request to the pending queue so requests are handled
11152 : // in the correct order.
11153 : PendingFullscreenRequestList::Iterator
11154 0 : iter(aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
11155 : if (!iter.AtEnd()) {
11156 0 : return false;
11157 0 : }
11158 0 : // We have to apply the fullscreen state directly in this case,
11159 : // because nsGlobalWindow::SetFullscreenInternal() will do nothing
11160 : // if it is already in fullscreen. If we do not apply the state but
11161 0 : // instead add it to the queue and wait for the window as normal,
11162 0 : // we would get stuck.
11163 0 : return true;
11164 : }
11165 : }
11166 :
11167 : void
11168 0 : nsIDocument::RequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
11169 0 : {
11170 0 : nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
11171 0 : if (!rootWin) {
11172 0 : return;
11173 0 : }
11174 :
11175 : if (ShouldApplyFullscreenDirectly(this, rootWin)) {
11176 : ApplyFullscreen(*aRequest);
11177 : return;
11178 0 : }
11179 :
11180 : // Per spec only HTML, <svg>, and <math> should be allowed, but
11181 : // we also need to allow XUL elements right now.
11182 0 : Element* elem = aRequest->GetElement();
11183 0 : if (!elem->IsHTMLElement() && !elem->IsXULElement() &&
11184 : !elem->IsSVGElement(nsGkAtoms::svg) &&
11185 : !elem->IsMathMLElement(nsGkAtoms::math)) {
11186 0 : DispatchFullscreenError("FullscreenDeniedNotHTMLSVGOrMathML");
11187 0 : return;
11188 0 : }
11189 :
11190 : // We don't need to check element ready before this point, because
11191 0 : // if we called ApplyFullscreen, it would check that for us.
11192 : if (!FullscreenElementReadyCheck(elem, aRequest->mIsCallerChrome)) {
11193 : return;
11194 : }
11195 :
11196 0 : PendingFullscreenRequestList::Add(std::move(aRequest));
11197 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
11198 0 : // If we are not the top level process, dispatch an event to make
11199 : // our parent process go fullscreen first.
11200 0 : nsContentUtils::DispatchEventOnlyToChrome(
11201 0 : this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Request"),
11202 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
11203 0 : } else {
11204 0 : // Make the window fullscreen.
11205 : rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
11206 0 : }
11207 : }
11208 0 :
11209 : /* static */ bool
11210 : nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
11211 : {
11212 0 : bool handled = false;
11213 : PendingFullscreenRequestList::Iterator iter(
11214 : aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
11215 16 : while (!iter.AtEnd()) {
11216 0 : const FullscreenRequest& request = iter.Get();
11217 0 : if (request.GetDocument()->ApplyFullscreen(request)) {
11218 : handled = true;
11219 0 : }
11220 : iter.DeleteAndNext();
11221 : }
11222 0 : return handled;
11223 : }
11224 0 :
11225 0 : static void
11226 : ClearPendingFullscreenRequests(nsIDocument* aDoc)
11227 : {
11228 : PendingFullscreenRequestList::Iterator iter(
11229 : aDoc, PendingFullscreenRequestList::eInclusiveDescendants);
11230 : while (!iter.AtEnd()) {
11231 0 : iter.DeleteAndNext();
11232 : }
11233 : }
11234 :
11235 : bool
11236 : nsIDocument::ApplyFullscreen(const FullscreenRequest& aRequest)
11237 : {
11238 0 : Element* elem = aRequest.GetElement();
11239 : if (!FullscreenElementReadyCheck(elem, aRequest.mIsCallerChrome)) {
11240 : return false;
11241 : }
11242 0 :
11243 : // Stash a reference to any existing fullscreen doc, we'll use this later
11244 : // to detect if the origin which is fullscreen has changed.
11245 : nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
11246 0 :
11247 : // Stores a list of documents which we must dispatch "fullscreenchange"
11248 : // too. We're required by the spec to dispatch the events in root-to-leaf
11249 : // order, but we traverse the doctree in a leaf-to-root order, so we save
11250 : // references to the documents we must dispatch to so that we get the order
11251 0 : // as specified.
11252 0 : AutoTArray<nsIDocument*, 8> changed;
11253 :
11254 0 : // Remember the root document, so that if a full-screen document is hidden
11255 : // we can reset full-screen state in the remaining visible full-screen documents.
11256 : nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
11257 0 :
11258 : // If a document is already in fullscreen, then unlock the mouse pointer
11259 : // before setting a new document to fullscreen
11260 : UnlockPointer();
11261 :
11262 : // Set the full-screen element. This sets the full-screen style on the
11263 : // element, and the full-screen-ancestor styles on ancestors of the element
11264 0 : // in this document.
11265 : DebugOnly<bool> x = FullScreenStackPush(elem);
11266 0 : NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
11267 0 : // Set the iframe fullscreen flag.
11268 : if (elem->IsHTMLElement(nsGkAtoms::iframe)) {
11269 0 : static_cast<HTMLIFrameElement*>(elem)->SetFullscreenFlag(true);
11270 : }
11271 : changed.AppendElement(this);
11272 0 :
11273 0 : // Propagate up the document hierarchy, setting the full-screen element as
11274 0 : // the element's container in ancestor documents. This also sets the
11275 0 : // appropriate css styles as well. Note we don't propagate down the
11276 0 : // document hierarchy, the full-screen element (or its container) is not
11277 : // visible there. Stop when we reach the root document.
11278 : nsIDocument* child = this;
11279 : while (true) {
11280 : child->SetFullscreenRoot(fullScreenRootDoc);
11281 : NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
11282 : "Fullscreen root should be set!");
11283 : if (child == fullScreenRootDoc) {
11284 0 : break;
11285 : }
11286 0 : nsIDocument* parent = child->GetParentDocument();
11287 : Element* element = parent->FindContentForSubDocument(child);
11288 : if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
11289 : changed.AppendElement(parent);
11290 : child = parent;
11291 : } else {
11292 : // We've reached either the root, or a point in the doctree where the
11293 0 : // new full-screen element container is the same as the previous
11294 0 : // full-screen element's container. No more changes need to be made
11295 0 : // to the full-screen stacks of documents further up the tree.
11296 0 : break;
11297 : }
11298 : }
11299 :
11300 : FullscreenRoots::Add(this);
11301 :
11302 : // If it is the first entry of the fullscreen, trigger an event so
11303 : // that the UI can response to this change, e.g. hide chrome, or
11304 : // notifying parent process to enter fullscreen. Note that chrome
11305 : // code may also want to listen to MozDOMFullscreen:NewOrigin event
11306 0 : // to pop up warning UI.
11307 0 : if (!previousFullscreenDoc) {
11308 0 : nsContentUtils::DispatchEventOnlyToChrome(
11309 0 : this, ToSupports(elem), NS_LITERAL_STRING("MozDOMFullscreen:Entered"),
11310 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
11311 : }
11312 :
11313 : // The origin which is fullscreen gets changed. Trigger an event so
11314 : // that the chrome knows to pop up a warning UI. Note that
11315 : // previousFullscreenDoc == nullptr upon first entry, so we always
11316 0 : // take this path on the first entry. Also note that, in a multi-
11317 0 : // process browser, the code in content process is responsible for
11318 : // sending message with the origin to its parent, and the parent
11319 0 : // shouldn't rely on this event itself.
11320 : if (aRequest.mShouldNotifyNewOrigin &&
11321 : !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11322 : DispatchCustomEventWithFlush(
11323 0 : this, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
11324 : /* Bubbles */ true, /* ChromeOnly */ true);
11325 0 : }
11326 :
11327 : // Dispatch "fullscreenchange" events. Note this loop is in reverse
11328 : // order so that the events for the root document arrives before the leaf
11329 0 : // document, as required by the spec.
11330 : for (uint32_t i = 0; i < changed.Length(); ++i) {
11331 0 : DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11332 0 : }
11333 : return true;
11334 : }
11335 0 :
11336 : bool
11337 0 : nsIDocument::FullscreenEnabled(CallerType aCallerType)
11338 0 : {
11339 : return !GetFullscreenError(this, aCallerType == CallerType::System);
11340 : }
11341 :
11342 : void
11343 0 : nsIDocument::SetOrientationPendingPromise(Promise* aPromise)
11344 : {
11345 0 : mOrientationPendingPromise = aPromise;
11346 0 : }
11347 :
11348 : static void
11349 : DispatchPointerLockChange(nsIDocument* aTarget)
11350 0 : {
11351 : if (!aTarget) {
11352 0 : return;
11353 0 : }
11354 :
11355 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
11356 : new AsyncEventDispatcher(aTarget,
11357 : NS_LITERAL_STRING("pointerlockchange"),
11358 0 : true,
11359 : false);
11360 0 : asyncDispatcher->PostDOMEvent();
11361 0 : }
11362 0 :
11363 0 : static void
11364 : DispatchPointerLockError(nsIDocument* aTarget, const char* aMessage)
11365 0 : {
11366 : if (!aTarget) {
11367 : return;
11368 0 : }
11369 :
11370 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
11371 0 : new AsyncEventDispatcher(aTarget,
11372 0 : NS_LITERAL_STRING("pointerlockerror"),
11373 0 : true,
11374 0 : false);
11375 0 : asyncDispatcher->PostDOMEvent();
11376 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
11377 : NS_LITERAL_CSTRING("DOM"), aTarget,
11378 : nsContentUtils::eDOM_PROPERTIES,
11379 : aMessage);
11380 : }
11381 :
11382 : class PointerLockRequest final : public Runnable
11383 : {
11384 : public:
11385 : PointerLockRequest(Element* aElement, bool aUserInputOrChromeCaller)
11386 : : mozilla::Runnable("PointerLockRequest")
11387 0 : , mElement(do_GetWeakReference(aElement))
11388 : , mDocument(do_GetWeakReference(aElement->OwnerDoc()))
11389 : , mUserInputOrChromeCaller(aUserInputOrChromeCaller)
11390 : {}
11391 0 :
11392 : NS_IMETHOD Run() final;
11393 :
11394 : private:
11395 0 : nsWeakPtr mElement;
11396 0 : nsWeakPtr mDocument;
11397 : bool mUserInputOrChromeCaller;
11398 : };
11399 :
11400 0 : static const char*
11401 : GetPointerLockError(Element* aElement, Element* aCurrentLock,
11402 : bool aNoFocusCheck = false)
11403 : {
11404 0 : // Check if pointer lock pref is enabled
11405 : if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
11406 : return "PointerLockDeniedDisabled";
11407 : }
11408 :
11409 0 : nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
11410 : if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) {
11411 : return "PointerLockDeniedInUse";
11412 0 : }
11413 0 :
11414 : if (!aElement->IsInComposedDoc()) {
11415 : return "PointerLockDeniedNotInDocument";
11416 0 : }
11417 0 :
11418 : if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) {
11419 : return "PointerLockDeniedSandboxed";
11420 0 : }
11421 :
11422 : // Check if the element is in a document with a docshell.
11423 : if (!ownerDoc->GetContainer()) {
11424 0 : return "PointerLockDeniedHidden";
11425 0 : }
11426 : nsCOMPtr<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow();
11427 : if (!ownerWindow) {
11428 : return "PointerLockDeniedHidden";
11429 0 : }
11430 0 : nsCOMPtr<nsPIDOMWindowInner> ownerInnerWindow = ownerDoc->GetInnerWindow();
11431 0 : if (!ownerInnerWindow) {
11432 0 : return "PointerLockDeniedHidden";
11433 : }
11434 : if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
11435 : return "PointerLockDeniedHidden";
11436 : }
11437 :
11438 : nsCOMPtr<nsPIDOMWindowOuter> top = ownerWindow->GetScriptableTop();
11439 : if (!top || !top->GetExtantDoc() || top->GetExtantDoc()->Hidden()) {
11440 0 : return "PointerLockDeniedHidden";
11441 : }
11442 :
11443 : if (!aNoFocusCheck) {
11444 : mozilla::ErrorResult rv;
11445 : if (!top->GetExtantDoc()->HasFocus(rv)) {
11446 : return "PointerLockDeniedNotFocused";
11447 : }
11448 0 : }
11449 0 :
11450 0 : return nullptr;
11451 0 : }
11452 0 :
11453 : static void
11454 0 : ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
11455 0 : Element* aPointerLockedElement)
11456 0 : {
11457 0 : // aDocument here is not really necessary, as it is the uncomposed
11458 0 : // document of both aElement and aPointerLockedElement as far as one
11459 0 : // is not nullptr, and they wouldn't both be nullptr in any case.
11460 : // But since the caller of this function should have known what the
11461 : // document is, we just don't try to figure out what it should be.
11462 : MOZ_ASSERT(aDocument);
11463 0 : MOZ_ASSERT(aElement != aPointerLockedElement);
11464 0 : if (aPointerLockedElement) {
11465 : MOZ_ASSERT(aPointerLockedElement->GetComposedDoc() == aDocument);
11466 : aPointerLockedElement->ClearPointerLock();
11467 : }
11468 0 : if (aElement) {
11469 0 : MOZ_ASSERT(aElement->GetComposedDoc() == aDocument);
11470 0 : aElement->SetPointerLock();
11471 : EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
11472 : EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
11473 0 : NS_ASSERTION(EventStateManager::sPointerLockedElement &&
11474 : EventStateManager::sPointerLockedDoc,
11475 0 : "aElement and this should support weak references!");
11476 0 : } else {
11477 0 : EventStateManager::sPointerLockedElement = nullptr;
11478 0 : EventStateManager::sPointerLockedDoc = nullptr;
11479 0 : }
11480 : // Retarget all events to aElement via capture or
11481 0 : // stop retargeting if aElement is nullptr.
11482 0 : nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
11483 : DispatchPointerLockChange(aDocument);
11484 0 : }
11485 :
11486 0 : NS_IMETHODIMP
11487 0 : PointerLockRequest::Run()
11488 0 : {
11489 0 : nsCOMPtr<Element> e = do_QueryReferent(mElement);
11490 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
11491 : nsDocument* d = static_cast<nsDocument*>(doc.get());
11492 0 : const char* error = nullptr;
11493 : if (!e || !d || !e->GetComposedDoc()) {
11494 : error = "PointerLockDeniedNotInDocument";
11495 0 : } else if (e->GetComposedDoc() != d) {
11496 0 : error = "PointerLockDeniedMovedDocument";
11497 0 : }
11498 : if (!error) {
11499 : nsCOMPtr<Element> pointerLockedElement =
11500 : do_QueryReferent(EventStateManager::sPointerLockedElement);
11501 : if (e == pointerLockedElement) {
11502 0 : DispatchPointerLockChange(d);
11503 0 : return NS_OK;
11504 : }
11505 0 : // Note, we must bypass focus change, so pass true as the last parameter!
11506 0 : error = GetPointerLockError(e, pointerLockedElement, true);
11507 : // Another element in the same document is requesting pointer lock,
11508 0 : // just grant it without user input check.
11509 0 : if (!error && pointerLockedElement) {
11510 0 : ChangePointerLockedElement(e, d, pointerLockedElement);
11511 : return NS_OK;
11512 : }
11513 0 : }
11514 0 : // If it is neither user input initiated, nor requested in fullscreen,
11515 0 : // it should be rejected.
11516 0 : if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) {
11517 0 : error = "PointerLockDeniedNotInputDriven";
11518 : }
11519 : if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
11520 : error = "PointerLockDeniedFailedToLock";
11521 0 : }
11522 : if (error) {
11523 0 : DispatchPointerLockError(d, error);
11524 : return NS_OK;
11525 : }
11526 :
11527 0 : ChangePointerLockedElement(e, d, nullptr);
11528 0 : nsContentUtils::DispatchEventOnlyToChrome(
11529 0 : doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"),
11530 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
11531 : return NS_OK;
11532 : }
11533 0 :
11534 0 : void
11535 0 : nsIDocument::RequestPointerLock(Element* aElement, CallerType aCallerType)
11536 : {
11537 : NS_ASSERTION(aElement,
11538 0 : "Must pass non-null element to nsDocument::RequestPointerLock");
11539 0 :
11540 : nsCOMPtr<Element> pointerLockedElement =
11541 0 : do_QueryReferent(EventStateManager::sPointerLockedElement);
11542 0 : if (aElement == pointerLockedElement) {
11543 : DispatchPointerLockChange(this);
11544 : return;
11545 : }
11546 0 :
11547 : if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) {
11548 0 : DispatchPointerLockError(this, msg);
11549 : return;
11550 : }
11551 :
11552 0 : bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() ||
11553 : aCallerType == CallerType::System;
11554 0 : nsCOMPtr<nsIRunnable> request =
11555 0 : new PointerLockRequest(aElement, userInputOrSystemCaller);
11556 : Dispatch(TaskCategory::Other, request.forget());
11557 : }
11558 :
11559 0 : bool
11560 0 : nsIDocument::SetPointerLock(Element* aElement, int aCursorStyle)
11561 0 : {
11562 0 : MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
11563 : "We should be either unlocking pointer (aElement is nullptr), "
11564 : "or locking pointer to an element in this document");
11565 : #ifdef DEBUG
11566 0 : if (!aElement) {
11567 0 : nsCOMPtr<nsIDocument> pointerLockedDoc =
11568 : do_QueryReferent(EventStateManager::sPointerLockedDoc);
11569 : MOZ_ASSERT(pointerLockedDoc == this);
11570 : }
11571 0 : #endif
11572 0 :
11573 0 : nsIPresShell* shell = GetShell();
11574 0 : if (!shell) {
11575 : NS_WARNING("SetPointerLock(): No PresShell");
11576 : if (!aElement) {
11577 0 : // If we are unlocking pointer lock, but for some reason the doc
11578 0 : // has already detached from the presshell, just ask the event
11579 0 : // state manager to release the pointer.
11580 0 : EventStateManager::SetPointerLock(nullptr, nullptr);
11581 0 : return true;
11582 : }
11583 : return false;
11584 : }
11585 0 : nsPresContext* presContext = shell->GetPresContext();
11586 : if (!presContext) {
11587 : NS_WARNING("SetPointerLock(): Unable to get PresContext");
11588 : return false;
11589 : }
11590 :
11591 0 : nsCOMPtr<nsIWidget> widget;
11592 0 : nsIFrame* rootFrame = shell->GetRootFrame();
11593 0 : if (!NS_WARN_IF(!rootFrame)) {
11594 0 : widget = rootFrame->GetNearestWidget();
11595 : NS_WARNING_ASSERTION(
11596 : widget,
11597 : "SetPointerLock(): Unable to find widget in "
11598 : "shell->GetRootFrame()->GetNearestWidget();");
11599 : if (aElement && !widget) {
11600 8 : return false;
11601 : }
11602 8 : }
11603 8 :
11604 : // Hide the cursor and set pointer lock for future mouse events
11605 : RefPtr<EventStateManager> esm = presContext->EventStateManager();
11606 : esm->SetCursor(aCursorStyle, nullptr, false,
11607 0 : 0.0f, 0.0f, widget, true);
11608 0 : EventStateManager::SetPointerLock(widget, aElement);
11609 0 :
11610 : return true;
11611 0 : }
11612 :
11613 : void
11614 : nsIDocument::UnlockPointer(nsIDocument* aDoc)
11615 : {
11616 0 : if (!EventStateManager::sIsPointerLocked) {
11617 0 : return;
11618 : }
11619 :
11620 : nsCOMPtr<nsIDocument> pointerLockedDoc =
11621 0 : do_QueryReferent(EventStateManager::sPointerLockedDoc);
11622 0 : if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
11623 0 : return;
11624 : }
11625 : if (!pointerLockedDoc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
11626 : return;
11627 0 : }
11628 :
11629 17 : nsCOMPtr<Element> pointerLockedElement =
11630 17 : do_QueryReferent(EventStateManager::sPointerLockedElement);
11631 0 : ChangePointerLockedElement(nullptr, pointerLockedDoc, pointerLockedElement);
11632 8 :
11633 0 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
11634 : new AsyncEventDispatcher(pointerLockedElement,
11635 0 : NS_LITERAL_STRING("MozDOMPointerLock:Exited"),
11636 0 : true, true);
11637 : asyncDispatcher->RunDOMEventWhenSafe();
11638 : }
11639 0 :
11640 0 : void
11641 : nsIDocument::UpdateVisibilityState()
11642 17 : {
11643 : dom::VisibilityState oldState = mVisibilityState;
11644 : mVisibilityState = ComputeVisibilityState();
11645 48 : if (oldState != mVisibilityState) {
11646 : nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
11647 : NS_LITERAL_STRING("visibilitychange"),
11648 : /* bubbles = */ true,
11649 : /* cancelable = */ false);
11650 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
11651 : }
11652 :
11653 : if (mVisibilityState == dom::VisibilityState::Visible) {
11654 96 : MaybeActiveMediaComponents();
11655 48 : }
11656 : }
11657 :
11658 : VisibilityState
11659 0 : nsIDocument::ComputeVisibilityState() const
11660 : {
11661 : // We have to check a few pieces of information here:
11662 : // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
11663 0 : // 2) Do we have an outer window? If not, we're hidden. Note that we don't
11664 : // want to use GetWindow here because it does weird groveling for windows
11665 : // in some cases.
11666 0 : // 3) Is our outer window background? If so, we're hidden.
11667 : // Otherwise, we're visible.
11668 0 : if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
11669 0 : mWindow->GetOuterWindow()->IsBackground()) {
11670 0 : return dom::VisibilityState::Hidden;
11671 : }
11672 :
11673 0 : return dom::VisibilityState::Visible;
11674 : }
11675 9 :
11676 : void
11677 : nsIDocument::PostVisibilityUpdateEvent()
11678 : {
11679 0 : nsCOMPtr<nsIRunnable> event =
11680 : NewRunnableMethod("nsIDocument::UpdateVisibilityState",
11681 : this,
11682 : &nsIDocument::UpdateVisibilityState);
11683 0 : Dispatch(TaskCategory::Other, event.forget());
11684 : }
11685 13 :
11686 : void
11687 0 : nsIDocument::MaybeActiveMediaComponents()
11688 13 : {
11689 : if (!mWindow) {
11690 : return;
11691 0 : }
11692 0 :
11693 : GetWindow()->MaybeActiveMediaComponents();
11694 13 : }
11695 0 :
11696 : /* virtual */ void
11697 : nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aSizes) const
11698 0 : {
11699 0 : nsINode::AddSizeOfExcludingThis(aSizes, &aSizes.mDOMOtherSize);
11700 :
11701 : if (mPresShell) {
11702 : mPresShell->AddSizeOfIncludingThis(aSizes);
11703 : }
11704 :
11705 13 : aSizes.mPropertyTablesSize +=
11706 : mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11707 :
11708 13 : if (EventListenerManager* elm = GetExistingListenerManager()) {
11709 : aSizes.mDOMEventListenersCount += elm->ListenerCount();
11710 13 : }
11711 13 :
11712 0 : if (mNodeInfoManager) {
11713 : mNodeInfoManager->AddSizeOfIncludingThis(aSizes);
11714 : }
11715 0 :
11716 : // Measurement of the following members may be added later if DMD finds it
11717 : // is worthwhile:
11718 52 : // - many!
11719 0 : }
11720 156 :
11721 0 : void
11722 : nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const
11723 : {
11724 : aWindowSizes.mDOMOtherSize += aWindowSizes.mState.mMallocSizeOf(this);
11725 0 : DocAddSizeOfExcludingThis(aWindowSizes);
11726 : }
11727 52 :
11728 : static size_t
11729 : SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets,
11730 : MallocSizeOf aMallocSizeOf)
11731 0 : {
11732 : size_t n = 0;
11733 : n += aSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
11734 : for (StyleSheet* sheet : aSheets) {
11735 : if (!sheet->GetAssociatedDocumentOrShadowRoot()) {
11736 : // Avoid over-reporting shared sheets.
11737 : continue;
11738 0 : }
11739 : n += sheet->SizeOfIncludingThis(aMallocSizeOf);
11740 : }
11741 : return n;
11742 1 : }
11743 :
11744 82 : void
11745 82 : nsDocument::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
11746 : size_t* aNodeSize) const
11747 : {
11748 : // This AddSizeOfExcludingThis() overrides the one from nsINode. But
11749 0 : // nsDocuments can only appear at the top of the DOM tree, and we use the
11750 : // specialized DocAddSizeOfExcludingThis() in that case. So this should never
11751 41 : // be called.
11752 41 : MOZ_CRASH();
11753 : }
11754 28 :
11755 0 : static void
11756 : AddSizeOfNodeTree(nsIContent* aNode, nsWindowSizes& aWindowSizes)
11757 0 : {
11758 0 : size_t nodeSize = 0;
11759 : aNode->AddSizeOfIncludingThis(aWindowSizes, &nodeSize);
11760 13 :
11761 0 : // This is where we transfer the nodeSize obtained from
11762 : // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
11763 0 : switch (aNode->NodeType()) {
11764 0 : case nsINode::ELEMENT_NODE:
11765 : aWindowSizes.mDOMElementNodesSize += nodeSize;
11766 : break;
11767 0 : case nsINode::TEXT_NODE:
11768 0 : aWindowSizes.mDOMTextNodesSize += nodeSize;
11769 : break;
11770 : case nsINode::CDATA_SECTION_NODE:
11771 0 : aWindowSizes.mDOMCDATANodesSize += nodeSize;
11772 0 : break;
11773 56 : case nsINode::COMMENT_NODE:
11774 : aWindowSizes.mDOMCommentNodesSize += nodeSize;
11775 0 : break;
11776 : default:
11777 : aWindowSizes.mDOMOtherSize += nodeSize;
11778 13 : break;
11779 : }
11780 :
11781 : if (EventListenerManager* elm = aNode->GetExistingListenerManager()) {
11782 : aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
11783 : }
11784 :
11785 : AllChildrenIterator iter(aNode, nsIContent::eAllChildren);
11786 : for (nsIContent* n = iter.GetNextChild(); n; n = iter.GetNextChild()) {
11787 39 : AddSizeOfNodeTree(n, aWindowSizes);
11788 39 : }
11789 26 : }
11790 26 :
11791 : void
11792 : nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
11793 : {
11794 : // We use AllChildrenIterator to iterate over DOM nodes in
11795 : // AddSizeOfNodeTree(). The obvious place to start is at the document's root
11796 : // element, using GetRootElement(). However, that will miss comment nodes
11797 : // that are siblings of the root element. Instead we use
11798 : // GetFirstChild()/GetNextSibling() to traverse the document's immediate
11799 : // child nodes, calling AddSizeOfNodeTree() on each to measure them and then
11800 13 : // all their descendants. (The comment nodes won't have any descendants).
11801 : for (nsIContent* node = nsINode::GetFirstChild();
11802 13 : node;
11803 13 : node = node->GetNextSibling()) {
11804 0 : AddSizeOfNodeTree(node, aWindowSizes);
11805 : }
11806 :
11807 0 : // IMPORTANT: for our ComputedValues measurements, we want to measure
11808 0 : // ComputedValues accessible from DOM elements before ComputedValues not
11809 26 : // accessible from DOM elements (i.e. accessible only from the frame tree).
11810 52 : //
11811 0 : // Therefore, the measurement of the nsIDocument superclass must happen after
11812 0 : // the measurement of DOM nodes (above), because nsIDocument contains the
11813 0 : // PresShell, which contains the frame tree.
11814 : nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
11815 :
11816 : aWindowSizes.mLayoutStyleSheetsSize +=
11817 : SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
11818 13 : aWindowSizes.mState.mMallocSizeOf);
11819 26 : // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
11820 : // (the nsLayoutStyleSheetCache singleton does).
11821 13 : aWindowSizes.mLayoutStyleSheetsSize +=
11822 0 : mOnDemandBuiltInUASheets.ShallowSizeOfExcludingThis(
11823 0 : aWindowSizes.mState.mMallocSizeOf);
11824 39 : for (auto& sheetArray : mAdditionalSheets) {
11825 : aWindowSizes.mLayoutStyleSheetsSize +=
11826 0 : SizeOfOwnedSheetArrayExcludingThis(sheetArray,
11827 0 : aWindowSizes.mState.mMallocSizeOf);
11828 : }
11829 13 : // Lumping in the loader with the style-sheets size is not ideal,
11830 0 : // but most of the things in there are in fact stylesheets, so it
11831 : // doesn't seem worthwhile to separate it out.
11832 : aWindowSizes.mLayoutStyleSheetsSize +=
11833 : CSSLoader()->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
11834 :
11835 13 : aWindowSizes.mDOMOtherSize += mAttrStyleSheet
11836 : ? mAttrStyleSheet->DOMSizeOfIncludingThis(
11837 : aWindowSizes.mState.mMallocSizeOf)
11838 0 : : 0;
11839 :
11840 : aWindowSizes.mDOMOtherSize +=
11841 0 : mStyledLinks.ShallowSizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
11842 0 :
11843 0 : aWindowSizes.mDOMOtherSize +=
11844 : mIdentifierMap.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
11845 :
11846 : // Measurement of the following members may be added later if DMD finds it
11847 0 : // is worthwhile:
11848 0 : // - many!
11849 0 : }
11850 :
11851 : already_AddRefed<nsIDocument>
11852 : nsIDocument::Constructor(const GlobalObject& aGlobal,
11853 0 : ErrorResult& rv)
11854 0 : {
11855 0 : nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
11856 0 : if (!global) {
11857 : rv.Throw(NS_ERROR_UNEXPECTED);
11858 : return nullptr;
11859 : }
11860 0 :
11861 : nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
11862 0 : if (!prin) {
11863 0 : rv.Throw(NS_ERROR_UNEXPECTED);
11864 0 : return nullptr;
11865 : }
11866 :
11867 : nsCOMPtr<nsIURI> uri;
11868 0 : NS_NewURI(getter_AddRefs(uri), "about:blank");
11869 : if (!uri) {
11870 : rv.Throw(NS_ERROR_OUT_OF_MEMORY);
11871 0 : return nullptr;
11872 0 : }
11873 0 :
11874 : nsCOMPtr<nsIDocument> doc;
11875 : nsresult res =
11876 : NS_NewDOMDocument(getter_AddRefs(doc),
11877 0 : VoidString(),
11878 : EmptyString(),
11879 0 : nullptr,
11880 : uri,
11881 : uri,
11882 : prin->GetPrincipal(),
11883 0 : true,
11884 : global,
11885 : DocumentFlavorPlain);
11886 : if (NS_FAILED(res)) {
11887 0 : rv.Throw(res);
11888 : return nullptr;
11889 : }
11890 :
11891 0 : doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
11892 :
11893 0 : return doc.forget();
11894 : }
11895 :
11896 : XPathExpression*
11897 0 : nsIDocument::CreateExpression(const nsAString& aExpression,
11898 : XPathNSResolver* aResolver,
11899 : ErrorResult& rv)
11900 : {
11901 : return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
11902 : }
11903 0 :
11904 : nsINode*
11905 : nsIDocument::CreateNSResolver(nsINode& aNodeResolver)
11906 : {
11907 0 : return XPathEvaluator()->CreateNSResolver(aNodeResolver);
11908 : }
11909 104 :
11910 52 : already_AddRefed<XPathResult>
11911 : nsIDocument::Evaluate(JSContext* aCx, const nsAString& aExpression,
11912 : nsINode& aContextNode, XPathNSResolver* aResolver,
11913 0 : uint16_t aType, JS::Handle<JSObject*> aResult,
11914 0 : ErrorResult& rv)
11915 21 : {
11916 7 : return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
11917 : aType, aResult, rv);
11918 : }
11919 0 :
11920 0 : already_AddRefed<nsIXULWindow>
11921 6 : nsIDocument::GetXULWindowIfToplevelChrome() const
11922 : {
11923 : nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
11924 0 : if (!item) {
11925 : return nullptr;
11926 : }
11927 : nsCOMPtr<nsIDocShellTreeOwner> owner;
11928 0 : item->GetTreeOwner(getter_AddRefs(owner));
11929 : nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner);
11930 : if (!xulWin) {
11931 : return nullptr;
11932 0 : }
11933 : nsCOMPtr<nsIDocShell> xulWinShell;
11934 : xulWin->GetDocShell(getter_AddRefs(xulWinShell));
11935 0 : if (!SameCOMIdentity(xulWinShell, item)) {
11936 0 : return nullptr;
11937 0 : }
11938 : return xulWin.forget();
11939 : }
11940 0 :
11941 0 : nsIDocument*
11942 : nsIDocument::GetTopLevelContentDocument()
11943 : {
11944 : nsIDocument* parent;
11945 :
11946 : if (!mLoadedAsData) {
11947 0 : parent = this;
11948 : } else {
11949 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
11950 : if (!window) {
11951 : return nullptr;
11952 : }
11953 0 :
11954 : parent = window->GetExtantDoc();
11955 : if (!parent) {
11956 : return nullptr;
11957 0 : }
11958 0 : }
11959 0 :
11960 : do {
11961 : if (parent->IsTopLevelContentDocument()) {
11962 : break;
11963 : }
11964 :
11965 14 : // If we ever have a non-content parent before we hit a toplevel content
11966 : // parent, then we're never going to find one. Just bail.
11967 14 : if (!parent->IsContentDocument()) {
11968 14 : return nullptr;
11969 0 : }
11970 14 :
11971 : nsIDocument* candidate = parent->GetParentDocument();
11972 : parent = static_cast<nsDocument*>(candidate);
11973 : } while (parent);
11974 0 :
11975 : return parent;
11976 0 : }
11977 0 :
11978 0 : static bool
11979 0 : MightBeChromeScheme(nsIURI* aURI)
11980 : {
11981 : MOZ_ASSERT(aURI);
11982 : bool isChrome = true;
11983 0 : aURI->SchemeIs("chrome", &isChrome);
11984 : return isChrome;
11985 14 : }
11986 :
11987 : static bool
11988 14 : MightBeAboutOrChromeScheme(nsIURI* aURI)
11989 0 : {
11990 28 : MOZ_ASSERT(aURI);
11991 14 : bool isAbout = true;
11992 : aURI->SchemeIs("about", &isAbout);
11993 : return isAbout || MightBeChromeScheme(aURI);
11994 : }
11995 :
11996 : void
11997 : nsIDocument::PropagateUseCounters(nsIDocument* aParentDocument)
11998 : {
11999 0 : MOZ_ASSERT(this != aParentDocument);
12000 :
12001 0 : // Don't count chrome resources, even in the web content.
12002 : nsCOMPtr<nsIURI> uri;
12003 : NodePrincipal()->GetURI(getter_AddRefs(uri));
12004 : if (!uri || MightBeChromeScheme(uri)) {
12005 0 : return;
12006 0 : }
12007 :
12008 : // What really matters here is that our use counters get propagated as
12009 : // high up in the content document hierarchy as possible. So,
12010 0 : // starting with aParentDocument, we need to find the toplevel content
12011 : // document, and propagate our use counters into its
12012 : // mChildDocumentUseCounters.
12013 : nsIDocument* contentParent = aParentDocument->GetTopLevelContentDocument();
12014 :
12015 : if (!contentParent) {
12016 56 : return;
12017 : }
12018 :
12019 52 : contentParent->mChildDocumentUseCounters |= mUseCounters;
12020 : contentParent->mChildDocumentUseCounters |= mChildDocumentUseCounters;
12021 52 : }
12022 :
12023 : void
12024 : nsIDocument::SetPageUseCounter(UseCounter aUseCounter)
12025 0 : {
12026 0 : // We want to set the use counter on the "page" that owns us; the definition
12027 0 : // of "page" depends on what kind of document we are. See the comments below
12028 : // for details. In any event, checking all the conditions below is
12029 : // reasonably expensive, so we cache whether we've notified our owning page.
12030 0 : if (mNotifiedPageForUseCounter[aUseCounter]) {
12031 : return;
12032 26 : }
12033 : mNotifiedPageForUseCounter[aUseCounter] = true;
12034 :
12035 : if (mDisplayDocument) {
12036 : // If we are a resource document, we won't have a docshell and so we won't
12037 : // record any page use counters on this document. Instead, we should
12038 : // forward it up to the document that loaded us.
12039 0 : MOZ_ASSERT(!mDocumentContainer);
12040 0 : mDisplayDocument->SetChildDocumentUseCounter(aUseCounter);
12041 : return;
12042 : }
12043 :
12044 0 : if (IsBeingUsedAsImage()) {
12045 0 : // If this is an SVG image document, we also won't have a docshell.
12046 : MOZ_ASSERT(!mDocumentContainer);
12047 : return;
12048 : }
12049 0 :
12050 : // We only care about use counters in content. If we're already a toplevel
12051 : // content document, then we should have already set the use counter on
12052 : // ourselves, and we are done.
12053 0 : nsIDocument* contentParent = GetTopLevelContentDocument();
12054 : if (!contentParent) {
12055 609 : return;
12056 : }
12057 :
12058 : if (this == contentParent) {
12059 0 : MOZ_ASSERT(GetUseCounter(aUseCounter));
12060 : return;
12061 : }
12062 :
12063 0 : contentParent->SetChildDocumentUseCounter(aUseCounter);
12064 0 : }
12065 0 :
12066 0 : bool
12067 0 : nsIDocument::HasScriptsBlockedBySandbox()
12068 0 : {
12069 0 : return mSandboxFlags & SANDBOXED_SCRIPTS;
12070 : }
12071 :
12072 : bool
12073 0 : nsIDocument::InlineScriptAllowedByCSP()
12074 0 : {
12075 : // this function assumes the inline script is parser created
12076 0 : // (e.g., before setting attribute(!) event handlers)
12077 : nsCOMPtr<nsIContentSecurityPolicy> csp;
12078 : nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
12079 : NS_ENSURE_SUCCESS(rv, true);
12080 0 : bool allowsInlineScript = true;
12081 : if (csp) {
12082 : nsresult rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
12083 0 : EmptyString(), // aNonce
12084 0 : true, // aParserCreated
12085 0 : nullptr, // FIXME get script sample (bug 1314567)
12086 : 0, // aLineNumber
12087 : &allowsInlineScript);
12088 : NS_ENSURE_SUCCESS(rv, true);
12089 0 : }
12090 : return allowsInlineScript;
12091 : }
12092 0 :
12093 : static bool
12094 : ReportExternalResourceUseCounters(nsIDocument* aDocument, void* aData)
12095 : {
12096 0 : const auto reportKind
12097 : = nsDocument::UseCounterReportKind::eIncludeExternalResources;
12098 0 : static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind);
12099 : return true;
12100 : }
12101 :
12102 0 : void
12103 0 : nsIDocument::ReportUseCounters(UseCounterReportKind aKind)
12104 0 : {
12105 0 : static const bool sDebugUseCounters = false;
12106 0 : if (mReportedUseCounters) {
12107 0 : return;
12108 : }
12109 :
12110 : mReportedUseCounters = true;
12111 :
12112 : if (aKind == UseCounterReportKind::eIncludeExternalResources) {
12113 : EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr);
12114 : }
12115 :
12116 : if (Telemetry::HistogramUseCounterCount > 0 &&
12117 : (IsContentDocument() || IsResourceDoc())) {
12118 : nsCOMPtr<nsIURI> uri;
12119 : NodePrincipal()->GetURI(getter_AddRefs(uri));
12120 : if (!uri || MightBeAboutOrChromeScheme(uri)) {
12121 : return;
12122 : }
12123 :
12124 : if (sDebugUseCounters) {
12125 : nsCString spec = uri->GetSpecOrDefault();
12126 :
12127 : // URIs can be rather long for data documents, so truncate them to
12128 : // some reasonable length.
12129 : spec.Truncate(std::min(128U, spec.Length()));
12130 : printf("-- Use counters for %s --\n", spec.get());
12131 : }
12132 :
12133 : // We keep separate counts for individual documents and top-level
12134 : // pages to more accurately track how many web pages might break if
12135 : // certain features were removed. Consider the case of a single
12136 : // HTML document with several SVG images and/or iframes with
12137 : // sub-documents of their own. If we maintained a single set of use
12138 : // counters and all the sub-documents use a particular feature, then
12139 : // telemetry would indicate that we would be breaking N documents if
12140 : // that feature were removed. Whereas with a document/top-level
12141 : // page split, we can see that N documents would be affected, but
12142 0 : // only a single web page would be affected.
12143 0 :
12144 0 : // The difference between the values of these two histograms and the
12145 : // related use counters below tell us how many pages did *not* use
12146 : // the feature in question. For instance, if we see that a given
12147 0 : // session has destroyed 30 content documents, but a particular use
12148 0 : // counter shows only a count of 5, we can infer that the use
12149 0 : // counter was *not* used in 25 of those 30 documents.
12150 : //
12151 : // We do things this way, rather than accumulating a boolean flag
12152 0 : // for each use counter, to avoid sending histograms for features
12153 0 : // that don't get widely used. Doing things in this fashion means
12154 : // smaller telemetry payloads and faster processing on the server
12155 0 : // side.
12156 : Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1);
12157 : if (IsTopLevelContentDocument()) {
12158 : Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1);
12159 : }
12160 :
12161 : for (int32_t c = 0;
12162 : c < eUseCounter_Count; ++c) {
12163 : UseCounter uc = static_cast<UseCounter>(c);
12164 :
12165 : Telemetry::HistogramID id =
12166 0 : static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter + uc * 2);
12167 : bool value = GetUseCounter(uc);
12168 :
12169 0 : if (value) {
12170 0 : if (sDebugUseCounters) {
12171 : const char* name = Telemetry::GetHistogramName(id);
12172 0 : if (name) {
12173 : printf(" %s", name);
12174 0 : } else {
12175 : printf(" #%d", id);
12176 : }
12177 : printf(": %d\n", value);
12178 : }
12179 :
12180 : Telemetry::Accumulate(id, 1);
12181 : }
12182 :
12183 : if (IsTopLevelContentDocument()) {
12184 : id = static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter +
12185 0 : uc * 2 + 1);
12186 : value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
12187 :
12188 : if (value) {
12189 : if (sDebugUseCounters) {
12190 : const char* name = Telemetry::GetHistogramName(id);
12191 0 : if (name) {
12192 : printf(" %s", name);
12193 : } else {
12194 0 : printf(" #%d", id);
12195 : }
12196 : printf(": %d\n", value);
12197 : }
12198 :
12199 0 : Telemetry::Accumulate(id, 1);
12200 0 : }
12201 0 : }
12202 0 : }
12203 : }
12204 :
12205 0 : if (IsTopLevelContentDocument() && !IsResourceDoc()) {
12206 : using mozilla::Telemetry::LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE;
12207 : LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE label;
12208 : switch (mViewportOverflowType) {
12209 : #define CASE_OVERFLOW_TYPE(t_) \
12210 0 : case ViewportOverflowType::t_: \
12211 : label = LABELS_HIDDEN_VIEWPORT_OVERFLOW_TYPE::t_; \
12212 0 : break;
12213 0 : CASE_OVERFLOW_TYPE(NoOverflow)
12214 : CASE_OVERFLOW_TYPE(Desktop)
12215 : CASE_OVERFLOW_TYPE(ButNotMinScaleSize)
12216 0 : CASE_OVERFLOW_TYPE(MinScaleSize)
12217 0 : #undef CASE_OVERFLOW_TYPE
12218 0 : }
12219 0 : Telemetry::AccumulateCategorical(label);
12220 0 : }
12221 : }
12222 :
12223 0 : void
12224 0 : nsIDocument::UpdateIntersectionObservations()
12225 0 : {
12226 0 : if (mIntersectionObservers.IsEmpty()) {
12227 : return;
12228 0 : }
12229 0 :
12230 0 : DOMHighResTimeStamp time = 0;
12231 : if (nsPIDOMWindowInner* window = GetInnerWindow()) {
12232 : Performance* perf = window->GetPerformance();
12233 : if (perf) {
12234 : time = perf->Now();
12235 : }
12236 0 : }
12237 : nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count());
12238 0 : for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
12239 0 : DOMIntersectionObserver* observer = iter.Get()->GetKey();
12240 : observers.AppendElement(observer);
12241 0 : }
12242 : for (const auto& observer : observers) {
12243 0 : if (observer) {
12244 : observer->Update(this, time);
12245 0 : }
12246 0 : }
12247 : }
12248 :
12249 : void
12250 0 : nsIDocument::ScheduleIntersectionObserverNotification()
12251 : {
12252 0 : if (mIntersectionObservers.IsEmpty()) {
12253 0 : return;
12254 0 : }
12255 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
12256 : nsCOMPtr<nsIRunnable> notification =
12257 0 : NewRunnableMethod("nsDocument::NotifyIntersectionObservers",
12258 0 : this,
12259 0 : &nsDocument::NotifyIntersectionObservers);
12260 : Dispatch(TaskCategory::Other, notification.forget());
12261 : }
12262 0 :
12263 : void
12264 : nsIDocument::NotifyIntersectionObservers()
12265 0 : {
12266 : nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count());
12267 0 : for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
12268 0 : DOMIntersectionObserver* observer = iter.Get()->GetKey();
12269 : observers.AppendElement(observer);
12270 : }
12271 : for (const auto& observer : observers) {
12272 0 : if (observer) {
12273 : observer->Notify();
12274 0 : }
12275 0 : }
12276 0 : }
12277 :
12278 : static bool
12279 0 : NotifyLayerManagerRecreatedCallback(nsIDocument* aDocument, void* aData)
12280 : {
12281 0 : aDocument->NotifyLayerManagerRecreated();
12282 0 : return true;
12283 : }
12284 0 :
12285 : void
12286 : nsIDocument::NotifyLayerManagerRecreated()
12287 : {
12288 0 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
12289 : EnumerateSubDocuments(NotifyLayerManagerRecreatedCallback, nullptr);
12290 0 : }
12291 :
12292 : XPathEvaluator*
12293 : nsIDocument::XPathEvaluator()
12294 0 : {
12295 : if (!mXPathEvaluator) {
12296 0 : mXPathEvaluator.reset(new dom::XPathEvaluator(this));
12297 0 : }
12298 : return mXPathEvaluator.get();
12299 : }
12300 0 :
12301 : already_AddRefed<nsIDocumentEncoder>
12302 0 : nsIDocument::GetCachedEncoder()
12303 137 : {
12304 0 : return mCachedEncoder.forget();
12305 : }
12306 :
12307 168 : void
12308 0 : nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
12309 336 : {
12310 168 : mCachedEncoder = aEncoder;
12311 : }
12312 :
12313 618 : void
12314 : nsIDocument::SetContentTypeInternal(const nsACString& aType)
12315 0 : {
12316 : if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
12317 : aType.EqualsLiteral("application/xhtml+xml")) {
12318 : mDefaultElementType = kNameSpaceID_XHTML;
12319 0 : }
12320 :
12321 0 : mCachedEncoder = nullptr;
12322 : mContentType = aType;
12323 : mContentTypeForWriteCalls = aType;
12324 : }
12325 2 :
12326 : nsILoadContext*
12327 0 : nsIDocument::GetLoadContext() const
12328 0 : {
12329 0 : return mDocumentContainer;
12330 : }
12331 :
12332 0 : nsIDocShell*
12333 : nsIDocument::GetDocShell() const
12334 36 : {
12335 36 : return mDocumentContainer;
12336 0 : }
12337 18 :
12338 : void
12339 0 : nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
12340 0 : {
12341 0 : mStateObjectContainer = scContainer;
12342 0 : mStateObjectCached = nullptr;
12343 : }
12344 18 :
12345 0 : already_AddRefed<Element>
12346 : nsIDocument::CreateHTMLElement(nsAtom* aTag)
12347 : {
12348 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
12349 0 : nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
12350 : ELEMENT_NODE);
12351 : MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
12352 0 :
12353 0 : nsCOMPtr<Element> element;
12354 0 : DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
12355 0 : nodeInfo.forget(),
12356 0 : mozilla::dom::NOT_FROM_PARSER);
12357 :
12358 0 : MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
12359 0 : return element.forget();
12360 : }
12361 0 :
12362 : bool
12363 : MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
12364 0 : {
12365 : nsCOMArray<nsIDocument>* documents =
12366 0 : static_cast<nsCOMArray<nsIDocument>*>(aData);
12367 0 : if (aDoc) {
12368 0 : aDoc->SetIsInSyncOperation(true);
12369 0 : if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc->GetInnerWindow()) {
12370 0 : window->TimeoutManager().BeginSyncOperation();
12371 : }
12372 0 : documents->AppendObject(aDoc);
12373 0 : aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
12374 0 : }
12375 0 : return true;
12376 0 : }
12377 :
12378 : nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
12379 : {
12380 0 : mMicroTaskLevel = 0;
12381 : CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
12382 0 : if (ccjs) {
12383 : mMicroTaskLevel = ccjs->MicroTaskLevel();
12384 0 : ccjs->SetMicroTaskLevel(0);
12385 0 : }
12386 0 : if (aDoc) {
12387 : if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
12388 0 : if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
12389 : nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
12390 0 : MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
12391 0 : }
12392 0 : }
12393 : }
12394 0 : }
12395 :
12396 : nsAutoSyncOperation::~nsAutoSyncOperation()
12397 707 : {
12398 : for (int32_t i = 0; i < mDocuments.Count(); ++i) {
12399 : if (nsCOMPtr<nsPIDOMWindowInner> window = mDocuments[i]->GetInnerWindow()) {
12400 : window->TimeoutManager().EndSyncOperation();
12401 : }
12402 : mDocuments[i]->SetIsInSyncOperation(false);
12403 : }
12404 : CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
12405 : if (ccjs) {
12406 : ccjs->SetMicroTaskLevel(mMicroTaskLevel);
12407 707 : }
12408 : }
12409 :
12410 : gfxUserFontSet*
12411 707 : nsIDocument::GetUserFontSet(bool aFlushUserFontSet)
12412 707 : {
12413 : // We want to initialize the user font set lazily the first time the
12414 : // user asks for it, rather than building it too early and forcing
12415 : // rule cascade creation. Thus we try to enforce the invariant that
12416 : // we *never* build the user font set until the first call to
12417 : // GetUserFontSet. However, once it's been requested, we can't wait
12418 : // for somebody to call GetUserFontSet in order to rebuild it (see
12419 29 : // comments below in MarkUserFontSetDirty for why).
12420 : #ifdef DEBUG
12421 : bool userFontSetGottenBefore = mGetUserFontSetCalled;
12422 : #endif
12423 0 : // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
12424 : // flush.
12425 : mGetUserFontSetCalled = true;
12426 1414 : if (mFontFaceSetDirty && aFlushUserFontSet) {
12427 : // If this assertion fails, and there have actually been changes to
12428 : // @font-face rules, then we will call StyleChangeReflow in
12429 : // FlushUserFontSet. If we're in the middle of reflow,
12430 0 : // that's a bad thing to do, and the caller was responsible for
12431 : // flushing first. If we're not (e.g., in frame construction), it's
12432 : // ok.
12433 : NS_ASSERTION(!userFontSetGottenBefore ||
12434 156 : !GetShell() ||
12435 : !GetShell()->IsReflowLocked(),
12436 0 : "FlushUserFontSet should have been called first");
12437 : FlushUserFontSet();
12438 : }
12439 :
12440 : if (!mFontFaceSet) {
12441 : return nullptr;
12442 144 : }
12443 :
12444 : return mFontFaceSet->GetUserFontSet();
12445 : }
12446 23 :
12447 : void
12448 0 : nsIDocument::FlushUserFontSet()
12449 0 : {
12450 0 : if (!mGetUserFontSetCalled) {
12451 46 : return; // No one cares about this font set yet, but we want to be careful
12452 0 : // to not unset our mFontFaceSetDirty bit, so when someone really
12453 : // does we'll create it.
12454 : }
12455 :
12456 69 : if (!mFontFaceSetDirty) {
12457 0 : return;
12458 0 : }
12459 :
12460 : mFontFaceSetDirty = false;
12461 23 :
12462 46 : if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
12463 0 : nsTArray<nsFontFaceRuleContainer> rules;
12464 : nsIPresShell* shell = GetShell();
12465 : if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) {
12466 : return;
12467 : }
12468 :
12469 :
12470 23 : if (!mFontFaceSet && !rules.IsEmpty()) {
12471 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
12472 0 : mFontFaceSet = new FontFaceSet(window, this);
12473 : }
12474 :
12475 : bool changed = false;
12476 : if (mFontFaceSet) {
12477 : changed = mFontFaceSet->UpdateRules(rules);
12478 : }
12479 0 :
12480 : // We need to enqueue a style change reflow (for later) to
12481 0 : // reflect that we're modifying @font-face rules. (However,
12482 : // without a reflow, nothing will happen to start any downloads
12483 : // that are needed.)
12484 : if (changed && shell) {
12485 : if (nsPresContext* presContext = shell->GetPresContext()) {
12486 : presContext->UserFontSetUpdated();
12487 : }
12488 10 : }
12489 : }
12490 : }
12491 :
12492 0 : void
12493 : nsIDocument::MarkUserFontSetDirty()
12494 0 : {
12495 0 : if (!mGetUserFontSetCalled) {
12496 0 : // We want to lazily build the user font set the first time it's
12497 0 : // requested (so we don't force creation of rule cascades too
12498 : // early), so don't do anything now.
12499 0 : return;
12500 : }
12501 :
12502 : mFontFaceSetDirty = true;
12503 0 : }
12504 :
12505 0 : FontFaceSet*
12506 : nsIDocument::Fonts()
12507 : {
12508 : if (!mFontFaceSet) {
12509 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
12510 0 : mFontFaceSet = new FontFaceSet(window, this);
12511 0 : GetUserFontSet(); // this will cause the user font set to be created/updated
12512 : }
12513 0 : return mFontFaceSet;
12514 : }
12515 :
12516 : void
12517 0 : nsIDocument::ReportHasScrollLinkedEffect()
12518 : {
12519 0 : if (mHasScrollLinkedEffect) {
12520 : // We already did this once for this document, don't do it again.
12521 0 : return;
12522 0 : }
12523 : mHasScrollLinkedEffect = true;
12524 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
12525 0 : NS_LITERAL_CSTRING("Async Pan/Zoom"),
12526 : this, nsContentUtils::eLAYOUT_PROPERTIES,
12527 0 : "ScrollLinkedEffectFound2");
12528 : }
12529 0 :
12530 0 : void
12531 0 : nsIDocument::SetUserHasInteracted(bool aUserHasInteracted)
12532 0 : {
12533 0 : MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
12534 : ("Document %p has been interacted by user.", this));
12535 0 : mUserHasInteracted = aUserHasInteracted;
12536 : }
12537 :
12538 0 : void
12539 : nsIDocument::NotifyUserActivation()
12540 0 : {
12541 0 : ActivateByUserGesture();
12542 0 : // Activate parent document which has same principle on the parent chain.
12543 0 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
12544 : nsCOMPtr<nsIDocument> parent = GetSameTypeParentDocument();
12545 : while (parent) {
12546 : parent->MaybeActivateByUserGesture(principal);
12547 : parent = parent->GetSameTypeParentDocument();
12548 0 : }
12549 0 : }
12550 :
12551 : void
12552 : nsIDocument::MaybeActivateByUserGesture(nsIPrincipal* aPrincipal)
12553 : {
12554 0 : bool isEqual = false;
12555 : nsresult rv = aPrincipal->Equals(NodePrincipal(), &isEqual);
12556 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12557 : return;
12558 : }
12559 :
12560 0 : // If a child frame is actived, it would always activate the top frame and its
12561 : // parent frames which has same priciple.
12562 0 : if (isEqual || IsTopLevelContentDocument()) {
12563 : ActivateByUserGesture();
12564 : }
12565 : }
12566 0 :
12567 : void
12568 0 : nsIDocument::ActivateByUserGesture()
12569 : {
12570 : if (mUserHasActivatedInteraction) {
12571 : return;
12572 0 : }
12573 0 :
12574 0 : MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
12575 : ("Document %p has been activated by user.", this));
12576 : mUserHasActivatedInteraction = true;
12577 : }
12578 0 :
12579 : bool
12580 : nsIDocument::HasBeenUserActivated()
12581 : {
12582 0 : if (!mUserHasActivatedInteraction) {
12583 : // If one of its parent on the parent chain has been activated and has same
12584 0 : // principal, then this child would also be treated as activated.
12585 0 : nsIDocument* parent =
12586 0 : GetFirstParentDocumentWithSamePrincipal(NodePrincipal());
12587 0 : if (parent) {
12588 0 : mUserHasActivatedInteraction = parent->HasBeenUserActivated();
12589 0 : }
12590 0 : }
12591 :
12592 : return mUserHasActivatedInteraction;
12593 0 : }
12594 :
12595 : nsIDocument*
12596 0 : nsIDocument::GetFirstParentDocumentWithSamePrincipal(nsIPrincipal* aPrincipal)
12597 : {
12598 : MOZ_ASSERT(aPrincipal);
12599 : nsIDocument* parent = GetSameTypeParentDocument();
12600 : while (parent) {
12601 : bool isEqual = false;
12602 : nsresult rv = aPrincipal->Equals(parent->NodePrincipal(), &isEqual);
12603 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12604 : return nullptr;
12605 0 : }
12606 0 :
12607 : if (isEqual) {
12608 : return parent;
12609 : }
12610 0 : parent = parent->GetSameTypeParentDocument();
12611 0 : }
12612 0 : MOZ_ASSERT(!parent);
12613 : return nullptr;
12614 : }
12615 :
12616 0 : nsIDocument*
12617 : nsIDocument::GetSameTypeParentDocument()
12618 : {
12619 : nsCOMPtr<nsIDocShellTreeItem> current = GetDocShell();
12620 : if (!current) {
12621 : return nullptr;
12622 : }
12623 :
12624 : nsCOMPtr<nsIDocShellTreeItem> parent;
12625 : current->GetSameTypeParent(getter_AddRefs(parent));
12626 : if (!parent) {
12627 : return nullptr;
12628 : }
12629 0 :
12630 : return parent->GetDocument();
12631 0 : }
12632 0 :
12633 0 : /**
12634 : * Retrieves the classification of the Flash plugins in the document based on
12635 : * the classification lists. We perform AsyncInitFlashClassification on
12636 : * StartDocumentLoad() and the result may not be initialized when this function
12637 : * gets called. In that case, We can only unfortunately have a blocking wait.
12638 : *
12639 : * For more information, see
12640 : * toolkit/components/url-classifier/flash-block-lists.rst
12641 : */
12642 : FlashClassification
12643 0 : nsIDocument::PrincipalFlashClassification()
12644 : {
12645 : MOZ_ASSERT(mPrincipalFlashClassifier);
12646 0 : return mPrincipalFlashClassifier->ClassifyMaybeSync(NodePrincipal(),
12647 : IsThirdParty());
12648 : }
12649 0 :
12650 0 : /**
12651 : * Helper function for |nsDocument::PrincipalFlashClassification|
12652 0 : *
12653 : * Adds a table name string to a table list (a comma separated string). The
12654 : * table will not be added if the name is an empty string.
12655 : */
12656 : static void
12657 : MaybeAddTableToTableList(const nsACString& aTableNames,
12658 : nsACString& aTableList)
12659 : {
12660 : if (aTableNames.IsEmpty()) {
12661 : return;
12662 : }
12663 0 : if (!aTableList.IsEmpty()) {
12664 : aTableList.AppendLiteral(",");
12665 : }
12666 0 : aTableList.Append(aTableNames);
12667 : }
12668 :
12669 0 : /**
12670 0 : * Helper function for |nsDocument::PrincipalFlashClassification|
12671 : *
12672 : * Takes an array of table names and a comma separated list of table names
12673 0 : * Returns |true| if any table name in the array matches a table name in the
12674 : * comma separated list.
12675 : */
12676 : static bool
12677 : ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
12678 : const nsACString& aTableNames)
12679 : {
12680 : for (const nsCString& table : aTableArray) {
12681 0 : // This check is sufficient because table names cannot contain commas and
12682 1 : // cannot contain another existing table name.
12683 0 : if (FindInReadable(table, aTableNames)) {
12684 0 : return true;
12685 1 : }
12686 0 : }
12687 : return false;
12688 : }
12689 0 :
12690 0 : namespace {
12691 0 :
12692 0 : // An object to store all preferences we need for flash blocking feature.
12693 0 : struct PrefStore
12694 0 : {
12695 : PrefStore()
12696 0 : {
12697 1 : Preferences::AddBoolVarCache(&mFlashBlockEnabled,
12698 : "plugins.flashBlock.enabled");
12699 0 : Preferences::AddBoolVarCache(&mPluginsHttpOnly,
12700 0 : "plugins.http_https_only");
12701 0 :
12702 0 : // We only need to register string-typed preferences.
12703 0 : Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowTable", this);
12704 0 : Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
12705 0 : Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashTable", this);
12706 0 : Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashExceptTable", this);
12707 0 : Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocTable", this);
12708 : Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
12709 0 :
12710 : UpdateStringPrefs();
12711 1 : }
12712 1 :
12713 1 : ~PrefStore()
12714 1 : {
12715 1 : Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowTable", this);
12716 1 : Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
12717 1 : Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashTable", this);
12718 : Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashExceptTable", this);
12719 0 : Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocTable", this);
12720 : Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
12721 0 : }
12722 0 :
12723 : void UpdateStringPrefs()
12724 : {
12725 : Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
12726 : Preferences::GetCString("urlclassifier.flashAllowExceptTable", mAllowExceptionsTables);
12727 : Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
12728 : Preferences::GetCString("urlclassifier.flashExceptTable", mDenyExceptionsTables);
12729 : Preferences::GetCString("urlclassifier.flashSubDocTable", mSubDocDenyTables);
12730 : Preferences::GetCString("urlclassifier.flashSubDocExceptTable", mSubDocDenyExceptionsTables);
12731 : }
12732 :
12733 : static void UpdateStringPrefs(const char*, void* aClosure)
12734 : {
12735 : static_cast<PrefStore*>(aClosure)->UpdateStringPrefs();
12736 49 : }
12737 :
12738 0 : bool mFlashBlockEnabled;
12739 49 : bool mPluginsHttpOnly;
12740 0 :
12741 1 : nsCString mAllowTables;
12742 : nsCString mAllowExceptionsTables;
12743 0 : nsCString mDenyTables;
12744 : nsCString mDenyExceptionsTables;
12745 : nsCString mSubDocDenyTables;
12746 : nsCString mSubDocDenyExceptionsTables;
12747 : };
12748 :
12749 : static const
12750 : PrefStore& GetPrefStore()
12751 0 : {
12752 : static UniquePtr<PrefStore> sPrefStore;
12753 360 : if (!sPrefStore) {
12754 : sPrefStore.reset(new PrefStore());
12755 60 : ClearOnShutdown(&sPrefStore);
12756 60 : }
12757 : return *sPrefStore;
12758 : }
12759 109 :
12760 : } // end of unnamed namespace.
12761 0 :
12762 0 : ////////////////////////////////////////////////////////////////////
12763 0 : // PrincipalFlashClassifier implementation.
12764 109 :
12765 : NS_IMPL_ISUPPORTS(PrincipalFlashClassifier, nsIURIClassifierCallback)
12766 :
12767 0 : PrincipalFlashClassifier::PrincipalFlashClassifier()
12768 : {
12769 : Reset();
12770 0 : }
12771 0 :
12772 : void
12773 0 : PrincipalFlashClassifier::Reset()
12774 0 : {
12775 0 : mAsyncClassified = false;
12776 0 : mMatchedTables.Clear();
12777 : mResult = FlashClassification::Unclassified;
12778 0 : }
12779 0 :
12780 0 : void
12781 : PrincipalFlashClassifier::GetClassificationTables(bool aIsThirdParty,
12782 0 : nsACString& aTables)
12783 : {
12784 : aTables.Truncate();
12785 0 : auto& prefs = GetPrefStore();
12786 :
12787 0 : MaybeAddTableToTableList(prefs.mAllowTables, aTables);
12788 0 : MaybeAddTableToTableList(prefs.mAllowExceptionsTables, aTables);
12789 : MaybeAddTableToTableList(prefs.mDenyTables, aTables);
12790 : MaybeAddTableToTableList(prefs.mDenyExceptionsTables, aTables);
12791 0 :
12792 : if (aIsThirdParty) {
12793 : MaybeAddTableToTableList(prefs.mSubDocDenyTables, aTables);
12794 : MaybeAddTableToTableList(prefs.mSubDocDenyExceptionsTables, aTables);
12795 0 : }
12796 : }
12797 0 :
12798 : bool
12799 : PrincipalFlashClassifier::EnsureUriClassifier()
12800 : {
12801 : if (!mUriClassifier) {
12802 : mUriClassifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
12803 : }
12804 0 :
12805 : return !!mUriClassifier;
12806 : }
12807 :
12808 : FlashClassification
12809 : PrincipalFlashClassifier::ClassifyMaybeSync(nsIPrincipal* aPrincipal, bool aIsThirdParty)
12810 : {
12811 : if (FlashClassification::Unclassified != mResult) {
12812 : // We already have the result. Just return it.
12813 : return mResult;
12814 : }
12815 0 :
12816 0 : // TODO: Bug 1342333 - Entirely remove the use of the sync API
12817 : // (ClassifyLocalWithTables).
12818 0 : if (!mAsyncClassified) {
12819 0 :
12820 : //
12821 : // We may
12822 : // 1) have called AsyncClassifyLocalWithTables but OnClassifyComplete
12823 : // hasn't been called.
12824 0 : // 2) haven't even called AsyncClassifyLocalWithTables.
12825 0 : //
12826 : // In both cases we need to do the synchronous classification as the fallback.
12827 0 : //
12828 0 :
12829 0 : if (!EnsureUriClassifier()) {
12830 0 : return FlashClassification::Denied;
12831 0 : }
12832 : mResult = CheckIfClassifyNeeded(aPrincipal);
12833 : if (FlashClassification::Unclassified != mResult) {
12834 : return mResult;
12835 0 : }
12836 :
12837 0 : nsresult rv;
12838 0 : nsAutoCString classificationTables;
12839 0 : GetClassificationTables(aIsThirdParty, classificationTables);
12840 :
12841 : if (!mClassificationURI) {
12842 0 : rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
12843 : if (NS_FAILED(rv) || !mClassificationURI) {
12844 0 : mResult = FlashClassification::Denied;
12845 : return mResult;
12846 0 : }
12847 : }
12848 :
12849 : rv = mUriClassifier->ClassifyLocalWithTables(mClassificationURI,
12850 : classificationTables,
12851 0 : mMatchedTables);
12852 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12853 : if (rv == NS_ERROR_MALFORMED_URI) {
12854 : // This means that the URI had no hostname (ex: file://doc.html). In this
12855 : // case, we allow the default (Unknown plugin) behavior.
12856 : mResult = FlashClassification::Unknown;
12857 : } else {
12858 : mResult = FlashClassification::Denied;
12859 : }
12860 0 : return mResult;
12861 : }
12862 : }
12863 :
12864 : // Resolve the result based on mMatchedTables and aIsThirdParty.
12865 0 : mResult = Resolve(aIsThirdParty);
12866 : MOZ_ASSERT(FlashClassification::Unclassified != mResult);
12867 0 :
12868 : // The subsequent call of Result() will return the resolved result
12869 : // and never reach here until Reset() is called.
12870 : return mResult;
12871 : }
12872 :
12873 : /*virtual*/ nsresult
12874 : PrincipalFlashClassifier::OnClassifyComplete(nsresult /*aErrorCode*/,
12875 : const nsACString& aLists, // Only this matters.
12876 : const nsACString& /*aProvider*/,
12877 : const nsACString& /*aPrefix*/)
12878 : {
12879 0 : mAsyncClassified = true;
12880 0 :
12881 0 : if (FlashClassification::Unclassified != mResult) {
12882 0 : // Result() has been called prior to this callback.
12883 0 : return NS_OK;
12884 0 : }
12885 0 :
12886 0 : // TODO: Bug 1364804 - We should use a callback type which notifies
12887 0 : // the result as a string array rather than a formatted string.
12888 :
12889 0 : // We only populate the matched list without resolving the classification
12890 0 : // result because we are not sure if the parent doc has been properly set.
12891 0 : // We also parse the comma-separated tables to array. (the code is copied
12892 : // from Classifier::SplitTables.)
12893 : nsACString::const_iterator begin, iter, end;
12894 : aLists.BeginReading(begin);
12895 : aLists.EndReading(end);
12896 : while (begin != end) {
12897 : iter = begin;
12898 : FindCharInReadable(',', iter, end);
12899 : nsDependentCSubstring table = Substring(begin,iter);
12900 : if (!table.IsEmpty()) {
12901 0 : mMatchedTables.AppendElement(Substring(begin, iter));
12902 : }
12903 0 : begin = iter;
12904 : if (begin != end) {
12905 : begin++;
12906 0 : }
12907 : }
12908 :
12909 : return NS_OK;
12910 0 : }
12911 0 :
12912 0 : // We resolve the classification result based on aIsThirdParty
12913 : // and the matched tables we got ealier on (via either sync or async API).
12914 0 : FlashClassification
12915 0 : PrincipalFlashClassifier::Resolve(bool aIsThirdParty)
12916 : {
12917 : MOZ_ASSERT(FlashClassification::Unclassified == mResult,
12918 : "We already have resolved classification result.");
12919 0 :
12920 0 : if (mMatchedTables.IsEmpty()) {
12921 : return FlashClassification::Unknown;
12922 : }
12923 :
12924 0 : auto& prefs = GetPrefStore();
12925 : if (ArrayContainsTable(mMatchedTables, prefs.mDenyTables) &&
12926 : !ArrayContainsTable(mMatchedTables, prefs.mDenyExceptionsTables)) {
12927 : return FlashClassification::Denied;
12928 0 : } else if (ArrayContainsTable(mMatchedTables, prefs.mAllowTables) &&
12929 : !ArrayContainsTable(mMatchedTables, prefs.mAllowExceptionsTables)) {
12930 49 : return FlashClassification::Allowed;
12931 : }
12932 49 :
12933 49 : if (aIsThirdParty && ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyTables) &&
12934 49 : !ArrayContainsTable(mMatchedTables, prefs.mSubDocDenyExceptionsTables)) {
12935 : return FlashClassification::Denied;
12936 : }
12937 49 :
12938 : return FlashClassification::Unknown;
12939 : }
12940 :
12941 0 : void
12942 : PrincipalFlashClassifier::AsyncClassify(nsIPrincipal* aPrincipal)
12943 : {
12944 49 : MOZ_ASSERT(FlashClassification::Unclassified == mResult,
12945 : "The old classification result should be reset first.");
12946 : Reset();
12947 : mResult = AsyncClassifyInternal(aPrincipal);
12948 98 : }
12949 49 :
12950 : FlashClassification
12951 : PrincipalFlashClassifier::CheckIfClassifyNeeded(nsIPrincipal* aPrincipal)
12952 : {
12953 0 : nsresult rv;
12954 0 :
12955 0 : auto& prefs = GetPrefStore();
12956 :
12957 : // If neither pref is on, skip the null-principal and principal URI checks.
12958 : if (prefs.mPluginsHttpOnly && !prefs.mFlashBlockEnabled) {
12959 16 : return FlashClassification::Unknown;
12960 : }
12961 :
12962 : nsCOMPtr<nsIPrincipal> principal = aPrincipal;
12963 : if (principal->GetIsNullPrincipal()) {
12964 : return FlashClassification::Denied;
12965 16 : }
12966 0 :
12967 48 : nsCOMPtr<nsIURI> classificationURI;
12968 32 : rv = principal->GetURI(getter_AddRefs(classificationURI));
12969 32 : if (NS_FAILED(rv) || !classificationURI) {
12970 : return FlashClassification::Denied;
12971 : }
12972 :
12973 : if (prefs.mPluginsHttpOnly) {
12974 : // Only allow plugins for documents from an HTTP/HTTPS origin. This should
12975 0 : // allow dependent data: URIs to load plugins, but not:
12976 : // * chrome documents
12977 : // * "bare" data: loads
12978 : // * FTP/gopher/file
12979 0 : nsAutoCString scheme;
12980 : rv = classificationURI->GetScheme(scheme);
12981 : if (NS_WARN_IF(NS_FAILED(rv)) ||
12982 : !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) {
12983 : return FlashClassification::Denied;
12984 : }
12985 49 : }
12986 :
12987 0 : // If flash blocking is disabled, it is equivalent to all sites being
12988 49 : // on neither list.
12989 : if (!prefs.mFlashBlockEnabled) {
12990 : return FlashClassification::Unknown;
12991 : }
12992 :
12993 : return FlashClassification::Unclassified;
12994 : }
12995 :
12996 : // Using nsIURIClassifier.asyncClassifyLocalWithTables to do classification
12997 : // against the flash related tables based on the given principal.
12998 : FlashClassification
12999 0 : PrincipalFlashClassifier::AsyncClassifyInternal(nsIPrincipal* aPrincipal)
13000 0 : {
13001 : auto result = CheckIfClassifyNeeded(aPrincipal);
13002 0 : if (FlashClassification::Unclassified != result) {
13003 : return result;
13004 : }
13005 :
13006 0 : // We haven't been able to decide if it's a third party document
13007 : // since determining if a document is third-party may depend on its
13008 : // parent document. At the time we call AsyncClassifyInternal
13009 : // (i.e. StartDocumentLoad) the parent document may not have been
13010 0 : // set. As a result, we wait until Resolve() to be called to
13011 0 : // take "is third party" into account. At this point, we just assume
13012 : // it's third-party to include every list.
13013 : nsAutoCString tables;
13014 : GetClassificationTables(true, tables);
13015 0 :
13016 : if (tables.IsEmpty()) {
13017 0 : return FlashClassification::Unknown;
13018 : }
13019 0 :
13020 0 : if (!EnsureUriClassifier()) {
13021 : return FlashClassification::Denied;
13022 : }
13023 :
13024 : nsresult rv = aPrincipal->GetURI(getter_AddRefs(mClassificationURI));
13025 0 : if (NS_FAILED(rv) || !mClassificationURI) {
13026 : return FlashClassification::Denied;
13027 : }
13028 :
13029 : rv = mUriClassifier->AsyncClassifyLocalWithTables(mClassificationURI,
13030 : tables,
13031 : this);
13032 :
13033 0 : if (NS_FAILED(rv)) {
13034 : if (rv == NS_ERROR_MALFORMED_URI) {
13035 0 : // This means that the URI had no hostname (ex: file://doc.html). In this
13036 0 : // case, we allow the default (Unknown plugin) behavior.
13037 : return FlashClassification::Unknown;
13038 : } else {
13039 0 : return FlashClassification::Denied;
13040 0 : }
13041 0 : }
13042 :
13043 : return FlashClassification::Unclassified;
13044 0 : }
13045 :
13046 0 : FlashClassification
13047 0 : nsIDocument::ComputeFlashClassification()
13048 : {
13049 0 : nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
13050 0 : if (!current) {
13051 0 : return FlashClassification::Denied;
13052 : }
13053 : nsCOMPtr<nsIDocShellTreeItem> parent;
13054 0 : DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
13055 : MOZ_ASSERT(NS_SUCCEEDED(rv),
13056 0 : "nsIDocShellTreeItem::GetSameTypeParent should never fail");
13057 :
13058 : bool isTopLevel = !parent;
13059 0 : FlashClassification classification;
13060 : if (isTopLevel) {
13061 : classification = PrincipalFlashClassification();
13062 : } else {
13063 0 : nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
13064 0 : if (!parentDocument) {
13065 0 : return FlashClassification::Denied;
13066 : }
13067 : FlashClassification parentClassification =
13068 : parentDocument->DocumentFlashClassification();
13069 :
13070 : if (parentClassification == FlashClassification::Denied) {
13071 : classification = FlashClassification::Denied;
13072 : } else {
13073 : classification = PrincipalFlashClassification();
13074 :
13075 : // Allow unknown children to inherit allowed status from parent, but
13076 : // do not allow denied children to do so.
13077 : if (classification == FlashClassification::Unknown &&
13078 : parentClassification == FlashClassification::Allowed) {
13079 : classification = FlashClassification::Allowed;
13080 : }
13081 : }
13082 0 : }
13083 :
13084 0 : return classification;
13085 0 : }
13086 0 :
13087 0 : /**
13088 : * Retrieves the classification of plugins in this document. This is dependent
13089 : * on the classification of this document and all parent documents.
13090 : * This function is infallible - It must return some classification that
13091 0 : * callers can act on.
13092 : *
13093 : * This function will NOT return FlashClassification::Unclassified
13094 : */
13095 : FlashClassification
13096 : nsIDocument::DocumentFlashClassification()
13097 : {
13098 : if (mFlashClassification == FlashClassification::Unclassified) {
13099 : FlashClassification result = ComputeFlashClassification();
13100 : mFlashClassification = result;
13101 : MOZ_ASSERT(result != FlashClassification::Unclassified,
13102 : "nsDocument::GetPluginClassification should never return Unclassified");
13103 : }
13104 :
13105 : return mFlashClassification;
13106 : }
13107 :
13108 : /**
13109 : * Initializes |mIsThirdParty| if necessary and returns its value. The value
13110 0 : * returned represents whether this document should be considered Third-Party.
13111 : *
13112 0 : * A top-level document cannot be a considered Third-Party; only subdocuments
13113 0 : * may. For a subdocument to be considered Third-Party, it must meet ANY ONE
13114 : * of the following requirements:
13115 : * - The document's parent is Third-Party
13116 0 : * - The document has a different scheme (http/https) than its parent document
13117 0 : * - The document's domain and subdomain do not match those of its parent
13118 0 : * document.
13119 0 : *
13120 : * If there is an error in determining whether the document is Third-Party,
13121 : * it will be assumed to be Third-Party for security reasons.
13122 0 : */
13123 0 : bool
13124 0 : nsIDocument::IsThirdParty()
13125 : {
13126 0 : if (mIsThirdParty.isSome()) {
13127 : return mIsThirdParty.value();
13128 0 : }
13129 0 :
13130 0 : nsCOMPtr<nsIDocShellTreeItem> docshell = this->GetDocShell();
13131 : if (!docshell) {
13132 : mIsThirdParty.emplace(true);
13133 0 : return mIsThirdParty.value();
13134 0 : }
13135 :
13136 0 : nsCOMPtr<nsIDocShellTreeItem> parent;
13137 0 : nsresult rv = docshell->GetSameTypeParent(getter_AddRefs(parent));
13138 : MOZ_ASSERT(NS_SUCCEEDED(rv),
13139 : "nsIDocShellTreeItem::GetSameTypeParent should never fail");
13140 0 : bool isTopLevel = !parent;
13141 0 :
13142 0 : if (isTopLevel) {
13143 : mIsThirdParty.emplace(false);
13144 : return mIsThirdParty.value();
13145 0 : }
13146 0 :
13147 0 : nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
13148 0 : if (!parentDocument) {
13149 : // Failure
13150 0 : mIsThirdParty.emplace(true);
13151 0 : return mIsThirdParty.value();
13152 : }
13153 0 :
13154 : if (parentDocument->IsThirdParty()) {
13155 0 : mIsThirdParty.emplace(true);
13156 0 : return mIsThirdParty.value();
13157 : }
13158 0 :
13159 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
13160 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(parentDocument,
13161 0 : &rv);
13162 : if (NS_WARN_IF(NS_FAILED(rv) || !sop)) {
13163 : // Failure
13164 0 : mIsThirdParty.emplace(true);
13165 0 : return mIsThirdParty.value();
13166 0 : }
13167 : nsCOMPtr<nsIPrincipal> parentPrincipal = sop->GetPrincipal();
13168 :
13169 : bool principalsMatch = false;
13170 0 : rv = principal->Equals(parentPrincipal, &principalsMatch);
13171 0 :
13172 : if (NS_WARN_IF(NS_FAILED(rv))) {
13173 : // Failure
13174 : mIsThirdParty.emplace(true);
13175 8 : return mIsThirdParty.value();
13176 : }
13177 16 :
13178 0 : if (!principalsMatch) {
13179 8 : mIsThirdParty.emplace(true);
13180 8 : return mIsThirdParty.value();
13181 8 : }
13182 :
13183 : // Fall-through. Document is not a Third-Party Document.
13184 0 : mIsThirdParty.emplace(false);
13185 : return mIsThirdParty.value();
13186 0 : }
13187 0 :
13188 : void
13189 : nsIDocument::ClearStaleServoData()
13190 : {
13191 0 : DocumentStyleRootIterator iter(this);
13192 : while (Element* root = iter.GetNextStyleRoot()) {
13193 : RestyleManager::ClearServoDataFromSubtree(root);
13194 : }
13195 0 : }
13196 :
13197 : Selection*
13198 : nsIDocument::GetSelection(ErrorResult& aRv)
13199 0 : {
13200 : nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
13201 0 : if (!window) {
13202 0 : return nullptr;
13203 : }
13204 0 :
13205 : if (!window->IsCurrentInnerWindow()) {
13206 : return nullptr;
13207 : }
13208 :
13209 0 : return nsGlobalWindowInner::Cast(window)->GetSelection(aRv);
13210 0 : }
13211 0 :
13212 0 : void
13213 : nsIDocument::RecordNavigationTiming(ReadyState aReadyState)
13214 0 : {
13215 0 : if (!XRE_IsContentProcess()) {
13216 : return;
13217 : }
13218 : if (!IsTopLevelContentDocument()) {
13219 0 : return;
13220 0 : }
13221 : // If we dont have the timing yet (mostly because the doc is still loading),
13222 0 : // get it from docshell.
13223 0 : RefPtr<nsDOMNavigationTiming> timing = mTiming;
13224 0 : if (!timing) {
13225 0 : if (!mDocumentContainer) {
13226 : return;
13227 : }
13228 : timing = mDocumentContainer->GetNavigationTiming();
13229 0 : if (!timing) {
13230 0 : return;
13231 0 : }
13232 0 : }
13233 : TimeStamp startTime = timing->GetNavigationStartTimeStamp();
13234 : switch (aReadyState) {
13235 : case READYSTATE_LOADING:
13236 0 : if (!mDOMLoadingSet) {
13237 0 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS,
13238 0 : startTime);
13239 0 : mDOMLoadingSet = true;
13240 : }
13241 : break;
13242 : case READYSTATE_INTERACTIVE:
13243 0 : if (!mDOMInteractiveSet) {
13244 0 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS,
13245 : startTime);
13246 : mDOMInteractiveSet = true;
13247 : }
13248 : break;
13249 1 : case READYSTATE_COMPLETE:
13250 : if (!mDOMCompleteSet) {
13251 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS,
13252 : startTime);
13253 9 : mDOMCompleteSet = true;
13254 1 : }
13255 1 : break;
13256 : default:
13257 : NS_WARNING("Unexpected ReadyState value");
13258 1 : break;
13259 : }
13260 : }
13261 :
13262 0 : bool
13263 : nsIDocument::ModuleScriptsEnabled()
13264 0 : {
13265 : static bool sEnabledForContent = false;
13266 : static bool sCachedPref = false;
13267 : if (!sCachedPref) {
13268 0 : sCachedPref = true;
13269 0 : Preferences::AddBoolVarCache(&sEnabledForContent, "dom.moduleScripts.enabled", false);
13270 : }
13271 :
13272 : return nsContentUtils::IsChromeDoc(this) || sEnabledForContent;
13273 : }
13274 :
13275 : void
13276 : nsIDocument::ReportShadowDOMUsage()
13277 : {
13278 : if (mHasReportedShadowDOMUsage) {
13279 : return;
13280 : }
13281 :
13282 : nsIDocument* topLevel = GetTopLevelContentDocument();
13283 : if (topLevel && !topLevel->mHasReportedShadowDOMUsage) {
13284 : topLevel->mHasReportedShadowDOMUsage = true;
13285 : nsString uri;
13286 : Unused << topLevel->GetDocumentURI(uri);
13287 : if (!uri.IsEmpty()) {
13288 : nsAutoString msg = NS_LITERAL_STRING("Shadow DOM used in [") + uri +
13289 : NS_LITERAL_STRING("] or in some of its subdocuments.");
13290 : nsContentUtils::ReportToConsoleNonLocalized(msg, nsIScriptError::infoFlag,
13291 : NS_LITERAL_CSTRING("DOM"),
13292 : topLevel);
13293 : }
13294 : }
13295 :
13296 : mHasReportedShadowDOMUsage = true;
13297 : }
|