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/DebugOnly.h"
19 : #include "mozilla/EffectSet.h"
20 : #include "mozilla/IntegerRange.h"
21 : #include "mozilla/MemoryReporting.h"
22 : #include "mozilla/Likely.h"
23 : #include "mozilla/PresShell.h"
24 : #include "mozilla/URLExtraData.h"
25 : #include <algorithm>
26 :
27 : #include "mozilla/Logging.h"
28 : #include "plstr.h"
29 : #include "mozilla/Sprintf.h"
30 :
31 : #include "mozilla/Telemetry.h"
32 : #include "nsIInterfaceRequestor.h"
33 : #include "nsIInterfaceRequestorUtils.h"
34 : #include "nsILoadContext.h"
35 : #include "nsITextControlFrame.h"
36 : #include "nsNumberControlFrame.h"
37 : #include "nsUnicharUtils.h"
38 : #include "nsContentList.h"
39 : #include "nsCSSPseudoElements.h"
40 : #include "nsIObserver.h"
41 : #include "nsIBaseWindow.h"
42 : #include "mozilla/css/Loader.h"
43 : #include "mozilla/css/ImageLoader.h"
44 : #include "nsDocShell.h"
45 : #include "nsDocShellLoadTypes.h"
46 : #include "nsIDocShellTreeItem.h"
47 : #include "nsCOMArray.h"
48 : #include "nsQueryObject.h"
49 : #include "nsDOMClassInfo.h"
50 : #include "mozilla/Services.h"
51 : #include "nsScreen.h"
52 :
53 : #include "mozilla/AsyncEventDispatcher.h"
54 : #include "mozilla/BasicEvents.h"
55 : #include "mozilla/EventListenerManager.h"
56 : #include "mozilla/EventStateManager.h"
57 : #include "nsIDOMNodeFilter.h"
58 :
59 : #include "nsIDOMStyleSheet.h"
60 : #include "mozilla/dom/Attr.h"
61 : #include "mozilla/dom/BindingDeclarations.h"
62 : #include "nsIDOMDOMImplementation.h"
63 : #include "nsIDOMDocumentXBL.h"
64 : #include "mozilla/dom/Element.h"
65 : #include "mozilla/dom/FramingChecker.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 "nsIDOMText.h"
77 : #include "nsIDOMComment.h"
78 : #include "mozilla/dom/DocumentType.h"
79 : #include "mozilla/dom/NodeIterator.h"
80 : #include "mozilla/dom/Promise.h"
81 : #include "mozilla/dom/PromiseNativeHandler.h"
82 : #include "mozilla/dom/TreeWalker.h"
83 :
84 : #include "nsIServiceManager.h"
85 : #include "mozilla/dom/workers/ServiceWorkerManager.h"
86 : #include "imgLoader.h"
87 :
88 : #include "nsCanvasFrame.h"
89 : #include "nsContentCID.h"
90 : #include "nsError.h"
91 : #include "nsPresContext.h"
92 : #include "nsIJSON.h"
93 : #include "nsThreadUtils.h"
94 : #include "nsNodeInfoManager.h"
95 : #include "nsIFileChannel.h"
96 : #include "nsIMultiPartChannel.h"
97 : #include "nsIRefreshURI.h"
98 : #include "nsIWebNavigation.h"
99 : #include "nsIScriptError.h"
100 : #include "nsISimpleEnumerator.h"
101 : #include "nsStyleSheetService.h"
102 :
103 : #include "nsNetUtil.h" // for NS_NewURI
104 : #include "nsIInputStreamChannel.h"
105 : #include "nsIAuthPrompt.h"
106 : #include "nsIAuthPrompt2.h"
107 :
108 : #include "nsIScriptSecurityManager.h"
109 : #include "nsIPermissionManager.h"
110 : #include "nsIPrincipal.h"
111 : #include "NullPrincipal.h"
112 :
113 : #include "nsIDOMWindow.h"
114 : #include "nsPIDOMWindow.h"
115 : #include "nsIDOMElement.h"
116 : #include "nsFocusManager.h"
117 :
118 : // for radio group stuff
119 : #include "nsIDOMHTMLInputElement.h"
120 : #include "nsIRadioVisitor.h"
121 : #include "nsIFormControl.h"
122 :
123 : #include "nsBidiUtils.h"
124 :
125 : #include "nsIParserService.h"
126 : #include "nsContentCreatorFunctions.h"
127 :
128 : #include "nsIScriptContext.h"
129 : #include "nsBindingManager.h"
130 : #include "nsIDOMHTMLDocument.h"
131 : #include "nsHTMLDocument.h"
132 : #include "nsIDOMHTMLFormElement.h"
133 : #include "nsIRequest.h"
134 : #include "nsHostObjectProtocolHandler.h"
135 :
136 : #include "nsCharsetSource.h"
137 : #include "nsIParser.h"
138 : #include "nsIContentSink.h"
139 :
140 : #include "mozilla/EventDispatcher.h"
141 : #include "mozilla/EventStates.h"
142 : #include "mozilla/InternalMutationEvent.h"
143 : #include "nsDOMCID.h"
144 :
145 : #include "jsapi.h"
146 : #include "nsIXPConnect.h"
147 : #include "xpcpublic.h"
148 : #include "nsCCUncollectableMarker.h"
149 : #include "nsIContentPolicy.h"
150 : #include "nsContentPolicyUtils.h"
151 : #include "nsICategoryManager.h"
152 : #include "nsIDocumentLoaderFactory.h"
153 : #include "nsIDocumentLoader.h"
154 : #include "nsIContentViewer.h"
155 : #include "nsIXMLContentSink.h"
156 : #include "nsIXULDocument.h"
157 : #include "nsIPrompt.h"
158 : #include "nsIPropertyBag2.h"
159 : #include "mozilla/dom/PageTransitionEvent.h"
160 : #include "mozilla/dom/StyleRuleChangeEvent.h"
161 : #include "mozilla/dom/StyleSheetChangeEvent.h"
162 : #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
163 : #include "nsJSUtils.h"
164 : #include "nsFrameLoader.h"
165 : #include "nsEscape.h"
166 : #include "nsObjectLoadingContent.h"
167 : #include "nsHtml5TreeOpExecutor.h"
168 : #include "mozilla/dom/HTMLLinkElement.h"
169 : #include "mozilla/dom/HTMLMediaElement.h"
170 : #include "mozilla/dom/HTMLIFrameElement.h"
171 : #include "mozilla/dom/HTMLImageElement.h"
172 : #include "mozilla/dom/MediaSource.h"
173 : #include "mozilla/dom/FlyWebService.h"
174 :
175 : #include "mozAutoDocUpdate.h"
176 : #include "nsGlobalWindow.h"
177 : #include "mozilla/Encoding.h"
178 : #include "nsDOMNavigationTiming.h"
179 :
180 : #include "nsSMILAnimationController.h"
181 : #include "imgIContainer.h"
182 : #include "nsSVGUtils.h"
183 :
184 : #include "nsRefreshDriver.h"
185 :
186 : // FOR CSP (autogenerated by xpidl)
187 : #include "nsIContentSecurityPolicy.h"
188 : #include "mozilla/dom/nsCSPContext.h"
189 : #include "mozilla/dom/nsCSPService.h"
190 : #include "mozilla/dom/nsCSPUtils.h"
191 : #include "nsHTMLStyleSheet.h"
192 : #include "nsHTMLCSSStyleSheet.h"
193 : #include "mozilla/dom/DOMImplementation.h"
194 : #include "mozilla/dom/ShadowRoot.h"
195 : #include "mozilla/dom/Comment.h"
196 : #include "nsTextNode.h"
197 : #include "mozilla/dom/Link.h"
198 : #include "mozilla/dom/HTMLElementBinding.h"
199 : #include "nsXULAppAPI.h"
200 : #include "mozilla/dom/Touch.h"
201 : #include "mozilla/dom/TouchEvent.h"
202 :
203 : #include "mozilla/Preferences.h"
204 :
205 : #include "imgILoader.h"
206 : #include "imgRequestProxy.h"
207 : #include "nsWrapperCacheInlines.h"
208 : #include "nsSandboxFlags.h"
209 : #include "nsIAddonPolicyService.h"
210 : #include "mozilla/dom/AnimatableBinding.h"
211 : #include "mozilla/dom/AnonymousContent.h"
212 : #include "mozilla/dom/BindingUtils.h"
213 : #include "mozilla/dom/DocumentFragment.h"
214 : #include "mozilla/dom/DocumentTimeline.h"
215 : #include "mozilla/dom/Event.h"
216 : #include "mozilla/dom/HTMLBodyElement.h"
217 : #include "mozilla/dom/HTMLInputElement.h"
218 : #include "mozilla/dom/ImageTracker.h"
219 : #include "mozilla/dom/MediaQueryList.h"
220 : #include "mozilla/dom/NodeFilterBinding.h"
221 : #include "mozilla/OwningNonNull.h"
222 : #include "mozilla/dom/TabChild.h"
223 : #include "mozilla/dom/WebComponentsBinding.h"
224 : #include "mozilla/dom/CustomElementRegistryBinding.h"
225 : #include "mozilla/dom/CustomElementRegistry.h"
226 : #include "mozilla/dom/TimeoutManager.h"
227 : #include "nsFrame.h"
228 : #include "nsDOMCaretPosition.h"
229 : #include "nsIDOMHTMLTextAreaElement.h"
230 : #include "nsViewportInfo.h"
231 : #include "mozilla/StaticPtr.h"
232 : #include "nsITextControlElement.h"
233 : #include "nsIDOMNSEditableElement.h"
234 : #include "nsIEditor.h"
235 : #include "nsIDOMCSSStyleRule.h"
236 : #include "mozilla/css/StyleRule.h"
237 : #include "nsIHttpChannelInternal.h"
238 : #include "nsISecurityConsoleMessage.h"
239 : #include "nsCharSeparatedTokenizer.h"
240 : #include "mozilla/dom/XPathEvaluator.h"
241 : #include "mozilla/dom/XPathNSResolverBinding.h"
242 : #include "mozilla/dom/XPathResult.h"
243 : #include "nsIDocumentEncoder.h"
244 : #include "nsIDocumentActivity.h"
245 : #include "nsIStructuredCloneContainer.h"
246 : #include "nsIMutableArray.h"
247 : #include "mozilla/dom/DOMStringList.h"
248 : #include "nsWindowMemoryReporter.h"
249 : #include "mozilla/dom/Location.h"
250 : #include "mozilla/dom/FontFaceSet.h"
251 : #include "gfxPrefs.h"
252 : #include "nsISupportsPrimitives.h"
253 : #include "mozilla/StyleSetHandle.h"
254 : #include "mozilla/StyleSetHandleInlines.h"
255 : #include "mozilla/StyleSheet.h"
256 : #include "mozilla/StyleSheetInlines.h"
257 : #include "mozilla/dom/SVGSVGElement.h"
258 : #include "mozilla/dom/DocGroup.h"
259 : #include "mozilla/dom/TabGroup.h"
260 : #ifdef MOZ_XUL
261 : #include "mozilla/dom/ContainerBoxObject.h"
262 : #include "mozilla/dom/ListBoxObject.h"
263 : #include "mozilla/dom/MenuBoxObject.h"
264 : #include "mozilla/dom/PopupBoxObject.h"
265 : #include "mozilla/dom/ScrollBoxObject.h"
266 : #include "mozilla/dom/TreeBoxObject.h"
267 : #endif
268 : #include "nsIPresShellInlines.h"
269 :
270 : #include "mozilla/DocLoadingTimelineMarker.h"
271 :
272 : #include "nsISpeculativeConnect.h"
273 :
274 : #include "mozilla/MediaManager.h"
275 : #ifdef MOZ_WEBRTC
276 : #include "IPeerConnection.h"
277 : #endif // MOZ_WEBRTC
278 :
279 : #include "nsIURIClassifier.h"
280 :
281 : using namespace mozilla;
282 : using namespace mozilla::dom;
283 :
284 : typedef nsTArray<Link*> LinkArray;
285 :
286 : static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
287 : static LazyLogModule gCspPRLog("CSP");
288 :
289 : static const char kChromeInContentPref[] = "security.allow_chrome_frames_inside_content";
290 : static bool sChromeInContentAllowed = false;
291 : static bool sChromeInContentPrefCached = false;
292 :
293 : static nsresult
294 51 : GetHttpChannelHelper(nsIChannel* aChannel, nsIHttpChannel** aHttpChannel)
295 : {
296 102 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
297 51 : if (httpChannel) {
298 2 : httpChannel.forget(aHttpChannel);
299 2 : return NS_OK;
300 : }
301 :
302 98 : nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
303 49 : if (!multipart) {
304 49 : *aHttpChannel = nullptr;
305 49 : return NS_OK;
306 : }
307 :
308 0 : nsCOMPtr<nsIChannel> baseChannel;
309 0 : nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
310 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
311 0 : return rv;
312 : }
313 :
314 0 : httpChannel = do_QueryInterface(baseChannel);
315 0 : httpChannel.forget(aHttpChannel);
316 :
317 0 : return NS_OK;
318 : }
319 :
320 : #define NAME_NOT_VALID ((nsSimpleContentList*)1)
321 :
322 1576 : nsIdentifierMapEntry::~nsIdentifierMapEntry()
323 : {
324 1576 : }
325 :
326 : void
327 0 : nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
328 : {
329 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
330 0 : "mIdentifierMap mNameContentList");
331 0 : aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
332 :
333 0 : if (mImageElement) {
334 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
335 0 : "mIdentifierMap mImageElement element");
336 0 : nsIContent* imageElement = mImageElement;
337 0 : aCallback->NoteXPCOMChild(imageElement);
338 : }
339 0 : }
340 :
341 : bool
342 28 : nsIdentifierMapEntry::IsEmpty()
343 : {
344 84 : return mIdContentList.IsEmpty() && !mNameContentList &&
345 84 : !mChangeCallbacks && !mImageElement;
346 : }
347 :
348 : Element*
349 403 : nsIdentifierMapEntry::GetIdElement()
350 : {
351 403 : return mIdContentList.SafeElementAt(0);
352 : }
353 :
354 : Element*
355 0 : nsIdentifierMapEntry::GetImageIdElement()
356 : {
357 0 : return mImageElement ? mImageElement.get() : GetIdElement();
358 : }
359 :
360 : void
361 4 : nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
362 : {
363 8 : for (size_t i = 0; i < mIdContentList.Length(); ++i) {
364 4 : aElements->AppendObject(mIdContentList[i]);
365 : }
366 4 : }
367 :
368 : void
369 25 : nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
370 : void* aData, bool aForImage)
371 : {
372 25 : if (!mChangeCallbacks) {
373 10 : mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
374 : }
375 :
376 25 : ChangeCallback cc = { aCallback, aData, aForImage };
377 25 : mChangeCallbacks->PutEntry(cc);
378 25 : }
379 :
380 : void
381 0 : nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
382 : void* aData, bool aForImage)
383 : {
384 0 : if (!mChangeCallbacks)
385 0 : return;
386 0 : ChangeCallback cc = { aCallback, aData, aForImage };
387 0 : mChangeCallbacks->RemoveEntry(cc);
388 0 : if (mChangeCallbacks->Count() == 0) {
389 0 : mChangeCallbacks = nullptr;
390 : }
391 : }
392 :
393 : void
394 1080 : nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
395 : Element* aNewElement,
396 : bool aImageOnly)
397 : {
398 1080 : if (!mChangeCallbacks)
399 1080 : return;
400 :
401 0 : for (auto iter = mChangeCallbacks->ConstIter(); !iter.Done(); iter.Next()) {
402 0 : nsIdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
403 : // Don't fire image changes for non-image observers, and don't fire element
404 : // changes for image observers when an image override is active.
405 0 : if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
406 0 : continue;
407 : }
408 :
409 0 : if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
410 0 : iter.Remove();
411 : }
412 : }
413 : }
414 :
415 : namespace {
416 :
417 : struct PositionComparator
418 : {
419 : Element* const mElement;
420 1078 : explicit PositionComparator(Element* const aElement) : mElement(aElement) {}
421 :
422 1078 : int operator()(void* aElement) const {
423 1078 : Element* curElement = static_cast<Element*>(aElement);
424 1078 : if (mElement == curElement) {
425 1078 : return 0;
426 : }
427 0 : if (nsContentUtils::PositionIsBefore(mElement, curElement)) {
428 0 : return -1;
429 : }
430 0 : return 1;
431 : }
432 : };
433 :
434 : } // namespace
435 :
436 : bool
437 2130 : nsIdentifierMapEntry::AddIdElement(Element* aElement)
438 : {
439 2130 : NS_PRECONDITION(aElement, "Must have element");
440 2130 : NS_PRECONDITION(!mIdContentList.Contains(nullptr),
441 : "Why is null in our list?");
442 :
443 : #ifdef DEBUG
444 2130 : Element* currentElement = mIdContentList.SafeElementAt(0);
445 : #endif
446 :
447 : // Common case
448 2130 : if (mIdContentList.IsEmpty()) {
449 1052 : if (!mIdContentList.AppendElement(aElement))
450 0 : return false;
451 1052 : NS_ASSERTION(currentElement == nullptr, "How did that happen?");
452 1052 : FireChangeCallbacks(nullptr, aElement);
453 1052 : return true;
454 : }
455 :
456 : // We seem to have multiple content nodes for the same id, or XUL is messing
457 : // with us. Search for the right place to insert the content.
458 :
459 : size_t idx;
460 2156 : if (BinarySearchIf(mIdContentList, 0, mIdContentList.Length(),
461 2156 : PositionComparator(aElement), &idx)) {
462 : // Already in the list, so already in the right spot. Get out of here.
463 : // XXXbz this only happens because XUL does all sorts of random
464 : // UpdateIdTableEntry calls. Hate, hate, hate!
465 1078 : return true;
466 : }
467 :
468 0 : if (!mIdContentList.InsertElementAt(idx, aElement)) {
469 0 : return false;
470 : }
471 :
472 0 : if (idx == 0) {
473 0 : Element* oldElement = mIdContentList.SafeElementAt(1);
474 0 : NS_ASSERTION(currentElement == oldElement, "How did that happen?");
475 0 : FireChangeCallbacks(oldElement, aElement);
476 : }
477 0 : return true;
478 : }
479 :
480 : void
481 28 : nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
482 : {
483 28 : NS_PRECONDITION(aElement, "Missing element");
484 :
485 : // This should only be called while the document is in an update.
486 : // Assertions near the call to this method guarantee this.
487 :
488 : // This could fire in OOM situations
489 : // Only assert this in HTML documents for now as XUL does all sorts of weird
490 : // crap.
491 28 : NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
492 : mIdContentList.Contains(aElement),
493 : "Removing id entry that doesn't exist");
494 :
495 : // XXXbz should this ever Compact() I guess when all the content is gone
496 : // we'll just get cleaned up in the natural order of things...
497 28 : Element* currentElement = mIdContentList.SafeElementAt(0);
498 28 : mIdContentList.RemoveElement(aElement);
499 28 : if (currentElement == aElement) {
500 28 : FireChangeCallbacks(currentElement, mIdContentList.SafeElementAt(0));
501 : }
502 28 : }
503 :
504 : void
505 0 : nsIdentifierMapEntry::SetImageElement(Element* aElement)
506 : {
507 0 : Element* oldElement = GetImageIdElement();
508 0 : mImageElement = aElement;
509 0 : Element* newElement = GetImageIdElement();
510 0 : if (oldElement != newElement) {
511 0 : FireChangeCallbacks(oldElement, newElement, true);
512 : }
513 0 : }
514 :
515 : void
516 0 : nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
517 : {
518 0 : if (!mNameContentList) {
519 0 : mNameContentList = new nsSimpleContentList(aNode);
520 : }
521 :
522 0 : mNameContentList->AppendElement(aElement);
523 0 : }
524 :
525 : void
526 0 : nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
527 : {
528 0 : if (mNameContentList) {
529 0 : mNameContentList->RemoveElement(aElement);
530 : }
531 0 : }
532 :
533 : bool
534 0 : nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
535 : {
536 0 : Element* idElement = GetIdElement();
537 0 : return idElement &&
538 0 : nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
539 : }
540 :
541 : size_t
542 31 : nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
543 : {
544 31 : return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
545 : }
546 :
547 : // Helper structs for the content->subdoc map
548 :
549 : class SubDocMapEntry : public PLDHashEntryHdr
550 : {
551 : public:
552 : // Both of these are strong references
553 : Element *mKey; // must be first, to look like PLDHashEntryStub
554 : nsIDocument *mSubDocument;
555 : };
556 :
557 :
558 : /**
559 : * A struct that holds all the information about a radio group.
560 : */
561 0 : struct nsRadioGroupStruct
562 : {
563 0 : nsRadioGroupStruct()
564 0 : : mRequiredRadioCount(0)
565 0 : , mGroupSuffersFromValueMissing(false)
566 0 : {}
567 :
568 : /**
569 : * A strong pointer to the currently selected radio button.
570 : */
571 : RefPtr<HTMLInputElement> mSelectedRadioButton;
572 : nsCOMArray<nsIFormControl> mRadioButtons;
573 : uint32_t mRequiredRadioCount;
574 : bool mGroupSuffersFromValueMissing;
575 : };
576 :
577 :
578 2 : nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument)
579 : {
580 2 : mLength = -1;
581 : // Not reference counted to avoid circular references.
582 : // The document will tell us when its going away.
583 2 : mDocument = aDocument;
584 2 : mDocument->AddObserver(this);
585 2 : }
586 :
587 0 : nsDOMStyleSheetList::~nsDOMStyleSheetList()
588 : {
589 0 : if (mDocument) {
590 0 : mDocument->RemoveObserver(this);
591 : }
592 0 : }
593 :
594 52 : NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList,
595 : nsIDocumentObserver,
596 : nsIMutationObserver)
597 :
598 : uint32_t
599 2 : nsDOMStyleSheetList::Length()
600 : {
601 2 : if (!mDocument) {
602 0 : return 0;
603 : }
604 :
605 : // XXX Find the number and then cache it. We'll use the
606 : // observer notification to figure out if new ones have
607 : // been added or removed.
608 2 : if (-1 == mLength) {
609 2 : mLength = mDocument->GetNumberOfStyleSheets();
610 : }
611 2 : return mLength;
612 : }
613 :
614 : StyleSheet*
615 0 : nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
616 : {
617 0 : if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
618 0 : aFound = false;
619 0 : return nullptr;
620 : }
621 0 : aFound = true;
622 0 : return mDocument->GetStyleSheetAt(aIndex);
623 : }
624 :
625 : void
626 0 : nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
627 : {
628 0 : mDocument = nullptr;
629 0 : }
630 :
631 : void
632 0 : nsDOMStyleSheetList::StyleSheetAdded(StyleSheet* aStyleSheet,
633 : bool aDocumentSheet)
634 : {
635 0 : if (aDocumentSheet && -1 != mLength) {
636 0 : mLength++;
637 : }
638 0 : }
639 :
640 : void
641 0 : nsDOMStyleSheetList::StyleSheetRemoved(StyleSheet* aStyleSheet,
642 : bool aDocumentSheet)
643 : {
644 0 : if (aDocumentSheet && -1 != mLength) {
645 0 : mLength--;
646 : }
647 0 : }
648 :
649 : // nsOnloadBlocker implementation
650 444 : NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
651 :
652 : NS_IMETHODIMP
653 82 : nsOnloadBlocker::GetName(nsACString &aResult)
654 : {
655 82 : aResult.AssignLiteral("about:document-onload-blocker");
656 82 : return NS_OK;
657 : }
658 :
659 : NS_IMETHODIMP
660 0 : nsOnloadBlocker::IsPending(bool *_retval)
661 : {
662 0 : *_retval = true;
663 0 : return NS_OK;
664 : }
665 :
666 : NS_IMETHODIMP
667 0 : nsOnloadBlocker::GetStatus(nsresult *status)
668 : {
669 0 : *status = NS_OK;
670 0 : return NS_OK;
671 : }
672 :
673 : NS_IMETHODIMP
674 1 : nsOnloadBlocker::Cancel(nsresult status)
675 : {
676 1 : return NS_OK;
677 : }
678 : NS_IMETHODIMP
679 0 : nsOnloadBlocker::Suspend(void)
680 : {
681 0 : return NS_OK;
682 : }
683 : NS_IMETHODIMP
684 0 : nsOnloadBlocker::Resume(void)
685 : {
686 0 : return NS_OK;
687 : }
688 :
689 : NS_IMETHODIMP
690 0 : nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
691 : {
692 0 : *aLoadGroup = nullptr;
693 0 : return NS_OK;
694 : }
695 :
696 : NS_IMETHODIMP
697 0 : nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
698 : {
699 0 : return NS_OK;
700 : }
701 :
702 : NS_IMETHODIMP
703 45 : nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
704 : {
705 45 : *aLoadFlags = nsIRequest::LOAD_NORMAL;
706 45 : return NS_OK;
707 : }
708 :
709 : NS_IMETHODIMP
710 0 : nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
711 : {
712 0 : return NS_OK;
713 : }
714 :
715 : // ==================================================================
716 :
717 55 : nsExternalResourceMap::nsExternalResourceMap()
718 55 : : mHaveShutDown(false)
719 : {
720 55 : }
721 :
722 : nsIDocument*
723 0 : nsExternalResourceMap::RequestResource(nsIURI* aURI,
724 : nsINode* aRequestingNode,
725 : nsDocument* aDisplayDocument,
726 : ExternalResourceLoad** aPendingLoad)
727 : {
728 : // If we ever start allowing non-same-origin loads here, we might need to do
729 : // something interesting with aRequestingPrincipal even for the hashtable
730 : // gets.
731 0 : NS_PRECONDITION(aURI, "Must have a URI");
732 0 : NS_PRECONDITION(aRequestingNode, "Must have a node");
733 0 : *aPendingLoad = nullptr;
734 0 : if (mHaveShutDown) {
735 0 : return nullptr;
736 : }
737 :
738 : // First, make sure we strip the ref from aURI.
739 0 : nsCOMPtr<nsIURI> clone;
740 0 : nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
741 0 : if (NS_FAILED(rv) || !clone) {
742 0 : return nullptr;
743 : }
744 :
745 : ExternalResource* resource;
746 0 : mMap.Get(clone, &resource);
747 0 : if (resource) {
748 0 : return resource->mDocument;
749 : }
750 :
751 0 : RefPtr<PendingLoad>& loadEntry = mPendingLoads.GetOrInsert(clone);
752 0 : if (loadEntry) {
753 0 : RefPtr<PendingLoad> load(loadEntry);
754 0 : load.forget(aPendingLoad);
755 0 : return nullptr;
756 : }
757 :
758 0 : RefPtr<PendingLoad> load(new PendingLoad(aDisplayDocument));
759 0 : loadEntry = load;
760 :
761 0 : if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
762 : // Make sure we don't thrash things by trying this load again, since
763 : // chances are it failed for good reasons (security check, etc).
764 0 : AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
765 : } else {
766 0 : load.forget(aPendingLoad);
767 : }
768 :
769 0 : return nullptr;
770 : }
771 :
772 : void
773 158 : nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
774 : void* aData)
775 : {
776 158 : for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
777 0 : nsExternalResourceMap::ExternalResource* resource = iter.UserData();
778 0 : if (resource->mDocument && !aCallback(resource->mDocument, aData)) {
779 0 : break;
780 : }
781 : }
782 158 : }
783 :
784 : void
785 15 : nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
786 : {
787 : // mPendingLoads will get cleared out as the requests complete, so
788 : // no need to worry about those here.
789 15 : for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
790 0 : nsExternalResourceMap::ExternalResource* resource = iter.UserData();
791 :
792 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
793 : "mExternalResourceMap.mMap entry"
794 0 : "->mDocument");
795 0 : aCallback->NoteXPCOMChild(resource->mDocument);
796 :
797 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
798 : "mExternalResourceMap.mMap entry"
799 0 : "->mViewer");
800 0 : aCallback->NoteXPCOMChild(resource->mViewer);
801 :
802 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
803 : "mExternalResourceMap.mMap entry"
804 0 : "->mLoadGroup");
805 0 : aCallback->NoteXPCOMChild(resource->mLoadGroup);
806 : }
807 15 : }
808 :
809 : void
810 4 : nsExternalResourceMap::HideViewers()
811 : {
812 4 : for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
813 0 : nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
814 0 : if (viewer) {
815 0 : viewer->Hide();
816 : }
817 : }
818 4 : }
819 :
820 : void
821 28 : nsExternalResourceMap::ShowViewers()
822 : {
823 28 : for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) {
824 0 : nsCOMPtr<nsIContentViewer> viewer = iter.UserData()->mViewer;
825 0 : if (viewer) {
826 0 : viewer->Show();
827 : }
828 : }
829 28 : }
830 :
831 : void
832 0 : TransferZoomLevels(nsIDocument* aFromDoc,
833 : nsIDocument* aToDoc)
834 : {
835 0 : MOZ_ASSERT(aFromDoc && aToDoc,
836 : "transferring zoom levels from/to null doc");
837 :
838 0 : nsIPresShell* fromShell = aFromDoc->GetShell();
839 0 : if (!fromShell)
840 0 : return;
841 :
842 0 : nsPresContext* fromCtxt = fromShell->GetPresContext();
843 0 : if (!fromCtxt)
844 0 : return;
845 :
846 0 : nsIPresShell* toShell = aToDoc->GetShell();
847 0 : if (!toShell)
848 0 : return;
849 :
850 0 : nsPresContext* toCtxt = toShell->GetPresContext();
851 0 : if (!toCtxt)
852 0 : return;
853 :
854 0 : toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
855 0 : toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
856 0 : toCtxt->SetTextZoom(fromCtxt->TextZoom());
857 0 : toCtxt->SetOverrideDPPX(fromCtxt->GetOverrideDPPX());
858 : }
859 :
860 : void
861 0 : TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
862 : {
863 0 : MOZ_ASSERT(aFromDoc && aToDoc,
864 : "transferring showing state from/to null doc");
865 :
866 0 : if (aFromDoc->IsShowing()) {
867 0 : aToDoc->OnPageShow(true, nullptr);
868 : }
869 0 : }
870 :
871 : nsresult
872 0 : nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
873 : nsIContentViewer* aViewer,
874 : nsILoadGroup* aLoadGroup,
875 : nsIDocument* aDisplayDocument)
876 : {
877 0 : NS_PRECONDITION(aURI, "Unexpected call");
878 0 : NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
879 : "Must have both or neither");
880 :
881 0 : RefPtr<PendingLoad> load;
882 0 : mPendingLoads.Remove(aURI, getter_AddRefs(load));
883 :
884 0 : nsresult rv = NS_OK;
885 :
886 0 : nsCOMPtr<nsIDocument> doc;
887 0 : if (aViewer) {
888 0 : doc = aViewer->GetDocument();
889 0 : NS_ASSERTION(doc, "Must have a document");
890 :
891 0 : nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
892 0 : if (xulDoc) {
893 : // We don't handle XUL stuff here yet.
894 0 : rv = NS_ERROR_NOT_AVAILABLE;
895 : } else {
896 0 : doc->SetDisplayDocument(aDisplayDocument);
897 :
898 : // Make sure that hiding our viewer will tear down its presentation.
899 0 : aViewer->SetSticky(false);
900 :
901 0 : rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
902 0 : if (NS_SUCCEEDED(rv)) {
903 0 : rv = aViewer->Open(nullptr, nullptr);
904 : }
905 : }
906 :
907 0 : if (NS_FAILED(rv)) {
908 0 : doc = nullptr;
909 0 : aViewer = nullptr;
910 0 : aLoadGroup = nullptr;
911 : }
912 : }
913 :
914 0 : ExternalResource* newResource = new ExternalResource();
915 0 : mMap.Put(aURI, newResource);
916 :
917 0 : newResource->mDocument = doc;
918 0 : newResource->mViewer = aViewer;
919 0 : newResource->mLoadGroup = aLoadGroup;
920 0 : if (doc) {
921 0 : TransferZoomLevels(aDisplayDocument, doc);
922 0 : TransferShowingState(aDisplayDocument, doc);
923 : }
924 :
925 0 : const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
926 0 : for (uint32_t i = 0; i < obs.Length(); ++i) {
927 0 : obs[i]->Observe(doc, "external-resource-document-created", nullptr);
928 : }
929 :
930 0 : return rv;
931 : }
932 :
933 0 : NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
934 : nsIStreamListener,
935 : nsIRequestObserver)
936 :
937 : NS_IMETHODIMP
938 0 : nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
939 : nsISupports *aContext)
940 : {
941 0 : nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
942 0 : if (map.HaveShutDown()) {
943 0 : return NS_BINDING_ABORTED;
944 : }
945 :
946 0 : nsCOMPtr<nsIContentViewer> viewer;
947 0 : nsCOMPtr<nsILoadGroup> loadGroup;
948 0 : nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
949 0 : getter_AddRefs(loadGroup));
950 :
951 : // Make sure to do this no matter what
952 0 : nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
953 0 : mDisplayDocument);
954 0 : if (NS_FAILED(rv)) {
955 0 : return rv;
956 : }
957 0 : if (NS_FAILED(rv2)) {
958 0 : mTargetListener = nullptr;
959 0 : return rv2;
960 : }
961 :
962 0 : return mTargetListener->OnStartRequest(aRequest, aContext);
963 : }
964 :
965 : nsresult
966 0 : nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
967 : nsIContentViewer** aViewer,
968 : nsILoadGroup** aLoadGroup)
969 : {
970 0 : NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
971 0 : *aViewer = nullptr;
972 0 : *aLoadGroup = nullptr;
973 :
974 0 : nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
975 0 : NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
976 :
977 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
978 0 : if (httpChannel) {
979 : bool requestSucceeded;
980 0 : if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
981 0 : !requestSucceeded) {
982 : // Bail out on this load, since it looks like we have an HTTP error page
983 0 : return NS_BINDING_ABORTED;
984 : }
985 : }
986 :
987 0 : nsAutoCString type;
988 0 : chan->GetContentType(type);
989 :
990 0 : nsCOMPtr<nsILoadGroup> loadGroup;
991 0 : chan->GetLoadGroup(getter_AddRefs(loadGroup));
992 :
993 : // Give this document its own loadgroup
994 : nsCOMPtr<nsILoadGroup> newLoadGroup =
995 0 : do_CreateInstance(NS_LOADGROUP_CONTRACTID);
996 0 : NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
997 0 : newLoadGroup->SetLoadGroup(loadGroup);
998 :
999 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
1000 0 : loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1001 :
1002 : nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1003 0 : new LoadgroupCallbacks(callbacks);
1004 0 : newLoadGroup->SetNotificationCallbacks(newCallbacks);
1005 :
1006 : // This is some serious hackery cribbed from docshell
1007 : nsCOMPtr<nsICategoryManager> catMan =
1008 0 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1009 0 : NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1010 0 : nsXPIDLCString contractId;
1011 0 : nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1012 0 : getter_Copies(contractId));
1013 0 : NS_ENSURE_SUCCESS(rv, rv);
1014 : nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1015 0 : do_GetService(contractId);
1016 0 : NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1017 :
1018 0 : nsCOMPtr<nsIContentViewer> viewer;
1019 0 : nsCOMPtr<nsIStreamListener> listener;
1020 0 : rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1021 : type, nullptr, nullptr,
1022 0 : getter_AddRefs(listener),
1023 0 : getter_AddRefs(viewer));
1024 0 : NS_ENSURE_SUCCESS(rv, rv);
1025 0 : NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1026 :
1027 0 : nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1028 0 : if (!parser) {
1029 : /// We don't want to deal with the various fake documents yet
1030 0 : return NS_ERROR_NOT_IMPLEMENTED;
1031 : }
1032 :
1033 : // We can't handle HTML and other weird things here yet.
1034 0 : nsIContentSink* sink = parser->GetContentSink();
1035 0 : nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1036 0 : if (!xmlSink) {
1037 0 : return NS_ERROR_NOT_IMPLEMENTED;
1038 : }
1039 :
1040 0 : listener.swap(mTargetListener);
1041 0 : viewer.forget(aViewer);
1042 0 : newLoadGroup.forget(aLoadGroup);
1043 0 : return NS_OK;
1044 : }
1045 :
1046 : NS_IMETHODIMP
1047 0 : nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1048 : nsISupports* aContext,
1049 : nsIInputStream* aStream,
1050 : uint64_t aOffset,
1051 : uint32_t aCount)
1052 : {
1053 0 : NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
1054 0 : if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1055 0 : return NS_BINDING_ABORTED;
1056 : }
1057 0 : return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1058 0 : aCount);
1059 : }
1060 :
1061 : NS_IMETHODIMP
1062 0 : nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1063 : nsISupports* aContext,
1064 : nsresult aStatus)
1065 : {
1066 : // mTargetListener might be null if SetupViewer or AddExternalResource failed
1067 0 : if (mTargetListener) {
1068 0 : nsCOMPtr<nsIStreamListener> listener;
1069 0 : mTargetListener.swap(listener);
1070 0 : return listener->OnStopRequest(aRequest, aContext, aStatus);
1071 : }
1072 :
1073 0 : return NS_OK;
1074 : }
1075 :
1076 : nsresult
1077 0 : nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1078 : nsINode* aRequestingNode)
1079 : {
1080 0 : NS_PRECONDITION(aURI, "Must have a URI");
1081 0 : NS_PRECONDITION(aRequestingNode, "Must have a node");
1082 :
1083 : nsCOMPtr<nsILoadGroup> loadGroup =
1084 0 : aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
1085 :
1086 0 : nsresult rv = NS_OK;
1087 0 : nsCOMPtr<nsIChannel> channel;
1088 0 : rv = NS_NewChannel(getter_AddRefs(channel),
1089 : aURI,
1090 : aRequestingNode,
1091 : nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
1092 : nsIContentPolicy::TYPE_OTHER,
1093 0 : loadGroup);
1094 0 : NS_ENSURE_SUCCESS(rv, rv);
1095 :
1096 0 : mURI = aURI;
1097 :
1098 0 : return channel->AsyncOpen2(this);
1099 : }
1100 :
1101 0 : NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1102 : nsIInterfaceRequestor)
1103 :
1104 : #define IMPL_SHIM(_i) \
1105 : NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1106 :
1107 0 : IMPL_SHIM(nsILoadContext)
1108 0 : IMPL_SHIM(nsIProgressEventSink)
1109 0 : IMPL_SHIM(nsIChannelEventSink)
1110 0 : IMPL_SHIM(nsISecurityEventSink)
1111 0 : IMPL_SHIM(nsIApplicationCacheContainer)
1112 :
1113 : #undef IMPL_SHIM
1114 :
1115 : #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1116 :
1117 : #define TRY_SHIM(_i) \
1118 : PR_BEGIN_MACRO \
1119 : if (IID_IS(_i)) { \
1120 : nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1121 : if (!real) { \
1122 : return NS_NOINTERFACE; \
1123 : } \
1124 : nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1125 : shim.forget(aSink); \
1126 : return NS_OK; \
1127 : } \
1128 : PR_END_MACRO
1129 :
1130 : NS_IMETHODIMP
1131 0 : nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1132 : void **aSink)
1133 : {
1134 0 : if (mCallbacks &&
1135 0 : (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
1136 0 : IID_IS(nsITabChild))) {
1137 0 : return mCallbacks->GetInterface(aIID, aSink);
1138 : }
1139 :
1140 0 : *aSink = nullptr;
1141 :
1142 0 : TRY_SHIM(nsILoadContext);
1143 0 : TRY_SHIM(nsIProgressEventSink);
1144 0 : TRY_SHIM(nsIChannelEventSink);
1145 0 : TRY_SHIM(nsISecurityEventSink);
1146 0 : TRY_SHIM(nsIApplicationCacheContainer);
1147 :
1148 0 : return NS_NOINTERFACE;
1149 : }
1150 :
1151 : #undef TRY_SHIM
1152 : #undef IID_IS
1153 :
1154 0 : nsExternalResourceMap::ExternalResource::~ExternalResource()
1155 : {
1156 0 : if (mViewer) {
1157 0 : mViewer->Close(nullptr);
1158 0 : mViewer->Destroy();
1159 : }
1160 0 : }
1161 :
1162 : // ==================================================================
1163 : // =
1164 : // ==================================================================
1165 :
1166 : // If we ever have an nsIDocumentObserver notification for stylesheet title
1167 : // changes we should update the list from that instead of overriding
1168 : // EnsureFresh.
1169 0 : class nsDOMStyleSheetSetList final : public DOMStringList
1170 : {
1171 : public:
1172 : explicit nsDOMStyleSheetSetList(nsIDocument* aDocument);
1173 :
1174 0 : void Disconnect()
1175 : {
1176 0 : mDocument = nullptr;
1177 0 : }
1178 :
1179 : virtual void EnsureFresh() override;
1180 :
1181 : protected:
1182 : nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it
1183 : // dies.
1184 : };
1185 :
1186 0 : nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1187 0 : : mDocument(aDocument)
1188 : {
1189 0 : NS_ASSERTION(mDocument, "Must have document!");
1190 0 : }
1191 :
1192 : void
1193 0 : nsDOMStyleSheetSetList::EnsureFresh()
1194 : {
1195 0 : MOZ_ASSERT(NS_IsMainThread());
1196 :
1197 0 : mNames.Clear();
1198 :
1199 0 : if (!mDocument) {
1200 0 : return; // Spec says "no exceptions", and we have no style sets if we have
1201 : // no document, for sure
1202 : }
1203 :
1204 0 : int32_t count = mDocument->GetNumberOfStyleSheets();
1205 0 : nsAutoString title;
1206 0 : for (int32_t index = 0; index < count; index++) {
1207 0 : StyleSheet* sheet = mDocument->GetStyleSheetAt(index);
1208 0 : NS_ASSERTION(sheet, "Null sheet in sheet list!");
1209 0 : sheet->GetTitle(title);
1210 0 : if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1211 0 : return;
1212 : }
1213 : }
1214 : }
1215 :
1216 : // ==================================================================
1217 2 : nsIDocument::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget)
1218 : : nsExpirationTracker<SelectorCacheKey, 4>(
1219 2 : 1000, "nsIDocument::SelectorCache", aEventTarget)
1220 2 : { }
1221 :
1222 0 : nsIDocument::SelectorCache::~SelectorCache()
1223 : {
1224 0 : AgeAllGenerations();
1225 0 : }
1226 :
1227 : // CacheList takes ownership of aSelectorList.
1228 7 : void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector,
1229 : nsCSSSelectorList* aSelectorList)
1230 : {
1231 7 : MOZ_ASSERT(NS_IsMainThread());
1232 7 : SelectorCacheKey* key = new SelectorCacheKey(aSelector);
1233 7 : mTable.Put(key->mKey, aSelectorList);
1234 7 : AddObject(key);
1235 7 : }
1236 :
1237 0 : void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
1238 : {
1239 0 : MOZ_ASSERT(NS_IsMainThread());
1240 0 : MOZ_ASSERT(aSelector);
1241 :
1242 : // There is no guarantee that this method won't be re-entered when selector
1243 : // matching is ongoing because "memory-pressure" could be notified immediately
1244 : // when OOM happens according to the design of nsExpirationTracker.
1245 : // The perfect solution is to delete the |aSelector| and its nsCSSSelectorList
1246 : // in mTable asynchronously.
1247 : // We remove these objects synchronously for now because NotifiyExpired() will
1248 : // never be triggered by "memory-pressure" which is not implemented yet in
1249 : // the stage 2 of mozalloc_handle_oom().
1250 : // Once these objects are removed asynchronously, we should update the warning
1251 : // added in mozalloc_handle_oom() as well.
1252 0 : RemoveObject(aSelector);
1253 0 : mTable.Remove(aSelector->mKey);
1254 0 : delete aSelector;
1255 0 : }
1256 :
1257 12 : struct nsIDocument::FrameRequest
1258 : {
1259 4 : FrameRequest(FrameRequestCallback& aCallback,
1260 4 : int32_t aHandle) :
1261 : mCallback(&aCallback),
1262 4 : mHandle(aHandle)
1263 4 : {}
1264 :
1265 : // Conversion operator so that we can append these to a
1266 : // FrameRequestCallbackList
1267 4 : operator const RefPtr<FrameRequestCallback>& () const {
1268 4 : return mCallback;
1269 : }
1270 :
1271 : // Comparator operators to allow RemoveElementSorted with an
1272 : // integer argument on arrays of FrameRequest
1273 0 : bool operator==(int32_t aHandle) const {
1274 0 : return mHandle == aHandle;
1275 : }
1276 0 : bool operator<(int32_t aHandle) const {
1277 0 : return mHandle < aHandle;
1278 : }
1279 :
1280 : RefPtr<FrameRequestCallback> mCallback;
1281 : int32_t mHandle;
1282 : };
1283 :
1284 3 : static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
1285 :
1286 : // ==================================================================
1287 : // =
1288 : // ==================================================================
1289 55 : nsIDocument::nsIDocument()
1290 : : nsINode(nullNodeInfo),
1291 : mReferrerPolicySet(false),
1292 : mReferrerPolicy(mozilla::net::RP_Unset),
1293 : mBlockAllMixedContent(false),
1294 : mBlockAllMixedContentPreloads(false),
1295 : mUpgradeInsecureRequests(false),
1296 : mUpgradeInsecurePreloads(false),
1297 : mCharacterSet(WINDOWS_1252_ENCODING),
1298 : mCharacterSetSource(0),
1299 : mParentDocument(nullptr),
1300 : mCachedRootElement(nullptr),
1301 : mNodeInfoManager(nullptr),
1302 : mBidiEnabled(false),
1303 : mMathMLEnabled(false),
1304 : mIsInitialDocumentInWindow(false),
1305 : mIgnoreDocGroupMismatches(false),
1306 : mLoadedAsData(false),
1307 : mLoadedAsInteractiveData(false),
1308 : mMayStartLayout(true),
1309 : mHaveFiredTitleChange(false),
1310 : mIsShowing(false),
1311 : mVisible(true),
1312 : mHasReferrerPolicyCSP(false),
1313 : mRemovedFromDocShell(false),
1314 : // mAllowDNSPrefetch starts true, so that we can always reliably && it
1315 : // with various values that might disable it. Since we never prefetch
1316 : // unless we get a window, and in that case the docshell value will get
1317 : // &&-ed in, this is safe.
1318 : mAllowDNSPrefetch(true),
1319 : mIsStaticDocument(false),
1320 : mCreatingStaticClone(false),
1321 : mInUnlinkOrDeletion(false),
1322 : mHasHadScriptHandlingObject(false),
1323 : mIsBeingUsedAsImage(false),
1324 : mIsSyntheticDocument(false),
1325 : mHasLinksToUpdate(false),
1326 : mHasLinksToUpdateRunnable(false),
1327 : mMayHaveDOMMutationObservers(false),
1328 : mMayHaveAnimationObservers(false),
1329 : mHasMixedActiveContentLoaded(false),
1330 : mHasMixedActiveContentBlocked(false),
1331 : mHasMixedDisplayContentLoaded(false),
1332 : mHasMixedDisplayContentBlocked(false),
1333 : mHasMixedContentObjectSubrequest(false),
1334 : mHasCSP(false),
1335 : mHasUnsafeEvalCSP(false),
1336 : mHasUnsafeInlineCSP(false),
1337 : mHasTrackingContentBlocked(false),
1338 : mHasTrackingContentLoaded(false),
1339 : mBFCacheDisallowed(false),
1340 : mHasHadDefaultView(false),
1341 : mStyleSheetChangeEventsEnabled(false),
1342 : mIsSrcdocDocument(false),
1343 : mDidDocumentOpen(false),
1344 : mHasDisplayDocument(false),
1345 : mFontFaceSetDirty(true),
1346 : mGetUserFontSetCalled(false),
1347 : mPostedFlushUserFontSet(false),
1348 : mDidFireDOMContentLoaded(true),
1349 : mHasScrollLinkedEffect(false),
1350 : mFrameRequestCallbacksScheduled(false),
1351 : mIsTopLevelContentDocument(false),
1352 : mIsContentDocument(false),
1353 : mIsScopedStyleEnabled(eScopedStyle_Unknown),
1354 : mCompatMode(eCompatibility_FullStandards),
1355 : mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
1356 : mStyleBackendType(StyleBackendType::None),
1357 : #ifdef MOZILLA_INTERNAL_API
1358 : mVisibilityState(dom::VisibilityState::Hidden),
1359 : #else
1360 : mDummy(0),
1361 : #endif
1362 : mType(eUnknown),
1363 : mDefaultElementType(0),
1364 : mAllowXULXBL(eTriUnset),
1365 : #ifdef DEBUG
1366 : mIsLinkUpdateRegistrationsForbidden(false),
1367 : #endif
1368 : mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
1369 : mSandboxFlags(0),
1370 : mPartID(0),
1371 : mMarkedCCGeneration(0),
1372 : mPresShell(nullptr),
1373 : mSubtreeModifiedDepth(0),
1374 : mEventsSuppressed(0),
1375 : mExternalScriptsBeingEvaluated(0),
1376 : mFrameRequestCallbackCounter(0),
1377 : mStaticCloneCount(0),
1378 : mWindow(nullptr),
1379 : mBFCacheEntry(nullptr),
1380 : mInSyncOperationCount(0),
1381 : mBlockDOMContentLoaded(0),
1382 : mUseCounters(0),
1383 : mChildDocumentUseCounters(0),
1384 : mNotifiedPageForUseCounter(0),
1385 : mIncCounters(),
1386 55 : mUserHasInteracted(false)
1387 : {
1388 55 : SetIsInDocument();
1389 110 : for (auto& cnt : mIncCounters) {
1390 55 : cnt = 0;
1391 : }
1392 55 : }
1393 :
1394 55 : nsDocument::nsDocument(const char* aContentType)
1395 : : nsIDocument()
1396 : , mSubDocuments(nullptr)
1397 : , mFlashClassification(FlashClassification::Unclassified)
1398 : , mHeaderData(nullptr)
1399 : , mIsGoingAway(false)
1400 : , mInDestructor(false)
1401 : , mMayHaveTitleElement(false)
1402 : , mHasWarnedAboutBoxObjects(false)
1403 : , mDelayFrameLoaderInitialization(false)
1404 : , mSynchronousDOMContentLoaded(false)
1405 : , mInXBLUpdate(false)
1406 : , mParserAborted(false)
1407 : , mCurrentOrientationAngle(0)
1408 : , mCurrentOrientationType(OrientationType::Portrait_primary)
1409 : , mSSApplicableStateNotificationPending(false)
1410 : , mReportedUseCounters(false)
1411 : , mStyleSetFilled(false)
1412 : , mPendingFullscreenRequests(0)
1413 : , mXMLDeclarationBits(0)
1414 : , mBoxObjectTable(nullptr)
1415 : , mUpdateNestLevel(0)
1416 : , mOnloadBlockCount(0)
1417 : , mAsyncOnloadBlockCount(0)
1418 : #ifdef DEBUG
1419 : , mStyledLinksCleared(false)
1420 : #endif
1421 : , mPreloadPictureDepth(0)
1422 : , mScrolledToRefAlready(0)
1423 : , mChangeScrollPosWhenScrollingToRef(0)
1424 : , mViewportType(Unknown)
1425 : , mValidWidth(false)
1426 : , mValidHeight(false)
1427 : , mAutoSize(false)
1428 : , mAllowZoom(false)
1429 : , mAllowDoubleTapZoom(false)
1430 : , mValidScaleFloat(false)
1431 : , mValidMaxScale(false)
1432 : , mScaleStrEmpty(false)
1433 : , mWidthStrEmpty(false)
1434 : , mStackRefCnt(0)
1435 : , mNeedsReleaseAfterStackRefCntRelease(false)
1436 : , mMaybeServiceWorkerControlled(false)
1437 : #ifdef DEBUG
1438 55 : , mWillReparent(false)
1439 : #endif
1440 : {
1441 55 : SetContentTypeInternal(nsDependentCString(aContentType));
1442 :
1443 55 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
1444 :
1445 : // Start out mLastStyleSheetSet as null, per spec
1446 55 : SetDOMStringToNull(mLastStyleSheetSet);
1447 :
1448 : // void state used to differentiate an empty source from an unselected source
1449 55 : mPreloadPictureFoundSource.SetIsVoid(true);
1450 55 : }
1451 :
1452 : void
1453 0 : nsDocument::ClearAllBoxObjects()
1454 : {
1455 0 : if (mBoxObjectTable) {
1456 0 : for (auto iter = mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1457 0 : nsPIBoxObject* boxObject = iter.UserData();
1458 0 : if (boxObject) {
1459 0 : boxObject->Clear();
1460 : }
1461 : }
1462 0 : delete mBoxObjectTable;
1463 0 : mBoxObjectTable = nullptr;
1464 : }
1465 0 : }
1466 :
1467 0 : nsIDocument::~nsIDocument()
1468 : {
1469 0 : MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
1470 : "must not have media query lists left");
1471 :
1472 0 : if (mNodeInfoManager) {
1473 0 : mNodeInfoManager->DropDocumentReference();
1474 : }
1475 :
1476 0 : if (mDocGroup) {
1477 0 : mDocGroup->RemoveDocument(this);
1478 : }
1479 :
1480 0 : UnlinkOriginalDocumentIfStatic();
1481 0 : }
1482 :
1483 : bool
1484 0 : nsDocument::IsAboutPage() const
1485 : {
1486 0 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
1487 0 : nsCOMPtr<nsIURI> uri;
1488 0 : principal->GetURI(getter_AddRefs(uri));
1489 0 : bool isAboutScheme = true;
1490 0 : if (uri) {
1491 0 : uri->SchemeIs("about", &isAboutScheme);
1492 : }
1493 0 : return isAboutScheme;
1494 : }
1495 :
1496 0 : nsDocument::~nsDocument()
1497 : {
1498 0 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
1499 :
1500 0 : NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1501 :
1502 0 : if (IsTopLevelContentDocument()) {
1503 : //don't report for about: pages
1504 0 : if (!IsAboutPage()) {
1505 : // Record the page load
1506 0 : uint32_t pageLoaded = 1;
1507 0 : Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1508 : // Record the mixed content status of the docshell in Telemetry
1509 : enum {
1510 : NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1511 : MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
1512 : MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
1513 : MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
1514 : };
1515 :
1516 0 : bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1517 0 : bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1518 :
1519 0 : bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1520 0 : bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1521 :
1522 0 : bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1523 0 : bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1524 :
1525 0 : uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1526 0 : if (hasMixedDisplay && hasMixedActive) {
1527 0 : mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1528 0 : } else if (hasMixedActive){
1529 0 : mixedContentLevel = MIXED_ACTIVE_CONTENT;
1530 0 : } else if (hasMixedDisplay) {
1531 0 : mixedContentLevel = MIXED_DISPLAY_CONTENT;
1532 : }
1533 0 : Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1534 :
1535 : // record mixed object subrequest telemetry
1536 0 : if (mHasMixedContentObjectSubrequest) {
1537 : /* mixed object subrequest loaded on page*/
1538 0 : Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 1);
1539 : } else {
1540 : /* no mixed object subrequests loaded on page*/
1541 0 : Accumulate(Telemetry::MIXED_CONTENT_OBJECT_SUBREQUEST, 0);
1542 : }
1543 :
1544 : // record CSP telemetry on this document
1545 0 : if (mHasCSP) {
1546 0 : Accumulate(Telemetry::CSP_DOCUMENTS_COUNT, 1);
1547 0 : Accumulate(Telemetry::CSP_REFERRER_DIRECTIVE, mHasReferrerPolicyCSP);
1548 : }
1549 0 : if (mHasUnsafeInlineCSP) {
1550 0 : Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT, 1);
1551 : }
1552 0 : if (mHasUnsafeEvalCSP) {
1553 0 : Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT, 1);
1554 : }
1555 : }
1556 : }
1557 :
1558 0 : ReportUseCounters();
1559 :
1560 0 : mInDestructor = true;
1561 0 : mInUnlinkOrDeletion = true;
1562 :
1563 0 : mozilla::DropJSObjects(this);
1564 :
1565 : // Clear mObservers to keep it in sync with the mutationobserver list
1566 0 : mObservers.Clear();
1567 :
1568 0 : mIntersectionObservers.Clear();
1569 :
1570 0 : if (mStyleSheetSetList) {
1571 0 : mStyleSheetSetList->Disconnect();
1572 : }
1573 :
1574 0 : if (mAnimationController) {
1575 0 : mAnimationController->Disconnect();
1576 : }
1577 :
1578 0 : MOZ_ASSERT(mTimelines.isEmpty());
1579 :
1580 0 : mParentDocument = nullptr;
1581 :
1582 : // Kill the subdocument map, doing this will release its strong
1583 : // references, if any.
1584 0 : delete mSubDocuments;
1585 0 : mSubDocuments = nullptr;
1586 :
1587 : // Destroy link map now so we don't waste time removing
1588 : // links one by one
1589 0 : DestroyElementMaps();
1590 :
1591 0 : nsAutoScriptBlocker scriptBlocker;
1592 :
1593 0 : for (uint32_t indx = mChildren.ChildCount(); indx-- != 0; ) {
1594 0 : mChildren.ChildAt(indx)->UnbindFromTree();
1595 0 : mChildren.RemoveChildAt(indx);
1596 : }
1597 0 : mFirstChild = nullptr;
1598 0 : mCachedRootElement = nullptr;
1599 :
1600 : // Let the stylesheets know we're going away
1601 0 : for (StyleSheet* sheet : mStyleSheets) {
1602 0 : sheet->ClearAssociatedDocument();
1603 : }
1604 0 : for (auto& sheets : mAdditionalSheets) {
1605 0 : for (StyleSheet* sheet : sheets) {
1606 0 : sheet->ClearAssociatedDocument();
1607 : }
1608 : }
1609 0 : if (mAttrStyleSheet) {
1610 0 : mAttrStyleSheet->SetOwningDocument(nullptr);
1611 : }
1612 : // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
1613 :
1614 0 : if (mListenerManager) {
1615 0 : mListenerManager->Disconnect();
1616 0 : UnsetFlags(NODE_HAS_LISTENERMANAGER);
1617 : }
1618 :
1619 0 : if (mScriptLoader) {
1620 0 : mScriptLoader->DropDocumentReference();
1621 : }
1622 :
1623 0 : if (mCSSLoader) {
1624 : // Could be null here if Init() failed or if we have been unlinked.
1625 0 : mCSSLoader->DropDocumentReference();
1626 : }
1627 :
1628 0 : if (mStyleImageLoader) {
1629 0 : mStyleImageLoader->DropDocumentReference();
1630 : }
1631 :
1632 0 : delete mHeaderData;
1633 :
1634 0 : ClearAllBoxObjects();
1635 :
1636 0 : mPendingTitleChangeEvent.Revoke();
1637 :
1638 0 : mPlugins.Clear();
1639 0 : }
1640 :
1641 10464 : NS_INTERFACE_TABLE_HEAD(nsDocument)
1642 10464 : NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
1643 : NS_INTERFACE_TABLE_BEGIN
1644 : NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1645 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1646 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1647 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument)
1648 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode)
1649 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
1650 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1651 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
1652 : NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1653 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1654 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1655 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1656 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1657 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver)
1658 : NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator)
1659 10444 : NS_INTERFACE_TABLE_END
1660 10444 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1661 981 : NS_INTERFACE_MAP_END
1662 :
1663 :
1664 17801 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1665 : NS_IMETHODIMP_(MozExternalRefCountType)
1666 17413 : nsDocument::Release()
1667 : {
1668 17413 : NS_PRECONDITION(0 != mRefCnt, "dup release");
1669 17413 : NS_ASSERT_OWNINGTHREAD(nsDocument);
1670 17413 : nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1671 17413 : bool shouldDelete = false;
1672 17413 : nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1673 17413 : NS_LOG_RELEASE(this, count, "nsDocument");
1674 17413 : if (count == 0) {
1675 0 : if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1676 0 : mNeedsReleaseAfterStackRefCntRelease = true;
1677 0 : NS_ADDREF_THIS();
1678 0 : return mRefCnt.get();
1679 : }
1680 0 : mRefCnt.incr(base);
1681 0 : nsNodeUtils::LastRelease(this);
1682 0 : mRefCnt.decr(base);
1683 0 : if (shouldDelete) {
1684 0 : mRefCnt.stabilizeForDeletion();
1685 0 : DeleteCycleCollectable();
1686 : }
1687 : }
1688 17413 : return count;
1689 : }
1690 :
1691 : NS_IMETHODIMP_(void)
1692 0 : nsDocument::DeleteCycleCollectable()
1693 : {
1694 0 : delete this;
1695 0 : }
1696 :
1697 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1698 0 : if (Element::CanSkip(tmp, aRemovingAllowed)) {
1699 0 : EventListenerManager* elm = tmp->GetExistingListenerManager();
1700 0 : if (elm) {
1701 0 : elm->MarkForCC();
1702 : }
1703 0 : return true;
1704 : }
1705 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1706 :
1707 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1708 0 : return Element::CanSkipInCC(tmp);
1709 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1710 :
1711 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1712 0 : return Element::CanSkipThis(tmp);
1713 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1714 :
1715 : static const char* kNSURIs[] = {
1716 : "([none])",
1717 : "(xmlns)",
1718 : "(xml)",
1719 : "(xhtml)",
1720 : "(XLink)",
1721 : "(XSLT)",
1722 : "(XBL)",
1723 : "(MathML)",
1724 : "(RDF)",
1725 : "(XUL)"
1726 : };
1727 :
1728 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1729 15 : if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1730 : char name[512];
1731 0 : nsAutoCString loadedAsData;
1732 0 : if (tmp->IsLoadedAsData()) {
1733 0 : loadedAsData.AssignLiteral("data");
1734 : } else {
1735 0 : loadedAsData.AssignLiteral("normal");
1736 : }
1737 0 : uint32_t nsid = tmp->GetDefaultNamespaceID();
1738 0 : nsAutoCString uri;
1739 0 : if (tmp->mDocumentURI)
1740 0 : uri = tmp->mDocumentURI->GetSpecOrDefault();
1741 0 : if (nsid < ArrayLength(kNSURIs)) {
1742 0 : SprintfLiteral(name, "nsDocument %s %s %s",
1743 0 : loadedAsData.get(), kNSURIs[nsid], uri.get());
1744 : }
1745 : else {
1746 0 : SprintfLiteral(name, "nsDocument %s %s",
1747 0 : loadedAsData.get(), uri.get());
1748 : }
1749 0 : cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1750 : }
1751 : else {
1752 15 : NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1753 : }
1754 :
1755 15 : if (!nsINode::Traverse(tmp, cb)) {
1756 0 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1757 : }
1758 :
1759 15 : if (tmp->mMaybeEndOutermostXBLUpdateRunner) {
1760 : // The cached runnable keeps a reference to the document object..
1761 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1762 0 : "mMaybeEndOutermostXBLUpdateRunner.mObj");
1763 0 : cb.NoteXPCOMChild(ToSupports(tmp));
1764 : }
1765 :
1766 15 : for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
1767 0 : iter.Next()) {
1768 0 : iter.Get()->Traverse(&cb);
1769 : }
1770 :
1771 15 : tmp->mExternalResourceMap.Traverse(&cb);
1772 :
1773 : // Traverse the mChildren nsAttrAndChildArray.
1774 25 : for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1775 10 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1776 10 : cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1777 : }
1778 :
1779 : // Traverse all nsIDocument pointer members.
1780 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1781 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1782 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
1783 :
1784 : // Traverse all nsDocument nsCOMPtrs.
1785 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1786 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1787 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1788 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1789 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1790 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1791 :
1792 15 : for (auto iter = tmp->mRadioGroups.Iter(); !iter.Done(); iter.Next()) {
1793 0 : nsRadioGroupStruct* radioGroup = iter.UserData();
1794 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1795 0 : cb, "mRadioGroups entry->mSelectedRadioButton");
1796 0 : cb.NoteXPCOMChild(ToSupports(radioGroup->mSelectedRadioButton));
1797 :
1798 0 : uint32_t i, count = radioGroup->mRadioButtons.Count();
1799 0 : for (i = 0; i < count; ++i) {
1800 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(
1801 0 : cb, "mRadioGroups entry->mRadioButtons[i]");
1802 0 : cb.NoteXPCOMChild(radioGroup->mRadioButtons[i]);
1803 : }
1804 : }
1805 :
1806 : // The boxobject for an element will only exist as long as it's in the
1807 : // document, so we'll traverse the table here instead of from the element.
1808 15 : if (tmp->mBoxObjectTable) {
1809 0 : for (auto iter = tmp->mBoxObjectTable->Iter(); !iter.Done(); iter.Next()) {
1810 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mBoxObjectTable entry");
1811 0 : cb.NoteXPCOMChild(iter.UserData());
1812 : }
1813 : }
1814 :
1815 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
1816 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
1817 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator)
1818 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
1819 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
1820 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
1821 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
1822 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
1823 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
1824 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
1825 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
1826 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
1827 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
1828 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
1829 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
1830 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
1831 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
1832 :
1833 : // Traverse all our nsCOMArrays.
1834 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
1835 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
1836 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
1837 :
1838 15 : for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
1839 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
1840 0 : cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback);
1841 : }
1842 :
1843 : // Traverse animation components
1844 15 : if (tmp->mAnimationController) {
1845 0 : tmp->mAnimationController->Traverse(&cb);
1846 : }
1847 :
1848 15 : if (tmp->mSubDocuments) {
1849 0 : for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
1850 0 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
1851 :
1852 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1853 0 : "mSubDocuments entry->mKey");
1854 0 : cb.NoteXPCOMChild(entry->mKey);
1855 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1856 0 : "mSubDocuments entry->mSubDocument");
1857 0 : cb.NoteXPCOMChild(entry->mSubDocument);
1858 : }
1859 : }
1860 :
1861 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
1862 :
1863 : // We own only the items in mDOMMediaQueryLists that have listeners;
1864 : // this reference is managed by their AddListener and RemoveListener
1865 : // methods.
1866 15 : for (auto mql : tmp->mDOMMediaQueryLists) {
1867 0 : if (mql->HasListeners()) {
1868 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
1869 0 : cb.NoteXPCOMChild(mql);
1870 : }
1871 : }
1872 15 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1873 :
1874 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
1875 :
1876 81 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDocument)
1877 :
1878 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
1879 0 : tmp->mInUnlinkOrDeletion = true;
1880 :
1881 : // Clear out our external resources
1882 0 : tmp->mExternalResourceMap.Shutdown();
1883 :
1884 0 : nsAutoScriptBlocker scriptBlocker;
1885 :
1886 0 : nsINode::Unlink(tmp);
1887 :
1888 : // Unlink the mChildren nsAttrAndChildArray.
1889 0 : uint32_t childCount = tmp->mChildren.ChildCount();
1890 0 : if (childCount) {
1891 0 : while (childCount-- > 0) {
1892 : // Hold a strong ref to the node when we remove it, because we may be
1893 : // the last reference to it. We need to call TakeChildAt() and
1894 : // update mFirstChild before calling UnbindFromTree, since this last
1895 : // can notify various observers and they should really see consistent
1896 : // tree state.
1897 : // If this code changes, change the corresponding code in
1898 : // FragmentOrElement's unlink impl and ContentUnbinder::UnbindSubtree.
1899 0 : nsCOMPtr<nsIContent> child = tmp->mChildren.TakeChildAt(childCount);
1900 0 : if (childCount == 0) {
1901 0 : tmp->mFirstChild = nullptr;
1902 : }
1903 0 : child->UnbindFromTree();
1904 : }
1905 : }
1906 0 : tmp->mFirstChild = nullptr;
1907 :
1908 0 : tmp->UnlinkOriginalDocumentIfStatic();
1909 :
1910 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator)
1911 0 : tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
1912 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
1913 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
1914 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaybeEndOutermostXBLUpdateRunner)
1915 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
1916 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
1917 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
1918 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
1919 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
1920 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
1921 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
1922 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
1923 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
1924 :
1925 0 : tmp->mParentDocument = nullptr;
1926 :
1927 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
1928 :
1929 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
1930 :
1931 0 : tmp->ClearAllBoxObjects();
1932 :
1933 0 : if (tmp->mListenerManager) {
1934 0 : tmp->mListenerManager->Disconnect();
1935 0 : tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
1936 0 : tmp->mListenerManager = nullptr;
1937 : }
1938 :
1939 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
1940 :
1941 0 : if (tmp->mStyleSheetSetList) {
1942 0 : tmp->mStyleSheetSetList->Disconnect();
1943 0 : tmp->mStyleSheetSetList = nullptr;
1944 : }
1945 :
1946 0 : delete tmp->mSubDocuments;
1947 0 : tmp->mSubDocuments = nullptr;
1948 :
1949 0 : tmp->mFrameRequestCallbacks.Clear();
1950 0 : MOZ_RELEASE_ASSERT(!tmp->mFrameRequestCallbacksScheduled,
1951 : "How did we get here without our presshell going away "
1952 : "first?");
1953 :
1954 0 : tmp->mRadioGroups.Clear();
1955 :
1956 : // nsDocument has a pretty complex destructor, so we're going to
1957 : // assume that *most* cycles you actually want to break somewhere
1958 : // else, and not unlink an awful lot here.
1959 :
1960 0 : tmp->mIdentifierMap.Clear();
1961 0 : tmp->mExpandoAndGeneration.OwnerUnlinked();
1962 :
1963 0 : if (tmp->mAnimationController) {
1964 0 : tmp->mAnimationController->Unlink();
1965 : }
1966 :
1967 0 : tmp->mPendingTitleChangeEvent.Revoke();
1968 :
1969 0 : if (tmp->mCSSLoader) {
1970 0 : tmp->mCSSLoader->DropDocumentReference();
1971 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
1972 : }
1973 :
1974 : // We own only the items in mDOMMediaQueryLists that have listeners;
1975 : // this reference is managed by their AddListener and RemoveListener
1976 : // methods.
1977 0 : for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
1978 0 : MediaQueryList* next = mql->getNext();
1979 0 : mql->Disconnect();
1980 0 : mql = next;
1981 : }
1982 :
1983 0 : tmp->mInUnlinkOrDeletion = false;
1984 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1985 :
1986 : nsresult
1987 55 : nsDocument::Init()
1988 : {
1989 55 : if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
1990 0 : return NS_ERROR_ALREADY_INITIALIZED;
1991 : }
1992 :
1993 : // Force initialization.
1994 55 : nsINode::nsSlots* slots = Slots();
1995 :
1996 : // Prepend self as mutation-observer whether we need it or not (some
1997 : // subclasses currently do, other don't). This is because the code in
1998 : // nsNodeUtils always notifies the first observer first, expecting the
1999 : // first observer to be the document.
2000 55 : NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
2001 : NS_ERROR_OUT_OF_MEMORY);
2002 :
2003 :
2004 55 : mOnloadBlocker = new nsOnloadBlocker();
2005 55 : mCSSLoader = new mozilla::css::Loader(this);
2006 : // Assume we're not quirky, until we know otherwise
2007 55 : mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2008 :
2009 55 : mStyleImageLoader = new mozilla::css::ImageLoader(this);
2010 :
2011 55 : mNodeInfoManager = new nsNodeInfoManager();
2012 55 : nsresult rv = mNodeInfoManager->Init(this);
2013 55 : NS_ENSURE_SUCCESS(rv, rv);
2014 :
2015 : // mNodeInfo keeps NodeInfoManager alive!
2016 55 : mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2017 55 : NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2018 55 : MOZ_ASSERT(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
2019 : "Bad NodeType in aNodeInfo");
2020 :
2021 55 : NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2022 :
2023 : // If after creation the owner js global is not set for a document
2024 : // we use the default compartment for this document, instead of creating
2025 : // wrapper in some random compartment when the document is exposed to js
2026 : // via some events.
2027 110 : nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2028 55 : NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2029 55 : mScopeObject = do_GetWeakReference(global);
2030 55 : MOZ_ASSERT(mScopeObject);
2031 :
2032 55 : mScriptLoader = new dom::ScriptLoader(this);
2033 :
2034 55 : mozilla::HoldJSObjects(this);
2035 :
2036 55 : return NS_OK;
2037 : }
2038 :
2039 : void
2040 0 : nsIDocument::DeleteAllProperties()
2041 : {
2042 0 : for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2043 0 : PropertyTable(i)->DeleteAllProperties();
2044 : }
2045 0 : }
2046 :
2047 : void
2048 0 : nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
2049 : {
2050 0 : for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2051 0 : PropertyTable(i)->DeleteAllPropertiesFor(aNode);
2052 : }
2053 0 : }
2054 :
2055 : nsPropertyTable*
2056 1 : nsIDocument::GetExtraPropertyTable(uint16_t aCategory)
2057 : {
2058 1 : NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
2059 3 : while (aCategory >= mExtraPropertyTables.Length() + 1) {
2060 1 : mExtraPropertyTables.AppendElement(new nsPropertyTable());
2061 : }
2062 1 : return mExtraPropertyTables[aCategory - 1];
2063 : }
2064 :
2065 : bool
2066 0 : nsIDocument::IsVisibleConsideringAncestors() const
2067 : {
2068 0 : const nsIDocument *parent = this;
2069 0 : do {
2070 0 : if (!parent->IsVisible()) {
2071 0 : return false;
2072 : }
2073 : } while ((parent = parent->GetParentDocument()));
2074 :
2075 0 : return true;
2076 : }
2077 :
2078 : void
2079 25 : nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
2080 : {
2081 50 : nsCOMPtr<nsIURI> uri;
2082 50 : nsCOMPtr<nsIPrincipal> principal;
2083 25 : if (aChannel) {
2084 : // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2085 : // nsScriptSecurityManager::GetChannelResultPrincipal.
2086 : // Note: this should match nsDocShell::OnLoadingSite
2087 25 : NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2088 :
2089 : nsIScriptSecurityManager *securityManager =
2090 25 : nsContentUtils::GetSecurityManager();
2091 25 : if (securityManager) {
2092 25 : securityManager->GetChannelResultPrincipal(aChannel,
2093 50 : getter_AddRefs(principal));
2094 : }
2095 : }
2096 :
2097 25 : principal = MaybeDowngradePrincipal(principal);
2098 :
2099 25 : ResetToURI(uri, aLoadGroup, principal);
2100 :
2101 : // Note that, since mTiming does not change during a reset, the
2102 : // navigationStart time remains unchanged and therefore any future new
2103 : // timeline will have the same global clock time as the old one.
2104 25 : mDocumentTimeline = nullptr;
2105 :
2106 50 : nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2107 25 : if (bag) {
2108 50 : nsCOMPtr<nsIURI> baseURI;
2109 100 : bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2110 100 : NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2111 25 : if (baseURI) {
2112 1 : mDocumentBaseURI = baseURI;
2113 1 : mChromeXHRDocBaseURI = nullptr;
2114 : }
2115 : }
2116 :
2117 25 : mChannel = aChannel;
2118 25 : }
2119 :
2120 : void
2121 29 : nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
2122 : nsIPrincipal* aPrincipal)
2123 : {
2124 29 : NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
2125 :
2126 29 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2127 : ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
2128 :
2129 29 : mSecurityInfo = nullptr;
2130 :
2131 29 : mDocumentLoadGroup = nullptr;
2132 :
2133 : // Delete references to sub-documents and kill the subdocument map,
2134 : // if any. It holds strong references
2135 29 : delete mSubDocuments;
2136 29 : mSubDocuments = nullptr;
2137 :
2138 : // Destroy link map now so we don't waste time removing
2139 : // links one by one
2140 29 : DestroyElementMaps();
2141 :
2142 29 : bool oldVal = mInUnlinkOrDeletion;
2143 29 : mInUnlinkOrDeletion = true;
2144 29 : uint32_t count = mChildren.ChildCount();
2145 : { // Scope for update
2146 58 : MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true);
2147 29 : for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
2148 0 : nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
2149 :
2150 0 : nsIContent* previousSibling = content->GetPreviousSibling();
2151 :
2152 0 : if (nsINode::GetFirstChild() == content) {
2153 0 : mFirstChild = content->GetNextSibling();
2154 : }
2155 0 : mChildren.RemoveChildAt(i);
2156 0 : if (content == mCachedRootElement) {
2157 : // Immediately clear mCachedRootElement, now that it's been removed
2158 : // from mChildren, so that GetRootElement() will stop returning this
2159 : // now-stale value.
2160 0 : mCachedRootElement = nullptr;
2161 : }
2162 0 : nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
2163 0 : content->UnbindFromTree();
2164 : }
2165 29 : MOZ_ASSERT(!mCachedRootElement,
2166 : "After removing all children, there should be no root elem");
2167 : }
2168 29 : mInUnlinkOrDeletion = oldVal;
2169 :
2170 : // Reset our stylesheets
2171 29 : ResetStylesheetsToURI(aURI);
2172 :
2173 : // Release the listener manager
2174 29 : if (mListenerManager) {
2175 0 : mListenerManager->Disconnect();
2176 0 : mListenerManager = nullptr;
2177 : }
2178 :
2179 : // Release the stylesheets list.
2180 29 : mDOMStyleSheets = nullptr;
2181 :
2182 : // Release our principal after tearing down the document, rather than before.
2183 : // This ensures that, during teardown, the document and the dying window (which
2184 : // already nulled out its document pointer and cached the principal) have
2185 : // matching principals.
2186 29 : SetPrincipal(nullptr);
2187 :
2188 : // Clear the original URI so SetDocumentURI sets it.
2189 29 : mOriginalURI = nullptr;
2190 :
2191 29 : SetDocumentURI(aURI);
2192 29 : mChromeXHRDocURI = nullptr;
2193 : // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2194 : // mDocumentURI.
2195 29 : mDocumentBaseURI = nullptr;
2196 29 : mChromeXHRDocBaseURI = nullptr;
2197 :
2198 29 : if (aLoadGroup) {
2199 29 : mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2200 : // there was an assertion here that aLoadGroup was not null. This
2201 : // is no longer valid: nsDocShell::SetDocument does not create a
2202 : // load group, and it works just fine
2203 :
2204 : // XXXbz what does "just fine" mean exactly? And given that there
2205 : // is no nsDocShell::SetDocument, what is this talking about?
2206 : }
2207 :
2208 29 : mLastModified.Truncate();
2209 : // XXXbz I guess we're assuming that the caller will either pass in
2210 : // a channel with a useful type or call SetContentType?
2211 29 : SetContentTypeInternal(EmptyCString());
2212 29 : mContentLanguage.Truncate();
2213 29 : mBaseTarget.Truncate();
2214 29 : mReferrer.Truncate();
2215 :
2216 29 : mXMLDeclarationBits = 0;
2217 :
2218 : // Now get our new principal
2219 29 : if (aPrincipal) {
2220 27 : SetPrincipal(aPrincipal);
2221 : } else {
2222 : nsIScriptSecurityManager *securityManager =
2223 2 : nsContentUtils::GetSecurityManager();
2224 2 : if (securityManager) {
2225 4 : nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2226 :
2227 2 : if (!loadContext && aLoadGroup) {
2228 4 : nsCOMPtr<nsIInterfaceRequestor> cbs;
2229 2 : aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2230 2 : loadContext = do_GetInterface(cbs);
2231 : }
2232 :
2233 2 : MOZ_ASSERT(loadContext,
2234 : "must have a load context or pass in an explicit principal");
2235 :
2236 4 : nsCOMPtr<nsIPrincipal> principal;
2237 : nsresult rv = securityManager->
2238 2 : GetLoadContextCodebasePrincipal(mDocumentURI, loadContext,
2239 4 : getter_AddRefs(principal));
2240 2 : if (NS_SUCCEEDED(rv)) {
2241 2 : SetPrincipal(principal);
2242 : }
2243 : }
2244 : }
2245 :
2246 : // Refresh the principal on the compartment.
2247 29 : if (nsPIDOMWindowInner* win = GetInnerWindow()) {
2248 0 : nsGlobalWindow::Cast(win)->RefreshCompartmentPrincipal();
2249 : }
2250 29 : }
2251 :
2252 : already_AddRefed<nsIPrincipal>
2253 26 : nsDocument::MaybeDowngradePrincipal(nsIPrincipal* aPrincipal)
2254 : {
2255 26 : if (!aPrincipal) {
2256 0 : return nullptr;
2257 : }
2258 :
2259 26 : if (!sChromeInContentPrefCached) {
2260 2 : sChromeInContentPrefCached = true;
2261 : Preferences::AddBoolVarCache(&sChromeInContentAllowed,
2262 2 : kChromeInContentPref, false);
2263 : }
2264 52 : if (!sChromeInContentAllowed &&
2265 26 : nsContentUtils::IsSystemPrincipal(aPrincipal)) {
2266 : // We basically want the parent document here, but because this is very
2267 : // early in the load, GetParentDocument() returns null, so we use the
2268 : // docshell hierarchy to get this information instead.
2269 3 : if (mDocumentContainer) {
2270 4 : nsCOMPtr<nsIDocShellTreeItem> parentDocShellItem;
2271 2 : mDocumentContainer->GetParent(getter_AddRefs(parentDocShellItem));
2272 4 : nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentDocShellItem);
2273 2 : if (parentDocShell) {
2274 2 : nsCOMPtr<nsIDocument> parentDoc;
2275 1 : parentDoc = parentDocShell->GetDocument();
2276 2 : if (!parentDoc ||
2277 1 : !nsContentUtils::IsSystemPrincipal(parentDoc->NodePrincipal())) {
2278 : nsCOMPtr<nsIPrincipal> nullPrincipal =
2279 0 : do_CreateInstance("@mozilla.org/nullprincipal;1");
2280 0 : return nullPrincipal.forget();
2281 : }
2282 : }
2283 : }
2284 : }
2285 52 : nsCOMPtr<nsIPrincipal> principal(aPrincipal);
2286 26 : return principal.forget();
2287 : }
2288 :
2289 : void
2290 0 : nsDocument::RemoveDocStyleSheetsFromStyleSets()
2291 : {
2292 : // The stylesheets should forget us
2293 0 : for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2294 0 : sheet->ClearAssociatedDocument();
2295 :
2296 0 : if (sheet->IsApplicable()) {
2297 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
2298 0 : if (shell) {
2299 0 : shell->StyleSet()->RemoveDocStyleSheet(sheet);
2300 : }
2301 : }
2302 : // XXX Tell observers?
2303 : }
2304 0 : }
2305 :
2306 : void
2307 0 : nsDocument::RemoveStyleSheetsFromStyleSets(
2308 : const nsTArray<RefPtr<StyleSheet>>& aSheets,
2309 : SheetType aType)
2310 : {
2311 : // The stylesheets should forget us
2312 0 : for (StyleSheet* sheet : Reversed(aSheets)) {
2313 0 : sheet->ClearAssociatedDocument();
2314 :
2315 0 : if (sheet->IsApplicable()) {
2316 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
2317 0 : if (shell) {
2318 0 : shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2319 : }
2320 : }
2321 : // XXX Tell observers?
2322 : }
2323 0 : }
2324 :
2325 : void
2326 30 : nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
2327 : {
2328 30 : MOZ_ASSERT(aURI);
2329 :
2330 60 : mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
2331 30 : if (mStyleSetFilled) {
2332 : // Skip removing style sheets from the style set if we know we haven't
2333 : // filled the style set. (This allows us to avoid calling
2334 : // GetStyleBackendType() too early.)
2335 0 : RemoveDocStyleSheetsFromStyleSets();
2336 0 : RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, SheetType::Agent);
2337 0 : RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent);
2338 0 : RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User);
2339 0 : RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc);
2340 :
2341 0 : if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2342 : RemoveStyleSheetsFromStyleSets(
2343 0 : *sheetService->AuthorStyleSheets(GetStyleBackendType()), SheetType::Doc);
2344 : }
2345 :
2346 0 : mStyleSetFilled = false;
2347 : }
2348 :
2349 : // Release all the sheets
2350 30 : mStyleSheets.Clear();
2351 30 : mOnDemandBuiltInUASheets.Clear();
2352 120 : for (auto& sheets : mAdditionalSheets) {
2353 90 : sheets.Clear();
2354 : }
2355 :
2356 : // NOTE: We don't release the catalog sheets. It doesn't really matter
2357 : // now, but it could in the future -- in which case not releasing them
2358 : // is probably the right thing to do.
2359 :
2360 : // Now reset our inline style and attribute sheets.
2361 30 : if (mAttrStyleSheet) {
2362 0 : mAttrStyleSheet->Reset();
2363 0 : mAttrStyleSheet->SetOwningDocument(this);
2364 : } else {
2365 30 : mAttrStyleSheet = new nsHTMLStyleSheet(this);
2366 : }
2367 :
2368 30 : if (!mStyleAttrStyleSheet) {
2369 30 : mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2370 : }
2371 :
2372 : // Now set up our style sets
2373 60 : nsCOMPtr<nsIPresShell> shell = GetShell();
2374 30 : if (shell) {
2375 0 : FillStyleSet(shell->StyleSet());
2376 : }
2377 30 : }
2378 :
2379 : static void
2380 84 : AppendSheetsToStyleSet(StyleSetHandle aStyleSet,
2381 : const nsTArray<RefPtr<StyleSheet>>& aSheets,
2382 : SheetType aType)
2383 : {
2384 84 : for (StyleSheet* sheet : Reversed(aSheets)) {
2385 0 : aStyleSet->AppendStyleSheet(aType, sheet);
2386 : }
2387 84 : }
2388 :
2389 :
2390 : void
2391 28 : nsDocument::FillStyleSet(StyleSetHandle aStyleSet)
2392 : {
2393 28 : NS_PRECONDITION(aStyleSet, "Must have a style set");
2394 28 : NS_PRECONDITION(aStyleSet->SheetCount(SheetType::Doc) == 0,
2395 : "Style set already has document sheets?");
2396 :
2397 28 : MOZ_ASSERT(!mStyleSetFilled);
2398 :
2399 28 : for (StyleSheet* sheet : Reversed(mStyleSheets)) {
2400 0 : if (sheet->IsApplicable()) {
2401 0 : aStyleSet->AddDocStyleSheet(sheet, this);
2402 : }
2403 : }
2404 :
2405 28 : if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
2406 : nsTArray<RefPtr<StyleSheet>>& sheets =
2407 28 : *sheetService->AuthorStyleSheets(aStyleSet->BackendType());
2408 28 : for (StyleSheet* sheet : sheets) {
2409 0 : aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
2410 : }
2411 : }
2412 :
2413 : // Iterate backwards to maintain order
2414 28 : for (StyleSheet* sheet : Reversed(mOnDemandBuiltInUASheets)) {
2415 0 : if (sheet->IsApplicable()) {
2416 0 : aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
2417 : }
2418 : }
2419 :
2420 56 : AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2421 28 : SheetType::Agent);
2422 56 : AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2423 28 : SheetType::User);
2424 56 : AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2425 28 : SheetType::Doc);
2426 :
2427 28 : mStyleSetFilled = true;
2428 28 : }
2429 :
2430 : static void
2431 3 : WarnIfSandboxIneffective(nsIDocShell* aDocShell,
2432 : uint32_t aSandboxFlags,
2433 : nsIChannel* aChannel)
2434 : {
2435 : // If the document is sandboxed (via the HTML5 iframe sandbox
2436 : // attribute) and both the allow-scripts and allow-same-origin
2437 : // keywords are supplied, the sandboxed document can call into its
2438 : // parent document and remove its sandboxing entirely - we print a
2439 : // warning to the web console in this case.
2440 3 : if (aSandboxFlags & SANDBOXED_NAVIGATION &&
2441 0 : !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
2442 0 : !(aSandboxFlags & SANDBOXED_ORIGIN)) {
2443 0 : nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2444 0 : aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2445 0 : nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentAsItem);
2446 0 : if (!parentDocShell) {
2447 0 : return;
2448 : }
2449 :
2450 : // Don't warn if our parent is not the top-level document.
2451 0 : nsCOMPtr<nsIDocShellTreeItem> grandParentAsItem;
2452 0 : parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem));
2453 0 : if (grandParentAsItem) {
2454 0 : return;
2455 : }
2456 :
2457 0 : nsCOMPtr<nsIChannel> parentChannel;
2458 0 : parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
2459 0 : if (!parentChannel) {
2460 0 : return;
2461 : }
2462 0 : nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
2463 0 : if (NS_FAILED(rv)) {
2464 0 : return;
2465 : }
2466 :
2467 0 : nsCOMPtr<nsIDocument> parentDocument = parentDocShell->GetDocument();
2468 0 : nsCOMPtr<nsIURI> iframeUri;
2469 0 : parentChannel->GetURI(getter_AddRefs(iframeUri));
2470 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2471 0 : NS_LITERAL_CSTRING("Iframe Sandbox"),
2472 : parentDocument,
2473 : nsContentUtils::eSECURITY_PROPERTIES,
2474 : "BothAllowScriptsAndSameOriginPresent",
2475 0 : nullptr, 0, iframeUri);
2476 : }
2477 : }
2478 :
2479 : nsresult
2480 25 : nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2481 : nsILoadGroup* aLoadGroup,
2482 : nsISupports* aContainer,
2483 : nsIStreamListener **aDocListener,
2484 : bool aReset, nsIContentSink* aSink)
2485 : {
2486 25 : if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
2487 0 : nsCOMPtr<nsIURI> uri;
2488 0 : aChannel->GetURI(getter_AddRefs(uri));
2489 0 : MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2490 : ("DOCUMENT %p StartDocumentLoad %s",
2491 : this, uri ? uri->GetSpecOrDefault().get() : ""));
2492 : }
2493 :
2494 25 : MOZ_ASSERT(NodePrincipal()->GetAppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2495 : "Document should never have UNKNOWN_APP_ID");
2496 :
2497 25 : MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2498 : "Bad readyState");
2499 25 : SetReadyStateInternal(READYSTATE_LOADING);
2500 :
2501 25 : if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2502 0 : mLoadedAsData = true;
2503 : // We need to disable script & style loading in this case.
2504 : // We leave them disabled even in EndLoad(), and let anyone
2505 : // who puts the document on display to worry about enabling.
2506 :
2507 : // Do not load/process scripts when loading as data
2508 0 : ScriptLoader()->SetEnabled(false);
2509 :
2510 : // styles
2511 0 : CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
2512 25 : } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2513 : // Allow CSS, but not scripts
2514 21 : ScriptLoader()->SetEnabled(false);
2515 : }
2516 :
2517 25 : mMayStartLayout = false;
2518 :
2519 25 : if (aReset) {
2520 25 : Reset(aChannel, aLoadGroup);
2521 : }
2522 :
2523 50 : nsAutoCString contentType;
2524 50 : nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2525 75 : if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2526 75 : NS_LITERAL_STRING("contentType"), contentType))) ||
2527 25 : NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2528 : // XXX this is only necessary for viewsource:
2529 25 : nsACString::const_iterator start, end, semicolon;
2530 25 : contentType.BeginReading(start);
2531 25 : contentType.EndReading(end);
2532 25 : semicolon = start;
2533 25 : FindCharInReadable(';', semicolon, end);
2534 25 : SetContentTypeInternal(Substring(start, semicolon));
2535 : }
2536 :
2537 25 : RetrieveRelevantHeaders(aChannel);
2538 :
2539 25 : mChannel = aChannel;
2540 50 : nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2541 25 : if (inStrmChan) {
2542 : bool isSrcdocChannel;
2543 1 : inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2544 1 : if (isSrcdocChannel) {
2545 0 : mIsSrcdocDocument = true;
2546 : }
2547 : }
2548 :
2549 : // If this document is being loaded by a docshell, copy its sandbox flags
2550 : // to the document, and store the fullscreen enabled flag. These are
2551 : // immutable after being set here.
2552 50 : nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2553 :
2554 : // If this is an error page, don't inherit sandbox flags from docshell
2555 50 : nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2556 25 : if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
2557 3 : nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2558 3 : NS_ENSURE_SUCCESS(rv, rv);
2559 3 : WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
2560 : }
2561 :
2562 : // The CSP directive upgrade-insecure-requests not only applies to the
2563 : // toplevel document, but also to nested documents. Let's propagate that
2564 : // flag from the parent to the nested document.
2565 50 : nsCOMPtr<nsIDocShellTreeItem> treeItem = this->GetDocShell();
2566 25 : if (treeItem) {
2567 6 : nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
2568 3 : treeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
2569 3 : if (sameTypeParent) {
2570 1 : nsIDocument* doc = sameTypeParent->GetDocument();
2571 1 : mBlockAllMixedContent = doc->GetBlockAllMixedContent(false);
2572 : // if the parent document makes use of block-all-mixed-content
2573 : // then subdocument preloads should always be blocked.
2574 1 : mBlockAllMixedContentPreloads =
2575 1 : mBlockAllMixedContent || doc->GetBlockAllMixedContent(true);
2576 :
2577 1 : mUpgradeInsecureRequests = doc->GetUpgradeInsecureRequests(false);
2578 : // if the parent document makes use of upgrade-insecure-requests
2579 : // then subdocument preloads should always be upgraded.
2580 1 : mUpgradeInsecurePreloads =
2581 1 : mUpgradeInsecureRequests || doc->GetUpgradeInsecureRequests(true);
2582 : }
2583 : }
2584 :
2585 : // If this is not a data document, set CSP.
2586 25 : if (!mLoadedAsData) {
2587 25 : nsresult rv = InitCSP(aChannel);
2588 25 : NS_ENSURE_SUCCESS(rv, rv);
2589 : }
2590 :
2591 : // XFO needs to be checked after CSP because it is ignored if
2592 : // the CSP defines frame-ancestors.
2593 25 : if (!FramingChecker::CheckFrameOptions(aChannel, docShell, NodePrincipal())) {
2594 0 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2595 : ("XFO doesn't like frame's ancestry, not loading."));
2596 : // stop! ERROR page!
2597 0 : aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2598 : }
2599 :
2600 25 : return NS_OK;
2601 : }
2602 :
2603 : void
2604 1 : nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
2605 : {
2606 1 : for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2607 0 : nsAutoString messageTag;
2608 0 : aMessages[i]->GetTag(messageTag);
2609 :
2610 0 : nsAutoString category;
2611 0 : aMessages[i]->GetCategory(category);
2612 :
2613 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2614 0 : NS_ConvertUTF16toUTF8(category),
2615 : this, nsContentUtils::eSECURITY_PROPERTIES,
2616 0 : NS_ConvertUTF16toUTF8(messageTag).get());
2617 : }
2618 1 : }
2619 :
2620 : void
2621 0 : nsDocument::ApplySettingsFromCSP(bool aSpeculative)
2622 : {
2623 0 : nsresult rv = NS_OK;
2624 0 : if (!aSpeculative) {
2625 : // 1) apply settings from regular CSP
2626 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
2627 0 : rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
2628 0 : NS_ENSURE_SUCCESS_VOID(rv);
2629 0 : if (csp) {
2630 : // Set up any Referrer Policy specified by CSP
2631 0 : bool hasReferrerPolicy = false;
2632 0 : uint32_t referrerPolicy = mozilla::net::RP_Unset;
2633 0 : rv = csp->GetReferrerPolicy(&referrerPolicy, &hasReferrerPolicy);
2634 0 : NS_ENSURE_SUCCESS_VOID(rv);
2635 0 : if (hasReferrerPolicy) {
2636 0 : mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
2637 0 : mReferrerPolicySet = true;
2638 : }
2639 :
2640 : // Set up 'block-all-mixed-content' if not already inherited
2641 : // from the parent context or set by any other CSP.
2642 0 : if (!mBlockAllMixedContent) {
2643 0 : rv = csp->GetBlockAllMixedContent(&mBlockAllMixedContent);
2644 0 : NS_ENSURE_SUCCESS_VOID(rv);
2645 : }
2646 0 : if (!mBlockAllMixedContentPreloads) {
2647 0 : mBlockAllMixedContentPreloads = mBlockAllMixedContent;
2648 : }
2649 :
2650 : // Set up 'upgrade-insecure-requests' if not already inherited
2651 : // from the parent context or set by any other CSP.
2652 0 : if (!mUpgradeInsecureRequests) {
2653 0 : rv = csp->GetUpgradeInsecureRequests(&mUpgradeInsecureRequests);
2654 0 : NS_ENSURE_SUCCESS_VOID(rv);
2655 : }
2656 0 : if (!mUpgradeInsecurePreloads) {
2657 0 : mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
2658 : }
2659 : }
2660 0 : return;
2661 : }
2662 :
2663 : // 2) apply settings from speculative csp
2664 0 : nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
2665 0 : rv = NodePrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
2666 0 : NS_ENSURE_SUCCESS_VOID(rv);
2667 0 : if (preloadCsp) {
2668 0 : if (!mBlockAllMixedContentPreloads) {
2669 0 : rv = preloadCsp->GetBlockAllMixedContent(&mBlockAllMixedContentPreloads);
2670 0 : NS_ENSURE_SUCCESS_VOID(rv);
2671 : }
2672 0 : if (!mUpgradeInsecurePreloads) {
2673 0 : rv = preloadCsp->GetUpgradeInsecureRequests(&mUpgradeInsecurePreloads);
2674 0 : NS_ENSURE_SUCCESS_VOID(rv);
2675 : }
2676 : }
2677 : }
2678 :
2679 : nsresult
2680 25 : nsDocument::InitCSP(nsIChannel* aChannel)
2681 : {
2682 25 : MOZ_ASSERT(!mScriptGlobalObject,
2683 : "CSP must be initialized before mScriptGlobalObject is set!");
2684 25 : if (!CSPService::sCSPEnabled) {
2685 0 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2686 : ("CSP is disabled, skipping CSP init for document %p", this));
2687 0 : return NS_OK;
2688 : }
2689 :
2690 50 : nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2691 :
2692 50 : nsCOMPtr<nsIHttpChannel> httpChannel;
2693 25 : nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
2694 25 : if (NS_WARN_IF(NS_FAILED(rv))) {
2695 0 : return rv;
2696 : }
2697 :
2698 25 : if (httpChannel) {
2699 2 : Unused << httpChannel->GetResponseHeader(
2700 2 : NS_LITERAL_CSTRING("content-security-policy"),
2701 2 : tCspHeaderValue);
2702 :
2703 2 : Unused << httpChannel->GetResponseHeader(
2704 2 : NS_LITERAL_CSTRING("content-security-policy-report-only"),
2705 2 : tCspROHeaderValue);
2706 : }
2707 50 : NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2708 50 : NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2709 :
2710 : // Check if this is a document from a WebExtension.
2711 50 : nsString addonId;
2712 50 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
2713 25 : principal->GetAddonId(addonId);
2714 25 : bool applyAddonCSP = !addonId.IsEmpty();
2715 :
2716 : // Check if this is a signed content to apply default CSP.
2717 25 : bool applySignedContentCSP = false;
2718 50 : nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
2719 25 : if (loadInfo && loadInfo->GetVerifySignedContent()) {
2720 0 : applySignedContentCSP = true;
2721 : }
2722 :
2723 : // If there's no CSP to apply, go ahead and return early
2724 75 : if (!applyAddonCSP &&
2725 50 : !applySignedContentCSP &&
2726 75 : cspHeaderValue.IsEmpty() &&
2727 25 : cspROHeaderValue.IsEmpty()) {
2728 25 : if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
2729 0 : nsCOMPtr<nsIURI> chanURI;
2730 0 : aChannel->GetURI(getter_AddRefs(chanURI));
2731 0 : nsAutoCString aspec;
2732 0 : chanURI->GetAsciiSpec(aspec);
2733 0 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2734 : ("no CSP for document, %s",
2735 : aspec.get()));
2736 : }
2737 :
2738 25 : return NS_OK;
2739 : }
2740 :
2741 0 : MOZ_LOG(gCspPRLog, LogLevel::Debug, ("Document is an add-on or CSP header specified %p", this));
2742 :
2743 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
2744 0 : rv = principal->EnsureCSP(this, getter_AddRefs(csp));
2745 0 : NS_ENSURE_SUCCESS(rv, rv);
2746 :
2747 : // ----- if the doc is an addon, apply its CSP.
2748 0 : if (applyAddonCSP) {
2749 0 : nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
2750 :
2751 0 : nsAutoString addonCSP;
2752 0 : rv = aps->GetBaseCSP(addonCSP);
2753 0 : if (NS_SUCCEEDED(rv)) {
2754 0 : csp->AppendPolicy(addonCSP, false, false);
2755 : }
2756 :
2757 0 : rv = aps->GetAddonCSP(addonId, addonCSP);
2758 0 : if (NS_SUCCEEDED(rv)) {
2759 0 : csp->AppendPolicy(addonCSP, false, false);
2760 : }
2761 : }
2762 :
2763 : // ----- if the doc is a signed content, apply the default CSP.
2764 : // Note that when the content signing becomes a standard, we might have
2765 : // to restrict this enforcement to "remote content" only.
2766 0 : if (applySignedContentCSP) {
2767 : nsAdoptingString signedContentCSP =
2768 0 : Preferences::GetString("security.signed_content.CSP.default");
2769 0 : csp->AppendPolicy(signedContentCSP, false, false);
2770 : }
2771 :
2772 : // ----- if there's a full-strength CSP header, apply it.
2773 0 : if (!cspHeaderValue.IsEmpty()) {
2774 0 : rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
2775 0 : NS_ENSURE_SUCCESS(rv, rv);
2776 : }
2777 :
2778 : // ----- if there's a report-only CSP header, apply it.
2779 0 : if (!cspROHeaderValue.IsEmpty()) {
2780 0 : rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
2781 0 : NS_ENSURE_SUCCESS(rv, rv);
2782 : }
2783 :
2784 : // ----- Enforce sandbox policy if supplied in CSP header
2785 : // The document may already have some sandbox flags set (e.g. if the document
2786 : // is an iframe with the sandbox attribute set). If we have a CSP sandbox
2787 : // directive, intersect the CSP sandbox flags with the existing flags. This
2788 : // corresponds to the _least_ permissive policy.
2789 0 : uint32_t cspSandboxFlags = SANDBOXED_NONE;
2790 0 : rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
2791 0 : NS_ENSURE_SUCCESS(rv, rv);
2792 :
2793 0 : mSandboxFlags |= cspSandboxFlags;
2794 :
2795 : // Probably the iframe sandbox attribute already caused the creation of a
2796 : // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
2797 : // and no one has been created yet.
2798 : bool needNewNullPrincipal =
2799 0 : (cspSandboxFlags & SANDBOXED_ORIGIN) && !(mSandboxFlags & SANDBOXED_ORIGIN);
2800 0 : if (needNewNullPrincipal) {
2801 0 : principal = NullPrincipal::CreateWithInheritedAttributes(principal);
2802 0 : principal->SetCsp(csp);
2803 0 : SetPrincipal(principal);
2804 : }
2805 :
2806 : // ----- Enforce frame-ancestor policy on any applied policies
2807 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2808 0 : if (docShell) {
2809 0 : bool safeAncestry = false;
2810 :
2811 : // PermitsAncestry sends violation reports when necessary
2812 0 : rv = csp->PermitsAncestry(docShell, &safeAncestry);
2813 :
2814 0 : if (NS_FAILED(rv) || !safeAncestry) {
2815 0 : MOZ_LOG(gCspPRLog, LogLevel::Debug,
2816 : ("CSP doesn't like frame's ancestry, not loading."));
2817 : // stop! ERROR page!
2818 0 : aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2819 : }
2820 : }
2821 0 : ApplySettingsFromCSP(false);
2822 0 : return NS_OK;
2823 : }
2824 :
2825 : already_AddRefed<nsIParser>
2826 0 : nsDocument::CreatorParserOrNull()
2827 : {
2828 0 : nsCOMPtr<nsIParser> parser = mParser;
2829 0 : return parser.forget();
2830 : }
2831 :
2832 : void
2833 4 : nsDocument::StopDocumentLoad()
2834 : {
2835 4 : if (mParser) {
2836 0 : mParserAborted = true;
2837 0 : mParser->Terminate();
2838 : }
2839 4 : }
2840 :
2841 : void
2842 54 : nsDocument::SetDocumentURI(nsIURI* aURI)
2843 : {
2844 108 : nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
2845 54 : mDocumentURI = NS_TryToMakeImmutable(aURI);
2846 54 : nsIURI* newBase = GetDocBaseURI();
2847 :
2848 54 : bool equalBases = false;
2849 : // Changing just the ref of a URI does not change how relative URIs would
2850 : // resolve wrt to it, so we can treat the bases as equal as long as they're
2851 : // equal ignoring the ref.
2852 54 : if (oldBase && newBase) {
2853 0 : oldBase->EqualsExceptRef(newBase, &equalBases);
2854 : }
2855 : else {
2856 54 : equalBases = !oldBase && !newBase;
2857 : }
2858 :
2859 : // If this is the first time we're setting the document's URI, set the
2860 : // document's original URI.
2861 54 : if (!mOriginalURI)
2862 54 : mOriginalURI = mDocumentURI;
2863 :
2864 : // If changing the document's URI changed the base URI of the document, we
2865 : // need to refresh the hrefs of all the links on the page.
2866 54 : if (!equalBases) {
2867 54 : RefreshLinkHrefs();
2868 : }
2869 54 : }
2870 :
2871 : void
2872 0 : nsDocument::SetChromeXHRDocURI(nsIURI* aURI)
2873 : {
2874 0 : mChromeXHRDocURI = aURI;
2875 0 : }
2876 :
2877 : void
2878 0 : nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI)
2879 : {
2880 0 : mChromeXHRDocBaseURI = aURI;
2881 0 : }
2882 :
2883 : NS_IMETHODIMP
2884 0 : nsDocument::GetLastModified(nsAString& aLastModified)
2885 : {
2886 0 : nsIDocument::GetLastModified(aLastModified);
2887 0 : return NS_OK;
2888 : }
2889 :
2890 : static void
2891 25 : GetFormattedTimeString(PRTime aTime, nsAString& aFormattedTimeString)
2892 : {
2893 : PRExplodedTime prtime;
2894 25 : PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime);
2895 : // "MM/DD/YYYY hh:mm:ss"
2896 : char formatedTime[24];
2897 75 : if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
2898 50 : prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
2899 : prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
2900 25 : CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
2901 : } else {
2902 : // If we for whatever reason failed to find the last modified time
2903 : // (or even the current time), fall back to what NS4.x returned.
2904 0 : aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
2905 : }
2906 25 : }
2907 :
2908 : void
2909 0 : nsIDocument::GetLastModified(nsAString& aLastModified) const
2910 : {
2911 0 : if (!mLastModified.IsEmpty()) {
2912 0 : aLastModified.Assign(mLastModified);
2913 : } else {
2914 0 : GetFormattedTimeString(PR_Now(), aLastModified);
2915 : }
2916 0 : }
2917 :
2918 : void
2919 0 : nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
2920 : {
2921 0 : MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
2922 : "Only put elements that need to be exposed as document['name'] in "
2923 : "the named table.");
2924 :
2925 0 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
2926 :
2927 : // Null for out-of-memory
2928 0 : if (entry) {
2929 0 : if (!entry->HasNameElement() &&
2930 0 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2931 0 : ++mExpandoAndGeneration.generation;
2932 : }
2933 0 : entry->AddNameElement(this, aElement);
2934 : }
2935 0 : }
2936 :
2937 : void
2938 0 : nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
2939 : {
2940 : // Speed up document teardown
2941 0 : if (mIdentifierMap.Count() == 0)
2942 0 : return;
2943 :
2944 0 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
2945 0 : if (!entry) // Could be false if the element was anonymous, hence never added
2946 0 : return;
2947 :
2948 0 : entry->RemoveNameElement(aElement);
2949 0 : if (!entry->HasNameElement() &&
2950 0 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2951 0 : ++mExpandoAndGeneration.generation;
2952 : }
2953 : }
2954 :
2955 : void
2956 2130 : nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
2957 : {
2958 2130 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
2959 :
2960 2130 : if (entry) { /* True except on OOM */
2961 4260 : if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
2962 2130 : !entry->HasNameElement() &&
2963 0 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2964 0 : ++mExpandoAndGeneration.generation;
2965 : }
2966 2130 : entry->AddIdElement(aElement);
2967 : }
2968 2130 : }
2969 :
2970 : void
2971 53 : nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
2972 : {
2973 53 : NS_ASSERTION(aId, "huhwhatnow?");
2974 :
2975 : // Speed up document teardown
2976 53 : if (mIdentifierMap.Count() == 0) {
2977 0 : return;
2978 : }
2979 :
2980 53 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
2981 53 : if (!entry) // Can be null for XML elements with changing ids.
2982 25 : return;
2983 :
2984 28 : entry->RemoveIdElement(aElement);
2985 56 : if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
2986 28 : !entry->HasNameElement() &&
2987 0 : !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2988 0 : ++mExpandoAndGeneration.generation;
2989 : }
2990 28 : if (entry->IsEmpty()) {
2991 28 : mIdentifierMap.RemoveEntry(entry);
2992 : }
2993 : }
2994 :
2995 : nsIPrincipal*
2996 47 : nsDocument::GetPrincipal()
2997 : {
2998 47 : return NodePrincipal();
2999 : }
3000 :
3001 : extern bool sDisablePrefetchHTTPSPref;
3002 :
3003 : void
3004 84 : nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
3005 : {
3006 84 : if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3007 110 : nsCOMPtr<nsIURI> uri;
3008 55 : aNewPrincipal->GetURI(getter_AddRefs(uri));
3009 : bool isHTTPS;
3010 55 : if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
3011 : isHTTPS) {
3012 30 : mAllowDNSPrefetch = false;
3013 : }
3014 : }
3015 84 : mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3016 :
3017 : #ifdef DEBUG
3018 : // Validate that the docgroup is set correctly by calling its getter and
3019 : // triggering its sanity check.
3020 : //
3021 : // If we're setting the principal to null, we don't want to perform the check,
3022 : // as the document is entering an intermediate state where it does not have a
3023 : // principal. It will be given another real principal shortly which we will
3024 : // check. It's not unsafe to have a document which has a null principal in the
3025 : // same docgroup as another document, so this should not be a problem.
3026 84 : if (aNewPrincipal) {
3027 55 : GetDocGroup();
3028 : }
3029 : #endif
3030 84 : }
3031 :
3032 : mozilla::dom::DocGroup*
3033 848 : nsIDocument::GetDocGroup() const
3034 : {
3035 : #ifdef DEBUG
3036 : // Sanity check that we have an up-to-date and accurate docgroup
3037 848 : if (mDocGroup) {
3038 1586 : nsAutoCString docGroupKey;
3039 :
3040 : // GetKey() can fail, e.g. after the TLD service has shut down.
3041 793 : nsresult rv = mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
3042 793 : if (NS_SUCCEEDED(rv)) {
3043 793 : MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
3044 : }
3045 : // XXX: Check that the TabGroup is correct as well!
3046 : }
3047 : #endif
3048 :
3049 848 : return mDocGroup;
3050 : }
3051 :
3052 : nsresult
3053 228 : nsIDocument::Dispatch(const char* aName,
3054 : TaskCategory aCategory,
3055 : already_AddRefed<nsIRunnable>&& aRunnable)
3056 : {
3057 : // Note that this method may be called off the main thread.
3058 228 : if (mDocGroup) {
3059 98 : return mDocGroup->Dispatch(aName, aCategory, Move(aRunnable));
3060 : }
3061 130 : return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
3062 : }
3063 :
3064 : nsISerialEventTarget*
3065 43 : nsIDocument::EventTargetFor(TaskCategory aCategory) const
3066 : {
3067 43 : if (mDocGroup) {
3068 22 : return mDocGroup->EventTargetFor(aCategory);
3069 : }
3070 21 : return DispatcherTrait::EventTargetFor(aCategory);
3071 : }
3072 :
3073 : AbstractThread*
3074 1 : nsIDocument::AbstractMainThreadFor(mozilla::TaskCategory aCategory)
3075 : {
3076 1 : MOZ_ASSERT(NS_IsMainThread());
3077 1 : if (mDocGroup) {
3078 1 : return mDocGroup->AbstractMainThreadFor(aCategory);
3079 : }
3080 0 : return DispatcherTrait::AbstractMainThreadFor(aCategory);
3081 : }
3082 :
3083 : void
3084 8 : nsIDocument::NoteScriptTrackingStatus(const nsACString& aURL, bool aIsTracking)
3085 : {
3086 8 : if (aIsTracking) {
3087 0 : mTrackingScripts.PutEntry(aURL);
3088 : } else {
3089 8 : MOZ_ASSERT(!mTrackingScripts.Contains(aURL));
3090 : }
3091 8 : }
3092 :
3093 : bool
3094 13 : nsIDocument::IsScriptTracking(const nsACString& aURL) const
3095 : {
3096 13 : return mTrackingScripts.Contains(aURL);
3097 : }
3098 :
3099 : bool
3100 0 : nsIDocument::PrerenderHref(nsIURI* aHref)
3101 : {
3102 0 : MOZ_ASSERT(aHref);
3103 :
3104 : static bool sPrerenderEnabled = false;
3105 : static bool sPrerenderPrefCached = false;
3106 0 : if (!sPrerenderPrefCached) {
3107 0 : sPrerenderPrefCached = true;
3108 : Preferences::AddBoolVarCache(&sPrerenderEnabled,
3109 : "dom.linkPrerender.enabled",
3110 0 : false);
3111 : }
3112 :
3113 : // Check if prerender is enabled
3114 0 : if (!sPrerenderEnabled) {
3115 0 : return false;
3116 : }
3117 :
3118 0 : nsCOMPtr<nsIURI> referrer = GetDocumentURI();
3119 0 : bool urisMatch = false;
3120 0 : aHref->EqualsExceptRef(referrer, &urisMatch);
3121 0 : if (urisMatch) {
3122 : // Prerender current document isn't quite meaningful, and we may not be able
3123 : // to load it out of process.
3124 0 : return false;
3125 : }
3126 :
3127 0 : nsCOMPtr<nsIDocShell> docShell = GetDocShell();
3128 0 : nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(docShell);
3129 0 : NS_ENSURE_TRUE(webNav, false);
3130 :
3131 0 : bool canGoForward = false;
3132 0 : nsresult rv = webNav->GetCanGoForward(&canGoForward);
3133 0 : if (NS_FAILED(rv) || canGoForward) {
3134 : // Skip prerender on history navigation as we don't support it yet.
3135 : // Remove this check once bug 1323650 is implemented.
3136 0 : return false;
3137 : }
3138 :
3139 : // Check if the document is in prerender state. We don't prerender in a
3140 : // prerendered document.
3141 0 : if (docShell->GetIsPrerendered()) {
3142 0 : return false;
3143 : }
3144 :
3145 : // We currently do not support prerendering in documents loaded within the
3146 : // chrome process.
3147 0 : if (!XRE_IsContentProcess()) {
3148 0 : return false;
3149 : }
3150 :
3151 : // Adopting an prerendered document is similar to performing a load within a
3152 : // different docshell, as the prerendering must have occurred in a different
3153 : // docshell.
3154 0 : if (!docShell->GetIsOnlyToplevelInTabGroup()) {
3155 0 : return false;
3156 : }
3157 :
3158 0 : TabChild* tabChild = TabChild::GetFrom(docShell);
3159 0 : NS_ENSURE_TRUE(tabChild, false);
3160 :
3161 0 : nsCOMPtr<nsIWebBrowserChrome3> wbc3;
3162 0 : tabChild->GetWebBrowserChrome(getter_AddRefs(wbc3));
3163 0 : NS_ENSURE_TRUE(wbc3, false);
3164 :
3165 0 : rv = wbc3->StartPrerenderingDocument(aHref, referrer, NodePrincipal());
3166 0 : NS_ENSURE_SUCCESS(rv, false);
3167 :
3168 0 : return true;
3169 : }
3170 :
3171 : NS_IMETHODIMP
3172 3 : nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
3173 : {
3174 3 : NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3175 :
3176 3 : return NS_OK;
3177 : }
3178 :
3179 : NS_IMETHODIMP
3180 0 : nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
3181 : {
3182 0 : mApplicationCache = aApplicationCache;
3183 :
3184 0 : return NS_OK;
3185 : }
3186 :
3187 : NS_IMETHODIMP
3188 9 : nsDocument::GetContentType(nsAString& aContentType)
3189 : {
3190 9 : CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3191 :
3192 9 : return NS_OK;
3193 : }
3194 :
3195 : void
3196 0 : nsDocument::SetContentType(const nsAString& aContentType)
3197 : {
3198 0 : SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3199 0 : }
3200 :
3201 : bool
3202 0 : nsDocument::GetAllowPlugins()
3203 : {
3204 : // First, we ask our docshell if it allows plugins.
3205 0 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3206 :
3207 0 : if (docShell) {
3208 0 : bool allowPlugins = false;
3209 0 : docShell->GetAllowPlugins(&allowPlugins);
3210 0 : if (!allowPlugins) {
3211 0 : return false;
3212 : }
3213 :
3214 : // If the docshell allows plugins, we check whether
3215 : // we are sandboxed and plugins should not be allowed.
3216 0 : if (mSandboxFlags & SANDBOXED_PLUGINS) {
3217 0 : return false;
3218 : }
3219 : }
3220 :
3221 0 : FlashClassification classification = DocumentFlashClassification();
3222 0 : if (classification == FlashClassification::Denied) {
3223 0 : return false;
3224 : }
3225 :
3226 0 : return true;
3227 : }
3228 :
3229 : bool
3230 2 : nsDocument::IsElementAnimateEnabled(JSContext* aCx, JSObject* /*unused*/)
3231 : {
3232 2 : MOZ_ASSERT(NS_IsMainThread());
3233 :
3234 3 : return nsContentUtils::IsSystemCaller(aCx) ||
3235 2 : nsContentUtils::AnimationsAPICoreEnabled() ||
3236 2 : nsContentUtils::AnimationsAPIElementAnimateEnabled();
3237 : }
3238 :
3239 : bool
3240 16 : nsDocument::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/)
3241 : {
3242 16 : MOZ_ASSERT(NS_IsMainThread());
3243 :
3244 25 : return nsContentUtils::IsSystemCaller(aCx) ||
3245 25 : nsContentUtils::AnimationsAPICoreEnabled();
3246 : }
3247 :
3248 : DocumentTimeline*
3249 271 : nsDocument::Timeline()
3250 : {
3251 271 : if (!mDocumentTimeline) {
3252 44 : mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0));
3253 : }
3254 :
3255 271 : return mDocumentTimeline;
3256 : }
3257 :
3258 : void
3259 0 : nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
3260 : {
3261 : // Hold a strong ref for the root element since Element::GetAnimations() calls
3262 : // FlushPendingNotifications() which may destroy the element.
3263 0 : RefPtr<Element> root = GetRootElement();
3264 0 : if (!root) {
3265 0 : return;
3266 : }
3267 0 : AnimationFilter filter;
3268 0 : filter.mSubtree = true;
3269 0 : root->GetAnimations(filter, aAnimations);
3270 : }
3271 :
3272 : SVGSVGElement*
3273 0 : nsIDocument::GetSVGRootElement() const
3274 : {
3275 0 : Element* root = GetRootElement();
3276 0 : if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
3277 0 : return nullptr;
3278 : }
3279 0 : return static_cast<SVGSVGElement*>(root);
3280 : }
3281 :
3282 : /* Return true if the document is in the focused top-level window, and is an
3283 : * ancestor of the focused DOMWindow. */
3284 : NS_IMETHODIMP
3285 8 : nsDocument::HasFocus(bool* aResult)
3286 : {
3287 16 : ErrorResult rv;
3288 8 : *aResult = nsIDocument::HasFocus(rv);
3289 16 : return rv.StealNSResult();
3290 : }
3291 :
3292 : bool
3293 8 : nsIDocument::HasFocus(ErrorResult& rv) const
3294 : {
3295 8 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3296 8 : if (!fm) {
3297 0 : rv.Throw(NS_ERROR_NOT_AVAILABLE);
3298 0 : return false;
3299 : }
3300 :
3301 : // Is there a focused DOMWindow?
3302 16 : nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
3303 8 : fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3304 8 : if (!focusedWindow) {
3305 7 : return false;
3306 : }
3307 :
3308 1 : nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(focusedWindow);
3309 :
3310 : // Are we an ancestor of the focused DOMWindow?
3311 1 : for (nsIDocument* currentDoc = piWindow->GetDoc(); currentDoc;
3312 : currentDoc = currentDoc->GetParentDocument()) {
3313 1 : if (currentDoc == this) {
3314 : // Yes, we are an ancestor
3315 1 : return true;
3316 : }
3317 : }
3318 :
3319 0 : return false;
3320 : }
3321 :
3322 : TimeStamp
3323 0 : nsIDocument::LastFocusTime() const
3324 : {
3325 0 : return mLastFocusTime;
3326 : }
3327 :
3328 : void
3329 3 : nsIDocument::SetLastFocusTime(const TimeStamp& aFocusTime)
3330 : {
3331 3 : MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
3332 3 : MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
3333 : aFocusTime >= mLastFocusTime);
3334 3 : mLastFocusTime = aFocusTime;
3335 3 : }
3336 :
3337 : NS_IMETHODIMP
3338 0 : nsDocument::GetReferrer(nsAString& aReferrer)
3339 : {
3340 0 : nsIDocument::GetReferrer(aReferrer);
3341 0 : return NS_OK;
3342 : }
3343 :
3344 : void
3345 0 : nsIDocument::GetReferrer(nsAString& aReferrer) const
3346 : {
3347 0 : if (mIsSrcdocDocument && mParentDocument)
3348 0 : mParentDocument->GetReferrer(aReferrer);
3349 : else
3350 0 : CopyUTF8toUTF16(mReferrer, aReferrer);
3351 0 : }
3352 :
3353 : nsresult
3354 0 : nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
3355 : {
3356 0 : if (mIsSrcdocDocument) {
3357 0 : nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3358 0 : if (inStrmChan) {
3359 0 : return inStrmChan->GetSrcdocData(aSrcdocData);
3360 : }
3361 : }
3362 0 : aSrcdocData = NullString();
3363 0 : return NS_OK;
3364 : }
3365 :
3366 : NS_IMETHODIMP
3367 0 : nsDocument::GetActiveElement(nsIDOMElement **aElement)
3368 : {
3369 0 : nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement()));
3370 0 : el.forget(aElement);
3371 0 : return NS_OK;
3372 : }
3373 :
3374 : Element*
3375 1 : nsIDocument::GetActiveElement()
3376 : {
3377 : // Get the focused element.
3378 2 : if (nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow()) {
3379 2 : nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
3380 : nsIContent* focusedContent =
3381 1 : nsFocusManager::GetFocusedDescendant(window, false,
3382 2 : getter_AddRefs(focusedWindow));
3383 : // be safe and make sure the element is from this document
3384 1 : if (focusedContent && focusedContent->OwnerDoc() == this) {
3385 0 : if (focusedContent->ChromeOnlyAccess()) {
3386 0 : focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
3387 : }
3388 0 : if (focusedContent) {
3389 0 : return focusedContent->AsElement();
3390 : }
3391 : }
3392 : }
3393 :
3394 : // No focused element anywhere in this document. Try to get the BODY.
3395 2 : RefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
3396 1 : if (htmlDoc) {
3397 : // Because of IE compatibility, return null when html document doesn't have
3398 : // a body.
3399 0 : return htmlDoc->GetBody();
3400 : }
3401 :
3402 : // If we couldn't get a BODY, return the root element.
3403 1 : return GetDocumentElement();
3404 : }
3405 :
3406 : NS_IMETHODIMP
3407 0 : nsDocument::GetCurrentScript(nsIDOMElement **aElement)
3408 : {
3409 0 : nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript()));
3410 0 : el.forget(aElement);
3411 0 : return NS_OK;
3412 : }
3413 :
3414 : Element*
3415 0 : nsIDocument::GetCurrentScript()
3416 : {
3417 0 : nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3418 0 : return el;
3419 : }
3420 :
3421 : NS_IMETHODIMP
3422 0 : nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
3423 : {
3424 0 : Element* el = nsIDocument::ElementFromPoint(aX, aY);
3425 0 : nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
3426 0 : retval.forget(aReturn);
3427 0 : return NS_OK;
3428 : }
3429 :
3430 : Element*
3431 0 : nsIDocument::ElementFromPoint(float aX, float aY)
3432 : {
3433 0 : return ElementFromPointHelper(aX, aY, false, true);
3434 : }
3435 :
3436 : void
3437 0 : nsIDocument::ElementsFromPoint(float aX, float aY,
3438 : nsTArray<RefPtr<Element>>& aElements)
3439 : {
3440 0 : ElementsFromPointHelper(aX, aY, nsIDocument::FLUSH_LAYOUT, aElements);
3441 0 : }
3442 :
3443 : Element*
3444 0 : nsDocument::ElementFromPointHelper(float aX, float aY,
3445 : bool aIgnoreRootScrollFrame,
3446 : bool aFlushLayout)
3447 : {
3448 0 : AutoTArray<RefPtr<Element>, 1> elementArray;
3449 0 : ElementsFromPointHelper(aX, aY,
3450 0 : ((aIgnoreRootScrollFrame ? nsIDocument::IGNORE_ROOT_SCROLL_FRAME : 0) |
3451 0 : (aFlushLayout ? nsIDocument::FLUSH_LAYOUT : 0) |
3452 : nsIDocument::IS_ELEMENT_FROM_POINT),
3453 0 : elementArray);
3454 0 : if (elementArray.IsEmpty()) {
3455 0 : return nullptr;
3456 : }
3457 0 : return elementArray[0];
3458 : }
3459 :
3460 : void
3461 0 : nsDocument::ElementsFromPointHelper(float aX, float aY,
3462 : uint32_t aFlags,
3463 : nsTArray<RefPtr<mozilla::dom::Element>>& aElements)
3464 : {
3465 : // As per the the spec, we return null if either coord is negative
3466 0 : if (!(aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) && (aX < 0 || aY < 0)) {
3467 0 : return;
3468 : }
3469 :
3470 0 : nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
3471 0 : nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
3472 0 : nsPoint pt(x, y);
3473 :
3474 : // Make sure the layout information we get is up-to-date, and
3475 : // ensure we get a root frame (for everything but XUL)
3476 0 : if (aFlags & nsIDocument::FLUSH_LAYOUT) {
3477 0 : FlushPendingNotifications(FlushType::Layout);
3478 : }
3479 :
3480 0 : nsIPresShell *ps = GetShell();
3481 0 : if (!ps) {
3482 0 : return;
3483 : }
3484 0 : nsIFrame *rootFrame = ps->GetRootFrame();
3485 :
3486 : // XUL docs, unlike HTML, have no frame tree until everything's done loading
3487 0 : if (!rootFrame) {
3488 0 : return; // return null to premature XUL callers as a reminder to wait
3489 : }
3490 :
3491 0 : nsTArray<nsIFrame*> outFrames;
3492 : // Emulate what GetFrameAtPoint does, since we want all the frames under our
3493 : // point.
3494 0 : nsLayoutUtils::GetFramesForArea(rootFrame, nsRect(pt, nsSize(1, 1)), outFrames,
3495 : nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3496 0 : ((aFlags & nsIDocument::IGNORE_ROOT_SCROLL_FRAME) ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3497 :
3498 : // Dunno when this would ever happen, as we should at least have a root frame under us?
3499 0 : if (outFrames.IsEmpty()) {
3500 0 : return;
3501 : }
3502 :
3503 : // Used to filter out repeated elements in sequence.
3504 0 : nsIContent* lastAdded = nullptr;
3505 :
3506 0 : for (uint32_t i = 0; i < outFrames.Length(); i++) {
3507 0 : nsIContent* node = GetContentInThisDocument(outFrames[i]);
3508 :
3509 0 : if (!node || !node->IsElement()) {
3510 : // If this helper is called via ElementsFromPoint, we need to make sure
3511 : // our frame is an element. Otherwise return whatever the top frame is
3512 : // even if it isn't the top-painted element.
3513 0 : if (!(aFlags & nsIDocument::IS_ELEMENT_FROM_POINT)) {
3514 0 : continue;
3515 : }
3516 0 : node = node->GetParent();
3517 : }
3518 0 : if (node && node != lastAdded) {
3519 0 : aElements.AppendElement(node->AsElement());
3520 0 : lastAdded = node;
3521 : // If this helper is called via ElementFromPoint, just return the first
3522 : // element we find.
3523 0 : if (aFlags & nsIDocument::IS_ELEMENT_FROM_POINT) {
3524 0 : return;
3525 : }
3526 : }
3527 : }
3528 : }
3529 :
3530 : nsresult
3531 0 : nsDocument::NodesFromRectHelper(float aX, float aY,
3532 : float aTopSize, float aRightSize,
3533 : float aBottomSize, float aLeftSize,
3534 : bool aIgnoreRootScrollFrame,
3535 : bool aFlushLayout,
3536 : nsIDOMNodeList** aReturn)
3537 : {
3538 0 : NS_ENSURE_ARG_POINTER(aReturn);
3539 :
3540 0 : nsSimpleContentList* elements = new nsSimpleContentList(this);
3541 0 : NS_ADDREF(elements);
3542 0 : *aReturn = elements;
3543 :
3544 : // Following the same behavior of elementFromPoint,
3545 : // we don't return anything if either coord is negative
3546 0 : if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
3547 0 : return NS_OK;
3548 :
3549 0 : nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3550 0 : nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3551 0 : nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3552 0 : nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3553 :
3554 0 : nsRect rect(x, y, w, h);
3555 :
3556 : // Make sure the layout information we get is up-to-date, and
3557 : // ensure we get a root frame (for everything but XUL)
3558 0 : if (aFlushLayout) {
3559 0 : FlushPendingNotifications(FlushType::Layout);
3560 : }
3561 :
3562 0 : nsIPresShell *ps = GetShell();
3563 0 : NS_ENSURE_STATE(ps);
3564 0 : nsIFrame *rootFrame = ps->GetRootFrame();
3565 :
3566 : // XUL docs, unlike HTML, have no frame tree until everything's done loading
3567 0 : if (!rootFrame)
3568 0 : return NS_OK; // return nothing to premature XUL callers as a reminder to wait
3569 :
3570 0 : AutoTArray<nsIFrame*,8> outFrames;
3571 0 : nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
3572 : nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3573 0 : (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3574 :
3575 : // Used to filter out repeated elements in sequence.
3576 0 : nsIContent* lastAdded = nullptr;
3577 :
3578 0 : for (uint32_t i = 0; i < outFrames.Length(); i++) {
3579 0 : nsIContent* node = GetContentInThisDocument(outFrames[i]);
3580 :
3581 0 : if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
3582 : // We have a node that isn't an element or a text node,
3583 : // use its parent content instead.
3584 0 : node = node->GetParent();
3585 : }
3586 0 : if (node && node != lastAdded) {
3587 0 : elements->AppendElement(node);
3588 0 : lastAdded = node;
3589 : }
3590 : }
3591 :
3592 0 : return NS_OK;
3593 : }
3594 :
3595 : NS_IMETHODIMP
3596 0 : nsDocument::GetElementsByClassName(const nsAString& aClasses,
3597 : nsIDOMNodeList** aReturn)
3598 : {
3599 0 : *aReturn = nsIDocument::GetElementsByClassName(aClasses).take();
3600 0 : return NS_OK;
3601 : }
3602 :
3603 : already_AddRefed<nsContentList>
3604 0 : nsIDocument::GetElementsByClassName(const nsAString& aClasses)
3605 : {
3606 0 : return nsContentUtils::GetElementsByClassName(this, aClasses);
3607 : }
3608 :
3609 : void
3610 0 : nsIDocument::ReleaseCapture() const
3611 : {
3612 : // only release the capture if the caller can access it. This prevents a
3613 : // page from stopping a scrollbar grab for example.
3614 0 : nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3615 0 : if (node && nsContentUtils::CanCallerAccess(node)) {
3616 0 : nsIPresShell::SetCapturingContent(nullptr, 0);
3617 : }
3618 0 : }
3619 :
3620 : already_AddRefed<nsIURI>
3621 255 : nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
3622 : {
3623 510 : nsCOMPtr<nsIURI> uri;
3624 255 : if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3625 0 : uri = mChromeXHRDocBaseURI;
3626 : } else {
3627 255 : uri = GetDocBaseURI();
3628 : }
3629 :
3630 510 : return uri.forget();
3631 : }
3632 :
3633 : void
3634 29 : nsDocument::SetBaseURI(nsIURI* aURI)
3635 : {
3636 29 : if (!aURI && !mDocumentBaseURI) {
3637 29 : return;
3638 : }
3639 :
3640 : // Don't do anything if the URI wasn't actually changed.
3641 0 : if (aURI && mDocumentBaseURI) {
3642 0 : bool equalBases = false;
3643 0 : mDocumentBaseURI->Equals(aURI, &equalBases);
3644 0 : if (equalBases) {
3645 0 : return;
3646 : }
3647 : }
3648 :
3649 0 : if (aURI) {
3650 0 : mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
3651 : } else {
3652 0 : mDocumentBaseURI = nullptr;
3653 : }
3654 0 : RefreshLinkHrefs();
3655 : }
3656 :
3657 : URLExtraData*
3658 0 : nsIDocument::DefaultStyleAttrURLData()
3659 : {
3660 : #ifdef MOZ_STYLO
3661 : MOZ_ASSERT(NS_IsMainThread());
3662 : nsIURI* baseURI = GetDocBaseURI();
3663 : nsIURI* docURI = GetDocumentURI();
3664 : nsIPrincipal* principal = NodePrincipal();
3665 : if (!mCachedURLData ||
3666 : mCachedURLData->BaseURI() != baseURI ||
3667 : mCachedURLData->GetReferrer() != docURI ||
3668 : mCachedURLData->GetPrincipal() != principal) {
3669 : mCachedURLData = new URLExtraData(baseURI, docURI, principal);
3670 : }
3671 : return mCachedURLData;
3672 : #else
3673 0 : MOZ_CRASH("Should not be called for non-stylo build");
3674 : return nullptr;
3675 : #endif
3676 : }
3677 :
3678 : void
3679 0 : nsDocument::GetBaseTarget(nsAString &aBaseTarget)
3680 : {
3681 0 : aBaseTarget = mBaseTarget;
3682 0 : }
3683 :
3684 : void
3685 78 : nsDocument::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding)
3686 : {
3687 78 : if (mCharacterSet != aEncoding) {
3688 54 : mCharacterSet = aEncoding;
3689 :
3690 108 : nsAutoCString charsetID;
3691 54 : aEncoding->Name(charsetID);
3692 108 : NS_ConvertASCIItoUTF16 charset16(charsetID);
3693 :
3694 54 : int32_t n = mCharSetObservers.Length();
3695 :
3696 56 : for (int32_t i = 0; i < n; i++) {
3697 2 : nsIObserver* observer = mCharSetObservers.ElementAt(i);
3698 :
3699 2 : observer->Observe(static_cast<nsIDocument *>(this), "charset",
3700 4 : charset16.get());
3701 : }
3702 : }
3703 78 : }
3704 :
3705 : nsresult
3706 28 : nsDocument::AddCharSetObserver(nsIObserver* aObserver)
3707 : {
3708 28 : NS_ENSURE_ARG_POINTER(aObserver);
3709 :
3710 28 : NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE);
3711 :
3712 28 : return NS_OK;
3713 : }
3714 :
3715 : void
3716 4 : nsDocument::RemoveCharSetObserver(nsIObserver* aObserver)
3717 : {
3718 4 : mCharSetObservers.RemoveElement(aObserver);
3719 4 : }
3720 :
3721 : void
3722 0 : nsIDocument::GetSandboxFlagsAsString(nsAString& aFlags)
3723 : {
3724 0 : nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags);
3725 0 : }
3726 :
3727 : void
3728 57 : nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const
3729 : {
3730 57 : aData.Truncate();
3731 57 : const nsDocHeaderData* data = mHeaderData;
3732 57 : while (data) {
3733 0 : if (data->mField == aHeaderField) {
3734 0 : aData = data->mData;
3735 :
3736 0 : break;
3737 : }
3738 0 : data = data->mNext;
3739 : }
3740 57 : }
3741 :
3742 : void
3743 0 : nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
3744 : {
3745 0 : if (!aHeaderField) {
3746 0 : NS_ERROR("null headerField");
3747 0 : return;
3748 : }
3749 :
3750 0 : if (!mHeaderData) {
3751 0 : if (!aData.IsEmpty()) { // don't bother storing empty string
3752 0 : mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3753 : }
3754 : }
3755 : else {
3756 0 : nsDocHeaderData* data = mHeaderData;
3757 0 : nsDocHeaderData** lastPtr = &mHeaderData;
3758 0 : bool found = false;
3759 0 : do { // look for existing and replace
3760 0 : if (data->mField == aHeaderField) {
3761 0 : if (!aData.IsEmpty()) {
3762 0 : data->mData.Assign(aData);
3763 : }
3764 : else { // don't store empty string
3765 0 : *lastPtr = data->mNext;
3766 0 : data->mNext = nullptr;
3767 0 : delete data;
3768 : }
3769 0 : found = true;
3770 :
3771 0 : break;
3772 : }
3773 0 : lastPtr = &(data->mNext);
3774 0 : data = *lastPtr;
3775 0 : } while (data);
3776 :
3777 0 : if (!aData.IsEmpty() && !found) {
3778 : // didn't find, append
3779 0 : *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3780 : }
3781 : }
3782 :
3783 0 : if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3784 0 : CopyUTF16toUTF8(aData, mContentLanguage);
3785 : }
3786 :
3787 0 : if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3788 : // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
3789 : // spec.
3790 0 : if (DOMStringIsNull(mLastStyleSheetSet)) {
3791 : // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
3792 : // per spec. The idea here is that we're changing our preferred set and
3793 : // that shouldn't change the value of lastStyleSheetSet. Also, we're
3794 : // using the Internal version so we can update the CSSLoader and not have
3795 : // to worry about null strings.
3796 0 : EnableStyleSheetsForSetInternal(aData, true);
3797 : }
3798 : }
3799 :
3800 0 : if (aHeaderField == nsGkAtoms::refresh) {
3801 : // We get into this code before we have a script global yet, so get to
3802 : // our container via mDocumentContainer.
3803 0 : nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3804 0 : if (refresher) {
3805 : // Note: using mDocumentURI instead of mBaseURI here, for consistency
3806 : // (used to just use the current URI of our webnavigation, but that
3807 : // should really be the same thing). Note that this code can run
3808 : // before the current URI of the webnavigation has been updated, so we
3809 : // can't assert equality here.
3810 0 : refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3811 0 : NS_ConvertUTF16toUTF8(aData));
3812 : }
3813 : }
3814 :
3815 0 : if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3816 0 : mAllowDNSPrefetch) {
3817 : // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3818 0 : mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3819 : }
3820 :
3821 0 : if (aHeaderField == nsGkAtoms::viewport ||
3822 0 : aHeaderField == nsGkAtoms::handheldFriendly ||
3823 0 : aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3824 0 : aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3825 0 : aHeaderField == nsGkAtoms::viewport_initial_scale ||
3826 0 : aHeaderField == nsGkAtoms::viewport_height ||
3827 0 : aHeaderField == nsGkAtoms::viewport_width ||
3828 0 : aHeaderField == nsGkAtoms::viewport_user_scalable) {
3829 0 : mViewportType = Unknown;
3830 : }
3831 :
3832 : // Referrer policy spec says to ignore any empty referrer policies.
3833 0 : if (aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
3834 0 : ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
3835 : // If policy is not the empty string, then set element's node document's
3836 : // referrer policy to policy
3837 0 : if (policy != mozilla::net::RP_Unset) {
3838 : // Referrer policy spec (section 6.1) says that we always use the newest
3839 : // referrer policy we find
3840 0 : mReferrerPolicy = policy;
3841 0 : mReferrerPolicySet = true;
3842 : }
3843 : }
3844 :
3845 0 : if (aHeaderField == nsGkAtoms::headerReferrerPolicy && !aData.IsEmpty()) {
3846 0 : ReferrerPolicy policy = nsContentUtils::GetReferrerPolicyFromHeader(aData);
3847 0 : if (policy != mozilla::net::RP_Unset) {
3848 0 : mReferrerPolicy = policy;
3849 0 : mReferrerPolicySet = true;
3850 : }
3851 : }
3852 :
3853 : }
3854 : void
3855 25 : nsDocument::TryChannelCharset(nsIChannel *aChannel,
3856 : int32_t& aCharsetSource,
3857 : NotNull<const Encoding*>& aEncoding,
3858 : nsHtml5TreeOpExecutor* aExecutor)
3859 : {
3860 25 : if (aChannel) {
3861 27 : nsAutoCString charsetVal;
3862 25 : nsresult rv = aChannel->GetContentCharset(charsetVal);
3863 25 : if (NS_SUCCEEDED(rv)) {
3864 25 : const Encoding* preferred = Encoding::ForLabel(charsetVal);
3865 25 : if (preferred) {
3866 23 : aEncoding = WrapNotNull(preferred);
3867 23 : aCharsetSource = kCharsetFromChannel;
3868 23 : return;
3869 2 : } else if (aExecutor && !charsetVal.IsEmpty()) {
3870 0 : aExecutor->ComplainAboutBogusProtocolCharset(this);
3871 : }
3872 : }
3873 : }
3874 : }
3875 :
3876 : already_AddRefed<nsIPresShell>
3877 28 : nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
3878 : StyleSetHandle aStyleSet)
3879 : {
3880 28 : NS_ASSERTION(!mPresShell, "We have a presshell already!");
3881 :
3882 28 : NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3883 :
3884 28 : FillStyleSet(aStyleSet);
3885 :
3886 56 : RefPtr<PresShell> shell = new PresShell;
3887 28 : shell->Init(this, aContext, aViewManager, aStyleSet);
3888 :
3889 : // Note: we don't hold a ref to the shell (it holds a ref to us)
3890 28 : mPresShell = shell;
3891 :
3892 : // Make sure to never paint if we belong to an invisible DocShell.
3893 56 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3894 28 : if (docShell && docShell->IsInvisible())
3895 0 : shell->SetNeverPainting(true);
3896 :
3897 28 : mExternalResourceMap.ShowViewers();
3898 :
3899 28 : UpdateFrameRequestCallbackSchedulingState();
3900 :
3901 : // Now that we have a shell, we might have @font-face rules.
3902 28 : RebuildUserFontSet();
3903 :
3904 28 : return shell.forget();
3905 : }
3906 :
3907 : void
3908 52 : nsIDocument::UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell)
3909 : {
3910 : // If the condition for shouldBeScheduled changes to depend on some other
3911 : // variable, add UpdateFrameRequestCallbackSchedulingState() calls to the
3912 : // places where that variable can change.
3913 : bool shouldBeScheduled =
3914 52 : mPresShell && IsEventHandlingEnabled() && !mFrameRequestCallbacks.IsEmpty();
3915 52 : if (shouldBeScheduled == mFrameRequestCallbacksScheduled) {
3916 : // nothing to do
3917 48 : return;
3918 : }
3919 :
3920 4 : nsIPresShell* presShell = aOldShell ? aOldShell : mPresShell;
3921 4 : MOZ_RELEASE_ASSERT(presShell);
3922 :
3923 4 : nsRefreshDriver* rd = presShell->GetPresContext()->RefreshDriver();
3924 4 : if (shouldBeScheduled) {
3925 4 : rd->ScheduleFrameRequestCallbacks(this);
3926 : } else {
3927 0 : rd->RevokeFrameRequestCallbacks(this);
3928 : }
3929 :
3930 4 : mFrameRequestCallbacksScheduled = shouldBeScheduled;
3931 : }
3932 :
3933 : void
3934 4 : nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
3935 : {
3936 4 : aCallbacks.AppendElements(mFrameRequestCallbacks);
3937 4 : mFrameRequestCallbacks.Clear();
3938 : // No need to manually remove ourselves from the refresh driver; it will
3939 : // handle that part. But we do have to update our state.
3940 4 : mFrameRequestCallbacksScheduled = false;
3941 4 : }
3942 :
3943 : bool
3944 5 : nsIDocument::ShouldThrottleFrameRequests()
3945 : {
3946 5 : if (mStaticCloneCount > 0) {
3947 : // Even if we're not visible, a static clone may be, so run at full speed.
3948 0 : return false;
3949 : }
3950 :
3951 5 : if (Hidden()) {
3952 : // We're not visible (probably in a background tab or the bf cache).
3953 0 : return true;
3954 : }
3955 :
3956 5 : if (!mPresShell) {
3957 0 : return false; // Can't do anything smarter.
3958 : }
3959 :
3960 5 : nsIFrame* frame = mPresShell->GetRootFrame();
3961 5 : if (!frame) {
3962 0 : return false; // Can't do anything smarter.
3963 : }
3964 :
3965 5 : nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame);
3966 5 : if (!displayRootFrame) {
3967 0 : return false; // Can't do anything smarter.
3968 : }
3969 :
3970 5 : if (!displayRootFrame->DidPaintPresShell(mPresShell)) {
3971 : // We didn't get painted during the last paint, so we're not visible.
3972 : // Throttle. Note that because we have to paint this document at least
3973 : // once to unthrottle it, we will drop one requestAnimationFrame frame
3974 : // when a document that previously wasn't visible scrolls into view. This
3975 : // is acceptable since it would happen outside the viewport on APZ
3976 : // platforms and is unlikely to be human-perceivable on non-APZ platforms.
3977 1 : return true;
3978 : }
3979 :
3980 : // We got painted during the last paint, so run at full speed.
3981 4 : return false;
3982 : }
3983 :
3984 : void
3985 4 : nsDocument::DeleteShell()
3986 : {
3987 4 : mExternalResourceMap.HideViewers();
3988 4 : if (nsPresContext* presContext = mPresShell->GetPresContext()) {
3989 4 : presContext->RefreshDriver()->CancelPendingEvents(this);
3990 : }
3991 :
3992 : // When our shell goes away, request that all our images be immediately
3993 : // discarded, so we don't carry around decoded image data for a document we
3994 : // no longer intend to paint.
3995 4 : ImageTracker()->RequestDiscardAll();
3996 :
3997 : // Now that we no longer have a shell, we need to forget about any FontFace
3998 : // objects for @font-face rules that came from the style set.
3999 4 : RebuildUserFontSet();
4000 :
4001 4 : nsIPresShell* oldShell = mPresShell;
4002 4 : mPresShell = nullptr;
4003 4 : UpdateFrameRequestCallbackSchedulingState(oldShell);
4004 4 : mStyleSetFilled = false;
4005 4 : }
4006 :
4007 : static void
4008 1 : SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
4009 : {
4010 1 : SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
4011 :
4012 1 : NS_RELEASE(e->mKey);
4013 1 : if (e->mSubDocument) {
4014 1 : e->mSubDocument->SetParentDocument(nullptr);
4015 1 : NS_RELEASE(e->mSubDocument);
4016 : }
4017 1 : }
4018 :
4019 : static void
4020 2 : SubDocInitEntry(PLDHashEntryHdr *entry, const void *key)
4021 : {
4022 : SubDocMapEntry *e =
4023 : const_cast<SubDocMapEntry *>
4024 2 : (static_cast<const SubDocMapEntry *>(entry));
4025 :
4026 2 : e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
4027 2 : NS_ADDREF(e->mKey);
4028 :
4029 2 : e->mSubDocument = nullptr;
4030 2 : }
4031 :
4032 : nsresult
4033 3 : nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
4034 : {
4035 3 : NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
4036 :
4037 3 : if (!aSubDoc) {
4038 : // aSubDoc is nullptr, remove the mapping
4039 :
4040 1 : if (mSubDocuments) {
4041 1 : mSubDocuments->Remove(aElement);
4042 : }
4043 : } else {
4044 2 : if (!mSubDocuments) {
4045 : // Create a new hashtable
4046 :
4047 : static const PLDHashTableOps hash_table_ops =
4048 : {
4049 : PLDHashTable::HashVoidPtrKeyStub,
4050 : PLDHashTable::MatchEntryStub,
4051 : PLDHashTable::MoveEntryStub,
4052 : SubDocClearEntry,
4053 : SubDocInitEntry
4054 : };
4055 :
4056 1 : mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
4057 : }
4058 :
4059 : // Add a mapping to the hash table
4060 : auto entry =
4061 2 : static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible));
4062 :
4063 2 : if (!entry) {
4064 0 : return NS_ERROR_OUT_OF_MEMORY;
4065 : }
4066 :
4067 2 : if (entry->mSubDocument) {
4068 0 : entry->mSubDocument->SetParentDocument(nullptr);
4069 :
4070 : // Release the old sub document
4071 0 : NS_RELEASE(entry->mSubDocument);
4072 : }
4073 :
4074 2 : entry->mSubDocument = aSubDoc;
4075 2 : NS_ADDREF(entry->mSubDocument);
4076 :
4077 2 : aSubDoc->SetParentDocument(this);
4078 : }
4079 :
4080 3 : return NS_OK;
4081 : }
4082 :
4083 : nsIDocument*
4084 35 : nsDocument::GetSubDocumentFor(nsIContent *aContent) const
4085 : {
4086 35 : if (mSubDocuments && aContent->IsElement()) {
4087 : auto entry = static_cast<SubDocMapEntry*>
4088 34 : (mSubDocuments->Search(aContent->AsElement()));
4089 :
4090 34 : if (entry) {
4091 0 : return entry->mSubDocument;
4092 : }
4093 : }
4094 :
4095 35 : return nullptr;
4096 : }
4097 :
4098 : Element*
4099 1 : nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
4100 : {
4101 1 : NS_ENSURE_TRUE(aDocument, nullptr);
4102 :
4103 1 : if (!mSubDocuments) {
4104 0 : return nullptr;
4105 : }
4106 :
4107 1 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
4108 1 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
4109 1 : if (entry->mSubDocument == aDocument) {
4110 1 : return entry->mKey;
4111 : }
4112 : }
4113 0 : return nullptr;
4114 : }
4115 :
4116 : bool
4117 397 : nsDocument::IsNodeOfType(uint32_t aFlags) const
4118 : {
4119 397 : return !(aFlags & ~eDOCUMENT);
4120 : }
4121 :
4122 : Element*
4123 44692 : nsIDocument::GetRootElement() const
4124 : {
4125 44840 : return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
4126 44840 : mCachedRootElement : GetRootElementInternal();
4127 : }
4128 :
4129 : Element*
4130 148 : nsDocument::GetRootElementInternal() const
4131 : {
4132 : // We invoke GetRootElement() immediately before the servo traversal, so we
4133 : // should always have a cache hit from Servo.
4134 148 : MOZ_ASSERT(NS_IsMainThread());
4135 :
4136 : // Loop backwards because any non-elements, such as doctypes and PIs
4137 : // are likely to appear before the root element.
4138 : uint32_t i;
4139 253 : for (i = mChildren.ChildCount(); i > 0; --i) {
4140 160 : nsIContent* child = mChildren.ChildAt(i - 1);
4141 160 : if (child->IsElement()) {
4142 55 : const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
4143 55 : return child->AsElement();
4144 : }
4145 : }
4146 :
4147 93 : const_cast<nsDocument*>(this)->mCachedRootElement = nullptr;
4148 93 : return nullptr;
4149 : }
4150 :
4151 : nsIContent *
4152 0 : nsDocument::GetChildAt(uint32_t aIndex) const
4153 : {
4154 0 : return mChildren.GetSafeChildAt(aIndex);
4155 : }
4156 :
4157 : int32_t
4158 10 : nsDocument::IndexOf(const nsINode* aPossibleChild) const
4159 : {
4160 10 : return mChildren.IndexOfChild(aPossibleChild);
4161 : }
4162 :
4163 : uint32_t
4164 93 : nsDocument::GetChildCount() const
4165 : {
4166 93 : return mChildren.ChildCount();
4167 : }
4168 :
4169 : nsIContent * const *
4170 0 : nsDocument::GetChildArray(uint32_t* aChildCount) const
4171 : {
4172 0 : return mChildren.GetChildArray(aChildCount);
4173 : }
4174 :
4175 :
4176 : nsresult
4177 88 : nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
4178 : bool aNotify)
4179 : {
4180 88 : if (aKid->IsElement() && GetRootElement()) {
4181 0 : NS_WARNING("Inserting root element when we already have one");
4182 0 : return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
4183 : }
4184 :
4185 88 : return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
4186 : }
4187 :
4188 : void
4189 0 : nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
4190 : {
4191 0 : nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
4192 0 : if (!oldKid) {
4193 0 : return;
4194 : }
4195 :
4196 0 : if (oldKid->IsElement()) {
4197 : // Destroy the link map up front before we mess with the child list.
4198 0 : DestroyElementMaps();
4199 : }
4200 :
4201 : // Preemptively clear mCachedRootElement, since we may be about to remove it
4202 : // from our child list, and we don't want to return this maybe-obsolete value
4203 : // from any GetRootElement() calls that happen inside of doRemoveChildAt().
4204 : // (NOTE: for this to be useful, doRemoveChildAt() must NOT trigger any
4205 : // GetRootElement() calls until after it's removed the child from mChildren.
4206 : // Any call before that point would restore this soon-to-be-obsolete cached
4207 : // answer, and our clearing here would be fruitless.)
4208 0 : mCachedRootElement = nullptr;
4209 0 : doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
4210 0 : MOZ_ASSERT(mCachedRootElement != oldKid,
4211 : "Stale pointer in mCachedRootElement, after we tried to clear it "
4212 : "(maybe somebody called GetRootElement() too early?)");
4213 : }
4214 :
4215 : void
4216 34 : nsDocument::EnsureOnDemandBuiltInUASheet(StyleSheet* aSheet)
4217 : {
4218 34 : if (mOnDemandBuiltInUASheets.Contains(aSheet)) {
4219 0 : return;
4220 : }
4221 34 : BeginUpdate(UPDATE_STYLE);
4222 34 : AddOnDemandBuiltInUASheet(aSheet);
4223 34 : EndUpdate(UPDATE_STYLE);
4224 : }
4225 :
4226 : void
4227 34 : nsDocument::AddOnDemandBuiltInUASheet(StyleSheet* aSheet)
4228 : {
4229 34 : MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(aSheet));
4230 :
4231 : // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in
4232 : // the same order that they should end up in the style set.
4233 34 : mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet);
4234 :
4235 34 : if (aSheet->IsApplicable()) {
4236 : // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
4237 68 : nsCOMPtr<nsIPresShell> shell = GetShell();
4238 34 : if (shell) {
4239 : // Note that prepending here is necessary to make sure that html.css etc.
4240 : // do not override Firefox OS/Mobile's content.css sheet. Maybe we should
4241 : // have an insertion point to match the order of
4242 : // nsDocumentViewer::CreateStyleSet though?
4243 34 : shell->StyleSet()->PrependStyleSheet(SheetType::Agent, aSheet);
4244 : }
4245 : }
4246 :
4247 34 : NotifyStyleSheetAdded(aSheet, false);
4248 34 : }
4249 :
4250 : int32_t
4251 14 : nsDocument::GetNumberOfStyleSheets() const
4252 : {
4253 14 : return mStyleSheets.Length();
4254 : }
4255 :
4256 : StyleSheet*
4257 5 : nsDocument::GetStyleSheetAt(int32_t aIndex) const
4258 : {
4259 5 : return mStyleSheets.SafeElementAt(aIndex, nullptr);
4260 : }
4261 :
4262 : int32_t
4263 26 : nsDocument::GetIndexOfStyleSheet(const StyleSheet* aSheet) const
4264 : {
4265 26 : return mStyleSheets.IndexOf(aSheet);
4266 : }
4267 :
4268 : void
4269 11 : nsDocument::AddStyleSheetToStyleSets(StyleSheet* aSheet)
4270 : {
4271 22 : nsCOMPtr<nsIPresShell> shell = GetShell();
4272 11 : if (shell) {
4273 11 : shell->StyleSet()->AddDocStyleSheet(aSheet, this);
4274 : }
4275 11 : }
4276 :
4277 : #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \
4278 : do { \
4279 : className##Init init; \
4280 : init.mBubbles = true; \
4281 : init.mCancelable = true; \
4282 : init.mStylesheet = aSheet; \
4283 : init.memberName = argName; \
4284 : \
4285 : RefPtr<className> event = \
4286 : className::Constructor(this, NS_LITERAL_STRING(type), init); \
4287 : event->SetTrusted(true); \
4288 : event->SetTarget(this); \
4289 : RefPtr<AsyncEventDispatcher> asyncDispatcher = \
4290 : new AsyncEventDispatcher(this, event); \
4291 : asyncDispatcher->mOnlyChromeDispatch = true; \
4292 : asyncDispatcher->PostDOMEvent(); \
4293 : } while (0);
4294 :
4295 : void
4296 46 : nsDocument::NotifyStyleSheetAdded(StyleSheet* aSheet, bool aDocumentSheet)
4297 : {
4298 46 : NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (aSheet, aDocumentSheet));
4299 :
4300 46 : if (StyleSheetChangeEventsEnabled()) {
4301 0 : DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4302 : "StyleSheetAdded",
4303 : mDocumentSheet,
4304 : aDocumentSheet);
4305 : }
4306 46 : }
4307 :
4308 : void
4309 0 : nsDocument::NotifyStyleSheetRemoved(StyleSheet* aSheet, bool aDocumentSheet)
4310 : {
4311 0 : NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (aSheet, aDocumentSheet));
4312 :
4313 0 : if (StyleSheetChangeEventsEnabled()) {
4314 0 : DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4315 : "StyleSheetRemoved",
4316 : mDocumentSheet,
4317 : aDocumentSheet);
4318 : }
4319 0 : }
4320 :
4321 : void
4322 0 : nsDocument::AddStyleSheet(StyleSheet* aSheet)
4323 : {
4324 0 : NS_PRECONDITION(aSheet, "null arg");
4325 0 : mStyleSheets.AppendElement(aSheet);
4326 0 : aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4327 :
4328 0 : if (aSheet->IsApplicable()) {
4329 0 : AddStyleSheetToStyleSets(aSheet);
4330 : }
4331 :
4332 0 : NotifyStyleSheetAdded(aSheet, true);
4333 0 : }
4334 :
4335 : void
4336 0 : nsDocument::RemoveStyleSheetFromStyleSets(StyleSheet* aSheet)
4337 : {
4338 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
4339 0 : if (shell) {
4340 0 : shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4341 : }
4342 0 : }
4343 :
4344 : void
4345 0 : nsDocument::RemoveStyleSheet(StyleSheet* aSheet)
4346 : {
4347 0 : NS_PRECONDITION(aSheet, "null arg");
4348 0 : RefPtr<StyleSheet> sheet = aSheet; // hold ref so it won't die too soon
4349 :
4350 0 : if (!mStyleSheets.RemoveElement(aSheet)) {
4351 0 : NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4352 0 : return;
4353 : }
4354 :
4355 0 : if (!mIsGoingAway) {
4356 0 : if (aSheet->IsApplicable()) {
4357 0 : RemoveStyleSheetFromStyleSets(aSheet);
4358 : }
4359 :
4360 0 : NotifyStyleSheetRemoved(aSheet, true);
4361 : }
4362 :
4363 0 : aSheet->ClearAssociatedDocument();
4364 : }
4365 :
4366 : void
4367 0 : nsDocument::UpdateStyleSheets(nsTArray<RefPtr<StyleSheet>>& aOldSheets,
4368 : nsTArray<RefPtr<StyleSheet>>& aNewSheets)
4369 : {
4370 0 : BeginUpdate(UPDATE_STYLE);
4371 :
4372 : // XXX Need to set the sheet on the ownernode, if any
4373 0 : NS_PRECONDITION(aOldSheets.Length() == aNewSheets.Length(),
4374 : "The lists must be the same length!");
4375 0 : int32_t count = aOldSheets.Length();
4376 :
4377 0 : RefPtr<StyleSheet> oldSheet;
4378 : int32_t i;
4379 0 : for (i = 0; i < count; ++i) {
4380 0 : oldSheet = aOldSheets[i];
4381 :
4382 : // First remove the old sheet.
4383 0 : NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4384 0 : int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4385 0 : RemoveStyleSheet(oldSheet); // This does the right notifications
4386 :
4387 : // Now put the new one in its place. If it's null, just ignore it.
4388 0 : StyleSheet* newSheet = aNewSheets[i];
4389 0 : if (newSheet) {
4390 0 : mStyleSheets.InsertElementAt(oldIndex, newSheet);
4391 0 : newSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4392 0 : if (newSheet->IsApplicable()) {
4393 0 : AddStyleSheetToStyleSets(newSheet);
4394 : }
4395 :
4396 0 : NotifyStyleSheetAdded(newSheet, true);
4397 : }
4398 : }
4399 :
4400 0 : EndUpdate(UPDATE_STYLE);
4401 0 : }
4402 :
4403 : void
4404 11 : nsDocument::InsertStyleSheetAt(StyleSheet* aSheet, int32_t aIndex)
4405 : {
4406 11 : NS_PRECONDITION(aSheet, "null ptr");
4407 :
4408 11 : mStyleSheets.InsertElementAt(aIndex, aSheet);
4409 :
4410 11 : aSheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4411 :
4412 11 : if (aSheet->IsApplicable()) {
4413 0 : AddStyleSheetToStyleSets(aSheet);
4414 : }
4415 :
4416 11 : NotifyStyleSheetAdded(aSheet, true);
4417 11 : }
4418 :
4419 :
4420 : void
4421 13 : nsDocument::SetStyleSheetApplicableState(StyleSheet* aSheet,
4422 : bool aApplicable)
4423 : {
4424 13 : NS_PRECONDITION(aSheet, "null arg");
4425 :
4426 : // If we're actually in the document style sheet list
4427 13 : if (mStyleSheets.IndexOf(aSheet) != mStyleSheets.NoIndex) {
4428 11 : if (aApplicable) {
4429 11 : AddStyleSheetToStyleSets(aSheet);
4430 : } else {
4431 0 : RemoveStyleSheetFromStyleSets(aSheet);
4432 : }
4433 : }
4434 :
4435 : // We have to always notify, since this will be called for sheets
4436 : // that are children of sheets in our style set, as well as some
4437 : // sheets for HTMLEditor.
4438 :
4439 13 : NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged, (aSheet));
4440 :
4441 13 : if (StyleSheetChangeEventsEnabled()) {
4442 0 : DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
4443 : "StyleSheetApplicableStateChanged",
4444 : mApplicable,
4445 : aApplicable);
4446 : }
4447 :
4448 13 : if (!mSSApplicableStateNotificationPending) {
4449 7 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
4450 : nsCOMPtr<nsIRunnable> notification =
4451 14 : NewRunnableMethod("nsDocument::NotifyStyleSheetApplicableStateChanged",
4452 : this,
4453 14 : &nsDocument::NotifyStyleSheetApplicableStateChanged);
4454 7 : mSSApplicableStateNotificationPending =
4455 14 : NS_SUCCEEDED(
4456 : Dispatch("nsDocument::NotifyStyleSheetApplicableStateChanged",
4457 : TaskCategory::Other, notification.forget()));
4458 : }
4459 13 : }
4460 :
4461 : void
4462 7 : nsDocument::NotifyStyleSheetApplicableStateChanged()
4463 : {
4464 7 : mSSApplicableStateNotificationPending = false;
4465 : nsCOMPtr<nsIObserverService> observerService =
4466 14 : mozilla::services::GetObserverService();
4467 7 : if (observerService) {
4468 14 : observerService->NotifyObservers(static_cast<nsIDocument*>(this),
4469 : "style-sheet-applicable-state-changed",
4470 14 : nullptr);
4471 : }
4472 7 : }
4473 :
4474 : static SheetType
4475 1 : ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
4476 : {
4477 1 : switch(aType) {
4478 : case nsIDocument::eAgentSheet:
4479 0 : return SheetType::Agent;
4480 : case nsIDocument::eUserSheet:
4481 0 : return SheetType::User;
4482 : case nsIDocument::eAuthorSheet:
4483 1 : return SheetType::Doc;
4484 : default:
4485 0 : MOZ_ASSERT(false, "wrong type");
4486 : // we must return something although this should never happen
4487 : return SheetType::Count;
4488 : }
4489 : }
4490 :
4491 : static int32_t
4492 0 : FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets, nsIURI* aSheetURI)
4493 : {
4494 0 : for (int32_t i = aSheets.Length() - 1; i >= 0; i-- ) {
4495 : bool bEqual;
4496 0 : nsIURI* uri = aSheets[i]->GetSheetURI();
4497 :
4498 0 : if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4499 0 : return i;
4500 : }
4501 :
4502 0 : return -1;
4503 : }
4504 :
4505 : nsresult
4506 0 : nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType,
4507 : nsIURI* aSheetURI)
4508 : {
4509 0 : NS_PRECONDITION(aSheetURI, "null arg");
4510 :
4511 : // Checking if we have loaded this one already.
4512 0 : if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4513 0 : return NS_ERROR_INVALID_ARG;
4514 :
4515 : // Loading the sheet sync.
4516 : RefPtr<css::Loader> loader =
4517 0 : new css::Loader(GetStyleBackendType(), GetDocGroup());
4518 :
4519 : css::SheetParsingMode parsingMode;
4520 0 : switch (aType) {
4521 : case nsIDocument::eAgentSheet:
4522 0 : parsingMode = css::eAgentSheetFeatures;
4523 0 : break;
4524 :
4525 : case nsIDocument::eUserSheet:
4526 0 : parsingMode = css::eUserSheetFeatures;
4527 0 : break;
4528 :
4529 : case nsIDocument::eAuthorSheet:
4530 0 : parsingMode = css::eAuthorSheetFeatures;
4531 0 : break;
4532 :
4533 : default:
4534 0 : MOZ_CRASH("impossible value for aType");
4535 : }
4536 :
4537 0 : RefPtr<StyleSheet> sheet;
4538 0 : nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet);
4539 0 : NS_ENSURE_SUCCESS(rv, rv);
4540 :
4541 0 : sheet->SetAssociatedDocument(this, StyleSheet::OwnedByDocument);
4542 0 : MOZ_ASSERT(sheet->IsApplicable());
4543 :
4544 0 : return AddAdditionalStyleSheet(aType, sheet);
4545 : }
4546 :
4547 : nsresult
4548 1 : nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, StyleSheet* aSheet)
4549 : {
4550 1 : if (mAdditionalSheets[aType].Contains(aSheet))
4551 0 : return NS_ERROR_INVALID_ARG;
4552 :
4553 1 : if (!aSheet->IsApplicable())
4554 0 : return NS_ERROR_INVALID_ARG;
4555 :
4556 1 : mAdditionalSheets[aType].AppendElement(aSheet);
4557 :
4558 1 : BeginUpdate(UPDATE_STYLE);
4559 2 : nsCOMPtr<nsIPresShell> shell = GetShell();
4560 1 : if (shell) {
4561 1 : SheetType type = ConvertAdditionalSheetType(aType);
4562 1 : shell->StyleSet()->AppendStyleSheet(type, aSheet);
4563 : }
4564 :
4565 : // Passing false, so documet.styleSheets.length will not be affected by
4566 : // these additional sheets.
4567 1 : NotifyStyleSheetAdded(aSheet, false);
4568 1 : EndUpdate(UPDATE_STYLE);
4569 1 : return NS_OK;
4570 : }
4571 :
4572 : void
4573 0 : nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4574 : {
4575 0 : MOZ_ASSERT(aSheetURI);
4576 :
4577 0 : nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType];
4578 :
4579 0 : int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4580 0 : if (i >= 0) {
4581 0 : RefPtr<StyleSheet> sheetRef = sheets[i];
4582 0 : sheets.RemoveElementAt(i);
4583 :
4584 0 : BeginUpdate(UPDATE_STYLE);
4585 0 : if (!mIsGoingAway) {
4586 0 : MOZ_ASSERT(sheetRef->IsApplicable());
4587 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
4588 0 : if (shell) {
4589 0 : SheetType type = ConvertAdditionalSheetType(aType);
4590 0 : shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4591 : }
4592 : }
4593 :
4594 : // Passing false, so documet.styleSheets.length will not be affected by
4595 : // these additional sheets.
4596 0 : NotifyStyleSheetRemoved(sheetRef, false);
4597 0 : EndUpdate(UPDATE_STYLE);
4598 :
4599 0 : sheetRef->ClearAssociatedDocument();
4600 : }
4601 0 : }
4602 :
4603 : StyleSheet*
4604 0 : nsDocument::GetFirstAdditionalAuthorSheet()
4605 : {
4606 0 : return mAdditionalSheets[eAuthorSheet].SafeElementAt(0);
4607 : }
4608 :
4609 : nsIGlobalObject*
4610 2588 : nsDocument::GetScopeObject() const
4611 : {
4612 5176 : nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4613 5176 : return scope;
4614 : }
4615 :
4616 : void
4617 8 : nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
4618 : {
4619 8 : mScopeObject = do_GetWeakReference(aGlobal);
4620 8 : if (aGlobal) {
4621 8 : mHasHadScriptHandlingObject = true;
4622 :
4623 16 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
4624 8 : if (window) {
4625 : // We want to get the tabgroup unconditionally, such that we can make
4626 : // certain that it is cached in the inner window early enough.
4627 8 : mozilla::dom::TabGroup* tabgroup = window->TabGroup();
4628 : // We should already have the principal, and now that we have been added to a
4629 : // window, we should be able to join a DocGroup!
4630 16 : nsAutoCString docGroupKey;
4631 : nsresult rv =
4632 8 : mozilla::dom::DocGroup::GetKey(NodePrincipal(), docGroupKey);
4633 8 : if (mDocGroup) {
4634 0 : if (NS_SUCCEEDED(rv)) {
4635 0 : MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
4636 : }
4637 0 : MOZ_RELEASE_ASSERT(mDocGroup->GetTabGroup() == tabgroup);
4638 : } else {
4639 8 : mDocGroup = tabgroup->AddDocument(docGroupKey, this);
4640 8 : MOZ_ASSERT(mDocGroup);
4641 : }
4642 : }
4643 : }
4644 8 : }
4645 :
4646 : static void
4647 0 : CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
4648 : {
4649 0 : nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4650 0 : if (domMediaElem) {
4651 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4652 0 : MOZ_ASSERT(content, "aSupports is not a content");
4653 0 : HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4654 0 : bool* contains = static_cast<bool*>(aContainsEME);
4655 0 : if (mediaElem->GetMediaKeys()) {
4656 0 : *contains = true;
4657 : }
4658 : }
4659 0 : }
4660 :
4661 : bool
4662 0 : nsDocument::ContainsEMEContent()
4663 : {
4664 0 : bool containsEME = false;
4665 0 : EnumerateActivityObservers(CheckIfContainsEMEContent,
4666 0 : static_cast<void*>(&containsEME));
4667 0 : return containsEME;
4668 : }
4669 :
4670 : static void
4671 0 : CheckIfContainsMSEContent(nsISupports* aSupports, void* aContainsMSE)
4672 : {
4673 0 : nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4674 0 : if (domMediaElem) {
4675 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4676 0 : MOZ_ASSERT(content, "aSupports is not a content");
4677 0 : HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4678 0 : bool* contains = static_cast<bool*>(aContainsMSE);
4679 0 : RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
4680 0 : if (ms) {
4681 0 : *contains = true;
4682 : }
4683 : }
4684 0 : }
4685 :
4686 : bool
4687 0 : nsDocument::ContainsMSEContent()
4688 : {
4689 0 : bool containsMSE = false;
4690 0 : EnumerateActivityObservers(CheckIfContainsMSEContent,
4691 0 : static_cast<void*>(&containsMSE));
4692 0 : return containsMSE;
4693 : }
4694 :
4695 : static void
4696 1 : NotifyActivityChanged(nsISupports *aSupports, void *aUnused)
4697 : {
4698 2 : nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4699 1 : if (domMediaElem) {
4700 2 : nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4701 1 : MOZ_ASSERT(content, "aSupports is not a content");
4702 1 : HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4703 1 : mediaElem->NotifyOwnerDocumentActivityChanged();
4704 : }
4705 2 : nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aSupports));
4706 1 : if (objectLoadingContent) {
4707 0 : nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4708 0 : olc->NotifyOwnerDocumentActivityChanged();
4709 : }
4710 2 : nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(do_QueryInterface(aSupports));
4711 1 : if (objectDocumentActivity) {
4712 0 : objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
4713 : }
4714 1 : }
4715 :
4716 : void
4717 50 : nsIDocument::SetContainer(nsDocShell* aContainer)
4718 : {
4719 50 : if (aContainer) {
4720 8 : mDocumentContainer = aContainer;
4721 : } else {
4722 42 : mDocumentContainer = WeakPtr<nsDocShell>();
4723 : }
4724 :
4725 50 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4726 50 : if (!aContainer) {
4727 42 : return;
4728 : }
4729 :
4730 : // Get the Docshell
4731 8 : if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4732 : // check if same type root
4733 6 : nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4734 3 : aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4735 3 : NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
4736 :
4737 3 : if (sameTypeRoot == aContainer) {
4738 3 : static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4739 : }
4740 :
4741 3 : static_cast<nsDocument*>(this)->SetIsContentDocument(true);
4742 : }
4743 : }
4744 :
4745 : nsISupports*
4746 51 : nsIDocument::GetContainer() const
4747 : {
4748 51 : return static_cast<nsIDocShell*>(mDocumentContainer);
4749 : }
4750 :
4751 : void
4752 16 : nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
4753 : {
4754 : #ifdef DEBUG
4755 : {
4756 32 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryInterface(aScriptGlobalObject));
4757 :
4758 16 : NS_ASSERTION(!win || win->IsInnerWindow(),
4759 : "Script global object must be an inner window!");
4760 : }
4761 : #endif
4762 16 : MOZ_ASSERT(aScriptGlobalObject || !mAnimationController ||
4763 : mAnimationController->IsPausedByType(
4764 : nsSMILTimeContainer::PAUSE_PAGEHIDE |
4765 : nsSMILTimeContainer::PAUSE_BEGIN),
4766 : "Clearing window pointer while animations are unpaused");
4767 :
4768 16 : if (mScriptGlobalObject && !aScriptGlobalObject) {
4769 : // We're detaching from the window. We need to grab a pointer to
4770 : // our layout history state now.
4771 4 : mLayoutHistoryState = GetLayoutHistoryState();
4772 :
4773 : // Also make sure to remove our onload blocker now if we haven't done it yet
4774 4 : if (mOnloadBlockCount != 0) {
4775 8 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4776 4 : if (loadGroup) {
4777 4 : loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4778 : }
4779 : }
4780 :
4781 : using mozilla::dom::workers::ServiceWorkerManager;
4782 8 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
4783 4 : if (swm) {
4784 8 : ErrorResult error;
4785 4 : if (swm->IsControlled(this, error)) {
4786 0 : imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this);
4787 0 : if (loader) {
4788 0 : loader->ClearCacheForControlledDocument(this);
4789 : }
4790 :
4791 : // We may become controlled again if this document comes back out
4792 : // of bfcache. Clear our state to allow that to happen. Only
4793 : // clear this flag if we are actually controlled, though, so pages
4794 : // that were force reloaded don't become controlled when they
4795 : // come out of bfcache.
4796 0 : mMaybeServiceWorkerControlled = false;
4797 : }
4798 4 : swm->MaybeStopControlling(this);
4799 : }
4800 :
4801 : // Remove ourself from the list of clients. We only register
4802 : // content principal documents in this list.
4803 6 : if (!nsContentUtils::IsSystemPrincipal(GetPrincipal()) &&
4804 2 : !GetPrincipal()->GetIsNullPrincipal()) {
4805 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
4806 0 : if (os) {
4807 0 : os->RemoveObserver(this, "service-worker-get-client");
4808 : }
4809 : }
4810 :
4811 44 : } else if (!mScriptGlobalObject && aScriptGlobalObject &&
4812 20 : mDocumentContainer && GetChannel() &&
4813 18 : !nsContentUtils::IsSystemPrincipal(GetPrincipal()) &&
4814 2 : !GetPrincipal()->GetIsNullPrincipal()) {
4815 : // This document is being activated. Register it in the list of
4816 : // clients. We only do this for content principal documents
4817 : // since we can never observe system or null principals.
4818 4 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
4819 2 : if (os) {
4820 2 : os->AddObserver(this, "service-worker-get-client", /* ownsWeak */ false);
4821 : }
4822 : }
4823 :
4824 : // BlockOnload() might be called before mScriptGlobalObject is set.
4825 : // We may need to add the blocker once mScriptGlobalObject is set.
4826 16 : bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
4827 :
4828 16 : mScriptGlobalObject = aScriptGlobalObject;
4829 :
4830 16 : if (needOnloadBlocker) {
4831 8 : EnsureOnloadBlocker();
4832 : }
4833 :
4834 16 : UpdateFrameRequestCallbackSchedulingState();
4835 :
4836 16 : if (aScriptGlobalObject) {
4837 : // Go back to using the docshell for the layout history state
4838 8 : mLayoutHistoryState = nullptr;
4839 8 : SetScopeObject(aScriptGlobalObject);
4840 8 : mHasHadDefaultView = true;
4841 : #ifdef DEBUG
4842 8 : if (!mWillReparent) {
4843 : // We really shouldn't have a wrapper here but if we do we need to make sure
4844 : // it has the correct parent.
4845 8 : JSObject *obj = GetWrapperPreserveColor();
4846 8 : if (obj) {
4847 0 : JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
4848 0 : NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
4849 : "Wrong scope, this is really bad!");
4850 : }
4851 : }
4852 : #endif
4853 :
4854 8 : if (mAllowDNSPrefetch) {
4855 8 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4856 4 : if (docShell) {
4857 : #ifdef DEBUG
4858 : nsCOMPtr<nsIWebNavigation> webNav =
4859 8 : do_GetInterface(aScriptGlobalObject);
4860 4 : NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4861 : "Unexpected container or script global?");
4862 : #endif
4863 : bool allowDNSPrefetch;
4864 4 : docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4865 4 : mAllowDNSPrefetch = allowDNSPrefetch;
4866 : }
4867 : }
4868 :
4869 : // If we are set in a window that is already focused we should remember this
4870 : // as the time the document gained focus.
4871 8 : bool focused = false;
4872 8 : Unused << HasFocus(&focused);
4873 8 : if (focused) {
4874 1 : SetLastFocusTime(TimeStamp::Now());
4875 : }
4876 : }
4877 :
4878 : // Remember the pointer to our window (or lack there of), to avoid
4879 : // having to QI every time it's asked for.
4880 32 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
4881 16 : mWindow = window;
4882 :
4883 : // Now that we know what our window is, we can flush the CSP errors to the
4884 : // Web Console. We are flushing all messages that occured and were stored
4885 : // in the queue prior to this point.
4886 32 : nsCOMPtr<nsIContentSecurityPolicy> csp;
4887 16 : NodePrincipal()->GetCsp(getter_AddRefs(csp));
4888 16 : if (csp) {
4889 0 : static_cast<nsCSPContext*>(csp.get())->flushConsoleMessages();
4890 : }
4891 :
4892 : nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4893 32 : do_QueryInterface(GetChannel());
4894 16 : if (internalChannel) {
4895 2 : nsCOMArray<nsISecurityConsoleMessage> messages;
4896 2 : DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
4897 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
4898 1 : SendToConsole(messages);
4899 : }
4900 :
4901 : // Set our visibility state, but do not fire the event. This is correct
4902 : // because either we're coming out of bfcache (in which case IsVisible() will
4903 : // still test false at this point and no state change will happen) or we're
4904 : // doing the initial document load and don't want to fire the event for this
4905 : // change.
4906 16 : dom::VisibilityState oldState = mVisibilityState;
4907 16 : mVisibilityState = GetVisibilityState();
4908 : // When the visibility is changed, notify it to observers.
4909 : // Some observers need the notification, for example HTMLMediaElement uses
4910 : // it to update internal media resource allocation.
4911 : // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
4912 : // creation are already done before nsDocument::SetScriptGlobalObject() call.
4913 : // MediaDecoder decides whether starting decoding is decided based on
4914 : // document's visibility. When the MediaDecoder is created,
4915 : // nsDocument::SetScriptGlobalObject() is not yet called and document is
4916 : // hidden state. Therefore the MediaDecoder decides that decoding is
4917 : // not yet necessary. But soon after nsDocument::SetScriptGlobalObject()
4918 : // call, the document becomes not hidden. At the time, MediaDecoder needs
4919 : // to know it and needs to start updating decoding.
4920 16 : if (oldState != mVisibilityState) {
4921 8 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4922 : }
4923 :
4924 : // The global in the template contents owner document should be the same.
4925 16 : if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
4926 0 : mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
4927 : }
4928 :
4929 16 : if (!mMaybeServiceWorkerControlled && mDocumentContainer && mScriptGlobalObject && GetChannel()) {
4930 8 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4931 : uint32_t loadType;
4932 4 : docShell->GetLoadType(&loadType);
4933 :
4934 : // If we are shift-reloaded, don't associate with a ServiceWorker.
4935 4 : if (IsForceReloadType(loadType)) {
4936 0 : NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
4937 0 : return;
4938 : }
4939 :
4940 8 : nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
4941 4 : if (swm) {
4942 : // If this document is being resurrected from the bfcache, then we may
4943 : // already have a document ID. In that case reuse the same ID. Otherwise
4944 : // get our document ID from the docshell.
4945 8 : nsString documentId(GetId());
4946 4 : if (documentId.IsEmpty()) {
4947 4 : static_cast<nsDocShell*>(docShell.get())->GetInterceptedDocumentId(documentId);
4948 : }
4949 :
4950 4 : swm->MaybeStartControlling(this, documentId);
4951 4 : mMaybeServiceWorkerControlled = true;
4952 : }
4953 : }
4954 : }
4955 :
4956 : nsIScriptGlobalObject*
4957 88 : nsDocument::GetScriptHandlingObjectInternal() const
4958 : {
4959 88 : MOZ_ASSERT(!mScriptGlobalObject,
4960 : "Do not call this when mScriptGlobalObject is set!");
4961 88 : if (mHasHadDefaultView) {
4962 0 : return nullptr;
4963 : }
4964 :
4965 : nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4966 176 : do_QueryReferent(mScopeObject);
4967 176 : nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject);
4968 88 : if (win) {
4969 0 : nsPIDOMWindowOuter* outer = win->GetOuterWindow();
4970 0 : if (!outer || outer->GetCurrentInnerWindow() != win) {
4971 0 : NS_WARNING("Wrong inner/outer window combination!");
4972 0 : return nullptr;
4973 : }
4974 : }
4975 88 : return scriptHandlingObject;
4976 : }
4977 : void
4978 0 : nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
4979 : {
4980 0 : NS_ASSERTION(!mScriptGlobalObject ||
4981 : mScriptGlobalObject == aScriptObject,
4982 : "Wrong script object!");
4983 0 : if (aScriptObject) {
4984 0 : SetScopeObject(aScriptObject);
4985 0 : mHasHadDefaultView = false;
4986 : }
4987 0 : }
4988 :
4989 : nsPIDOMWindowOuter*
4990 334 : nsDocument::GetWindowInternal() const
4991 : {
4992 334 : MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4993 : // Let's use mScriptGlobalObject. Even if the document is already removed from
4994 : // the docshell, the outer window might be still obtainable from the it.
4995 668 : nsCOMPtr<nsPIDOMWindowOuter> win;
4996 334 : if (mRemovedFromDocShell) {
4997 : // The docshell returns the outer window we are done.
4998 0 : nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer);
4999 0 : if (kungFuDeathGrip) {
5000 0 : win = kungFuDeathGrip->GetWindow();
5001 : }
5002 : } else {
5003 668 : if (nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(mScriptGlobalObject)) {
5004 : // mScriptGlobalObject is always the inner window, let's get the outer.
5005 0 : win = inner->GetOuterWindow();
5006 : }
5007 : }
5008 :
5009 668 : return win;
5010 : }
5011 :
5012 : ScriptLoader*
5013 81 : nsDocument::ScriptLoader()
5014 : {
5015 81 : return mScriptLoader;
5016 : }
5017 :
5018 : bool
5019 25 : nsDocument::InternalAllowXULXBL()
5020 : {
5021 25 : if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
5022 21 : mAllowXULXBL = eTriTrue;
5023 21 : return true;
5024 : }
5025 :
5026 4 : mAllowXULXBL = eTriFalse;
5027 4 : return false;
5028 : }
5029 :
5030 : // Note: We don't hold a reference to the document observer; we assume
5031 : // that it has a live reference to the document.
5032 : void
5033 76 : nsDocument::AddObserver(nsIDocumentObserver* aObserver)
5034 : {
5035 76 : NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
5036 : "Observer already in the list");
5037 76 : mObservers.AppendElement(aObserver);
5038 76 : AddMutationObserver(aObserver);
5039 76 : }
5040 :
5041 : bool
5042 50 : nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
5043 : {
5044 : // If we're in the process of destroying the document (and we're
5045 : // informing the observers of the destruction), don't remove the
5046 : // observers from the list. This is not a big deal, since we
5047 : // don't hold a live reference to the observers.
5048 50 : if (!mInDestructor) {
5049 50 : RemoveMutationObserver(aObserver);
5050 50 : return mObservers.RemoveElement(aObserver);
5051 : }
5052 :
5053 0 : return mObservers.Contains(aObserver);
5054 : }
5055 :
5056 : void
5057 792 : nsDocument::MaybeEndOutermostXBLUpdate()
5058 : {
5059 : // Only call BindingManager()->EndOutermostUpdate() when
5060 : // we're not in an update and it is safe to run scripts.
5061 792 : if (mUpdateNestLevel == 0 && mInXBLUpdate) {
5062 515 : if (nsContentUtils::IsSafeToRunScript()) {
5063 459 : mInXBLUpdate = false;
5064 459 : BindingManager()->EndOutermostUpdate();
5065 56 : } else if (!mInDestructor) {
5066 56 : if (!mMaybeEndOutermostXBLUpdateRunner) {
5067 : mMaybeEndOutermostXBLUpdateRunner =
5068 62 : NewRunnableMethod("nsDocument::MaybeEndOutermostXBLUpdate",
5069 : this,
5070 31 : &nsDocument::MaybeEndOutermostXBLUpdate);
5071 : }
5072 56 : nsContentUtils::AddScriptRunner(mMaybeEndOutermostXBLUpdateRunner);
5073 : }
5074 : }
5075 792 : }
5076 :
5077 : void
5078 736 : nsDocument::BeginUpdate(nsUpdateType aUpdateType)
5079 : {
5080 : // If the document is going away, then it's probably okay to do things to it
5081 : // in the wrong DocGroup. We're unlikely to run JS or do anything else
5082 : // observable at this point. We reach this point when cycle collecting a
5083 : // <link> element and the unlink code removes a style sheet.
5084 736 : if (mDocGroup && !mIsGoingAway && !mInUnlinkOrDeletion && !mIgnoreDocGroupMismatches) {
5085 454 : mDocGroup->ValidateAccess();
5086 : }
5087 :
5088 736 : if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
5089 459 : mInXBLUpdate = true;
5090 459 : BindingManager()->BeginOutermostUpdate();
5091 : }
5092 :
5093 736 : ++mUpdateNestLevel;
5094 736 : nsContentUtils::AddScriptBlocker();
5095 736 : NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
5096 736 : }
5097 :
5098 : void
5099 736 : nsDocument::EndUpdate(nsUpdateType aUpdateType)
5100 : {
5101 736 : NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
5102 :
5103 736 : nsContentUtils::RemoveScriptBlocker();
5104 :
5105 736 : --mUpdateNestLevel;
5106 :
5107 : // This set of updates may have created XBL bindings. Let the
5108 : // binding manager know we're done.
5109 736 : MaybeEndOutermostXBLUpdate();
5110 :
5111 736 : MaybeInitializeFinalizeFrameLoaders();
5112 736 : }
5113 :
5114 : void
5115 25 : nsDocument::BeginLoad()
5116 : {
5117 : // Block onload here to prevent having to deal with blocking and
5118 : // unblocking it while we know the document is loading.
5119 25 : BlockOnload();
5120 25 : mDidFireDOMContentLoaded = false;
5121 25 : BlockDOMContentLoaded();
5122 :
5123 25 : if (mScriptLoader) {
5124 25 : mScriptLoader->BeginDeferringScripts();
5125 : }
5126 :
5127 25 : NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
5128 25 : }
5129 :
5130 : void
5131 0 : nsDocument::ReportEmptyGetElementByIdArg()
5132 : {
5133 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
5134 0 : NS_LITERAL_CSTRING("DOM"), this,
5135 : nsContentUtils::eDOM_PROPERTIES,
5136 0 : "EmptyGetElementByIdParam");
5137 0 : }
5138 :
5139 : Element*
5140 25 : nsDocument::GetElementById(const nsAString& aElementId)
5141 : {
5142 25 : if (!CheckGetElementByIdArg(aElementId)) {
5143 0 : return nullptr;
5144 : }
5145 :
5146 25 : nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
5147 25 : return entry ? entry->GetIdElement() : nullptr;
5148 : }
5149 :
5150 : const nsTArray<Element*>*
5151 3 : nsDocument::GetAllElementsForId(const nsAString& aElementId) const
5152 : {
5153 3 : if (aElementId.IsEmpty()) {
5154 0 : return nullptr;
5155 : }
5156 :
5157 3 : nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
5158 3 : return entry ? &entry->GetIdElements() : nullptr;
5159 : }
5160 :
5161 : NS_IMETHODIMP
5162 9 : nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
5163 : {
5164 9 : Element *content = GetElementById(aId);
5165 9 : if (content) {
5166 9 : return CallQueryInterface(content, aReturn);
5167 : }
5168 :
5169 0 : *aReturn = nullptr;
5170 :
5171 0 : return NS_OK;
5172 : }
5173 :
5174 : Element*
5175 25 : nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
5176 : void* aData, bool aForImage)
5177 : {
5178 50 : nsDependentAtomString id(aID);
5179 :
5180 25 : if (!CheckGetElementByIdArg(id))
5181 0 : return nullptr;
5182 :
5183 25 : nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aID);
5184 25 : NS_ENSURE_TRUE(entry, nullptr);
5185 :
5186 25 : entry->AddContentChangeCallback(aObserver, aData, aForImage);
5187 25 : return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
5188 : }
5189 :
5190 : void
5191 0 : nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
5192 : void* aData, bool aForImage)
5193 : {
5194 0 : nsDependentAtomString id(aID);
5195 :
5196 0 : if (!CheckGetElementByIdArg(id))
5197 0 : return;
5198 :
5199 0 : nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aID);
5200 0 : if (!entry) {
5201 0 : return;
5202 : }
5203 :
5204 0 : entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
5205 : }
5206 :
5207 : NS_IMETHODIMP
5208 0 : nsDocument::MozSetImageElement(const nsAString& aImageElementId,
5209 : nsIDOMElement* aImageElement)
5210 : {
5211 0 : nsCOMPtr<Element> el = do_QueryInterface(aImageElement);
5212 0 : MozSetImageElement(aImageElementId, el);
5213 0 : return NS_OK;
5214 : }
5215 :
5216 : void
5217 0 : nsDocument::MozSetImageElement(const nsAString& aImageElementId,
5218 : Element* aElement)
5219 : {
5220 0 : if (aImageElementId.IsEmpty())
5221 0 : return;
5222 :
5223 : // Hold a script blocker while calling SetImageElement since that can call
5224 : // out to id-observers
5225 0 : nsAutoScriptBlocker scriptBlocker;
5226 :
5227 0 : nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId);
5228 0 : if (entry) {
5229 0 : entry->SetImageElement(aElement);
5230 0 : if (entry->IsEmpty()) {
5231 0 : mIdentifierMap.RemoveEntry(entry);
5232 : }
5233 : }
5234 : }
5235 :
5236 : Element*
5237 0 : nsDocument::LookupImageElement(const nsAString& aId)
5238 : {
5239 0 : if (aId.IsEmpty())
5240 0 : return nullptr;
5241 :
5242 0 : nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
5243 0 : return entry ? entry->GetImageIdElement() : nullptr;
5244 : }
5245 :
5246 : void
5247 26 : nsDocument::DispatchContentLoadedEvents()
5248 : {
5249 : // If you add early returns from this method, make sure you're
5250 : // calling UnblockOnload properly.
5251 :
5252 : // Unpin references to preloaded images
5253 26 : mPreloadingImages.Clear();
5254 :
5255 : // DOM manipulation after content loaded should not care if the element
5256 : // came from the preloader.
5257 26 : mPreloadedPreconnects.Clear();
5258 :
5259 26 : if (mTiming) {
5260 25 : mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
5261 : }
5262 :
5263 : // Dispatch observer notification to notify observers document is interactive.
5264 52 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
5265 26 : if (os) {
5266 26 : nsIPrincipal *principal = GetPrincipal();
5267 52 : os->NotifyObservers(static_cast<nsIDocument*>(this),
5268 26 : nsContentUtils::IsSystemPrincipal(principal) ?
5269 : "chrome-document-interactive" :
5270 : "content-document-interactive",
5271 52 : nullptr);
5272 : }
5273 :
5274 : // Fire a DOM event notifying listeners that this document has been
5275 : // loaded (excluding images and other loads initiated by this
5276 : // document).
5277 26 : nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
5278 52 : NS_LITERAL_STRING("DOMContentLoaded"),
5279 52 : true, false);
5280 :
5281 52 : RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
5282 26 : nsIDocShell* docShell = this->GetDocShell();
5283 :
5284 26 : if (timelines && timelines->HasConsumer(docShell)) {
5285 0 : timelines->AddMarkerForDocShell(docShell,
5286 0 : MakeUnique<DocLoadingTimelineMarker>("document::DOMContentLoaded"));
5287 : }
5288 :
5289 26 : if (mTiming) {
5290 25 : mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
5291 : }
5292 :
5293 : // If this document is a [i]frame, fire a DOMFrameContentLoaded
5294 : // event on all parent documents notifying that the HTML (excluding
5295 : // other external files such as images and stylesheets) in a frame
5296 : // has finished loading.
5297 :
5298 : // target_frame is the [i]frame element that will be used as the
5299 : // target for the event. It's the [i]frame whose content is done
5300 : // loading.
5301 52 : nsCOMPtr<EventTarget> target_frame;
5302 :
5303 26 : if (mParentDocument) {
5304 1 : target_frame = mParentDocument->FindContentForSubDocument(this);
5305 : }
5306 :
5307 26 : if (target_frame) {
5308 2 : nsCOMPtr<nsIDocument> parent = mParentDocument;
5309 1 : do {
5310 2 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent);
5311 :
5312 2 : nsCOMPtr<nsIDOMEvent> event;
5313 1 : if (domDoc) {
5314 4 : domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
5315 4 : getter_AddRefs(event));
5316 :
5317 : }
5318 :
5319 1 : if (event) {
5320 4 : event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
5321 3 : true);
5322 :
5323 1 : event->SetTarget(target_frame);
5324 1 : event->SetTrusted(true);
5325 :
5326 : // To dispatch this event we must manually call
5327 : // EventDispatcher::Dispatch() on the ancestor document since the
5328 : // target is not in the same document, so the event would never reach
5329 : // the ancestor document if we used the normal event
5330 : // dispatching code.
5331 :
5332 1 : WidgetEvent* innerEvent = event->WidgetEventPtr();
5333 1 : if (innerEvent) {
5334 1 : nsEventStatus status = nsEventStatus_eIgnore;
5335 :
5336 1 : nsIPresShell *shell = parent->GetShell();
5337 1 : if (shell) {
5338 2 : RefPtr<nsPresContext> context = shell->GetPresContext();
5339 :
5340 1 : if (context) {
5341 1 : EventDispatcher::Dispatch(parent, context, innerEvent, event,
5342 1 : &status);
5343 : }
5344 : }
5345 : }
5346 : }
5347 :
5348 1 : parent = parent->GetParentDocument();
5349 : } while (parent);
5350 : }
5351 :
5352 : // If the document has a manifest attribute, fire a MozApplicationManifest
5353 : // event.
5354 26 : Element* root = GetRootElement();
5355 26 : if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
5356 0 : nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
5357 0 : NS_LITERAL_STRING("MozApplicationManifest"),
5358 0 : true, true);
5359 : }
5360 :
5361 26 : if (mMaybeServiceWorkerControlled) {
5362 : using mozilla::dom::workers::ServiceWorkerManager;
5363 8 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
5364 4 : if (swm) {
5365 4 : swm->MaybeCheckNavigationUpdate(this);
5366 : }
5367 : }
5368 :
5369 26 : UnblockOnload(true);
5370 26 : }
5371 :
5372 : void
5373 25 : nsDocument::EndLoad()
5374 : {
5375 : // Drop the ref to our parser, if any, but keep hold of the sink so that we
5376 : // can flush it from FlushPendingNotifications as needed. We might have to
5377 : // do that to get a StartLayout() to happen.
5378 25 : if (mParser) {
5379 25 : mWeakSink = do_GetWeakReference(mParser->GetContentSink());
5380 25 : mParser = nullptr;
5381 : }
5382 :
5383 25 : NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
5384 :
5385 25 : UnblockDOMContentLoaded();
5386 25 : }
5387 :
5388 : void
5389 25 : nsDocument::UnblockDOMContentLoaded()
5390 : {
5391 25 : MOZ_ASSERT(mBlockDOMContentLoaded);
5392 25 : if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
5393 0 : return;
5394 : }
5395 25 : mDidFireDOMContentLoaded = true;
5396 :
5397 25 : MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
5398 25 : if (!mSynchronousDOMContentLoaded) {
5399 24 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
5400 : nsCOMPtr<nsIRunnable> ev =
5401 48 : NewRunnableMethod("nsDocument::DispatchContentLoadedEvents",
5402 : this,
5403 48 : &nsDocument::DispatchContentLoadedEvents);
5404 24 : Dispatch("nsDocument::DispatchContentLoadedEvents", TaskCategory::Other, ev.forget());
5405 : } else {
5406 1 : DispatchContentLoadedEvents();
5407 : }
5408 : }
5409 :
5410 : void
5411 55 : nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
5412 : {
5413 55 : NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
5414 : "Someone forgot a scriptblocker");
5415 55 : NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
5416 : (this, aContent, aStateMask));
5417 55 : }
5418 :
5419 : void
5420 5 : nsDocument::DocumentStatesChanged(EventStates aStateMask)
5421 : {
5422 : // Invalidate our cached state.
5423 5 : mGotDocumentState &= ~aStateMask;
5424 5 : mDocumentState &= ~aStateMask;
5425 :
5426 5 : NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
5427 5 : }
5428 :
5429 : void
5430 0 : nsDocument::StyleRuleChanged(StyleSheet* aSheet,
5431 : css::Rule* aStyleRule)
5432 : {
5433 0 : NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (aSheet, aStyleRule));
5434 :
5435 0 : if (StyleSheetChangeEventsEnabled()) {
5436 0 : DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5437 : "StyleRuleChanged",
5438 : mRule,
5439 : aStyleRule);
5440 : }
5441 0 : }
5442 :
5443 : void
5444 0 : nsDocument::StyleRuleAdded(StyleSheet* aSheet,
5445 : css::Rule* aStyleRule)
5446 : {
5447 0 : NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (aSheet, aStyleRule));
5448 :
5449 0 : if (StyleSheetChangeEventsEnabled()) {
5450 0 : DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5451 : "StyleRuleAdded",
5452 : mRule,
5453 : aStyleRule);
5454 : }
5455 0 : }
5456 :
5457 : void
5458 0 : nsDocument::StyleRuleRemoved(StyleSheet* aSheet,
5459 : css::Rule* aStyleRule)
5460 : {
5461 0 : NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (aSheet, aStyleRule));
5462 :
5463 0 : if (StyleSheetChangeEventsEnabled()) {
5464 0 : DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5465 : "StyleRuleRemoved",
5466 : mRule,
5467 : aStyleRule);
5468 : }
5469 0 : }
5470 :
5471 : #undef DO_STYLESHEET_NOTIFICATION
5472 :
5473 : already_AddRefed<AnonymousContent>
5474 0 : nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
5475 : {
5476 0 : nsIPresShell* shell = GetShell();
5477 0 : if (!shell || !shell->GetCanvasFrame()) {
5478 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5479 0 : return nullptr;
5480 : }
5481 :
5482 0 : nsAutoScriptBlocker scriptBlocker;
5483 0 : nsCOMPtr<Element> container = shell->GetCanvasFrame()
5484 0 : ->GetCustomContentContainer();
5485 0 : if (!container) {
5486 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5487 0 : return nullptr;
5488 : }
5489 :
5490 : // Clone the node to avoid returning a direct reference
5491 0 : nsCOMPtr<nsINode> clonedElement = aElement.CloneNode(true, aRv);
5492 0 : if (aRv.Failed()) {
5493 0 : return nullptr;
5494 : }
5495 :
5496 : // Insert the element into the container
5497 : nsresult rv;
5498 0 : rv = container->AppendChildTo(clonedElement->AsContent(), true);
5499 0 : if (NS_FAILED(rv)) {
5500 0 : return nullptr;
5501 : }
5502 :
5503 : RefPtr<AnonymousContent> anonymousContent =
5504 0 : new AnonymousContent(clonedElement->AsElement());
5505 0 : mAnonymousContents.AppendElement(anonymousContent);
5506 :
5507 0 : shell->GetCanvasFrame()->ShowCustomContentContainer();
5508 :
5509 0 : return anonymousContent.forget();
5510 : }
5511 :
5512 : void
5513 0 : nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
5514 : ErrorResult& aRv)
5515 : {
5516 0 : nsIPresShell* shell = GetShell();
5517 0 : if (!shell || !shell->GetCanvasFrame()) {
5518 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5519 0 : return;
5520 : }
5521 :
5522 0 : nsAutoScriptBlocker scriptBlocker;
5523 0 : nsCOMPtr<Element> container = shell->GetCanvasFrame()
5524 0 : ->GetCustomContentContainer();
5525 0 : if (!container) {
5526 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
5527 0 : return;
5528 : }
5529 :
5530 : // Iterate over mAnonymousContents to find and remove the given node.
5531 0 : for (size_t i = 0, len = mAnonymousContents.Length(); i < len; ++i) {
5532 0 : if (mAnonymousContents[i] == &aContent) {
5533 : // Get the node from the customContent
5534 0 : nsCOMPtr<Element> node = aContent.GetContentNode();
5535 :
5536 : // Remove the entry in mAnonymousContents
5537 0 : mAnonymousContents.RemoveElementAt(i);
5538 :
5539 : // Remove the node from its container
5540 0 : container->RemoveChild(*node, aRv);
5541 0 : if (aRv.Failed()) {
5542 0 : return;
5543 : }
5544 :
5545 0 : break;
5546 : }
5547 : }
5548 0 : if (mAnonymousContents.IsEmpty()) {
5549 0 : shell->GetCanvasFrame()->HideCustomContentContainer();
5550 : }
5551 : }
5552 :
5553 : Element*
5554 0 : nsIDocument::GetAnonRootIfInAnonymousContentContainer(nsINode* aNode) const
5555 : {
5556 0 : if (!aNode->IsInNativeAnonymousSubtree()) {
5557 0 : return nullptr;
5558 : }
5559 :
5560 0 : nsIPresShell* shell = GetShell();
5561 0 : if (!shell || !shell->GetCanvasFrame()) {
5562 0 : return nullptr;
5563 : }
5564 :
5565 0 : nsAutoScriptBlocker scriptBlocker;
5566 0 : nsCOMPtr<Element> customContainer = shell->GetCanvasFrame()
5567 0 : ->GetCustomContentContainer();
5568 0 : if (!customContainer) {
5569 0 : return nullptr;
5570 : }
5571 :
5572 : // An arbitrary number of elements can be inserted as children of the custom
5573 : // container frame. We want the one that was added that contains aNode, so
5574 : // we need to keep track of the last child separately using |child| here.
5575 0 : nsINode* child = aNode;
5576 0 : nsINode* parent = aNode->GetParentNode();
5577 0 : while (parent && parent->IsInNativeAnonymousSubtree()) {
5578 0 : if (parent == customContainer) {
5579 0 : return child->IsElement() ? child->AsElement() : nullptr;
5580 : }
5581 0 : child = parent;
5582 0 : parent = child->GetParentNode();
5583 : }
5584 0 : return nullptr;
5585 : }
5586 :
5587 : //
5588 : // nsIDOMDocument interface
5589 : //
5590 : DocumentType*
5591 0 : nsIDocument::GetDoctype() const
5592 : {
5593 0 : for (nsIContent* child = GetFirstChild();
5594 0 : child;
5595 0 : child = child->GetNextSibling()) {
5596 0 : if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
5597 0 : return static_cast<DocumentType*>(child);
5598 : }
5599 : }
5600 0 : return nullptr;
5601 : }
5602 :
5603 : NS_IMETHODIMP
5604 0 : nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
5605 : {
5606 0 : MOZ_ASSERT(aDoctype);
5607 0 : nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype();
5608 0 : doctype.forget(aDoctype);
5609 0 : return NS_OK;
5610 : }
5611 :
5612 : NS_IMETHODIMP
5613 0 : nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
5614 : {
5615 0 : ErrorResult rv;
5616 0 : *aImplementation = GetImplementation(rv);
5617 0 : if (rv.Failed()) {
5618 0 : MOZ_ASSERT(!*aImplementation);
5619 0 : return rv.StealNSResult();
5620 : }
5621 0 : NS_ADDREF(*aImplementation);
5622 0 : return NS_OK;
5623 : }
5624 :
5625 : DOMImplementation*
5626 0 : nsDocument::GetImplementation(ErrorResult& rv)
5627 : {
5628 0 : if (!mDOMImplementation) {
5629 0 : nsCOMPtr<nsIURI> uri;
5630 0 : NS_NewURI(getter_AddRefs(uri), "about:blank");
5631 0 : if (!uri) {
5632 0 : rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5633 0 : return nullptr;
5634 : }
5635 0 : bool hasHadScriptObject = true;
5636 : nsIScriptGlobalObject* scriptObject =
5637 0 : GetScriptHandlingObject(hasHadScriptObject);
5638 0 : if (!scriptObject && hasHadScriptObject) {
5639 0 : rv.Throw(NS_ERROR_UNEXPECTED);
5640 0 : return nullptr;
5641 : }
5642 : mDOMImplementation = new DOMImplementation(this,
5643 0 : scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5644 : }
5645 :
5646 0 : return mDOMImplementation;
5647 : }
5648 :
5649 : NS_IMETHODIMP
5650 2 : nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
5651 : {
5652 2 : NS_ENSURE_ARG_POINTER(aDocumentElement);
5653 :
5654 2 : Element* root = GetRootElement();
5655 2 : if (root) {
5656 2 : return CallQueryInterface(root, aDocumentElement);
5657 : }
5658 :
5659 0 : *aDocumentElement = nullptr;
5660 :
5661 0 : return NS_OK;
5662 : }
5663 :
5664 : NS_IMETHODIMP
5665 0 : nsDocument::CreateElement(const nsAString& aTagName,
5666 : nsIDOMElement** aReturn)
5667 : {
5668 0 : *aReturn = nullptr;
5669 0 : ErrorResult rv;
5670 0 : ElementCreationOptionsOrString options;
5671 :
5672 0 : options.SetAsString();
5673 0 : nsCOMPtr<Element> element = CreateElement(aTagName, options, rv);
5674 0 : NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
5675 0 : return CallQueryInterface(element, aReturn);
5676 : }
5677 :
5678 0 : bool IsLowercaseASCII(const nsAString& aValue)
5679 : {
5680 0 : int32_t len = aValue.Length();
5681 0 : for (int32_t i = 0; i < len; ++i) {
5682 0 : char16_t c = aValue[i];
5683 0 : if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5684 0 : return false;
5685 : }
5686 : }
5687 0 : return true;
5688 : }
5689 :
5690 : already_AddRefed<mozilla::dom::CustomElementRegistry>
5691 0 : nsDocument::GetCustomElementRegistry()
5692 : {
5693 0 : nsAutoString contentType;
5694 0 : GetContentType(contentType);
5695 0 : if (!IsHTMLDocument() &&
5696 0 : !contentType.EqualsLiteral("application/xhtml+xml")) {
5697 0 : return nullptr;
5698 : }
5699 :
5700 : nsCOMPtr<nsPIDOMWindowInner> window(
5701 : do_QueryInterface(mScriptGlobalObject ? mScriptGlobalObject
5702 0 : : GetScopeObject()));
5703 0 : if (!window) {
5704 0 : return nullptr;
5705 : }
5706 :
5707 0 : RefPtr<CustomElementRegistry> registry = window->CustomElements();
5708 0 : if (!registry) {
5709 0 : return nullptr;
5710 : }
5711 :
5712 0 : return registry.forget();
5713 : }
5714 :
5715 : // We only support pseudo-elements with two colons in this function.
5716 : static CSSPseudoElementType
5717 0 : GetPseudoElementType(const nsString& aString, ErrorResult& aRv)
5718 : {
5719 0 : MOZ_ASSERT(!aString.IsEmpty(), "GetPseudoElementType aString should be non-null");
5720 0 : if (aString.Length() <= 2 || aString[0] != ':' || aString[1] != ':') {
5721 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5722 0 : return CSSPseudoElementType::NotPseudo;
5723 : }
5724 0 : nsCOMPtr<nsIAtom> pseudo = NS_Atomize(Substring(aString, 1));
5725 0 : return nsCSSPseudoElements::GetPseudoType(pseudo,
5726 0 : nsCSSProps::EnabledState::eInUASheets);
5727 : }
5728 :
5729 : already_AddRefed<Element>
5730 48 : nsDocument::CreateElement(const nsAString& aTagName,
5731 : const ElementCreationOptionsOrString& aOptions,
5732 : ErrorResult& rv)
5733 : {
5734 48 : rv = nsContentUtils::CheckQName(aTagName, false);
5735 48 : if (rv.Failed()) {
5736 0 : return nullptr;
5737 : }
5738 :
5739 48 : bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
5740 96 : nsAutoString lcTagName;
5741 48 : if (needsLowercase) {
5742 0 : nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5743 : }
5744 :
5745 48 : const nsString* is = nullptr;
5746 48 : CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
5747 48 : if (aOptions.IsElementCreationOptions()) {
5748 : const ElementCreationOptions& options =
5749 48 : aOptions.GetAsElementCreationOptions();
5750 :
5751 48 : if (CustomElementRegistry::IsCustomElementEnabled() &&
5752 0 : options.mIs.WasPassed()) {
5753 0 : is = &options.mIs.Value();
5754 : }
5755 :
5756 : // Check 'pseudo' and throw an exception if it's not one allowed
5757 : // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
5758 48 : if (options.mPseudo.WasPassed()) {
5759 0 : pseudoType = GetPseudoElementType(options.mPseudo.Value(), rv);
5760 0 : if (rv.Failed() ||
5761 0 : pseudoType == CSSPseudoElementType::NotPseudo ||
5762 0 : !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType)) {
5763 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5764 0 : return nullptr;
5765 : }
5766 : }
5767 : }
5768 :
5769 96 : RefPtr<Element> elem = CreateElem(
5770 144 : needsLowercase ? lcTagName : aTagName, nullptr, mDefaultElementType, is);
5771 :
5772 48 : if (pseudoType != CSSPseudoElementType::NotPseudo) {
5773 0 : elem->SetPseudoElementType(pseudoType);
5774 : }
5775 :
5776 48 : return elem.forget();
5777 : }
5778 :
5779 : NS_IMETHODIMP
5780 0 : nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5781 : const nsAString& aQualifiedName,
5782 : nsIDOMElement** aReturn)
5783 : {
5784 0 : *aReturn = nullptr;
5785 0 : ElementCreationOptionsOrString options;
5786 :
5787 0 : options.SetAsString();
5788 0 : ErrorResult rv;
5789 : nsCOMPtr<Element> element =
5790 0 : CreateElementNS(aNamespaceURI, aQualifiedName, options, rv);
5791 0 : NS_ENSURE_FALSE(rv.Failed(), rv.StealNSResult());
5792 0 : return CallQueryInterface(element, aReturn);
5793 : }
5794 :
5795 : already_AddRefed<Element>
5796 33 : nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5797 : const nsAString& aQualifiedName,
5798 : const ElementCreationOptionsOrString& aOptions,
5799 : ErrorResult& rv)
5800 : {
5801 66 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5802 33 : rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5803 : aQualifiedName,
5804 : mNodeInfoManager,
5805 : nsIDOMNode::ELEMENT_NODE,
5806 66 : getter_AddRefs(nodeInfo));
5807 33 : if (rv.Failed()) {
5808 0 : return nullptr;
5809 : }
5810 :
5811 33 : const nsString* is = nullptr;
5812 33 : if (CustomElementRegistry::IsCustomElementEnabled() &&
5813 0 : aOptions.IsElementCreationOptions()) {
5814 0 : const ElementCreationOptions& options = aOptions.GetAsElementCreationOptions();
5815 0 : if (options.mIs.WasPassed()) {
5816 0 : is = &options.mIs.Value();
5817 : }
5818 : }
5819 :
5820 66 : nsCOMPtr<Element> element;
5821 66 : rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5822 33 : NOT_FROM_PARSER, is);
5823 33 : if (rv.Failed()) {
5824 0 : return nullptr;
5825 : }
5826 :
5827 33 : return element.forget();
5828 : }
5829 :
5830 : NS_IMETHODIMP
5831 0 : nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
5832 : {
5833 0 : *aReturn = nsIDocument::CreateTextNode(aData).take();
5834 0 : return NS_OK;
5835 : }
5836 :
5837 : already_AddRefed<nsTextNode>
5838 1 : nsIDocument::CreateTextNode(const nsAString& aData) const
5839 : {
5840 2 : RefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5841 : // Don't notify; this node is still being created.
5842 1 : text->SetText(aData, false);
5843 2 : return text.forget();
5844 : }
5845 :
5846 : NS_IMETHODIMP
5847 0 : nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
5848 : {
5849 0 : *aReturn = nsIDocument::CreateDocumentFragment().take();
5850 0 : return NS_OK;
5851 : }
5852 :
5853 : already_AddRefed<DocumentFragment>
5854 4 : nsIDocument::CreateDocumentFragment() const
5855 : {
5856 8 : RefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5857 8 : return frag.forget();
5858 : }
5859 :
5860 : NS_IMETHODIMP
5861 0 : nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
5862 : {
5863 0 : *aReturn = nsIDocument::CreateComment(aData).take();
5864 0 : return NS_OK;
5865 : }
5866 :
5867 : // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
5868 : already_AddRefed<dom::Comment>
5869 0 : nsIDocument::CreateComment(const nsAString& aData) const
5870 : {
5871 0 : RefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5872 :
5873 : // Don't notify; this node is still being created.
5874 0 : comment->SetText(aData, false);
5875 0 : return comment.forget();
5876 : }
5877 :
5878 : NS_IMETHODIMP
5879 0 : nsDocument::CreateCDATASection(const nsAString& aData,
5880 : nsIDOMCDATASection** aReturn)
5881 : {
5882 0 : NS_ENSURE_ARG_POINTER(aReturn);
5883 0 : ErrorResult rv;
5884 0 : *aReturn = nsIDocument::CreateCDATASection(aData, rv).take();
5885 0 : return rv.StealNSResult();
5886 : }
5887 :
5888 : already_AddRefed<CDATASection>
5889 0 : nsIDocument::CreateCDATASection(const nsAString& aData,
5890 : ErrorResult& rv)
5891 : {
5892 0 : if (IsHTMLDocument()) {
5893 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5894 0 : return nullptr;
5895 : }
5896 :
5897 0 : if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5898 0 : rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5899 0 : return nullptr;
5900 : }
5901 :
5902 0 : RefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5903 :
5904 : // Don't notify; this node is still being created.
5905 0 : cdata->SetText(aData, false);
5906 :
5907 0 : return cdata.forget();
5908 : }
5909 :
5910 : NS_IMETHODIMP
5911 0 : nsDocument::CreateProcessingInstruction(const nsAString& aTarget,
5912 : const nsAString& aData,
5913 : nsIDOMProcessingInstruction** aReturn)
5914 : {
5915 0 : ErrorResult rv;
5916 0 : *aReturn =
5917 0 : nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take();
5918 0 : return rv.StealNSResult();
5919 : }
5920 :
5921 : already_AddRefed<ProcessingInstruction>
5922 0 : nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5923 : const nsAString& aData,
5924 : ErrorResult& rv) const
5925 : {
5926 0 : nsresult res = nsContentUtils::CheckQName(aTarget, false);
5927 0 : if (NS_FAILED(res)) {
5928 0 : rv.Throw(res);
5929 0 : return nullptr;
5930 : }
5931 :
5932 0 : if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5933 0 : rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5934 0 : return nullptr;
5935 : }
5936 :
5937 : RefPtr<ProcessingInstruction> pi =
5938 0 : NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5939 :
5940 0 : return pi.forget();
5941 : }
5942 :
5943 : NS_IMETHODIMP
5944 0 : nsDocument::CreateAttribute(const nsAString& aName,
5945 : nsIDOMAttr** aReturn)
5946 : {
5947 0 : ErrorResult rv;
5948 0 : *aReturn = nsIDocument::CreateAttribute(aName, rv).take();
5949 0 : return rv.StealNSResult();
5950 : }
5951 :
5952 : already_AddRefed<Attr>
5953 0 : nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
5954 : {
5955 0 : WarnOnceAbout(eCreateAttribute);
5956 :
5957 0 : if (!mNodeInfoManager) {
5958 0 : rv.Throw(NS_ERROR_NOT_INITIALIZED);
5959 0 : return nullptr;
5960 : }
5961 :
5962 0 : nsresult res = nsContentUtils::CheckQName(aName, false);
5963 0 : if (NS_FAILED(res)) {
5964 0 : rv.Throw(res);
5965 0 : return nullptr;
5966 : }
5967 :
5968 0 : nsAutoString name;
5969 0 : if (IsHTMLDocument()) {
5970 0 : nsContentUtils::ASCIIToLower(aName, name);
5971 : } else {
5972 0 : name = aName;
5973 : }
5974 :
5975 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
5976 0 : res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None,
5977 : nsIDOMNode::ATTRIBUTE_NODE,
5978 0 : getter_AddRefs(nodeInfo));
5979 0 : if (NS_FAILED(res)) {
5980 0 : rv.Throw(res);
5981 0 : return nullptr;
5982 : }
5983 :
5984 0 : RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5985 0 : EmptyString());
5986 0 : return attribute.forget();
5987 : }
5988 :
5989 : NS_IMETHODIMP
5990 0 : nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI,
5991 : const nsAString & aQualifiedName,
5992 : nsIDOMAttr **aResult)
5993 : {
5994 0 : ErrorResult rv;
5995 0 : *aResult =
5996 0 : nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take();
5997 0 : return rv.StealNSResult();
5998 : }
5999 :
6000 : already_AddRefed<Attr>
6001 0 : nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
6002 : const nsAString& aQualifiedName,
6003 : ErrorResult& rv)
6004 : {
6005 0 : WarnOnceAbout(eCreateAttributeNS);
6006 :
6007 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
6008 0 : rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
6009 : aQualifiedName,
6010 : mNodeInfoManager,
6011 : nsIDOMNode::ATTRIBUTE_NODE,
6012 0 : getter_AddRefs(nodeInfo));
6013 0 : if (rv.Failed()) {
6014 0 : return nullptr;
6015 : }
6016 :
6017 0 : RefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
6018 0 : EmptyString());
6019 0 : return attribute.forget();
6020 : }
6021 :
6022 : bool
6023 0 : nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
6024 : {
6025 0 : JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
6026 :
6027 : JS::Rooted<JSObject*> global(aCx,
6028 0 : JS_GetGlobalForObject(aCx, &args.callee()));
6029 0 : RefPtr<nsGlobalWindow> window;
6030 0 : UNWRAP_OBJECT(Window, global, window);
6031 0 : MOZ_ASSERT(window, "Should have a non-null window");
6032 :
6033 0 : nsDocument* document = static_cast<nsDocument*>(window->GetDoc());
6034 :
6035 : // Function name is the type of the custom element.
6036 : JSString* jsFunName =
6037 0 : JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
6038 0 : nsAutoJSString elemName;
6039 0 : if (!elemName.init(aCx, jsFunName)) {
6040 0 : return true;
6041 : }
6042 :
6043 0 : RefPtr<mozilla::dom::CustomElementRegistry> registry = window->CustomElements();
6044 0 : if (!registry) {
6045 0 : return true;
6046 : }
6047 :
6048 0 : nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
6049 0 : CustomElementDefinition* definition = registry->mCustomDefinitions.Get(typeAtom);
6050 0 : if (!definition) {
6051 0 : return true;
6052 : }
6053 :
6054 0 : nsDependentAtomString localName(definition->mLocalName);
6055 :
6056 : nsCOMPtr<Element> element =
6057 0 : document->CreateElem(localName, nullptr, kNameSpaceID_XHTML);
6058 0 : NS_ENSURE_TRUE(element, true);
6059 :
6060 0 : if (definition->mLocalName != typeAtom) {
6061 : // This element is a custom element by extension, thus we need to
6062 : // do some special setup. For non-extended custom elements, this happens
6063 : // when the element is created.
6064 0 : nsContentUtils::SetupCustomElement(element, &elemName);
6065 : }
6066 :
6067 0 : nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval());
6068 0 : NS_ENSURE_SUCCESS(rv, true);
6069 :
6070 0 : return true;
6071 : }
6072 :
6073 : bool
6074 4 : nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
6075 : {
6076 4 : if (!nsContentUtils::IsWebComponentsEnabled()) {
6077 4 : return false;
6078 : }
6079 :
6080 0 : JS::Rooted<JSObject*> obj(aCx, aObject);
6081 :
6082 0 : JSAutoCompartment ac(aCx, obj);
6083 0 : JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
6084 : nsCOMPtr<nsPIDOMWindowInner> window =
6085 0 : do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
6086 :
6087 0 : nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
6088 0 : if (doc && doc->IsStyledByServo()) {
6089 0 : NS_WARNING("stylo: Web Components not supported yet");
6090 0 : return false;
6091 : }
6092 :
6093 0 : return true;
6094 : }
6095 :
6096 : bool
6097 0 : nsDocument::IsWebComponentsEnabled(dom::NodeInfo* aNodeInfo)
6098 : {
6099 0 : if (!nsContentUtils::IsWebComponentsEnabled()) {
6100 0 : return false;
6101 : }
6102 :
6103 0 : nsIDocument* doc = aNodeInfo->GetDocument();
6104 0 : if (doc->IsStyledByServo()) {
6105 0 : NS_WARNING("stylo: Web Components not supported yet");
6106 0 : return false;
6107 : }
6108 :
6109 0 : return true;
6110 : }
6111 :
6112 : void
6113 0 : nsDocument::ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
6114 : {
6115 0 : mLazySVGPresElements.PutEntry(aSVG);
6116 0 : }
6117 :
6118 : void
6119 0 : nsDocument::UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG)
6120 : {
6121 0 : mLazySVGPresElements.RemoveEntry(aSVG);
6122 0 : }
6123 :
6124 : void
6125 0 : nsDocument::ResolveScheduledSVGPresAttrs()
6126 : {
6127 0 : for (auto iter = mLazySVGPresElements.Iter(); !iter.Done(); iter.Next()) {
6128 0 : nsSVGElement* svg = iter.Get()->GetKey();
6129 0 : svg->UpdateContentDeclarationBlock(StyleBackendType::Servo);
6130 : }
6131 0 : mLazySVGPresElements.Clear();
6132 0 : }
6133 :
6134 : void
6135 0 : nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
6136 : const ElementRegistrationOptions& aOptions,
6137 : JS::MutableHandle<JSObject*> aRetval,
6138 : ErrorResult& rv)
6139 : {
6140 0 : RefPtr<CustomElementRegistry> registry(GetCustomElementRegistry());
6141 0 : if (!registry) {
6142 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6143 0 : return;
6144 : }
6145 :
6146 0 : AutoCEReaction ceReaction(this->GetDocGroup()->CustomElementReactionsStack());
6147 : // Unconditionally convert TYPE to lowercase.
6148 0 : nsAutoString lcType;
6149 0 : nsContentUtils::ASCIIToLower(aType, lcType);
6150 :
6151 0 : nsIGlobalObject* sgo = GetScopeObject();
6152 0 : if (!sgo) {
6153 0 : rv.Throw(NS_ERROR_UNEXPECTED);
6154 0 : return;
6155 : }
6156 :
6157 0 : JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
6158 0 : JS::Rooted<JSObject*> protoObject(aCx);
6159 :
6160 0 : if (!aOptions.mPrototype) {
6161 0 : JS::Rooted<JSObject*> htmlProto(aCx);
6162 0 : htmlProto = HTMLElementBinding::GetProtoObjectHandle(aCx);
6163 0 : if (!htmlProto) {
6164 0 : rv.Throw(NS_ERROR_OUT_OF_MEMORY);
6165 0 : return;
6166 : }
6167 :
6168 0 : protoObject = JS_NewObjectWithGivenProto(aCx, nullptr, htmlProto);
6169 0 : if (!protoObject) {
6170 0 : rv.Throw(NS_ERROR_UNEXPECTED);
6171 0 : return;
6172 : }
6173 : } else {
6174 0 : protoObject = aOptions.mPrototype;
6175 :
6176 : // Get the unwrapped prototype to do some checks.
6177 0 : JS::Rooted<JSObject*> protoObjectUnwrapped(aCx, js::CheckedUnwrap(protoObject));
6178 0 : if (!protoObjectUnwrapped) {
6179 : // If the caller's compartment does not have permission to access the
6180 : // unwrapped prototype then throw.
6181 0 : rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
6182 0 : return;
6183 : }
6184 :
6185 : // If PROTOTYPE is already an interface prototype object for any interface
6186 : // object or PROTOTYPE has a non-configurable property named constructor,
6187 : // throw a NotSupportedError and stop.
6188 0 : const js::Class* clasp = js::GetObjectClass(protoObjectUnwrapped);
6189 0 : if (IsDOMIfaceAndProtoClass(clasp)) {
6190 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6191 0 : return;
6192 : }
6193 :
6194 0 : JS::Rooted<JS::PropertyDescriptor> descRoot(aCx);
6195 0 : JS::MutableHandle<JS::PropertyDescriptor> desc(&descRoot);
6196 : // This check may go through a wrapper, but as we checked above
6197 : // it should be transparent or an xray. This should be fine for now,
6198 : // until the spec is sorted out.
6199 0 : if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
6200 0 : rv.Throw(NS_ERROR_UNEXPECTED);
6201 0 : return;
6202 : }
6203 :
6204 0 : if (!desc.configurable()) {
6205 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6206 0 : return;
6207 : }
6208 : }
6209 :
6210 0 : JS::Rooted<JSFunction*> constructor(aCx);
6211 : {
6212 : // Go into the document's global compartment when creating the constructor
6213 : // function because we want to get the correct document (where the
6214 : // definition is registered) when it is called.
6215 0 : JSAutoCompartment ac(aCx, global);
6216 :
6217 : // Create constructor to return. Store the name of the custom element as the
6218 : // name of the function.
6219 0 : constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
6220 : JSFUN_CONSTRUCTOR,
6221 0 : NS_ConvertUTF16toUTF8(lcType).get());
6222 0 : if (!constructor) {
6223 0 : rv.Throw(NS_ERROR_OUT_OF_MEMORY);
6224 0 : return;
6225 : }
6226 : }
6227 :
6228 0 : JS::Rooted<JSObject*> wrappedConstructor(aCx);
6229 0 : wrappedConstructor = JS_GetFunctionObject(constructor);
6230 0 : if (!JS_WrapObject(aCx, &wrappedConstructor)) {
6231 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6232 0 : return;
6233 : }
6234 :
6235 0 : if (!JS_LinkConstructorAndPrototype(aCx, wrappedConstructor, protoObject)) {
6236 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6237 0 : return;
6238 : }
6239 :
6240 0 : ElementDefinitionOptions options;
6241 0 : if (!aOptions.mExtends.IsVoid()) {
6242 : // Only convert NAME to lowercase in HTML documents.
6243 0 : nsAutoString lcName;
6244 0 : IsHTMLDocument() ? nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName)
6245 0 : : lcName.Assign(aOptions.mExtends);
6246 :
6247 0 : options.mExtends.Construct(lcName);
6248 : }
6249 :
6250 : RefPtr<Function> functionConstructor =
6251 0 : new Function(aCx, wrappedConstructor, sgo);
6252 :
6253 0 : registry->Define(lcType, *functionConstructor, options, rv);
6254 :
6255 0 : aRetval.set(wrappedConstructor);
6256 : }
6257 :
6258 : NS_IMETHODIMP
6259 0 : nsDocument::GetElementsByTagName(const nsAString& aTagname,
6260 : nsIDOMNodeList** aReturn)
6261 : {
6262 0 : RefPtr<nsContentList> list = GetElementsByTagName(aTagname);
6263 0 : NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
6264 :
6265 : // transfer ref to aReturn
6266 0 : list.forget(aReturn);
6267 0 : return NS_OK;
6268 : }
6269 :
6270 : long
6271 0 : nsDocument::BlockedTrackingNodeCount() const
6272 : {
6273 0 : return mBlockedTrackingNodes.Length();
6274 : }
6275 :
6276 : already_AddRefed<nsSimpleContentList>
6277 0 : nsDocument::BlockedTrackingNodes() const
6278 : {
6279 0 : RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
6280 :
6281 0 : nsTArray<nsWeakPtr> blockedTrackingNodes;
6282 0 : blockedTrackingNodes = mBlockedTrackingNodes;
6283 :
6284 0 : for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
6285 0 : nsWeakPtr weakNode = blockedTrackingNodes[i];
6286 0 : nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
6287 : // Consider only nodes to which we have managed to get strong references.
6288 : // Coping with nullptrs since it's expected for nodes to disappear when
6289 : // nobody else is referring to them.
6290 0 : if (node) {
6291 0 : list->AppendElement(node);
6292 : }
6293 : }
6294 :
6295 0 : return list.forget();
6296 : }
6297 :
6298 : already_AddRefed<nsContentList>
6299 0 : nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6300 : const nsAString& aLocalName,
6301 : ErrorResult& aResult)
6302 : {
6303 0 : int32_t nameSpaceId = kNameSpaceID_Wildcard;
6304 :
6305 0 : if (!aNamespaceURI.EqualsLiteral("*")) {
6306 0 : aResult =
6307 : nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
6308 0 : nameSpaceId);
6309 0 : if (aResult.Failed()) {
6310 0 : return nullptr;
6311 : }
6312 : }
6313 :
6314 0 : NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
6315 :
6316 0 : return NS_GetContentList(this, nameSpaceId, aLocalName);
6317 : }
6318 :
6319 : NS_IMETHODIMP
6320 0 : nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6321 : const nsAString& aLocalName,
6322 : nsIDOMNodeList** aReturn)
6323 : {
6324 0 : ErrorResult rv;
6325 : RefPtr<nsContentList> list =
6326 0 : nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
6327 0 : if (rv.Failed()) {
6328 0 : return rv.StealNSResult();
6329 : }
6330 :
6331 : // transfer ref to aReturn
6332 0 : list.forget(aReturn);
6333 0 : return NS_OK;
6334 : }
6335 :
6336 : NS_IMETHODIMP
6337 0 : nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
6338 : {
6339 0 : NS_ADDREF(*aStyleSheets = StyleSheets());
6340 0 : return NS_OK;
6341 : }
6342 :
6343 : StyleSheetList*
6344 2 : nsDocument::StyleSheets()
6345 : {
6346 2 : if (!mDOMStyleSheets) {
6347 2 : mDOMStyleSheets = new nsDOMStyleSheetList(this);
6348 : }
6349 2 : return mDOMStyleSheets;
6350 : }
6351 :
6352 : NS_IMETHODIMP
6353 0 : nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet)
6354 : {
6355 0 : nsIDocument::GetSelectedStyleSheetSet(aSheetSet);
6356 0 : return NS_OK;
6357 : }
6358 :
6359 : void
6360 1 : nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
6361 : {
6362 1 : aSheetSet.Truncate();
6363 :
6364 : // Look through our sheets, find the selected set title
6365 1 : int32_t count = GetNumberOfStyleSheets();
6366 2 : nsAutoString title;
6367 1 : for (int32_t index = 0; index < count; index++) {
6368 0 : StyleSheet* sheet = GetStyleSheetAt(index);
6369 0 : NS_ASSERTION(sheet, "Null sheet in sheet list!");
6370 :
6371 0 : if (sheet->Disabled()) {
6372 : // Disabled sheets don't affect the currently selected set
6373 0 : continue;
6374 : }
6375 :
6376 0 : sheet->GetTitle(title);
6377 :
6378 0 : if (aSheetSet.IsEmpty()) {
6379 0 : aSheetSet = title;
6380 0 : } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
6381 : // Sheets from multiple sets enabled; return null string, per spec.
6382 0 : SetDOMStringToNull(aSheetSet);
6383 0 : return;
6384 : }
6385 : }
6386 : }
6387 :
6388 : NS_IMETHODIMP
6389 0 : nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet)
6390 : {
6391 0 : SetSelectedStyleSheetSet(aSheetSet);
6392 0 : return NS_OK;
6393 : }
6394 :
6395 : void
6396 0 : nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
6397 : {
6398 0 : if (DOMStringIsNull(aSheetSet)) {
6399 0 : return;
6400 : }
6401 :
6402 : // Must update mLastStyleSheetSet before doing anything else with stylesheets
6403 : // or CSSLoaders.
6404 0 : mLastStyleSheetSet = aSheetSet;
6405 0 : EnableStyleSheetsForSetInternal(aSheetSet, true);
6406 : }
6407 :
6408 : NS_IMETHODIMP
6409 0 : nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet)
6410 : {
6411 0 : nsString sheetSet;
6412 0 : GetLastStyleSheetSet(sheetSet);
6413 0 : aSheetSet = sheetSet;
6414 0 : return NS_OK;
6415 : }
6416 :
6417 : void
6418 1 : nsDocument::GetLastStyleSheetSet(nsString& aSheetSet)
6419 : {
6420 1 : aSheetSet = mLastStyleSheetSet;
6421 1 : }
6422 :
6423 : NS_IMETHODIMP
6424 55 : nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6425 : {
6426 55 : nsIDocument::GetPreferredStyleSheetSet(aSheetSet);
6427 55 : return NS_OK;
6428 : }
6429 :
6430 : void
6431 57 : nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6432 : {
6433 57 : GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
6434 57 : }
6435 :
6436 : NS_IMETHODIMP
6437 0 : nsDocument::GetStyleSheetSets(nsISupports** aList)
6438 : {
6439 0 : NS_ADDREF(*aList = StyleSheetSets());
6440 0 : return NS_OK;
6441 : }
6442 :
6443 : DOMStringList*
6444 0 : nsDocument::StyleSheetSets()
6445 : {
6446 0 : if (!mStyleSheetSetList) {
6447 0 : mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
6448 : }
6449 0 : return mStyleSheetSetList;
6450 : }
6451 :
6452 : NS_IMETHODIMP
6453 0 : nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet)
6454 : {
6455 0 : EnableStyleSheetsForSet(aSheetSet);
6456 0 : return NS_OK;
6457 : }
6458 :
6459 : void
6460 0 : nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
6461 : {
6462 : // Per spec, passing in null is a no-op.
6463 0 : if (!DOMStringIsNull(aSheetSet)) {
6464 : // Note: must make sure to not change the CSSLoader's preferred sheet --
6465 : // that value should be equal to either our lastStyleSheetSet (if that's
6466 : // non-null) or to our preferredStyleSheetSet. And this method doesn't
6467 : // change either of those.
6468 0 : EnableStyleSheetsForSetInternal(aSheetSet, false);
6469 : }
6470 0 : }
6471 :
6472 : void
6473 0 : nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
6474 : bool aUpdateCSSLoader)
6475 : {
6476 0 : BeginUpdate(UPDATE_STYLE);
6477 0 : int32_t count = GetNumberOfStyleSheets();
6478 0 : nsAutoString title;
6479 0 : for (int32_t index = 0; index < count; index++) {
6480 0 : StyleSheet* sheet = GetStyleSheetAt(index);
6481 0 : NS_ASSERTION(sheet, "Null sheet in sheet list!");
6482 :
6483 0 : sheet->GetTitle(title);
6484 0 : if (!title.IsEmpty()) {
6485 0 : sheet->SetEnabled(title.Equals(aSheetSet));
6486 : }
6487 : }
6488 0 : if (aUpdateCSSLoader) {
6489 0 : CSSLoader()->SetPreferredSheet(aSheetSet);
6490 : }
6491 0 : EndUpdate(UPDATE_STYLE);
6492 0 : }
6493 :
6494 : NS_IMETHODIMP
6495 0 : nsDocument::GetCharacterSet(nsAString& aCharacterSet)
6496 : {
6497 0 : nsIDocument::GetCharacterSet(aCharacterSet);
6498 0 : return NS_OK;
6499 : }
6500 :
6501 : void
6502 5 : nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
6503 : {
6504 10 : nsAutoCString charset;
6505 5 : GetDocumentCharacterSet()->Name(charset);
6506 5 : CopyASCIItoUTF16(charset, aCharacterSet);
6507 5 : }
6508 :
6509 : NS_IMETHODIMP
6510 0 : nsDocument::ImportNode(nsIDOMNode* aImportedNode,
6511 : bool aDeep,
6512 : uint8_t aArgc,
6513 : nsIDOMNode** aResult)
6514 : {
6515 0 : if (aArgc == 0) {
6516 0 : aDeep = true;
6517 : }
6518 :
6519 0 : *aResult = nullptr;
6520 :
6521 0 : nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode);
6522 0 : NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED);
6523 :
6524 0 : ErrorResult rv;
6525 0 : nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv);
6526 0 : if (rv.Failed()) {
6527 0 : return rv.StealNSResult();
6528 : }
6529 :
6530 0 : NS_ADDREF(*aResult = result->AsDOMNode());
6531 0 : return NS_OK;
6532 : }
6533 :
6534 : already_AddRefed<nsINode>
6535 0 : nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
6536 : {
6537 0 : nsINode* imported = &aNode;
6538 :
6539 0 : switch (imported->NodeType()) {
6540 : case nsIDOMNode::DOCUMENT_NODE:
6541 : {
6542 0 : break;
6543 : }
6544 : case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
6545 : {
6546 0 : if (ShadowRoot::FromNode(imported)) {
6547 0 : break;
6548 : }
6549 : MOZ_FALLTHROUGH;
6550 : }
6551 : case nsIDOMNode::ATTRIBUTE_NODE:
6552 : case nsIDOMNode::ELEMENT_NODE:
6553 : case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
6554 : case nsIDOMNode::TEXT_NODE:
6555 : case nsIDOMNode::CDATA_SECTION_NODE:
6556 : case nsIDOMNode::COMMENT_NODE:
6557 : case nsIDOMNode::DOCUMENT_TYPE_NODE:
6558 : {
6559 0 : nsCOMPtr<nsINode> newNode;
6560 0 : rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager, nullptr,
6561 0 : getter_AddRefs(newNode));
6562 0 : if (rv.Failed()) {
6563 0 : return nullptr;
6564 : }
6565 0 : return newNode.forget();
6566 : }
6567 : default:
6568 : {
6569 0 : NS_WARNING("Don't know how to clone this nodetype for importNode.");
6570 : }
6571 : }
6572 :
6573 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6574 0 : return nullptr;
6575 : }
6576 :
6577 : NS_IMETHODIMP
6578 0 : nsDocument::LoadBindingDocument(const nsAString& aURI)
6579 : {
6580 0 : ErrorResult rv;
6581 0 : nsIDocument::LoadBindingDocument(aURI,
6582 0 : nsContentUtils::GetCurrentJSContext()
6583 0 : ? Some(nsContentUtils::SubjectPrincipal())
6584 0 : : Nothing(),
6585 0 : rv);
6586 0 : return rv.StealNSResult();
6587 : }
6588 :
6589 : void
6590 0 : nsIDocument::LoadBindingDocument(const nsAString& aURI,
6591 : nsIPrincipal& aSubjectPrincipal,
6592 : ErrorResult& rv)
6593 : {
6594 0 : LoadBindingDocument(aURI, Some(&aSubjectPrincipal), rv);
6595 0 : }
6596 :
6597 : void
6598 0 : nsIDocument::LoadBindingDocument(const nsAString& aURI,
6599 : const Maybe<nsIPrincipal*>& aSubjectPrincipal,
6600 : ErrorResult& rv)
6601 : {
6602 0 : nsCOMPtr<nsIURI> uri;
6603 0 : rv = NS_NewURI(getter_AddRefs(uri), aURI, mCharacterSet, GetDocBaseURI());
6604 0 : if (rv.Failed()) {
6605 0 : return;
6606 : }
6607 :
6608 : // Note - This computation of subjectPrincipal isn't necessarily sensical.
6609 : // It's just designed to preserve the old semantics during a mass-conversion
6610 : // patch.
6611 : nsCOMPtr<nsIPrincipal> subjectPrincipal =
6612 0 : aSubjectPrincipal.isSome() ? aSubjectPrincipal.value() : NodePrincipal();
6613 0 : BindingManager()->LoadBindingDocument(this, uri, subjectPrincipal);
6614 : }
6615 :
6616 : NS_IMETHODIMP
6617 0 : nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
6618 : {
6619 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
6620 0 : NS_ENSURE_ARG_POINTER(node);
6621 :
6622 0 : Element* bindingParent = nsIDocument::GetBindingParent(*node);
6623 0 : nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent);
6624 0 : retval.forget(aResult);
6625 0 : return NS_OK;
6626 : }
6627 :
6628 : Element*
6629 2 : nsIDocument::GetBindingParent(nsINode& aNode)
6630 : {
6631 4 : nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
6632 2 : if (!content)
6633 0 : return nullptr;
6634 :
6635 2 : nsIContent* bindingParent = content->GetBindingParent();
6636 2 : return bindingParent ? bindingParent->AsElement() : nullptr;
6637 : }
6638 :
6639 : static Element*
6640 149 : GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
6641 : const nsAString& aAttrValue, bool aUniversalMatch)
6642 : {
6643 149 : if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
6644 : aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
6645 : aAttrValue, eCaseMatters)) {
6646 19 : return aContent->AsElement();
6647 : }
6648 :
6649 205 : for (nsIContent* child = aContent->GetFirstChild();
6650 205 : child;
6651 75 : child = child->GetNextSibling()) {
6652 :
6653 : Element* matchedElement =
6654 97 : GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch);
6655 97 : if (matchedElement)
6656 22 : return matchedElement;
6657 : }
6658 :
6659 108 : return nullptr;
6660 : }
6661 :
6662 : Element*
6663 19 : nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
6664 : nsIAtom* aAttrName,
6665 : const nsAString& aAttrValue) const
6666 : {
6667 19 : nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
6668 19 : if (!nodeList)
6669 0 : return nullptr;
6670 :
6671 19 : uint32_t length = 0;
6672 19 : nodeList->GetLength(&length);
6673 :
6674 19 : bool universalMatch = aAttrValue.EqualsLiteral("*");
6675 :
6676 52 : for (uint32_t i = 0; i < length; ++i) {
6677 52 : nsIContent* current = nodeList->Item(i);
6678 : Element* matchedElm =
6679 52 : GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
6680 52 : if (matchedElm)
6681 19 : return matchedElm;
6682 : }
6683 :
6684 0 : return nullptr;
6685 : }
6686 :
6687 : NS_IMETHODIMP
6688 0 : nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
6689 : const nsAString& aAttrName,
6690 : const nsAString& aAttrValue,
6691 : nsIDOMElement** aResult)
6692 : {
6693 0 : nsCOMPtr<Element> element = do_QueryInterface(aElement);
6694 0 : NS_ENSURE_ARG_POINTER(element);
6695 :
6696 : Element* anonEl =
6697 0 : nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName,
6698 0 : aAttrValue);
6699 0 : nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl);
6700 0 : retval.forget(aResult);
6701 0 : return NS_OK;
6702 : }
6703 :
6704 : Element*
6705 16 : nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
6706 : const nsAString& aAttrName,
6707 : const nsAString& aAttrValue)
6708 : {
6709 32 : nsCOMPtr<nsIAtom> attribute = NS_Atomize(aAttrName);
6710 :
6711 32 : return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
6712 : }
6713 :
6714 :
6715 : NS_IMETHODIMP
6716 0 : nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
6717 : nsIDOMNodeList** aResult)
6718 : {
6719 0 : *aResult = nullptr;
6720 :
6721 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
6722 0 : return BindingManager()->GetAnonymousNodesFor(content, aResult);
6723 : }
6724 :
6725 : nsINodeList*
6726 0 : nsIDocument::GetAnonymousNodes(Element& aElement)
6727 : {
6728 0 : return BindingManager()->GetAnonymousNodesFor(&aElement);
6729 : }
6730 :
6731 : NS_IMETHODIMP
6732 0 : nsDocument::CreateRange(nsIDOMRange** aReturn)
6733 : {
6734 0 : ErrorResult rv;
6735 0 : *aReturn = nsIDocument::CreateRange(rv).take();
6736 0 : return rv.StealNSResult();
6737 : }
6738 :
6739 : already_AddRefed<nsRange>
6740 2 : nsIDocument::CreateRange(ErrorResult& rv)
6741 : {
6742 4 : RefPtr<nsRange> range = new nsRange(this);
6743 2 : nsresult res = range->CollapseTo(this, 0);
6744 2 : if (NS_FAILED(res)) {
6745 0 : rv.Throw(res);
6746 0 : return nullptr;
6747 : }
6748 :
6749 2 : return range.forget();
6750 : }
6751 :
6752 : NS_IMETHODIMP
6753 0 : nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
6754 : uint32_t aWhatToShow,
6755 : nsIDOMNodeFilter *aFilter,
6756 : uint8_t aOptionalArgc,
6757 : nsIDOMNodeIterator **_retval)
6758 : {
6759 0 : *_retval = nullptr;
6760 :
6761 0 : if (!aOptionalArgc) {
6762 0 : aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6763 : }
6764 :
6765 0 : if (!aRoot) {
6766 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
6767 : }
6768 :
6769 0 : nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6770 0 : NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
6771 :
6772 0 : ErrorResult rv;
6773 0 : *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow,
6774 0 : NodeFilterHolder(aFilter),
6775 0 : rv).take();
6776 0 : return rv.StealNSResult();
6777 : }
6778 :
6779 : already_AddRefed<NodeIterator>
6780 0 : nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6781 : NodeFilter* aFilter,
6782 : ErrorResult& rv) const
6783 : {
6784 0 : return CreateNodeIterator(aRoot, aWhatToShow, NodeFilterHolder(aFilter), rv);
6785 : }
6786 :
6787 : already_AddRefed<NodeIterator>
6788 0 : nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6789 : NodeFilterHolder aFilter,
6790 : ErrorResult& rv) const
6791 : {
6792 0 : nsINode* root = &aRoot;
6793 : RefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow,
6794 0 : Move(aFilter));
6795 0 : return iterator.forget();
6796 : }
6797 :
6798 : NS_IMETHODIMP
6799 0 : nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
6800 : uint32_t aWhatToShow,
6801 : nsIDOMNodeFilter *aFilter,
6802 : uint8_t aOptionalArgc,
6803 : nsIDOMTreeWalker **_retval)
6804 : {
6805 0 : *_retval = nullptr;
6806 :
6807 0 : if (!aOptionalArgc) {
6808 0 : aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6809 : }
6810 :
6811 0 : nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6812 0 : NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6813 :
6814 0 : ErrorResult rv;
6815 0 : *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow,
6816 0 : NodeFilterHolder(aFilter),
6817 0 : rv).take();
6818 0 : return rv.StealNSResult();
6819 : }
6820 :
6821 : already_AddRefed<TreeWalker>
6822 0 : nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6823 : NodeFilter* aFilter,
6824 : ErrorResult& rv) const
6825 : {
6826 0 : return CreateTreeWalker(aRoot, aWhatToShow, NodeFilterHolder(aFilter), rv);
6827 : }
6828 :
6829 : already_AddRefed<TreeWalker>
6830 0 : nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6831 : NodeFilterHolder aFilter, ErrorResult& rv) const
6832 : {
6833 0 : nsINode* root = &aRoot;
6834 0 : RefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, Move(aFilter));
6835 0 : return walker.forget();
6836 : }
6837 :
6838 :
6839 : NS_IMETHODIMP
6840 0 : nsDocument::GetDefaultView(mozIDOMWindowProxy** aDefaultView)
6841 : {
6842 0 : *aDefaultView = nullptr;
6843 0 : nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
6844 0 : win.forget(aDefaultView);
6845 0 : return NS_OK;
6846 : }
6847 :
6848 : already_AddRefed<Location>
6849 1 : nsIDocument::GetLocation() const
6850 : {
6851 2 : nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
6852 :
6853 1 : if (!w) {
6854 0 : return nullptr;
6855 : }
6856 :
6857 1 : nsGlobalWindow* window = nsGlobalWindow::Cast(w);
6858 2 : RefPtr<Location> loc = window->GetLocation();
6859 1 : return loc.forget();
6860 : }
6861 :
6862 : Element*
6863 101 : nsIDocument::GetHtmlElement() const
6864 : {
6865 101 : Element* rootElement = GetRootElement();
6866 101 : if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html))
6867 65 : return rootElement;
6868 36 : return nullptr;
6869 : }
6870 :
6871 : Element*
6872 95 : nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
6873 : {
6874 95 : Element* html = GetHtmlElement();
6875 95 : if (!html)
6876 36 : return nullptr;
6877 :
6878 : // Look for the element with aTag inside html. This needs to run
6879 : // forwards to find the first such element.
6880 118 : for (nsIContent* child = html->GetFirstChild();
6881 118 : child;
6882 59 : child = child->GetNextSibling()) {
6883 118 : if (child->IsHTMLElement(aTag))
6884 59 : return child->AsElement();
6885 : }
6886 0 : return nullptr;
6887 : }
6888 :
6889 : Element*
6890 26 : nsDocument::GetTitleElement()
6891 : {
6892 : // mMayHaveTitleElement will have been set to true if any HTML or SVG
6893 : // <title> element has been bound to this document. So if it's false,
6894 : // we know there is nothing to do here. This avoids us having to search
6895 : // the whole DOM if someone calls document.title on a large document
6896 : // without a title.
6897 26 : if (!mMayHaveTitleElement)
6898 25 : return nullptr;
6899 :
6900 1 : Element* root = GetRootElement();
6901 1 : if (root && root->IsSVGElement(nsGkAtoms::svg)) {
6902 : // In SVG, the document's title must be a child
6903 0 : for (nsIContent* child = root->GetFirstChild();
6904 0 : child; child = child->GetNextSibling()) {
6905 0 : if (child->IsSVGElement(nsGkAtoms::title)) {
6906 0 : return child->AsElement();
6907 : }
6908 : }
6909 0 : return nullptr;
6910 : }
6911 :
6912 : // We check the HTML namespace even for non-HTML documents, except SVG. This
6913 : // matches the spec and the behavior of all tested browsers.
6914 : RefPtr<nsContentList> list =
6915 2 : NS_GetContentList(this, kNameSpaceID_XHTML, NS_LITERAL_STRING("title"));
6916 :
6917 1 : nsIContent* first = list->Item(0, false);
6918 :
6919 1 : return first ? first->AsElement() : nullptr;
6920 : }
6921 :
6922 : NS_IMETHODIMP
6923 0 : nsDocument::GetTitle(nsAString& aTitle)
6924 : {
6925 0 : nsString title;
6926 0 : GetTitle(title);
6927 0 : aTitle = title;
6928 0 : return NS_OK;
6929 : }
6930 :
6931 : void
6932 28 : nsDocument::GetTitle(nsString& aTitle)
6933 : {
6934 28 : aTitle.Truncate();
6935 :
6936 28 : Element* rootElement = GetRootElement();
6937 28 : if (!rootElement) {
6938 27 : return;
6939 : }
6940 :
6941 29 : nsAutoString tmp;
6942 :
6943 : #ifdef MOZ_XUL
6944 27 : if (rootElement->IsXULElement()) {
6945 1 : rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
6946 : } else
6947 : #endif
6948 : {
6949 26 : Element* title = GetTitleElement();
6950 26 : if (!title) {
6951 25 : return;
6952 : }
6953 1 : nsContentUtils::GetNodeTextContent(title, false, tmp);
6954 : }
6955 :
6956 2 : tmp.CompressWhitespace();
6957 2 : aTitle = tmp;
6958 : }
6959 :
6960 : NS_IMETHODIMP
6961 1 : nsDocument::SetTitle(const nsAString& aTitle)
6962 : {
6963 1 : Element* rootElement = GetRootElement();
6964 1 : if (!rootElement) {
6965 0 : return NS_OK;
6966 : }
6967 :
6968 : #ifdef MOZ_XUL
6969 1 : if (rootElement->IsXULElement()) {
6970 1 : return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
6971 1 : aTitle, true);
6972 : }
6973 : #endif
6974 :
6975 : // Batch updates so that mutation events don't change "the title
6976 : // element" under us
6977 0 : mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
6978 :
6979 0 : nsCOMPtr<Element> title = GetTitleElement();
6980 0 : if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
6981 0 : if (!title) {
6982 : RefPtr<mozilla::dom::NodeInfo> titleInfo =
6983 0 : mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6984 : kNameSpaceID_SVG,
6985 0 : nsIDOMNode::ELEMENT_NODE);
6986 0 : NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
6987 0 : NOT_FROM_PARSER);
6988 0 : if (!title) {
6989 0 : return NS_OK;
6990 : }
6991 0 : rootElement->InsertChildAt(title, 0, true);
6992 : }
6993 0 : } else if (rootElement->IsHTMLElement()) {
6994 0 : if (!title) {
6995 0 : Element* head = GetHeadElement();
6996 0 : if (!head) {
6997 0 : return NS_OK;
6998 : }
6999 :
7000 0 : RefPtr<mozilla::dom::NodeInfo> titleInfo;
7001 0 : titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
7002 : kNameSpaceID_XHTML,
7003 0 : nsIDOMNode::ELEMENT_NODE);
7004 0 : title = NS_NewHTMLTitleElement(titleInfo.forget());
7005 0 : if (!title) {
7006 0 : return NS_OK;
7007 : }
7008 :
7009 0 : head->AppendChildTo(title, true);
7010 : }
7011 : } else {
7012 0 : return NS_OK;
7013 : }
7014 :
7015 0 : return nsContentUtils::SetNodeTextContent(title, aTitle, false);
7016 : }
7017 :
7018 : void
7019 1 : nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv)
7020 : {
7021 1 : rv = SetTitle(aTitle);
7022 1 : }
7023 :
7024 : void
7025 27 : nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
7026 : {
7027 27 : NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
7028 : "Setting a title while unlinking or destroying the element?");
7029 27 : if (mInUnlinkOrDeletion) {
7030 1 : return;
7031 : }
7032 :
7033 27 : if (aBoundTitleElement) {
7034 1 : mMayHaveTitleElement = true;
7035 : }
7036 27 : if (mPendingTitleChangeEvent.IsPending())
7037 1 : return;
7038 :
7039 26 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
7040 : RefPtr<nsRunnableMethod<nsDocument, void, false>> event =
7041 52 : NewNonOwningRunnableMethod("nsDocument::DoNotifyPossibleTitleChange",
7042 : this,
7043 52 : &nsDocument::DoNotifyPossibleTitleChange);
7044 78 : nsresult rv = Dispatch("nsDocument::DoNotifyPossibleTitleChange",
7045 78 : TaskCategory::Other, do_AddRef(event));
7046 26 : if (NS_SUCCEEDED(rv)) {
7047 26 : mPendingTitleChangeEvent = event;
7048 : }
7049 : }
7050 :
7051 : void
7052 26 : nsDocument::DoNotifyPossibleTitleChange()
7053 : {
7054 26 : mPendingTitleChangeEvent.Forget();
7055 26 : mHaveFiredTitleChange = true;
7056 :
7057 52 : nsAutoString title;
7058 26 : GetTitle(title);
7059 :
7060 52 : nsCOMPtr<nsIPresShell> shell = GetShell();
7061 26 : if (shell) {
7062 : nsCOMPtr<nsISupports> container =
7063 48 : shell->GetPresContext()->GetContainerWeak();
7064 24 : if (container) {
7065 6 : nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
7066 3 : if (docShellWin) {
7067 3 : docShellWin->SetTitle(title.get());
7068 : }
7069 : }
7070 : }
7071 :
7072 : // Fire a DOM event for the title change.
7073 26 : nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
7074 52 : NS_LITERAL_STRING("DOMTitleChanged"),
7075 52 : true, true);
7076 26 : }
7077 :
7078 : already_AddRefed<BoxObject>
7079 12 : nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
7080 : {
7081 12 : if (!aElement) {
7082 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
7083 0 : return nullptr;
7084 : }
7085 :
7086 12 : nsIDocument* doc = aElement->OwnerDoc();
7087 12 : if (doc != this) {
7088 0 : aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
7089 0 : return nullptr;
7090 : }
7091 :
7092 12 : if (!mHasWarnedAboutBoxObjects && !aElement->IsXULElement()) {
7093 0 : mHasWarnedAboutBoxObjects = true;
7094 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
7095 0 : NS_LITERAL_CSTRING("BoxObjects"), this,
7096 : nsContentUtils::eDOM_PROPERTIES,
7097 0 : "UseOfGetBoxObjectForWarning");
7098 : }
7099 :
7100 12 : if (!mBoxObjectTable) {
7101 1 : mBoxObjectTable = new nsRefPtrHashtable<nsPtrHashKey<nsIContent>, BoxObject>(6);
7102 : }
7103 :
7104 24 : RefPtr<BoxObject> boxObject;
7105 24 : auto entry = mBoxObjectTable->LookupForAdd(aElement);
7106 12 : if (entry) {
7107 1 : boxObject = entry.Data();
7108 1 : return boxObject.forget();
7109 : }
7110 :
7111 : int32_t namespaceID;
7112 22 : nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
7113 : #ifdef MOZ_XUL
7114 11 : if (namespaceID == kNameSpaceID_XUL) {
7115 33 : if (tag == nsGkAtoms::browser ||
7116 22 : tag == nsGkAtoms::editor ||
7117 11 : tag == nsGkAtoms::iframe) {
7118 0 : boxObject = new ContainerBoxObject();
7119 11 : } else if (tag == nsGkAtoms::menu) {
7120 1 : boxObject = new MenuBoxObject();
7121 30 : } else if (tag == nsGkAtoms::popup ||
7122 17 : tag == nsGkAtoms::menupopup ||
7123 22 : tag == nsGkAtoms::panel ||
7124 5 : tag == nsGkAtoms::tooltip) {
7125 6 : boxObject = new PopupBoxObject();
7126 4 : } else if (tag == nsGkAtoms::tree) {
7127 0 : boxObject = new TreeBoxObject();
7128 4 : } else if (tag == nsGkAtoms::listbox) {
7129 0 : boxObject = new ListBoxObject();
7130 4 : } else if (tag == nsGkAtoms::scrollbox) {
7131 0 : boxObject = new ScrollBoxObject();
7132 : } else {
7133 4 : boxObject = new BoxObject();
7134 : }
7135 : } else
7136 : #endif // MOZ_XUL
7137 : {
7138 0 : boxObject = new BoxObject();
7139 : }
7140 :
7141 11 : boxObject->Init(aElement);
7142 22 : entry.OrInsert([&boxObject]() { return boxObject; });
7143 :
7144 11 : return boxObject.forget();
7145 : }
7146 :
7147 : void
7148 120 : nsDocument::ClearBoxObjectFor(nsIContent* aContent)
7149 : {
7150 120 : if (mBoxObjectTable) {
7151 77 : if (auto entry = mBoxObjectTable->Lookup(aContent)) {
7152 0 : nsPIBoxObject* boxObject = entry.Data();
7153 0 : boxObject->Clear();
7154 0 : entry.Remove();
7155 : }
7156 : }
7157 120 : }
7158 :
7159 : already_AddRefed<MediaQueryList>
7160 3 : nsIDocument::MatchMedia(const nsAString& aMediaQueryList)
7161 : {
7162 6 : RefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
7163 :
7164 3 : mDOMMediaQueryLists.insertBack(result);
7165 :
7166 6 : return result.forget();
7167 : }
7168 :
7169 : void
7170 0 : nsDocument::FlushSkinBindings()
7171 : {
7172 0 : BindingManager()->FlushSkinBindings();
7173 0 : }
7174 :
7175 : nsresult
7176 3 : nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
7177 : {
7178 3 : mInitializableFrameLoaders.RemoveElement(aLoader);
7179 : // Don't even try to initialize.
7180 3 : if (mInDestructor) {
7181 : NS_WARNING("Trying to initialize a frame loader while"
7182 0 : "document is being deleted");
7183 0 : return NS_ERROR_FAILURE;
7184 : }
7185 :
7186 3 : mInitializableFrameLoaders.AppendElement(aLoader);
7187 3 : if (!mFrameLoaderRunner) {
7188 : mFrameLoaderRunner =
7189 6 : NewRunnableMethod("nsDocument::MaybeInitializeFinalizeFrameLoaders",
7190 : this,
7191 3 : &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7192 3 : NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
7193 3 : nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7194 : }
7195 3 : return NS_OK;
7196 : }
7197 :
7198 : nsresult
7199 1 : nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader, nsIRunnable* aFinalizer)
7200 : {
7201 1 : mInitializableFrameLoaders.RemoveElement(aLoader);
7202 1 : if (mInDestructor) {
7203 0 : return NS_ERROR_FAILURE;
7204 : }
7205 :
7206 1 : mFrameLoaderFinalizers.AppendElement(aFinalizer);
7207 1 : if (!mFrameLoaderRunner) {
7208 : mFrameLoaderRunner =
7209 2 : NewRunnableMethod("nsDocument::MaybeInitializeFinalizeFrameLoaders",
7210 : this,
7211 1 : &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7212 1 : NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
7213 1 : nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7214 : }
7215 1 : return NS_OK;
7216 : }
7217 :
7218 : void
7219 741 : nsDocument::MaybeInitializeFinalizeFrameLoaders()
7220 : {
7221 741 : if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
7222 : // This method will be recalled when mUpdateNestLevel drops to 0,
7223 : // or when !mDelayFrameLoaderInitialization.
7224 311 : mFrameLoaderRunner = nullptr;
7225 311 : return;
7226 : }
7227 :
7228 : // We're not in an update, but it is not safe to run scripts, so
7229 : // postpone frameloader initialization and finalization.
7230 430 : if (!nsContentUtils::IsSafeToRunScript()) {
7231 110 : if (!mInDestructor && !mFrameLoaderRunner &&
7232 110 : (mInitializableFrameLoaders.Length() ||
7233 55 : mFrameLoaderFinalizers.Length())) {
7234 : mFrameLoaderRunner =
7235 0 : NewRunnableMethod("nsDocument::MaybeInitializeFinalizeFrameLoaders",
7236 : this,
7237 0 : &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7238 0 : nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7239 : }
7240 55 : return;
7241 : }
7242 375 : mFrameLoaderRunner = nullptr;
7243 :
7244 : // Don't use a temporary array for mInitializableFrameLoaders, because
7245 : // loading a frame may cause some other frameloader to be removed from the
7246 : // array. But be careful to keep the loader alive when starting the load!
7247 381 : while (mInitializableFrameLoaders.Length()) {
7248 6 : RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
7249 3 : mInitializableFrameLoaders.RemoveElementAt(0);
7250 3 : NS_ASSERTION(loader, "null frameloader in the array?");
7251 3 : loader->ReallyStartLoading();
7252 : }
7253 :
7254 375 : uint32_t length = mFrameLoaderFinalizers.Length();
7255 375 : if (length > 0) {
7256 2 : nsTArray<nsCOMPtr<nsIRunnable> > finalizers;
7257 1 : mFrameLoaderFinalizers.SwapElements(finalizers);
7258 2 : for (uint32_t i = 0; i < length; ++i) {
7259 1 : finalizers[i]->Run();
7260 : }
7261 : }
7262 : }
7263 :
7264 : void
7265 2 : nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
7266 : {
7267 2 : uint32_t length = mInitializableFrameLoaders.Length();
7268 3 : for (uint32_t i = 0; i < length; ++i) {
7269 1 : if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
7270 0 : mInitializableFrameLoaders.RemoveElementAt(i);
7271 0 : return;
7272 : }
7273 : }
7274 : }
7275 :
7276 : nsIDocument*
7277 0 : nsDocument::RequestExternalResource(nsIURI* aURI,
7278 : nsINode* aRequestingNode,
7279 : ExternalResourceLoad** aPendingLoad)
7280 : {
7281 0 : NS_PRECONDITION(aURI, "Must have a URI");
7282 0 : NS_PRECONDITION(aRequestingNode, "Must have a node");
7283 0 : if (mDisplayDocument) {
7284 0 : return mDisplayDocument->RequestExternalResource(aURI,
7285 : aRequestingNode,
7286 0 : aPendingLoad);
7287 : }
7288 :
7289 0 : return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
7290 0 : this, aPendingLoad);
7291 : }
7292 :
7293 : void
7294 158 : nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
7295 : {
7296 158 : mExternalResourceMap.EnumerateResources(aCallback, aData);
7297 158 : }
7298 :
7299 : nsSMILAnimationController*
7300 350 : nsDocument::GetAnimationController()
7301 : {
7302 : // We create the animation controller lazily because most documents won't want
7303 : // one and only SVG documents and the like will call this
7304 350 : if (mAnimationController)
7305 328 : return mAnimationController;
7306 : // Refuse to create an Animation Controller for data documents.
7307 22 : if (mLoadedAsData || mLoadedAsInteractiveData)
7308 0 : return nullptr;
7309 :
7310 22 : mAnimationController = new nsSMILAnimationController(this);
7311 :
7312 : // If there's a presContext then check the animation mode and pause if
7313 : // necessary.
7314 22 : nsIPresShell *shell = GetShell();
7315 22 : if (mAnimationController && shell) {
7316 1 : nsPresContext *context = shell->GetPresContext();
7317 2 : if (context &&
7318 1 : context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
7319 0 : mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
7320 : }
7321 : }
7322 :
7323 : // If we're hidden (or being hidden), notify the newly-created animation
7324 : // controller. (Skip this check for SVG-as-an-image documents, though,
7325 : // because they don't get OnPageShow / OnPageHide calls).
7326 22 : if (!mIsShowing && !mIsBeingUsedAsImage) {
7327 1 : mAnimationController->OnPageHide();
7328 : }
7329 :
7330 22 : return mAnimationController;
7331 : }
7332 :
7333 : PendingAnimationTracker*
7334 2 : nsDocument::GetOrCreatePendingAnimationTracker()
7335 : {
7336 2 : if (!mPendingAnimationTracker) {
7337 1 : mPendingAnimationTracker = new PendingAnimationTracker(this);
7338 : }
7339 :
7340 2 : return mPendingAnimationTracker;
7341 : }
7342 :
7343 : /**
7344 : * Retrieve the "direction" property of the document.
7345 : *
7346 : * @lina 01/09/2001
7347 : */
7348 : NS_IMETHODIMP
7349 0 : nsDocument::GetDir(nsAString& aDirection)
7350 : {
7351 0 : nsIDocument::GetDir(aDirection);
7352 0 : return NS_OK;
7353 : }
7354 :
7355 : void
7356 0 : nsIDocument::GetDir(nsAString& aDirection) const
7357 : {
7358 0 : aDirection.Truncate();
7359 0 : Element* rootElement = GetHtmlElement();
7360 0 : if (rootElement) {
7361 0 : static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
7362 : }
7363 0 : }
7364 :
7365 : /**
7366 : * Set the "direction" property of the document.
7367 : *
7368 : * @lina 01/09/2001
7369 : */
7370 : NS_IMETHODIMP
7371 0 : nsDocument::SetDir(const nsAString& aDirection)
7372 : {
7373 0 : nsIDocument::SetDir(aDirection);
7374 0 : return NS_OK;
7375 : }
7376 :
7377 : void
7378 0 : nsIDocument::SetDir(const nsAString& aDirection)
7379 : {
7380 0 : Element* rootElement = GetHtmlElement();
7381 0 : if (rootElement) {
7382 : rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
7383 0 : aDirection, true);
7384 : }
7385 0 : }
7386 :
7387 : NS_IMETHODIMP
7388 0 : nsDocument::GetInputEncoding(nsAString& aInputEncoding)
7389 : {
7390 0 : nsIDocument::GetCharacterSet(aInputEncoding);
7391 0 : return NS_OK;
7392 : }
7393 :
7394 : NS_IMETHODIMP
7395 0 : nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument)
7396 : {
7397 0 : *aSyntheticDocument = mIsSyntheticDocument;
7398 0 : return NS_OK;
7399 : }
7400 :
7401 : NS_IMETHODIMP
7402 0 : nsDocument::GetDocumentURI(nsAString& aDocumentURI)
7403 : {
7404 0 : nsString temp;
7405 0 : nsresult rv = nsIDocument::GetDocumentURI(temp);
7406 0 : aDocumentURI = temp;
7407 0 : return rv;
7408 : }
7409 :
7410 : nsresult
7411 12 : nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
7412 : {
7413 12 : if (mDocumentURI) {
7414 24 : nsAutoCString uri;
7415 12 : nsresult rv = mDocumentURI->GetSpec(uri);
7416 12 : NS_ENSURE_SUCCESS(rv, rv);
7417 :
7418 12 : CopyUTF8toUTF16(uri, aDocumentURI);
7419 : } else {
7420 0 : aDocumentURI.Truncate();
7421 : }
7422 :
7423 12 : return NS_OK;
7424 : }
7425 :
7426 : // Alias of above
7427 : NS_IMETHODIMP
7428 0 : nsDocument::GetURL(nsAString& aURL)
7429 : {
7430 0 : return GetDocumentURI(aURL);
7431 : }
7432 :
7433 : nsresult
7434 0 : nsIDocument::GetURL(nsString& aURL) const
7435 : {
7436 0 : return GetDocumentURI(aURL);
7437 : }
7438 :
7439 : void
7440 12 : nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI, CallerType aCallerType,
7441 : ErrorResult& aRv) const
7442 : {
7443 12 : if (!mChromeXHRDocURI || aCallerType != CallerType::System) {
7444 12 : aRv = GetDocumentURI(aDocumentURI);
7445 12 : return;
7446 : }
7447 :
7448 0 : nsAutoCString uri;
7449 0 : nsresult res = mChromeXHRDocURI->GetSpec(uri);
7450 0 : if (NS_FAILED(res)) {
7451 0 : aRv.Throw(res);
7452 0 : return;
7453 : }
7454 0 : CopyUTF8toUTF16(uri, aDocumentURI);
7455 : }
7456 :
7457 : nsIURI*
7458 7 : nsIDocument::GetDocumentURIObject() const
7459 : {
7460 7 : if (!mChromeXHRDocURI) {
7461 7 : return GetDocumentURI();
7462 : }
7463 :
7464 0 : return mChromeXHRDocURI;
7465 : }
7466 :
7467 : void
7468 0 : nsIDocument::GetCompatMode(nsString& aCompatMode) const
7469 : {
7470 0 : NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
7471 : mCompatMode == eCompatibility_AlmostStandards ||
7472 : mCompatMode == eCompatibility_FullStandards,
7473 : "mCompatMode is neither quirks nor strict for this document");
7474 :
7475 0 : if (mCompatMode == eCompatibility_NavQuirks) {
7476 0 : aCompatMode.AssignLiteral("BackCompat");
7477 : } else {
7478 0 : aCompatMode.AssignLiteral("CSS1Compat");
7479 : }
7480 0 : }
7481 :
7482 : void
7483 0 : nsDOMAttributeMap::BlastSubtreeToPieces(nsINode *aNode)
7484 : {
7485 0 : if (aNode->IsElement()) {
7486 0 : Element *element = aNode->AsElement();
7487 0 : const nsDOMAttributeMap *map = element->GetAttributeMap();
7488 0 : if (map) {
7489 : // This non-standard style of iteration is presumably used because some
7490 : // of the code in the loop body can trigger element removal, which
7491 : // invalidates the iterator.
7492 : while (true) {
7493 0 : auto iter = map->mAttributeCache.ConstIter();
7494 0 : if (iter.Done()) {
7495 0 : break;
7496 : }
7497 0 : nsCOMPtr<nsIAttribute> attr = iter.UserData();
7498 0 : NS_ASSERTION(attr.get(),
7499 : "non-nsIAttribute somehow made it into the hashmap?!");
7500 :
7501 0 : BlastSubtreeToPieces(attr);
7502 :
7503 : DebugOnly<nsresult> rv =
7504 0 : element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
7505 : attr->NodeInfo()->NameAtom(),
7506 0 : false);
7507 :
7508 : // XXX Should we abort here?
7509 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
7510 0 : }
7511 : }
7512 : }
7513 :
7514 0 : uint32_t count = aNode->GetChildCount();
7515 0 : for (uint32_t i = 0; i < count; ++i) {
7516 0 : BlastSubtreeToPieces(aNode->GetFirstChild());
7517 0 : aNode->RemoveChildAt(0, false);
7518 : }
7519 0 : }
7520 :
7521 : NS_IMETHODIMP
7522 0 : nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
7523 : {
7524 0 : *aResult = nullptr;
7525 :
7526 0 : nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
7527 0 : NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED);
7528 :
7529 0 : ErrorResult rv;
7530 0 : nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv);
7531 0 : if (rv.Failed()) {
7532 0 : return rv.StealNSResult();
7533 : }
7534 :
7535 0 : NS_ADDREF(*aResult = result->AsDOMNode());
7536 0 : return NS_OK;
7537 : }
7538 :
7539 : nsINode*
7540 0 : nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
7541 : {
7542 0 : nsINode* adoptedNode = &aAdoptedNode;
7543 :
7544 : // Scope firing mutation events so that we don't carry any state that
7545 : // might be stale
7546 : {
7547 0 : nsINode* parent = adoptedNode->GetParentNode();
7548 0 : if (parent) {
7549 0 : nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
7550 0 : adoptedNode->OwnerDoc());
7551 : }
7552 : }
7553 :
7554 0 : nsAutoScriptBlocker scriptBlocker;
7555 :
7556 0 : switch (adoptedNode->NodeType()) {
7557 : case nsIDOMNode::ATTRIBUTE_NODE:
7558 : {
7559 : // Remove from ownerElement.
7560 0 : RefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
7561 :
7562 0 : nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
7563 0 : if (rv.Failed()) {
7564 0 : return nullptr;
7565 : }
7566 :
7567 0 : if (ownerElement) {
7568 : RefPtr<Attr> newAttr =
7569 0 : ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
7570 0 : if (rv.Failed()) {
7571 0 : return nullptr;
7572 : }
7573 :
7574 0 : newAttr.swap(adoptedAttr);
7575 : }
7576 :
7577 0 : break;
7578 : }
7579 : case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
7580 : {
7581 0 : if (ShadowRoot::FromNode(adoptedNode)) {
7582 0 : rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7583 0 : return nullptr;
7584 : }
7585 : MOZ_FALLTHROUGH;
7586 : }
7587 : case nsIDOMNode::ELEMENT_NODE:
7588 : case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
7589 : case nsIDOMNode::TEXT_NODE:
7590 : case nsIDOMNode::CDATA_SECTION_NODE:
7591 : case nsIDOMNode::COMMENT_NODE:
7592 : case nsIDOMNode::DOCUMENT_TYPE_NODE:
7593 : {
7594 : // Don't allow adopting a node's anonymous subtree out from under it.
7595 0 : if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
7596 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7597 0 : return nullptr;
7598 : }
7599 :
7600 : // We don't want to adopt an element into its own contentDocument or into
7601 : // a descendant contentDocument, so we check if the frameElement of this
7602 : // document or any of its parents is the adopted node or one of its
7603 : // descendants.
7604 0 : nsIDocument *doc = this;
7605 0 : do {
7606 0 : if (nsPIDOMWindowOuter *win = doc->GetWindow()) {
7607 0 : nsCOMPtr<nsINode> node = win->GetFrameElementInternal();
7608 0 : if (node &&
7609 0 : nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
7610 0 : rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7611 0 : return nullptr;
7612 : }
7613 : }
7614 : } while ((doc = doc->GetParentDocument()));
7615 :
7616 : // Remove from parent.
7617 0 : nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
7618 0 : if (parent) {
7619 0 : int32_t idx = parent->IndexOf(adoptedNode);
7620 0 : MOZ_ASSERT(idx >= 0);
7621 0 : parent->RemoveChildAt(idx, true);
7622 : } else {
7623 0 : MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc());
7624 :
7625 : // If we're adopting a node that's not in a document, it might still
7626 : // have a binding applied. Remove the binding from the element now
7627 : // that it's getting adopted into a new document.
7628 : // TODO Fully tear down the binding.
7629 0 : adoptedNode->AsContent()->SetXBLBinding(nullptr);
7630 : }
7631 :
7632 0 : break;
7633 : }
7634 : case nsIDOMNode::DOCUMENT_NODE:
7635 : {
7636 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7637 0 : return nullptr;
7638 : }
7639 : default:
7640 : {
7641 0 : NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
7642 :
7643 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7644 0 : return nullptr;
7645 : }
7646 : }
7647 :
7648 0 : nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
7649 0 : bool sameDocument = oldDocument == this;
7650 :
7651 0 : AutoJSContext cx;
7652 0 : JS::Rooted<JSObject*> newScope(cx, nullptr);
7653 0 : if (!sameDocument) {
7654 0 : newScope = GetWrapper();
7655 0 : if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
7656 : // Make sure cx is in a semi-sane compartment before we call WrapNative.
7657 : // It's kind of irrelevant, given that we're passing aAllowWrapping =
7658 : // false, and documents should always insist on being wrapped in an
7659 : // canonical scope. But we try to pass something sane anyway.
7660 0 : JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject());
7661 0 : JS::Rooted<JS::Value> v(cx);
7662 0 : rv = nsContentUtils::WrapNative(cx, this, this, &v,
7663 0 : /* aAllowWrapping = */ false);
7664 0 : if (rv.Failed())
7665 0 : return nullptr;
7666 0 : newScope = &v.toObject();
7667 : }
7668 : }
7669 :
7670 0 : nsCOMArray<nsINode> nodesWithProperties;
7671 0 : rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
7672 0 : newScope, nodesWithProperties);
7673 0 : if (rv.Failed()) {
7674 : // Disconnect all nodes from their parents, since some have the old document
7675 : // as their ownerDocument and some have this as their ownerDocument.
7676 0 : nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
7677 :
7678 0 : if (!sameDocument && oldDocument) {
7679 0 : uint32_t count = nodesWithProperties.Count();
7680 0 : for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7681 0 : for (uint32_t i = 0; i < count; ++i) {
7682 : // Remove all properties.
7683 : oldDocument->PropertyTable(j)->
7684 0 : DeleteAllPropertiesFor(nodesWithProperties[i]);
7685 : }
7686 : }
7687 : }
7688 :
7689 0 : return nullptr;
7690 : }
7691 :
7692 0 : uint32_t count = nodesWithProperties.Count();
7693 0 : if (!sameDocument && oldDocument) {
7694 0 : for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7695 0 : nsPropertyTable *oldTable = oldDocument->PropertyTable(j);
7696 0 : nsPropertyTable *newTable = PropertyTable(j);
7697 0 : for (uint32_t i = 0; i < count; ++i) {
7698 0 : rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
7699 0 : newTable);
7700 : }
7701 : }
7702 :
7703 0 : if (rv.Failed()) {
7704 : // Disconnect all nodes from their parents.
7705 0 : nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
7706 :
7707 0 : return nullptr;
7708 : }
7709 : }
7710 :
7711 0 : NS_ASSERTION(adoptedNode->OwnerDoc() == this,
7712 : "Should still be in the document we just got adopted into");
7713 :
7714 0 : return adoptedNode;
7715 : }
7716 :
7717 : nsViewportInfo
7718 1 : nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
7719 : {
7720 : // Compute the CSS-to-LayoutDevice pixel scale as the product of the
7721 : // widget scale and the full zoom.
7722 1 : nsPresContext* context = mPresShell->GetPresContext();
7723 : // When querying the full zoom, get it from the device context rather than
7724 : // directly from the pres context, because the device context's value can
7725 : // include an adjustment necessay to keep the number of app units per device
7726 : // pixel an integer, and we want the adjusted value.
7727 1 : float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
7728 1 : fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
7729 1 : CSSToLayoutDeviceScale layoutDeviceScale = context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
7730 :
7731 : CSSToScreenScale defaultScale = layoutDeviceScale
7732 1 : * LayoutDeviceToScreenScale(1.0);
7733 :
7734 : // Special behaviour for desktop mode, provided we are not on an about: page
7735 1 : nsPIDOMWindowOuter* win = GetWindow();
7736 1 : if (win && win->IsDesktopModeViewport() && !IsAboutPage())
7737 : {
7738 0 : CSSCoord viewportWidth = gfxPrefs::DesktopViewportWidth() / fullZoom;
7739 0 : CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth);
7740 0 : float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
7741 0 : CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
7742 0 : ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
7743 : return nsViewportInfo(fakeDesktopSize,
7744 : scaleToFit,
7745 0 : /*allowZoom*/ true);
7746 : }
7747 :
7748 1 : if (!gfxPrefs::MetaViewportEnabled()) {
7749 : return nsViewportInfo(aDisplaySize,
7750 : defaultScale,
7751 1 : /*allowZoom*/ false);
7752 : }
7753 :
7754 : // In cases where the width of the CSS viewport is less than or equal to the width
7755 : // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
7756 : // behaviour. See bug 941995 for details.
7757 :
7758 0 : switch (mViewportType) {
7759 : case DisplayWidthHeight:
7760 : return nsViewportInfo(aDisplaySize,
7761 : defaultScale,
7762 0 : /*allowZoom*/ true);
7763 : case Unknown:
7764 : {
7765 0 : nsAutoString viewport;
7766 0 : GetHeaderData(nsGkAtoms::viewport, viewport);
7767 0 : if (viewport.IsEmpty()) {
7768 : // If the docType specifies that we are on a site optimized for mobile,
7769 : // then we want to return specially crafted defaults for the viewport info.
7770 0 : nsCOMPtr<nsIDOMDocumentType> docType;
7771 0 : nsresult rv = GetDoctype(getter_AddRefs(docType));
7772 0 : if (NS_SUCCEEDED(rv) && docType) {
7773 0 : nsAutoString docId;
7774 0 : rv = docType->GetPublicId(docId);
7775 0 : if (NS_SUCCEEDED(rv)) {
7776 0 : if ((docId.Find("WAP") != -1) ||
7777 0 : (docId.Find("Mobile") != -1) ||
7778 0 : (docId.Find("WML") != -1))
7779 : {
7780 : // We're making an assumption that the docType can't change here
7781 0 : mViewportType = DisplayWidthHeight;
7782 : return nsViewportInfo(aDisplaySize,
7783 : defaultScale,
7784 0 : /*allowZoom*/true);
7785 : }
7786 : }
7787 : }
7788 :
7789 0 : nsAutoString handheldFriendly;
7790 0 : GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
7791 0 : if (handheldFriendly.EqualsLiteral("true")) {
7792 0 : mViewportType = DisplayWidthHeight;
7793 : return nsViewportInfo(aDisplaySize,
7794 : defaultScale,
7795 0 : /*allowZoom*/true);
7796 : }
7797 : }
7798 :
7799 0 : nsAutoString minScaleStr;
7800 0 : GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
7801 :
7802 : nsresult errorCode;
7803 0 : mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
7804 :
7805 0 : if (NS_FAILED(errorCode)) {
7806 0 : mScaleMinFloat = kViewportMinScale;
7807 : }
7808 :
7809 0 : mScaleMinFloat = mozilla::clamped(
7810 0 : mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
7811 :
7812 0 : nsAutoString maxScaleStr;
7813 0 : GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
7814 :
7815 : // We define a special error code variable for the scale and max scale,
7816 : // because they are used later (see the width calculations).
7817 : nsresult scaleMaxErrorCode;
7818 0 : mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
7819 :
7820 0 : if (NS_FAILED(scaleMaxErrorCode)) {
7821 0 : mScaleMaxFloat = kViewportMaxScale;
7822 : }
7823 :
7824 0 : mScaleMaxFloat = mozilla::clamped(
7825 0 : mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
7826 :
7827 0 : nsAutoString scaleStr;
7828 0 : GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
7829 :
7830 : nsresult scaleErrorCode;
7831 0 : mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
7832 :
7833 0 : nsAutoString widthStr, heightStr;
7834 :
7835 0 : GetHeaderData(nsGkAtoms::viewport_height, heightStr);
7836 0 : GetHeaderData(nsGkAtoms::viewport_width, widthStr);
7837 :
7838 0 : mAutoSize = false;
7839 :
7840 0 : if (widthStr.EqualsLiteral("device-width")) {
7841 0 : mAutoSize = true;
7842 : }
7843 :
7844 0 : if (widthStr.IsEmpty() &&
7845 0 : (heightStr.EqualsLiteral("device-height") ||
7846 0 : (mScaleFloat.scale == 1.0)))
7847 : {
7848 0 : mAutoSize = true;
7849 : }
7850 :
7851 : nsresult widthErrorCode, heightErrorCode;
7852 0 : mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
7853 0 : mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
7854 :
7855 : // If width or height has not been set to a valid number by this point,
7856 : // fall back to a default value.
7857 0 : mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
7858 0 : mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
7859 :
7860 0 : mAllowZoom = true;
7861 0 : nsAutoString userScalable;
7862 0 : GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
7863 :
7864 0 : if ((userScalable.EqualsLiteral("0")) ||
7865 0 : (userScalable.EqualsLiteral("no")) ||
7866 0 : (userScalable.EqualsLiteral("false"))) {
7867 0 : mAllowZoom = false;
7868 : }
7869 :
7870 0 : mScaleStrEmpty = scaleStr.IsEmpty();
7871 0 : mWidthStrEmpty = widthStr.IsEmpty();
7872 0 : mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
7873 0 : mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
7874 :
7875 0 : mViewportType = Specified;
7876 : MOZ_FALLTHROUGH;
7877 : }
7878 : case Specified:
7879 : default:
7880 0 : LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
7881 0 : LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
7882 0 : bool effectiveValidMaxScale = mValidMaxScale;
7883 0 : bool effectiveAllowZoom = mAllowZoom;
7884 0 : if (gfxPrefs::ForceUserScalable()) {
7885 : // If the pref to force user-scalable is enabled, we ignore the values
7886 : // from the meta-viewport tag for these properties and just assume they
7887 : // allow the page to be scalable. Note in particular that this code is
7888 : // in the "Specified" branch of the enclosing switch statement, so that
7889 : // calls to GetViewportInfo always use the latest value of the
7890 : // ForceUserScalable pref. Other codepaths that return nsViewportInfo
7891 : // instances are all consistent with ForceUserScalable() already.
7892 0 : effectiveMinScale = kViewportMinScale;
7893 0 : effectiveMaxScale = kViewportMaxScale;
7894 0 : effectiveValidMaxScale = true;
7895 0 : effectiveAllowZoom = true;
7896 : }
7897 :
7898 0 : CSSSize size = mViewportSize;
7899 :
7900 0 : if (!mValidWidth) {
7901 0 : if (mValidHeight && !aDisplaySize.IsEmpty()) {
7902 0 : size.width = size.height * aDisplaySize.width / aDisplaySize.height;
7903 : } else {
7904 : // Stretch CSS pixel size of viewport to keep device pixel size
7905 : // unchanged after full zoom applied.
7906 : // See bug 974242.
7907 0 : size.width = gfxPrefs::DesktopViewportWidth() / fullZoom;
7908 : }
7909 : }
7910 :
7911 0 : if (!mValidHeight) {
7912 0 : if (!aDisplaySize.IsEmpty()) {
7913 0 : size.height = size.width * aDisplaySize.height / aDisplaySize.width;
7914 : } else {
7915 0 : size.height = size.width;
7916 : }
7917 : }
7918 :
7919 0 : CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
7920 0 : CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
7921 0 : CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
7922 :
7923 0 : if (mAutoSize) {
7924 : // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
7925 0 : CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f);
7926 0 : size = ScreenSize(aDisplaySize) / defaultPixelScale;
7927 : }
7928 :
7929 0 : size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width));
7930 :
7931 : // Also recalculate the default zoom, if it wasn't specified in the metadata,
7932 : // and the width is specified.
7933 0 : if (mScaleStrEmpty && !mWidthStrEmpty) {
7934 0 : CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width);
7935 0 : scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
7936 : }
7937 :
7938 0 : size.height = clamped(size.height, float(kViewportMinSize.height), float(kViewportMaxSize.height));
7939 :
7940 : // We need to perform a conversion, but only if the initial or maximum
7941 : // scale were set explicitly by the user.
7942 0 : if (mValidScaleFloat && scaleFloat >= scaleMinFloat && scaleFloat <= scaleMaxFloat) {
7943 0 : CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
7944 0 : size.width = std::max(size.width, displaySize.width);
7945 0 : size.height = std::max(size.height, displaySize.height);
7946 0 : } else if (effectiveValidMaxScale) {
7947 0 : CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
7948 0 : size.width = std::max(size.width, displaySize.width);
7949 0 : size.height = std::max(size.height, displaySize.height);
7950 : }
7951 :
7952 : return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
7953 0 : mAutoSize, effectiveAllowZoom);
7954 : }
7955 : }
7956 :
7957 : EventListenerManager*
7958 182 : nsDocument::GetOrCreateListenerManager()
7959 : {
7960 182 : if (!mListenerManager) {
7961 : mListenerManager =
7962 28 : new EventListenerManager(static_cast<EventTarget*>(this));
7963 28 : SetFlags(NODE_HAS_LISTENERMANAGER);
7964 : }
7965 :
7966 182 : return mListenerManager;
7967 : }
7968 :
7969 : EventListenerManager*
7970 562 : nsDocument::GetExistingListenerManager() const
7971 : {
7972 562 : return mListenerManager;
7973 : }
7974 :
7975 : nsresult
7976 388 : nsDocument::GetEventTargetParent(EventChainPreVisitor& aVisitor)
7977 : {
7978 559 : if (mDocGroup && aVisitor.mEvent->mMessage != eVoidEvent &&
7979 171 : !mIgnoreDocGroupMismatches) {
7980 168 : mDocGroup->ValidateAccess();
7981 : }
7982 :
7983 388 : aVisitor.mCanHandle = true;
7984 : // FIXME! This is a hack to make middle mouse paste working also in Editor.
7985 : // Bug 329119
7986 388 : aVisitor.mForceContentDispatch = true;
7987 :
7988 : // Load events must not propagate to |window| object, see bug 335251.
7989 388 : if (aVisitor.mEvent->mMessage != eLoad) {
7990 319 : nsGlobalWindow* window = nsGlobalWindow::Cast(GetWindow());
7991 319 : aVisitor.mParentTarget =
7992 319 : window ? window->GetTargetForEventTargetChain() : nullptr;
7993 : }
7994 388 : return NS_OK;
7995 : }
7996 :
7997 : NS_IMETHODIMP
7998 124 : nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
7999 : {
8000 124 : NS_ENSURE_ARG_POINTER(aReturn);
8001 248 : ErrorResult rv;
8002 248 : *aReturn = nsIDocument::CreateEvent(aEventType, CallerType::System,
8003 124 : rv).take();
8004 124 : return rv.StealNSResult();
8005 : }
8006 :
8007 : already_AddRefed<Event>
8008 127 : nsIDocument::CreateEvent(const nsAString& aEventType, CallerType aCallerType,
8009 : ErrorResult& rv) const
8010 : {
8011 127 : nsIPresShell *shell = GetShell();
8012 :
8013 127 : nsPresContext *presContext = nullptr;
8014 :
8015 127 : if (shell) {
8016 : // Retrieve the context
8017 119 : presContext = shell->GetPresContext();
8018 : }
8019 :
8020 : // Create event even without presContext.
8021 : RefPtr<Event> ev =
8022 254 : EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this), presContext,
8023 254 : nullptr, aEventType, aCallerType);
8024 127 : if (!ev) {
8025 0 : rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
8026 0 : return nullptr;
8027 : }
8028 127 : WidgetEvent* e = ev->WidgetEventPtr();
8029 127 : e->mFlags.mBubbles = false;
8030 127 : e->mFlags.mCancelable = false;
8031 127 : return ev.forget();
8032 : }
8033 :
8034 : void
8035 594 : nsDocument::FlushPendingNotifications(FlushType aType)
8036 : {
8037 615 : nsDocumentOnStack dos(this);
8038 :
8039 : // We need to flush the sink for non-HTML documents (because the XML
8040 : // parser still does insertion with deferred notifications). We
8041 : // also need to flush the sink if this is a layout-related flush, to
8042 : // make sure that layout is started as needed. But we can skip that
8043 : // part if we have no presshell or if it's already done an initial
8044 : // reflow.
8045 1245 : if ((!IsHTMLDocument() ||
8046 65 : (aType > FlushType::ContentAndNotify && mPresShell &&
8047 1192 : !mPresShell->DidInitialize())) &&
8048 1076 : (mParser || mWeakSink)) {
8049 114 : nsCOMPtr<nsIContentSink> sink;
8050 57 : if (mParser) {
8051 0 : sink = mParser->GetContentSink();
8052 : } else {
8053 57 : sink = do_QueryReferent(mWeakSink);
8054 57 : if (!sink) {
8055 0 : mWeakSink = nullptr;
8056 : }
8057 : }
8058 : // Determine if it is safe to flush the sink notifications
8059 : // by determining if it safe to flush all the presshells.
8060 57 : if (sink && (aType == FlushType::Content || IsSafeToFlush())) {
8061 57 : sink->FlushPendingNotifications(aType);
8062 : }
8063 : }
8064 :
8065 : // Should we be flushing pending binding constructors in here?
8066 :
8067 594 : if (aType <= FlushType::ContentAndNotify) {
8068 : // Nothing to do here
8069 573 : return;
8070 : }
8071 :
8072 : // If we have a parent we must flush the parent too to ensure that our
8073 : // container is reflowed if its size was changed. But if it's not safe to
8074 : // flush ourselves, then don't flush the parent, since that can cause things
8075 : // like resizes of our frame's widget, which we can't handle while flushing
8076 : // is unsafe.
8077 : // Since media queries mean that a size change of our container can
8078 : // affect style, we need to promote a style flush on ourself to a
8079 : // layout flush on our parent, since we need our container to be the
8080 : // correct size to determine the correct style.
8081 21 : if (mParentDocument && IsSafeToFlush()) {
8082 1 : FlushType parentType = aType;
8083 1 : if (aType >= FlushType::Style)
8084 1 : parentType = std::max(FlushType::Layout, aType);
8085 1 : mParentDocument->FlushPendingNotifications(parentType);
8086 : }
8087 :
8088 21 : if (nsIPresShell* shell = GetShell()) {
8089 19 : shell->FlushPendingNotifications(aType);
8090 : }
8091 : }
8092 :
8093 : static bool
8094 0 : Copy(nsIDocument* aDocument, void* aData)
8095 : {
8096 : nsTArray<nsCOMPtr<nsIDocument> >* resources =
8097 0 : static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
8098 0 : resources->AppendElement(aDocument);
8099 0 : return true;
8100 : }
8101 :
8102 : void
8103 122 : nsDocument::FlushExternalResources(FlushType aType)
8104 : {
8105 122 : NS_ASSERTION(aType >= FlushType::Style,
8106 : "should only need to flush for style or higher in external resources");
8107 122 : if (GetDisplayDocument()) {
8108 0 : return;
8109 : }
8110 244 : nsTArray<nsCOMPtr<nsIDocument> > resources;
8111 122 : EnumerateExternalResources(Copy, &resources);
8112 :
8113 122 : for (uint32_t i = 0; i < resources.Length(); i++) {
8114 0 : resources[i]->FlushPendingNotifications(aType);
8115 : }
8116 : }
8117 :
8118 : void
8119 4 : nsDocument::SetXMLDeclaration(const char16_t *aVersion,
8120 : const char16_t *aEncoding,
8121 : const int32_t aStandalone)
8122 : {
8123 4 : if (!aVersion || *aVersion == '\0') {
8124 0 : mXMLDeclarationBits = 0;
8125 0 : return;
8126 : }
8127 :
8128 4 : mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
8129 :
8130 4 : if (aEncoding && *aEncoding != '\0') {
8131 3 : mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
8132 : }
8133 :
8134 4 : if (aStandalone == 1) {
8135 0 : mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
8136 0 : XML_DECLARATION_BITS_STANDALONE_YES;
8137 : }
8138 4 : else if (aStandalone == 0) {
8139 0 : mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
8140 : }
8141 : }
8142 :
8143 : void
8144 0 : nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
8145 : nsAString& aStandalone)
8146 : {
8147 0 : aVersion.Truncate();
8148 0 : aEncoding.Truncate();
8149 0 : aStandalone.Truncate();
8150 :
8151 0 : if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
8152 0 : return;
8153 : }
8154 :
8155 : // always until we start supporting 1.1 etc.
8156 0 : aVersion.AssignLiteral("1.0");
8157 :
8158 0 : if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
8159 : // This is what we have stored, not necessarily what was written
8160 : // in the original
8161 0 : GetCharacterSet(aEncoding);
8162 : }
8163 :
8164 0 : if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
8165 0 : if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
8166 0 : aStandalone.AssignLiteral("yes");
8167 : } else {
8168 0 : aStandalone.AssignLiteral("no");
8169 : }
8170 : }
8171 : }
8172 :
8173 : bool
8174 18 : nsDocument::IsScriptEnabled()
8175 : {
8176 : // If this document is sandboxed without 'allow-scripts'
8177 : // script is not enabled
8178 18 : if (HasScriptsBlockedBySandbox()) {
8179 0 : return false;
8180 : }
8181 :
8182 36 : nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
8183 18 : if (!globalObject || !globalObject->GetGlobalJSObject()) {
8184 2 : return false;
8185 : }
8186 :
8187 16 : return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
8188 : }
8189 :
8190 : nsRadioGroupStruct*
8191 0 : nsDocument::GetRadioGroup(const nsAString& aName) const
8192 : {
8193 0 : nsRadioGroupStruct* radioGroup = nullptr;
8194 0 : mRadioGroups.Get(aName, &radioGroup);
8195 0 : return radioGroup;
8196 : }
8197 :
8198 : nsRadioGroupStruct*
8199 0 : nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
8200 : {
8201 0 : return mRadioGroups.LookupForAdd(aName).OrInsert(
8202 0 : [] () { return new nsRadioGroupStruct(); });
8203 : }
8204 :
8205 : void
8206 0 : nsDocument::SetCurrentRadioButton(const nsAString& aName,
8207 : HTMLInputElement* aRadio)
8208 : {
8209 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8210 0 : radioGroup->mSelectedRadioButton = aRadio;
8211 0 : }
8212 :
8213 : HTMLInputElement*
8214 0 : nsDocument::GetCurrentRadioButton(const nsAString& aName)
8215 : {
8216 0 : return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
8217 : }
8218 :
8219 : NS_IMETHODIMP
8220 0 : nsDocument::GetNextRadioButton(const nsAString& aName,
8221 : const bool aPrevious,
8222 : HTMLInputElement* aFocusedRadio,
8223 : HTMLInputElement** aRadioOut)
8224 : {
8225 : // XXX Can we combine the HTML radio button method impls of
8226 : // nsDocument and nsHTMLFormControl?
8227 : // XXX Why is HTML radio button stuff in nsDocument, as
8228 : // opposed to nsHTMLDocument?
8229 0 : *aRadioOut = nullptr;
8230 :
8231 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8232 :
8233 : // Return the radio button relative to the focused radio button.
8234 : // If no radio is focused, get the radio relative to the selected one.
8235 0 : RefPtr<HTMLInputElement> currentRadio;
8236 0 : if (aFocusedRadio) {
8237 0 : currentRadio = aFocusedRadio;
8238 : }
8239 : else {
8240 0 : currentRadio = radioGroup->mSelectedRadioButton;
8241 0 : if (!currentRadio) {
8242 0 : return NS_ERROR_FAILURE;
8243 : }
8244 : }
8245 0 : int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
8246 0 : if (index < 0) {
8247 0 : return NS_ERROR_FAILURE;
8248 : }
8249 :
8250 0 : int32_t numRadios = radioGroup->mRadioButtons.Count();
8251 0 : RefPtr<HTMLInputElement> radio;
8252 0 : do {
8253 0 : if (aPrevious) {
8254 0 : if (--index < 0) {
8255 0 : index = numRadios -1;
8256 : }
8257 : }
8258 0 : else if (++index >= numRadios) {
8259 0 : index = 0;
8260 : }
8261 0 : NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTMLElement(nsGkAtoms::input),
8262 : "mRadioButtons holding a non-radio button");
8263 0 : radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
8264 0 : } while (radio->Disabled() && radio != currentRadio);
8265 :
8266 0 : radio.forget(aRadioOut);
8267 0 : return NS_OK;
8268 : }
8269 :
8270 : void
8271 0 : nsDocument::AddToRadioGroup(const nsAString& aName,
8272 : nsIFormControl* aRadio)
8273 : {
8274 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8275 0 : radioGroup->mRadioButtons.AppendObject(aRadio);
8276 :
8277 0 : nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8278 0 : NS_ASSERTION(element, "radio controls have to be content elements");
8279 0 : if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8280 0 : radioGroup->mRequiredRadioCount++;
8281 : }
8282 0 : }
8283 :
8284 : void
8285 0 : nsDocument::RemoveFromRadioGroup(const nsAString& aName,
8286 : nsIFormControl* aRadio)
8287 : {
8288 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8289 0 : radioGroup->mRadioButtons.RemoveObject(aRadio);
8290 :
8291 0 : nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8292 0 : NS_ASSERTION(element, "radio controls have to be content elements");
8293 0 : if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8294 0 : NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8295 : "mRequiredRadioCount about to wrap below 0!");
8296 0 : radioGroup->mRequiredRadioCount--;
8297 : }
8298 0 : }
8299 :
8300 : NS_IMETHODIMP
8301 0 : nsDocument::WalkRadioGroup(const nsAString& aName,
8302 : nsIRadioVisitor* aVisitor,
8303 : bool aFlushContent)
8304 : {
8305 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8306 :
8307 0 : for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
8308 0 : if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
8309 0 : return NS_OK;
8310 : }
8311 : }
8312 :
8313 0 : return NS_OK;
8314 : }
8315 :
8316 : uint32_t
8317 0 : nsDocument::GetRequiredRadioCount(const nsAString& aName) const
8318 : {
8319 0 : nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8320 0 : return radioGroup ? radioGroup->mRequiredRadioCount : 0;
8321 : }
8322 :
8323 : void
8324 0 : nsDocument::RadioRequiredWillChange(const nsAString& aName, bool aRequiredAdded)
8325 : {
8326 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8327 :
8328 0 : if (aRequiredAdded) {
8329 0 : radioGroup->mRequiredRadioCount++;
8330 : } else {
8331 0 : NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8332 : "mRequiredRadioCount about to wrap below 0!");
8333 0 : radioGroup->mRequiredRadioCount--;
8334 : }
8335 0 : }
8336 :
8337 : bool
8338 0 : nsDocument::GetValueMissingState(const nsAString& aName) const
8339 : {
8340 0 : nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8341 0 : return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
8342 : }
8343 :
8344 : void
8345 0 : nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
8346 : {
8347 0 : nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8348 0 : radioGroup->mGroupSuffersFromValueMissing = aValue;
8349 0 : }
8350 :
8351 : void
8352 26 : nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
8353 : {
8354 26 : PRTime modDate = 0;
8355 : nsresult rv;
8356 :
8357 52 : nsCOMPtr<nsIHttpChannel> httpChannel;
8358 26 : rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
8359 26 : if (NS_WARN_IF(NS_FAILED(rv))) {
8360 0 : return;
8361 : }
8362 :
8363 26 : if (httpChannel) {
8364 2 : nsAutoCString tmp;
8365 4 : rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
8366 3 : tmp);
8367 :
8368 1 : if (NS_SUCCEEDED(rv)) {
8369 : PRTime time;
8370 1 : PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
8371 1 : if (st == PR_SUCCESS) {
8372 1 : modDate = time;
8373 : }
8374 : }
8375 :
8376 : // The misspelled key 'referer' is as per the HTTP spec
8377 4 : rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
8378 3 : mReferrer);
8379 :
8380 : static const char *const headers[] = {
8381 : "default-style",
8382 : "content-style-type",
8383 : "content-language",
8384 : "content-disposition",
8385 : "refresh",
8386 : "x-dns-prefetch-control",
8387 : "x-frame-options",
8388 : "referrer-policy",
8389 : // add more http headers if you need
8390 : // XXXbz don't add content-location support without reading bug
8391 : // 238654 and its dependencies/dups first.
8392 : 0
8393 : };
8394 :
8395 2 : nsAutoCString headerVal;
8396 1 : const char *const *name = headers;
8397 17 : while (*name) {
8398 : rv =
8399 8 : httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
8400 8 : if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
8401 0 : nsCOMPtr<nsIAtom> key = NS_Atomize(*name);
8402 0 : SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
8403 : }
8404 8 : ++name;
8405 : }
8406 : } else {
8407 50 : nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
8408 25 : if (fileChannel) {
8409 48 : nsCOMPtr<nsIFile> file;
8410 24 : fileChannel->GetFile(getter_AddRefs(file));
8411 24 : if (file) {
8412 : PRTime msecs;
8413 24 : rv = file->GetLastModifiedTime(&msecs);
8414 :
8415 24 : if (NS_SUCCEEDED(rv)) {
8416 24 : modDate = msecs * int64_t(PR_USEC_PER_MSEC);
8417 : }
8418 : }
8419 : } else {
8420 2 : nsAutoCString contentDisp;
8421 1 : rv = aChannel->GetContentDispositionHeader(contentDisp);
8422 1 : if (NS_SUCCEEDED(rv)) {
8423 : SetHeaderData(nsGkAtoms::headerContentDisposition,
8424 0 : NS_ConvertASCIItoUTF16(contentDisp));
8425 : }
8426 : }
8427 : }
8428 :
8429 26 : mLastModified.Truncate();
8430 26 : if (modDate != 0) {
8431 25 : GetFormattedTimeString(modDate, mLastModified);
8432 : }
8433 : }
8434 :
8435 : already_AddRefed<Element>
8436 192 : nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix,
8437 : int32_t aNamespaceID, const nsAString* aIs)
8438 : {
8439 : #ifdef DEBUG
8440 384 : nsAutoString qName;
8441 192 : if (aPrefix) {
8442 0 : aPrefix->ToString(qName);
8443 0 : qName.Append(':');
8444 : }
8445 192 : qName.Append(aName);
8446 :
8447 : // Note: "a:b:c" is a valid name in non-namespaces XML, and
8448 : // nsDocument::CreateElement can call us with such a name and no prefix,
8449 : // which would cause an error if we just used true here.
8450 192 : bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
8451 192 : NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
8452 : "Don't pass invalid prefixes to nsDocument::CreateElem, "
8453 : "check caller.");
8454 : #endif
8455 :
8456 384 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
8457 192 : mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
8458 : nsIDOMNode::ELEMENT_NODE,
8459 384 : getter_AddRefs(nodeInfo));
8460 192 : NS_ENSURE_TRUE(nodeInfo, nullptr);
8461 :
8462 384 : nsCOMPtr<Element> element;
8463 384 : nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
8464 192 : NOT_FROM_PARSER, aIs);
8465 192 : return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
8466 : }
8467 :
8468 : bool
8469 58 : nsDocument::IsSafeToFlush() const
8470 : {
8471 58 : nsIPresShell* shell = GetShell();
8472 58 : if (!shell)
8473 1 : return true;
8474 :
8475 57 : return shell->IsSafeToFlush();
8476 : }
8477 :
8478 : void
8479 0 : nsDocument::Sanitize()
8480 : {
8481 : // Sanitize the document by resetting all password fields and any form
8482 : // fields with autocomplete=off to their default values. We do this now,
8483 : // instead of when the presentation is restored, to offer some protection
8484 : // in case there is ever an exploit that allows a cached document to be
8485 : // accessed from a different document.
8486 :
8487 : // First locate all input elements, regardless of whether they are
8488 : // in a form, and reset the password and autocomplete=off elements.
8489 :
8490 0 : RefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
8491 :
8492 0 : nsAutoString value;
8493 :
8494 0 : uint32_t length = nodes->Length(true);
8495 0 : for (uint32_t i = 0; i < length; ++i) {
8496 0 : NS_ASSERTION(nodes->Item(i), "null item in node list!");
8497 :
8498 0 : RefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i));
8499 0 : if (!input)
8500 0 : continue;
8501 :
8502 0 : bool resetValue = false;
8503 :
8504 0 : input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
8505 0 : if (value.LowerCaseEqualsLiteral("off")) {
8506 0 : resetValue = true;
8507 : } else {
8508 0 : input->GetType(value);
8509 0 : if (value.LowerCaseEqualsLiteral("password"))
8510 0 : resetValue = true;
8511 : }
8512 :
8513 0 : if (resetValue) {
8514 0 : input->Reset();
8515 : }
8516 : }
8517 :
8518 : // Now locate all _form_ elements that have autocomplete=off and reset them
8519 0 : nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
8520 :
8521 0 : length = nodes->Length(true);
8522 0 : for (uint32_t i = 0; i < length; ++i) {
8523 0 : NS_ASSERTION(nodes->Item(i), "null item in nodelist");
8524 :
8525 0 : nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i));
8526 0 : if (!form)
8527 0 : continue;
8528 :
8529 0 : nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None,
8530 0 : nsGkAtoms::autocomplete, value);
8531 0 : if (value.LowerCaseEqualsLiteral("off"))
8532 0 : form->Reset();
8533 : }
8534 0 : }
8535 :
8536 : struct SubDocEnumArgs
8537 : {
8538 : nsIDocument::nsSubDocEnumFunc callback;
8539 : void *data;
8540 : };
8541 :
8542 : void
8543 269 : nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
8544 : {
8545 269 : if (!mSubDocuments) {
8546 136 : return;
8547 : }
8548 :
8549 : // PLDHashTable::Iterator can't handle modifications while iterating so we
8550 : // copy all entries to an array first before calling any callbacks.
8551 266 : AutoTArray<nsCOMPtr<nsIDocument>, 8> subdocs;
8552 256 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
8553 123 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
8554 123 : nsIDocument* subdoc = entry->mSubDocument;
8555 123 : if (subdoc) {
8556 123 : subdocs.AppendElement(subdoc);
8557 : }
8558 : }
8559 256 : for (auto subdoc : subdocs) {
8560 123 : if (!aCallback(subdoc, aData)) {
8561 0 : break;
8562 : }
8563 : }
8564 : }
8565 :
8566 : #ifdef DEBUG_bryner
8567 : #define DEBUG_PAGE_CACHE
8568 : #endif
8569 :
8570 : bool
8571 0 : nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
8572 : {
8573 0 : if (EventHandlingSuppressed()) {
8574 0 : return false;
8575 : }
8576 :
8577 : // Do not allow suspended windows to be placed in the
8578 : // bfcache. This method is also used to verify a document
8579 : // coming out of the bfcache is ok to restore, though. So
8580 : // we only want to block suspend windows that aren't also
8581 : // frozen.
8582 0 : nsPIDOMWindowInner* win = GetInnerWindow();
8583 0 : if (win && win->IsSuspended() && !win->IsFrozen()) {
8584 0 : return false;
8585 : }
8586 :
8587 : // Check our event listener manager for unload/beforeunload listeners.
8588 0 : nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
8589 0 : if (piTarget) {
8590 0 : EventListenerManager* manager = piTarget->GetExistingListenerManager();
8591 0 : if (manager && manager->HasUnloadListeners()) {
8592 0 : return false;
8593 : }
8594 : }
8595 :
8596 : // Check if we have pending network requests
8597 0 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8598 0 : if (loadGroup) {
8599 0 : nsCOMPtr<nsISimpleEnumerator> requests;
8600 0 : loadGroup->GetRequests(getter_AddRefs(requests));
8601 :
8602 0 : bool hasMore = false;
8603 :
8604 : // We want to bail out if we have any requests other than aNewRequest (or
8605 : // in the case when aNewRequest is a part of a multipart response the base
8606 : // channel the multipart response is coming in on).
8607 0 : nsCOMPtr<nsIChannel> baseChannel;
8608 0 : nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
8609 0 : if (part) {
8610 0 : part->GetBaseChannel(getter_AddRefs(baseChannel));
8611 : }
8612 :
8613 0 : while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8614 0 : nsCOMPtr<nsISupports> elem;
8615 0 : requests->GetNext(getter_AddRefs(elem));
8616 :
8617 0 : nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8618 0 : if (request && request != aNewRequest && request != baseChannel) {
8619 : #ifdef DEBUG_PAGE_CACHE
8620 : nsAutoCString requestName, docSpec;
8621 : request->GetName(requestName);
8622 : if (mDocumentURI)
8623 : mDocumentURI->GetSpec(docSpec);
8624 :
8625 : printf("document %s has request %s\n",
8626 : docSpec.get(), requestName.get());
8627 : #endif
8628 0 : return false;
8629 : }
8630 : }
8631 : }
8632 :
8633 : // Check if we have active GetUserMedia use
8634 0 : if (MediaManager::Exists() && win &&
8635 0 : MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
8636 0 : return false;
8637 : }
8638 :
8639 : #ifdef MOZ_WEBRTC
8640 : // Check if we have active PeerConnections
8641 : nsCOMPtr<IPeerConnectionManager> pcManager =
8642 0 : do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
8643 :
8644 0 : if (pcManager && win) {
8645 : bool active;
8646 0 : pcManager->HasActivePeerConnection(win->WindowID(), &active);
8647 0 : if (active) {
8648 0 : return false;
8649 : }
8650 : }
8651 : #endif // MOZ_WEBRTC
8652 :
8653 : // Don't save presentations for documents containing EME content, so that
8654 : // CDMs reliably shutdown upon user navigation.
8655 0 : if (ContainsEMEContent()) {
8656 0 : return false;
8657 : }
8658 :
8659 : // Don't save presentations for documents containing MSE content, to
8660 : // reduce memory usage.
8661 0 : if (ContainsMSEContent()) {
8662 0 : return false;
8663 : }
8664 :
8665 : // Don't save presentation if there are active FlyWeb connections or FlyWeb
8666 : // servers.
8667 0 : FlyWebService* flyWebService = FlyWebService::GetExisting();
8668 0 : if (flyWebService && flyWebService->HasConnectionOrServer(win->WindowID())) {
8669 0 : return false;
8670 : }
8671 :
8672 0 : if (mSubDocuments) {
8673 0 : for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
8674 0 : auto entry = static_cast<SubDocMapEntry*>(iter.Get());
8675 0 : nsIDocument* subdoc = entry->mSubDocument;
8676 :
8677 : // The aIgnoreRequest we were passed is only for us, so don't pass it on.
8678 0 : bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
8679 0 : if (!canCache) {
8680 0 : return false;
8681 : }
8682 : }
8683 : }
8684 :
8685 :
8686 0 : if (win) {
8687 0 : auto* globalWindow = nsGlobalWindow::Cast(win);
8688 : #ifdef MOZ_WEBSPEECH
8689 0 : if (globalWindow->HasActiveSpeechSynthesis()) {
8690 0 : return false;
8691 : }
8692 : #endif
8693 0 : if (globalWindow->HasUsedVR()) {
8694 0 : return false;
8695 : }
8696 : }
8697 :
8698 0 : return true;
8699 : }
8700 :
8701 : void
8702 4 : nsDocument::Destroy()
8703 : {
8704 : // The ContentViewer wants to release the document now. So, tell our content
8705 : // to drop any references to the document so that it can be destroyed.
8706 4 : if (mIsGoingAway)
8707 0 : return;
8708 :
8709 4 : mIsGoingAway = true;
8710 :
8711 4 : SetScriptGlobalObject(nullptr);
8712 4 : RemovedFromDocShell();
8713 :
8714 4 : bool oldVal = mInUnlinkOrDeletion;
8715 4 : mInUnlinkOrDeletion = true;
8716 4 : uint32_t i, count = mChildren.ChildCount();
8717 8 : for (i = 0; i < count; ++i) {
8718 4 : mChildren.ChildAt(i)->DestroyContent();
8719 : }
8720 4 : mInUnlinkOrDeletion = oldVal;
8721 :
8722 4 : mLayoutHistoryState = nullptr;
8723 :
8724 : // Shut down our external resource map. We might not need this for
8725 : // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
8726 : // tearing down all those frame trees right now is the right thing to do.
8727 4 : mExternalResourceMap.Shutdown();
8728 : }
8729 :
8730 : void
8731 8 : nsDocument::RemovedFromDocShell()
8732 : {
8733 8 : if (mRemovedFromDocShell)
8734 4 : return;
8735 :
8736 4 : mRemovedFromDocShell = true;
8737 4 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8738 :
8739 4 : uint32_t i, count = mChildren.ChildCount();
8740 8 : for (i = 0; i < count; ++i) {
8741 4 : mChildren.ChildAt(i)->SaveSubtreeState();
8742 : }
8743 : }
8744 :
8745 : already_AddRefed<nsILayoutHistoryState>
8746 64 : nsDocument::GetLayoutHistoryState() const
8747 : {
8748 128 : nsCOMPtr<nsILayoutHistoryState> state;
8749 64 : if (!mScriptGlobalObject) {
8750 46 : state = mLayoutHistoryState;
8751 : } else {
8752 36 : nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
8753 18 : if (docShell) {
8754 18 : docShell->GetLayoutHistoryState(getter_AddRefs(state));
8755 : }
8756 : }
8757 :
8758 128 : return state.forget();
8759 : }
8760 :
8761 : void
8762 8 : nsDocument::EnsureOnloadBlocker()
8763 : {
8764 : // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8765 : // -- it's not ours.
8766 8 : if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
8767 0 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8768 0 : if (loadGroup) {
8769 : // Check first to see if mOnloadBlocker is in the loadgroup.
8770 0 : nsCOMPtr<nsISimpleEnumerator> requests;
8771 0 : loadGroup->GetRequests(getter_AddRefs(requests));
8772 :
8773 0 : bool hasMore = false;
8774 0 : while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8775 0 : nsCOMPtr<nsISupports> elem;
8776 0 : requests->GetNext(getter_AddRefs(elem));
8777 0 : nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8778 0 : if (request && request == mOnloadBlocker) {
8779 0 : return;
8780 : }
8781 : }
8782 :
8783 : // Not in the loadgroup, so add it.
8784 0 : loadGroup->AddRequest(mOnloadBlocker, nullptr);
8785 : }
8786 : }
8787 : }
8788 :
8789 : void
8790 4 : nsDocument::AsyncBlockOnload()
8791 : {
8792 6 : while (mAsyncOnloadBlockCount) {
8793 2 : --mAsyncOnloadBlockCount;
8794 2 : BlockOnload();
8795 : }
8796 2 : }
8797 :
8798 : void
8799 251 : nsDocument::BlockOnload()
8800 : {
8801 251 : if (mDisplayDocument) {
8802 0 : mDisplayDocument->BlockOnload();
8803 0 : return;
8804 : }
8805 :
8806 : // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8807 : // -- it's not ours.
8808 251 : if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
8809 15 : if (!nsContentUtils::IsSafeToRunScript()) {
8810 : // Because AddRequest may lead to OnStateChange calls in chrome,
8811 : // block onload only when there are no script blockers.
8812 2 : ++mAsyncOnloadBlockCount;
8813 2 : if (mAsyncOnloadBlockCount == 1) {
8814 4 : nsContentUtils::AddScriptRunner(NewRunnableMethod(
8815 2 : "nsDocument::AsyncBlockOnload", this, &nsDocument::AsyncBlockOnload));
8816 : }
8817 2 : return;
8818 : }
8819 26 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8820 13 : if (loadGroup) {
8821 13 : loadGroup->AddRequest(mOnloadBlocker, nullptr);
8822 : }
8823 : }
8824 249 : ++mOnloadBlockCount;
8825 : }
8826 :
8827 : void
8828 249 : nsDocument::UnblockOnload(bool aFireSync)
8829 : {
8830 249 : if (mDisplayDocument) {
8831 0 : mDisplayDocument->UnblockOnload(aFireSync);
8832 0 : return;
8833 : }
8834 :
8835 249 : if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
8836 0 : NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
8837 0 : return;
8838 : }
8839 :
8840 249 : --mOnloadBlockCount;
8841 :
8842 249 : if (mOnloadBlockCount == 0) {
8843 36 : if (mScriptGlobalObject) {
8844 : // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
8845 : // is null, it's not ours.
8846 14 : if (aFireSync && mAsyncOnloadBlockCount == 0) {
8847 : // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
8848 4 : ++mOnloadBlockCount;
8849 4 : DoUnblockOnload();
8850 : } else {
8851 10 : PostUnblockOnloadEvent();
8852 : }
8853 22 : } else if (mIsBeingUsedAsImage) {
8854 : // To correctly unblock onload for a document that contains an SVG
8855 : // image, we need to know when all of the SVG document's resources are
8856 : // done loading, in a way comparable to |window.onload|. We fire this
8857 : // event to indicate that the SVG should be considered fully loaded.
8858 : // Because scripting is disabled on SVG-as-image documents, this event
8859 : // is not accessible to content authors. (See bug 837315.)
8860 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
8861 : new AsyncEventDispatcher(this,
8862 42 : NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
8863 : false,
8864 63 : false);
8865 21 : asyncDispatcher->PostDOMEvent();
8866 : }
8867 : }
8868 : }
8869 :
8870 30 : class nsUnblockOnloadEvent : public Runnable {
8871 : public:
8872 10 : explicit nsUnblockOnloadEvent(nsDocument* aDoc)
8873 10 : : mozilla::Runnable("nsUnblockOnloadEvent")
8874 10 : , mDoc(aDoc)
8875 : {
8876 10 : }
8877 10 : NS_IMETHOD Run() override {
8878 10 : mDoc->DoUnblockOnload();
8879 10 : return NS_OK;
8880 : }
8881 : private:
8882 : RefPtr<nsDocument> mDoc;
8883 : };
8884 :
8885 : void
8886 10 : nsDocument::PostUnblockOnloadEvent()
8887 : {
8888 10 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
8889 20 : nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
8890 : nsresult rv =
8891 10 : Dispatch("nsUnblockOnloadEvent", TaskCategory::Other, evt.forget());
8892 10 : if (NS_SUCCEEDED(rv)) {
8893 : // Stabilize block count so we don't post more events while this one is up
8894 10 : ++mOnloadBlockCount;
8895 : } else {
8896 0 : NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
8897 : }
8898 10 : }
8899 :
8900 : void
8901 14 : nsDocument::DoUnblockOnload()
8902 : {
8903 14 : NS_PRECONDITION(!mDisplayDocument,
8904 : "Shouldn't get here for resource document");
8905 14 : NS_PRECONDITION(mOnloadBlockCount != 0,
8906 : "Shouldn't have a count of zero here, since we stabilized in "
8907 : "PostUnblockOnloadEvent");
8908 :
8909 14 : --mOnloadBlockCount;
8910 :
8911 14 : if (mOnloadBlockCount != 0) {
8912 : // We blocked again after the last unblock. Nothing to do here. We'll
8913 : // post a new event when we unblock again.
8914 1 : return;
8915 : }
8916 :
8917 13 : if (mAsyncOnloadBlockCount != 0) {
8918 : // We need to wait until the async onload block has been handled.
8919 0 : PostUnblockOnloadEvent();
8920 : }
8921 :
8922 : // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8923 : // -- it's not ours.
8924 13 : if (mScriptGlobalObject) {
8925 18 : nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8926 9 : if (loadGroup) {
8927 9 : loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
8928 : }
8929 : }
8930 : }
8931 :
8932 : nsIContent*
8933 0 : nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
8934 : {
8935 0 : for (nsIFrame* f = aFrame; f;
8936 : f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
8937 0 : nsIContent* content = f->GetContent();
8938 0 : if (!content || content->IsInAnonymousSubtree())
8939 0 : continue;
8940 :
8941 0 : if (content->OwnerDoc() == this) {
8942 0 : return content;
8943 : }
8944 : // We must be in a subdocument so jump directly to the root frame.
8945 : // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
8946 : // the containing document.
8947 0 : f = f->PresContext()->GetPresShell()->GetRootFrame();
8948 : }
8949 :
8950 0 : return nullptr;
8951 : }
8952 :
8953 : void
8954 9 : nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
8955 : const nsAString& aType,
8956 : bool aPersisted)
8957 : {
8958 9 : if (!aDispatchTarget) {
8959 0 : return;
8960 : }
8961 :
8962 9 : PageTransitionEventInit init;
8963 9 : init.mBubbles = true;
8964 9 : init.mCancelable = true;
8965 9 : init.mPersisted = aPersisted;
8966 :
8967 : RefPtr<PageTransitionEvent> event =
8968 18 : PageTransitionEvent::Constructor(this, aType, init);
8969 :
8970 9 : event->SetTrusted(true);
8971 9 : event->SetTarget(this);
8972 : EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
8973 9 : nullptr, nullptr);
8974 : }
8975 :
8976 : static bool
8977 0 : NotifyPageShow(nsIDocument* aDocument, void* aData)
8978 : {
8979 0 : const bool* aPersistedPtr = static_cast<const bool*>(aData);
8980 0 : aDocument->OnPageShow(*aPersistedPtr, nullptr);
8981 0 : return true;
8982 : }
8983 :
8984 : void
8985 5 : nsDocument::OnPageShow(bool aPersisted,
8986 : EventTarget* aDispatchStartTarget)
8987 : {
8988 5 : mVisible = true;
8989 :
8990 5 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8991 5 : EnumerateExternalResources(NotifyPageShow, &aPersisted);
8992 :
8993 5 : Element* root = GetRootElement();
8994 5 : if (aPersisted && root) {
8995 : // Send out notifications that our <link> elements are attached.
8996 0 : RefPtr<nsContentList> links = NS_GetContentList(root,
8997 : kNameSpaceID_XHTML,
8998 0 : NS_LITERAL_STRING("link"));
8999 :
9000 0 : uint32_t linkCount = links->Length(true);
9001 0 : for (uint32_t i = 0; i < linkCount; ++i) {
9002 0 : static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
9003 : }
9004 : }
9005 :
9006 : // See nsIDocument
9007 5 : if (!aDispatchStartTarget) {
9008 : // Set mIsShowing before firing events, in case those event handlers
9009 : // move us around.
9010 5 : mIsShowing = true;
9011 : }
9012 :
9013 5 : if (mAnimationController) {
9014 1 : mAnimationController->OnPageShow();
9015 : }
9016 :
9017 5 : if (aPersisted) {
9018 0 : ImageTracker()->SetAnimatingState(true);
9019 : }
9020 :
9021 5 : UpdateVisibilityState();
9022 :
9023 5 : if (!mIsBeingUsedAsImage) {
9024 : // Dispatch observer notification to notify observers page is shown.
9025 10 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9026 5 : if (os) {
9027 5 : nsIPrincipal *principal = GetPrincipal();
9028 10 : os->NotifyObservers(static_cast<nsIDocument*>(this),
9029 5 : nsContentUtils::IsSystemPrincipal(principal) ?
9030 : "chrome-page-shown" :
9031 : "content-page-shown",
9032 10 : nullptr);
9033 : }
9034 :
9035 10 : nsCOMPtr<EventTarget> target = aDispatchStartTarget;
9036 5 : if (!target) {
9037 5 : target = do_QueryInterface(GetWindow());
9038 : }
9039 5 : DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
9040 : }
9041 5 : }
9042 :
9043 : static bool
9044 0 : NotifyPageHide(nsIDocument* aDocument, void* aData)
9045 : {
9046 0 : const bool* aPersistedPtr = static_cast<const bool*>(aData);
9047 0 : aDocument->OnPageHide(*aPersistedPtr, nullptr);
9048 0 : return true;
9049 : }
9050 :
9051 : static void
9052 0 : DispatchCustomEventWithFlush(nsINode* aTarget, const nsAString& aEventType,
9053 : bool aBubbles, bool aOnlyChromeDispatch)
9054 : {
9055 0 : RefPtr<Event> event = NS_NewDOMEvent(aTarget, nullptr, nullptr);
9056 0 : event->InitEvent(aEventType, aBubbles, false);
9057 0 : event->SetTrusted(true);
9058 0 : if (aOnlyChromeDispatch) {
9059 0 : event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
9060 : }
9061 0 : if (nsIPresShell* shell = aTarget->OwnerDoc()->GetShell()) {
9062 : shell->GetPresContext()->
9063 0 : RefreshDriver()->ScheduleEventDispatch(aTarget, event);
9064 : }
9065 0 : }
9066 :
9067 : static void
9068 0 : DispatchFullScreenChange(nsIDocument* aTarget)
9069 : {
9070 0 : DispatchCustomEventWithFlush(
9071 0 : aTarget, NS_LITERAL_STRING("fullscreenchange"),
9072 0 : /* Bubbles */ true, /* OnlyChrome */ false);
9073 0 : }
9074 :
9075 : static void ClearPendingFullscreenRequests(nsIDocument* aDoc);
9076 :
9077 : void
9078 4 : nsDocument::OnPageHide(bool aPersisted,
9079 : EventTarget* aDispatchStartTarget)
9080 : {
9081 : // Send out notifications that our <link> elements are detached,
9082 : // but only if this is not a full unload.
9083 4 : Element* root = GetRootElement();
9084 4 : if (aPersisted && root) {
9085 0 : RefPtr<nsContentList> links = NS_GetContentList(root,
9086 : kNameSpaceID_XHTML,
9087 0 : NS_LITERAL_STRING("link"));
9088 :
9089 0 : uint32_t linkCount = links->Length(true);
9090 0 : for (uint32_t i = 0; i < linkCount; ++i) {
9091 0 : static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
9092 : }
9093 : }
9094 :
9095 : // See nsIDocument
9096 4 : if (!aDispatchStartTarget) {
9097 : // Set mIsShowing before firing events, in case those event handlers
9098 : // move us around.
9099 4 : mIsShowing = false;
9100 : }
9101 :
9102 4 : if (mAnimationController) {
9103 0 : mAnimationController->OnPageHide();
9104 : }
9105 :
9106 : // We do not stop the animations (bug 1024343)
9107 : // when the page is refreshing while being dragged out
9108 4 : nsDocShell* docShell = mDocumentContainer.get();
9109 4 : if (aPersisted && !(docShell && docShell->InFrameSwap())) {
9110 0 : ImageTracker()->SetAnimatingState(false);
9111 : }
9112 :
9113 4 : ExitPointerLock();
9114 :
9115 4 : if (!mIsBeingUsedAsImage) {
9116 : // Dispatch observer notification to notify observers page is hidden.
9117 8 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9118 4 : if (os) {
9119 4 : nsIPrincipal* principal = GetPrincipal();
9120 8 : os->NotifyObservers(static_cast<nsIDocument*>(this),
9121 4 : nsContentUtils::IsSystemPrincipal(principal) ?
9122 : "chrome-page-hidden" :
9123 : "content-page-hidden",
9124 8 : nullptr);
9125 : }
9126 :
9127 : // Now send out a PageHide event.
9128 8 : nsCOMPtr<EventTarget> target = aDispatchStartTarget;
9129 4 : if (!target) {
9130 4 : target = do_QueryInterface(GetWindow());
9131 : }
9132 : {
9133 8 : PageUnloadingEventTimeStamp timeStamp(this);
9134 4 : DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
9135 : }
9136 : }
9137 :
9138 4 : mVisible = false;
9139 :
9140 4 : UpdateVisibilityState();
9141 :
9142 4 : EnumerateExternalResources(NotifyPageHide, &aPersisted);
9143 4 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
9144 :
9145 4 : ClearPendingFullscreenRequests(this);
9146 4 : if (GetFullscreenElement()) {
9147 : // If this document was fullscreen, we should exit fullscreen in this
9148 : // doctree branch. This ensures that if the user navigates while in
9149 : // fullscreen mode we don't leave its still visible ancestor documents
9150 : // in fullscreen mode. So exit fullscreen in the document's fullscreen
9151 : // root document, as this will exit fullscreen in all the root's
9152 : // descendant documents. Note that documents are removed from the
9153 : // doctree by the time OnPageHide() is called, so we must store a
9154 : // reference to the root (in nsDocument::mFullscreenRoot) since we can't
9155 : // just traverse the doctree to get the root.
9156 0 : nsIDocument::ExitFullscreenInDocTree(this);
9157 :
9158 : // Since the document is removed from the doctree before OnPageHide() is
9159 : // called, ExitFullscreen() can't traverse from the root down to *this*
9160 : // document, so we must manually call CleanupFullscreenState() below too.
9161 : // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
9162 : // so we *must* call it after ExitFullscreen(), not before.
9163 : // OnPageHide() is called in every hidden (i.e. descendant) document,
9164 : // so calling CleanupFullscreenState() here will ensure all hidden
9165 : // documents have their fullscreen state reset.
9166 0 : CleanupFullscreenState();
9167 :
9168 : // If anyone was listening to this document's state, advertizing the state
9169 : // change would be the least of the politeness.
9170 0 : DispatchFullScreenChange(this);
9171 : }
9172 4 : }
9173 :
9174 : void
9175 0 : nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
9176 : {
9177 0 : NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
9178 : mSubtreeModifiedTargets.Count() == 0,
9179 : "mSubtreeModifiedTargets not cleared after dispatching?");
9180 0 : ++mSubtreeModifiedDepth;
9181 0 : if (aTarget) {
9182 : // MayDispatchMutationEvent is often called just before this method,
9183 : // so it has already appended the node to mSubtreeModifiedTargets.
9184 0 : int32_t count = mSubtreeModifiedTargets.Count();
9185 0 : if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
9186 0 : mSubtreeModifiedTargets.AppendObject(aTarget);
9187 : }
9188 : }
9189 0 : }
9190 :
9191 : void
9192 0 : nsDocument::MutationEventDispatched(nsINode* aTarget)
9193 : {
9194 0 : --mSubtreeModifiedDepth;
9195 0 : if (mSubtreeModifiedDepth == 0) {
9196 0 : int32_t count = mSubtreeModifiedTargets.Count();
9197 0 : if (!count) {
9198 0 : return;
9199 : }
9200 :
9201 0 : nsPIDOMWindowInner* window = GetInnerWindow();
9202 0 : if (window &&
9203 0 : !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
9204 0 : mSubtreeModifiedTargets.Clear();
9205 0 : return;
9206 : }
9207 :
9208 0 : nsCOMArray<nsINode> realTargets;
9209 0 : for (int32_t i = 0; i < count; ++i) {
9210 0 : nsINode* possibleTarget = mSubtreeModifiedTargets[i];
9211 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
9212 0 : if (content && content->ChromeOnlyAccess()) {
9213 0 : continue;
9214 : }
9215 :
9216 0 : nsINode* commonAncestor = nullptr;
9217 0 : int32_t realTargetCount = realTargets.Count();
9218 0 : for (int32_t j = 0; j < realTargetCount; ++j) {
9219 : commonAncestor =
9220 0 : nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
9221 0 : if (commonAncestor) {
9222 0 : realTargets.ReplaceObjectAt(commonAncestor, j);
9223 0 : break;
9224 : }
9225 : }
9226 0 : if (!commonAncestor) {
9227 0 : realTargets.AppendObject(possibleTarget);
9228 : }
9229 : }
9230 :
9231 0 : mSubtreeModifiedTargets.Clear();
9232 :
9233 0 : int32_t realTargetCount = realTargets.Count();
9234 0 : for (int32_t k = 0; k < realTargetCount; ++k) {
9235 0 : InternalMutationEvent mutation(true, eLegacySubtreeModified);
9236 0 : (new AsyncEventDispatcher(realTargets[k], mutation))->
9237 0 : RunDOMEventWhenSafe();
9238 : }
9239 : }
9240 : }
9241 :
9242 : void
9243 0 : nsDocument::AddStyleRelevantLink(Link* aLink)
9244 : {
9245 0 : NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9246 : #ifdef DEBUG
9247 0 : nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9248 0 : NS_ASSERTION(!entry, "Document already knows about this Link!");
9249 0 : mStyledLinksCleared = false;
9250 : #endif
9251 0 : (void)mStyledLinks.PutEntry(aLink);
9252 0 : }
9253 :
9254 : void
9255 0 : nsDocument::ForgetLink(Link* aLink)
9256 : {
9257 0 : NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9258 : #ifdef DEBUG
9259 0 : nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9260 0 : NS_ASSERTION(entry || mStyledLinksCleared,
9261 : "Document knows nothing about this Link!");
9262 : #endif
9263 0 : mStyledLinks.RemoveEntry(aLink);
9264 0 : }
9265 :
9266 : void
9267 29 : nsDocument::DestroyElementMaps()
9268 : {
9269 : #ifdef DEBUG
9270 29 : mStyledLinksCleared = true;
9271 : #endif
9272 29 : mStyledLinks.Clear();
9273 29 : mIdentifierMap.Clear();
9274 29 : ++mExpandoAndGeneration.generation;
9275 29 : }
9276 :
9277 : void
9278 54 : nsDocument::RefreshLinkHrefs()
9279 : {
9280 : // Get a list of all links we know about. We will reset them, which will
9281 : // remove them from the document, so we need a copy of what is in the
9282 : // hashtable.
9283 108 : LinkArray linksToNotify(mStyledLinks.Count());
9284 54 : for (auto iter = mStyledLinks.ConstIter(); !iter.Done(); iter.Next()) {
9285 0 : linksToNotify.AppendElement(iter.Get()->GetKey());
9286 : }
9287 :
9288 : // Reset all of our styled links.
9289 108 : nsAutoScriptBlocker scriptBlocker;
9290 54 : for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
9291 0 : linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
9292 : }
9293 54 : }
9294 :
9295 : nsresult
9296 0 : nsDocument::CloneDocHelper(nsDocument* clone, bool aPreallocateChildren) const
9297 : {
9298 0 : clone->mIsStaticDocument = mCreatingStaticClone;
9299 :
9300 : // Init document
9301 0 : nsresult rv = clone->Init();
9302 0 : NS_ENSURE_SUCCESS(rv, rv);
9303 :
9304 0 : if (mCreatingStaticClone) {
9305 0 : nsCOMPtr<nsILoadGroup> loadGroup;
9306 :
9307 : // |mDocumentContainer| is the container of the document that is being
9308 : // created and not the original container. See CreateStaticClone function().
9309 0 : nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
9310 0 : if (docLoader) {
9311 0 : docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
9312 : }
9313 0 : nsCOMPtr<nsIChannel> channel = GetChannel();
9314 0 : nsCOMPtr<nsIURI> uri;
9315 0 : if (channel) {
9316 0 : NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
9317 : } else {
9318 0 : uri = nsIDocument::GetDocumentURI();
9319 : }
9320 0 : clone->mChannel = channel;
9321 0 : if (uri) {
9322 0 : clone->ResetToURI(uri, loadGroup, NodePrincipal());
9323 : }
9324 :
9325 0 : clone->SetContainer(mDocumentContainer);
9326 : }
9327 :
9328 : // Now ensure that our clone has the same URI, base URI, and principal as us.
9329 : // We do this after the mCreatingStaticClone block above, because that block
9330 : // can set the base URI to an incorrect value in cases when base URI
9331 : // information came from the channel. So we override explicitly, and do it
9332 : // for all these properties, in case ResetToURI messes with any of the rest of
9333 : // them.
9334 0 : clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
9335 0 : clone->SetChromeXHRDocURI(mChromeXHRDocURI);
9336 0 : clone->SetPrincipal(NodePrincipal());
9337 0 : clone->mDocumentBaseURI = mDocumentBaseURI;
9338 0 : clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
9339 :
9340 0 : bool hasHadScriptObject = true;
9341 : nsIScriptGlobalObject* scriptObject =
9342 0 : GetScriptHandlingObject(hasHadScriptObject);
9343 0 : NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
9344 0 : if (mCreatingStaticClone) {
9345 : // If we're doing a static clone (print, print preview), then we're going to
9346 : // be setting a scope object after the clone. It's better to set it only
9347 : // once, so we don't do that here. However, we do want to act as if there is
9348 : // a script handling object. So we set mHasHadScriptHandlingObject.
9349 0 : clone->mHasHadScriptHandlingObject = true;
9350 0 : } else if (scriptObject) {
9351 0 : clone->SetScriptHandlingObject(scriptObject);
9352 : } else {
9353 0 : clone->SetScopeObject(GetScopeObject());
9354 : }
9355 : // Make the clone a data document
9356 0 : clone->SetLoadedAsData(true);
9357 :
9358 : // Misc state
9359 :
9360 : // State from nsIDocument
9361 0 : clone->mCharacterSet = mCharacterSet;
9362 0 : clone->mCharacterSetSource = mCharacterSetSource;
9363 0 : clone->mCompatMode = mCompatMode;
9364 0 : clone->mBidiOptions = mBidiOptions;
9365 0 : clone->mContentLanguage = mContentLanguage;
9366 0 : clone->SetContentTypeInternal(GetContentTypeInternal());
9367 0 : clone->mSecurityInfo = mSecurityInfo;
9368 :
9369 : // State from nsDocument
9370 0 : clone->mType = mType;
9371 0 : clone->mXMLDeclarationBits = mXMLDeclarationBits;
9372 0 : clone->mBaseTarget = mBaseTarget;
9373 :
9374 : // Preallocate attributes and child arrays
9375 0 : rv = clone->mChildren.EnsureCapacityToClone(mChildren, aPreallocateChildren);
9376 0 : NS_ENSURE_SUCCESS(rv, rv);
9377 :
9378 0 : return NS_OK;
9379 : }
9380 :
9381 : void
9382 80 : nsDocument::SetReadyStateInternal(ReadyState rs)
9383 : {
9384 80 : mReadyState = rs;
9385 80 : if (rs == READYSTATE_UNINITIALIZED) {
9386 : // Transition back to uninitialized happens only to keep assertions happy
9387 : // right before readyState transitions to something else. Make this
9388 : // transition undetectable by Web content.
9389 0 : return;
9390 : }
9391 80 : if (mTiming) {
9392 28 : switch (rs) {
9393 : case READYSTATE_LOADING:
9394 0 : mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
9395 0 : break;
9396 : case READYSTATE_INTERACTIVE:
9397 24 : mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
9398 24 : break;
9399 : case READYSTATE_COMPLETE:
9400 4 : mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
9401 4 : break;
9402 : default:
9403 0 : NS_WARNING("Unexpected ReadyState value");
9404 0 : break;
9405 : }
9406 : }
9407 : // At the time of loading start, we don't have timing object, record time.
9408 80 : if (READYSTATE_LOADING == rs) {
9409 25 : mLoadingTimeStamp = mozilla::TimeStamp::Now();
9410 : }
9411 :
9412 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
9413 160 : new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
9414 240 : false, false);
9415 80 : asyncDispatcher->RunDOMEventWhenSafe();
9416 : }
9417 :
9418 : NS_IMETHODIMP
9419 0 : nsDocument::GetReadyState(nsAString& aReadyState)
9420 : {
9421 0 : nsIDocument::GetReadyState(aReadyState);
9422 0 : return NS_OK;
9423 : }
9424 :
9425 : void
9426 10 : nsIDocument::GetReadyState(nsAString& aReadyState) const
9427 : {
9428 10 : switch(mReadyState) {
9429 : case READYSTATE_LOADING :
9430 0 : aReadyState.AssignLiteral(u"loading");
9431 0 : break;
9432 : case READYSTATE_INTERACTIVE :
9433 0 : aReadyState.AssignLiteral(u"interactive");
9434 0 : break;
9435 : case READYSTATE_COMPLETE :
9436 5 : aReadyState.AssignLiteral(u"complete");
9437 5 : break;
9438 : default:
9439 5 : aReadyState.AssignLiteral(u"uninitialized");
9440 : }
9441 10 : }
9442 :
9443 : static bool
9444 0 : SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
9445 : {
9446 0 : aDocument->SuppressEventHandling(*static_cast<uint32_t*>(aData));
9447 :
9448 0 : return true;
9449 : }
9450 :
9451 : void
9452 0 : nsDocument::SuppressEventHandling(uint32_t aIncrease)
9453 : {
9454 0 : mEventsSuppressed += aIncrease;
9455 0 : UpdateFrameRequestCallbackSchedulingState();
9456 0 : for (uint32_t i = 0; i < aIncrease; ++i) {
9457 0 : ScriptLoader()->AddExecuteBlocker();
9458 : }
9459 :
9460 0 : EnumerateSubDocuments(SuppressEventHandlingInDocument, &aIncrease);
9461 0 : }
9462 :
9463 : static void
9464 0 : FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments,
9465 : bool aFireEvents)
9466 : {
9467 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9468 0 : if (!fm)
9469 0 : return;
9470 :
9471 0 : for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
9472 : // NB: Don't bother trying to fire delayed events on documents that were
9473 : // closed before this event ran.
9474 0 : if (!aDocuments[i]->EventHandlingSuppressed()) {
9475 0 : fm->FireDelayedEvents(aDocuments[i]);
9476 0 : nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
9477 0 : if (shell) {
9478 : // Only fire events for active documents.
9479 0 : bool fire = aFireEvents &&
9480 0 : aDocuments[i]->GetInnerWindow() &&
9481 0 : aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
9482 0 : shell->FireOrClearDelayedEvents(fire);
9483 : }
9484 : }
9485 : }
9486 : }
9487 :
9488 : void
9489 0 : nsDocument::PreloadPictureOpened()
9490 : {
9491 0 : mPreloadPictureDepth++;
9492 0 : }
9493 :
9494 : void
9495 0 : nsDocument::PreloadPictureClosed()
9496 : {
9497 0 : mPreloadPictureDepth--;
9498 0 : if (mPreloadPictureDepth == 0) {
9499 0 : mPreloadPictureFoundSource.SetIsVoid(true);
9500 : } else {
9501 0 : MOZ_ASSERT(mPreloadPictureDepth >= 0);
9502 : }
9503 0 : }
9504 :
9505 : void
9506 0 : nsDocument::PreloadPictureImageSource(const nsAString& aSrcsetAttr,
9507 : const nsAString& aSizesAttr,
9508 : const nsAString& aTypeAttr,
9509 : const nsAString& aMediaAttr)
9510 : {
9511 : // Nested pictures are not valid syntax, so while we'll eventually load them,
9512 : // it's not worth tracking sources mixed between nesting levels to preload
9513 : // them effectively.
9514 0 : if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) {
9515 : // <picture> selects the first matching source, so if this returns a URI we
9516 : // needn't consider new sources until a new <picture> is encountered.
9517 : bool found =
9518 0 : HTMLImageElement::SelectSourceForTagWithAttrs(this, true, NullString(),
9519 : aSrcsetAttr, aSizesAttr,
9520 : aTypeAttr, aMediaAttr,
9521 0 : mPreloadPictureFoundSource);
9522 0 : if (found && mPreloadPictureFoundSource.IsVoid()) {
9523 : // Found an empty source, which counts
9524 0 : mPreloadPictureFoundSource.SetIsVoid(false);
9525 : }
9526 : }
9527 0 : }
9528 :
9529 : already_AddRefed<nsIURI>
9530 0 : nsDocument::ResolvePreloadImage(nsIURI *aBaseURI,
9531 : const nsAString& aSrcAttr,
9532 : const nsAString& aSrcsetAttr,
9533 : const nsAString& aSizesAttr)
9534 : {
9535 0 : nsString sourceURL;
9536 0 : if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
9537 : // We're in a <picture> element and found a URI from a source previous to
9538 : // this image, use it.
9539 0 : sourceURL = mPreloadPictureFoundSource;
9540 : } else {
9541 : // Otherwise try to use this <img> as a source
9542 0 : HTMLImageElement::SelectSourceForTagWithAttrs(this, false, aSrcAttr,
9543 : aSrcsetAttr, aSizesAttr,
9544 0 : NullString(), NullString(),
9545 0 : sourceURL);
9546 : }
9547 :
9548 : // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
9549 0 : if (sourceURL.IsEmpty()) {
9550 0 : return nullptr;
9551 : }
9552 :
9553 : // Construct into URI using passed baseURI (the parser may know of base URI
9554 : // changes that have not reached us)
9555 : nsresult rv;
9556 0 : nsCOMPtr<nsIURI> uri;
9557 0 : rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL,
9558 0 : this, aBaseURI);
9559 0 : if (NS_FAILED(rv)) {
9560 0 : return nullptr;
9561 : }
9562 :
9563 : // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
9564 : // this this <picture> share the same <sources> (though this is not valid per
9565 : // spec)
9566 0 : return uri.forget();
9567 : }
9568 :
9569 : void
9570 0 : nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
9571 : ReferrerPolicy aReferrerPolicy)
9572 : {
9573 : // Early exit if the img is already present in the img-cache
9574 : // which indicates that the "real" load has already started and
9575 : // that we shouldn't preload it.
9576 : int16_t blockingStatus;
9577 0 : if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
9578 0 : !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
9579 : this, NodePrincipal(), &blockingStatus,
9580 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD)) {
9581 0 : return;
9582 : }
9583 :
9584 0 : nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
9585 0 : switch (Element::StringToCORSMode(aCrossOriginAttr)) {
9586 : case CORS_NONE:
9587 : // Nothing to do
9588 0 : break;
9589 : case CORS_ANONYMOUS:
9590 0 : loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
9591 0 : break;
9592 : case CORS_USE_CREDENTIALS:
9593 0 : loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
9594 0 : break;
9595 : default:
9596 0 : MOZ_CRASH("Unknown CORS mode!");
9597 : }
9598 :
9599 : // Image not in cache - trigger preload
9600 0 : RefPtr<imgRequestProxy> request;
9601 : nsresult rv =
9602 0 : nsContentUtils::LoadImage(uri,
9603 : static_cast<nsINode*>(this),
9604 : this,
9605 : NodePrincipal(),
9606 : mDocumentURI, // uri of document used as referrer
9607 : aReferrerPolicy,
9608 : nullptr, // no observer
9609 : loadFlags,
9610 0 : NS_LITERAL_STRING("img"),
9611 0 : getter_AddRefs(request),
9612 0 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD);
9613 :
9614 : // Pin image-reference to avoid evicting it from the img-cache before
9615 : // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
9616 : // unlink
9617 0 : if (NS_SUCCEEDED(rv)) {
9618 0 : mPreloadingImages.Put(uri, request.forget());
9619 : }
9620 : }
9621 :
9622 : void
9623 0 : nsDocument::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode)
9624 : {
9625 0 : nsCOMPtr<nsIURI> uri;
9626 0 : if (NS_FAILED(aOrigURI->Clone(getter_AddRefs(uri)))) {
9627 0 : return;
9628 : }
9629 :
9630 : // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
9631 : // which ignores the path and uses only the origin. The other is for the
9632 : // document mPreloadedPreconnects de-duplication hash. Anonymous vs
9633 : // non-Anonymous preconnects create different connections on the wire and
9634 : // therefore should not be considred duplicates of each other and we
9635 : // normalize the path before putting it in the hash to accomplish that.
9636 :
9637 0 : if (aCORSMode == CORS_ANONYMOUS) {
9638 0 : uri->SetPath(NS_LITERAL_CSTRING("/anonymous"));
9639 : } else {
9640 0 : uri->SetPath(NS_LITERAL_CSTRING("/"));
9641 : }
9642 :
9643 0 : auto entry = mPreloadedPreconnects.LookupForAdd(uri);
9644 0 : if (entry) {
9645 0 : return; // we found an existing entry
9646 : }
9647 0 : entry.OrInsert([] () { return true; });
9648 :
9649 : nsCOMPtr<nsISpeculativeConnect>
9650 0 : speculator(do_QueryInterface(nsContentUtils::GetIOService()));
9651 0 : if (!speculator) {
9652 0 : return;
9653 : }
9654 :
9655 0 : if (aCORSMode == CORS_ANONYMOUS) {
9656 0 : speculator->SpeculativeAnonymousConnect2(uri, NodePrincipal(), nullptr);
9657 : } else {
9658 0 : speculator->SpeculativeConnect2(uri, NodePrincipal(), nullptr);
9659 : }
9660 : }
9661 :
9662 : void
9663 4 : nsDocument::ForgetImagePreload(nsIURI* aURI)
9664 : {
9665 : // Checking count is faster than hashing the URI in the common
9666 : // case of empty table.
9667 4 : if (mPreloadingImages.Count() != 0) {
9668 0 : nsCOMPtr<imgIRequest> req;
9669 0 : mPreloadingImages.Remove(aURI, getter_AddRefs(req));
9670 0 : if (req) {
9671 : // Make sure to cancel the request so imagelib knows it's gone.
9672 0 : req->CancelAndForgetObserver(NS_BINDING_ABORTED);
9673 : }
9674 : }
9675 4 : }
9676 :
9677 : void
9678 693 : nsDocument::UpdatePossiblyStaleDocumentState()
9679 : {
9680 693 : if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
9681 1 : if (IsDocumentRightToLeft()) {
9682 0 : mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9683 : }
9684 1 : mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9685 : }
9686 693 : if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
9687 2 : nsIPresShell* shell = GetShell();
9688 4 : if (shell && shell->GetPresContext() &&
9689 2 : shell->GetPresContext()->IsTopLevelWindowInactive()) {
9690 1 : mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9691 : }
9692 2 : mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9693 : }
9694 693 : }
9695 :
9696 : EventStates
9697 693 : nsDocument::ThreadSafeGetDocumentState() const
9698 : {
9699 693 : return mDocumentState;
9700 : }
9701 :
9702 : EventStates
9703 693 : nsDocument::GetDocumentState()
9704 : {
9705 693 : UpdatePossiblyStaleDocumentState();
9706 693 : return ThreadSafeGetDocumentState();
9707 : }
9708 :
9709 : namespace {
9710 :
9711 : /**
9712 : * Stub for LoadSheet(), since all we want is to get the sheet into
9713 : * the CSSLoader's style cache
9714 : */
9715 0 : class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
9716 0 : ~StubCSSLoaderObserver() {}
9717 : public:
9718 : NS_IMETHOD
9719 0 : StyleSheetLoaded(StyleSheet*, bool, nsresult) override
9720 : {
9721 0 : return NS_OK;
9722 : }
9723 : NS_DECL_ISUPPORTS
9724 : };
9725 0 : NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
9726 :
9727 : } // namespace
9728 :
9729 : void
9730 0 : nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
9731 : const nsAString& aCrossOriginAttr,
9732 : const ReferrerPolicy aReferrerPolicy,
9733 : const nsAString& aIntegrity)
9734 : {
9735 : // The CSSLoader will retain this object after we return.
9736 0 : nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
9737 :
9738 : // Charset names are always ASCII.
9739 0 : CSSLoader()->LoadSheet(uri, true, NodePrincipal(),
9740 0 : NS_LossyConvertUTF16toASCII(charset),
9741 : obs,
9742 0 : Element::StringToCORSMode(aCrossOriginAttr),
9743 0 : aReferrerPolicy, aIntegrity);
9744 0 : }
9745 :
9746 : nsresult
9747 0 : nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
9748 : RefPtr<mozilla::StyleSheet>* aSheet)
9749 : {
9750 : css::SheetParsingMode mode =
9751 0 : isAgentSheet ? css::eAgentSheetFeatures
9752 0 : : css::eAuthorSheetFeatures;
9753 0 : return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, aSheet);
9754 : }
9755 :
9756 : class nsDelayedEventDispatcher : public Runnable
9757 : {
9758 : public:
9759 0 : explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
9760 0 : : mozilla::Runnable("nsDelayedEventDispatcher")
9761 : {
9762 0 : mDocuments.SwapElements(aDocuments);
9763 0 : }
9764 0 : virtual ~nsDelayedEventDispatcher() {}
9765 :
9766 0 : NS_IMETHOD Run() override
9767 : {
9768 0 : FireOrClearDelayedEvents(mDocuments, true);
9769 0 : return NS_OK;
9770 : }
9771 :
9772 : private:
9773 : nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
9774 : };
9775 :
9776 : static bool
9777 0 : GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
9778 : void* aData)
9779 : {
9780 0 : if (aDocument->EventHandlingSuppressed() > 0) {
9781 0 : static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
9782 0 : aDocument->ScriptLoader()->RemoveExecuteBlocker();
9783 : }
9784 :
9785 : nsTArray<nsCOMPtr<nsIDocument> >* docs =
9786 0 : static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
9787 :
9788 0 : docs->AppendElement(aDocument);
9789 0 : aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
9790 0 : return true;
9791 : }
9792 :
9793 : void
9794 0 : nsDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents)
9795 : {
9796 0 : nsTArray<nsCOMPtr<nsIDocument>> documents;
9797 0 : GetAndUnsuppressSubDocuments(this, &documents);
9798 :
9799 0 : if (aFireEvents) {
9800 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
9801 0 : nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
9802 0 : Dispatch("nsDelayedEventDispatcher", TaskCategory::Other, ded.forget());
9803 : } else {
9804 0 : FireOrClearDelayedEvents(documents, false);
9805 : }
9806 0 : }
9807 :
9808 : nsISupports*
9809 0 : nsDocument::GetCurrentContentSink()
9810 : {
9811 0 : return mParser ? mParser->GetContentSink() : nullptr;
9812 : }
9813 :
9814 : nsIDocument*
9815 0 : nsDocument::GetTemplateContentsOwner()
9816 : {
9817 0 : if (!mTemplateContentsOwner) {
9818 0 : bool hasHadScriptObject = true;
9819 : nsIScriptGlobalObject* scriptObject =
9820 0 : GetScriptHandlingObject(hasHadScriptObject);
9821 :
9822 0 : nsCOMPtr<nsIDOMDocument> domDocument;
9823 0 : nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
9824 0 : EmptyString(), // aNamespaceURI
9825 0 : EmptyString(), // aQualifiedName
9826 : nullptr, // aDoctype
9827 : nsIDocument::GetDocumentURI(),
9828 : nsIDocument::GetDocBaseURI(),
9829 : NodePrincipal(),
9830 : true, // aLoadedAsData
9831 : scriptObject, // aEventObject
9832 0 : DocumentFlavorHTML);
9833 0 : NS_ENSURE_SUCCESS(rv, nullptr);
9834 :
9835 0 : mTemplateContentsOwner = do_QueryInterface(domDocument);
9836 0 : NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
9837 :
9838 0 : nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
9839 :
9840 0 : if (!scriptObject) {
9841 0 : mTemplateContentsOwner->SetScopeObject(GetScopeObject());
9842 : }
9843 :
9844 0 : doc->mHasHadScriptHandlingObject = hasHadScriptObject;
9845 :
9846 : // Set |doc| as the template contents owner of itself so that
9847 : // |doc| is the template contents owner of template elements created
9848 : // by |doc|.
9849 0 : doc->mTemplateContentsOwner = doc;
9850 : }
9851 :
9852 0 : return mTemplateContentsOwner;
9853 : }
9854 :
9855 : void
9856 25 : nsDocument::SetScrollToRef(nsIURI *aDocumentURI)
9857 : {
9858 25 : if (!aDocumentURI) {
9859 0 : return;
9860 : }
9861 :
9862 50 : nsAutoCString ref;
9863 :
9864 : // Since all URI's that pass through here aren't URL's we can't
9865 : // rely on the nsIURI implementation for providing a way for
9866 : // finding the 'ref' part of the URI, we'll haveto revert to
9867 : // string routines for finding the data past '#'
9868 :
9869 25 : nsresult rv = aDocumentURI->GetSpec(ref);
9870 25 : if (NS_FAILED(rv)) {
9871 0 : Unused << aDocumentURI->GetRef(mScrollToRef);
9872 0 : return;
9873 : }
9874 :
9875 25 : nsReadingIterator<char> start, end;
9876 :
9877 25 : ref.BeginReading(start);
9878 25 : ref.EndReading(end);
9879 :
9880 25 : if (FindCharInReadable('#', start, end)) {
9881 2 : ++start; // Skip over the '#'
9882 :
9883 2 : mScrollToRef = Substring(start, end);
9884 : }
9885 : }
9886 :
9887 : void
9888 30 : nsDocument::ScrollToRef()
9889 : {
9890 30 : if (mScrolledToRefAlready) {
9891 0 : nsCOMPtr<nsIPresShell> shell = GetShell();
9892 0 : if (shell) {
9893 0 : shell->ScrollToAnchor();
9894 : }
9895 0 : return;
9896 : }
9897 :
9898 30 : if (mScrollToRef.IsEmpty()) {
9899 28 : return;
9900 : }
9901 :
9902 2 : char* tmpstr = ToNewCString(mScrollToRef);
9903 2 : if (!tmpstr) {
9904 0 : return;
9905 : }
9906 :
9907 2 : nsUnescape(tmpstr);
9908 4 : nsAutoCString unescapedRef;
9909 2 : unescapedRef.Assign(tmpstr);
9910 2 : free(tmpstr);
9911 :
9912 2 : nsresult rv = NS_ERROR_FAILURE;
9913 : // We assume that the bytes are in UTF-8, as it says in the spec:
9914 : // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9915 4 : NS_ConvertUTF8toUTF16 ref(unescapedRef);
9916 :
9917 4 : nsCOMPtr<nsIPresShell> shell = GetShell();
9918 2 : if (shell) {
9919 : // Check an empty string which might be caused by the UTF-8 conversion
9920 2 : if (!ref.IsEmpty()) {
9921 : // Note that GoToAnchor will handle flushing layout as needed.
9922 2 : rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9923 : } else {
9924 0 : rv = NS_ERROR_FAILURE;
9925 : }
9926 :
9927 : // If UTF-8 URI failed then try to assume the string as a
9928 : // document's charset.
9929 :
9930 2 : if (NS_FAILED(rv)) {
9931 0 : auto encoding = GetDocumentCharacterSet();
9932 :
9933 0 : rv = encoding->DecodeWithoutBOMHandling(unescapedRef, ref);
9934 :
9935 0 : if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
9936 0 : rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9937 : }
9938 : }
9939 2 : if (NS_SUCCEEDED(rv)) {
9940 2 : mScrolledToRefAlready = true;
9941 : }
9942 : }
9943 : }
9944 :
9945 : void
9946 25 : nsDocument::ResetScrolledToRefAlready()
9947 : {
9948 25 : mScrolledToRefAlready = false;
9949 25 : }
9950 :
9951 : void
9952 3 : nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue)
9953 : {
9954 3 : mChangeScrollPosWhenScrollingToRef = aValue;
9955 3 : }
9956 :
9957 : void
9958 1 : nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
9959 : {
9960 1 : if (!mActivityObservers) {
9961 1 : mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports> >();
9962 : }
9963 1 : mActivityObservers->PutEntry(aSupports);
9964 1 : }
9965 :
9966 : bool
9967 0 : nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
9968 : {
9969 0 : if (!mActivityObservers) {
9970 0 : return false;
9971 : }
9972 0 : nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports);
9973 0 : if (!entry) {
9974 0 : return false;
9975 : }
9976 0 : mActivityObservers->RemoveEntry(entry);
9977 0 : return true;
9978 : }
9979 :
9980 : void
9981 83 : nsIDocument::EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
9982 : void* aData)
9983 : {
9984 83 : if (!mActivityObservers)
9985 82 : return;
9986 :
9987 2 : for (auto iter = mActivityObservers->ConstIter(); !iter.Done();
9988 1 : iter.Next()) {
9989 1 : aEnumerator(iter.Get()->GetKey(), aData);
9990 : }
9991 : }
9992 :
9993 : void
9994 0 : nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
9995 : {
9996 0 : MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9997 :
9998 0 : if (aLink->HasPendingLinkUpdate()) {
9999 0 : return;
10000 : }
10001 :
10002 0 : aLink->SetHasPendingLinkUpdate();
10003 :
10004 0 : if (!mHasLinksToUpdateRunnable) {
10005 : nsCOMPtr<nsIRunnable> event =
10006 0 : NewRunnableMethod("nsIDocument::FlushPendingLinkUpdatesFromRunnable",
10007 : this,
10008 0 : &nsIDocument::FlushPendingLinkUpdatesFromRunnable);
10009 : // Do this work in a second in the worst case.
10010 : nsresult rv =
10011 0 : NS_IdleDispatchToCurrentThread(event.forget(), 1000);
10012 0 : if (NS_FAILED(rv)) {
10013 : // If during shutdown posting a runnable doesn't succeed, we probably
10014 : // don't need to update link states.
10015 0 : return;
10016 : }
10017 0 : mHasLinksToUpdateRunnable = true;
10018 : }
10019 :
10020 0 : mLinksToUpdate.InfallibleAppend(aLink);
10021 0 : mHasLinksToUpdate = true;
10022 : }
10023 :
10024 : void
10025 0 : nsIDocument::FlushPendingLinkUpdatesFromRunnable()
10026 : {
10027 0 : MOZ_ASSERT(mHasLinksToUpdateRunnable);
10028 0 : mHasLinksToUpdateRunnable = false;
10029 0 : FlushPendingLinkUpdates();
10030 0 : }
10031 :
10032 : void
10033 2036 : nsIDocument::FlushPendingLinkUpdates()
10034 : {
10035 2036 : MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
10036 2036 : if (!mHasLinksToUpdate)
10037 2036 : return;
10038 :
10039 : #ifdef DEBUG
10040 0 : AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden);
10041 0 : mIsLinkUpdateRegistrationsForbidden = true;
10042 : #endif
10043 0 : for (auto iter = mLinksToUpdate.Iter(); !iter.Done(); iter.Next()) {
10044 0 : Link* link = iter.Get();
10045 0 : Element* element = link->GetElement();
10046 0 : if (element->OwnerDoc() == this) {
10047 0 : link->ClearHasPendingLinkUpdate();
10048 0 : if (element->IsInComposedDoc()) {
10049 0 : element->UpdateLinkState(link->LinkState());
10050 : }
10051 : }
10052 : }
10053 0 : mLinksToUpdate.Clear();
10054 0 : mHasLinksToUpdate = false;
10055 : }
10056 :
10057 : already_AddRefed<nsIDocument>
10058 0 : nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
10059 : {
10060 0 : nsDocument* thisAsDoc = static_cast<nsDocument*>(this);
10061 0 : mCreatingStaticClone = true;
10062 :
10063 : // Make document use different container during cloning.
10064 0 : RefPtr<nsDocShell> originalShell = mDocumentContainer.get();
10065 0 : SetContainer(static_cast<nsDocShell*>(aCloneContainer));
10066 0 : nsCOMPtr<nsIDOMNode> clonedNode;
10067 0 : nsresult rv = thisAsDoc->CloneNode(true, 1, getter_AddRefs(clonedNode));
10068 0 : SetContainer(originalShell);
10069 :
10070 0 : RefPtr<nsDocument> clonedDoc;
10071 0 : if (NS_SUCCEEDED(rv)) {
10072 0 : nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode);
10073 0 : if (tmp) {
10074 0 : clonedDoc = static_cast<nsDocument*>(tmp.get());
10075 0 : if (IsStaticDocument()) {
10076 0 : clonedDoc->mOriginalDocument = mOriginalDocument;
10077 : } else {
10078 0 : clonedDoc->mOriginalDocument = this;
10079 : }
10080 :
10081 0 : clonedDoc->mOriginalDocument->mStaticCloneCount++;
10082 :
10083 0 : int32_t sheetsCount = GetNumberOfStyleSheets();
10084 0 : for (int32_t i = 0; i < sheetsCount; ++i) {
10085 0 : RefPtr<StyleSheet> sheet = GetStyleSheetAt(i);
10086 0 : if (sheet) {
10087 0 : if (sheet->IsApplicable()) {
10088 : RefPtr<StyleSheet> clonedSheet =
10089 0 : sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
10090 0 : NS_WARNING_ASSERTION(clonedSheet,
10091 : "Cloning a stylesheet didn't work!");
10092 0 : if (clonedSheet) {
10093 0 : clonedDoc->AddStyleSheet(clonedSheet);
10094 : }
10095 : }
10096 : }
10097 : }
10098 :
10099 : // Iterate backwards to maintain order
10100 0 : for (StyleSheet* sheet : Reversed(thisAsDoc->mOnDemandBuiltInUASheets)) {
10101 0 : if (sheet) {
10102 0 : if (sheet->IsApplicable()) {
10103 : RefPtr<StyleSheet> clonedSheet =
10104 0 : sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
10105 0 : NS_WARNING_ASSERTION(clonedSheet,
10106 : "Cloning a stylesheet didn't work!");
10107 0 : if (clonedSheet) {
10108 0 : clonedDoc->AddOnDemandBuiltInUASheet(clonedSheet);
10109 : }
10110 : }
10111 : }
10112 : }
10113 : }
10114 : }
10115 0 : mCreatingStaticClone = false;
10116 0 : return clonedDoc.forget();
10117 : }
10118 :
10119 : void
10120 0 : nsIDocument::UnlinkOriginalDocumentIfStatic()
10121 : {
10122 0 : if (IsStaticDocument() && mOriginalDocument) {
10123 0 : MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0);
10124 0 : mOriginalDocument->mStaticCloneCount--;
10125 0 : mOriginalDocument = nullptr;
10126 : }
10127 0 : MOZ_ASSERT(!mOriginalDocument);
10128 0 : }
10129 :
10130 : nsresult
10131 4 : nsIDocument::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
10132 : int32_t *aHandle)
10133 : {
10134 4 : if (mFrameRequestCallbackCounter == INT32_MAX) {
10135 : // Can't increment without overflowing; bail out
10136 0 : return NS_ERROR_NOT_AVAILABLE;
10137 : }
10138 4 : int32_t newHandle = ++mFrameRequestCallbackCounter;
10139 :
10140 : DebugOnly<FrameRequest*> request =
10141 8 : mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
10142 4 : NS_ASSERTION(request, "This is supposed to be infallible!");
10143 4 : UpdateFrameRequestCallbackSchedulingState();
10144 :
10145 4 : *aHandle = newHandle;
10146 4 : return NS_OK;
10147 : }
10148 :
10149 : void
10150 0 : nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
10151 : {
10152 : // mFrameRequestCallbacks is stored sorted by handle
10153 0 : if (mFrameRequestCallbacks.RemoveElementSorted(aHandle)) {
10154 0 : UpdateFrameRequestCallbackSchedulingState();
10155 : }
10156 0 : }
10157 :
10158 : nsresult
10159 0 : nsDocument::GetStateObject(nsIVariant** aState)
10160 : {
10161 : // Get the document's current state object. This is the object backing both
10162 : // history.state and popStateEvent.state.
10163 : //
10164 : // mStateObjectContainer may be null; this just means that there's no
10165 : // current state object.
10166 :
10167 0 : if (!mStateObjectCached && mStateObjectContainer) {
10168 0 : AutoJSContext cx;
10169 0 : nsIGlobalObject* sgo = GetScopeObject();
10170 0 : NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
10171 0 : JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
10172 0 : NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
10173 0 : JSAutoCompartment ac(cx, global);
10174 :
10175 0 : mStateObjectContainer->
10176 0 : DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
10177 : }
10178 :
10179 0 : NS_IF_ADDREF(*aState = mStateObjectCached);
10180 :
10181 0 : return NS_OK;
10182 : }
10183 :
10184 : nsDOMNavigationTiming*
10185 148 : nsDocument::GetNavigationTiming() const
10186 : {
10187 148 : return mTiming;
10188 : }
10189 :
10190 : nsresult
10191 29 : nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
10192 : {
10193 29 : mTiming = aTiming;
10194 29 : if (!mLoadingTimeStamp.IsNull() && mTiming) {
10195 24 : mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp);
10196 : }
10197 29 : return NS_OK;
10198 : }
10199 :
10200 : Element*
10201 0 : nsDocument::FindImageMap(const nsAString& aUseMapValue)
10202 : {
10203 0 : if (aUseMapValue.IsEmpty()) {
10204 0 : return nullptr;
10205 : }
10206 :
10207 0 : nsAString::const_iterator start, end;
10208 0 : aUseMapValue.BeginReading(start);
10209 0 : aUseMapValue.EndReading(end);
10210 :
10211 0 : int32_t hash = aUseMapValue.FindChar('#');
10212 0 : if (hash < 0) {
10213 0 : return nullptr;
10214 : }
10215 : // aUsemap contains a '#', set start to point right after the '#'
10216 0 : start.advance(hash + 1);
10217 :
10218 0 : if (start == end) {
10219 0 : return nullptr; // aUsemap == "#"
10220 : }
10221 :
10222 0 : const nsAString& mapName = Substring(start, end);
10223 :
10224 0 : if (!mImageMaps) {
10225 0 : mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
10226 : }
10227 :
10228 0 : uint32_t i, n = mImageMaps->Length(true);
10229 0 : nsString name;
10230 0 : for (i = 0; i < n; ++i) {
10231 0 : nsIContent* map = mImageMaps->Item(i);
10232 0 : if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
10233 0 : eCaseMatters) ||
10234 0 : map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, mapName,
10235 : eCaseMatters)) {
10236 0 : return map->AsElement();
10237 : }
10238 : }
10239 :
10240 0 : return nullptr;
10241 : }
10242 :
10243 : #define DEPRECATED_OPERATION(_op) #_op "Warning",
10244 : static const char* kDeprecationWarnings[] = {
10245 : #include "nsDeprecatedOperationList.h"
10246 : nullptr
10247 : };
10248 : #undef DEPRECATED_OPERATION
10249 :
10250 : #define DOCUMENT_WARNING(_op) #_op "Warning",
10251 : static const char* kDocumentWarnings[] = {
10252 : #include "nsDocumentWarningList.h"
10253 : nullptr
10254 : };
10255 : #undef DOCUMENT_WARNING
10256 :
10257 : static UseCounter
10258 0 : OperationToUseCounter(nsIDocument::DeprecatedOperations aOperation)
10259 : {
10260 0 : switch(aOperation) {
10261 : #define DEPRECATED_OPERATION(_op) \
10262 : case nsIDocument::e##_op: return eUseCounter_##_op;
10263 : #include "nsDeprecatedOperationList.h"
10264 : #undef DEPRECATED_OPERATION
10265 : default:
10266 0 : MOZ_CRASH();
10267 : }
10268 : }
10269 :
10270 : bool
10271 8 : nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const
10272 : {
10273 8 : return mDeprecationWarnedAbout[aOperation];
10274 : }
10275 :
10276 : void
10277 0 : nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
10278 : bool asError /* = false */) const
10279 : {
10280 0 : MOZ_ASSERT(NS_IsMainThread());
10281 0 : if (HasWarnedAbout(aOperation)) {
10282 0 : return;
10283 : }
10284 0 : mDeprecationWarnedAbout[aOperation] = true;
10285 : // Don't count deprecated operations for about pages since those pages
10286 : // are almost in our control, and we always need to remove uses there
10287 : // before we remove the operation itself anyway.
10288 0 : if (!static_cast<const nsDocument*>(this)->IsAboutPage()) {
10289 : const_cast<nsIDocument*>(this)->
10290 0 : SetDocumentAndPageUseCounter(OperationToUseCounter(aOperation));
10291 : }
10292 0 : uint32_t flags = asError ? nsIScriptError::errorFlag
10293 0 : : nsIScriptError::warningFlag;
10294 0 : nsContentUtils::ReportToConsole(flags,
10295 0 : NS_LITERAL_CSTRING("DOM Core"), this,
10296 : nsContentUtils::eDOM_PROPERTIES,
10297 0 : kDeprecationWarnings[aOperation]);
10298 : }
10299 :
10300 : bool
10301 0 : nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const
10302 : {
10303 0 : return mDocWarningWarnedAbout[aWarning];
10304 : }
10305 :
10306 : void
10307 0 : nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
10308 : bool asError /* = false */,
10309 : const char16_t **aParams /* = nullptr */,
10310 : uint32_t aParamsLength /* = 0 */) const
10311 : {
10312 0 : MOZ_ASSERT(NS_IsMainThread());
10313 0 : if (HasWarnedAbout(aWarning)) {
10314 0 : return;
10315 : }
10316 0 : mDocWarningWarnedAbout[aWarning] = true;
10317 0 : uint32_t flags = asError ? nsIScriptError::errorFlag
10318 0 : : nsIScriptError::warningFlag;
10319 0 : nsContentUtils::ReportToConsole(flags,
10320 0 : NS_LITERAL_CSTRING("DOM Core"), this,
10321 : nsContentUtils::eDOM_PROPERTIES,
10322 : kDocumentWarnings[aWarning],
10323 : aParams,
10324 0 : aParamsLength);
10325 : }
10326 :
10327 : mozilla::dom::ImageTracker*
10328 56 : nsIDocument::ImageTracker()
10329 : {
10330 56 : if (!mImageTracker) {
10331 28 : mImageTracker = new mozilla::dom::ImageTracker;
10332 : }
10333 56 : return mImageTracker;
10334 : }
10335 :
10336 : nsresult
10337 0 : nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
10338 : {
10339 0 : MOZ_ASSERT(aPlugin);
10340 0 : if (!mPlugins.PutEntry(aPlugin)) {
10341 0 : return NS_ERROR_OUT_OF_MEMORY;
10342 : }
10343 0 : return NS_OK;
10344 : }
10345 :
10346 : void
10347 0 : nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin)
10348 : {
10349 0 : MOZ_ASSERT(aPlugin);
10350 0 : mPlugins.RemoveEntry(aPlugin);
10351 0 : }
10352 :
10353 : static bool
10354 0 : AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
10355 : {
10356 : nsTArray<nsIObjectLoadingContent*>* plugins =
10357 0 : reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
10358 0 : MOZ_ASSERT(plugins);
10359 0 : aDocument->GetPlugins(*plugins);
10360 0 : return true;
10361 : }
10362 :
10363 : void
10364 0 : nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
10365 : {
10366 0 : aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
10367 0 : for (auto iter = mPlugins.ConstIter(); !iter.Done(); iter.Next()) {
10368 0 : aPlugins.AppendElement(iter.Get()->GetKey());
10369 : }
10370 0 : EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
10371 0 : }
10372 :
10373 : nsresult
10374 0 : nsDocument::AddResponsiveContent(nsIContent* aContent)
10375 : {
10376 0 : MOZ_ASSERT(aContent);
10377 0 : MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::img));
10378 0 : mResponsiveContent.PutEntry(aContent);
10379 0 : return NS_OK;
10380 : }
10381 :
10382 : void
10383 0 : nsDocument::RemoveResponsiveContent(nsIContent* aContent)
10384 : {
10385 0 : MOZ_ASSERT(aContent);
10386 0 : mResponsiveContent.RemoveEntry(aContent);
10387 0 : }
10388 :
10389 : void
10390 3 : nsDocument::NotifyMediaFeatureValuesChanged()
10391 : {
10392 3 : for (auto iter = mResponsiveContent.ConstIter(); !iter.Done();
10393 0 : iter.Next()) {
10394 0 : nsCOMPtr<nsIContent> content = iter.Get()->GetKey();
10395 0 : if (content->IsHTMLElement(nsGkAtoms::img)) {
10396 0 : auto* imageElement = static_cast<HTMLImageElement*>(content.get());
10397 0 : imageElement->MediaFeatureValuesChanged();
10398 : }
10399 : }
10400 3 : }
10401 :
10402 : already_AddRefed<Touch>
10403 0 : nsIDocument::CreateTouch(nsGlobalWindow* aView,
10404 : EventTarget* aTarget,
10405 : int32_t aIdentifier,
10406 : int32_t aPageX, int32_t aPageY,
10407 : int32_t aScreenX, int32_t aScreenY,
10408 : int32_t aClientX, int32_t aClientY,
10409 : int32_t aRadiusX, int32_t aRadiusY,
10410 : float aRotationAngle,
10411 : float aForce)
10412 : {
10413 0 : MOZ_ASSERT_IF(aView, aView->IsInnerWindow());
10414 : RefPtr<Touch> touch = new Touch(aTarget,
10415 : aIdentifier,
10416 : aPageX, aPageY,
10417 : aScreenX, aScreenY,
10418 : aClientX, aClientY,
10419 : aRadiusX, aRadiusY,
10420 : aRotationAngle,
10421 0 : aForce);
10422 0 : return touch.forget();
10423 : }
10424 :
10425 : already_AddRefed<TouchList>
10426 0 : nsIDocument::CreateTouchList()
10427 : {
10428 0 : RefPtr<TouchList> retval = new TouchList(ToSupports(this));
10429 0 : return retval.forget();
10430 : }
10431 :
10432 : already_AddRefed<TouchList>
10433 0 : nsIDocument::CreateTouchList(Touch& aTouch,
10434 : const Sequence<OwningNonNull<Touch> >& aTouches)
10435 : {
10436 0 : RefPtr<TouchList> retval = new TouchList(ToSupports(this));
10437 0 : retval->Append(&aTouch);
10438 0 : for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10439 0 : retval->Append(aTouches[i].get());
10440 : }
10441 0 : return retval.forget();
10442 : }
10443 :
10444 : already_AddRefed<TouchList>
10445 0 : nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
10446 : {
10447 0 : RefPtr<TouchList> retval = new TouchList(ToSupports(this));
10448 0 : for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10449 0 : retval->Append(aTouches[i].get());
10450 : }
10451 0 : return retval.forget();
10452 : }
10453 :
10454 : already_AddRefed<nsDOMCaretPosition>
10455 0 : nsIDocument::CaretPositionFromPoint(float aX, float aY)
10456 : {
10457 0 : nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
10458 0 : nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
10459 0 : nsPoint pt(x, y);
10460 :
10461 0 : FlushPendingNotifications(FlushType::Layout);
10462 :
10463 0 : nsIPresShell *ps = GetShell();
10464 0 : if (!ps) {
10465 0 : return nullptr;
10466 : }
10467 :
10468 0 : nsIFrame *rootFrame = ps->GetRootFrame();
10469 :
10470 : // XUL docs, unlike HTML, have no frame tree until everything's done loading
10471 0 : if (!rootFrame) {
10472 0 : return nullptr;
10473 : }
10474 :
10475 0 : nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
10476 0 : nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
10477 0 : if (!ptFrame) {
10478 0 : return nullptr;
10479 : }
10480 :
10481 : // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
10482 : // to adjust to frame-relative coordinates before we can perform this call.
10483 : // It should also not take into account the padding of the frame.
10484 0 : nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
10485 :
10486 : nsFrame::ContentOffsets offsets =
10487 0 : ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
10488 :
10489 0 : nsCOMPtr<nsIContent> node = offsets.content;
10490 0 : uint32_t offset = offsets.offset;
10491 0 : nsCOMPtr<nsIContent> anonNode = node;
10492 0 : bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
10493 0 : if (nodeIsAnonymous) {
10494 0 : node = ptFrame->GetContent();
10495 0 : nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
10496 0 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
10497 0 : nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
10498 0 : nsNumberControlFrame* numberFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
10499 0 : if (textFrame || numberFrame) {
10500 : // If the anonymous content node has a child, then we need to make sure
10501 : // that we get the appropriate child, as otherwise the offset may not be
10502 : // correct when we construct a range for it.
10503 0 : nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
10504 0 : if (firstChild) {
10505 0 : anonNode = firstChild;
10506 : }
10507 :
10508 0 : if (textArea) {
10509 0 : offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
10510 : }
10511 :
10512 0 : node = nonanon;
10513 : } else {
10514 0 : node = nullptr;
10515 0 : offset = 0;
10516 : }
10517 : }
10518 :
10519 0 : RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
10520 0 : if (nodeIsAnonymous) {
10521 0 : aCaretPos->SetAnonymousContentNode(anonNode);
10522 : }
10523 0 : return aCaretPos.forget();
10524 : }
10525 :
10526 : NS_IMETHODIMP
10527 0 : nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
10528 : {
10529 0 : NS_ENSURE_ARG_POINTER(aCaretPos);
10530 0 : *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take();
10531 0 : return NS_OK;
10532 : }
10533 :
10534 : bool
10535 0 : nsIDocument::IsPotentiallyScrollable(HTMLBodyElement* aBody)
10536 : {
10537 : // We rely on correct frame information here, so need to flush frames.
10538 0 : FlushPendingNotifications(FlushType::Frames);
10539 :
10540 : // An element is potentially scrollable if all of the following conditions are
10541 : // true:
10542 :
10543 : // The element has an associated CSS layout box.
10544 0 : nsIFrame* bodyFrame = aBody->GetPrimaryFrame();
10545 0 : if (!bodyFrame) {
10546 0 : return false;
10547 : }
10548 :
10549 : // The element is not the HTML body element, or it is and the root element's
10550 : // used value of the overflow-x or overflow-y properties is not visible.
10551 0 : MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement());
10552 0 : nsIFrame* parentFrame = aBody->GetParent()->GetPrimaryFrame();
10553 0 : if (parentFrame &&
10554 0 : parentFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
10555 0 : parentFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
10556 0 : return false;
10557 : }
10558 :
10559 : // The element's used value of the overflow-x or overflow-y properties is not
10560 : // visible.
10561 0 : if (bodyFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE &&
10562 0 : bodyFrame->StyleDisplay()->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) {
10563 0 : return false;
10564 : }
10565 :
10566 0 : return true;
10567 : }
10568 :
10569 : Element*
10570 0 : nsIDocument::GetScrollingElement()
10571 : {
10572 : // Keep this in sync with IsScrollingElement.
10573 0 : if (GetCompatibilityMode() == eCompatibility_NavQuirks) {
10574 0 : HTMLBodyElement* body = GetBodyElement();
10575 0 : if (body && !IsPotentiallyScrollable(body)) {
10576 0 : return body;
10577 : }
10578 :
10579 0 : return nullptr;
10580 : }
10581 :
10582 0 : return GetRootElement();
10583 : }
10584 :
10585 : bool
10586 2 : nsIDocument::IsScrollingElement(Element* aElement)
10587 : {
10588 : // Keep this in sync with GetScrollingElement.
10589 2 : MOZ_ASSERT(aElement);
10590 :
10591 2 : if (GetCompatibilityMode() != eCompatibility_NavQuirks) {
10592 2 : return aElement == GetRootElement();
10593 : }
10594 :
10595 0 : HTMLBodyElement* body = GetBodyElement();
10596 0 : if (aElement != body) {
10597 0 : return false;
10598 : }
10599 :
10600 : // Now we know body is non-null, since aElement is not null. It's the
10601 : // scrolling element for the document if it itself is not potentially
10602 : // scrollable.
10603 0 : return !IsPotentiallyScrollable(body);
10604 : }
10605 :
10606 : void
10607 0 : nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
10608 : {
10609 0 : nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
10610 0 : if (NS_FAILED(res)) {
10611 0 : rv.Throw(res);
10612 : }
10613 0 : }
10614 :
10615 : void
10616 0 : nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
10617 : {
10618 0 : nsCOMPtr<nsIURI> uri;
10619 0 : nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
10620 0 : if (NS_FAILED(res)) {
10621 0 : rv.Throw(res);
10622 0 : return;
10623 : }
10624 0 : res = CSSLoader()->ObsoleteSheet(uri);
10625 0 : if (NS_FAILED(res)) {
10626 0 : rv.Throw(res);
10627 : }
10628 : }
10629 :
10630 : class UnblockParsingPromiseHandler final : public PromiseNativeHandler
10631 : {
10632 : public:
10633 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
10634 0 : NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
10635 :
10636 0 : explicit UnblockParsingPromiseHandler(nsIDocument* aDocument, Promise* aPromise)
10637 0 : : mPromise(aPromise)
10638 : {
10639 0 : nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
10640 0 : if (parser) {
10641 0 : parser->BlockParser();
10642 0 : mParser = do_GetWeakReference(parser);
10643 0 : mDocument = aDocument;
10644 : }
10645 0 : }
10646 :
10647 : void
10648 0 : ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
10649 : {
10650 0 : MaybeUnblockParser();
10651 :
10652 0 : mPromise->MaybeResolve(aCx, aValue);
10653 0 : }
10654 :
10655 : void
10656 0 : RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
10657 : {
10658 0 : MaybeUnblockParser();
10659 :
10660 0 : mPromise->MaybeReject(aCx, aValue);
10661 0 : }
10662 :
10663 : protected:
10664 0 : virtual ~UnblockParsingPromiseHandler()
10665 0 : {
10666 : // If we're being cleaned up by the cycle collector, our mDocument reference
10667 : // may have been unlinked while our mParser weak reference is still alive.
10668 0 : if (mDocument) {
10669 0 : MaybeUnblockParser();
10670 : }
10671 0 : }
10672 :
10673 : private:
10674 0 : void MaybeUnblockParser() {
10675 0 : nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
10676 0 : if (parser) {
10677 0 : MOZ_DIAGNOSTIC_ASSERT(mDocument);
10678 0 : nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
10679 0 : if (parser == docParser) {
10680 0 : parser->UnblockParser();
10681 0 : parser->ContinueInterruptedParsingAsync();
10682 : }
10683 : }
10684 0 : mParser = nullptr;
10685 0 : mDocument = nullptr;
10686 0 : }
10687 :
10688 : nsWeakPtr mParser;
10689 : RefPtr<Promise> mPromise;
10690 : RefPtr<nsIDocument> mDocument;
10691 : };
10692 :
10693 0 : NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
10694 :
10695 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
10696 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
10697 0 : NS_INTERFACE_MAP_END
10698 :
10699 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler)
10700 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler)
10701 :
10702 : already_AddRefed<Promise>
10703 0 : nsIDocument::BlockParsing(Promise& aPromise, ErrorResult& aRv)
10704 : {
10705 0 : RefPtr<Promise> resultPromise = Promise::Create(aPromise.GetParentObject(), aRv);
10706 0 : if (aRv.Failed()) {
10707 0 : return nullptr;
10708 : }
10709 :
10710 0 : RefPtr<PromiseNativeHandler> promiseHandler = new UnblockParsingPromiseHandler(this, resultPromise);
10711 0 : aPromise.AppendNativeHandler(promiseHandler);
10712 :
10713 0 : return resultPromise.forget();
10714 : }
10715 :
10716 : already_AddRefed<nsIURI>
10717 0 : nsIDocument::GetMozDocumentURIIfNotForErrorPages()
10718 : {
10719 0 : if (mFailedChannel) {
10720 0 : nsCOMPtr<nsIURI> failedURI;
10721 0 : if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
10722 0 : return failedURI.forget();
10723 : }
10724 : }
10725 :
10726 0 : nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
10727 0 : if (!uri) {
10728 0 : return nullptr;
10729 : }
10730 :
10731 0 : return uri.forget();
10732 : }
10733 :
10734 : nsIHTMLCollection*
10735 0 : nsIDocument::Children()
10736 : {
10737 0 : if (!mChildrenCollection) {
10738 0 : mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
10739 : nsGkAtoms::_asterisk,
10740 : nsGkAtoms::_asterisk,
10741 0 : false);
10742 : }
10743 :
10744 0 : return mChildrenCollection;
10745 : }
10746 :
10747 : uint32_t
10748 0 : nsIDocument::ChildElementCount()
10749 : {
10750 0 : return Children()->Length();
10751 : }
10752 :
10753 : namespace mozilla {
10754 :
10755 : // Singleton class to manage the list of fullscreen documents which are the
10756 : // root of a branch which contains fullscreen documents. We maintain this list
10757 : // so that we can easily exit all windows from fullscreen when the user
10758 : // presses the escape key.
10759 : class FullscreenRoots {
10760 : public:
10761 : // Adds the root of given document to the manager. Calling this method
10762 : // with a document whose root is already contained has no effect.
10763 : static void Add(nsIDocument* aDoc);
10764 :
10765 : // Iterates over every root in the root list, and calls aFunction, passing
10766 : // each root once to aFunction. It is safe to call Add() and Remove() while
10767 : // iterating over the list (i.e. in aFunction). Documents that are removed
10768 : // from the manager during traversal are not traversed, and documents that
10769 : // are added to the manager during traversal are also not traversed.
10770 : static void ForEach(void(*aFunction)(nsIDocument* aDoc));
10771 :
10772 : // Removes the root of a specific document from the manager.
10773 : static void Remove(nsIDocument* aDoc);
10774 :
10775 : // Returns true if all roots added to the list have been removed.
10776 : static bool IsEmpty();
10777 :
10778 : private:
10779 :
10780 0 : FullscreenRoots() {
10781 0 : MOZ_COUNT_CTOR(FullscreenRoots);
10782 0 : }
10783 0 : ~FullscreenRoots() {
10784 0 : MOZ_COUNT_DTOR(FullscreenRoots);
10785 0 : }
10786 :
10787 : enum {
10788 : NotFound = uint32_t(-1)
10789 : };
10790 : // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
10791 : static uint32_t Find(nsIDocument* aRoot);
10792 :
10793 : // Returns true if aRoot is in the list of fullscreen roots.
10794 : static bool Contains(nsIDocument* aRoot);
10795 :
10796 : // Singleton instance of the FullscreenRoots. This is instantiated when a
10797 : // root is added, and it is deleted when the last root is removed.
10798 : static FullscreenRoots* sInstance;
10799 :
10800 : // List of weak pointers to roots.
10801 : nsTArray<nsWeakPtr> mRoots;
10802 : };
10803 :
10804 : FullscreenRoots* FullscreenRoots::sInstance = nullptr;
10805 :
10806 : /* static */
10807 : void
10808 0 : FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
10809 : {
10810 0 : if (!sInstance) {
10811 0 : return;
10812 : }
10813 : // Create a copy of the roots array, and iterate over the copy. This is so
10814 : // that if an element is removed from mRoots we don't mess up our iteration.
10815 0 : nsTArray<nsWeakPtr> roots(sInstance->mRoots);
10816 : // Call aFunction on all entries.
10817 0 : for (uint32_t i = 0; i < roots.Length(); i++) {
10818 0 : nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
10819 : // Check that the root isn't in the manager. This is so that new additions
10820 : // while we were running don't get traversed.
10821 0 : if (root && FullscreenRoots::Contains(root)) {
10822 0 : aFunction(root);
10823 : }
10824 : }
10825 : }
10826 :
10827 : /* static */
10828 : bool
10829 0 : FullscreenRoots::Contains(nsIDocument* aRoot)
10830 : {
10831 0 : return FullscreenRoots::Find(aRoot) != NotFound;
10832 : }
10833 :
10834 : /* static */
10835 : void
10836 0 : FullscreenRoots::Add(nsIDocument* aDoc)
10837 : {
10838 0 : nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
10839 0 : if (!FullscreenRoots::Contains(root)) {
10840 0 : if (!sInstance) {
10841 0 : sInstance = new FullscreenRoots();
10842 : }
10843 0 : sInstance->mRoots.AppendElement(do_GetWeakReference(root));
10844 : }
10845 0 : }
10846 :
10847 : /* static */
10848 : uint32_t
10849 0 : FullscreenRoots::Find(nsIDocument* aRoot)
10850 : {
10851 0 : if (!sInstance) {
10852 0 : return NotFound;
10853 : }
10854 0 : nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
10855 0 : for (uint32_t i = 0; i < roots.Length(); i++) {
10856 0 : nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
10857 0 : if (otherRoot == aRoot) {
10858 0 : return i;
10859 : }
10860 : }
10861 0 : return NotFound;
10862 : }
10863 :
10864 : /* static */
10865 : void
10866 0 : FullscreenRoots::Remove(nsIDocument* aDoc)
10867 : {
10868 0 : nsCOMPtr<nsIDocument> root = nsContentUtils::GetRootDocument(aDoc);
10869 0 : uint32_t index = Find(root);
10870 0 : NS_ASSERTION(index != NotFound,
10871 : "Should only try to remove roots which are still added!");
10872 0 : if (index == NotFound || !sInstance) {
10873 0 : return;
10874 : }
10875 0 : sInstance->mRoots.RemoveElementAt(index);
10876 0 : if (sInstance->mRoots.IsEmpty()) {
10877 0 : delete sInstance;
10878 0 : sInstance = nullptr;
10879 : }
10880 : }
10881 :
10882 : /* static */
10883 : bool
10884 0 : FullscreenRoots::IsEmpty()
10885 : {
10886 0 : return !sInstance;
10887 : }
10888 :
10889 : } // end namespace mozilla.
10890 : using mozilla::FullscreenRoots;
10891 :
10892 : nsIDocument*
10893 0 : nsDocument::GetFullscreenRoot()
10894 : {
10895 0 : nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
10896 0 : return root;
10897 : }
10898 :
10899 : void
10900 0 : nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
10901 : {
10902 0 : mFullscreenRoot = do_GetWeakReference(aRoot);
10903 0 : }
10904 :
10905 : void
10906 0 : nsIDocument::ExitFullscreen()
10907 : {
10908 0 : RestorePreviousFullScreenState();
10909 0 : }
10910 :
10911 : static void
10912 0 : AskWindowToExitFullscreen(nsIDocument* aDoc)
10913 : {
10914 0 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
10915 0 : nsContentUtils::DispatchEventOnlyToChrome(
10916 0 : aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
10917 : /* Bubbles */ true, /* Cancelable */ false,
10918 0 : /* DefaultAction */ nullptr);
10919 : } else {
10920 0 : if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
10921 0 : win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false);
10922 : }
10923 : }
10924 0 : }
10925 :
10926 0 : class nsCallExitFullscreen : public Runnable
10927 : {
10928 : public:
10929 0 : explicit nsCallExitFullscreen(nsIDocument* aDoc)
10930 0 : : mozilla::Runnable("nsCallExitFullscreen")
10931 0 : , mDoc(aDoc)
10932 : {
10933 0 : }
10934 :
10935 0 : NS_IMETHOD Run() override final
10936 : {
10937 0 : if (!mDoc) {
10938 0 : FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
10939 : } else {
10940 0 : AskWindowToExitFullscreen(mDoc);
10941 : }
10942 0 : return NS_OK;
10943 : }
10944 :
10945 : private:
10946 : nsCOMPtr<nsIDocument> mDoc;
10947 : };
10948 :
10949 : /* static */ void
10950 0 : nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
10951 : {
10952 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
10953 0 : nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
10954 0 : if (aDoc) {
10955 0 : aDoc->Dispatch("nsCallExitFullscreen", TaskCategory::Other, exit.forget());
10956 : } else {
10957 0 : NS_DispatchToCurrentThread(exit.forget());
10958 : }
10959 0 : }
10960 :
10961 : static bool
10962 0 : CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
10963 : {
10964 0 : if (aDoc->GetFullscreenElement()) {
10965 0 : uint32_t* count = static_cast<uint32_t*>(aData);
10966 0 : (*count)++;
10967 : }
10968 0 : return true;
10969 : }
10970 :
10971 : static uint32_t
10972 0 : CountFullscreenSubDocuments(nsIDocument* aDoc)
10973 : {
10974 0 : uint32_t count = 0;
10975 0 : aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
10976 0 : return count;
10977 : }
10978 :
10979 : bool
10980 0 : nsDocument::IsFullscreenLeaf()
10981 : {
10982 : // A fullscreen leaf document is fullscreen, and has no fullscreen
10983 : // subdocuments.
10984 0 : if (!GetFullscreenElement()) {
10985 0 : return false;
10986 : }
10987 0 : return CountFullscreenSubDocuments(this) == 0;
10988 : }
10989 :
10990 : static bool
10991 0 : ResetFullScreen(nsIDocument* aDocument, void* aData)
10992 : {
10993 0 : if (aDocument->GetFullscreenElement()) {
10994 0 : NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
10995 : "Should have at most 1 fullscreen subdocument.");
10996 0 : static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
10997 0 : NS_ASSERTION(!aDocument->GetFullscreenElement(),
10998 : "Should reset full-screen");
10999 0 : auto changed = reinterpret_cast<nsCOMArray<nsIDocument>*>(aData);
11000 0 : changed->AppendElement(aDocument);
11001 0 : aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
11002 : }
11003 0 : return true;
11004 : }
11005 :
11006 : // Since nsIDocument::ExitFullscreenInDocTree() could be called from
11007 : // Element::UnbindFromTree() where it is not safe to synchronously run
11008 : // script. This runnable is the script part of that function.
11009 0 : class ExitFullscreenScriptRunnable : public Runnable
11010 : {
11011 : public:
11012 0 : explicit ExitFullscreenScriptRunnable(nsCOMArray<nsIDocument>&& aDocuments)
11013 0 : : mozilla::Runnable("ExitFullscreenScriptRunnable")
11014 0 : , mDocuments(Move(aDocuments))
11015 : {
11016 0 : }
11017 :
11018 0 : NS_IMETHOD Run() override
11019 : {
11020 : // Dispatch MozDOMFullscreen:Exited to the last document in
11021 : // the list since we want this event to follow the same path
11022 : // MozDOMFullscreen:Entered dispatched.
11023 0 : nsIDocument* lastDocument = mDocuments[mDocuments.Length() - 1];
11024 0 : nsContentUtils::DispatchEventOnlyToChrome(
11025 : lastDocument, ToSupports(lastDocument),
11026 0 : NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
11027 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
11028 : // Ensure the window exits fullscreen.
11029 0 : if (nsPIDOMWindowOuter* win = mDocuments[0]->GetWindow()) {
11030 0 : win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false);
11031 : }
11032 0 : return NS_OK;
11033 : }
11034 :
11035 : private:
11036 : nsCOMArray<nsIDocument> mDocuments;
11037 : };
11038 :
11039 : /* static */ void
11040 0 : nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
11041 : {
11042 0 : MOZ_ASSERT(aMaybeNotARootDoc);
11043 :
11044 : // Unlock the pointer
11045 0 : UnlockPointer();
11046 :
11047 0 : nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
11048 0 : if (!root || !root->GetFullscreenElement()) {
11049 : // If a document was detached before exiting from fullscreen, it is
11050 : // possible that the root had left fullscreen state. In this case,
11051 : // we would not get anything from the ResetFullScreen() call. Root's
11052 : // not being a fullscreen doc also means the widget should have
11053 : // exited fullscreen state. It means even if we do not return here,
11054 : // we would actually do nothing below except crashing ourselves via
11055 : // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
11056 : // document.
11057 0 : return;
11058 : }
11059 :
11060 : // Stores a list of documents to which we must dispatch "fullscreenchange".
11061 : // We're required by the spec to dispatch the events in leaf-to-root
11062 : // order when exiting fullscreen, but we traverse the doctree in a
11063 : // root-to-leaf order, so we save references to the documents we must
11064 : // dispatch to so that we dispatch in the specified order.
11065 0 : nsCOMArray<nsIDocument> changed;
11066 :
11067 : // Walk the tree of fullscreen documents, and reset their fullscreen state.
11068 0 : ResetFullScreen(root, static_cast<void*>(&changed));
11069 :
11070 : // Dispatch "fullscreenchange" events. Note this loop is in reverse
11071 : // order so that the events for the leaf document arrives before the root
11072 : // document, as required by the spec.
11073 0 : for (uint32_t i = 0; i < changed.Length(); ++i) {
11074 0 : DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11075 : }
11076 :
11077 0 : NS_ASSERTION(!root->GetFullscreenElement(),
11078 : "Fullscreen root should no longer be a fullscreen doc...");
11079 :
11080 : // Move the top-level window out of fullscreen mode.
11081 0 : FullscreenRoots::Remove(root);
11082 :
11083 0 : nsContentUtils::AddScriptRunner(
11084 0 : new ExitFullscreenScriptRunnable(Move(changed)));
11085 : }
11086 :
11087 : bool
11088 0 : GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
11089 : {
11090 0 : if (aDoc->IsFullscreenLeaf()) {
11091 0 : nsIDocument** result = static_cast<nsIDocument**>(aData);
11092 0 : *result = aDoc;
11093 0 : return false;
11094 0 : } else if (aDoc->GetFullscreenElement()) {
11095 0 : aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
11096 : }
11097 0 : return true;
11098 : }
11099 :
11100 : static nsIDocument*
11101 0 : GetFullscreenLeaf(nsIDocument* aDoc)
11102 : {
11103 0 : nsIDocument* leaf = nullptr;
11104 0 : GetFullscreenLeaf(aDoc, &leaf);
11105 0 : if (leaf) {
11106 0 : return leaf;
11107 : }
11108 : // Otherwise we could be either in a non-fullscreen doc tree, or we're
11109 : // below the fullscreen doc. Start the search from the root.
11110 0 : nsIDocument* root = nsContentUtils::GetRootDocument(aDoc);
11111 : // Check that the root is actually fullscreen so we don't waste time walking
11112 : // around its descendants.
11113 0 : if (!root->GetFullscreenElement()) {
11114 0 : return nullptr;
11115 : }
11116 0 : GetFullscreenLeaf(root, &leaf);
11117 0 : return leaf;
11118 : }
11119 :
11120 : void
11121 0 : nsDocument::RestorePreviousFullScreenState()
11122 : {
11123 0 : NS_ASSERTION(!GetFullscreenElement() || !FullscreenRoots::IsEmpty(),
11124 : "Should have at least 1 fullscreen root when fullscreen!");
11125 :
11126 0 : if (!GetFullscreenElement() || !GetWindow() || FullscreenRoots::IsEmpty()) {
11127 0 : return;
11128 : }
11129 :
11130 0 : nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
11131 0 : AutoTArray<nsDocument*, 8> exitDocs;
11132 :
11133 0 : nsIDocument* doc = fullScreenDoc;
11134 : // Collect all subdocuments.
11135 0 : for (; doc != this; doc = doc->GetParentDocument()) {
11136 0 : exitDocs.AppendElement(static_cast<nsDocument*>(doc));
11137 : }
11138 0 : MOZ_ASSERT(doc == this, "Must have reached this doc");
11139 : // Collect all ancestor documents which we are going to change.
11140 0 : for (; doc; doc = doc->GetParentDocument()) {
11141 0 : nsDocument* theDoc = static_cast<nsDocument*>(doc);
11142 0 : MOZ_ASSERT(!theDoc->mFullScreenStack.IsEmpty(),
11143 : "Ancestor of fullscreen document must also be in fullscreen");
11144 0 : if (doc != this) {
11145 0 : Element* top = theDoc->FullScreenStackTop();
11146 0 : if (top->IsHTMLElement(nsGkAtoms::iframe)) {
11147 0 : if (static_cast<HTMLIFrameElement*>(top)->FullscreenFlag()) {
11148 : // If this is an iframe, and it explicitly requested
11149 : // fullscreen, don't rollback it automatically.
11150 0 : break;
11151 : }
11152 : }
11153 : }
11154 0 : exitDocs.AppendElement(theDoc);
11155 0 : if (theDoc->mFullScreenStack.Length() > 1) {
11156 0 : break;
11157 : }
11158 : }
11159 :
11160 0 : nsDocument* lastDoc = exitDocs.LastElement();
11161 0 : if (!lastDoc->GetParentDocument() &&
11162 0 : lastDoc->mFullScreenStack.Length() == 1) {
11163 : // If we are fully exiting fullscreen, don't touch anything here,
11164 : // just wait for the window to get out from fullscreen first.
11165 0 : AskWindowToExitFullscreen(this);
11166 0 : return;
11167 : }
11168 :
11169 : // If fullscreen mode is updated the pointer should be unlocked
11170 0 : UnlockPointer();
11171 : // All documents listed in the array except the last one are going to
11172 : // completely exit from the fullscreen state.
11173 0 : for (auto i : IntegerRange(exitDocs.Length() - 1)) {
11174 0 : exitDocs[i]->CleanupFullscreenState();
11175 : }
11176 : // The last document will either rollback one fullscreen element, or
11177 : // completely exit from the fullscreen state as well.
11178 : nsIDocument* newFullscreenDoc;
11179 0 : if (lastDoc->mFullScreenStack.Length() > 1) {
11180 0 : lastDoc->FullScreenStackPop();
11181 0 : newFullscreenDoc = lastDoc;
11182 : } else {
11183 0 : lastDoc->CleanupFullscreenState();
11184 0 : newFullscreenDoc = lastDoc->GetParentDocument();
11185 : }
11186 : // Dispatch the fullscreenchange event to all document listed.
11187 0 : for (nsDocument* d : exitDocs) {
11188 0 : DispatchFullScreenChange(d);
11189 : }
11190 :
11191 0 : MOZ_ASSERT(newFullscreenDoc, "If we were going to exit from fullscreen on "
11192 : "all documents in this doctree, we should've asked the window to "
11193 : "exit first instead of reaching here.");
11194 0 : if (fullScreenDoc != newFullscreenDoc &&
11195 0 : !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) {
11196 : // We've popped so enough off the stack that we've rolled back to
11197 : // a fullscreen element in a parent document. If this document is
11198 : // cross origin, dispatch an event to chrome so it knows to show
11199 : // the warning UI.
11200 0 : DispatchCustomEventWithFlush(
11201 0 : newFullscreenDoc, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
11202 0 : /* Bubbles */ true, /* ChromeOnly */ true);
11203 : }
11204 : }
11205 :
11206 0 : class nsCallRequestFullScreen : public Runnable
11207 : {
11208 : public:
11209 0 : explicit nsCallRequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
11210 0 : : mozilla::Runnable("nsCallRequestFullScreen")
11211 0 : , mRequest(Move(aRequest))
11212 : {
11213 0 : }
11214 :
11215 0 : NS_IMETHOD Run() override
11216 : {
11217 0 : mRequest->GetDocument()->RequestFullScreen(Move(mRequest));
11218 0 : return NS_OK;
11219 : }
11220 :
11221 : UniquePtr<FullscreenRequest> mRequest;
11222 : };
11223 :
11224 : void
11225 0 : nsDocument::AsyncRequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
11226 : {
11227 0 : if (!aRequest->GetElement()) {
11228 0 : MOZ_ASSERT_UNREACHABLE(
11229 : "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
11230 : return;
11231 : }
11232 :
11233 : // Request full-screen asynchronously.
11234 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
11235 0 : nsCOMPtr<nsIRunnable> event = new nsCallRequestFullScreen(Move(aRequest));
11236 0 : Dispatch("nsCallRequestFullScreen", TaskCategory::Other, event.forget());
11237 0 : }
11238 :
11239 : void
11240 0 : nsIDocument::DispatchFullscreenError(const char* aMessage)
11241 : {
11242 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
11243 : new AsyncEventDispatcher(this,
11244 0 : NS_LITERAL_STRING("fullscreenerror"),
11245 : true,
11246 0 : false);
11247 0 : asyncDispatcher->PostDOMEvent();
11248 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
11249 0 : NS_LITERAL_CSTRING("DOM"), this,
11250 : nsContentUtils::eDOM_PROPERTIES,
11251 0 : aMessage);
11252 0 : }
11253 :
11254 : static void
11255 0 : UpdateViewportScrollbarOverrideForFullscreen(nsIDocument* aDoc)
11256 : {
11257 0 : if (nsIPresShell* presShell = aDoc->GetShell()) {
11258 0 : if (nsPresContext* presContext = presShell->GetPresContext()) {
11259 0 : presContext->UpdateViewportScrollbarStylesOverride();
11260 : }
11261 : }
11262 0 : }
11263 :
11264 : static void
11265 0 : ClearFullscreenStateOnElement(Element* aElement)
11266 : {
11267 : // Remove styles from existing top element.
11268 0 : EventStateManager::SetFullScreenState(aElement, false);
11269 : // Reset iframe fullscreen flag.
11270 0 : if (aElement->IsHTMLElement(nsGkAtoms::iframe)) {
11271 0 : static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false);
11272 : }
11273 0 : }
11274 :
11275 : void
11276 0 : nsDocument::CleanupFullscreenState()
11277 : {
11278 : // Iterate the fullscreen stack and clear the fullscreen states.
11279 : // Since we also need to clear the fullscreen-ancestor state, and
11280 : // currently fullscreen elements can only be placed in hierarchy
11281 : // order in the stack, reversely iterating the stack could be more
11282 : // efficient. NOTE that fullscreen-ancestor state would be removed
11283 : // in bug 1199529, and the elements may not in hierarchy order
11284 : // after bug 1195213.
11285 0 : for (nsWeakPtr& weakPtr : Reversed(mFullScreenStack)) {
11286 0 : if (nsCOMPtr<Element> element = do_QueryReferent(weakPtr)) {
11287 0 : ClearFullscreenStateOnElement(element);
11288 : }
11289 : }
11290 0 : mFullScreenStack.Clear();
11291 0 : mFullscreenRoot = nullptr;
11292 0 : UpdateViewportScrollbarOverrideForFullscreen(this);
11293 0 : }
11294 :
11295 : bool
11296 0 : nsDocument::FullScreenStackPush(Element* aElement)
11297 : {
11298 0 : NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
11299 0 : Element* top = FullScreenStackTop();
11300 0 : if (top == aElement || !aElement) {
11301 0 : return false;
11302 : }
11303 0 : EventStateManager::SetFullScreenState(aElement, true);
11304 0 : mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
11305 0 : NS_ASSERTION(GetFullscreenElement() == aElement, "Should match");
11306 0 : UpdateViewportScrollbarOverrideForFullscreen(this);
11307 0 : return true;
11308 : }
11309 :
11310 : void
11311 0 : nsDocument::FullScreenStackPop()
11312 : {
11313 0 : if (mFullScreenStack.IsEmpty()) {
11314 0 : return;
11315 : }
11316 :
11317 0 : ClearFullscreenStateOnElement(FullScreenStackTop());
11318 :
11319 : // Remove top element. Note the remaining top element in the stack
11320 : // will not have full-screen style bits set, so we will need to restore
11321 : // them on the new top element before returning.
11322 0 : uint32_t last = mFullScreenStack.Length() - 1;
11323 0 : mFullScreenStack.RemoveElementAt(last);
11324 :
11325 : // Pop from the stack null elements (references to elements which have
11326 : // been GC'd since they were added to the stack) and elements which are
11327 : // no longer in this document.
11328 0 : while (!mFullScreenStack.IsEmpty()) {
11329 0 : Element* element = FullScreenStackTop();
11330 0 : if (!element || !element->IsInUncomposedDoc() || element->OwnerDoc() != this) {
11331 0 : NS_ASSERTION(!element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
11332 : "Should have already removed full-screen styles");
11333 0 : uint32_t last = mFullScreenStack.Length() - 1;
11334 0 : mFullScreenStack.RemoveElementAt(last);
11335 : } else {
11336 : // The top element of the stack is now an in-doc element. Return here.
11337 0 : break;
11338 : }
11339 : }
11340 :
11341 0 : UpdateViewportScrollbarOverrideForFullscreen(this);
11342 : }
11343 :
11344 : Element*
11345 32 : nsDocument::FullScreenStackTop()
11346 : {
11347 32 : if (mFullScreenStack.IsEmpty()) {
11348 32 : return nullptr;
11349 : }
11350 0 : uint32_t last = mFullScreenStack.Length() - 1;
11351 0 : nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
11352 0 : NS_ASSERTION(element, "Should have full-screen element!");
11353 0 : NS_ASSERTION(element->IsInUncomposedDoc(), "Full-screen element should be in doc");
11354 0 : NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
11355 0 : return element;
11356 : }
11357 :
11358 : /* virtual */ nsTArray<Element*>
11359 53 : nsDocument::GetFullscreenStack() const
11360 : {
11361 53 : nsTArray<Element*> elements;
11362 53 : for (const nsWeakPtr& ptr : mFullScreenStack) {
11363 0 : if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
11364 0 : MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN));
11365 0 : elements.AppendElement(elem);
11366 : }
11367 : }
11368 53 : return elements;
11369 : }
11370 :
11371 : // Returns true if aDoc is in the focused tab in the active window.
11372 : static bool
11373 0 : IsInActiveTab(nsIDocument* aDoc)
11374 : {
11375 0 : nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
11376 0 : if (!docshell) {
11377 0 : return false;
11378 : }
11379 :
11380 0 : bool isActive = false;
11381 0 : docshell->GetIsActive(&isActive);
11382 0 : if (!isActive) {
11383 0 : return false;
11384 : }
11385 :
11386 0 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
11387 0 : docshell->GetRootTreeItem(getter_AddRefs(rootItem));
11388 0 : if (!rootItem) {
11389 0 : return false;
11390 : }
11391 0 : nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
11392 0 : if (!rootWin) {
11393 0 : return false;
11394 : }
11395 :
11396 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11397 0 : if (!fm) {
11398 0 : return false;
11399 : }
11400 :
11401 0 : nsCOMPtr<mozIDOMWindowProxy> activeWindow;
11402 0 : fm->GetActiveWindow(getter_AddRefs(activeWindow));
11403 0 : if (!activeWindow) {
11404 0 : return false;
11405 : }
11406 :
11407 0 : return activeWindow == rootWin;
11408 : }
11409 :
11410 0 : nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement)
11411 : {
11412 : // Ensure the frame element is the fullscreen element in this document.
11413 : // If the frame element is already the fullscreen element in this document,
11414 : // this has no effect.
11415 0 : nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
11416 0 : auto request = MakeUnique<FullscreenRequest>(content->AsElement());
11417 0 : request->mIsCallerChrome = false;
11418 0 : request->mShouldNotifyNewOrigin = false;
11419 0 : RequestFullScreen(Move(request));
11420 :
11421 0 : return NS_OK;
11422 : }
11423 :
11424 0 : nsresult nsDocument::RemoteFrameFullscreenReverted()
11425 : {
11426 0 : RestorePreviousFullScreenState();
11427 0 : return NS_OK;
11428 : }
11429 :
11430 : /* static */ bool
11431 37 : nsDocument::IsUnprefixedFullscreenEnabled(JSContext* aCx, JSObject* aObject)
11432 : {
11433 37 : MOZ_ASSERT(NS_IsMainThread());
11434 58 : return nsContentUtils::IsSystemCaller(aCx) ||
11435 58 : nsContentUtils::IsUnprefixedFullscreenApiEnabled();
11436 : }
11437 :
11438 : static bool
11439 0 : HasFullScreenSubDocument(nsIDocument* aDoc)
11440 : {
11441 0 : uint32_t count = CountFullscreenSubDocuments(aDoc);
11442 0 : NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
11443 0 : return count >= 1;
11444 : }
11445 :
11446 : // Returns nullptr if a request for Fullscreen API is currently enabled
11447 : // in the given document. Returns a static string indicates the reason
11448 : // why it is not enabled otherwise.
11449 : static const char*
11450 0 : GetFullscreenError(nsIDocument* aDoc, bool aCallerIsChrome)
11451 : {
11452 0 : bool apiEnabled = nsContentUtils::IsFullScreenApiEnabled();
11453 0 : if (apiEnabled && aCallerIsChrome) {
11454 : // Chrome code can always use the full-screen API, provided it's not
11455 : // explicitly disabled.
11456 0 : return nullptr;
11457 : }
11458 :
11459 0 : if (!apiEnabled) {
11460 0 : return "FullscreenDeniedDisabled";
11461 : }
11462 :
11463 : // Ensure that all containing elements are <iframe> and have
11464 : // allowfullscreen attribute set.
11465 0 : nsCOMPtr<nsIDocShell> docShell(aDoc->GetDocShell());
11466 0 : if (!docShell || !docShell->GetFullscreenAllowed()) {
11467 0 : return "FullscreenDeniedContainerNotAllowed";
11468 : }
11469 0 : return nullptr;
11470 : }
11471 :
11472 : bool
11473 0 : nsDocument::FullscreenElementReadyCheck(Element* aElement,
11474 : bool aWasCallerChrome)
11475 : {
11476 0 : NS_ASSERTION(aElement,
11477 : "Must pass non-null element to nsDocument::RequestFullScreen");
11478 0 : if (!aElement || aElement == GetFullscreenElement()) {
11479 0 : return false;
11480 : }
11481 0 : if (!aElement->IsInUncomposedDoc()) {
11482 0 : DispatchFullscreenError("FullscreenDeniedNotInDocument");
11483 0 : return false;
11484 : }
11485 0 : if (aElement->OwnerDoc() != this) {
11486 0 : DispatchFullscreenError("FullscreenDeniedMovedDocument");
11487 0 : return false;
11488 : }
11489 0 : if (!GetWindow()) {
11490 0 : DispatchFullscreenError("FullscreenDeniedLostWindow");
11491 0 : return false;
11492 : }
11493 0 : if (const char* msg = GetFullscreenError(this, aWasCallerChrome)) {
11494 0 : DispatchFullscreenError(msg);
11495 0 : return false;
11496 : }
11497 0 : if (!IsVisible()) {
11498 0 : DispatchFullscreenError("FullscreenDeniedHidden");
11499 0 : return false;
11500 : }
11501 0 : if (HasFullScreenSubDocument(this)) {
11502 0 : DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
11503 0 : return false;
11504 : }
11505 0 : if (GetFullscreenElement() &&
11506 0 : !nsContentUtils::ContentIsDescendantOf(aElement, GetFullscreenElement())) {
11507 : // If this document is full-screen, only grant full-screen requests from
11508 : // a descendant of the current full-screen element.
11509 0 : DispatchFullscreenError("FullscreenDeniedNotDescendant");
11510 0 : return false;
11511 : }
11512 0 : if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
11513 0 : DispatchFullscreenError("FullscreenDeniedNotFocusedTab");
11514 0 : return false;
11515 : }
11516 : // Deny requests when a windowed plugin is focused.
11517 0 : nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11518 0 : if (!fm) {
11519 0 : NS_WARNING("Failed to retrieve focus manager in full-screen request.");
11520 0 : return false;
11521 : }
11522 0 : nsCOMPtr<nsIDOMElement> focusedElement;
11523 0 : fm->GetFocusedElement(getter_AddRefs(focusedElement));
11524 0 : if (focusedElement) {
11525 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
11526 0 : if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
11527 0 : DispatchFullscreenError("FullscreenDeniedFocusedPlugin");
11528 0 : return false;
11529 : }
11530 : }
11531 0 : return true;
11532 : }
11533 :
11534 0 : FullscreenRequest::FullscreenRequest(Element* aElement)
11535 : : mElement(aElement)
11536 0 : , mDocument(static_cast<nsDocument*>(aElement->OwnerDoc()))
11537 : {
11538 0 : MOZ_COUNT_CTOR(FullscreenRequest);
11539 0 : }
11540 :
11541 0 : FullscreenRequest::~FullscreenRequest()
11542 : {
11543 0 : MOZ_COUNT_DTOR(FullscreenRequest);
11544 0 : }
11545 :
11546 : // Any fullscreen request waiting for the widget to finish being full-
11547 : // screen is queued here. This is declared static instead of a member
11548 : // of nsDocument because in the majority of time, there would be at most
11549 : // one document requesting fullscreen. We shouldn't waste the space to
11550 : // hold for it in every document.
11551 : class PendingFullscreenRequestList
11552 : {
11553 : public:
11554 0 : static void Add(UniquePtr<FullscreenRequest>&& aRequest)
11555 : {
11556 0 : sList.insertBack(aRequest.release());
11557 0 : }
11558 :
11559 : static const FullscreenRequest* GetLast()
11560 : {
11561 : return sList.getLast();
11562 : }
11563 :
11564 : enum IteratorOption
11565 : {
11566 : // When we are committing fullscreen changes or preparing for
11567 : // that, we generally want to iterate all requests in the same
11568 : // window with eDocumentsWithSameRoot option.
11569 : eDocumentsWithSameRoot,
11570 : // If we are removing a document from the tree, we would only
11571 : // want to remove the requests from the given document and its
11572 : // descendants. For that case, use eInclusiveDescendants.
11573 : eInclusiveDescendants
11574 : };
11575 :
11576 4 : class Iterator
11577 : {
11578 : public:
11579 4 : explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
11580 4 : : mCurrent(PendingFullscreenRequestList::sList.getFirst())
11581 4 : , mRootShellForIteration(aDoc->GetDocShell())
11582 : {
11583 4 : if (mCurrent) {
11584 0 : if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
11585 0 : mRootShellForIteration->
11586 0 : GetRootTreeItem(getter_AddRefs(mRootShellForIteration));
11587 : }
11588 0 : SkipToNextMatch();
11589 : }
11590 4 : }
11591 :
11592 0 : void DeleteAndNext()
11593 : {
11594 0 : DeleteAndNextInternal();
11595 0 : SkipToNextMatch();
11596 0 : }
11597 4 : bool AtEnd() const { return mCurrent == nullptr; }
11598 0 : const FullscreenRequest& Get() const { return *mCurrent; }
11599 :
11600 : private:
11601 0 : void DeleteAndNextInternal()
11602 : {
11603 0 : FullscreenRequest* thisRequest = mCurrent;
11604 0 : mCurrent = mCurrent->getNext();
11605 0 : delete thisRequest;
11606 0 : }
11607 0 : void SkipToNextMatch()
11608 : {
11609 0 : while (mCurrent) {
11610 : nsCOMPtr<nsIDocShellTreeItem>
11611 0 : docShell = mCurrent->GetDocument()->GetDocShell();
11612 0 : if (!docShell) {
11613 : // Always automatically drop documents which has been
11614 : // detached from the doc shell.
11615 0 : DeleteAndNextInternal();
11616 : } else {
11617 0 : while (docShell && docShell != mRootShellForIteration) {
11618 0 : docShell->GetParent(getter_AddRefs(docShell));
11619 : }
11620 0 : if (!docShell) {
11621 : // We've gone over the root, but haven't find the target
11622 : // ancestor, so skip this item.
11623 0 : mCurrent = mCurrent->getNext();
11624 : } else {
11625 0 : break;
11626 : }
11627 : }
11628 : }
11629 0 : }
11630 :
11631 : FullscreenRequest* mCurrent;
11632 : nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
11633 : };
11634 :
11635 : private:
11636 : PendingFullscreenRequestList() = delete;
11637 :
11638 : static LinkedList<FullscreenRequest> sList;
11639 : };
11640 :
11641 3 : /* static */ LinkedList<FullscreenRequest> PendingFullscreenRequestList::sList;
11642 :
11643 : static nsCOMPtr<nsPIDOMWindowOuter>
11644 0 : GetRootWindow(nsIDocument* aDoc)
11645 : {
11646 0 : nsIDocShell* docShell = aDoc->GetDocShell();
11647 0 : if (!docShell) {
11648 0 : return nullptr;
11649 : }
11650 0 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
11651 0 : docShell->GetRootTreeItem(getter_AddRefs(rootItem));
11652 0 : return rootItem ? rootItem->GetWindow() : nullptr;
11653 : }
11654 :
11655 : static bool
11656 0 : ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
11657 : nsPIDOMWindowOuter* aRootWin)
11658 : {
11659 0 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
11660 : // If we are in the content process, we can apply the fullscreen
11661 : // state directly only if we have been in DOM fullscreen, because
11662 : // otherwise we always need to notify the chrome.
11663 0 : return !!nsContentUtils::GetRootDocument(aDoc)->GetFullscreenElement();
11664 : } else {
11665 : // If we are in the chrome process, and the window has not been in
11666 : // fullscreen, we certainly need to make that fullscreen first.
11667 0 : if (!aRootWin->GetFullScreen()) {
11668 0 : return false;
11669 : }
11670 : // The iterator not being at end indicates there is still some
11671 : // pending fullscreen request relates to this document. We have to
11672 : // push the request to the pending queue so requests are handled
11673 : // in the correct order.
11674 : PendingFullscreenRequestList::Iterator
11675 0 : iter(aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
11676 0 : if (!iter.AtEnd()) {
11677 0 : return false;
11678 : }
11679 : // We have to apply the fullscreen state directly in this case,
11680 : // because nsGlobalWindow::SetFullscreenInternal() will do nothing
11681 : // if it is already in fullscreen. If we do not apply the state but
11682 : // instead add it to the queue and wait for the window as normal,
11683 : // we would get stuck.
11684 0 : return true;
11685 : }
11686 : }
11687 :
11688 : void
11689 0 : nsDocument::RequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
11690 : {
11691 0 : nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
11692 0 : if (!rootWin) {
11693 0 : return;
11694 : }
11695 :
11696 0 : if (ShouldApplyFullscreenDirectly(this, rootWin)) {
11697 0 : ApplyFullscreen(*aRequest);
11698 0 : return;
11699 : }
11700 :
11701 : // Per spec only HTML, <svg>, and <math> should be allowed, but
11702 : // we also need to allow XUL elements right now.
11703 0 : Element* elem = aRequest->GetElement();
11704 0 : if (!elem->IsHTMLElement() && !elem->IsXULElement() &&
11705 0 : !elem->IsSVGElement(nsGkAtoms::svg) &&
11706 0 : !elem->IsMathMLElement(nsGkAtoms::math)) {
11707 0 : DispatchFullscreenError("FullscreenDeniedNotHTMLSVGOrMathML");
11708 0 : return;
11709 : }
11710 :
11711 : // We don't need to check element ready before this point, because
11712 : // if we called ApplyFullscreen, it would check that for us.
11713 0 : if (!FullscreenElementReadyCheck(elem, aRequest->mIsCallerChrome)) {
11714 0 : return;
11715 : }
11716 :
11717 0 : PendingFullscreenRequestList::Add(Move(aRequest));
11718 0 : if (XRE_GetProcessType() == GeckoProcessType_Content) {
11719 : // If we are not the top level process, dispatch an event to make
11720 : // our parent process go fullscreen first.
11721 0 : nsContentUtils::DispatchEventOnlyToChrome(
11722 0 : this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Request"),
11723 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
11724 : } else {
11725 : // Make the window fullscreen.
11726 0 : rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
11727 : }
11728 : }
11729 :
11730 : /* static */ bool
11731 0 : nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
11732 : {
11733 0 : bool handled = false;
11734 : PendingFullscreenRequestList::Iterator iter(
11735 0 : aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
11736 0 : while (!iter.AtEnd()) {
11737 0 : const FullscreenRequest& request = iter.Get();
11738 0 : if (request.GetDocument()->ApplyFullscreen(request)) {
11739 0 : handled = true;
11740 : }
11741 0 : iter.DeleteAndNext();
11742 : }
11743 0 : return handled;
11744 : }
11745 :
11746 : static void
11747 4 : ClearPendingFullscreenRequests(nsIDocument* aDoc)
11748 : {
11749 : PendingFullscreenRequestList::Iterator iter(
11750 8 : aDoc, PendingFullscreenRequestList::eInclusiveDescendants);
11751 4 : while (!iter.AtEnd()) {
11752 0 : iter.DeleteAndNext();
11753 : }
11754 4 : }
11755 :
11756 : bool
11757 0 : nsDocument::ApplyFullscreen(const FullscreenRequest& aRequest)
11758 : {
11759 0 : Element* elem = aRequest.GetElement();
11760 0 : if (!FullscreenElementReadyCheck(elem, aRequest.mIsCallerChrome)) {
11761 0 : return false;
11762 : }
11763 :
11764 : // Stash a reference to any existing fullscreen doc, we'll use this later
11765 : // to detect if the origin which is fullscreen has changed.
11766 0 : nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
11767 :
11768 : // Stores a list of documents which we must dispatch "fullscreenchange"
11769 : // too. We're required by the spec to dispatch the events in root-to-leaf
11770 : // order, but we traverse the doctree in a leaf-to-root order, so we save
11771 : // references to the documents we must dispatch to so that we get the order
11772 : // as specified.
11773 0 : AutoTArray<nsIDocument*, 8> changed;
11774 :
11775 : // Remember the root document, so that if a full-screen document is hidden
11776 : // we can reset full-screen state in the remaining visible full-screen documents.
11777 0 : nsIDocument* fullScreenRootDoc = nsContentUtils::GetRootDocument(this);
11778 :
11779 : // If a document is already in fullscreen, then unlock the mouse pointer
11780 : // before setting a new document to fullscreen
11781 0 : UnlockPointer();
11782 :
11783 : // Set the full-screen element. This sets the full-screen style on the
11784 : // element, and the full-screen-ancestor styles on ancestors of the element
11785 : // in this document.
11786 0 : DebugOnly<bool> x = FullScreenStackPush(elem);
11787 0 : NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
11788 : // Set the iframe fullscreen flag.
11789 0 : if (elem->IsHTMLElement(nsGkAtoms::iframe)) {
11790 0 : static_cast<HTMLIFrameElement*>(elem)->SetFullscreenFlag(true);
11791 : }
11792 0 : changed.AppendElement(this);
11793 :
11794 : // Propagate up the document hierarchy, setting the full-screen element as
11795 : // the element's container in ancestor documents. This also sets the
11796 : // appropriate css styles as well. Note we don't propagate down the
11797 : // document hierarchy, the full-screen element (or its container) is not
11798 : // visible there. Stop when we reach the root document.
11799 0 : nsIDocument* child = this;
11800 : while (true) {
11801 0 : child->SetFullscreenRoot(fullScreenRootDoc);
11802 0 : NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
11803 : "Fullscreen root should be set!");
11804 0 : if (child == fullScreenRootDoc) {
11805 0 : break;
11806 : }
11807 0 : nsIDocument* parent = child->GetParentDocument();
11808 0 : Element* element = parent->FindContentForSubDocument(child)->AsElement();
11809 0 : if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
11810 0 : changed.AppendElement(parent);
11811 0 : child = parent;
11812 : } else {
11813 : // We've reached either the root, or a point in the doctree where the
11814 : // new full-screen element container is the same as the previous
11815 : // full-screen element's container. No more changes need to be made
11816 : // to the full-screen stacks of documents further up the tree.
11817 0 : break;
11818 : }
11819 0 : }
11820 :
11821 0 : FullscreenRoots::Add(this);
11822 :
11823 : // If it is the first entry of the fullscreen, trigger an event so
11824 : // that the UI can response to this change, e.g. hide chrome, or
11825 : // notifying parent process to enter fullscreen. Note that chrome
11826 : // code may also want to listen to MozDOMFullscreen:NewOrigin event
11827 : // to pop up warning UI.
11828 0 : if (!previousFullscreenDoc) {
11829 0 : nsContentUtils::DispatchEventOnlyToChrome(
11830 0 : this, ToSupports(elem), NS_LITERAL_STRING("MozDOMFullscreen:Entered"),
11831 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
11832 : }
11833 :
11834 : // The origin which is fullscreen gets changed. Trigger an event so
11835 : // that the chrome knows to pop up a warning UI. Note that
11836 : // previousFullscreenDoc == nullptr upon first entry, so we always
11837 : // take this path on the first entry. Also note that, in a multi-
11838 : // process browser, the code in content process is responsible for
11839 : // sending message with the origin to its parent, and the parent
11840 : // shouldn't rely on this event itself.
11841 0 : if (aRequest.mShouldNotifyNewOrigin &&
11842 0 : !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11843 0 : DispatchCustomEventWithFlush(
11844 0 : this, NS_LITERAL_STRING("MozDOMFullscreen:NewOrigin"),
11845 0 : /* Bubbles */ true, /* ChromeOnly */ true);
11846 : }
11847 :
11848 : // Dispatch "fullscreenchange" events. Note this loop is in reverse
11849 : // order so that the events for the root document arrives before the leaf
11850 : // document, as required by the spec.
11851 0 : for (uint32_t i = 0; i < changed.Length(); ++i) {
11852 0 : DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11853 : }
11854 0 : return true;
11855 : }
11856 :
11857 : Element*
11858 32 : nsDocument::GetFullscreenElement()
11859 : {
11860 32 : Element* element = FullScreenStackTop();
11861 32 : NS_ASSERTION(!element ||
11862 : element->State().HasState(NS_EVENT_STATE_FULL_SCREEN),
11863 : "Fullscreen element should have fullscreen styles applied");
11864 32 : return element;
11865 : }
11866 :
11867 : bool
11868 0 : nsDocument::FullscreenEnabled(CallerType aCallerType)
11869 : {
11870 0 : return !GetFullscreenError(this, aCallerType == CallerType::System);
11871 : }
11872 :
11873 : uint16_t
11874 0 : nsDocument::CurrentOrientationAngle() const
11875 : {
11876 0 : return mCurrentOrientationAngle;
11877 : }
11878 :
11879 : OrientationType
11880 0 : nsDocument::CurrentOrientationType() const
11881 : {
11882 0 : return mCurrentOrientationType;
11883 : }
11884 :
11885 : void
11886 1 : nsDocument::SetCurrentOrientation(mozilla::dom::OrientationType aType,
11887 : uint16_t aAngle)
11888 : {
11889 1 : mCurrentOrientationType = aType;
11890 1 : mCurrentOrientationAngle = aAngle;
11891 1 : }
11892 :
11893 : Promise*
11894 0 : nsDocument::GetOrientationPendingPromise() const
11895 : {
11896 0 : return mOrientationPendingPromise;
11897 : }
11898 :
11899 : void
11900 0 : nsDocument::SetOrientationPendingPromise(Promise* aPromise)
11901 : {
11902 0 : mOrientationPendingPromise = aPromise;
11903 0 : }
11904 :
11905 : static void
11906 0 : DispatchPointerLockChange(nsIDocument* aTarget)
11907 : {
11908 0 : if (!aTarget) {
11909 0 : return;
11910 : }
11911 :
11912 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
11913 : new AsyncEventDispatcher(aTarget,
11914 0 : NS_LITERAL_STRING("pointerlockchange"),
11915 : true,
11916 0 : false);
11917 0 : asyncDispatcher->PostDOMEvent();
11918 : }
11919 :
11920 : static void
11921 0 : DispatchPointerLockError(nsIDocument* aTarget, const char* aMessage)
11922 : {
11923 0 : if (!aTarget) {
11924 0 : return;
11925 : }
11926 :
11927 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
11928 : new AsyncEventDispatcher(aTarget,
11929 0 : NS_LITERAL_STRING("pointerlockerror"),
11930 : true,
11931 0 : false);
11932 0 : asyncDispatcher->PostDOMEvent();
11933 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
11934 0 : NS_LITERAL_CSTRING("DOM"), aTarget,
11935 : nsContentUtils::eDOM_PROPERTIES,
11936 0 : aMessage);
11937 : }
11938 :
11939 0 : class PointerLockRequest final : public Runnable
11940 : {
11941 : public:
11942 0 : PointerLockRequest(Element* aElement, bool aUserInputOrChromeCaller)
11943 0 : : mozilla::Runnable("PointerLockRequest")
11944 0 : , mElement(do_GetWeakReference(aElement))
11945 0 : , mDocument(do_GetWeakReference(aElement->OwnerDoc()))
11946 0 : , mUserInputOrChromeCaller(aUserInputOrChromeCaller)
11947 0 : {}
11948 :
11949 : NS_IMETHOD Run() final;
11950 :
11951 : private:
11952 : nsWeakPtr mElement;
11953 : nsWeakPtr mDocument;
11954 : bool mUserInputOrChromeCaller;
11955 : };
11956 :
11957 : static const char*
11958 0 : GetPointerLockError(Element* aElement, Element* aCurrentLock,
11959 : bool aNoFocusCheck = false)
11960 : {
11961 : // Check if pointer lock pref is enabled
11962 0 : if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
11963 0 : return "PointerLockDeniedDisabled";
11964 : }
11965 :
11966 0 : nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
11967 0 : if (aCurrentLock && aCurrentLock->OwnerDoc() != ownerDoc) {
11968 0 : return "PointerLockDeniedInUse";
11969 : }
11970 :
11971 0 : if (!aElement->IsInUncomposedDoc()) {
11972 0 : return "PointerLockDeniedNotInDocument";
11973 : }
11974 :
11975 0 : if (ownerDoc->GetSandboxFlags() & SANDBOXED_POINTER_LOCK) {
11976 0 : return "PointerLockDeniedSandboxed";
11977 : }
11978 :
11979 : // Check if the element is in a document with a docshell.
11980 0 : if (!ownerDoc->GetContainer()) {
11981 0 : return "PointerLockDeniedHidden";
11982 : }
11983 0 : nsCOMPtr<nsPIDOMWindowOuter> ownerWindow = ownerDoc->GetWindow();
11984 0 : if (!ownerWindow) {
11985 0 : return "PointerLockDeniedHidden";
11986 : }
11987 0 : nsCOMPtr<nsPIDOMWindowInner> ownerInnerWindow = ownerDoc->GetInnerWindow();
11988 0 : if (!ownerInnerWindow) {
11989 0 : return "PointerLockDeniedHidden";
11990 : }
11991 0 : if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
11992 0 : return "PointerLockDeniedHidden";
11993 : }
11994 :
11995 0 : nsCOMPtr<nsPIDOMWindowOuter> top = ownerWindow->GetScriptableTop();
11996 0 : if (!top || !top->GetExtantDoc() || top->GetExtantDoc()->Hidden()) {
11997 0 : return "PointerLockDeniedHidden";
11998 : }
11999 :
12000 0 : if (!aNoFocusCheck) {
12001 0 : mozilla::ErrorResult rv;
12002 0 : if (!top->GetExtantDoc()->HasFocus(rv)) {
12003 0 : return "PointerLockDeniedNotFocused";
12004 : }
12005 : }
12006 :
12007 0 : return nullptr;
12008 : }
12009 :
12010 : static void
12011 0 : ChangePointerLockedElement(Element* aElement, nsIDocument* aDocument,
12012 : Element* aPointerLockedElement)
12013 : {
12014 : // aDocument here is not really necessary, as it is the uncomposed
12015 : // document of both aElement and aPointerLockedElement as far as one
12016 : // is not nullptr, and they wouldn't both be nullptr in any case.
12017 : // But since the caller of this function should have known what the
12018 : // document is, we just don't try to figure out what it should be.
12019 0 : MOZ_ASSERT(aDocument);
12020 0 : MOZ_ASSERT(aElement != aPointerLockedElement);
12021 0 : if (aPointerLockedElement) {
12022 0 : MOZ_ASSERT(aPointerLockedElement->GetUncomposedDoc() == aDocument);
12023 0 : aPointerLockedElement->ClearPointerLock();
12024 : }
12025 0 : if (aElement) {
12026 0 : MOZ_ASSERT(aElement->GetUncomposedDoc() == aDocument);
12027 0 : aElement->SetPointerLock();
12028 0 : EventStateManager::sPointerLockedElement = do_GetWeakReference(aElement);
12029 0 : EventStateManager::sPointerLockedDoc = do_GetWeakReference(aDocument);
12030 0 : NS_ASSERTION(EventStateManager::sPointerLockedElement &&
12031 : EventStateManager::sPointerLockedDoc,
12032 : "aElement and this should support weak references!");
12033 : } else {
12034 0 : EventStateManager::sPointerLockedElement = nullptr;
12035 0 : EventStateManager::sPointerLockedDoc = nullptr;
12036 : }
12037 : // Retarget all events to aElement via capture or
12038 : // stop retargeting if aElement is nullptr.
12039 0 : nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
12040 0 : DispatchPointerLockChange(aDocument);
12041 0 : }
12042 :
12043 : NS_IMETHODIMP
12044 0 : PointerLockRequest::Run()
12045 : {
12046 0 : nsCOMPtr<Element> e = do_QueryReferent(mElement);
12047 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
12048 0 : nsDocument* d = static_cast<nsDocument*>(doc.get());
12049 0 : const char* error = nullptr;
12050 0 : if (!e || !d || !e->GetUncomposedDoc()) {
12051 0 : error = "PointerLockDeniedNotInDocument";
12052 0 : } else if (e->GetUncomposedDoc() != d) {
12053 0 : error = "PointerLockDeniedMovedDocument";
12054 : }
12055 0 : if (!error) {
12056 : nsCOMPtr<Element> pointerLockedElement =
12057 0 : do_QueryReferent(EventStateManager::sPointerLockedElement);
12058 0 : if (e == pointerLockedElement) {
12059 0 : DispatchPointerLockChange(d);
12060 0 : return NS_OK;
12061 : }
12062 : // Note, we must bypass focus change, so pass true as the last parameter!
12063 0 : error = GetPointerLockError(e, pointerLockedElement, true);
12064 : // Another element in the same document is requesting pointer lock,
12065 : // just grant it without user input check.
12066 0 : if (!error && pointerLockedElement) {
12067 0 : ChangePointerLockedElement(e, d, pointerLockedElement);
12068 0 : return NS_OK;
12069 : }
12070 : }
12071 : // If it is neither user input initiated, nor requested in fullscreen,
12072 : // it should be rejected.
12073 0 : if (!error && !mUserInputOrChromeCaller && !doc->GetFullscreenElement()) {
12074 0 : error = "PointerLockDeniedNotInputDriven";
12075 : }
12076 0 : if (!error && !d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
12077 0 : error = "PointerLockDeniedFailedToLock";
12078 : }
12079 0 : if (error) {
12080 0 : DispatchPointerLockError(d, error);
12081 0 : return NS_OK;
12082 : }
12083 :
12084 0 : ChangePointerLockedElement(e, d, nullptr);
12085 0 : nsContentUtils::DispatchEventOnlyToChrome(
12086 0 : doc, ToSupports(e), NS_LITERAL_STRING("MozDOMPointerLock:Entered"),
12087 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
12088 0 : return NS_OK;
12089 : }
12090 :
12091 : void
12092 0 : nsDocument::RequestPointerLock(Element* aElement, CallerType aCallerType)
12093 : {
12094 0 : NS_ASSERTION(aElement,
12095 : "Must pass non-null element to nsDocument::RequestPointerLock");
12096 :
12097 : nsCOMPtr<Element> pointerLockedElement =
12098 0 : do_QueryReferent(EventStateManager::sPointerLockedElement);
12099 0 : if (aElement == pointerLockedElement) {
12100 0 : DispatchPointerLockChange(this);
12101 0 : return;
12102 : }
12103 :
12104 0 : if (const char* msg = GetPointerLockError(aElement, pointerLockedElement)) {
12105 0 : DispatchPointerLockError(this, msg);
12106 0 : return;
12107 : }
12108 :
12109 0 : bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() ||
12110 0 : aCallerType == CallerType::System;
12111 : nsCOMPtr<nsIRunnable> request =
12112 0 : new PointerLockRequest(aElement, userInputOrSystemCaller);
12113 0 : Dispatch("PointerLockRequest", TaskCategory::Other, request.forget());
12114 : }
12115 :
12116 : bool
12117 0 : nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
12118 : {
12119 0 : MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
12120 : "We should be either unlocking pointer (aElement is nullptr), "
12121 : "or locking pointer to an element in this document");
12122 : #ifdef DEBUG
12123 0 : if (!aElement) {
12124 : nsCOMPtr<nsIDocument> pointerLockedDoc =
12125 0 : do_QueryReferent(EventStateManager::sPointerLockedDoc);
12126 0 : MOZ_ASSERT(pointerLockedDoc == this);
12127 : }
12128 : #endif
12129 :
12130 0 : nsIPresShell* shell = GetShell();
12131 0 : if (!shell) {
12132 0 : NS_WARNING("SetPointerLock(): No PresShell");
12133 0 : if (!aElement) {
12134 : // If we are unlocking pointer lock, but for some reason the doc
12135 : // has already detached from the presshell, just ask the event
12136 : // state manager to release the pointer.
12137 0 : EventStateManager::SetPointerLock(nullptr, nullptr);
12138 0 : return true;
12139 : }
12140 0 : return false;
12141 : }
12142 0 : nsPresContext* presContext = shell->GetPresContext();
12143 0 : if (!presContext) {
12144 0 : NS_WARNING("SetPointerLock(): Unable to get PresContext");
12145 0 : return false;
12146 : }
12147 :
12148 0 : nsCOMPtr<nsIWidget> widget;
12149 0 : nsIFrame* rootFrame = shell->GetRootFrame();
12150 0 : if (!NS_WARN_IF(!rootFrame)) {
12151 0 : widget = rootFrame->GetNearestWidget();
12152 0 : NS_WARNING_ASSERTION(
12153 : widget,
12154 : "SetPointerLock(): Unable to find widget in "
12155 : "shell->GetRootFrame()->GetNearestWidget();");
12156 0 : if (aElement && !widget) {
12157 0 : return false;
12158 : }
12159 : }
12160 :
12161 : // Hide the cursor and set pointer lock for future mouse events
12162 0 : RefPtr<EventStateManager> esm = presContext->EventStateManager();
12163 0 : esm->SetCursor(aCursorStyle, nullptr, false,
12164 0 : 0.0f, 0.0f, widget, true);
12165 0 : EventStateManager::SetPointerLock(widget, aElement);
12166 :
12167 0 : return true;
12168 : }
12169 :
12170 : void
12171 4 : nsDocument::UnlockPointer(nsIDocument* aDoc)
12172 : {
12173 4 : if (!EventStateManager::sIsPointerLocked) {
12174 8 : return;
12175 : }
12176 :
12177 : nsCOMPtr<nsIDocument> pointerLockedDoc =
12178 0 : do_QueryReferent(EventStateManager::sPointerLockedDoc);
12179 0 : if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
12180 0 : return;
12181 : }
12182 0 : nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
12183 0 : if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
12184 0 : return;
12185 : }
12186 :
12187 : nsCOMPtr<Element> pointerLockedElement =
12188 0 : do_QueryReferent(EventStateManager::sPointerLockedElement);
12189 0 : ChangePointerLockedElement(nullptr, doc, pointerLockedElement);
12190 :
12191 0 : nsContentUtils::DispatchEventOnlyToChrome(
12192 0 : doc, ToSupports(pointerLockedElement),
12193 0 : NS_LITERAL_STRING("MozDOMPointerLock:Exited"),
12194 0 : /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
12195 : }
12196 :
12197 : void
12198 4 : nsIDocument::UnlockPointer(nsIDocument* aDoc)
12199 : {
12200 4 : nsDocument::UnlockPointer(aDoc);
12201 4 : }
12202 :
12203 : Element*
12204 0 : nsIDocument::GetPointerLockElement()
12205 : {
12206 : nsCOMPtr<Element> pointerLockedElement =
12207 0 : do_QueryReferent(EventStateManager::sPointerLockedElement);
12208 0 : if (!pointerLockedElement) {
12209 0 : return nullptr;
12210 : }
12211 :
12212 : // Make sure pointer locked element is in the same document.
12213 : nsCOMPtr<nsIDocument> pointerLockedDoc =
12214 0 : do_QueryReferent(EventStateManager::sPointerLockedDoc);
12215 0 : if (pointerLockedDoc != this) {
12216 0 : return nullptr;
12217 : }
12218 :
12219 0 : return pointerLockedElement;
12220 : }
12221 :
12222 : nsresult
12223 0 : nsDocument::Observe(nsISupports *aSubject,
12224 : const char *aTopic,
12225 : const char16_t *aData)
12226 : {
12227 0 : if (strcmp("service-worker-get-client", aTopic) == 0) {
12228 : // No need to generate the ID if it doesn't exist here. The ID being
12229 : // requested must already be generated in order to passed in as
12230 : // aSubject.
12231 0 : nsString clientId = GetId();
12232 0 : if (!clientId.IsEmpty() && clientId.Equals(aData)) {
12233 0 : nsCOMPtr<nsISupportsInterfacePointer> ifptr = do_QueryInterface(aSubject);
12234 0 : if (ifptr) {
12235 : #ifdef DEBUG
12236 0 : nsCOMPtr<nsISupports> value;
12237 0 : MOZ_ALWAYS_SUCCEEDS(ifptr->GetData(getter_AddRefs(value)));
12238 0 : MOZ_ASSERT(!value);
12239 : #endif
12240 0 : ifptr->SetData(static_cast<nsIDocument*>(this));
12241 0 : ifptr->SetDataIID(&NS_GET_IID(nsIDocument));
12242 : }
12243 : }
12244 : }
12245 0 : return NS_OK;
12246 : }
12247 :
12248 : void
12249 9 : nsDocument::UpdateVisibilityState()
12250 : {
12251 9 : dom::VisibilityState oldState = mVisibilityState;
12252 9 : mVisibilityState = GetVisibilityState();
12253 9 : if (oldState != mVisibilityState) {
12254 4 : nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
12255 8 : NS_LITERAL_STRING("visibilitychange"),
12256 : /* bubbles = */ true,
12257 8 : /* cancelable = */ false);
12258 4 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
12259 : }
12260 :
12261 9 : if (mVisibilityState == dom::VisibilityState::Visible) {
12262 5 : MaybeActiveMediaComponents();
12263 : }
12264 9 : }
12265 :
12266 : VisibilityState
12267 25 : nsDocument::GetVisibilityState() const
12268 : {
12269 : // We have to check a few pieces of information here:
12270 : // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
12271 : // 2) Do we have an outer window? If not, we're hidden. Note that we don't
12272 : // want to use GetWindow here because it does weird groveling for windows
12273 : // in some cases.
12274 : // 3) Is our outer window background? If so, we're hidden.
12275 : // Otherwise, we're visible.
12276 38 : if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
12277 13 : mWindow->GetOuterWindow()->IsBackground()) {
12278 :
12279 : // Check if the document is in prerender state.
12280 24 : nsCOMPtr<nsIDocShell> docshell = GetDocShell();
12281 12 : if (docshell && docshell->GetIsPrerendered()) {
12282 0 : return dom::VisibilityState::Prerender;
12283 : }
12284 :
12285 12 : return dom::VisibilityState::Hidden;
12286 : }
12287 :
12288 13 : return dom::VisibilityState::Visible;
12289 : }
12290 :
12291 : /* virtual */ void
12292 0 : nsDocument::PostVisibilityUpdateEvent()
12293 : {
12294 : nsCOMPtr<nsIRunnable> event =
12295 0 : NewRunnableMethod("nsDocument::UpdateVisibilityState",
12296 : this,
12297 0 : &nsDocument::UpdateVisibilityState);
12298 0 : Dispatch("nsDocument::UpdateVisibilityState", TaskCategory::Other, event.forget());
12299 0 : }
12300 :
12301 : void
12302 5 : nsDocument::MaybeActiveMediaComponents()
12303 : {
12304 5 : if (!mWindow) {
12305 0 : return;
12306 : }
12307 :
12308 5 : GetWindow()->MaybeActiveMediaComponents();
12309 : }
12310 :
12311 : NS_IMETHODIMP
12312 0 : nsDocument::GetHidden(bool* aHidden)
12313 : {
12314 0 : *aHidden = Hidden();
12315 0 : return NS_OK;
12316 : }
12317 :
12318 : NS_IMETHODIMP
12319 0 : nsDocument::GetVisibilityState(nsAString& aState)
12320 : {
12321 : const EnumEntry& entry =
12322 0 : VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
12323 0 : aState.AssignASCII(entry.value, entry.length);
12324 0 : return NS_OK;
12325 : }
12326 :
12327 : /* virtual */ void
12328 21 : nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
12329 : {
12330 21 : aWindowSizes->mDOMOtherSize +=
12331 21 : nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12332 :
12333 21 : if (mPresShell) {
12334 42 : mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
12335 : &aWindowSizes->mArenaStats,
12336 : &aWindowSizes->mLayoutPresShellSize,
12337 : &aWindowSizes->mLayoutStyleSetsSize,
12338 : &aWindowSizes->mLayoutTextRunsSize,
12339 : &aWindowSizes->mLayoutPresContextSize,
12340 42 : &aWindowSizes->mLayoutFramePropertiesSize);
12341 : }
12342 :
12343 21 : aWindowSizes->mPropertyTablesSize +=
12344 21 : mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12345 21 : for (uint32_t i = 0, count = mExtraPropertyTables.Length();
12346 21 : i < count; ++i) {
12347 0 : aWindowSizes->mPropertyTablesSize +=
12348 0 : mExtraPropertyTables[i]->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12349 : }
12350 :
12351 21 : if (EventListenerManager* elm = GetExistingListenerManager()) {
12352 21 : aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
12353 : }
12354 :
12355 : // Measurement of the following members may be added later if DMD finds it
12356 : // is worthwhile:
12357 : // - many!
12358 21 : }
12359 :
12360 : void
12361 21 : nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
12362 : {
12363 21 : aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
12364 21 : DocAddSizeOfExcludingThis(aWindowSizes);
12365 21 : }
12366 :
12367 : static size_t
12368 84 : SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets,
12369 : MallocSizeOf aMallocSizeOf)
12370 : {
12371 84 : size_t n = 0;
12372 84 : n += aSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
12373 89 : for (StyleSheet* sheet : aSheets) {
12374 5 : if (!sheet->GetAssociatedDocument()) {
12375 : // Avoid over-reporting shared sheets.
12376 0 : continue;
12377 : }
12378 5 : n += sheet->SizeOfIncludingThis(aMallocSizeOf);
12379 : }
12380 84 : return n;
12381 : }
12382 :
12383 : size_t
12384 0 : nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
12385 : {
12386 : // This SizeOfExcludingThis() overrides the one from nsINode. But
12387 : // nsDocuments can only appear at the top of the DOM tree, and we use the
12388 : // specialized DocAddSizeOfExcludingThis() in that case. So this should never
12389 : // be called.
12390 0 : MOZ_CRASH();
12391 : }
12392 :
12393 : void
12394 21 : nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
12395 : {
12396 21 : nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
12397 :
12398 335 : for (nsIContent* node = nsINode::GetFirstChild();
12399 335 : node;
12400 314 : node = node->GetNextNode(this))
12401 : {
12402 314 : size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12403 : size_t* p;
12404 :
12405 314 : switch (node->NodeType()) {
12406 : case nsIDOMNode::ELEMENT_NODE:
12407 130 : p = &aWindowSizes->mDOMElementNodesSize;
12408 130 : break;
12409 : case nsIDOMNode::TEXT_NODE:
12410 162 : p = &aWindowSizes->mDOMTextNodesSize;
12411 162 : break;
12412 : case nsIDOMNode::CDATA_SECTION_NODE:
12413 0 : p = &aWindowSizes->mDOMCDATANodesSize;
12414 0 : break;
12415 : case nsIDOMNode::COMMENT_NODE:
12416 22 : p = &aWindowSizes->mDOMCommentNodesSize;
12417 22 : break;
12418 : default:
12419 0 : p = &aWindowSizes->mDOMOtherSize;
12420 0 : break;
12421 : }
12422 :
12423 314 : *p += nodeSize;
12424 :
12425 314 : if (EventListenerManager* elm = node->GetExistingListenerManager()) {
12426 0 : aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
12427 : }
12428 : }
12429 :
12430 21 : aWindowSizes->mStyleSheetsSize +=
12431 21 : SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
12432 21 : aWindowSizes->mMallocSizeOf);
12433 : // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
12434 : // (the nsLayoutStyleSheetCache singleton does).
12435 21 : aWindowSizes->mStyleSheetsSize +=
12436 21 : mOnDemandBuiltInUASheets.ShallowSizeOfExcludingThis(
12437 21 : aWindowSizes->mMallocSizeOf);
12438 84 : for (auto& sheetArray : mAdditionalSheets) {
12439 63 : aWindowSizes->mStyleSheetsSize +=
12440 63 : SizeOfOwnedSheetArrayExcludingThis(sheetArray,
12441 63 : aWindowSizes->mMallocSizeOf);
12442 : }
12443 : // Lumping in the loader with the style-sheets size is not ideal,
12444 : // but most of the things in there are in fact stylesheets, so it
12445 : // doesn't seem worthwhile to separate it out.
12446 21 : aWindowSizes->mStyleSheetsSize +=
12447 21 : CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12448 :
12449 21 : aWindowSizes->mDOMOtherSize +=
12450 42 : mAttrStyleSheet ?
12451 21 : mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
12452 42 : 0;
12453 :
12454 21 : aWindowSizes->mDOMOtherSize +=
12455 21 : mStyledLinks.ShallowSizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12456 :
12457 21 : aWindowSizes->mDOMOtherSize +=
12458 21 : mIdentifierMap.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12459 :
12460 : // Measurement of the following members may be added later if DMD finds it
12461 : // is worthwhile:
12462 : // - many!
12463 21 : }
12464 :
12465 : already_AddRefed<nsIDocument>
12466 0 : nsIDocument::Constructor(const GlobalObject& aGlobal,
12467 : ErrorResult& rv)
12468 : {
12469 0 : nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
12470 0 : if (!global) {
12471 0 : rv.Throw(NS_ERROR_UNEXPECTED);
12472 0 : return nullptr;
12473 : }
12474 :
12475 0 : nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
12476 0 : if (!prin) {
12477 0 : rv.Throw(NS_ERROR_UNEXPECTED);
12478 0 : return nullptr;
12479 : }
12480 :
12481 0 : nsCOMPtr<nsIURI> uri;
12482 0 : NS_NewURI(getter_AddRefs(uri), "about:blank");
12483 0 : if (!uri) {
12484 0 : rv.Throw(NS_ERROR_OUT_OF_MEMORY);
12485 0 : return nullptr;
12486 : }
12487 :
12488 0 : nsCOMPtr<nsIDOMDocument> document;
12489 : nsresult res =
12490 0 : NS_NewDOMDocument(getter_AddRefs(document),
12491 0 : NullString(),
12492 0 : EmptyString(),
12493 : nullptr,
12494 : uri,
12495 : uri,
12496 0 : prin->GetPrincipal(),
12497 : true,
12498 : global,
12499 0 : DocumentFlavorPlain);
12500 0 : if (NS_FAILED(res)) {
12501 0 : rv.Throw(res);
12502 0 : return nullptr;
12503 : }
12504 :
12505 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
12506 0 : doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
12507 :
12508 0 : return doc.forget();
12509 : }
12510 :
12511 : XPathExpression*
12512 0 : nsIDocument::CreateExpression(const nsAString& aExpression,
12513 : XPathNSResolver* aResolver,
12514 : ErrorResult& rv)
12515 : {
12516 0 : return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
12517 : }
12518 :
12519 : nsINode*
12520 0 : nsIDocument::CreateNSResolver(nsINode& aNodeResolver)
12521 : {
12522 0 : return XPathEvaluator()->CreateNSResolver(aNodeResolver);
12523 : }
12524 :
12525 : already_AddRefed<XPathResult>
12526 0 : nsIDocument::Evaluate(JSContext* aCx, const nsAString& aExpression,
12527 : nsINode& aContextNode, XPathNSResolver* aResolver,
12528 : uint16_t aType, JS::Handle<JSObject*> aResult,
12529 : ErrorResult& rv)
12530 : {
12531 : return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
12532 0 : aType, aResult, rv);
12533 : }
12534 :
12535 : NS_IMETHODIMP
12536 0 : nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode,
12537 : nsIDOMNode* aResolver, uint16_t aType,
12538 : nsISupports* aInResult, nsISupports** aResult)
12539 : {
12540 0 : return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
12541 0 : aInResult, aResult);
12542 : }
12543 :
12544 : nsIDocument*
12545 27 : nsIDocument::GetTopLevelContentDocument()
12546 : {
12547 : nsIDocument* parent;
12548 :
12549 27 : if (!mLoadedAsData) {
12550 27 : parent = this;
12551 : } else {
12552 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
12553 0 : if (!window) {
12554 0 : return nullptr;
12555 : }
12556 :
12557 0 : parent = window->GetExtantDoc();
12558 0 : if (!parent) {
12559 0 : return nullptr;
12560 : }
12561 : }
12562 :
12563 0 : do {
12564 27 : if (parent->IsTopLevelContentDocument()) {
12565 0 : break;
12566 : }
12567 :
12568 : // If we ever have a non-content parent before we hit a toplevel content
12569 : // parent, then we're never going to find one. Just bail.
12570 27 : if (!parent->IsContentDocument()) {
12571 27 : return nullptr;
12572 : }
12573 :
12574 0 : nsIDocument* candidate = parent->GetParentDocument();
12575 0 : parent = static_cast<nsDocument*>(candidate);
12576 0 : } while (parent);
12577 :
12578 0 : return parent;
12579 : }
12580 :
12581 : void
12582 25 : nsIDocument::PropagateUseCounters(nsIDocument* aParentDocument)
12583 : {
12584 25 : MOZ_ASSERT(this != aParentDocument);
12585 :
12586 : // What really matters here is that our use counters get propagated as
12587 : // high up in the content document hierarchy as possible. So,
12588 : // starting with aParentDocument, we need to find the toplevel content
12589 : // document, and propagate our use counters into its
12590 : // mChildDocumentUseCounters.
12591 25 : nsIDocument* contentParent = aParentDocument->GetTopLevelContentDocument();
12592 :
12593 25 : if (!contentParent) {
12594 25 : return;
12595 : }
12596 :
12597 0 : contentParent->mChildDocumentUseCounters |= mUseCounters;
12598 0 : contentParent->mChildDocumentUseCounters |= mChildDocumentUseCounters;
12599 : }
12600 :
12601 : void
12602 115 : nsIDocument::SetPageUseCounter(UseCounter aUseCounter)
12603 : {
12604 : // We want to set the use counter on the "page" that owns us; the definition
12605 : // of "page" depends on what kind of document we are. See the comments below
12606 : // for details. In any event, checking all the conditions below is
12607 : // reasonably expensive, so we cache whether we've notified our owning page.
12608 115 : if (mNotifiedPageForUseCounter[aUseCounter]) {
12609 89 : return;
12610 : }
12611 26 : mNotifiedPageForUseCounter[aUseCounter] = true;
12612 :
12613 26 : if (mDisplayDocument) {
12614 : // If we are a resource document, we won't have a docshell and so we won't
12615 : // record any page use counters on this document. Instead, we should
12616 : // forward it up to the document that loaded us.
12617 0 : MOZ_ASSERT(!mDocumentContainer);
12618 0 : mDisplayDocument->SetChildDocumentUseCounter(aUseCounter);
12619 0 : return;
12620 : }
12621 :
12622 26 : if (IsBeingUsedAsImage()) {
12623 : // If this is an SVG image document, we also won't have a docshell.
12624 24 : MOZ_ASSERT(!mDocumentContainer);
12625 24 : return;
12626 : }
12627 :
12628 : // We only care about use counters in content. If we're already a toplevel
12629 : // content document, then we should have already set the use counter on
12630 : // ourselves, and we are done.
12631 2 : nsIDocument* contentParent = GetTopLevelContentDocument();
12632 2 : if (!contentParent) {
12633 2 : return;
12634 : }
12635 :
12636 0 : if (this == contentParent) {
12637 0 : MOZ_ASSERT(GetUseCounter(aUseCounter));
12638 0 : return;
12639 : }
12640 :
12641 0 : contentParent->SetChildDocumentUseCounter(aUseCounter);
12642 : }
12643 :
12644 : bool
12645 577 : nsIDocument::HasScriptsBlockedBySandbox()
12646 : {
12647 577 : return mSandboxFlags & SANDBOXED_SCRIPTS;
12648 : }
12649 :
12650 : bool
12651 0 : nsIDocument::InlineScriptAllowedByCSP()
12652 : {
12653 : // this function assumes the inline script is parser created
12654 : // (e.g., before setting attribute(!) event handlers)
12655 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
12656 0 : nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
12657 0 : NS_ENSURE_SUCCESS(rv, true);
12658 0 : bool allowsInlineScript = true;
12659 0 : if (csp) {
12660 0 : nsresult rv = csp->GetAllowsInline(nsIContentPolicy::TYPE_SCRIPT,
12661 0 : EmptyString(), // aNonce
12662 : true, // aParserCreated
12663 0 : EmptyString(), // FIXME get script sample (bug 1314567)
12664 : 0, // aLineNumber
12665 0 : &allowsInlineScript);
12666 0 : NS_ENSURE_SUCCESS(rv, true);
12667 : }
12668 0 : return allowsInlineScript;
12669 : }
12670 :
12671 : static bool
12672 0 : MightBeAboutOrChromeScheme(nsIURI* aURI)
12673 : {
12674 0 : MOZ_ASSERT(aURI);
12675 0 : bool isAbout = true;
12676 0 : bool isChrome = true;
12677 0 : aURI->SchemeIs("about", &isAbout);
12678 0 : aURI->SchemeIs("chrome", &isChrome);
12679 0 : return isAbout || isChrome;
12680 : }
12681 :
12682 : static bool
12683 0 : ReportExternalResourceUseCounters(nsIDocument* aDocument, void* aData)
12684 : {
12685 : const auto reportKind
12686 0 : = nsDocument::UseCounterReportKind::eIncludeExternalResources;
12687 0 : static_cast<nsDocument*>(aDocument)->ReportUseCounters(reportKind);
12688 0 : return true;
12689 : }
12690 :
12691 : void
12692 0 : nsDocument::ReportUseCounters(UseCounterReportKind aKind)
12693 : {
12694 : static const bool sDebugUseCounters = false;
12695 0 : if (mReportedUseCounters) {
12696 0 : return;
12697 : }
12698 :
12699 0 : mReportedUseCounters = true;
12700 :
12701 0 : if (aKind == UseCounterReportKind::eIncludeExternalResources) {
12702 0 : EnumerateExternalResources(ReportExternalResourceUseCounters, nullptr);
12703 : }
12704 :
12705 0 : if (Telemetry::HistogramUseCounterCount > 0 &&
12706 0 : (IsContentDocument() || IsResourceDoc())) {
12707 0 : nsCOMPtr<nsIURI> uri;
12708 0 : NodePrincipal()->GetURI(getter_AddRefs(uri));
12709 0 : if (!uri || MightBeAboutOrChromeScheme(uri)) {
12710 0 : return;
12711 : }
12712 :
12713 : if (sDebugUseCounters) {
12714 : nsCString spec = uri->GetSpecOrDefault();
12715 :
12716 : // URIs can be rather long for data documents, so truncate them to
12717 : // some reasonable length.
12718 : spec.Truncate(std::min(128U, spec.Length()));
12719 : printf("-- Use counters for %s --\n", spec.get());
12720 : }
12721 :
12722 : // We keep separate counts for individual documents and top-level
12723 : // pages to more accurately track how many web pages might break if
12724 : // certain features were removed. Consider the case of a single
12725 : // HTML document with several SVG images and/or iframes with
12726 : // sub-documents of their own. If we maintained a single set of use
12727 : // counters and all the sub-documents use a particular feature, then
12728 : // telemetry would indicate that we would be breaking N documents if
12729 : // that feature were removed. Whereas with a document/top-level
12730 : // page split, we can see that N documents would be affected, but
12731 : // only a single web page would be affected.
12732 :
12733 : // The difference between the values of these two histograms and the
12734 : // related use counters below tell us how many pages did *not* use
12735 : // the feature in question. For instance, if we see that a given
12736 : // session has destroyed 30 content documents, but a particular use
12737 : // counter shows only a count of 5, we can infer that the use
12738 : // counter was *not* used in 25 of those 30 documents.
12739 : //
12740 : // We do things this way, rather than accumulating a boolean flag
12741 : // for each use counter, to avoid sending histograms for features
12742 : // that don't get widely used. Doing things in this fashion means
12743 : // smaller telemetry payloads and faster processing on the server
12744 : // side.
12745 0 : Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1);
12746 0 : if (IsTopLevelContentDocument()) {
12747 0 : Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1);
12748 : }
12749 :
12750 0 : for (int32_t c = 0;
12751 0 : c < eUseCounter_Count; ++c) {
12752 0 : UseCounter uc = static_cast<UseCounter>(c);
12753 :
12754 : Telemetry::HistogramID id =
12755 0 : static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter + uc * 2);
12756 0 : bool value = GetUseCounter(uc);
12757 :
12758 0 : if (value) {
12759 : if (sDebugUseCounters) {
12760 : const char* name = Telemetry::GetHistogramName(id);
12761 : if (name) {
12762 : printf(" %s", name);
12763 : } else {
12764 : printf(" #%d", id);
12765 : }
12766 : printf(": %d\n", value);
12767 : }
12768 :
12769 0 : Telemetry::Accumulate(id, 1);
12770 : }
12771 :
12772 0 : if (IsTopLevelContentDocument()) {
12773 0 : id = static_cast<Telemetry::HistogramID>(Telemetry::HistogramFirstUseCounter +
12774 0 : uc * 2 + 1);
12775 0 : value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
12776 :
12777 0 : if (value) {
12778 : if (sDebugUseCounters) {
12779 : const char* name = Telemetry::GetHistogramName(id);
12780 : if (name) {
12781 : printf(" %s", name);
12782 : } else {
12783 : printf(" #%d", id);
12784 : }
12785 : printf(": %d\n", value);
12786 : }
12787 :
12788 0 : Telemetry::Accumulate(id, 1);
12789 : }
12790 : }
12791 : }
12792 : }
12793 :
12794 0 : if (IsContentDocument() || IsResourceDoc()) {
12795 0 : uint16_t num = mIncCounters[eIncCounter_ScriptTag];
12796 0 : Telemetry::Accumulate(Telemetry::DOM_SCRIPT_EVAL_PER_DOCUMENT, num);
12797 : }
12798 : }
12799 :
12800 : void
12801 0 : nsDocument::AddIntersectionObserver(DOMIntersectionObserver* aObserver)
12802 : {
12803 0 : MOZ_ASSERT(!mIntersectionObservers.Contains(aObserver),
12804 : "Intersection observer already in the list");
12805 0 : mIntersectionObservers.PutEntry(aObserver);
12806 0 : }
12807 :
12808 : void
12809 0 : nsDocument::RemoveIntersectionObserver(DOMIntersectionObserver* aObserver)
12810 : {
12811 0 : mIntersectionObservers.RemoveEntry(aObserver);
12812 0 : }
12813 :
12814 : void
12815 74 : nsDocument::UpdateIntersectionObservations()
12816 : {
12817 74 : if (mIntersectionObservers.IsEmpty()) {
12818 74 : return;
12819 : }
12820 :
12821 0 : DOMHighResTimeStamp time = 0;
12822 0 : if (nsPIDOMWindowInner* window = GetInnerWindow()) {
12823 0 : Performance* perf = window->GetPerformance();
12824 0 : if (perf) {
12825 0 : time = perf->Now();
12826 : }
12827 : }
12828 0 : for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
12829 0 : DOMIntersectionObserver* observer = iter.Get()->GetKey();
12830 0 : observer->Update(this, time);
12831 : }
12832 : }
12833 :
12834 : void
12835 74 : nsDocument::ScheduleIntersectionObserverNotification()
12836 : {
12837 74 : if (mIntersectionObservers.IsEmpty()) {
12838 74 : return;
12839 : }
12840 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
12841 : nsCOMPtr<nsIRunnable> notification =
12842 0 : NewRunnableMethod("nsDocument::NotifyIntersectionObservers",
12843 : this,
12844 0 : &nsDocument::NotifyIntersectionObservers);
12845 0 : Dispatch("nsDocument::IntersectionObserverNotification", TaskCategory::Other,
12846 0 : notification.forget());
12847 : }
12848 :
12849 : void
12850 0 : nsDocument::NotifyIntersectionObservers()
12851 : {
12852 0 : nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count());
12853 0 : for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
12854 0 : DOMIntersectionObserver* observer = iter.Get()->GetKey();
12855 0 : observers.AppendElement(observer);
12856 : }
12857 0 : for (const auto& observer : observers) {
12858 0 : observer->Notify();
12859 : }
12860 0 : }
12861 :
12862 : static bool
12863 0 : NotifyLayerManagerRecreatedCallback(nsIDocument* aDocument, void* aData)
12864 : {
12865 0 : aDocument->NotifyLayerManagerRecreated();
12866 0 : return true;
12867 : }
12868 :
12869 : void
12870 0 : nsDocument::NotifyLayerManagerRecreated()
12871 : {
12872 0 : EnumerateActivityObservers(NotifyActivityChanged, nullptr);
12873 0 : EnumerateSubDocuments(NotifyLayerManagerRecreatedCallback, nullptr);
12874 0 : }
12875 :
12876 : XPathEvaluator*
12877 0 : nsIDocument::XPathEvaluator()
12878 : {
12879 0 : if (!mXPathEvaluator) {
12880 0 : mXPathEvaluator = new dom::XPathEvaluator(this);
12881 : }
12882 0 : return mXPathEvaluator;
12883 : }
12884 :
12885 : already_AddRefed<nsIDocumentEncoder>
12886 0 : nsIDocument::GetCachedEncoder()
12887 : {
12888 0 : return mCachedEncoder.forget();
12889 : }
12890 :
12891 : void
12892 0 : nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
12893 : {
12894 0 : mCachedEncoder = aEncoder;
12895 0 : }
12896 :
12897 : void
12898 116 : nsIDocument::SetContentTypeInternal(const nsACString& aType)
12899 : {
12900 215 : if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
12901 99 : aType.EqualsLiteral("application/xhtml+xml")) {
12902 0 : mDefaultElementType = kNameSpaceID_XHTML;
12903 : }
12904 :
12905 116 : mCachedEncoder = nullptr;
12906 116 : mContentType = aType;
12907 116 : }
12908 :
12909 : nsILoadContext*
12910 2075 : nsIDocument::GetLoadContext() const
12911 : {
12912 2075 : return mDocumentContainer;
12913 : }
12914 :
12915 : nsIDocShell*
12916 291 : nsIDocument::GetDocShell() const
12917 : {
12918 291 : return mDocumentContainer;
12919 : }
12920 :
12921 : void
12922 1 : nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
12923 : {
12924 1 : mStateObjectContainer = scContainer;
12925 1 : mStateObjectCached = nullptr;
12926 1 : }
12927 :
12928 : already_AddRefed<Element>
12929 23 : nsIDocument::CreateHTMLElement(nsIAtom* aTag)
12930 : {
12931 46 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
12932 46 : nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
12933 23 : nsIDOMNode::ELEMENT_NODE);
12934 23 : MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
12935 :
12936 46 : nsCOMPtr<Element> element;
12937 46 : DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
12938 46 : nodeInfo.forget(),
12939 69 : mozilla::dom::NOT_FROM_PARSER);
12940 :
12941 23 : MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
12942 46 : return element.forget();
12943 : }
12944 :
12945 : /* static */
12946 : nsresult
12947 0 : nsIDocument::GenerateDocumentId(nsAString& aId)
12948 : {
12949 : nsID id;
12950 0 : nsresult rv = nsContentUtils::GenerateUUIDInPlace(id);
12951 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12952 0 : return rv;
12953 : }
12954 :
12955 : // Build a string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format
12956 : char buffer[NSID_LENGTH];
12957 0 : id.ToProvidedString(buffer);
12958 0 : NS_ConvertASCIItoUTF16 uuid(buffer);
12959 :
12960 : // Remove {} and the null terminator
12961 0 : aId.Assign(Substring(uuid, 1, NSID_LENGTH - 3));
12962 0 : return NS_OK;
12963 : }
12964 :
12965 : nsresult
12966 0 : nsIDocument::GetOrCreateId(nsAString& aId)
12967 : {
12968 0 : if (mId.IsEmpty()) {
12969 0 : nsresult rv = GenerateDocumentId(mId);
12970 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
12971 0 : return rv;
12972 : }
12973 : }
12974 :
12975 0 : aId = mId;
12976 0 : return NS_OK;
12977 : }
12978 :
12979 : void
12980 0 : nsIDocument::SetId(const nsAString& aId)
12981 : {
12982 : // The ID should only be set one time, but we may get the same value
12983 : // more than once if the document is controlled coming out of bfcache.
12984 0 : MOZ_ASSERT_IF(mId != aId, mId.IsEmpty());
12985 0 : mId = aId;
12986 0 : }
12987 :
12988 : bool
12989 0 : MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
12990 : {
12991 : nsCOMArray<nsIDocument>* documents =
12992 0 : static_cast<nsCOMArray<nsIDocument>*>(aData);
12993 0 : if (aDoc) {
12994 0 : aDoc->SetIsInSyncOperation(true);
12995 0 : if (nsCOMPtr<nsPIDOMWindowInner> window = aDoc->GetInnerWindow()) {
12996 0 : window->TimeoutManager().BeginSyncOperation();
12997 : }
12998 0 : documents->AppendObject(aDoc);
12999 0 : aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
13000 : }
13001 0 : return true;
13002 : }
13003 :
13004 0 : nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
13005 : {
13006 0 : mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
13007 0 : nsContentUtils::SetMicroTaskLevel(0);
13008 0 : if (aDoc) {
13009 0 : if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
13010 0 : if (nsCOMPtr<nsPIDOMWindowOuter> top = win->GetTop()) {
13011 0 : nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
13012 0 : MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
13013 : }
13014 : }
13015 : }
13016 0 : }
13017 :
13018 0 : nsAutoSyncOperation::~nsAutoSyncOperation()
13019 : {
13020 0 : for (int32_t i = 0; i < mDocuments.Count(); ++i) {
13021 0 : if (nsCOMPtr<nsPIDOMWindowInner> window = mDocuments[i]->GetInnerWindow()) {
13022 0 : window->TimeoutManager().EndSyncOperation();
13023 : }
13024 0 : mDocuments[i]->SetIsInSyncOperation(false);
13025 : }
13026 0 : nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
13027 0 : }
13028 :
13029 : gfxUserFontSet*
13030 423 : nsIDocument::GetUserFontSet(bool aFlushUserFontSet)
13031 : {
13032 : // We want to initialize the user font set lazily the first time the
13033 : // user asks for it, rather than building it too early and forcing
13034 : // rule cascade creation. Thus we try to enforce the invariant that
13035 : // we *never* build the user font set until the first call to
13036 : // GetUserFontSet. However, once it's been requested, we can't wait
13037 : // for somebody to call GetUserFontSet in order to rebuild it (see
13038 : // comments below in RebuildUserFontSet for why).
13039 : #ifdef DEBUG
13040 423 : bool userFontSetGottenBefore = mGetUserFontSetCalled;
13041 : #endif
13042 : // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
13043 : // flush.
13044 423 : mGetUserFontSetCalled = true;
13045 423 : if (mFontFaceSetDirty && aFlushUserFontSet) {
13046 : // If this assertion fails, and there have actually been changes to
13047 : // @font-face rules, then we will call StyleChangeReflow in
13048 : // FlushUserFontSet. If we're in the middle of reflow,
13049 : // that's a bad thing to do, and the caller was responsible for
13050 : // flushing first. If we're not (e.g., in frame construction), it's
13051 : // ok.
13052 8 : NS_ASSERTION(!userFontSetGottenBefore ||
13053 : !GetShell() ||
13054 : !GetShell()->IsReflowLocked(),
13055 : "FlushUserFontSet should have been called first");
13056 8 : FlushUserFontSet();
13057 : }
13058 :
13059 423 : if (!mFontFaceSet) {
13060 423 : return nullptr;
13061 : }
13062 :
13063 0 : return mFontFaceSet->GetUserFontSet();
13064 : }
13065 :
13066 : void
13067 204 : nsIDocument::FlushUserFontSet()
13068 : {
13069 204 : if (!mGetUserFontSetCalled) {
13070 79 : return; // No one cares about this font set yet, but we want to be careful
13071 : // to not unset our mFontFaceSetDirty bit, so when someone really
13072 : // does we'll create it.
13073 : }
13074 :
13075 125 : if (mFontFaceSetDirty) {
13076 13 : if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
13077 26 : nsTArray<nsFontFaceRuleContainer> rules;
13078 13 : nsIPresShell* shell = GetShell();
13079 13 : if (shell && !shell->StyleSet()->AppendFontFaceRules(rules)) {
13080 0 : return;
13081 : }
13082 :
13083 13 : bool changed = false;
13084 :
13085 13 : if (!mFontFaceSet && !rules.IsEmpty()) {
13086 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
13087 0 : mFontFaceSet = new FontFaceSet(window, this);
13088 : }
13089 13 : if (mFontFaceSet) {
13090 0 : changed = mFontFaceSet->UpdateRules(rules);
13091 : }
13092 :
13093 : // We need to enqueue a style change reflow (for later) to
13094 : // reflect that we're modifying @font-face rules. (However,
13095 : // without a reflow, nothing will happen to start any downloads
13096 : // that are needed.)
13097 13 : if (changed && shell) {
13098 0 : nsPresContext* presContext = shell->GetPresContext();
13099 0 : if (presContext) {
13100 0 : presContext->UserFontSetUpdated();
13101 : }
13102 : }
13103 : }
13104 :
13105 13 : mFontFaceSetDirty = false;
13106 : }
13107 : }
13108 :
13109 : void
13110 89 : nsIDocument::RebuildUserFontSet()
13111 : {
13112 89 : if (!mGetUserFontSetCalled) {
13113 : // We want to lazily build the user font set the first time it's
13114 : // requested (so we don't force creation of rule cascades too
13115 : // early), so don't do anything now.
13116 82 : return;
13117 : }
13118 :
13119 7 : mFontFaceSetDirty = true;
13120 7 : if (nsIPresShell* shell = GetShell()) {
13121 7 : shell->SetNeedStyleFlush();
13122 : }
13123 :
13124 : // Somebody has already asked for the user font set, so we need to
13125 : // post an event to rebuild it. Setting the user font set to be dirty
13126 : // and lazily rebuilding it isn't sufficient, since it is only the act
13127 : // of rebuilding it that will trigger the style change reflow that
13128 : // calls GetUserFontSet. (This reflow causes rebuilding of text runs,
13129 : // which starts font loads, whose completion causes another style
13130 : // change reflow).
13131 7 : if (!mPostedFlushUserFontSet) {
13132 6 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
13133 : nsCOMPtr<nsIRunnable> ev =
13134 12 : NewRunnableMethod("nsIDocument::HandleRebuildUserFontSet",
13135 : this,
13136 12 : &nsIDocument::HandleRebuildUserFontSet);
13137 6 : if (NS_SUCCEEDED(Dispatch("nsIDocument::HandleRebuildUserFontSet",
13138 : TaskCategory::Other, ev.forget()))) {
13139 6 : mPostedFlushUserFontSet = true;
13140 : }
13141 : }
13142 : }
13143 :
13144 : FontFaceSet*
13145 0 : nsIDocument::Fonts()
13146 : {
13147 0 : if (!mFontFaceSet) {
13148 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
13149 0 : mFontFaceSet = new FontFaceSet(window, this);
13150 0 : GetUserFontSet(); // this will cause the user font set to be created/updated
13151 : }
13152 0 : return mFontFaceSet;
13153 : }
13154 :
13155 : void
13156 0 : nsIDocument::ReportHasScrollLinkedEffect()
13157 : {
13158 0 : if (mHasScrollLinkedEffect) {
13159 : // We already did this once for this document, don't do it again.
13160 0 : return;
13161 : }
13162 0 : mHasScrollLinkedEffect = true;
13163 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
13164 0 : NS_LITERAL_CSTRING("Async Pan/Zoom"),
13165 : this, nsContentUtils::eLAYOUT_PROPERTIES,
13166 0 : "ScrollLinkedEffectFound2");
13167 : }
13168 :
13169 : void
13170 28 : nsIDocument::UpdateStyleBackendType()
13171 : {
13172 28 : MOZ_ASSERT(mStyleBackendType == StyleBackendType::None,
13173 : "no need to call UpdateStyleBackendType now");
13174 :
13175 : // Assume Gecko by default.
13176 28 : mStyleBackendType = StyleBackendType::Gecko;
13177 :
13178 : #ifdef MOZ_STYLO
13179 : if (nsLayoutUtils::StyloEnabled()) {
13180 : if (!mDocumentContainer) {
13181 : NS_WARNING("stylo: No docshell yet, assuming Gecko style system");
13182 : } else if ((IsHTMLOrXHTML() || IsSVGDocument()) &&
13183 : IsContentDocument()) {
13184 : // Disable stylo for about: pages other than about:blank, since
13185 : // they tend to use unsupported selectors like XUL tree pseudos.
13186 : bool isAbout = false;
13187 : mDocumentURI->SchemeIs("about", &isAbout);
13188 : if (!isAbout || NS_IsAboutBlank(mDocumentURI)) {
13189 : mStyleBackendType = StyleBackendType::Servo;
13190 : }
13191 : }
13192 : }
13193 : #endif
13194 28 : }
13195 :
13196 : /**
13197 : * Helper function for |nsDocument::PrincipalFlashClassification|
13198 : *
13199 : * Adds a table name string to a table list (a comma separated string). The
13200 : * table will not be added if the name is an empty string.
13201 : */
13202 : static void
13203 0 : MaybeAddTableToTableList(const nsACString& aTableNames,
13204 : nsACString& aTableList)
13205 : {
13206 0 : if (aTableNames.IsEmpty()) {
13207 0 : return;
13208 : }
13209 0 : if (!aTableList.IsEmpty()) {
13210 0 : aTableList.AppendLiteral(",");
13211 : }
13212 0 : aTableList.Append(aTableNames);
13213 : }
13214 :
13215 : /**
13216 : * Helper function for |nsDocument::PrincipalFlashClassification|
13217 : *
13218 : * Takes an array of table names and a comma separated list of table names
13219 : * Returns |true| if any table name in the array matches a table name in the
13220 : * comma separated list.
13221 : */
13222 : static bool
13223 0 : ArrayContainsTable(const nsTArray<nsCString>& aTableArray,
13224 : const nsACString& aTableNames)
13225 : {
13226 0 : for (const nsCString& table : aTableArray) {
13227 : // This check is sufficient because table names cannot contain commas and
13228 : // cannot contain another existing table name.
13229 0 : if (FindInReadable(table, aTableNames)) {
13230 0 : return true;
13231 : }
13232 : }
13233 0 : return false;
13234 : }
13235 :
13236 : /**
13237 : * Retrieves the classification of the Flash plugins in the document based on
13238 : * the classification lists.
13239 : *
13240 : * For more information, see
13241 : * toolkit/components/url-classifier/flash-block-lists.rst
13242 : */
13243 : FlashClassification
13244 0 : nsDocument::PrincipalFlashClassification()
13245 : {
13246 : nsresult rv;
13247 :
13248 0 : bool httpOnly = Preferences::GetBool("plugins.http_https_only", true);
13249 0 : bool flashBlock = Preferences::GetBool("plugins.flashBlock.enabled", false);
13250 :
13251 : // If neither pref is on, skip the null-principal and principal URI checks.
13252 0 : if (!httpOnly && !flashBlock) {
13253 0 : return FlashClassification::Unknown;
13254 : }
13255 :
13256 0 : nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
13257 0 : if (principal->GetIsNullPrincipal()) {
13258 0 : return FlashClassification::Denied;
13259 : }
13260 :
13261 0 : nsCOMPtr<nsIURI> classificationURI;
13262 0 : rv = principal->GetURI(getter_AddRefs(classificationURI));
13263 0 : if (NS_FAILED(rv) || !classificationURI) {
13264 0 : return FlashClassification::Denied;
13265 : }
13266 :
13267 0 : if (httpOnly) {
13268 : // Only allow plugins for documents from an HTTP/HTTPS origin. This should
13269 : // allow dependent data: URIs to load plugins, but not:
13270 : // * chrome documents
13271 : // * "bare" data: loads
13272 : // * FTP/gopher/file
13273 0 : nsAutoCString scheme;
13274 0 : rv = classificationURI->GetScheme(scheme);
13275 0 : if (NS_WARN_IF(NS_FAILED(rv)) ||
13276 0 : !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) {
13277 0 : return FlashClassification::Denied;
13278 : }
13279 : }
13280 :
13281 : // If flash blocking is disabled, it is equivalent to all sites being
13282 : // on neither list.
13283 0 : if (!flashBlock) {
13284 0 : return FlashClassification::Unknown;
13285 : }
13286 :
13287 0 : nsAutoCString allowTables, allowExceptionsTables,
13288 0 : denyTables, denyExceptionsTables,
13289 0 : subDocDenyTables, subDocDenyExceptionsTables,
13290 0 : tables;
13291 0 : Preferences::GetCString("urlclassifier.flashAllowTable", &allowTables);
13292 0 : MaybeAddTableToTableList(allowTables, tables);
13293 : Preferences::GetCString("urlclassifier.flashAllowExceptTable",
13294 0 : &allowExceptionsTables);
13295 0 : MaybeAddTableToTableList(allowExceptionsTables, tables);
13296 0 : Preferences::GetCString("urlclassifier.flashTable", &denyTables);
13297 0 : MaybeAddTableToTableList(denyTables, tables);
13298 : Preferences::GetCString("urlclassifier.flashExceptTable",
13299 0 : &denyExceptionsTables);
13300 0 : MaybeAddTableToTableList(denyExceptionsTables, tables);
13301 :
13302 0 : bool isThirdPartyDoc = IsThirdParty();
13303 0 : if (isThirdPartyDoc) {
13304 : Preferences::GetCString("urlclassifier.flashSubDocTable",
13305 0 : &subDocDenyTables);
13306 0 : MaybeAddTableToTableList(subDocDenyTables, tables);
13307 : Preferences::GetCString("urlclassifier.flashSubDocExceptTable",
13308 0 : &subDocDenyExceptionsTables);
13309 0 : MaybeAddTableToTableList(subDocDenyExceptionsTables, tables);
13310 : }
13311 :
13312 0 : if (tables.IsEmpty()) {
13313 0 : return FlashClassification::Unknown;
13314 : }
13315 :
13316 : nsCOMPtr<nsIURIClassifier> uriClassifier =
13317 0 : do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
13318 0 : if (NS_FAILED(rv)) {
13319 0 : return FlashClassification::Denied;
13320 : }
13321 :
13322 0 : nsTArray<nsCString> results;
13323 0 : rv = uriClassifier->ClassifyLocalWithTables(classificationURI,
13324 : tables,
13325 0 : results);
13326 0 : if (NS_FAILED(rv)) {
13327 0 : if (rv == NS_ERROR_MALFORMED_URI) {
13328 : // This means that the URI had no hostname (ex: file://doc.html). In this
13329 : // case, we allow the default (Unknown plugin) behavior.
13330 0 : return FlashClassification::Unknown;
13331 : } else {
13332 0 : return FlashClassification::Denied;
13333 : }
13334 : }
13335 :
13336 0 : if (results.IsEmpty()) {
13337 0 : return FlashClassification::Unknown;
13338 : }
13339 :
13340 0 : if (ArrayContainsTable(results, denyTables) &&
13341 0 : !ArrayContainsTable(results, denyExceptionsTables)) {
13342 0 : return FlashClassification::Denied;
13343 0 : } else if (ArrayContainsTable(results, allowTables) &&
13344 0 : !ArrayContainsTable(results, allowExceptionsTables)) {
13345 0 : return FlashClassification::Allowed;
13346 : }
13347 :
13348 0 : if (isThirdPartyDoc && ArrayContainsTable(results, subDocDenyTables) &&
13349 0 : !ArrayContainsTable(results, subDocDenyExceptionsTables)) {
13350 0 : return FlashClassification::Denied;
13351 : }
13352 :
13353 0 : return FlashClassification::Unknown;
13354 : }
13355 :
13356 : FlashClassification
13357 0 : nsDocument::ComputeFlashClassification()
13358 : {
13359 0 : nsCOMPtr<nsIDocShellTreeItem> current = this->GetDocShell();
13360 0 : if (!current) {
13361 0 : return FlashClassification::Denied;
13362 : }
13363 0 : nsCOMPtr<nsIDocShellTreeItem> parent;
13364 0 : DebugOnly<nsresult> rv = current->GetSameTypeParent(getter_AddRefs(parent));
13365 0 : MOZ_ASSERT(NS_SUCCEEDED(rv),
13366 : "nsIDocShellTreeItem::GetSameTypeParent should never fail");
13367 :
13368 0 : bool isTopLevel = !parent;
13369 : FlashClassification classification;
13370 0 : if (isTopLevel) {
13371 0 : classification = PrincipalFlashClassification();
13372 : } else {
13373 0 : nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
13374 0 : if (!parentDocument) {
13375 0 : return FlashClassification::Denied;
13376 : }
13377 : FlashClassification parentClassification =
13378 0 : parentDocument->DocumentFlashClassification();
13379 :
13380 0 : if (parentClassification == FlashClassification::Denied) {
13381 0 : classification = FlashClassification::Denied;
13382 : } else {
13383 0 : classification = PrincipalFlashClassification();
13384 :
13385 : // Allow unknown children to inherit allowed status from parent, but
13386 : // do not allow denied children to do so.
13387 0 : if (classification == FlashClassification::Unknown &&
13388 : parentClassification == FlashClassification::Allowed) {
13389 0 : classification = FlashClassification::Allowed;
13390 : }
13391 : }
13392 : }
13393 :
13394 0 : return classification;
13395 : }
13396 :
13397 : /**
13398 : * Retrieves the classification of plugins in this document. This is dependent
13399 : * on the classification of this document and all parent documents.
13400 : * This function is infallible - It must return some classification that
13401 : * callers can act on.
13402 : *
13403 : * This function will NOT return FlashClassification::Unclassified
13404 : */
13405 : FlashClassification
13406 0 : nsDocument::DocumentFlashClassification()
13407 : {
13408 0 : if (mFlashClassification == FlashClassification::Unclassified) {
13409 0 : FlashClassification result = ComputeFlashClassification();
13410 0 : mFlashClassification = result;
13411 0 : MOZ_ASSERT(result != FlashClassification::Unclassified,
13412 : "nsDocument::GetPluginClassification should never return Unclassified");
13413 : }
13414 :
13415 0 : return mFlashClassification;
13416 : }
13417 :
13418 : /**
13419 : * Initializes |mIsThirdParty| if necessary and returns its value. The value
13420 : * returned represents whether this document should be considered Third-Party.
13421 : *
13422 : * A top-level document cannot be a considered Third-Party; only subdocuments
13423 : * may. For a subdocument to be considered Third-Party, it must meet ANY ONE
13424 : * of the following requirements:
13425 : * - The document's parent is Third-Party
13426 : * - The document has a different scheme (http/https) than its parent document
13427 : * - The document's domain and subdomain do not match those of its parent
13428 : * document.
13429 : *
13430 : * If there is an error in determining whether the document is Third-Party,
13431 : * it will be assumed to be Third-Party for security reasons.
13432 : */
13433 : bool
13434 0 : nsDocument::IsThirdParty()
13435 : {
13436 0 : if (mIsThirdParty.isSome()) {
13437 0 : return mIsThirdParty.value();
13438 : }
13439 :
13440 0 : nsCOMPtr<nsIDocShellTreeItem> docshell = this->GetDocShell();
13441 0 : if (!docshell) {
13442 0 : mIsThirdParty.emplace(true);
13443 0 : return mIsThirdParty.value();
13444 : }
13445 :
13446 0 : nsCOMPtr<nsIDocShellTreeItem> parent;
13447 0 : nsresult rv = docshell->GetSameTypeParent(getter_AddRefs(parent));
13448 0 : MOZ_ASSERT(NS_SUCCEEDED(rv),
13449 : "nsIDocShellTreeItem::GetSameTypeParent should never fail");
13450 0 : bool isTopLevel = !parent;
13451 :
13452 0 : if (isTopLevel) {
13453 0 : mIsThirdParty.emplace(false);
13454 0 : return mIsThirdParty.value();
13455 : }
13456 :
13457 0 : nsCOMPtr<nsIDocument> parentDocument = GetParentDocument();
13458 0 : if (!parentDocument) {
13459 : // Failure
13460 0 : mIsThirdParty.emplace(true);
13461 0 : return mIsThirdParty.value();
13462 : }
13463 :
13464 0 : if (parentDocument->IsThirdParty()) {
13465 0 : mIsThirdParty.emplace(true);
13466 0 : return mIsThirdParty.value();
13467 : }
13468 :
13469 0 : nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
13470 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(parentDocument,
13471 0 : &rv);
13472 0 : if (NS_WARN_IF(NS_FAILED(rv) || !sop)) {
13473 : // Failure
13474 0 : mIsThirdParty.emplace(true);
13475 0 : return mIsThirdParty.value();
13476 : }
13477 0 : nsCOMPtr<nsIPrincipal> parentPrincipal = sop->GetPrincipal();
13478 :
13479 0 : bool principalsMatch = false;
13480 0 : rv = principal->Equals(parentPrincipal, &principalsMatch);
13481 :
13482 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
13483 : // Failure
13484 0 : mIsThirdParty.emplace(true);
13485 0 : return mIsThirdParty.value();
13486 : }
13487 :
13488 0 : if (!principalsMatch) {
13489 0 : mIsThirdParty.emplace(true);
13490 0 : return mIsThirdParty.value();
13491 : }
13492 :
13493 : // Fall-through. Document is not a Third-Party Document.
13494 0 : mIsThirdParty.emplace(false);
13495 0 : return mIsThirdParty.value();
13496 : }
13497 :
13498 : static bool
13499 0 : IsAboutReader(nsIURI* aURI)
13500 : {
13501 0 : if (!aURI) {
13502 0 : return false;
13503 : }
13504 :
13505 0 : nsCString spec;
13506 0 : aURI->GetSpec(spec);
13507 :
13508 : // Reader mode URLs look like about:reader?[...].
13509 0 : return StringBeginsWith(spec, NS_LITERAL_CSTRING("about:reader"));
13510 : }
13511 :
13512 : bool
13513 0 : nsIDocument::IsScopedStyleEnabled()
13514 : {
13515 0 : if (mIsScopedStyleEnabled == eScopedStyle_Unknown) {
13516 : // We allow <style scoped> in about:reader pages since on Android
13517 : // we use it to inject some in-page UI. (We currently don't
13518 : // support styling about:reader pages in stylo anyway, so for
13519 : // now it's OK to enable it here.)
13520 0 : mIsScopedStyleEnabled = nsContentUtils::IsChromeDoc(this) ||
13521 0 : IsAboutReader(mDocumentURI) ||
13522 0 : nsContentUtils::IsScopedStylePrefEnabled()
13523 0 : ? eScopedStyle_Enabled
13524 : : eScopedStyle_Disabled;
13525 : }
13526 0 : return mIsScopedStyleEnabled == eScopedStyle_Enabled;
13527 : }
|