Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsDocument.cpp
blob57355dcfb6ed5af710fa39f4cec0b96571da5aca
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Base class for all our document implementations.
9 */
11 #include "nsDocument.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/AutoRestore.h"
15 #include "mozilla/BinarySearch.h"
16 #include "mozilla/DebugOnly.h"
17 #include "mozilla/MemoryReporting.h"
18 #include "mozilla/Likely.h"
19 #include <algorithm>
21 #include "prlog.h"
22 #include "plstr.h"
23 #include "prprf.h"
25 #include "mozilla/Telemetry.h"
26 #include "nsIInterfaceRequestor.h"
27 #include "nsIInterfaceRequestorUtils.h"
28 #include "nsILoadContext.h"
29 #include "nsUnicharUtils.h"
30 #include "nsContentList.h"
31 #include "nsIObserver.h"
32 #include "nsIBaseWindow.h"
33 #include "mozilla/css/Loader.h"
34 #include "mozilla/css/ImageLoader.h"
35 #include "nsDocShell.h"
36 #include "nsIDocShellTreeItem.h"
37 #include "nsCOMArray.h"
38 #include "nsDOMClassInfo.h"
39 #include "mozilla/Services.h"
41 #include "mozilla/AsyncEventDispatcher.h"
42 #include "mozilla/BasicEvents.h"
43 #include "mozilla/EventListenerManager.h"
44 #include "mozilla/EventStateManager.h"
45 #include "nsIDOMNodeFilter.h"
47 #include "nsIDOMStyleSheet.h"
48 #include "mozilla/dom/Attr.h"
49 #include "nsIDOMDOMImplementation.h"
50 #include "nsIDOMDocumentXBL.h"
51 #include "mozilla/dom/Element.h"
52 #include "nsGenericHTMLElement.h"
53 #include "mozilla/dom/CDATASection.h"
54 #include "mozilla/dom/ProcessingInstruction.h"
55 #include "nsDOMString.h"
56 #include "nsNodeUtils.h"
57 #include "nsLayoutUtils.h" // for GetFrameForPoint
58 #include "nsIFrame.h"
59 #include "nsITabChild.h"
61 #include "nsRange.h"
62 #include "nsIDOMText.h"
63 #include "nsIDOMComment.h"
64 #include "mozilla/dom/DocumentType.h"
65 #include "mozilla/dom/NodeIterator.h"
66 #include "mozilla/dom/TreeWalker.h"
68 #include "nsIServiceManager.h"
69 #include "nsIServiceWorkerManager.h"
71 #include "nsCanvasFrame.h"
72 #include "nsContentCID.h"
73 #include "nsError.h"
74 #include "nsPresShell.h"
75 #include "nsPresContext.h"
76 #include "nsIJSON.h"
77 #include "nsThreadUtils.h"
78 #include "nsNodeInfoManager.h"
79 #include "nsIFileChannel.h"
80 #include "nsIMultiPartChannel.h"
81 #include "nsIRefreshURI.h"
82 #include "nsIWebNavigation.h"
83 #include "nsIScriptError.h"
84 #include "nsStyleSheetService.h"
86 #include "nsNetUtil.h" // for NS_MakeAbsoluteURI
88 #include "nsIScriptSecurityManager.h"
89 #include "nsIPrincipal.h"
91 #include "nsIDOMWindow.h"
92 #include "nsPIDOMWindow.h"
93 #include "nsIDOMElement.h"
94 #include "nsFocusManager.h"
96 // for radio group stuff
97 #include "nsIDOMHTMLInputElement.h"
98 #include "nsIRadioVisitor.h"
99 #include "nsIFormControl.h"
101 #include "nsBidiUtils.h"
103 #include "nsIParserService.h"
104 #include "nsContentCreatorFunctions.h"
106 #include "nsIScriptContext.h"
107 #include "nsBindingManager.h"
108 #include "nsIDOMHTMLDocument.h"
109 #include "nsHTMLDocument.h"
110 #include "nsIDOMHTMLFormElement.h"
111 #include "nsIRequest.h"
112 #include "nsHostObjectProtocolHandler.h"
114 #include "nsCharsetSource.h"
115 #include "nsIParser.h"
116 #include "nsIContentSink.h"
118 #include "nsDateTimeFormatCID.h"
119 #include "nsIDateTimeFormat.h"
120 #include "mozilla/EventDispatcher.h"
121 #include "mozilla/EventStates.h"
122 #include "mozilla/InternalMutationEvent.h"
123 #include "nsDOMCID.h"
125 #include "jsapi.h"
126 #include "nsIXPConnect.h"
127 #include "nsCCUncollectableMarker.h"
128 #include "nsIContentPolicy.h"
129 #include "nsContentPolicyUtils.h"
130 #include "nsICategoryManager.h"
131 #include "nsIDocumentLoaderFactory.h"
132 #include "nsIDocumentLoader.h"
133 #include "nsIContentViewer.h"
134 #include "nsIXMLContentSink.h"
135 #include "nsIXULDocument.h"
136 #include "nsIPrompt.h"
137 #include "nsIPropertyBag2.h"
138 #include "mozilla/dom/PageTransitionEvent.h"
139 #include "mozilla/dom/StyleRuleChangeEvent.h"
140 #include "mozilla/dom/StyleSheetChangeEvent.h"
141 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
142 #include "nsJSUtils.h"
143 #include "nsFrameLoader.h"
144 #include "nsEscape.h"
145 #include "nsObjectLoadingContent.h"
146 #include "nsHtml5TreeOpExecutor.h"
147 #include "mozilla/dom/HTMLLinkElement.h"
148 #include "mozilla/dom/HTMLMediaElement.h"
150 #include "mozAutoDocUpdate.h"
151 #include "nsGlobalWindow.h"
152 #include "mozilla/dom/EncodingUtils.h"
153 #include "nsDOMNavigationTiming.h"
155 #include "nsSMILAnimationController.h"
156 #include "imgIContainer.h"
157 #include "nsSVGUtils.h"
158 #include "SVGElementFactory.h"
160 #include "nsRefreshDriver.h"
162 // FOR CSP (autogenerated by xpidl)
163 #include "nsIContentSecurityPolicy.h"
164 #include "mozilla/dom/nsCSPService.h"
165 #include "nsHTMLStyleSheet.h"
166 #include "nsHTMLCSSStyleSheet.h"
167 #include "SVGAttrAnimationRuleProcessor.h"
168 #include "mozilla/dom/DOMImplementation.h"
169 #include "mozilla/dom/ShadowRoot.h"
170 #include "mozilla/dom/Comment.h"
171 #include "nsTextNode.h"
172 #include "mozilla/dom/Link.h"
173 #include "mozilla/dom/HTMLElementBinding.h"
174 #include "mozilla/dom/SVGElementBinding.h"
175 #include "nsXULAppAPI.h"
176 #include "mozilla/dom/Touch.h"
177 #include "mozilla/dom/TouchEvent.h"
179 #include "mozilla/Preferences.h"
181 #include "imgILoader.h"
182 #include "imgRequestProxy.h"
183 #include "nsWrapperCacheInlines.h"
184 #include "nsSandboxFlags.h"
185 #include "nsIAppsService.h"
186 #include "mozilla/dom/AnonymousContent.h"
187 #include "mozilla/dom/AnimationTimeline.h"
188 #include "mozilla/dom/BindingUtils.h"
189 #include "mozilla/dom/DocumentFragment.h"
190 #include "mozilla/dom/Event.h"
191 #include "mozilla/dom/HTMLBodyElement.h"
192 #include "mozilla/dom/HTMLInputElement.h"
193 #include "mozilla/dom/MediaQueryList.h"
194 #include "mozilla/dom/NodeFilterBinding.h"
195 #include "mozilla/dom/OwningNonNull.h"
196 #include "mozilla/dom/TabChild.h"
197 #include "mozilla/dom/UndoManager.h"
198 #include "mozilla/dom/WebComponentsBinding.h"
199 #include "nsFrame.h"
200 #include "nsDOMCaretPosition.h"
201 #include "nsIDOMHTMLTextAreaElement.h"
202 #include "nsViewportInfo.h"
203 #include "nsIContentPermissionPrompt.h"
204 #include "mozilla/StaticPtr.h"
205 #include "nsITextControlElement.h"
206 #include "nsIDOMNSEditableElement.h"
207 #include "nsIEditor.h"
208 #include "nsIDOMCSSStyleRule.h"
209 #include "mozilla/css/Rule.h"
210 #include "nsIDOMLocation.h"
211 #include "nsIHttpChannelInternal.h"
212 #include "nsISecurityConsoleMessage.h"
213 #include "nsCharSeparatedTokenizer.h"
214 #include "mozilla/dom/XPathEvaluator.h"
215 #include "mozilla/dom/XPathNSResolverBinding.h"
216 #include "mozilla/dom/XPathResult.h"
217 #include "nsIDocumentEncoder.h"
218 #include "nsIDocumentActivity.h"
219 #include "nsIStructuredCloneContainer.h"
220 #include "nsIMutableArray.h"
221 #include "nsContentPermissionHelper.h"
222 #include "mozilla/dom/DOMStringList.h"
223 #include "nsWindowMemoryReporter.h"
224 #include "nsLocation.h"
225 #include "mozilla/dom/FontFaceSet.h"
226 #include "mozilla/dom/BoxObject.h"
228 #ifdef MOZ_MEDIA_NAVIGATOR
229 #include "mozilla/MediaManager.h"
230 #endif // MOZ_MEDIA_NAVIGATOR
231 #ifdef MOZ_WEBRTC
232 #include "IPeerConnection.h"
233 #endif // MOZ_WEBRTC
235 using namespace mozilla;
236 using namespace mozilla::dom;
238 typedef nsTArray<Link*> LinkArray;
240 #ifdef PR_LOGGING
241 static PRLogModuleInfo* gDocumentLeakPRLog;
242 static PRLogModuleInfo* gCspPRLog;
243 #endif
245 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
247 nsIdentifierMapEntry::~nsIdentifierMapEntry()
251 void
252 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
254 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
255 "mIdentifierMap mNameContentList");
256 aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
258 if (mImageElement) {
259 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
260 "mIdentifierMap mImageElement element");
261 nsIContent* imageElement = mImageElement;
262 aCallback->NoteXPCOMChild(imageElement);
266 bool
267 nsIdentifierMapEntry::IsEmpty()
269 return mIdContentList.Count() == 0 && !mNameContentList &&
270 !mChangeCallbacks && !mImageElement;
273 Element*
274 nsIdentifierMapEntry::GetIdElement()
276 return static_cast<Element*>(mIdContentList.SafeElementAt(0));
279 Element*
280 nsIdentifierMapEntry::GetImageIdElement()
282 return mImageElement ? mImageElement.get() : GetIdElement();
285 void
286 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
288 for (int32_t i = 0; i < mIdContentList.Count(); ++i) {
289 aElements->AppendObject(static_cast<Element*>(mIdContentList[i]));
293 void
294 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
295 void* aData, bool aForImage)
297 if (!mChangeCallbacks) {
298 mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
299 if (!mChangeCallbacks)
300 return;
303 ChangeCallback cc = { aCallback, aData, aForImage };
304 mChangeCallbacks->PutEntry(cc);
307 void
308 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
309 void* aData, bool aForImage)
311 if (!mChangeCallbacks)
312 return;
313 ChangeCallback cc = { aCallback, aData, aForImage };
314 mChangeCallbacks->RemoveEntry(cc);
315 if (mChangeCallbacks->Count() == 0) {
316 mChangeCallbacks = nullptr;
320 struct FireChangeArgs {
321 Element* mFrom;
322 Element* mTo;
323 bool mImageOnly;
324 bool mHaveImageOverride;
327 // XXX Workaround for bug 980560 to maintain the existing broken semantics
328 template<>
329 struct nsIStyleRule::COMTypeInfo<css::Rule, void> {
330 static const nsIID kIID;
332 const nsIID nsIStyleRule::COMTypeInfo<css::Rule, void>::kIID = NS_ISTYLE_RULE_IID;
334 namespace mozilla {
335 namespace dom {
337 static PLDHashOperator
338 CustomDefinitionsTraverse(CustomElementHashKey* aKey,
339 CustomElementDefinition* aDefinition,
340 void* aArg)
342 nsCycleCollectionTraversalCallback* cb =
343 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
345 nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks;
347 if (callbacks->mAttributeChangedCallback.WasPassed()) {
348 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
349 "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
350 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value());
353 if (callbacks->mCreatedCallback.WasPassed()) {
354 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
355 "mCustomDefinitions->mCallbacks->mCreatedCallback");
356 cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
359 if (callbacks->mAttachedCallback.WasPassed()) {
360 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
361 "mCustomDefinitions->mCallbacks->mAttachedCallback");
362 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value());
365 if (callbacks->mDetachedCallback.WasPassed()) {
366 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
367 "mCustomDefinitions->mCallbacks->mDetachedCallback");
368 cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value());
371 return PL_DHASH_NEXT;
374 static PLDHashOperator
375 CandidatesTraverse(CustomElementHashKey* aKey,
376 nsTArray<nsRefPtr<Element>>* aData,
377 void* aArg)
379 nsCycleCollectionTraversalCallback *cb =
380 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
381 for (size_t i = 0; i < aData->Length(); ++i) {
382 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element");
383 cb->NoteXPCOMChild(aData->ElementAt(i));
385 return PL_DHASH_NEXT;
388 struct CustomDefinitionTraceArgs
390 const TraceCallbacks& callbacks;
391 void* closure;
394 static PLDHashOperator
395 CustomDefinitionTrace(CustomElementHashKey *aKey,
396 CustomElementDefinition *aData,
397 void *aArg)
399 CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg);
400 MOZ_ASSERT(aData, "Definition must not be null");
401 traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype",
402 traceArgs->closure);
403 return PL_DHASH_NEXT;
406 NS_IMPL_CYCLE_COLLECTION_CLASS(Registry)
408 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry)
409 CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure };
410 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace,
411 &customDefinitionArgs);
412 NS_IMPL_CYCLE_COLLECTION_TRACE_END
414 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry)
415 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb);
416 tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb);
417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
418 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
420 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry)
421 tmp->mCustomDefinitions.Clear();
422 tmp->mCandidatesMap.Clear();
423 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
425 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry)
426 NS_INTERFACE_MAP_ENTRY(nsISupports)
427 NS_INTERFACE_MAP_END
429 NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry)
430 NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry)
432 Registry::Registry()
434 mozilla::HoldJSObjects(this);
437 Registry::~Registry()
439 mozilla::DropJSObjects(this);
442 void
443 CustomElementCallback::Call()
445 ErrorResult rv;
446 switch (mType) {
447 case nsIDocument::eCreated:
449 // For the duration of this callback invocation, the element is being created
450 // flag must be set to true.
451 mOwnerData->mElementIsBeingCreated = true;
453 // The callback hasn't actually been invoked yet, but we need to flip
454 // this now in order to enqueue the attached callback. This is a spec
455 // bug (w3c bug 27437).
456 mOwnerData->mCreatedCallbackInvoked = true;
458 // If ELEMENT is in a document and this document has a browsing context,
459 // enqueue attached callback for ELEMENT.
460 nsIDocument* document = mThisObject->GetUncomposedDoc();
461 if (document && document->GetDocShell()) {
462 document->EnqueueLifecycleCallback(nsIDocument::eAttached, mThisObject);
465 static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
466 mOwnerData->mElementIsBeingCreated = false;
467 break;
469 case nsIDocument::eAttached:
470 static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
471 break;
472 case nsIDocument::eDetached:
473 static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
474 break;
475 case nsIDocument::eAttributeChanged:
476 static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
477 mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
478 break;
482 void
483 CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
485 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
486 aCb.NoteXPCOMChild(mThisObject);
488 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
489 aCb.NoteXPCOMChild(mCallback);
492 CustomElementCallback::CustomElementCallback(Element* aThisObject,
493 nsIDocument::ElementCallbackType aCallbackType,
494 mozilla::dom::CallbackFunction* aCallback,
495 CustomElementData* aOwnerData)
496 : mThisObject(aThisObject),
497 mCallback(aCallback),
498 mType(aCallbackType),
499 mOwnerData(aOwnerData)
503 CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
504 nsIAtom* aType,
505 nsIAtom* aLocalName,
506 LifecycleCallbacks* aCallbacks,
507 uint32_t aNamespaceID,
508 uint32_t aDocOrder)
509 : mPrototype(aPrototype),
510 mType(aType),
511 mLocalName(aLocalName),
512 mCallbacks(aCallbacks),
513 mNamespaceID(aNamespaceID),
514 mDocOrder(aDocOrder)
518 CustomElementData::CustomElementData(nsIAtom* aType)
519 : mType(aType),
520 mCurrentCallback(-1),
521 mElementIsBeingCreated(false),
522 mCreatedCallbackInvoked(true),
523 mAssociatedMicroTask(-1)
527 void
528 CustomElementData::RunCallbackQueue()
530 // Note: It's possible to re-enter this method.
531 while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
532 mCallbackQueue[mCurrentCallback]->Call();
535 mCallbackQueue.Clear();
536 mCurrentCallback = -1;
539 } // namespace dom
540 } // namespace mozilla
542 static PLDHashOperator
543 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
545 FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
546 // Don't fire image changes for non-image observers, and don't fire element
547 // changes for image observers when an image override is active.
548 if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) :
549 args->mImageOnly)
550 return PL_DHASH_NEXT;
551 return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
552 ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
555 void
556 nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
557 Element* aNewElement,
558 bool aImageOnly)
560 if (!mChangeCallbacks)
561 return;
563 FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement };
564 mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
567 namespace {
569 struct PositionComparator
571 Element* const mElement;
572 explicit PositionComparator(Element* const aElement) : mElement(aElement) {}
574 int operator()(void* aElement) const {
575 Element* curElement = static_cast<Element*>(aElement);
576 if (mElement == curElement) {
577 return 0;
579 if (nsContentUtils::PositionIsBefore(mElement, curElement)) {
580 return -1;
582 return 1;
585 } // namespace
587 bool
588 nsIdentifierMapEntry::AddIdElement(Element* aElement)
590 NS_PRECONDITION(aElement, "Must have element");
591 NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0,
592 "Why is null in our list?");
594 #ifdef DEBUG
595 Element* currentElement =
596 static_cast<Element*>(mIdContentList.SafeElementAt(0));
597 #endif
599 // Common case
600 if (mIdContentList.Count() == 0) {
601 if (!mIdContentList.AppendElement(aElement))
602 return false;
603 NS_ASSERTION(currentElement == nullptr, "How did that happen?");
604 FireChangeCallbacks(nullptr, aElement);
605 return true;
608 // We seem to have multiple content nodes for the same id, or XUL is messing
609 // with us. Search for the right place to insert the content.
611 size_t idx;
612 if (BinarySearchIf(mIdContentList, 0, mIdContentList.Count(),
613 PositionComparator(aElement), &idx)) {
614 // Already in the list, so already in the right spot. Get out of here.
615 // XXXbz this only happens because XUL does all sorts of random
616 // UpdateIdTableEntry calls. Hate, hate, hate!
617 return true;
620 if (!mIdContentList.InsertElementAt(aElement, idx))
621 return false;
623 if (idx == 0) {
624 Element* oldElement =
625 static_cast<Element*>(mIdContentList.SafeElementAt(1));
626 NS_ASSERTION(currentElement == oldElement, "How did that happen?");
627 FireChangeCallbacks(oldElement, aElement);
629 return true;
632 void
633 nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
635 NS_PRECONDITION(aElement, "Missing element");
637 // This should only be called while the document is in an update.
638 // Assertions near the call to this method guarantee this.
640 // This could fire in OOM situations
641 // Only assert this in HTML documents for now as XUL does all sorts of weird
642 // crap.
643 NS_ASSERTION(!aElement->OwnerDoc()->IsHTML() ||
644 mIdContentList.IndexOf(aElement) >= 0,
645 "Removing id entry that doesn't exist");
647 // XXXbz should this ever Compact() I guess when all the content is gone
648 // we'll just get cleaned up in the natural order of things...
649 Element* currentElement =
650 static_cast<Element*>(mIdContentList.SafeElementAt(0));
651 mIdContentList.RemoveElement(aElement);
652 if (currentElement == aElement) {
653 FireChangeCallbacks(currentElement,
654 static_cast<Element*>(mIdContentList.SafeElementAt(0)));
658 void
659 nsIdentifierMapEntry::SetImageElement(Element* aElement)
661 Element* oldElement = GetImageIdElement();
662 mImageElement = aElement;
663 Element* newElement = GetImageIdElement();
664 if (oldElement != newElement) {
665 FireChangeCallbacks(oldElement, newElement, true);
669 void
670 nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
672 if (!mNameContentList) {
673 mNameContentList = new nsSimpleContentList(aNode);
676 mNameContentList->AppendElement(aElement);
679 void
680 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
682 if (mNameContentList) {
683 mNameContentList->RemoveElement(aElement);
687 bool
688 nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
690 Element* idElement = GetIdElement();
691 return idElement &&
692 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
695 size_t
696 nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
698 return nsStringHashKey::SizeOfExcludingThis(aMallocSizeOf);
701 // Helper structs for the content->subdoc map
703 class SubDocMapEntry : public PLDHashEntryHdr
705 public:
706 // Both of these are strong references
707 Element *mKey; // must be first, to look like PLDHashEntryStub
708 nsIDocument *mSubDocument;
711 struct FindContentData
713 explicit FindContentData(nsIDocument* aSubDoc)
714 : mSubDocument(aSubDoc), mResult(nullptr)
718 nsISupports *mSubDocument;
719 Element *mResult;
724 * A struct that holds all the information about a radio group.
726 struct nsRadioGroupStruct
728 nsRadioGroupStruct()
729 : mRequiredRadioCount(0)
730 , mGroupSuffersFromValueMissing(false)
734 * A strong pointer to the currently selected radio button.
736 nsRefPtr<HTMLInputElement> mSelectedRadioButton;
737 nsCOMArray<nsIFormControl> mRadioButtons;
738 uint32_t mRequiredRadioCount;
739 bool mGroupSuffersFromValueMissing;
743 nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument)
745 mLength = -1;
746 // Not reference counted to avoid circular references.
747 // The document will tell us when its going away.
748 mDocument = aDocument;
749 mDocument->AddObserver(this);
752 nsDOMStyleSheetList::~nsDOMStyleSheetList()
754 if (mDocument) {
755 mDocument->RemoveObserver(this);
759 NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList,
760 nsIDocumentObserver,
761 nsIMutationObserver)
763 uint32_t
764 nsDOMStyleSheetList::Length()
766 if (!mDocument) {
767 return 0;
770 // XXX Find the number and then cache it. We'll use the
771 // observer notification to figure out if new ones have
772 // been added or removed.
773 if (-1 == mLength) {
774 mLength = mDocument->GetNumberOfStyleSheets();
776 #ifdef DEBUG
777 int32_t i;
778 for (i = 0; i < mLength; i++) {
779 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
780 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
781 NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
783 #endif
785 return mLength;
788 CSSStyleSheet*
789 nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
791 if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
792 aFound = false;
793 return nullptr;
796 aFound = true;
797 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
798 NS_ASSERTION(sheet, "Must have a sheet");
800 return static_cast<CSSStyleSheet*>(sheet);
803 void
804 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
806 mDocument = nullptr;
809 void
810 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
811 nsIStyleSheet* aStyleSheet,
812 bool aDocumentSheet)
814 if (aDocumentSheet && -1 != mLength) {
815 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
816 if (domss) {
817 mLength++;
822 void
823 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
824 nsIStyleSheet* aStyleSheet,
825 bool aDocumentSheet)
827 if (aDocumentSheet && -1 != mLength) {
828 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
829 if (domss) {
830 mLength--;
835 // nsOnloadBlocker implementation
836 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
838 NS_IMETHODIMP
839 nsOnloadBlocker::GetName(nsACString &aResult)
841 aResult.AssignLiteral("about:document-onload-blocker");
842 return NS_OK;
845 NS_IMETHODIMP
846 nsOnloadBlocker::IsPending(bool *_retval)
848 *_retval = true;
849 return NS_OK;
852 NS_IMETHODIMP
853 nsOnloadBlocker::GetStatus(nsresult *status)
855 *status = NS_OK;
856 return NS_OK;
859 NS_IMETHODIMP
860 nsOnloadBlocker::Cancel(nsresult status)
862 return NS_OK;
864 NS_IMETHODIMP
865 nsOnloadBlocker::Suspend(void)
867 return NS_OK;
869 NS_IMETHODIMP
870 nsOnloadBlocker::Resume(void)
872 return NS_OK;
875 NS_IMETHODIMP
876 nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
878 *aLoadGroup = nullptr;
879 return NS_OK;
882 NS_IMETHODIMP
883 nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
885 return NS_OK;
888 NS_IMETHODIMP
889 nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
891 *aLoadFlags = nsIRequest::LOAD_NORMAL;
892 return NS_OK;
895 NS_IMETHODIMP
896 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
898 return NS_OK;
901 // ==================================================================
903 nsExternalResourceMap::nsExternalResourceMap()
904 : mHaveShutDown(false)
908 nsIDocument*
909 nsExternalResourceMap::RequestResource(nsIURI* aURI,
910 nsINode* aRequestingNode,
911 nsDocument* aDisplayDocument,
912 ExternalResourceLoad** aPendingLoad)
914 // If we ever start allowing non-same-origin loads here, we might need to do
915 // something interesting with aRequestingPrincipal even for the hashtable
916 // gets.
917 NS_PRECONDITION(aURI, "Must have a URI");
918 NS_PRECONDITION(aRequestingNode, "Must have a node");
919 *aPendingLoad = nullptr;
920 if (mHaveShutDown) {
921 return nullptr;
924 // First, make sure we strip the ref from aURI.
925 nsCOMPtr<nsIURI> clone;
926 nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
927 if (NS_FAILED(rv) || !clone) {
928 return nullptr;
931 ExternalResource* resource;
932 mMap.Get(clone, &resource);
933 if (resource) {
934 return resource->mDocument;
937 nsRefPtr<PendingLoad> load;
938 mPendingLoads.Get(clone, getter_AddRefs(load));
939 if (load) {
940 load.forget(aPendingLoad);
941 return nullptr;
944 load = new PendingLoad(aDisplayDocument);
946 mPendingLoads.Put(clone, load);
948 if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
949 // Make sure we don't thrash things by trying this load again, since
950 // chances are it failed for good reasons (security check, etc).
951 AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
952 } else {
953 load.forget(aPendingLoad);
956 return nullptr;
959 struct
960 nsExternalResourceEnumArgs
962 nsIDocument::nsSubDocEnumFunc callback;
963 void *data;
966 static PLDHashOperator
967 ExternalResourceEnumerator(nsIURI* aKey,
968 nsExternalResourceMap::ExternalResource* aData,
969 void* aClosure)
971 nsExternalResourceEnumArgs* args =
972 static_cast<nsExternalResourceEnumArgs*>(aClosure);
973 bool next =
974 aData->mDocument ? args->callback(aData->mDocument, args->data) : true;
975 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
978 void
979 nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
980 void* aData)
982 nsExternalResourceEnumArgs args = { aCallback, aData };
983 mMap.EnumerateRead(ExternalResourceEnumerator, &args);
986 static PLDHashOperator
987 ExternalResourceTraverser(nsIURI* aKey,
988 nsExternalResourceMap::ExternalResource* aData,
989 void* aClosure)
991 nsCycleCollectionTraversalCallback *cb =
992 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
994 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
995 "mExternalResourceMap.mMap entry"
996 "->mDocument");
997 cb->NoteXPCOMChild(aData->mDocument);
999 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1000 "mExternalResourceMap.mMap entry"
1001 "->mViewer");
1002 cb->NoteXPCOMChild(aData->mViewer);
1004 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1005 "mExternalResourceMap.mMap entry"
1006 "->mLoadGroup");
1007 cb->NoteXPCOMChild(aData->mLoadGroup);
1009 return PL_DHASH_NEXT;
1012 void
1013 nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
1015 // mPendingLoads will get cleared out as the requests complete, so
1016 // no need to worry about those here.
1017 mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
1020 static PLDHashOperator
1021 ExternalResourceHider(nsIURI* aKey,
1022 nsExternalResourceMap::ExternalResource* aData,
1023 void* aClosure)
1025 if (aData->mViewer) {
1026 aData->mViewer->Hide();
1028 return PL_DHASH_NEXT;
1031 void
1032 nsExternalResourceMap::HideViewers()
1034 mMap.EnumerateRead(ExternalResourceHider, nullptr);
1037 static PLDHashOperator
1038 ExternalResourceShower(nsIURI* aKey,
1039 nsExternalResourceMap::ExternalResource* aData,
1040 void* aClosure)
1042 if (aData->mViewer) {
1043 aData->mViewer->Show();
1045 return PL_DHASH_NEXT;
1048 void
1049 nsExternalResourceMap::ShowViewers()
1051 mMap.EnumerateRead(ExternalResourceShower, nullptr);
1054 void
1055 TransferZoomLevels(nsIDocument* aFromDoc,
1056 nsIDocument* aToDoc)
1058 NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
1059 "transferring zoom levels from/to null doc");
1061 nsIPresShell* fromShell = aFromDoc->GetShell();
1062 if (!fromShell)
1063 return;
1065 nsPresContext* fromCtxt = fromShell->GetPresContext();
1066 if (!fromCtxt)
1067 return;
1069 nsIPresShell* toShell = aToDoc->GetShell();
1070 if (!toShell)
1071 return;
1073 nsPresContext* toCtxt = toShell->GetPresContext();
1074 if (!toCtxt)
1075 return;
1077 toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
1078 toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
1079 toCtxt->SetTextZoom(fromCtxt->TextZoom());
1082 void
1083 TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
1085 NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
1086 "transferring showing state from/to null doc");
1088 if (aFromDoc->IsShowing()) {
1089 aToDoc->OnPageShow(true, nullptr);
1093 nsresult
1094 nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
1095 nsIContentViewer* aViewer,
1096 nsILoadGroup* aLoadGroup,
1097 nsIDocument* aDisplayDocument)
1099 NS_PRECONDITION(aURI, "Unexpected call");
1100 NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
1101 "Must have both or neither");
1103 nsRefPtr<PendingLoad> load;
1104 mPendingLoads.Get(aURI, getter_AddRefs(load));
1105 mPendingLoads.Remove(aURI);
1107 nsresult rv = NS_OK;
1109 nsCOMPtr<nsIDocument> doc;
1110 if (aViewer) {
1111 doc = aViewer->GetDocument();
1112 NS_ASSERTION(doc, "Must have a document");
1114 nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
1115 if (xulDoc) {
1116 // We don't handle XUL stuff here yet.
1117 rv = NS_ERROR_NOT_AVAILABLE;
1118 } else {
1119 doc->SetDisplayDocument(aDisplayDocument);
1121 // Make sure that hiding our viewer will tear down its presentation.
1122 aViewer->SetSticky(false);
1124 rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
1125 if (NS_SUCCEEDED(rv)) {
1126 rv = aViewer->Open(nullptr, nullptr);
1130 if (NS_FAILED(rv)) {
1131 doc = nullptr;
1132 aViewer = nullptr;
1133 aLoadGroup = nullptr;
1137 ExternalResource* newResource = new ExternalResource();
1138 mMap.Put(aURI, newResource);
1140 newResource->mDocument = doc;
1141 newResource->mViewer = aViewer;
1142 newResource->mLoadGroup = aLoadGroup;
1143 if (doc) {
1144 TransferZoomLevels(aDisplayDocument, doc);
1145 TransferShowingState(aDisplayDocument, doc);
1148 const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
1149 for (uint32_t i = 0; i < obs.Length(); ++i) {
1150 obs[i]->Observe(doc, "external-resource-document-created", nullptr);
1153 return rv;
1156 NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
1157 nsIStreamListener,
1158 nsIRequestObserver)
1160 NS_IMETHODIMP
1161 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
1162 nsISupports *aContext)
1164 nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
1165 if (map.HaveShutDown()) {
1166 return NS_BINDING_ABORTED;
1169 nsCOMPtr<nsIContentViewer> viewer;
1170 nsCOMPtr<nsILoadGroup> loadGroup;
1171 nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
1172 getter_AddRefs(loadGroup));
1174 // Make sure to do this no matter what
1175 nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
1176 mDisplayDocument);
1177 if (NS_FAILED(rv)) {
1178 return rv;
1180 if (NS_FAILED(rv2)) {
1181 mTargetListener = nullptr;
1182 return rv2;
1185 return mTargetListener->OnStartRequest(aRequest, aContext);
1188 nsresult
1189 nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
1190 nsIContentViewer** aViewer,
1191 nsILoadGroup** aLoadGroup)
1193 NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
1194 *aViewer = nullptr;
1195 *aLoadGroup = nullptr;
1197 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1198 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
1200 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
1201 if (httpChannel) {
1202 bool requestSucceeded;
1203 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
1204 !requestSucceeded) {
1205 // Bail out on this load, since it looks like we have an HTTP error page
1206 return NS_BINDING_ABORTED;
1210 nsAutoCString type;
1211 chan->GetContentType(type);
1213 nsCOMPtr<nsILoadGroup> loadGroup;
1214 chan->GetLoadGroup(getter_AddRefs(loadGroup));
1216 // Give this document its own loadgroup
1217 nsCOMPtr<nsILoadGroup> newLoadGroup =
1218 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1219 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
1220 newLoadGroup->SetLoadGroup(loadGroup);
1222 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1223 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1225 nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1226 new LoadgroupCallbacks(callbacks);
1227 newLoadGroup->SetNotificationCallbacks(newCallbacks);
1229 // This is some serious hackery cribbed from docshell
1230 nsCOMPtr<nsICategoryManager> catMan =
1231 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1232 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1233 nsXPIDLCString contractId;
1234 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1235 getter_Copies(contractId));
1236 NS_ENSURE_SUCCESS(rv, rv);
1237 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1238 do_GetService(contractId);
1239 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1241 nsCOMPtr<nsIContentViewer> viewer;
1242 nsCOMPtr<nsIStreamListener> listener;
1243 rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1244 type.get(), nullptr, nullptr,
1245 getter_AddRefs(listener),
1246 getter_AddRefs(viewer));
1247 NS_ENSURE_SUCCESS(rv, rv);
1248 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1250 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1251 if (!parser) {
1252 /// We don't want to deal with the various fake documents yet
1253 return NS_ERROR_NOT_IMPLEMENTED;
1256 // We can't handle HTML and other weird things here yet.
1257 nsIContentSink* sink = parser->GetContentSink();
1258 nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1259 if (!xmlSink) {
1260 return NS_ERROR_NOT_IMPLEMENTED;
1263 listener.swap(mTargetListener);
1264 viewer.forget(aViewer);
1265 newLoadGroup.forget(aLoadGroup);
1266 return NS_OK;
1269 NS_IMETHODIMP
1270 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1271 nsISupports* aContext,
1272 nsIInputStream* aStream,
1273 uint64_t aOffset,
1274 uint32_t aCount)
1276 NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
1277 if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1278 return NS_BINDING_ABORTED;
1280 return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1281 aCount);
1284 NS_IMETHODIMP
1285 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1286 nsISupports* aContext,
1287 nsresult aStatus)
1289 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1290 if (mTargetListener) {
1291 nsCOMPtr<nsIStreamListener> listener;
1292 mTargetListener.swap(listener);
1293 return listener->OnStopRequest(aRequest, aContext, aStatus);
1296 return NS_OK;
1299 nsresult
1300 nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1301 nsINode* aRequestingNode)
1303 NS_PRECONDITION(aURI, "Must have a URI");
1304 NS_PRECONDITION(aRequestingNode, "Must have a node");
1306 // Time to start a load. First, the security checks.
1308 nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal();
1310 nsresult rv = nsContentUtils::GetSecurityManager()->
1311 CheckLoadURIWithPrincipal(requestingPrincipal, aURI,
1312 nsIScriptSecurityManager::STANDARD);
1313 NS_ENSURE_SUCCESS(rv, rv);
1315 // Allow data URIs and other URI's that inherit their principal by passing
1316 // true as the 3rd argument of CheckMayLoad, since we want
1317 // to allow external resources from data URIs regardless of the difference
1318 // in URI scheme.
1319 rv = requestingPrincipal->CheckMayLoad(aURI, true, true);
1320 NS_ENSURE_SUCCESS(rv, rv);
1322 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1323 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
1324 aURI,
1325 requestingPrincipal,
1326 aRequestingNode,
1327 EmptyCString(), //mime guess
1328 nullptr, //extra
1329 &shouldLoad,
1330 nsContentUtils::GetContentPolicy(),
1331 nsContentUtils::GetSecurityManager());
1332 if (NS_FAILED(rv)) return rv;
1333 if (NS_CP_REJECTED(shouldLoad)) {
1334 // Disallowed by content policy
1335 return NS_ERROR_CONTENT_BLOCKED;
1338 nsIDocument* doc = aRequestingNode->OwnerDoc();
1340 nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
1341 NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
1343 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
1344 nsCOMPtr<nsIChannel> channel;
1345 rv = NS_NewChannel(getter_AddRefs(channel),
1346 aURI,
1347 aRequestingNode,
1348 nsILoadInfo::SEC_NORMAL,
1349 nsIContentPolicy::TYPE_OTHER,
1350 loadGroup,
1351 req); // aCallbacks
1353 NS_ENSURE_SUCCESS(rv, rv);
1355 mURI = aURI;
1357 return channel->AsyncOpen(this, nullptr);
1360 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1361 nsIInterfaceRequestor)
1363 #define IMPL_SHIM(_i) \
1364 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1366 IMPL_SHIM(nsILoadContext)
1367 IMPL_SHIM(nsIProgressEventSink)
1368 IMPL_SHIM(nsIChannelEventSink)
1369 IMPL_SHIM(nsISecurityEventSink)
1370 IMPL_SHIM(nsIApplicationCacheContainer)
1372 #undef IMPL_SHIM
1374 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1376 #define TRY_SHIM(_i) \
1377 PR_BEGIN_MACRO \
1378 if (IID_IS(_i)) { \
1379 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1380 if (!real) { \
1381 return NS_NOINTERFACE; \
1383 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1384 if (!shim) { \
1385 return NS_ERROR_OUT_OF_MEMORY; \
1387 shim.forget(aSink); \
1388 return NS_OK; \
1390 PR_END_MACRO
1392 NS_IMETHODIMP
1393 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1394 void **aSink)
1396 if (mCallbacks &&
1397 (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
1398 IID_IS(nsITabChild))) {
1399 return mCallbacks->GetInterface(aIID, aSink);
1402 *aSink = nullptr;
1404 TRY_SHIM(nsILoadContext);
1405 TRY_SHIM(nsIProgressEventSink);
1406 TRY_SHIM(nsIChannelEventSink);
1407 TRY_SHIM(nsISecurityEventSink);
1408 TRY_SHIM(nsIApplicationCacheContainer);
1410 return NS_NOINTERFACE;
1413 #undef TRY_SHIM
1414 #undef IID_IS
1416 nsExternalResourceMap::ExternalResource::~ExternalResource()
1418 if (mViewer) {
1419 mViewer->Close(nullptr);
1420 mViewer->Destroy();
1424 // ==================================================================
1425 // =
1426 // ==================================================================
1428 // If we ever have an nsIDocumentObserver notification for stylesheet title
1429 // changes we should update the list from that instead of overriding
1430 // EnsureFresh.
1431 class nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList
1433 public:
1434 explicit nsDOMStyleSheetSetList(nsIDocument* aDocument);
1436 void Disconnect()
1438 mDocument = nullptr;
1441 virtual void EnsureFresh() MOZ_OVERRIDE;
1443 protected:
1444 nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it
1445 // dies.
1448 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1449 : mDocument(aDocument)
1451 NS_ASSERTION(mDocument, "Must have document!");
1454 void
1455 nsDOMStyleSheetSetList::EnsureFresh()
1457 MOZ_ASSERT(NS_IsMainThread());
1459 mNames.Clear();
1461 if (!mDocument) {
1462 return; // Spec says "no exceptions", and we have no style sets if we have
1463 // no document, for sure
1466 int32_t count = mDocument->GetNumberOfStyleSheets();
1467 nsAutoString title;
1468 for (int32_t index = 0; index < count; index++) {
1469 nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
1470 NS_ASSERTION(sheet, "Null sheet in sheet list!");
1471 sheet->GetTitle(title);
1472 if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1473 return;
1478 // ==================================================================
1479 nsIDocument::SelectorCache::SelectorCache()
1480 : nsExpirationTracker<SelectorCacheKey, 4>(1000) { }
1482 // CacheList takes ownership of aSelectorList.
1483 void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector,
1484 nsCSSSelectorList* aSelectorList)
1486 SelectorCacheKey* key = new SelectorCacheKey(aSelector);
1487 mTable.Put(key->mKey, aSelectorList);
1488 AddObject(key);
1491 class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable
1493 public:
1494 explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete)
1495 : mSelector(aToDelete)
1497 MOZ_COUNT_CTOR(SelectorCacheKeyDeleter);
1500 protected:
1501 ~SelectorCacheKeyDeleter()
1503 MOZ_COUNT_DTOR(SelectorCacheKeyDeleter);
1506 public:
1507 NS_IMETHOD Run()
1509 return NS_OK;
1512 private:
1513 nsAutoPtr<SelectorCacheKey> mSelector;
1516 void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
1518 RemoveObject(aSelector);
1519 mTable.Remove(aSelector->mKey);
1520 nsCOMPtr<nsIRunnable> runnable = new SelectorCacheKeyDeleter(aSelector);
1521 NS_DispatchToCurrentThread(runnable);
1525 struct nsIDocument::FrameRequest
1527 FrameRequest(const FrameRequestCallbackHolder& aCallback,
1528 int32_t aHandle) :
1529 mCallback(aCallback),
1530 mHandle(aHandle)
1533 // Conversion operator so that we can append these to a
1534 // FrameRequestCallbackList
1535 operator const FrameRequestCallbackHolder& () const {
1536 return mCallback;
1539 // Comparator operators to allow RemoveElementSorted with an
1540 // integer argument on arrays of FrameRequest
1541 bool operator==(int32_t aHandle) const {
1542 return mHandle == aHandle;
1544 bool operator<(int32_t aHandle) const {
1545 return mHandle < aHandle;
1548 FrameRequestCallbackHolder mCallback;
1549 int32_t mHandle;
1552 static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
1554 // ==================================================================
1555 // =
1556 // ==================================================================
1557 nsIDocument::nsIDocument()
1558 : nsINode(nullNodeInfo),
1559 mReferrerPolicySet(false),
1560 mReferrerPolicy(mozilla::net::RP_Default),
1561 mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
1562 mNodeInfoManager(nullptr),
1563 mCompatMode(eCompatibility_FullStandards),
1564 mVisibilityState(dom::VisibilityState::Hidden),
1565 mIsInitialDocumentInWindow(false),
1566 mMayStartLayout(true),
1567 mVisible(true),
1568 mRemovedFromDocShell(false),
1569 // mAllowDNSPrefetch starts true, so that we can always reliably && it
1570 // with various values that might disable it. Since we never prefetch
1571 // unless we get a window, and in that case the docshell value will get
1572 // &&-ed in, this is safe.
1573 mAllowDNSPrefetch(true),
1574 mIsBeingUsedAsImage(false),
1575 mHasLinksToUpdate(false),
1576 mPartID(0),
1577 mDidFireDOMContentLoaded(true)
1579 SetInDocument();
1581 PR_INIT_CLIST(&mDOMMediaQueryLists);
1584 // NOTE! nsDocument::operator new() zeroes out all members, so don't
1585 // bother initializing members to 0.
1587 nsDocument::nsDocument(const char* aContentType)
1588 : nsIDocument()
1589 , mAnimatingImages(true)
1590 , mViewportType(Unknown)
1592 SetContentTypeInternal(nsDependentCString(aContentType));
1594 #ifdef PR_LOGGING
1595 if (!gDocumentLeakPRLog)
1596 gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
1598 if (gDocumentLeakPRLog)
1599 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1600 ("DOCUMENT %p created", this));
1602 if (!gCspPRLog)
1603 gCspPRLog = PR_NewLogModule("CSP");
1604 #endif
1606 // Start out mLastStyleSheetSet as null, per spec
1607 SetDOMStringToNull(mLastStyleSheetSet);
1609 if (!sProcessingStack) {
1610 sProcessingStack.emplace();
1611 // Add the base queue sentinel to the processing stack.
1612 sProcessingStack->AppendElement((CustomElementData*) nullptr);
1616 static PLDHashOperator
1617 ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
1619 if (aBoxObject) {
1620 aBoxObject->Clear();
1622 return PL_DHASH_NEXT;
1625 nsIDocument::~nsIDocument()
1627 NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
1628 "must not have media query lists left");
1630 if (mNodeInfoManager) {
1631 mNodeInfoManager->DropDocumentReference();
1636 nsDocument::~nsDocument()
1638 #ifdef PR_LOGGING
1639 if (gDocumentLeakPRLog)
1640 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1641 ("DOCUMENT %p destroyed", this));
1642 #endif
1644 NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1646 // Note: This assert is only non-fatal because mochitest-bc triggers
1647 // it... as well as the preceding assert about !mIsShowing.
1648 NS_ASSERTION(!mObservingAppThemeChanged,
1649 "Document leaked to shutdown, then the observer service dropped "
1650 "its ref to us so we were able to go away.");
1652 if (IsTopLevelContentDocument()) {
1653 //don't report for about: pages
1654 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1655 nsCOMPtr<nsIURI> uri;
1656 principal->GetURI(getter_AddRefs(uri));
1657 bool isAboutScheme = true;
1658 if (uri) {
1659 uri->SchemeIs("about", &isAboutScheme);
1662 if (!isAboutScheme) {
1663 // Record the page load
1664 uint32_t pageLoaded = 1;
1665 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1666 // Record the mixed content status of the docshell in Telemetry
1667 enum {
1668 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1669 MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
1670 MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
1671 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
1674 bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1675 bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1677 bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1678 bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1680 bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1681 bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1683 uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1684 if (hasMixedDisplay && hasMixedActive) {
1685 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1686 } else if (hasMixedActive){
1687 mixedContentLevel = MIXED_ACTIVE_CONTENT;
1688 } else if (hasMixedDisplay) {
1689 mixedContentLevel = MIXED_DISPLAY_CONTENT;
1691 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1695 mInDestructor = true;
1696 mInUnlinkOrDeletion = true;
1698 mRegistry = nullptr;
1700 mozilla::DropJSObjects(this);
1702 // Clear mObservers to keep it in sync with the mutationobserver list
1703 mObservers.Clear();
1705 if (mStyleSheetSetList) {
1706 mStyleSheetSetList->Disconnect();
1709 if (mAnimationController) {
1710 mAnimationController->Disconnect();
1713 mParentDocument = nullptr;
1715 // Kill the subdocument map, doing this will release its strong
1716 // references, if any.
1717 if (mSubDocuments) {
1718 PL_DHashTableDestroy(mSubDocuments);
1720 mSubDocuments = nullptr;
1723 // Destroy link map now so we don't waste time removing
1724 // links one by one
1725 DestroyElementMaps();
1727 nsAutoScriptBlocker scriptBlocker;
1729 int32_t indx; // must be signed
1730 uint32_t count = mChildren.ChildCount();
1731 for (indx = int32_t(count) - 1; indx >= 0; --indx) {
1732 mChildren.ChildAt(indx)->UnbindFromTree();
1733 mChildren.RemoveChildAt(indx);
1735 mFirstChild = nullptr;
1736 mCachedRootElement = nullptr;
1738 // Let the stylesheets know we're going away
1739 indx = mStyleSheets.Count();
1740 while (--indx >= 0) {
1741 mStyleSheets[indx]->SetOwningDocument(nullptr);
1743 if (mAttrStyleSheet) {
1744 mAttrStyleSheet->SetOwningDocument(nullptr);
1746 // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
1748 if (mListenerManager) {
1749 mListenerManager->Disconnect();
1750 UnsetFlags(NODE_HAS_LISTENERMANAGER);
1753 if (mScriptLoader) {
1754 mScriptLoader->DropDocumentReference();
1757 if (mCSSLoader) {
1758 // Could be null here if Init() failed or if we have been unlinked.
1759 mCSSLoader->DropDocumentReference();
1762 if (mStyleImageLoader) {
1763 mStyleImageLoader->DropDocumentReference();
1766 delete mHeaderData;
1768 if (mBoxObjectTable) {
1769 mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
1770 delete mBoxObjectTable;
1773 mPendingTitleChangeEvent.Revoke();
1775 for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) {
1776 nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]);
1779 // We don't want to leave residual locks on images. Make sure we're in an
1780 // unlocked state, and then clear the table.
1781 SetImageLockingState(false);
1782 mImageTracker.Clear();
1784 mPlugins.Clear();
1787 NS_INTERFACE_TABLE_HEAD(nsDocument)
1788 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
1789 NS_INTERFACE_TABLE_BEGIN
1790 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1791 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1792 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1793 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument)
1794 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode)
1795 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
1796 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1797 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
1798 NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1799 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1800 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1801 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1802 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1803 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver)
1804 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator)
1805 NS_INTERFACE_TABLE_END
1806 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1807 NS_INTERFACE_MAP_END
1810 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1811 NS_IMETHODIMP_(MozExternalRefCountType)
1812 nsDocument::Release()
1814 NS_PRECONDITION(0 != mRefCnt, "dup release");
1815 NS_ASSERT_OWNINGTHREAD(nsDocument);
1816 nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1817 bool shouldDelete = false;
1818 nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1819 NS_LOG_RELEASE(this, count, "nsDocument");
1820 if (count == 0) {
1821 if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1822 mNeedsReleaseAfterStackRefCntRelease = true;
1823 NS_ADDREF_THIS();
1824 return mRefCnt.get();
1826 mRefCnt.incr(base);
1827 nsNodeUtils::LastRelease(this);
1828 mRefCnt.decr(base);
1829 if (shouldDelete) {
1830 mRefCnt.stabilizeForDeletion();
1831 DeleteCycleCollectable();
1834 return count;
1837 NS_IMETHODIMP_(void)
1838 nsDocument::DeleteCycleCollectable()
1840 delete this;
1843 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1844 if (Element::CanSkip(tmp, aRemovingAllowed)) {
1845 EventListenerManager* elm = tmp->GetExistingListenerManager();
1846 if (elm) {
1847 elm->MarkForCC();
1849 return true;
1851 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1853 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1854 return Element::CanSkipInCC(tmp);
1855 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1857 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1858 return Element::CanSkipThis(tmp);
1859 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1861 static PLDHashOperator
1862 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
1863 void *arg)
1865 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
1866 nsCycleCollectionTraversalCallback *cb =
1867 static_cast<nsCycleCollectionTraversalCallback*>(arg);
1869 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
1870 cb->NoteXPCOMChild(entry->mKey);
1871 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
1872 cb->NoteXPCOMChild(entry->mSubDocument);
1874 return PL_DHASH_NEXT;
1877 static PLDHashOperator
1878 RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
1879 void* aClosure)
1881 nsCycleCollectionTraversalCallback *cb =
1882 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
1884 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1885 "mRadioGroups entry->mSelectedRadioButton");
1886 cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton));
1888 uint32_t i, count = aData->mRadioButtons.Count();
1889 for (i = 0; i < count; ++i) {
1890 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1891 "mRadioGroups entry->mRadioButtons[i]");
1892 cb->NoteXPCOMChild(aData->mRadioButtons[i]);
1895 return PL_DHASH_NEXT;
1898 static PLDHashOperator
1899 BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg)
1901 nsCycleCollectionTraversalCallback *cb =
1902 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
1904 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
1905 cb->NoteXPCOMChild(boxObject);
1907 return PL_DHASH_NEXT;
1910 static PLDHashOperator
1911 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
1913 nsCycleCollectionTraversalCallback *cb =
1914 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
1915 aEntry->Traverse(cb);
1916 return PL_DHASH_NEXT;
1919 static const char* kNSURIs[] = {
1920 "([none])",
1921 "(xmlns)",
1922 "(xml)",
1923 "(xhtml)",
1924 "(XLink)",
1925 "(XSLT)",
1926 "(XBL)",
1927 "(MathML)",
1928 "(RDF)",
1929 "(XUL)"
1932 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1933 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1934 char name[512];
1935 nsAutoCString loadedAsData;
1936 if (tmp->IsLoadedAsData()) {
1937 loadedAsData.AssignLiteral("data");
1938 } else {
1939 loadedAsData.AssignLiteral("normal");
1941 uint32_t nsid = tmp->GetDefaultNamespaceID();
1942 nsAutoCString uri;
1943 if (tmp->mDocumentURI)
1944 tmp->mDocumentURI->GetSpec(uri);
1945 if (nsid < ArrayLength(kNSURIs)) {
1946 PR_snprintf(name, sizeof(name), "nsDocument %s %s %s",
1947 loadedAsData.get(), kNSURIs[nsid], uri.get());
1949 else {
1950 PR_snprintf(name, sizeof(name), "nsDocument %s %s",
1951 loadedAsData.get(), uri.get());
1953 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1955 else {
1956 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1959 // Always need to traverse script objects, so do that before we check
1960 // if we're uncollectable.
1961 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1963 if (!nsINode::Traverse(tmp, cb)) {
1964 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1967 tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
1969 tmp->mExternalResourceMap.Traverse(&cb);
1971 // Traverse the mChildren nsAttrAndChildArray.
1972 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1973 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1974 cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1977 // Traverse all nsIDocument pointer members.
1978 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1979 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1981 // Traverse all nsDocument nsCOMPtrs.
1982 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1983 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1984 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1985 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1986 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1987 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1988 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterDocument)
1989 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImportManager)
1991 tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
1993 // The boxobject for an element will only exist as long as it's in the
1994 // document, so we'll traverse the table here instead of from the element.
1995 if (tmp->mBoxObjectTable) {
1996 tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
1999 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
2000 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
2001 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator)
2002 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
2003 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
2004 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
2005 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
2006 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
2007 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
2008 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
2009 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
2010 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
2011 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationTimeline)
2012 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayerTracker)
2013 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
2014 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
2015 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
2016 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
2018 // Traverse all our nsCOMArrays.
2019 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
2020 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
2021 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
2023 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSubImportLinks)
2025 for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
2026 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
2027 cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
2030 // Traverse animation components
2031 if (tmp->mAnimationController) {
2032 tmp->mAnimationController->Traverse(&cb);
2035 if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
2036 PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
2039 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
2041 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
2042 nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb);
2045 // We own only the items in mDOMMediaQueryLists that have listeners;
2046 // this reference is managed by their AddListener and RemoveListener
2047 // methods.
2048 for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
2049 l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
2050 MediaQueryList *mql = static_cast<MediaQueryList*>(l);
2051 if (mql->HasListeners()) {
2052 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
2053 cb.NoteXPCOMChild(mql);
2056 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2058 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
2060 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
2061 if (tmp->PreservingWrapper()) {
2062 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
2064 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2065 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2068 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
2069 tmp->mInUnlinkOrDeletion = true;
2071 // Clear out our external resources
2072 tmp->mExternalResourceMap.Shutdown();
2074 nsAutoScriptBlocker scriptBlocker;
2076 nsINode::Unlink(tmp);
2078 // Unlink the mChildren nsAttrAndChildArray.
2079 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1;
2080 indx >= 0; --indx) {
2081 tmp->mChildren.ChildAt(indx)->UnbindFromTree();
2082 tmp->mChildren.RemoveChildAt(indx);
2084 tmp->mFirstChild = nullptr;
2086 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator)
2087 tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
2088 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
2089 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
2090 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
2091 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
2092 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
2093 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
2094 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
2095 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline)
2096 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayerTracker)
2097 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
2098 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
2099 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
2100 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
2101 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
2102 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks)
2104 tmp->mParentDocument = nullptr;
2106 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
2109 if (tmp->mBoxObjectTable) {
2110 tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
2111 delete tmp->mBoxObjectTable;
2112 tmp->mBoxObjectTable = nullptr;
2115 if (tmp->mListenerManager) {
2116 tmp->mListenerManager->Disconnect();
2117 tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
2118 tmp->mListenerManager = nullptr;
2121 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
2123 if (tmp->mStyleSheetSetList) {
2124 tmp->mStyleSheetSetList->Disconnect();
2125 tmp->mStyleSheetSetList = nullptr;
2128 if (tmp->mSubDocuments) {
2129 PL_DHashTableDestroy(tmp->mSubDocuments);
2130 tmp->mSubDocuments = nullptr;
2133 tmp->mFrameRequestCallbacks.Clear();
2135 tmp->mRadioGroups.Clear();
2137 // nsDocument has a pretty complex destructor, so we're going to
2138 // assume that *most* cycles you actually want to break somewhere
2139 // else, and not unlink an awful lot here.
2141 tmp->mIdentifierMap.Clear();
2142 tmp->mExpandoAndGeneration.Unlink();
2144 if (tmp->mAnimationController) {
2145 tmp->mAnimationController->Unlink();
2148 tmp->mPendingTitleChangeEvent.Revoke();
2150 if (tmp->mCSSLoader) {
2151 tmp->mCSSLoader->DropDocumentReference();
2152 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
2155 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
2156 nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]);
2159 // We own only the items in mDOMMediaQueryLists that have listeners;
2160 // this reference is managed by their AddListener and RemoveListener
2161 // methods.
2162 for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
2163 l != &tmp->mDOMMediaQueryLists; ) {
2164 PRCList *next = PR_NEXT_LINK(l);
2165 MediaQueryList *mql = static_cast<MediaQueryList*>(l);
2166 mql->RemoveAllListeners();
2167 l = next;
2170 tmp->mInUnlinkOrDeletion = false;
2171 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2173 static bool sPrefsInitialized = false;
2174 static uint32_t sOnloadDecodeLimit = 0;
2176 nsresult
2177 nsDocument::Init()
2179 if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
2180 return NS_ERROR_ALREADY_INITIALIZED;
2183 if (!sPrefsInitialized) {
2184 sPrefsInitialized = true;
2185 Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0);
2188 // Force initialization.
2189 nsINode::nsSlots* slots = Slots();
2191 // Prepend self as mutation-observer whether we need it or not (some
2192 // subclasses currently do, other don't). This is because the code in
2193 // nsNodeUtils always notifies the first observer first, expecting the
2194 // first observer to be the document.
2195 NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
2196 NS_ERROR_OUT_OF_MEMORY);
2199 mOnloadBlocker = new nsOnloadBlocker();
2200 mCSSLoader = new mozilla::css::Loader(this);
2201 // Assume we're not quirky, until we know otherwise
2202 mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2204 mStyleImageLoader = new mozilla::css::ImageLoader(this);
2206 mNodeInfoManager = new nsNodeInfoManager();
2207 nsresult rv = mNodeInfoManager->Init(this);
2208 NS_ENSURE_SUCCESS(rv, rv);
2210 // mNodeInfo keeps NodeInfoManager alive!
2211 mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2212 NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2213 NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
2214 "Bad NodeType in aNodeInfo");
2216 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2218 // If after creation the owner js global is not set for a document
2219 // we use the default compartment for this document, instead of creating
2220 // wrapper in some random compartment when the document is exposed to js
2221 // via some events.
2222 nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2223 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2224 mScopeObject = do_GetWeakReference(global);
2225 MOZ_ASSERT(mScopeObject);
2227 mScriptLoader = new nsScriptLoader(this);
2229 mozilla::HoldJSObjects(this);
2231 return NS_OK;
2234 void
2235 nsIDocument::DeleteAllProperties()
2237 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2238 PropertyTable(i)->DeleteAllProperties();
2242 void
2243 nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
2245 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2246 PropertyTable(i)->DeleteAllPropertiesFor(aNode);
2250 nsPropertyTable*
2251 nsIDocument::GetExtraPropertyTable(uint16_t aCategory)
2253 NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
2254 while (aCategory >= mExtraPropertyTables.Length() + 1) {
2255 mExtraPropertyTables.AppendElement(new nsPropertyTable());
2257 return mExtraPropertyTables[aCategory - 1];
2260 bool
2261 nsIDocument::IsVisibleConsideringAncestors() const
2263 const nsIDocument *parent = this;
2264 do {
2265 if (!parent->IsVisible()) {
2266 return false;
2268 } while ((parent = parent->GetParentDocument()));
2270 return true;
2273 void
2274 nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
2276 nsCOMPtr<nsIURI> uri;
2277 nsCOMPtr<nsIPrincipal> principal;
2278 if (aChannel) {
2279 // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2280 // nsScriptSecurityManager::GetChannelResultPrincipal.
2281 // Note: this should match nsDocShell::OnLoadingSite
2282 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2284 nsIScriptSecurityManager *securityManager =
2285 nsContentUtils::GetSecurityManager();
2286 if (securityManager) {
2287 securityManager->GetChannelResultPrincipal(aChannel,
2288 getter_AddRefs(principal));
2292 ResetToURI(uri, aLoadGroup, principal);
2294 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2295 if (bag) {
2296 nsCOMPtr<nsIURI> baseURI;
2297 bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2298 NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2299 if (baseURI) {
2300 mDocumentBaseURI = baseURI;
2301 mChromeXHRDocBaseURI = baseURI;
2305 mChannel = aChannel;
2308 void
2309 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
2310 nsIPrincipal* aPrincipal)
2312 NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
2314 #ifdef PR_LOGGING
2315 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2316 nsAutoCString spec;
2317 aURI->GetSpec(spec);
2318 PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
2320 #endif
2322 mSecurityInfo = nullptr;
2324 mDocumentLoadGroup = nullptr;
2326 // Delete references to sub-documents and kill the subdocument map,
2327 // if any. It holds strong references
2328 if (mSubDocuments) {
2329 PL_DHashTableDestroy(mSubDocuments);
2331 mSubDocuments = nullptr;
2334 // Destroy link map now so we don't waste time removing
2335 // links one by one
2336 DestroyElementMaps();
2338 bool oldVal = mInUnlinkOrDeletion;
2339 mInUnlinkOrDeletion = true;
2340 uint32_t count = mChildren.ChildCount();
2341 { // Scope for update
2342 MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true);
2343 for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
2344 nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
2346 nsIContent* previousSibling = content->GetPreviousSibling();
2348 if (nsINode::GetFirstChild() == content) {
2349 mFirstChild = content->GetNextSibling();
2351 mChildren.RemoveChildAt(i);
2352 nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
2353 content->UnbindFromTree();
2355 mCachedRootElement = nullptr;
2357 mInUnlinkOrDeletion = oldVal;
2359 if (!mMasterDocument) {
2360 // "When creating an import, use the registry of the master document."
2361 // Note: at this point the mMasterDocument is already set for imports
2362 // (and only for imports)
2363 mRegistry = nullptr;
2366 // Reset our stylesheets
2367 ResetStylesheetsToURI(aURI);
2369 // Release the listener manager
2370 if (mListenerManager) {
2371 mListenerManager->Disconnect();
2372 mListenerManager = nullptr;
2375 // Release the stylesheets list.
2376 mDOMStyleSheets = nullptr;
2378 // Release our principal after tearing down the document, rather than before.
2379 // This ensures that, during teardown, the document and the dying window (which
2380 // already nulled out its document pointer and cached the principal) have
2381 // matching principals.
2382 SetPrincipal(nullptr);
2384 // Clear the original URI so SetDocumentURI sets it.
2385 mOriginalURI = nullptr;
2387 SetDocumentURI(aURI);
2388 mChromeXHRDocURI = aURI;
2389 // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2390 // mDocumentURI.
2391 mDocumentBaseURI = nullptr;
2392 mChromeXHRDocBaseURI = nullptr;
2394 if (aLoadGroup) {
2395 mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2396 // there was an assertion here that aLoadGroup was not null. This
2397 // is no longer valid: nsDocShell::SetDocument does not create a
2398 // load group, and it works just fine
2400 // XXXbz what does "just fine" mean exactly? And given that there
2401 // is no nsDocShell::SetDocument, what is this talking about?
2404 mLastModified.Truncate();
2405 // XXXbz I guess we're assuming that the caller will either pass in
2406 // a channel with a useful type or call SetContentType?
2407 SetContentTypeInternal(EmptyCString());
2408 mContentLanguage.Truncate();
2409 mBaseTarget.Truncate();
2410 mReferrer.Truncate();
2412 mXMLDeclarationBits = 0;
2414 // Now get our new principal
2415 if (aPrincipal) {
2416 SetPrincipal(aPrincipal);
2417 } else {
2418 nsIScriptSecurityManager *securityManager =
2419 nsContentUtils::GetSecurityManager();
2420 if (securityManager) {
2421 nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2423 if (!loadContext && aLoadGroup) {
2424 nsCOMPtr<nsIInterfaceRequestor> cbs;
2425 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2426 loadContext = do_GetInterface(cbs);
2429 MOZ_ASSERT(loadContext,
2430 "must have a load context or pass in an explicit principal");
2432 nsCOMPtr<nsIPrincipal> principal;
2433 nsresult rv = securityManager->
2434 GetLoadContextCodebasePrincipal(mDocumentURI, loadContext,
2435 getter_AddRefs(principal));
2436 if (NS_SUCCEEDED(rv)) {
2437 SetPrincipal(principal);
2442 // Refresh the principal on the compartment.
2443 nsPIDOMWindow* win = GetInnerWindow();
2444 if (win) {
2445 win->RefreshCompartmentPrincipal();
2449 void
2450 nsDocument::RemoveDocStyleSheetsFromStyleSets()
2452 // The stylesheets should forget us
2453 int32_t indx = mStyleSheets.Count();
2454 while (--indx >= 0) {
2455 nsIStyleSheet* sheet = mStyleSheets[indx];
2456 sheet->SetOwningDocument(nullptr);
2458 if (sheet->IsApplicable()) {
2459 nsCOMPtr<nsIPresShell> shell = GetShell();
2460 if (shell) {
2461 shell->StyleSet()->RemoveDocStyleSheet(sheet);
2464 // XXX Tell observers?
2468 void
2469 nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType)
2471 // The stylesheets should forget us
2472 int32_t indx = aSheets.Count();
2473 while (--indx >= 0) {
2474 nsIStyleSheet* sheet = aSheets[indx];
2475 sheet->SetOwningDocument(nullptr);
2477 if (sheet->IsApplicable()) {
2478 nsCOMPtr<nsIPresShell> shell = GetShell();
2479 if (shell) {
2480 shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2484 // XXX Tell observers?
2489 void
2490 nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
2492 MOZ_ASSERT(aURI);
2494 mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
2495 RemoveDocStyleSheetsFromStyleSets();
2496 RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, nsStyleSet::eAgentSheet);
2497 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet);
2498 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet);
2499 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet);
2501 // Release all the sheets
2502 mStyleSheets.Clear();
2503 mOnDemandBuiltInUASheets.Clear();
2504 for (uint32_t i = 0; i < SheetTypeCount; ++i)
2505 mAdditionalSheets[i].Clear();
2507 // NOTE: We don't release the catalog sheets. It doesn't really matter
2508 // now, but it could in the future -- in which case not releasing them
2509 // is probably the right thing to do.
2511 // Now reset our inline style and attribute sheets.
2512 if (mAttrStyleSheet) {
2513 mAttrStyleSheet->Reset();
2514 mAttrStyleSheet->SetOwningDocument(this);
2515 } else {
2516 mAttrStyleSheet = new nsHTMLStyleSheet(this);
2519 if (!mStyleAttrStyleSheet) {
2520 mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2523 if (!mSVGAttrAnimationRuleProcessor) {
2524 mSVGAttrAnimationRuleProcessor =
2525 new mozilla::SVGAttrAnimationRuleProcessor();
2528 // Now set up our style sets
2529 nsCOMPtr<nsIPresShell> shell = GetShell();
2530 if (shell) {
2531 FillStyleSet(shell->StyleSet());
2535 static bool
2536 AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
2538 nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
2539 styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet);
2540 return true;
2543 static void
2544 AppendSheetsToStyleSet(nsStyleSet* aStyleSet,
2545 const nsCOMArray<nsIStyleSheet>& aSheets,
2546 nsStyleSet::sheetType aType)
2548 for (int32_t i = aSheets.Count() - 1; i >= 0; --i) {
2549 aStyleSet->AppendStyleSheet(aType, aSheets[i]);
2554 void
2555 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
2557 NS_PRECONDITION(aStyleSet, "Must have a style set");
2558 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
2559 "Style set already has document sheets?");
2561 // We could consider moving this to nsStyleSet::Init, to match its
2562 // handling of the eAnimationSheet and eTransitionSheet levels.
2563 aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet);
2564 aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet);
2566 int32_t i;
2567 for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
2568 nsIStyleSheet* sheet = mStyleSheets[i];
2569 if (sheet->IsApplicable()) {
2570 aStyleSet->AddDocStyleSheet(sheet, this);
2574 nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
2575 if (sheetService) {
2576 sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet,
2577 aStyleSet);
2580 // Iterate backwards to maintain order
2581 for (i = mOnDemandBuiltInUASheets.Count() - 1; i >= 0; --i) {
2582 nsIStyleSheet* sheet = mOnDemandBuiltInUASheets[i];
2583 if (sheet->IsApplicable()) {
2584 aStyleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
2588 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2589 nsStyleSet::eAgentSheet);
2590 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2591 nsStyleSet::eUserSheet);
2592 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2593 nsStyleSet::eDocSheet);
2596 static void
2597 WarnIfSandboxIneffective(nsIDocShell* aDocShell,
2598 uint32_t aSandboxFlags,
2599 nsIChannel* aChannel)
2601 // If the document is sandboxed (via the HTML5 iframe sandbox
2602 // attribute) and both the allow-scripts and allow-same-origin
2603 // keywords are supplied, the sandboxed document can call into its
2604 // parent document and remove its sandboxing entirely - we print a
2605 // warning to the web console in this case.
2606 if (aSandboxFlags & SANDBOXED_NAVIGATION &&
2607 !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
2608 !(aSandboxFlags & SANDBOXED_ORIGIN)) {
2609 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2610 aDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2611 nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentAsItem);
2612 if (!parentDocShell) {
2613 return;
2616 // Don't warn if our parent is not the top-level document.
2617 nsCOMPtr<nsIDocShellTreeItem> grandParentAsItem;
2618 parentDocShell->GetSameTypeParent(getter_AddRefs(grandParentAsItem));
2619 if (grandParentAsItem) {
2620 return;
2623 nsCOMPtr<nsIChannel> parentChannel;
2624 parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
2625 if (!parentChannel) {
2626 return;
2628 nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
2629 if (NS_FAILED(rv)) {
2630 return;
2633 nsCOMPtr<nsIDocument> parentDocument = do_GetInterface(parentDocShell);
2634 nsCOMPtr<nsIURI> iframeUri;
2635 parentChannel->GetURI(getter_AddRefs(iframeUri));
2636 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2637 NS_LITERAL_CSTRING("Iframe Sandbox"),
2638 parentDocument,
2639 nsContentUtils::eSECURITY_PROPERTIES,
2640 "BothAllowScriptsAndSameOriginPresent",
2641 nullptr, 0, iframeUri);
2645 nsresult
2646 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2647 nsILoadGroup* aLoadGroup,
2648 nsISupports* aContainer,
2649 nsIStreamListener **aDocListener,
2650 bool aReset, nsIContentSink* aSink)
2652 #ifdef PR_LOGGING
2653 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2654 nsCOMPtr<nsIURI> uri;
2655 aChannel->GetURI(getter_AddRefs(uri));
2656 nsAutoCString spec;
2657 if (uri)
2658 uri->GetSpec(spec);
2659 PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get());
2661 #endif
2663 #ifdef DEBUG
2665 uint32_t appId;
2666 nsresult rv = NodePrincipal()->GetAppId(&appId);
2667 NS_ENSURE_SUCCESS(rv, rv);
2668 MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2669 "Document should never have UNKNOWN_APP_ID");
2671 #endif
2673 MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2674 "Bad readyState");
2675 SetReadyStateInternal(READYSTATE_LOADING);
2677 if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2678 mLoadedAsData = true;
2679 // We need to disable script & style loading in this case.
2680 // We leave them disabled even in EndLoad(), and let anyone
2681 // who puts the document on display to worry about enabling.
2683 // Do not load/process scripts when loading as data
2684 ScriptLoader()->SetEnabled(false);
2686 // styles
2687 CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
2688 } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2689 // Allow CSS, but not scripts
2690 ScriptLoader()->SetEnabled(false);
2693 mMayStartLayout = false;
2695 mHaveInputEncoding = true;
2697 if (aReset) {
2698 Reset(aChannel, aLoadGroup);
2701 nsAutoCString contentType;
2702 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2703 if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2704 NS_LITERAL_STRING("contentType"), contentType))) ||
2705 NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2706 // XXX this is only necessary for viewsource:
2707 nsACString::const_iterator start, end, semicolon;
2708 contentType.BeginReading(start);
2709 contentType.EndReading(end);
2710 semicolon = start;
2711 FindCharInReadable(';', semicolon, end);
2712 SetContentTypeInternal(Substring(start, semicolon));
2715 RetrieveRelevantHeaders(aChannel);
2717 mChannel = aChannel;
2718 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2719 if (inStrmChan) {
2720 bool isSrcdocChannel;
2721 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2722 if (isSrcdocChannel) {
2723 mIsSrcdocDocument = true;
2727 // If this document is being loaded by a docshell, copy its sandbox flags
2728 // to the document. These are immutable after being set here.
2729 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2731 if (docShell) {
2732 nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2733 NS_ENSURE_SUCCESS(rv, rv);
2734 WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
2737 // If this is not a data document, set CSP.
2738 if (!mLoadedAsData) {
2739 nsresult rv = InitCSP(aChannel);
2740 NS_ENSURE_SUCCESS(rv, rv);
2743 return NS_OK;
2746 void
2747 CSPErrorQueue::Add(const char* aMessageName)
2749 mErrors.AppendElement(aMessageName);
2752 void
2753 CSPErrorQueue::Flush(nsIDocument* aDocument)
2755 for (uint32_t i = 0; i < mErrors.Length(); i++) {
2756 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2757 NS_LITERAL_CSTRING("CSP"), aDocument,
2758 nsContentUtils::eSECURITY_PROPERTIES,
2759 mErrors[i]);
2761 mErrors.Clear();
2764 void
2765 nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
2767 for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2768 nsAutoString messageTag;
2769 aMessages[i]->GetTag(messageTag);
2771 nsAutoString category;
2772 aMessages[i]->GetCategory(category);
2774 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2775 NS_ConvertUTF16toUTF8(category),
2776 this, nsContentUtils::eSECURITY_PROPERTIES,
2777 NS_ConvertUTF16toUTF8(messageTag).get());
2781 static nsresult
2782 AppendCSPFromHeader(nsIContentSecurityPolicy* csp,
2783 const nsAString& aHeaderValue,
2784 bool aReportOnly)
2786 // Need to tokenize the header value since multiple headers could be
2787 // concatenated into one comma-separated list of policies.
2788 // See RFC2616 section 4.2 (last paragraph)
2789 nsresult rv = NS_OK;
2790 nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
2791 while (tokenizer.hasMoreTokens()) {
2792 const nsSubstring& policy = tokenizer.nextToken();
2793 rv = csp->AppendPolicy(policy, aReportOnly);
2794 NS_ENSURE_SUCCESS(rv, rv);
2795 #ifdef PR_LOGGING
2797 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2798 ("CSP refined with policy: \"%s\"",
2799 NS_ConvertUTF16toUTF8(policy).get()));
2801 #endif
2803 return NS_OK;
2806 bool
2807 nsDocument::IsLoopDocument(nsIChannel *aChannel)
2809 nsCOMPtr<nsIURI> chanURI;
2810 nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(chanURI));
2811 NS_ENSURE_SUCCESS(rv, false);
2813 bool isAbout = false;
2814 bool isLoop = false;
2815 rv = chanURI->SchemeIs("about", &isAbout);
2816 NS_ENSURE_SUCCESS(rv, false);
2817 if (isAbout) {
2818 nsCOMPtr<nsIURI> loopURI;
2819 rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
2820 NS_ENSURE_SUCCESS(rv, false);
2821 rv = chanURI->EqualsExceptRef(loopURI, &isLoop);
2822 NS_ENSURE_SUCCESS(rv, false);
2823 if (!isLoop) {
2824 rv = NS_NewURI(getter_AddRefs(loopURI), "about:looppanel");
2825 NS_ENSURE_SUCCESS(rv, false);
2826 rv = chanURI->EqualsExceptRef(loopURI, &isLoop);
2827 NS_ENSURE_SUCCESS(rv, false);
2830 return isLoop;
2833 nsresult
2834 nsDocument::InitCSP(nsIChannel* aChannel)
2836 nsCOMPtr<nsIContentSecurityPolicy> csp;
2837 if (!CSPService::sCSPEnabled) {
2838 #ifdef PR_LOGGING
2839 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2840 ("CSP is disabled, skipping CSP init for document %p", this));
2841 #endif
2842 return NS_OK;
2845 nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2847 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
2848 if (httpChannel) {
2849 httpChannel->GetResponseHeader(
2850 NS_LITERAL_CSTRING("content-security-policy"),
2851 tCspHeaderValue);
2853 httpChannel->GetResponseHeader(
2854 NS_LITERAL_CSTRING("content-security-policy-report-only"),
2855 tCspROHeaderValue);
2857 NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2858 NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2860 // Figure out if we need to apply an app default CSP or a CSP from an app manifest
2861 nsIPrincipal* principal = NodePrincipal();
2863 uint16_t appStatus = principal->GetAppStatus();
2864 bool applyAppDefaultCSP = false;
2865 bool applyAppManifestCSP = false;
2867 nsAutoString appManifestCSP;
2868 nsAutoString appDefaultCSP;
2869 if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
2870 nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
2871 if (appsService) {
2872 uint32_t appId = 0;
2873 if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
2874 appsService->GetManifestCSPByLocalId(appId, appManifestCSP);
2875 if (!appManifestCSP.IsEmpty()) {
2876 applyAppManifestCSP = true;
2878 appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP);
2879 if (!appDefaultCSP.IsEmpty()) {
2880 applyAppDefaultCSP = true;
2886 // Check if this is part of the Loop/Hello service
2887 bool applyLoopCSP = IsLoopDocument(aChannel);
2889 // If there's no CSP to apply, go ahead and return early
2890 if (!applyAppDefaultCSP &&
2891 !applyAppManifestCSP &&
2892 !applyLoopCSP &&
2893 cspHeaderValue.IsEmpty() &&
2894 cspROHeaderValue.IsEmpty()) {
2895 #ifdef PR_LOGGING
2896 nsCOMPtr<nsIURI> chanURI;
2897 aChannel->GetURI(getter_AddRefs(chanURI));
2898 nsAutoCString aspec;
2899 chanURI->GetAsciiSpec(aspec);
2900 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2901 ("no CSP for document, %s, %s",
2902 aspec.get(),
2903 applyAppDefaultCSP ? "is app" : "not an app"));
2904 #endif
2905 return NS_OK;
2908 #ifdef PR_LOGGING
2909 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
2910 #endif
2912 nsresult rv;
2914 // If Document is an app check to see if we already set CSP and return early
2915 // if that is indeed the case.
2917 // In general (see bug 947831), we should not be setting CSP on a principal
2918 // that aliases another document. For non-app code this is not a problem
2919 // since we only share the underlying principal with nested browsing
2920 // contexts for which a header cannot be set (e.g., about:blank and
2921 // about:srcodoc iframes) and thus won't try to set the CSP again. This
2922 // check ensures that we do not try to set CSP for an app.
2923 if (applyAppDefaultCSP || applyAppManifestCSP) {
2924 nsCOMPtr<nsIContentSecurityPolicy> csp;
2925 rv = principal->GetCsp(getter_AddRefs(csp));
2926 NS_ENSURE_SUCCESS(rv, rv);
2928 if (csp) {
2929 #ifdef PR_LOGGING
2930 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s",
2931 "This document is sharing principal with another document.",
2932 "Since the document is an app, CSP was already set.",
2933 "Skipping attempt to set CSP."));
2934 #endif
2935 return NS_OK;
2939 csp = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
2941 if (NS_FAILED(rv)) {
2942 #ifdef PR_LOGGING
2943 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
2944 #endif
2945 return rv;
2948 // used as a "self" identifier for the CSP.
2949 nsCOMPtr<nsIURI> selfURI;
2950 aChannel->GetURI(getter_AddRefs(selfURI));
2952 // Store the request context for violation reports
2953 csp->SetRequestContext(nullptr, nullptr, aChannel);
2955 // ----- if the doc is an app and we want a default CSP, apply it.
2956 if (applyAppDefaultCSP) {
2957 csp->AppendPolicy(appDefaultCSP, false);
2960 // ----- if the doc is an app and specifies a CSP in its manifest, apply it.
2961 if (applyAppManifestCSP) {
2962 csp->AppendPolicy(appManifestCSP, false);
2965 // ----- if the doc is part of Loop, apply the loop CSP
2966 if (applyLoopCSP) {
2967 nsAdoptingString loopCSP;
2968 loopCSP = Preferences::GetString("loop.CSP");
2969 NS_ASSERTION(loopCSP, "Missing loop.CSP preference");
2970 // If the pref has been removed, we continue without setting a CSP
2971 if (loopCSP) {
2972 csp->AppendPolicy(loopCSP, false);
2976 // ----- if there's a full-strength CSP header, apply it.
2977 if (!cspHeaderValue.IsEmpty()) {
2978 rv = AppendCSPFromHeader(csp, cspHeaderValue, false);
2979 NS_ENSURE_SUCCESS(rv, rv);
2982 // ----- if there's a report-only CSP header, apply it.
2983 if (!cspROHeaderValue.IsEmpty()) {
2984 rv = AppendCSPFromHeader(csp, cspROHeaderValue, true);
2985 NS_ENSURE_SUCCESS(rv, rv);
2988 // ----- Enforce frame-ancestor policy on any applied policies
2989 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2990 if (docShell) {
2991 bool safeAncestry = false;
2993 // PermitsAncestry sends violation reports when necessary
2994 rv = csp->PermitsAncestry(docShell, &safeAncestry);
2996 if (NS_FAILED(rv) || !safeAncestry) {
2997 #ifdef PR_LOGGING
2998 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2999 ("CSP doesn't like frame's ancestry, not loading."));
3000 #endif
3001 // stop! ERROR page!
3002 aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
3006 // ----- Set up any Referrer Policy specified by CSP
3007 bool hasReferrerPolicy = false;
3008 uint32_t referrerPolicy = mozilla::net::RP_Default;
3009 rv = csp->GetReferrerPolicy(&referrerPolicy, &hasReferrerPolicy);
3010 NS_ENSURE_SUCCESS(rv, rv);
3011 if (hasReferrerPolicy) {
3012 // Referrer policy spec (section 6.1) says that once the referrer policy
3013 // is set, any future attempts to change it result in No-Referrer.
3014 if (!mReferrerPolicySet) {
3015 mReferrerPolicy = static_cast<ReferrerPolicy>(referrerPolicy);
3016 mReferrerPolicySet = true;
3017 } else if (mReferrerPolicy != referrerPolicy) {
3018 mReferrerPolicy = mozilla::net::RP_No_Referrer;
3019 #ifdef PR_LOGGING
3021 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s",
3022 "CSP wants to set referrer, but nsDocument"
3023 "already has it set. No referrers will be sent"));
3025 #endif
3028 // Referrer Policy is set separately for the speculative parser in
3029 // nsHTMLDocument::StartDocumentLoad() so there's nothing to do here for
3030 // speculative loads.
3033 rv = principal->SetCsp(csp);
3034 NS_ENSURE_SUCCESS(rv, rv);
3035 #ifdef PR_LOGGING
3036 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
3037 ("Inserted CSP into principal %p", principal));
3038 #endif
3040 return NS_OK;
3043 void
3044 nsDocument::StopDocumentLoad()
3046 if (mParser) {
3047 mParserAborted = true;
3048 mParser->Terminate();
3052 void
3053 nsDocument::SetDocumentURI(nsIURI* aURI)
3055 nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
3056 mDocumentURI = NS_TryToMakeImmutable(aURI);
3057 nsIURI* newBase = GetDocBaseURI();
3059 bool equalBases = false;
3060 // Changing just the ref of a URI does not change how relative URIs would
3061 // resolve wrt to it, so we can treat the bases as equal as long as they're
3062 // equal ignoring the ref.
3063 if (oldBase && newBase) {
3064 oldBase->EqualsExceptRef(newBase, &equalBases);
3066 else {
3067 equalBases = !oldBase && !newBase;
3070 // If this is the first time we're setting the document's URI, set the
3071 // document's original URI.
3072 if (!mOriginalURI)
3073 mOriginalURI = mDocumentURI;
3075 // If changing the document's URI changed the base URI of the document, we
3076 // need to refresh the hrefs of all the links on the page.
3077 if (!equalBases) {
3078 RefreshLinkHrefs();
3082 void
3083 nsDocument::SetChromeXHRDocURI(nsIURI* aURI)
3085 mChromeXHRDocURI = aURI;
3088 void
3089 nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI)
3091 mChromeXHRDocBaseURI = aURI;
3094 NS_IMETHODIMP
3095 nsDocument::GetLastModified(nsAString& aLastModified)
3097 nsIDocument::GetLastModified(aLastModified);
3098 return NS_OK;
3101 void
3102 nsIDocument::GetLastModified(nsAString& aLastModified) const
3104 if (!mLastModified.IsEmpty()) {
3105 aLastModified.Assign(mLastModified);
3106 } else {
3107 // If we for whatever reason failed to find the last modified time
3108 // (or even the current time), fall back to what NS4.x returned.
3109 aLastModified.AssignLiteral(MOZ_UTF16("01/01/1970 00:00:00"));
3113 void
3114 nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
3116 MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
3117 "Only put elements that need to be exposed as document['name'] in "
3118 "the named table.");
3120 nsIdentifierMapEntry *entry =
3121 mIdentifierMap.PutEntry(nsDependentAtomString(aName));
3123 // Null for out-of-memory
3124 if (entry) {
3125 if (!entry->HasNameElement() &&
3126 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3127 ++mExpandoAndGeneration.generation;
3129 entry->AddNameElement(this, aElement);
3133 void
3134 nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
3136 // Speed up document teardown
3137 if (mIdentifierMap.Count() == 0)
3138 return;
3140 nsIdentifierMapEntry *entry =
3141 mIdentifierMap.GetEntry(nsDependentAtomString(aName));
3142 if (!entry) // Could be false if the element was anonymous, hence never added
3143 return;
3145 entry->RemoveNameElement(aElement);
3146 if (!entry->HasNameElement() &&
3147 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3148 ++mExpandoAndGeneration.generation;
3152 void
3153 nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
3155 nsIdentifierMapEntry *entry =
3156 mIdentifierMap.PutEntry(nsDependentAtomString(aId));
3158 if (entry) { /* True except on OOM */
3159 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3160 !entry->HasNameElement() &&
3161 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3162 ++mExpandoAndGeneration.generation;
3164 entry->AddIdElement(aElement);
3168 void
3169 nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
3171 NS_ASSERTION(aId, "huhwhatnow?");
3173 // Speed up document teardown
3174 if (mIdentifierMap.Count() == 0) {
3175 return;
3178 nsIdentifierMapEntry *entry =
3179 mIdentifierMap.GetEntry(nsDependentAtomString(aId));
3180 if (!entry) // Can be null for XML elements with changing ids.
3181 return;
3183 entry->RemoveIdElement(aElement);
3184 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3185 !entry->HasNameElement() &&
3186 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3187 ++mExpandoAndGeneration.generation;
3189 if (entry->IsEmpty()) {
3190 mIdentifierMap.RawRemoveEntry(entry);
3194 nsIPrincipal*
3195 nsDocument::GetPrincipal()
3197 return NodePrincipal();
3200 extern bool sDisablePrefetchHTTPSPref;
3202 void
3203 nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
3205 if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3206 nsCOMPtr<nsIURI> uri;
3207 aNewPrincipal->GetURI(getter_AddRefs(uri));
3208 bool isHTTPS;
3209 if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
3210 isHTTPS) {
3211 mAllowDNSPrefetch = false;
3214 mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3217 NS_IMETHODIMP
3218 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
3220 NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3222 return NS_OK;
3225 NS_IMETHODIMP
3226 nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
3228 mApplicationCache = aApplicationCache;
3230 return NS_OK;
3233 NS_IMETHODIMP
3234 nsDocument::GetContentType(nsAString& aContentType)
3236 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3238 return NS_OK;
3241 void
3242 nsDocument::SetContentType(const nsAString& aContentType)
3244 NS_ASSERTION(GetContentTypeInternal().IsEmpty() ||
3245 GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)),
3246 "Do you really want to change the content-type?");
3248 SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3251 nsresult
3252 nsDocument::GetAllowPlugins(bool * aAllowPlugins)
3254 // First, we ask our docshell if it allows plugins.
3255 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3257 if (docShell) {
3258 docShell->GetAllowPlugins(aAllowPlugins);
3260 // If the docshell allows plugins, we check whether
3261 // we are sandboxed and plugins should not be allowed.
3262 if (*aAllowPlugins)
3263 *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS);
3266 return NS_OK;
3269 already_AddRefed<UndoManager>
3270 nsDocument::GetUndoManager()
3272 Element* rootElement = GetRootElement();
3273 if (!rootElement) {
3274 return nullptr;
3277 if (!mUndoManager) {
3278 mUndoManager = new UndoManager(rootElement);
3281 nsRefPtr<UndoManager> undoManager = mUndoManager;
3282 return undoManager.forget();
3285 bool
3286 nsDocument::IsWebAnimationsEnabled(JSContext* /*unused*/, JSObject* /*unused*/)
3288 MOZ_ASSERT(NS_IsMainThread());
3290 return nsContentUtils::IsCallerChrome() ||
3291 Preferences::GetBool("dom.animations-api.core.enabled");
3294 AnimationTimeline*
3295 nsDocument::Timeline()
3297 if (!mAnimationTimeline) {
3298 mAnimationTimeline = new AnimationTimeline(this);
3301 return mAnimationTimeline;
3304 /* Return true if the document is in the focused top-level window, and is an
3305 * ancestor of the focused DOMWindow. */
3306 NS_IMETHODIMP
3307 nsDocument::HasFocus(bool* aResult)
3309 ErrorResult rv;
3310 *aResult = nsIDocument::HasFocus(rv);
3311 return rv.ErrorCode();
3314 bool
3315 nsIDocument::HasFocus(ErrorResult& rv) const
3317 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3318 if (!fm) {
3319 rv.Throw(NS_ERROR_NOT_AVAILABLE);
3320 return false;
3323 // Is there a focused DOMWindow?
3324 nsCOMPtr<nsIDOMWindow> focusedWindow;
3325 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3326 if (!focusedWindow) {
3327 return false;
3330 // Are we an ancestor of the focused DOMWindow?
3331 nsCOMPtr<nsIDOMDocument> domDocument;
3332 focusedWindow->GetDocument(getter_AddRefs(domDocument));
3333 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
3335 for (nsIDocument* currentDoc = document; currentDoc;
3336 currentDoc = currentDoc->GetParentDocument()) {
3337 if (currentDoc == this) {
3338 // Yes, we are an ancestor
3339 return true;
3343 return false;
3346 NS_IMETHODIMP
3347 nsDocument::GetReferrer(nsAString& aReferrer)
3349 nsIDocument::GetReferrer(aReferrer);
3350 return NS_OK;
3353 void
3354 nsIDocument::GetReferrer(nsAString& aReferrer) const
3356 if (mIsSrcdocDocument && mParentDocument)
3357 mParentDocument->GetReferrer(aReferrer);
3358 else
3359 CopyUTF8toUTF16(mReferrer, aReferrer);
3362 nsresult
3363 nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
3365 if (mIsSrcdocDocument) {
3366 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3367 if (inStrmChan) {
3368 return inStrmChan->GetSrcdocData(aSrcdocData);
3371 aSrcdocData = NullString();
3372 return NS_OK;
3375 NS_IMETHODIMP
3376 nsDocument::GetActiveElement(nsIDOMElement **aElement)
3378 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement()));
3379 el.forget(aElement);
3380 return NS_OK;
3383 Element*
3384 nsIDocument::GetActiveElement()
3386 // Get the focused element.
3387 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
3388 if (window) {
3389 nsCOMPtr<nsPIDOMWindow> focusedWindow;
3390 nsIContent* focusedContent =
3391 nsFocusManager::GetFocusedDescendant(window, false,
3392 getter_AddRefs(focusedWindow));
3393 // be safe and make sure the element is from this document
3394 if (focusedContent && focusedContent->OwnerDoc() == this) {
3395 if (focusedContent->ChromeOnlyAccess()) {
3396 focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
3398 if (focusedContent) {
3399 return focusedContent->AsElement();
3404 // No focused element anywhere in this document. Try to get the BODY.
3405 nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
3406 if (htmlDoc) {
3407 // Because of IE compatibility, return null when html document doesn't have
3408 // a body.
3409 return htmlDoc->GetBody();
3412 // If we couldn't get a BODY, return the root element.
3413 return GetDocumentElement();
3416 NS_IMETHODIMP
3417 nsDocument::GetCurrentScript(nsIDOMElement **aElement)
3419 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript()));
3420 el.forget(aElement);
3421 return NS_OK;
3424 Element*
3425 nsIDocument::GetCurrentScript()
3427 nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3428 return el;
3431 NS_IMETHODIMP
3432 nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
3434 Element* el = nsIDocument::ElementFromPoint(aX, aY);
3435 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
3436 retval.forget(aReturn);
3437 return NS_OK;
3440 Element*
3441 nsIDocument::ElementFromPoint(float aX, float aY)
3443 return ElementFromPointHelper(aX, aY, false, true);
3446 Element*
3447 nsDocument::ElementFromPointHelper(float aX, float aY,
3448 bool aIgnoreRootScrollFrame,
3449 bool aFlushLayout)
3451 // As per the the spec, we return null if either coord is negative
3452 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
3453 return nullptr;
3456 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
3457 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
3458 nsPoint pt(x, y);
3460 // Make sure the layout information we get is up-to-date, and
3461 // ensure we get a root frame (for everything but XUL)
3462 if (aFlushLayout)
3463 FlushPendingNotifications(Flush_Layout);
3465 nsIPresShell *ps = GetShell();
3466 if (!ps) {
3467 return nullptr;
3469 nsIFrame *rootFrame = ps->GetRootFrame();
3471 // XUL docs, unlike HTML, have no frame tree until everything's done loading
3472 if (!rootFrame) {
3473 return nullptr; // return null to premature XUL callers as a reminder to wait
3476 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
3477 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3478 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3479 if (!ptFrame) {
3480 return nullptr;
3483 nsIContent* elem = GetContentInThisDocument(ptFrame);
3484 if (elem && !elem->IsElement()) {
3485 elem = elem->GetParent();
3487 return elem ? elem->AsElement() : nullptr;
3490 nsresult
3491 nsDocument::NodesFromRectHelper(float aX, float aY,
3492 float aTopSize, float aRightSize,
3493 float aBottomSize, float aLeftSize,
3494 bool aIgnoreRootScrollFrame,
3495 bool aFlushLayout,
3496 nsIDOMNodeList** aReturn)
3498 NS_ENSURE_ARG_POINTER(aReturn);
3500 nsSimpleContentList* elements = new nsSimpleContentList(this);
3501 NS_ADDREF(elements);
3502 *aReturn = elements;
3504 // Following the same behavior of elementFromPoint,
3505 // we don't return anything if either coord is negative
3506 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
3507 return NS_OK;
3509 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3510 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3511 nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3512 nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3514 nsRect rect(x, y, w, h);
3516 // Make sure the layout information we get is up-to-date, and
3517 // ensure we get a root frame (for everything but XUL)
3518 if (aFlushLayout) {
3519 FlushPendingNotifications(Flush_Layout);
3522 nsIPresShell *ps = GetShell();
3523 NS_ENSURE_STATE(ps);
3524 nsIFrame *rootFrame = ps->GetRootFrame();
3526 // XUL docs, unlike HTML, have no frame tree until everything's done loading
3527 if (!rootFrame)
3528 return NS_OK; // return nothing to premature XUL callers as a reminder to wait
3530 nsAutoTArray<nsIFrame*,8> outFrames;
3531 nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
3532 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3533 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3535 // Used to filter out repeated elements in sequence.
3536 nsIContent* lastAdded = nullptr;
3538 for (uint32_t i = 0; i < outFrames.Length(); i++) {
3539 nsIContent* node = GetContentInThisDocument(outFrames[i]);
3541 if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
3542 // We have a node that isn't an element or a text node,
3543 // use its parent content instead.
3544 node = node->GetParent();
3546 if (node && node != lastAdded) {
3547 elements->AppendElement(node);
3548 lastAdded = node;
3552 return NS_OK;
3555 NS_IMETHODIMP
3556 nsDocument::GetElementsByClassName(const nsAString& aClasses,
3557 nsIDOMNodeList** aReturn)
3559 *aReturn = nsIDocument::GetElementsByClassName(aClasses).take();
3560 return NS_OK;
3563 already_AddRefed<nsContentList>
3564 nsIDocument::GetElementsByClassName(const nsAString& aClasses)
3566 return nsContentUtils::GetElementsByClassName(this, aClasses);
3569 NS_IMETHODIMP
3570 nsDocument::ReleaseCapture()
3572 nsIDocument::ReleaseCapture();
3573 return NS_OK;
3576 void
3577 nsIDocument::ReleaseCapture() const
3579 // only release the capture if the caller can access it. This prevents a
3580 // page from stopping a scrollbar grab for example.
3581 nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3582 if (node && nsContentUtils::CanCallerAccess(node)) {
3583 nsIPresShell::SetCapturingContent(nullptr, 0);
3587 already_AddRefed<nsIURI>
3588 nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
3590 nsCOMPtr<nsIURI> uri;
3591 if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3592 uri = mChromeXHRDocBaseURI;
3593 } else {
3594 uri = GetDocBaseURI();
3597 return uri.forget();
3600 nsresult
3601 nsDocument::SetBaseURI(nsIURI* aURI)
3603 if (!aURI && !mDocumentBaseURI) {
3604 return NS_OK;
3607 // Don't do anything if the URI wasn't actually changed.
3608 if (aURI && mDocumentBaseURI) {
3609 bool equalBases = false;
3610 mDocumentBaseURI->Equals(aURI, &equalBases);
3611 if (equalBases) {
3612 return NS_OK;
3616 // Check if CSP allows this base-uri
3617 nsCOMPtr<nsIContentSecurityPolicy> csp;
3618 nsresult rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
3619 NS_ENSURE_SUCCESS(rv, rv);
3620 if (csp && aURI) {
3621 bool permitsBaseURI = false;
3623 // base-uri is only enforced if explicitly defined in the
3624 // policy - do *not* consult default-src, see:
3625 // http://www.w3.org/TR/CSP2/#directive-default-src
3626 rv = csp->Permits(aURI, nsIContentSecurityPolicy::BASE_URI_DIRECTIVE,
3627 true, &permitsBaseURI);
3628 NS_ENSURE_SUCCESS(rv, rv);
3629 if (!permitsBaseURI) {
3630 return NS_OK;
3634 if (aURI) {
3635 mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
3636 } else {
3637 mDocumentBaseURI = nullptr;
3639 RefreshLinkHrefs();
3641 return NS_OK;
3644 void
3645 nsDocument::GetBaseTarget(nsAString &aBaseTarget)
3647 aBaseTarget = mBaseTarget;
3650 void
3651 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
3653 // XXX it would be a good idea to assert the sanity of the argument,
3654 // but before we figure out what to do about non-Encoding Standard
3655 // encodings in the charset menu and in mailnews, assertions are futile.
3656 if (!mCharacterSet.Equals(aCharSetID)) {
3657 if (mMasterDocument && !aCharSetID.EqualsLiteral("UTF-8")) {
3658 // Imports are always UTF-8
3659 return;
3661 mCharacterSet = aCharSetID;
3663 int32_t n = mCharSetObservers.Length();
3665 for (int32_t i = 0; i < n; i++) {
3666 nsIObserver* observer = mCharSetObservers.ElementAt(i);
3668 observer->Observe(static_cast<nsIDocument *>(this), "charset",
3669 NS_ConvertASCIItoUTF16(aCharSetID).get());
3674 nsresult
3675 nsDocument::AddCharSetObserver(nsIObserver* aObserver)
3677 NS_ENSURE_ARG_POINTER(aObserver);
3679 NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE);
3681 return NS_OK;
3684 void
3685 nsDocument::RemoveCharSetObserver(nsIObserver* aObserver)
3687 mCharSetObservers.RemoveElement(aObserver);
3690 void
3691 nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const
3693 aData.Truncate();
3694 const nsDocHeaderData* data = mHeaderData;
3695 while (data) {
3696 if (data->mField == aHeaderField) {
3697 aData = data->mData;
3699 break;
3701 data = data->mNext;
3705 void
3706 nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
3708 if (!aHeaderField) {
3709 NS_ERROR("null headerField");
3710 return;
3713 if (!mHeaderData) {
3714 if (!aData.IsEmpty()) { // don't bother storing empty string
3715 mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3718 else {
3719 nsDocHeaderData* data = mHeaderData;
3720 nsDocHeaderData** lastPtr = &mHeaderData;
3721 bool found = false;
3722 do { // look for existing and replace
3723 if (data->mField == aHeaderField) {
3724 if (!aData.IsEmpty()) {
3725 data->mData.Assign(aData);
3727 else { // don't store empty string
3728 *lastPtr = data->mNext;
3729 data->mNext = nullptr;
3730 delete data;
3732 found = true;
3734 break;
3736 lastPtr = &(data->mNext);
3737 data = *lastPtr;
3738 } while (data);
3740 if (!aData.IsEmpty() && !found) {
3741 // didn't find, append
3742 *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3746 if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3747 CopyUTF16toUTF8(aData, mContentLanguage);
3750 if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3751 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
3752 // spec.
3753 if (DOMStringIsNull(mLastStyleSheetSet)) {
3754 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
3755 // per spec. The idea here is that we're changing our preferred set and
3756 // that shouldn't change the value of lastStyleSheetSet. Also, we're
3757 // using the Internal version so we can update the CSSLoader and not have
3758 // to worry about null strings.
3759 EnableStyleSheetsForSetInternal(aData, true);
3763 if (aHeaderField == nsGkAtoms::refresh) {
3764 // We get into this code before we have a script global yet, so get to
3765 // our container via mDocumentContainer.
3766 nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3767 if (refresher) {
3768 // Note: using mDocumentURI instead of mBaseURI here, for consistency
3769 // (used to just use the current URI of our webnavigation, but that
3770 // should really be the same thing). Note that this code can run
3771 // before the current URI of the webnavigation has been updated, so we
3772 // can't assert equality here.
3773 refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3774 NS_ConvertUTF16toUTF8(aData));
3778 if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3779 mAllowDNSPrefetch) {
3780 // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3781 mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3784 if (aHeaderField == nsGkAtoms::viewport ||
3785 aHeaderField == nsGkAtoms::handheldFriendly ||
3786 aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3787 aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3788 aHeaderField == nsGkAtoms::viewport_initial_scale ||
3789 aHeaderField == nsGkAtoms::viewport_height ||
3790 aHeaderField == nsGkAtoms::viewport_width ||
3791 aHeaderField == nsGkAtoms::viewport_user_scalable) {
3792 mViewportType = Unknown;
3795 // Referrer policy spec says to ignore any empty referrer policies.
3796 // Disabled for now.
3797 if (false && aHeaderField == nsGkAtoms::referrer && !aData.IsEmpty()) {
3798 ReferrerPolicy policy = mozilla::net::ReferrerPolicyFromString(aData);
3800 // Referrer policy spec (section 6.1) says that once the referrer policy
3801 // is set, any future attempts to change it result in No-Referrer.
3802 if (!mReferrerPolicySet) {
3803 mReferrerPolicy = policy;
3804 mReferrerPolicySet = true;
3805 } else if (mReferrerPolicy != policy) {
3806 mReferrerPolicy = mozilla::net::RP_No_Referrer;
3811 void
3812 nsDocument::TryChannelCharset(nsIChannel *aChannel,
3813 int32_t& aCharsetSource,
3814 nsACString& aCharset,
3815 nsHtml5TreeOpExecutor* aExecutor)
3817 if (aChannel) {
3818 nsAutoCString charsetVal;
3819 nsresult rv = aChannel->GetContentCharset(charsetVal);
3820 if (NS_SUCCEEDED(rv)) {
3821 nsAutoCString preferred;
3822 if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) {
3823 aCharset = preferred;
3824 aCharsetSource = kCharsetFromChannel;
3825 return;
3826 } else if (aExecutor && !charsetVal.IsEmpty()) {
3827 aExecutor->ComplainAboutBogusProtocolCharset(this);
3833 already_AddRefed<nsIPresShell>
3834 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
3835 nsStyleSet* aStyleSet)
3837 // Don't add anything here. Add it to |doCreateShell| instead.
3838 // This exists so that subclasses can pass other values for the 4th
3839 // parameter some of the time.
3840 return doCreateShell(aContext, aViewManager, aStyleSet,
3841 eCompatibility_FullStandards);
3844 already_AddRefed<nsIPresShell>
3845 nsDocument::doCreateShell(nsPresContext* aContext,
3846 nsViewManager* aViewManager, nsStyleSet* aStyleSet,
3847 nsCompatibility aCompatMode)
3849 NS_ASSERTION(!mPresShell, "We have a presshell already!");
3851 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3853 FillStyleSet(aStyleSet);
3855 nsRefPtr<PresShell> shell = new PresShell;
3856 shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
3858 // Note: we don't hold a ref to the shell (it holds a ref to us)
3859 mPresShell = shell;
3861 // Make sure to never paint if we belong to an invisible DocShell.
3862 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3863 if (docShell && docShell->IsInvisible())
3864 shell->SetNeverPainting(true);
3866 mExternalResourceMap.ShowViewers();
3868 MaybeRescheduleAnimationFrameNotifications();
3870 return shell.forget();
3873 void
3874 nsDocument::MaybeRescheduleAnimationFrameNotifications()
3876 if (!mPresShell || !IsEventHandlingEnabled()) {
3877 // bail out for now, until one of those conditions changes
3878 return;
3881 nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver();
3882 if (!mFrameRequestCallbacks.IsEmpty()) {
3883 rd->ScheduleFrameRequestCallbacks(this);
3887 void
3888 nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
3890 aCallbacks.AppendElements(mFrameRequestCallbacks);
3891 mFrameRequestCallbacks.Clear();
3894 PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey,
3895 uint32_t aData,
3896 void* userArg)
3898 aKey->RequestDiscard();
3899 return PL_DHASH_NEXT;
3902 void
3903 nsDocument::DeleteShell()
3905 mExternalResourceMap.HideViewers();
3906 if (IsEventHandlingEnabled()) {
3907 RevokeAnimationFrameNotifications();
3910 // When our shell goes away, request that all our images be immediately
3911 // discarded, so we don't carry around decoded image data for a document we
3912 // no longer intend to paint.
3913 mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr);
3915 mPresShell = nullptr;
3918 void
3919 nsDocument::RevokeAnimationFrameNotifications()
3921 if (!mFrameRequestCallbacks.IsEmpty()) {
3922 mPresShell->GetPresContext()->RefreshDriver()->
3923 RevokeFrameRequestCallbacks(this);
3927 static void
3928 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
3930 SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
3932 NS_RELEASE(e->mKey);
3933 if (e->mSubDocument) {
3934 e->mSubDocument->SetParentDocument(nullptr);
3935 NS_RELEASE(e->mSubDocument);
3939 static bool
3940 SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key)
3942 SubDocMapEntry *e =
3943 const_cast<SubDocMapEntry *>
3944 (static_cast<const SubDocMapEntry *>(entry));
3946 e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
3947 NS_ADDREF(e->mKey);
3949 e->mSubDocument = nullptr;
3950 return true;
3953 nsresult
3954 nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
3956 NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
3958 if (!aSubDoc) {
3959 // aSubDoc is nullptr, remove the mapping
3961 if (mSubDocuments) {
3962 SubDocMapEntry *entry =
3963 static_cast<SubDocMapEntry*>
3964 (PL_DHashTableLookup(mSubDocuments, aElement));
3966 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3967 PL_DHashTableRawRemove(mSubDocuments, entry);
3970 } else {
3971 if (!mSubDocuments) {
3972 // Create a new hashtable
3974 static const PLDHashTableOps hash_table_ops =
3976 PL_DHashAllocTable,
3977 PL_DHashFreeTable,
3978 PL_DHashVoidPtrKeyStub,
3979 PL_DHashMatchEntryStub,
3980 PL_DHashMoveEntryStub,
3981 SubDocClearEntry,
3982 PL_DHashFinalizeStub,
3983 SubDocInitEntry
3986 mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr,
3987 sizeof(SubDocMapEntry));
3988 if (!mSubDocuments) {
3989 return NS_ERROR_OUT_OF_MEMORY;
3993 // Add a mapping to the hash table
3994 SubDocMapEntry *entry =
3995 static_cast<SubDocMapEntry*>
3996 (PL_DHashTableAdd(mSubDocuments, aElement));
3998 if (!entry) {
3999 return NS_ERROR_OUT_OF_MEMORY;
4002 if (entry->mSubDocument) {
4003 entry->mSubDocument->SetParentDocument(nullptr);
4005 // Release the old sub document
4006 NS_RELEASE(entry->mSubDocument);
4009 entry->mSubDocument = aSubDoc;
4010 NS_ADDREF(entry->mSubDocument);
4012 aSubDoc->SetParentDocument(this);
4015 return NS_OK;
4018 nsIDocument*
4019 nsDocument::GetSubDocumentFor(nsIContent *aContent) const
4021 if (mSubDocuments && aContent->IsElement()) {
4022 SubDocMapEntry *entry =
4023 static_cast<SubDocMapEntry*>
4024 (PL_DHashTableLookup(mSubDocuments, aContent->AsElement()));
4026 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
4027 return entry->mSubDocument;
4031 return nullptr;
4034 static PLDHashOperator
4035 FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
4036 uint32_t number, void *arg)
4038 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
4039 FindContentData *data = static_cast<FindContentData*>(arg);
4041 if (entry->mSubDocument == data->mSubDocument) {
4042 data->mResult = entry->mKey;
4044 return PL_DHASH_STOP;
4047 return PL_DHASH_NEXT;
4050 Element*
4051 nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
4053 NS_ENSURE_TRUE(aDocument, nullptr);
4055 if (!mSubDocuments) {
4056 return nullptr;
4059 FindContentData data(aDocument);
4060 PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data);
4062 return data.mResult;
4065 bool
4066 nsDocument::IsNodeOfType(uint32_t aFlags) const
4068 return !(aFlags & ~eDOCUMENT);
4071 Element*
4072 nsIDocument::GetRootElement() const
4074 return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
4075 mCachedRootElement : GetRootElementInternal();
4078 Element*
4079 nsDocument::GetRootElementInternal() const
4081 // Loop backwards because any non-elements, such as doctypes and PIs
4082 // are likely to appear before the root element.
4083 uint32_t i;
4084 for (i = mChildren.ChildCount(); i > 0; --i) {
4085 nsIContent* child = mChildren.ChildAt(i - 1);
4086 if (child->IsElement()) {
4087 const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
4088 return child->AsElement();
4092 const_cast<nsDocument*>(this)->mCachedRootElement = nullptr;
4093 return nullptr;
4096 nsIContent *
4097 nsDocument::GetChildAt(uint32_t aIndex) const
4099 return mChildren.GetSafeChildAt(aIndex);
4102 int32_t
4103 nsDocument::IndexOf(const nsINode* aPossibleChild) const
4105 return mChildren.IndexOfChild(aPossibleChild);
4108 uint32_t
4109 nsDocument::GetChildCount() const
4111 return mChildren.ChildCount();
4114 nsIContent * const *
4115 nsDocument::GetChildArray(uint32_t* aChildCount) const
4117 return mChildren.GetChildArray(aChildCount);
4121 nsresult
4122 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
4123 bool aNotify)
4125 if (aKid->IsElement() && GetRootElement()) {
4126 NS_WARNING("Inserting root element when we already have one");
4127 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
4130 return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
4133 void
4134 nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
4136 nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
4137 if (!oldKid) {
4138 return;
4141 if (oldKid->IsElement()) {
4142 // Destroy the link map up front before we mess with the child list.
4143 DestroyElementMaps();
4146 doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
4147 mCachedRootElement = nullptr;
4150 void
4151 nsDocument::EnsureOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
4153 // Contains() takes nsISupport*, so annoyingly we have to cast here
4154 if (mOnDemandBuiltInUASheets.Contains(static_cast<nsIStyleSheet*>(aSheet))) {
4155 return;
4157 BeginUpdate(UPDATE_STYLE);
4158 AddOnDemandBuiltInUASheet(aSheet);
4159 EndUpdate(UPDATE_STYLE);
4162 void
4163 nsDocument::AddOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
4165 // Contains() takes nsISupport*, so annoyingly we have to cast here
4166 MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(static_cast<nsIStyleSheet*>(aSheet)));
4168 // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in
4169 // the same order that they should end up in the style set.
4170 mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet);
4172 if (aSheet->IsApplicable()) {
4173 // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
4174 nsCOMPtr<nsIPresShell> shell = GetShell();
4175 if (shell) {
4176 // Note that prepending here is necessary to make sure that html.css etc.
4177 // do not override Firefox OS/Mobile's content.css sheet. Maybe we should
4178 // have an insertion point to match the order of
4179 // nsDocumentViewer::CreateStyleSet though?
4180 shell->StyleSet()->PrependStyleSheet(nsStyleSet::eAgentSheet, aSheet);
4184 NotifyStyleSheetAdded(aSheet, false);
4187 int32_t
4188 nsDocument::GetNumberOfStyleSheets() const
4190 return mStyleSheets.Count();
4193 nsIStyleSheet*
4194 nsDocument::GetStyleSheetAt(int32_t aIndex) const
4196 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr);
4197 return mStyleSheets[aIndex];
4200 int32_t
4201 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
4203 return mStyleSheets.IndexOf(aSheet);
4206 void
4207 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
4209 nsCOMPtr<nsIPresShell> shell = GetShell();
4210 if (shell) {
4211 shell->StyleSet()->AddDocStyleSheet(aSheet, this);
4215 #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \
4216 do { \
4217 nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(aSheet); \
4218 if (!cssSheet) { \
4219 return; \
4222 className##Init init; \
4223 init.mBubbles = true; \
4224 init.mCancelable = true; \
4225 init.mStylesheet = cssSheet; \
4226 init.memberName = argName; \
4228 nsRefPtr<className> event = \
4229 className::Constructor(this, NS_LITERAL_STRING(type), init); \
4230 event->SetTrusted(true); \
4231 event->SetTarget(this); \
4232 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = \
4233 new AsyncEventDispatcher(this, event); \
4234 asyncDispatcher->mDispatchChromeOnly = true; \
4235 asyncDispatcher->PostDOMEvent(); \
4236 } while (0);
4238 void
4239 nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
4241 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
4243 if (StyleSheetChangeEventsEnabled()) {
4244 DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4245 "StyleSheetAdded",
4246 mDocumentSheet,
4247 aDocumentSheet);
4251 void
4252 nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet)
4254 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
4256 if (StyleSheetChangeEventsEnabled()) {
4257 DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4258 "StyleSheetRemoved",
4259 mDocumentSheet,
4260 aDocumentSheet);
4264 void
4265 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
4267 NS_PRECONDITION(aSheet, "null arg");
4268 mStyleSheets.AppendObject(aSheet);
4269 aSheet->SetOwningDocument(this);
4271 if (aSheet->IsApplicable()) {
4272 AddStyleSheetToStyleSets(aSheet);
4275 NotifyStyleSheetAdded(aSheet, true);
4278 void
4279 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
4281 nsCOMPtr<nsIPresShell> shell = GetShell();
4282 if (shell) {
4283 shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4287 void
4288 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
4290 NS_PRECONDITION(aSheet, "null arg");
4291 nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
4293 if (!mStyleSheets.RemoveObject(aSheet)) {
4294 NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4295 return;
4298 if (!mIsGoingAway) {
4299 if (aSheet->IsApplicable()) {
4300 RemoveStyleSheetFromStyleSets(aSheet);
4303 NotifyStyleSheetRemoved(aSheet, true);
4306 aSheet->SetOwningDocument(nullptr);
4309 void
4310 nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
4311 nsCOMArray<nsIStyleSheet>& aNewSheets)
4313 BeginUpdate(UPDATE_STYLE);
4315 // XXX Need to set the sheet on the ownernode, if any
4316 NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(),
4317 "The lists must be the same length!");
4318 int32_t count = aOldSheets.Count();
4320 nsCOMPtr<nsIStyleSheet> oldSheet;
4321 int32_t i;
4322 for (i = 0; i < count; ++i) {
4323 oldSheet = aOldSheets[i];
4325 // First remove the old sheet.
4326 NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4327 int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4328 RemoveStyleSheet(oldSheet); // This does the right notifications
4330 // Now put the new one in its place. If it's null, just ignore it.
4331 nsIStyleSheet* newSheet = aNewSheets[i];
4332 if (newSheet) {
4333 mStyleSheets.InsertObjectAt(newSheet, oldIndex);
4334 newSheet->SetOwningDocument(this);
4335 if (newSheet->IsApplicable()) {
4336 AddStyleSheetToStyleSets(newSheet);
4339 NotifyStyleSheetAdded(newSheet, true);
4343 EndUpdate(UPDATE_STYLE);
4346 void
4347 nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex)
4349 NS_PRECONDITION(aSheet, "null ptr");
4350 mStyleSheets.InsertObjectAt(aSheet, aIndex);
4352 aSheet->SetOwningDocument(this);
4354 if (aSheet->IsApplicable()) {
4355 AddStyleSheetToStyleSets(aSheet);
4358 NotifyStyleSheetAdded(aSheet, true);
4362 void
4363 nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
4364 bool aApplicable)
4366 NS_PRECONDITION(aSheet, "null arg");
4368 // If we're actually in the document style sheet list
4369 if (-1 != mStyleSheets.IndexOf(aSheet)) {
4370 if (aApplicable) {
4371 AddStyleSheetToStyleSets(aSheet);
4372 } else {
4373 RemoveStyleSheetFromStyleSets(aSheet);
4377 // We have to always notify, since this will be called for sheets
4378 // that are children of sheets in our style set, as well as some
4379 // sheets for nsHTMLEditor.
4381 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
4382 (this, aSheet, aApplicable));
4384 if (StyleSheetChangeEventsEnabled()) {
4385 DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
4386 "StyleSheetApplicableStateChanged",
4387 mApplicable,
4388 aApplicable);
4391 if (!mSSApplicableStateNotificationPending) {
4392 nsRefPtr<nsIRunnable> notification = NS_NewRunnableMethod(this,
4393 &nsDocument::NotifyStyleSheetApplicableStateChanged);
4394 mSSApplicableStateNotificationPending =
4395 NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
4399 void
4400 nsDocument::NotifyStyleSheetApplicableStateChanged()
4402 mSSApplicableStateNotificationPending = false;
4403 nsCOMPtr<nsIObserverService> observerService =
4404 mozilla::services::GetObserverService();
4405 if (observerService) {
4406 observerService->NotifyObservers(static_cast<nsIDocument*>(this),
4407 "style-sheet-applicable-state-changed",
4408 nullptr);
4412 static nsStyleSet::sheetType
4413 ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
4415 switch(aType) {
4416 case nsIDocument::eAgentSheet:
4417 return nsStyleSet::eAgentSheet;
4418 case nsIDocument::eUserSheet:
4419 return nsStyleSet::eUserSheet;
4420 case nsIDocument::eAuthorSheet:
4421 return nsStyleSet::eDocSheet;
4422 default:
4423 NS_ASSERTION(false, "wrong type");
4424 // we must return something although this should never happen
4425 return nsStyleSet::eSheetTypeCount;
4429 static int32_t
4430 FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI)
4432 for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) {
4433 bool bEqual;
4434 nsIURI* uri = aSheets[i]->GetSheetURI();
4436 if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4437 return i;
4440 return -1;
4443 nsresult
4444 nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4446 NS_PRECONDITION(aSheetURI, "null arg");
4448 // Checking if we have loaded this one already.
4449 if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4450 return NS_ERROR_INVALID_ARG;
4452 // Loading the sheet sync.
4453 nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader();
4455 nsRefPtr<CSSStyleSheet> sheet;
4456 nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet,
4457 true, getter_AddRefs(sheet));
4458 NS_ENSURE_SUCCESS(rv, rv);
4460 sheet->SetOwningDocument(this);
4461 MOZ_ASSERT(sheet->IsApplicable());
4463 return AddAdditionalStyleSheet(aType, sheet);
4466 nsresult
4467 nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aSheet)
4469 if (mAdditionalSheets[aType].Contains(aSheet))
4470 return NS_ERROR_INVALID_ARG;
4472 if (!aSheet->IsApplicable())
4473 return NS_ERROR_INVALID_ARG;
4475 mAdditionalSheets[aType].AppendObject(aSheet);
4477 BeginUpdate(UPDATE_STYLE);
4478 nsCOMPtr<nsIPresShell> shell = GetShell();
4479 if (shell) {
4480 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
4481 shell->StyleSet()->AppendStyleSheet(type, aSheet);
4484 // Passing false, so documet.styleSheets.length will not be affected by
4485 // these additional sheets.
4486 NotifyStyleSheetAdded(aSheet, false);
4487 EndUpdate(UPDATE_STYLE);
4488 return NS_OK;
4491 void
4492 nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4494 MOZ_ASSERT(aSheetURI);
4496 nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType];
4498 int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4499 if (i >= 0) {
4500 nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i];
4501 sheets.RemoveObjectAt(i);
4503 BeginUpdate(UPDATE_STYLE);
4504 if (!mIsGoingAway) {
4505 MOZ_ASSERT(sheetRef->IsApplicable());
4506 nsCOMPtr<nsIPresShell> shell = GetShell();
4507 if (shell) {
4508 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
4509 shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4513 // Passing false, so documet.styleSheets.length will not be affected by
4514 // these additional sheets.
4515 NotifyStyleSheetRemoved(sheetRef, false);
4516 EndUpdate(UPDATE_STYLE);
4518 sheetRef->SetOwningDocument(nullptr);
4522 nsIStyleSheet*
4523 nsDocument::FirstAdditionalAuthorSheet()
4525 return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0);
4528 nsIGlobalObject*
4529 nsDocument::GetScopeObject() const
4531 nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4532 return scope;
4535 void
4536 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
4538 mScopeObject = do_GetWeakReference(aGlobal);
4539 if (aGlobal) {
4540 mHasHadScriptHandlingObject = true;
4544 #ifdef MOZ_EME
4545 static void
4546 CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
4548 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4549 if (domMediaElem) {
4550 nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4551 MOZ_ASSERT(content, "aSupports is not a content");
4552 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4553 bool* contains = static_cast<bool*>(aContainsEME);
4554 if (mediaElem->GetMediaKeys()) {
4555 *contains = true;
4560 bool
4561 nsDocument::ContainsEMEContent()
4563 bool containsEME = false;
4564 EnumerateActivityObservers(CheckIfContainsEMEContent,
4565 static_cast<void*>(&containsEME));
4566 return containsEME;
4568 #endif // MOZ_EME
4570 static void
4571 NotifyActivityChanged(nsISupports *aSupports, void *aUnused)
4573 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4574 if (domMediaElem) {
4575 nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4576 MOZ_ASSERT(content, "aSupports is not a content");
4577 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4578 mediaElem->NotifyOwnerDocumentActivityChanged();
4580 nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aSupports));
4581 if (objectLoadingContent) {
4582 nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4583 olc->NotifyOwnerDocumentActivityChanged();
4585 nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(do_QueryInterface(aSupports));
4586 if (objectDocumentActivity) {
4587 objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
4591 void
4592 nsIDocument::SetContainer(nsDocShell* aContainer)
4594 if (aContainer) {
4595 mDocumentContainer = aContainer;
4596 } else {
4597 mDocumentContainer = WeakPtr<nsDocShell>();
4600 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4601 if (!aContainer) {
4602 return;
4605 // Get the Docshell
4606 if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4607 // check if same type root
4608 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4609 aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4610 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
4612 if (sameTypeRoot == aContainer) {
4613 static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4618 nsISupports*
4619 nsIDocument::GetContainer() const
4621 return static_cast<nsIDocShell*>(mDocumentContainer);
4624 void
4625 nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
4627 #ifdef DEBUG
4629 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject));
4631 NS_ASSERTION(!win || win->IsInnerWindow(),
4632 "Script global object must be an inner window!");
4634 #endif
4635 NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController ||
4636 mAnimationController->IsPausedByType(
4637 nsSMILTimeContainer::PAUSE_PAGEHIDE |
4638 nsSMILTimeContainer::PAUSE_BEGIN),
4639 "Clearing window pointer while animations are unpaused");
4641 if (mScriptGlobalObject && !aScriptGlobalObject) {
4642 // We're detaching from the window. We need to grab a pointer to
4643 // our layout history state now.
4644 mLayoutHistoryState = GetLayoutHistoryState();
4646 if (mPresShell && !EventHandlingSuppressed()) {
4647 RevokeAnimationFrameNotifications();
4650 // Also make sure to remove our onload blocker now if we haven't done it yet
4651 if (mOnloadBlockCount != 0) {
4652 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4653 if (loadGroup) {
4654 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4659 mScriptGlobalObject = aScriptGlobalObject;
4661 if (aScriptGlobalObject) {
4662 mHasHadScriptHandlingObject = true;
4663 mHasHadDefaultView = true;
4664 // Go back to using the docshell for the layout history state
4665 mLayoutHistoryState = nullptr;
4666 mScopeObject = do_GetWeakReference(aScriptGlobalObject);
4667 #ifdef DEBUG
4668 if (!mWillReparent) {
4669 // We really shouldn't have a wrapper here but if we do we need to make sure
4670 // it has the correct parent.
4671 JSObject *obj = GetWrapperPreserveColor();
4672 if (obj) {
4673 JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
4674 NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
4675 "Wrong scope, this is really bad!");
4678 #endif
4680 if (mAllowDNSPrefetch) {
4681 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4682 if (docShell) {
4683 #ifdef DEBUG
4684 nsCOMPtr<nsIWebNavigation> webNav =
4685 do_GetInterface(aScriptGlobalObject);
4686 NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4687 "Unexpected container or script global?");
4688 #endif
4689 bool allowDNSPrefetch;
4690 docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4691 mAllowDNSPrefetch = allowDNSPrefetch;
4695 MaybeRescheduleAnimationFrameNotifications();
4696 mRegistry = new Registry();
4699 // Remember the pointer to our window (or lack there of), to avoid
4700 // having to QI every time it's asked for.
4701 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject);
4702 mWindow = window;
4704 // Now that we know what our window is, we can flush the CSP errors to the
4705 // Web Console. We are flushing all messages that occured and were stored
4706 // in the queue prior to this point.
4707 FlushCSPWebConsoleErrorQueue();
4708 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4709 do_QueryInterface(GetChannel());
4710 if (internalChannel) {
4711 nsCOMArray<nsISecurityConsoleMessage> messages;
4712 internalChannel->TakeAllSecurityMessages(messages);
4713 SendToConsole(messages);
4716 // Set our visibility state, but do not fire the event. This is correct
4717 // because either we're coming out of bfcache (in which case IsVisible() will
4718 // still test false at this point and no state change will happen) or we're
4719 // doing the initial document load and don't want to fire the event for this
4720 // change.
4721 dom::VisibilityState oldState = mVisibilityState;
4722 mVisibilityState = GetVisibilityState();
4723 // When the visibility is changed, notify it to observers.
4724 // Some observers need the notification, for example HTMLMediaElement uses
4725 // it to update internal media resource allocation.
4726 // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
4727 // creation are already done before nsDocument::SetScriptGlobalObject() call.
4728 // MediaDecoder decides whether starting decoding is decided based on
4729 // document's visibility. When the MediaDecoder is created,
4730 // nsDocument::SetScriptGlobalObject() is not yet called and document is
4731 // hidden state. Therefore the MediaDecoder decides that decoding is
4732 // not yet necessary. But soon after nsDocument::SetScriptGlobalObject()
4733 // call, the document becomes not hidden. At the time, MediaDecoder needs
4734 // to know it and needs to start updating decoding.
4735 if (oldState != mVisibilityState) {
4736 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4739 // The global in the template contents owner document should be the same.
4740 if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
4741 mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
4744 nsCOMPtr<nsIChannel> channel = GetChannel();
4745 if (!mMaybeServiceWorkerControlled && channel) {
4746 nsLoadFlags loadFlags = 0;
4747 channel->GetLoadFlags(&loadFlags);
4748 // If we are shift-reloaded, don't associate with a ServiceWorker.
4749 // FIXME(nsm): Bug 1041339.
4750 if (loadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
4751 NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
4752 return;
4755 nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
4756 if (swm) {
4757 swm->MaybeStartControlling(this);
4758 mMaybeServiceWorkerControlled = true;
4763 nsIScriptGlobalObject*
4764 nsDocument::GetScriptHandlingObjectInternal() const
4766 MOZ_ASSERT(!mScriptGlobalObject,
4767 "Do not call this when mScriptGlobalObject is set!");
4768 if (mHasHadDefaultView) {
4769 return nullptr;
4772 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4773 do_QueryReferent(mScopeObject);
4774 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject);
4775 if (win) {
4776 NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!");
4777 nsPIDOMWindow* outer = win->GetOuterWindow();
4778 if (!outer || outer->GetCurrentInnerWindow() != win) {
4779 NS_WARNING("Wrong inner/outer window combination!");
4780 return nullptr;
4783 return scriptHandlingObject;
4785 void
4786 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
4788 NS_ASSERTION(!mScriptGlobalObject ||
4789 mScriptGlobalObject == aScriptObject,
4790 "Wrong script object!");
4791 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject);
4792 NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!");
4793 if (aScriptObject) {
4794 mScopeObject = do_GetWeakReference(aScriptObject);
4795 mHasHadScriptHandlingObject = true;
4796 mHasHadDefaultView = false;
4800 bool
4801 nsDocument::IsTopLevelContentDocument()
4803 return mIsTopLevelContentDocument;
4806 void
4807 nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument)
4809 mIsTopLevelContentDocument = aIsTopLevelContentDocument;
4812 nsPIDOMWindow *
4813 nsDocument::GetWindowInternal() const
4815 MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4816 // Let's use mScriptGlobalObject. Even if the document is already removed from
4817 // the docshell, the outer window might be still obtainable from the it.
4818 nsCOMPtr<nsPIDOMWindow> win;
4819 if (mRemovedFromDocShell) {
4820 // The docshell returns the outer window we are done.
4821 nsCOMPtr<nsIDocShell> kungfuDeathGrip(mDocumentContainer);
4822 if (mDocumentContainer) {
4823 win = mDocumentContainer->GetWindow();
4825 } else {
4826 win = do_QueryInterface(mScriptGlobalObject);
4827 if (win) {
4828 // mScriptGlobalObject is always the inner window, let's get the outer.
4829 win = win->GetOuterWindow();
4833 return win;
4836 nsScriptLoader*
4837 nsDocument::ScriptLoader()
4839 return mScriptLoader;
4842 bool
4843 nsDocument::InternalAllowXULXBL()
4845 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
4846 mAllowXULXBL = eTriTrue;
4847 return true;
4850 mAllowXULXBL = eTriFalse;
4851 return false;
4854 // Note: We don't hold a reference to the document observer; we assume
4855 // that it has a live reference to the document.
4856 void
4857 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
4859 NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
4860 "Observer already in the list");
4861 mObservers.AppendElement(aObserver);
4862 AddMutationObserver(aObserver);
4865 bool
4866 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
4868 // If we're in the process of destroying the document (and we're
4869 // informing the observers of the destruction), don't remove the
4870 // observers from the list. This is not a big deal, since we
4871 // don't hold a live reference to the observers.
4872 if (!mInDestructor) {
4873 RemoveMutationObserver(aObserver);
4874 return mObservers.RemoveElement(aObserver);
4877 return mObservers.Contains(aObserver);
4880 void
4881 nsDocument::MaybeEndOutermostXBLUpdate()
4883 // Only call BindingManager()->EndOutermostUpdate() when
4884 // we're not in an update and it is safe to run scripts.
4885 if (mUpdateNestLevel == 0 && mInXBLUpdate) {
4886 if (nsContentUtils::IsSafeToRunScript()) {
4887 mInXBLUpdate = false;
4888 BindingManager()->EndOutermostUpdate();
4889 } else if (!mInDestructor) {
4890 nsContentUtils::AddScriptRunner(
4891 NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate));
4896 void
4897 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
4899 if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
4900 mInXBLUpdate = true;
4901 BindingManager()->BeginOutermostUpdate();
4904 ++mUpdateNestLevel;
4905 nsContentUtils::AddScriptBlocker();
4906 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
4909 void
4910 nsDocument::EndUpdate(nsUpdateType aUpdateType)
4912 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
4914 nsContentUtils::RemoveScriptBlocker();
4916 --mUpdateNestLevel;
4918 // This set of updates may have created XBL bindings. Let the
4919 // binding manager know we're done.
4920 MaybeEndOutermostXBLUpdate();
4922 MaybeInitializeFinalizeFrameLoaders();
4925 void
4926 nsDocument::BeginLoad()
4928 // Block onload here to prevent having to deal with blocking and
4929 // unblocking it while we know the document is loading.
4930 BlockOnload();
4931 mDidFireDOMContentLoaded = false;
4932 BlockDOMContentLoaded();
4934 if (mScriptLoader) {
4935 mScriptLoader->BeginDeferringScripts();
4938 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
4941 void
4942 nsDocument::ReportEmptyGetElementByIdArg()
4944 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
4945 NS_LITERAL_CSTRING("DOM"), this,
4946 nsContentUtils::eDOM_PROPERTIES,
4947 "EmptyGetElementByIdParam");
4950 Element*
4951 nsDocument::GetElementById(const nsAString& aElementId)
4953 if (!CheckGetElementByIdArg(aElementId)) {
4954 return nullptr;
4957 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
4958 return entry ? entry->GetIdElement() : nullptr;
4961 const nsSmallVoidArray*
4962 nsDocument::GetAllElementsForId(const nsAString& aElementId) const
4964 if (aElementId.IsEmpty()) {
4965 return nullptr;
4968 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
4969 return entry ? entry->GetIdElements() : nullptr;
4972 NS_IMETHODIMP
4973 nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
4975 Element *content = GetElementById(aId);
4976 if (content) {
4977 return CallQueryInterface(content, aReturn);
4980 *aReturn = nullptr;
4982 return NS_OK;
4985 Element*
4986 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
4987 void* aData, bool aForImage)
4989 nsDependentAtomString id(aID);
4991 if (!CheckGetElementByIdArg(id))
4992 return nullptr;
4994 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
4995 NS_ENSURE_TRUE(entry, nullptr);
4997 entry->AddContentChangeCallback(aObserver, aData, aForImage);
4998 return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
5001 void
5002 nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
5003 void* aData, bool aForImage)
5005 nsDependentAtomString id(aID);
5007 if (!CheckGetElementByIdArg(id))
5008 return;
5010 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
5011 if (!entry) {
5012 return;
5015 entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
5018 NS_IMETHODIMP
5019 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
5020 nsIDOMElement* aImageElement)
5022 nsCOMPtr<Element> el = do_QueryInterface(aImageElement);
5023 MozSetImageElement(aImageElementId, el);
5024 return NS_OK;
5027 void
5028 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
5029 Element* aElement)
5031 if (aImageElementId.IsEmpty())
5032 return;
5034 // Hold a script blocker while calling SetImageElement since that can call
5035 // out to id-observers
5036 nsAutoScriptBlocker scriptBlocker;
5038 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId);
5039 if (entry) {
5040 entry->SetImageElement(aElement);
5041 if (entry->IsEmpty()) {
5042 mIdentifierMap.RemoveEntry(aImageElementId);
5047 Element*
5048 nsDocument::LookupImageElement(const nsAString& aId)
5050 if (aId.IsEmpty())
5051 return nullptr;
5053 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
5054 return entry ? entry->GetImageIdElement() : nullptr;
5057 void
5058 nsDocument::DispatchContentLoadedEvents()
5060 // If you add early returns from this method, make sure you're
5061 // calling UnblockOnload properly.
5063 // Unpin references to preloaded images
5064 mPreloadingImages.Clear();
5066 if (mTiming) {
5067 mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
5070 // Dispatch observer notification to notify observers document is interactive.
5071 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
5072 nsIPrincipal *principal = GetPrincipal();
5073 os->NotifyObservers(static_cast<nsIDocument*>(this),
5074 nsContentUtils::IsSystemPrincipal(principal) ?
5075 "chrome-document-interactive" :
5076 "content-document-interactive",
5077 nullptr);
5079 // Fire a DOM event notifying listeners that this document has been
5080 // loaded (excluding images and other loads initiated by this
5081 // document).
5082 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
5083 NS_LITERAL_STRING("DOMContentLoaded"),
5084 true, true);
5086 if (mTiming) {
5087 mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
5090 // If this document is a [i]frame, fire a DOMFrameContentLoaded
5091 // event on all parent documents notifying that the HTML (excluding
5092 // other external files such as images and stylesheets) in a frame
5093 // has finished loading.
5095 // target_frame is the [i]frame element that will be used as the
5096 // target for the event. It's the [i]frame whose content is done
5097 // loading.
5098 nsCOMPtr<EventTarget> target_frame;
5100 if (mParentDocument) {
5101 target_frame = mParentDocument->FindContentForSubDocument(this);
5104 if (target_frame) {
5105 nsCOMPtr<nsIDocument> parent = mParentDocument;
5106 do {
5107 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent);
5109 nsCOMPtr<nsIDOMEvent> event;
5110 if (domDoc) {
5111 domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
5112 getter_AddRefs(event));
5116 if (event) {
5117 event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
5118 true);
5120 event->SetTarget(target_frame);
5121 event->SetTrusted(true);
5123 // To dispatch this event we must manually call
5124 // EventDispatcher::Dispatch() on the ancestor document since the
5125 // target is not in the same document, so the event would never reach
5126 // the ancestor document if we used the normal event
5127 // dispatching code.
5129 WidgetEvent* innerEvent = event->GetInternalNSEvent();
5130 if (innerEvent) {
5131 nsEventStatus status = nsEventStatus_eIgnore;
5133 nsIPresShell *shell = parent->GetShell();
5134 if (shell) {
5135 nsRefPtr<nsPresContext> context = shell->GetPresContext();
5137 if (context) {
5138 EventDispatcher::Dispatch(parent, context, innerEvent, event,
5139 &status);
5145 parent = parent->GetParentDocument();
5146 } while (parent);
5149 // If the document has a manifest attribute, fire a MozApplicationManifest
5150 // event.
5151 Element* root = GetRootElement();
5152 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
5153 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
5154 NS_LITERAL_STRING("MozApplicationManifest"),
5155 true, true);
5158 UnblockOnload(true);
5161 void
5162 nsDocument::EndLoad()
5164 // Drop the ref to our parser, if any, but keep hold of the sink so that we
5165 // can flush it from FlushPendingNotifications as needed. We might have to
5166 // do that to get a StartLayout() to happen.
5167 if (mParser) {
5168 mWeakSink = do_GetWeakReference(mParser->GetContentSink());
5169 mParser = nullptr;
5172 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
5174 UnblockDOMContentLoaded();
5177 void
5178 nsDocument::UnblockDOMContentLoaded()
5180 MOZ_ASSERT(mBlockDOMContentLoaded);
5181 if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
5182 return;
5184 mDidFireDOMContentLoaded = true;
5186 MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
5187 if (!mSynchronousDOMContentLoaded) {
5188 nsRefPtr<nsIRunnable> ev =
5189 NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
5190 NS_DispatchToCurrentThread(ev);
5191 } else {
5192 DispatchContentLoadedEvents();
5196 void
5197 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
5199 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
5200 "Someone forgot a scriptblocker");
5201 NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
5202 (this, aContent, aStateMask));
5205 void
5206 nsDocument::DocumentStatesChanged(EventStates aStateMask)
5208 // Invalidate our cached state.
5209 mGotDocumentState &= ~aStateMask;
5210 mDocumentState &= ~aStateMask;
5212 NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
5215 void
5216 nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
5217 nsIStyleRule* aOldStyleRule,
5218 nsIStyleRule* aNewStyleRule)
5220 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
5221 (this, aSheet,
5222 aOldStyleRule, aNewStyleRule));
5224 if (StyleSheetChangeEventsEnabled()) {
5225 nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule);
5226 DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5227 "StyleRuleChanged",
5228 mRule,
5229 rule ? rule->GetDOMRule() : nullptr);
5233 void
5234 nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
5235 nsIStyleRule* aStyleRule)
5237 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
5238 (this, aSheet, aStyleRule));
5240 if (StyleSheetChangeEventsEnabled()) {
5241 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
5242 DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5243 "StyleRuleAdded",
5244 mRule,
5245 rule ? rule->GetDOMRule() : nullptr);
5249 void
5250 nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
5251 nsIStyleRule* aStyleRule)
5253 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
5254 (this, aSheet, aStyleRule));
5256 if (StyleSheetChangeEventsEnabled()) {
5257 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
5258 DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5259 "StyleRuleRemoved",
5260 mRule,
5261 rule ? rule->GetDOMRule() : nullptr);
5265 #undef DO_STYLESHEET_NOTIFICATION
5267 already_AddRefed<AnonymousContent>
5268 nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
5270 nsIPresShell* shell = GetShell();
5271 if (!shell || !shell->GetCanvasFrame()) {
5272 aRv.Throw(NS_ERROR_UNEXPECTED);
5273 return nullptr;
5276 nsCOMPtr<Element> container = shell->GetCanvasFrame()
5277 ->GetCustomContentContainer();
5278 if (!container) {
5279 aRv.Throw(NS_ERROR_UNEXPECTED);
5280 return nullptr;
5283 // Clone the node to avoid returning a direct reference
5284 nsCOMPtr<nsINode> clonedElement = aElement.CloneNode(true, aRv);
5285 if (aRv.Failed()) {
5286 return nullptr;
5289 // Insert the element into the container
5290 nsresult rv;
5291 rv = container->AppendChildTo(clonedElement->AsContent(), true);
5292 if (NS_FAILED(rv)) {
5293 return nullptr;
5296 nsRefPtr<AnonymousContent> anonymousContent =
5297 new AnonymousContent(clonedElement->AsElement());
5298 mAnonymousContents.AppendElement(anonymousContent);
5300 return anonymousContent.forget();
5303 void
5304 nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
5305 ErrorResult& aRv)
5307 nsIPresShell* shell = GetShell();
5308 if (!shell || !shell->GetCanvasFrame()) {
5309 aRv.Throw(NS_ERROR_UNEXPECTED);
5310 return;
5313 nsCOMPtr<Element> container = shell->GetCanvasFrame()
5314 ->GetCustomContentContainer();
5315 if (!container) {
5316 aRv.Throw(NS_ERROR_UNEXPECTED);
5317 return;
5320 // Iterate over know customContents to get and remove the right one
5321 for (int32_t i = mAnonymousContents.Length() - 1; i >= 0; --i) {
5322 if (mAnonymousContents[i] == &aContent) {
5323 // Get the node from the customContent
5324 nsCOMPtr<Element> node = aContent.GetContentNode();
5326 // Remove the entry in mAnonymousContents
5327 mAnonymousContents.RemoveElementAt(i);
5329 // Remove the node from its container
5330 container->RemoveChild(*node, aRv);
5331 if (aRv.Failed()) {
5332 return;
5335 break;
5341 // nsIDOMDocument interface
5343 DocumentType*
5344 nsIDocument::GetDoctype() const
5346 for (nsIContent* child = GetFirstChild();
5347 child;
5348 child = child->GetNextSibling()) {
5349 if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
5350 return static_cast<DocumentType*>(child);
5353 return nullptr;
5356 NS_IMETHODIMP
5357 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
5359 MOZ_ASSERT(aDoctype);
5360 nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype();
5361 doctype.forget(aDoctype);
5362 return NS_OK;
5365 NS_IMETHODIMP
5366 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
5368 ErrorResult rv;
5369 *aImplementation = GetImplementation(rv);
5370 if (rv.Failed()) {
5371 MOZ_ASSERT(!*aImplementation);
5372 return rv.ErrorCode();
5374 NS_ADDREF(*aImplementation);
5375 return NS_OK;
5378 DOMImplementation*
5379 nsDocument::GetImplementation(ErrorResult& rv)
5381 if (!mDOMImplementation) {
5382 nsCOMPtr<nsIURI> uri;
5383 NS_NewURI(getter_AddRefs(uri), "about:blank");
5384 if (!uri) {
5385 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5386 return nullptr;
5388 bool hasHadScriptObject = true;
5389 nsIScriptGlobalObject* scriptObject =
5390 GetScriptHandlingObject(hasHadScriptObject);
5391 if (!scriptObject && hasHadScriptObject) {
5392 rv.Throw(NS_ERROR_UNEXPECTED);
5393 return nullptr;
5395 mDOMImplementation = new DOMImplementation(this,
5396 scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5399 return mDOMImplementation;
5402 NS_IMETHODIMP
5403 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
5405 NS_ENSURE_ARG_POINTER(aDocumentElement);
5407 Element* root = GetRootElement();
5408 if (root) {
5409 return CallQueryInterface(root, aDocumentElement);
5412 *aDocumentElement = nullptr;
5414 return NS_OK;
5417 NS_IMETHODIMP
5418 nsDocument::CreateElement(const nsAString& aTagName,
5419 nsIDOMElement** aReturn)
5421 *aReturn = nullptr;
5422 ErrorResult rv;
5423 nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
5424 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
5425 return CallQueryInterface(element, aReturn);
5428 bool IsLowercaseASCII(const nsAString& aValue)
5430 int32_t len = aValue.Length();
5431 for (int32_t i = 0; i < len; ++i) {
5432 char16_t c = aValue[i];
5433 if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5434 return false;
5437 return true;
5440 already_AddRefed<Element>
5441 nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
5443 rv = nsContentUtils::CheckQName(aTagName, false);
5444 if (rv.Failed()) {
5445 return nullptr;
5448 bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName);
5449 nsAutoString lcTagName;
5450 if (needsLowercase) {
5451 nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5454 nsCOMPtr<nsIContent> content;
5455 rv = CreateElem(needsLowercase ? lcTagName : aTagName,
5456 nullptr, mDefaultElementType, getter_AddRefs(content));
5457 if (rv.Failed()) {
5458 return nullptr;
5460 return dont_AddRef(content.forget().take()->AsElement());
5463 void
5464 nsDocument::SetupCustomElement(Element* aElement,
5465 uint32_t aNamespaceID,
5466 const nsAString* aTypeExtension)
5468 if (!mRegistry) {
5469 return;
5472 nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
5473 nsCOMPtr<nsIAtom> typeAtom = aTypeExtension ?
5474 do_GetAtom(*aTypeExtension) : tagAtom;
5476 if (aTypeExtension && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
5477 // Custom element setup in the parser happens after the "is"
5478 // attribute is added.
5479 aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, *aTypeExtension, true);
5482 CustomElementDefinition* data;
5483 CustomElementHashKey key(aNamespaceID, typeAtom);
5484 if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
5485 // The type extension doesn't exist in the registry,
5486 // thus we don't need to enqueue callback or adjust
5487 // the "is" attribute, but it is possibly an upgrade candidate.
5488 RegisterUnresolvedElement(aElement, typeAtom);
5489 return;
5492 if (data->mLocalName != tagAtom) {
5493 // The element doesn't match the local name for the
5494 // definition, thus the element isn't a custom element
5495 // and we don't need to do anything more.
5496 return;
5499 // Enqueuing the created callback will set the CustomElementData on the
5500 // element, causing prototype swizzling to occur in Element::WrapObject.
5501 EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
5504 already_AddRefed<Element>
5505 nsDocument::CreateElement(const nsAString& aTagName,
5506 const nsAString& aTypeExtension,
5507 ErrorResult& rv)
5509 nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
5510 if (rv.Failed()) {
5511 return nullptr;
5514 if (!aTagName.Equals(aTypeExtension)) {
5515 // Custom element type can not extend itself.
5516 SetupCustomElement(elem, GetDefaultNamespaceID(), &aTypeExtension);
5519 return elem.forget();
5522 NS_IMETHODIMP
5523 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5524 const nsAString& aQualifiedName,
5525 nsIDOMElement** aReturn)
5527 *aReturn = nullptr;
5528 ErrorResult rv;
5529 nsCOMPtr<Element> element =
5530 nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
5531 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
5532 return CallQueryInterface(element, aReturn);
5535 already_AddRefed<Element>
5536 nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
5537 const nsAString& aQualifiedName,
5538 ErrorResult& rv)
5540 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
5541 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5542 aQualifiedName,
5543 mNodeInfoManager,
5544 nsIDOMNode::ELEMENT_NODE,
5545 getter_AddRefs(nodeInfo));
5546 if (rv.Failed()) {
5547 return nullptr;
5550 nsCOMPtr<Element> element;
5551 rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5552 NOT_FROM_PARSER);
5553 if (rv.Failed()) {
5554 return nullptr;
5556 return element.forget();
5559 already_AddRefed<Element>
5560 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5561 const nsAString& aQualifiedName,
5562 const nsAString& aTypeExtension,
5563 ErrorResult& rv)
5565 nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
5566 aQualifiedName,
5567 rv);
5568 if (rv.Failed()) {
5569 return nullptr;
5572 int32_t nameSpaceId = kNameSpaceID_Wildcard;
5573 if (!aNamespaceURI.EqualsLiteral("*")) {
5574 rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
5575 nameSpaceId);
5576 if (rv.Failed()) {
5577 return nullptr;
5581 if (!aQualifiedName.Equals(aTypeExtension)) {
5582 // A custom element type can not extend itself.
5583 SetupCustomElement(elem, nameSpaceId, &aTypeExtension);
5586 return elem.forget();
5589 NS_IMETHODIMP
5590 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
5592 *aReturn = nsIDocument::CreateTextNode(aData).take();
5593 return NS_OK;
5596 already_AddRefed<nsTextNode>
5597 nsIDocument::CreateTextNode(const nsAString& aData) const
5599 nsRefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5600 // Don't notify; this node is still being created.
5601 text->SetText(aData, false);
5602 return text.forget();
5605 NS_IMETHODIMP
5606 nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
5608 *aReturn = nsIDocument::CreateDocumentFragment().take();
5609 return NS_OK;
5612 already_AddRefed<DocumentFragment>
5613 nsIDocument::CreateDocumentFragment() const
5615 nsRefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5616 return frag.forget();
5619 NS_IMETHODIMP
5620 nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
5622 *aReturn = nsIDocument::CreateComment(aData).take();
5623 return NS_OK;
5626 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
5627 already_AddRefed<dom::Comment>
5628 nsIDocument::CreateComment(const nsAString& aData) const
5630 nsRefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5632 // Don't notify; this node is still being created.
5633 comment->SetText(aData, false);
5634 return comment.forget();
5637 NS_IMETHODIMP
5638 nsDocument::CreateCDATASection(const nsAString& aData,
5639 nsIDOMCDATASection** aReturn)
5641 NS_ENSURE_ARG_POINTER(aReturn);
5642 ErrorResult rv;
5643 *aReturn = nsIDocument::CreateCDATASection(aData, rv).take();
5644 return rv.ErrorCode();
5647 already_AddRefed<CDATASection>
5648 nsIDocument::CreateCDATASection(const nsAString& aData,
5649 ErrorResult& rv)
5651 if (IsHTML()) {
5652 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5653 return nullptr;
5656 if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5657 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5658 return nullptr;
5661 nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5663 // Don't notify; this node is still being created.
5664 cdata->SetText(aData, false);
5666 return cdata.forget();
5669 NS_IMETHODIMP
5670 nsDocument::CreateProcessingInstruction(const nsAString& aTarget,
5671 const nsAString& aData,
5672 nsIDOMProcessingInstruction** aReturn)
5674 ErrorResult rv;
5675 *aReturn =
5676 nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take();
5677 return rv.ErrorCode();
5680 already_AddRefed<ProcessingInstruction>
5681 nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5682 const nsAString& aData,
5683 ErrorResult& rv) const
5685 nsresult res = nsContentUtils::CheckQName(aTarget, false);
5686 if (NS_FAILED(res)) {
5687 rv.Throw(res);
5688 return nullptr;
5691 if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5692 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5693 return nullptr;
5696 nsRefPtr<ProcessingInstruction> pi =
5697 NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5699 return pi.forget();
5702 NS_IMETHODIMP
5703 nsDocument::CreateAttribute(const nsAString& aName,
5704 nsIDOMAttr** aReturn)
5706 ErrorResult rv;
5707 *aReturn = nsIDocument::CreateAttribute(aName, rv).take();
5708 return rv.ErrorCode();
5711 already_AddRefed<Attr>
5712 nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
5714 WarnOnceAbout(eCreateAttribute);
5716 if (!mNodeInfoManager) {
5717 rv.Throw(NS_ERROR_NOT_INITIALIZED);
5718 return nullptr;
5721 nsresult res = nsContentUtils::CheckQName(aName, false);
5722 if (NS_FAILED(res)) {
5723 rv.Throw(res);
5724 return nullptr;
5727 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
5728 res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None,
5729 nsIDOMNode::ATTRIBUTE_NODE,
5730 getter_AddRefs(nodeInfo));
5731 if (NS_FAILED(res)) {
5732 rv.Throw(res);
5733 return nullptr;
5736 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5737 EmptyString(), false);
5738 return attribute.forget();
5741 NS_IMETHODIMP
5742 nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI,
5743 const nsAString & aQualifiedName,
5744 nsIDOMAttr **aResult)
5746 ErrorResult rv;
5747 *aResult =
5748 nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take();
5749 return rv.ErrorCode();
5752 already_AddRefed<Attr>
5753 nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
5754 const nsAString& aQualifiedName,
5755 ErrorResult& rv)
5757 WarnOnceAbout(eCreateAttributeNS);
5759 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
5760 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5761 aQualifiedName,
5762 mNodeInfoManager,
5763 nsIDOMNode::ATTRIBUTE_NODE,
5764 getter_AddRefs(nodeInfo));
5765 if (rv.Failed()) {
5766 return nullptr;
5769 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5770 EmptyString(), true);
5771 return attribute.forget();
5774 bool
5775 nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
5777 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
5779 JS::Rooted<JSObject*> global(aCx,
5780 JS_GetGlobalForObject(aCx, &args.callee()));
5781 nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
5782 MOZ_ASSERT(window, "Should have a non-null window");
5784 nsDocument* document = static_cast<nsDocument*>(window->GetDoc());
5786 // Function name is the type of the custom element.
5787 JSString* jsFunName =
5788 JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
5789 nsAutoJSString elemName;
5790 if (!elemName.init(aCx, jsFunName)) {
5791 return true;
5794 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName));
5795 CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom);
5796 CustomElementDefinition* definition;
5797 if (!document->mRegistry ||
5798 !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
5799 return true;
5802 nsDependentAtomString localName(definition->mLocalName);
5804 nsCOMPtr<nsIContent> newElement;
5805 nsresult rv = document->CreateElem(localName, nullptr,
5806 definition->mNamespaceID,
5807 getter_AddRefs(newElement));
5808 NS_ENSURE_SUCCESS(rv, true);
5810 nsCOMPtr<Element> element = do_QueryInterface(newElement);
5811 if (definition->mLocalName != typeAtom) {
5812 // This element is a custom element by extension, thus we need to
5813 // do some special setup. For non-extended custom elements, this happens
5814 // when the element is created.
5815 document->SetupCustomElement(element, definition->mNamespaceID, &elemName);
5818 rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval());
5819 NS_ENSURE_SUCCESS(rv, true);
5821 return true;
5824 bool
5825 nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
5827 JS::Rooted<JSObject*> obj(aCx, aObject);
5828 return Preferences::GetBool("dom.webcomponents.enabled") ||
5829 IsInCertifiedApp(aCx, obj);
5832 nsresult
5833 nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
5835 if (!mRegistry) {
5836 return NS_OK;
5839 mozilla::dom::NodeInfo* info = aElement->NodeInfo();
5841 // Candidate may be a custom element through extension,
5842 // in which case the custom element type name will not
5843 // match the element tag name. e.g. <button is="x-button">.
5844 nsCOMPtr<nsIAtom> typeName = aTypeName;
5845 if (!typeName) {
5846 typeName = info->NameAtom();
5849 CustomElementHashKey key(info->NamespaceID(), typeName);
5850 if (mRegistry->mCustomDefinitions.Get(&key)) {
5851 return NS_OK;
5854 nsTArray<nsRefPtr<Element>>* unresolved;
5855 mRegistry->mCandidatesMap.Get(&key, &unresolved);
5856 if (!unresolved) {
5857 unresolved = new nsTArray<nsRefPtr<Element>>();
5858 // Ownership of unresolved is taken by mCandidatesMap.
5859 mRegistry->mCandidatesMap.Put(&key, unresolved);
5862 nsRefPtr<Element>* elem = unresolved->AppendElement();
5863 *elem = aElement;
5865 return NS_OK;
5868 namespace {
5870 class ProcessStackRunner MOZ_FINAL : public nsIRunnable
5872 ~ProcessStackRunner() {}
5873 public:
5874 explicit ProcessStackRunner(bool aIsBaseQueue = false)
5875 : mIsBaseQueue(aIsBaseQueue)
5878 NS_DECL_ISUPPORTS
5879 NS_IMETHOD Run() MOZ_OVERRIDE
5881 nsDocument::ProcessTopElementQueue(mIsBaseQueue);
5882 return NS_OK;
5884 bool mIsBaseQueue;
5887 NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable);
5889 } // anonymous namespace
5891 void
5892 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
5893 Element* aCustomElement,
5894 LifecycleCallbackArgs* aArgs,
5895 CustomElementDefinition* aDefinition)
5897 if (!mRegistry) {
5898 // The element might not belong to a document that
5899 // has a browsing context, and thus no registry.
5900 return;
5903 CustomElementData* elementData = aCustomElement->GetCustomElementData();
5905 // Let DEFINITION be ELEMENT's definition
5906 CustomElementDefinition* definition = aDefinition;
5907 if (!definition) {
5908 mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
5910 // Make sure we get the correct definition in case the element
5911 // is a extended custom element e.g. <button is="x-button">.
5912 nsCOMPtr<nsIAtom> typeAtom = elementData ?
5913 elementData->mType.get() : info->NameAtom();
5915 CustomElementHashKey key(info->NamespaceID(), typeAtom);
5916 if (!mRegistry->mCustomDefinitions.Get(&key, &definition) ||
5917 definition->mLocalName != info->NameAtom()) {
5918 // Trying to enqueue a callback for an element that is not
5919 // a custom element. We are done, nothing to do.
5920 return;
5924 if (!elementData) {
5925 // Create the custom element data the first time
5926 // that we try to enqueue a callback.
5927 elementData = new CustomElementData(definition->mType);
5928 // aCustomElement takes ownership of elementData
5929 aCustomElement->SetCustomElementData(elementData);
5930 MOZ_ASSERT(aType == nsIDocument::eCreated,
5931 "First callback should be the created callback");
5934 // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
5935 CallbackFunction* func = nullptr;
5936 switch (aType) {
5937 case nsIDocument::eCreated:
5938 if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
5939 func = definition->mCallbacks->mCreatedCallback.Value();
5941 break;
5943 case nsIDocument::eAttached:
5944 if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
5945 func = definition->mCallbacks->mAttachedCallback.Value();
5947 break;
5949 case nsIDocument::eDetached:
5950 if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
5951 func = definition->mCallbacks->mDetachedCallback.Value();
5953 break;
5955 case nsIDocument::eAttributeChanged:
5956 if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
5957 func = definition->mCallbacks->mAttributeChangedCallback.Value();
5959 break;
5962 // If there is no such callback, stop.
5963 if (!func) {
5964 return;
5967 if (aType == nsIDocument::eCreated) {
5968 elementData->mCreatedCallbackInvoked = false;
5969 } else if (!elementData->mCreatedCallbackInvoked) {
5970 // Callbacks other than created callback must not be enqueued
5971 // until after the created callback has been invoked.
5972 return;
5975 // Add CALLBACK to ELEMENT's callback queue.
5976 CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
5977 aType,
5978 func,
5979 elementData);
5980 // Ownership of callback is taken by mCallbackQueue.
5981 elementData->mCallbackQueue.AppendElement(callback);
5982 if (aArgs) {
5983 callback->SetArgs(*aArgs);
5986 if (!elementData->mElementIsBeingCreated) {
5987 CustomElementData* lastData =
5988 sProcessingStack->SafeLastElement(nullptr);
5990 // A new element queue needs to be pushed if the queue at the
5991 // top of the stack is associated with another microtask level.
5992 // Don't push a queue for the level 0 microtask (base element queue)
5993 // because we don't want to process the queue until the
5994 // microtask checkpoint.
5995 bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 &&
5996 (!lastData || lastData->mAssociatedMicroTask <
5997 static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
5999 // Push a new element queue onto the processing stack when appropriate
6000 // (when we enter a new microtask).
6001 if (shouldPushElementQueue) {
6002 // Push a sentinel value on the processing stack to mark the
6003 // boundary between the element queues.
6004 sProcessingStack->AppendElement((CustomElementData*) nullptr);
6007 sProcessingStack->AppendElement(elementData);
6008 elementData->mAssociatedMicroTask =
6009 static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
6011 // Add a script runner to pop and process the element queue at
6012 // the top of the processing stack.
6013 if (shouldPushElementQueue) {
6014 // Lifecycle callbacks enqueued by user agent implementation
6015 // should be invoked prior to returning control back to script.
6016 // Create a script runner to process the top of the processing
6017 // stack as soon as it is safe to run script.
6018 nsContentUtils::AddScriptRunner(new ProcessStackRunner());
6023 // static
6024 void
6025 nsDocument::ProcessBaseElementQueue()
6027 // Prevent re-entrance. Also, if a microtask checkpoint is reached
6028 // and there is no processing stack to process, then we are done.
6029 if (sProcessingBaseElementQueue || !sProcessingStack) {
6030 return;
6033 MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0);
6034 sProcessingBaseElementQueue = true;
6035 nsContentUtils::AddScriptRunner(new ProcessStackRunner(true));
6038 // static
6039 void
6040 nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
6042 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
6044 nsTArray<nsRefPtr<CustomElementData>>& stack = *sProcessingStack;
6045 uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
6047 if (aIsBaseQueue && firstQueue != 0) {
6048 return;
6051 for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
6052 // Callback queue may have already been processed in an earlier
6053 // element queue or in an element queue that was popped
6054 // off more recently.
6055 if (stack[i]->mAssociatedMicroTask != -1) {
6056 stack[i]->RunCallbackQueue();
6057 stack[i]->mAssociatedMicroTask = -1;
6061 // If this was actually the base element queue, don't bother trying to pop
6062 // the first "queue" marker (sentinel).
6063 if (firstQueue != 0) {
6064 stack.SetLength(firstQueue);
6065 } else {
6066 // Don't pop sentinel for base element queue.
6067 stack.SetLength(1);
6068 sProcessingBaseElementQueue = false;
6072 bool
6073 nsDocument::RegisterEnabled()
6075 static bool sPrefValue =
6076 Preferences::GetBool("dom.webcomponents.enabled", false);
6077 return sPrefValue;
6080 // static
6081 Maybe<nsTArray<nsRefPtr<mozilla::dom::CustomElementData>>>
6082 nsDocument::sProcessingStack;
6084 // static
6085 bool
6086 nsDocument::sProcessingBaseElementQueue;
6088 void
6089 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
6090 const ElementRegistrationOptions& aOptions,
6091 JS::MutableHandle<JSObject*> aRetval,
6092 ErrorResult& rv)
6094 if (!mRegistry) {
6095 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6096 return;
6099 Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
6101 // Unconditionally convert TYPE to lowercase.
6102 nsAutoString lcType;
6103 nsContentUtils::ASCIIToLower(aType, lcType);
6105 // Only convert NAME to lowercase in HTML documents. Note that NAME is
6106 // options.extends.
6107 nsAutoString lcName;
6108 if (IsHTML()) {
6109 nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
6110 } else {
6111 lcName.Assign(aOptions.mExtends);
6114 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
6115 if (!nsContentUtils::IsCustomElementName(typeAtom)) {
6116 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
6117 return;
6120 // If there already exists a definition with the same TYPE, set ERROR to
6121 // DuplicateDefinition and stop.
6122 // Note that we need to find existing custom elements from either namespace.
6123 CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom);
6124 if (definitions.Get(&duplicateFinder)) {
6125 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6126 return;
6129 nsIGlobalObject* sgo = GetScopeObject();
6130 if (!sgo) {
6131 rv.Throw(NS_ERROR_UNEXPECTED);
6132 return;
6135 JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
6136 nsCOMPtr<nsIAtom> nameAtom;
6137 int32_t namespaceID = kNameSpaceID_XHTML;
6138 JS::Rooted<JSObject*> protoObject(aCx);
6140 JSAutoCompartment ac(aCx, global);
6142 JS::Handle<JSObject*> htmlProto(
6143 HTMLElementBinding::GetProtoObjectHandle(aCx, global));
6144 if (!htmlProto) {
6145 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
6146 return;
6149 if (!aOptions.mPrototype) {
6150 protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr());
6151 if (!protoObject) {
6152 rv.Throw(NS_ERROR_UNEXPECTED);
6153 return;
6155 } else {
6156 protoObject = aOptions.mPrototype;
6158 // We are already operating on the document's (/global's) compartment. Let's
6159 // get a view of the passed in proto from this compartment.
6160 if (!JS_WrapObject(aCx, &protoObject)) {
6161 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6162 return;
6165 // We also need an unwrapped version of it for various checks.
6166 JS::Rooted<JSObject*> protoObjectUnwrapped(aCx,
6167 js::CheckedUnwrap(protoObject));
6168 if (!protoObjectUnwrapped) {
6169 // If the documents compartment does not have same origin access
6170 // to the compartment of the proto we should just throw.
6171 rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
6172 return;
6175 // If PROTOTYPE is already an interface prototype object for any interface
6176 // object or PROTOTYPE has a non-configurable property named constructor,
6177 // throw a NotSupportedError and stop.
6178 const js::Class* clasp = js::GetObjectClass(protoObjectUnwrapped);
6179 if (IsDOMIfaceAndProtoClass(clasp)) {
6180 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6181 return;
6184 JS::Rooted<JSPropertyDescriptor> descRoot(aCx);
6185 JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot);
6186 // This check will go through a wrapper, but as we checked above
6187 // it should be transparent or an xray. This should be fine for now,
6188 // until the spec is sorted out.
6189 if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
6190 rv.Throw(NS_ERROR_UNEXPECTED);
6191 return;
6194 // Check if non-configurable
6195 if (desc.isPermanent()) {
6196 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6197 return;
6200 JS::Handle<JSObject*> svgProto(
6201 SVGElementBinding::GetProtoObjectHandle(aCx, global));
6202 if (!svgProto) {
6203 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
6204 return;
6207 JS::Rooted<JSObject*> protoProto(aCx, protoObject);
6209 // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
6210 // Namespace.
6211 while (protoProto) {
6212 if (protoProto == htmlProto) {
6213 break;
6216 if (protoProto == svgProto) {
6217 namespaceID = kNameSpaceID_SVG;
6218 break;
6221 if (!JS_GetPrototype(aCx, protoProto, &protoProto)) {
6222 rv.Throw(NS_ERROR_UNEXPECTED);
6223 return;
6228 // If name was provided and not null...
6229 if (!lcName.IsEmpty()) {
6230 // Let BASE be the element interface for NAME and NAMESPACE.
6231 bool known = false;
6232 nameAtom = do_GetAtom(lcName);
6233 if (namespaceID == kNameSpaceID_XHTML) {
6234 nsIParserService* ps = nsContentUtils::GetParserService();
6235 if (!ps) {
6236 rv.Throw(NS_ERROR_UNEXPECTED);
6237 return;
6240 known =
6241 ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined;
6242 } else {
6243 known = SVGElementFactory::Exists(nameAtom);
6246 // If BASE does not exist or is an interface for a custom element, set ERROR
6247 // to InvalidName and stop.
6248 // If BASE exists, then it cannot be an interface for a custom element.
6249 if (!known) {
6250 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
6251 return;
6253 } else {
6254 // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
6255 if (namespaceID == kNameSpaceID_SVG) {
6256 rv.Throw(NS_ERROR_UNEXPECTED);
6257 return;
6260 nameAtom = typeAtom;
6262 } // Leaving the document's compartment for the LifecycleCallbacks init
6264 // Note: We call the init from the caller compartment here
6265 nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
6266 JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject));
6267 if (!JS_WrapValue(aCx, &rootedv) || !callbacksHolder->Init(aCx, rootedv)) {
6268 rv.Throw(NS_ERROR_FAILURE);
6269 return;
6272 // Entering the global's compartment again
6273 JSAutoCompartment ac(aCx, global);
6275 // Associate the definition with the custom element.
6276 CustomElementHashKey key(namespaceID, typeAtom);
6277 LifecycleCallbacks* callbacks = callbacksHolder.forget();
6278 CustomElementDefinition* definition =
6279 new CustomElementDefinition(protoObject,
6280 typeAtom,
6281 nameAtom,
6282 callbacks,
6283 namespaceID,
6284 0 /* TODO dependent on HTML imports. Bug 877072 */);
6285 definitions.Put(&key, definition);
6287 // Do element upgrade.
6288 nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates;
6289 mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
6290 if (candidates) {
6291 for (size_t i = 0; i < candidates->Length(); ++i) {
6292 Element *elem = candidates->ElementAt(i);
6294 // Make sure that the element name matches the name in the definition.
6295 // (e.g. a definition for x-button extending button should match
6296 // <button is="x-button"> but not <x-button>.
6297 if (elem->NodeInfo()->NameAtom() != nameAtom) {
6298 // Skip over this element because definition does not apply.
6299 continue;
6302 nsWrapperCache* cache;
6303 CallQueryInterface(elem, &cache);
6304 MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
6306 JS::RootedObject wrapper(aCx);
6307 if ((wrapper = cache->GetWrapper())) {
6308 if (!JS_SetPrototype(aCx, wrapper, protoObject)) {
6309 continue;
6313 EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
6317 // Create constructor to return. Store the name of the custom element as the
6318 // name of the function.
6319 JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
6320 JSFUN_CONSTRUCTOR, JS::NullPtr(),
6321 NS_ConvertUTF16toUTF8(lcType).get());
6322 if (!constructor) {
6323 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
6324 return;
6327 JS::Rooted<JSObject*> constructorObj(aCx, JS_GetFunctionObject(constructor));
6328 if (!JS_LinkConstructorAndPrototype(aCx, constructorObj, protoObject)) {
6329 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6330 return;
6333 aRetval.set(constructorObj);
6336 void
6337 nsDocument::UseRegistryFromDocument(nsIDocument* aDocument)
6339 nsDocument* doc = static_cast<nsDocument*>(aDocument);
6340 MOZ_ASSERT(!mRegistry, "There should be no existing registry.");
6341 mRegistry = doc->mRegistry;
6344 NS_IMETHODIMP
6345 nsDocument::GetElementsByTagName(const nsAString& aTagname,
6346 nsIDOMNodeList** aReturn)
6348 nsRefPtr<nsContentList> list = GetElementsByTagName(aTagname);
6349 NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
6351 // transfer ref to aReturn
6352 list.forget(aReturn);
6353 return NS_OK;
6356 long
6357 nsDocument::BlockedTrackingNodeCount() const
6359 return mBlockedTrackingNodes.Length();
6362 already_AddRefed<nsSimpleContentList>
6363 nsDocument::BlockedTrackingNodes() const
6365 nsRefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
6367 nsTArray<nsWeakPtr> blockedTrackingNodes;
6368 blockedTrackingNodes = mBlockedTrackingNodes;
6370 for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
6371 nsWeakPtr weakNode = blockedTrackingNodes[i];
6372 nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
6373 // Consider only nodes to which we have managed to get strong references.
6374 // Coping with nullptrs since it's expected for nodes to disappear when
6375 // nobody else is referring to them.
6376 if (node) {
6377 list->AppendElement(node);
6381 return list.forget();
6384 already_AddRefed<nsContentList>
6385 nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6386 const nsAString& aLocalName,
6387 ErrorResult& aResult)
6389 int32_t nameSpaceId = kNameSpaceID_Wildcard;
6391 if (!aNamespaceURI.EqualsLiteral("*")) {
6392 aResult =
6393 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
6394 nameSpaceId);
6395 if (aResult.Failed()) {
6396 return nullptr;
6400 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
6402 return NS_GetContentList(this, nameSpaceId, aLocalName);
6405 NS_IMETHODIMP
6406 nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6407 const nsAString& aLocalName,
6408 nsIDOMNodeList** aReturn)
6410 ErrorResult rv;
6411 nsRefPtr<nsContentList> list =
6412 nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
6413 if (rv.Failed()) {
6414 return rv.ErrorCode();
6417 // transfer ref to aReturn
6418 list.forget(aReturn);
6419 return NS_OK;
6422 NS_IMETHODIMP
6423 nsDocument::GetAsync(bool *aAsync)
6425 NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
6427 return NS_ERROR_NOT_IMPLEMENTED;
6430 NS_IMETHODIMP
6431 nsDocument::SetAsync(bool aAsync)
6433 NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!");
6435 return NS_ERROR_NOT_IMPLEMENTED;
6438 NS_IMETHODIMP
6439 nsDocument::Load(const nsAString& aUrl, bool *aReturn)
6441 NS_ERROR("nsDocument::Load() should be overriden by subclass!");
6443 return NS_ERROR_NOT_IMPLEMENTED;
6446 NS_IMETHODIMP
6447 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
6449 NS_ADDREF(*aStyleSheets = StyleSheets());
6450 return NS_OK;
6453 StyleSheetList*
6454 nsDocument::StyleSheets()
6456 if (!mDOMStyleSheets) {
6457 mDOMStyleSheets = new nsDOMStyleSheetList(this);
6459 return mDOMStyleSheets;
6462 NS_IMETHODIMP
6463 nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet)
6465 nsIDocument::GetSelectedStyleSheetSet(aSheetSet);
6466 return NS_OK;
6469 void
6470 nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
6472 aSheetSet.Truncate();
6474 // Look through our sheets, find the selected set title
6475 int32_t count = GetNumberOfStyleSheets();
6476 nsAutoString title;
6477 for (int32_t index = 0; index < count; index++) {
6478 nsIStyleSheet* sheet = GetStyleSheetAt(index);
6479 NS_ASSERTION(sheet, "Null sheet in sheet list!");
6481 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet);
6482 NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet");
6483 bool disabled;
6484 domSheet->GetDisabled(&disabled);
6485 if (disabled) {
6486 // Disabled sheets don't affect the currently selected set
6487 continue;
6490 sheet->GetTitle(title);
6492 if (aSheetSet.IsEmpty()) {
6493 aSheetSet = title;
6494 } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
6495 // Sheets from multiple sets enabled; return null string, per spec.
6496 SetDOMStringToNull(aSheetSet);
6497 return;
6502 NS_IMETHODIMP
6503 nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet)
6505 SetSelectedStyleSheetSet(aSheetSet);
6506 return NS_OK;
6509 void
6510 nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
6512 if (DOMStringIsNull(aSheetSet)) {
6513 return;
6516 // Must update mLastStyleSheetSet before doing anything else with stylesheets
6517 // or CSSLoaders.
6518 mLastStyleSheetSet = aSheetSet;
6519 EnableStyleSheetsForSetInternal(aSheetSet, true);
6522 NS_IMETHODIMP
6523 nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet)
6525 nsString sheetSet;
6526 GetLastStyleSheetSet(sheetSet);
6527 aSheetSet = sheetSet;
6528 return NS_OK;
6531 void
6532 nsDocument::GetLastStyleSheetSet(nsString& aSheetSet)
6534 aSheetSet = mLastStyleSheetSet;
6537 NS_IMETHODIMP
6538 nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6540 nsIDocument::GetPreferredStyleSheetSet(aSheetSet);
6541 return NS_OK;
6544 void
6545 nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6547 GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
6550 NS_IMETHODIMP
6551 nsDocument::GetStyleSheetSets(nsISupports** aList)
6553 NS_ADDREF(*aList = StyleSheetSets());
6554 return NS_OK;
6557 DOMStringList*
6558 nsDocument::StyleSheetSets()
6560 if (!mStyleSheetSetList) {
6561 mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
6563 return mStyleSheetSetList;
6566 NS_IMETHODIMP
6567 nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet)
6569 EnableStyleSheetsForSet(aSheetSet);
6570 return NS_OK;
6573 void
6574 nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
6576 // Per spec, passing in null is a no-op.
6577 if (!DOMStringIsNull(aSheetSet)) {
6578 // Note: must make sure to not change the CSSLoader's preferred sheet --
6579 // that value should be equal to either our lastStyleSheetSet (if that's
6580 // non-null) or to our preferredStyleSheetSet. And this method doesn't
6581 // change either of those.
6582 EnableStyleSheetsForSetInternal(aSheetSet, false);
6586 void
6587 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
6588 bool aUpdateCSSLoader)
6590 BeginUpdate(UPDATE_STYLE);
6591 int32_t count = GetNumberOfStyleSheets();
6592 nsAutoString title;
6593 for (int32_t index = 0; index < count; index++) {
6594 nsIStyleSheet* sheet = GetStyleSheetAt(index);
6595 NS_ASSERTION(sheet, "Null sheet in sheet list!");
6596 sheet->GetTitle(title);
6597 if (!title.IsEmpty()) {
6598 sheet->SetEnabled(title.Equals(aSheetSet));
6601 if (aUpdateCSSLoader) {
6602 CSSLoader()->SetPreferredSheet(aSheetSet);
6604 EndUpdate(UPDATE_STYLE);
6607 NS_IMETHODIMP
6608 nsDocument::GetCharacterSet(nsAString& aCharacterSet)
6610 nsIDocument::GetCharacterSet(aCharacterSet);
6611 return NS_OK;
6614 void
6615 nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
6617 CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet);
6620 NS_IMETHODIMP
6621 nsDocument::ImportNode(nsIDOMNode* aImportedNode,
6622 bool aDeep,
6623 uint8_t aArgc,
6624 nsIDOMNode** aResult)
6626 if (aArgc == 0) {
6627 aDeep = true;
6630 *aResult = nullptr;
6632 nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode);
6633 NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED);
6635 ErrorResult rv;
6636 nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv);
6637 if (rv.Failed()) {
6638 return rv.ErrorCode();
6641 NS_ADDREF(*aResult = result->AsDOMNode());
6642 return NS_OK;
6645 already_AddRefed<nsINode>
6646 nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
6648 nsINode* imported = &aNode;
6650 switch (imported->NodeType()) {
6651 case nsIDOMNode::ATTRIBUTE_NODE:
6652 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
6653 case nsIDOMNode::ELEMENT_NODE:
6654 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
6655 case nsIDOMNode::TEXT_NODE:
6656 case nsIDOMNode::CDATA_SECTION_NODE:
6657 case nsIDOMNode::COMMENT_NODE:
6658 case nsIDOMNode::DOCUMENT_TYPE_NODE:
6660 nsCOMPtr<nsINode> newNode;
6661 nsCOMArray<nsINode> nodesWithProperties;
6662 rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
6663 nodesWithProperties, getter_AddRefs(newNode));
6664 if (rv.Failed()) {
6665 return nullptr;
6667 return newNode.forget();
6669 default:
6671 NS_WARNING("Don't know how to clone this nodetype for importNode.");
6673 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6677 return nullptr;
6680 NS_IMETHODIMP
6681 nsDocument::LoadBindingDocument(const nsAString& aURI)
6683 ErrorResult rv;
6684 nsIDocument::LoadBindingDocument(aURI, rv);
6685 return rv.ErrorCode();
6688 void
6689 nsIDocument::LoadBindingDocument(const nsAString& aURI, ErrorResult& rv)
6691 nsCOMPtr<nsIURI> uri;
6692 rv = NS_NewURI(getter_AddRefs(uri), aURI,
6693 mCharacterSet.get(),
6694 GetDocBaseURI());
6695 if (rv.Failed()) {
6696 return;
6699 // Note - This computation of subjectPrincipal isn't necessarily sensical.
6700 // It's just designed to preserve the old semantics during a mass-conversion
6701 // patch.
6702 nsCOMPtr<nsIPrincipal> subjectPrincipal =
6703 nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal()
6704 : NodePrincipal();
6705 BindingManager()->LoadBindingDocument(this, uri, subjectPrincipal);
6708 NS_IMETHODIMP
6709 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
6711 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
6712 NS_ENSURE_ARG_POINTER(node);
6714 Element* bindingParent = nsIDocument::GetBindingParent(*node);
6715 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent);
6716 retval.forget(aResult);
6717 return NS_OK;
6720 Element*
6721 nsIDocument::GetBindingParent(nsINode& aNode)
6723 nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
6724 if (!content)
6725 return nullptr;
6727 nsIContent* bindingParent = content->GetBindingParent();
6728 return bindingParent ? bindingParent->AsElement() : nullptr;
6731 static Element*
6732 GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
6733 const nsAString& aAttrValue, bool aUniversalMatch)
6735 if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
6736 aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
6737 aAttrValue, eCaseMatters)) {
6738 return aContent->AsElement();
6741 for (nsIContent* child = aContent->GetFirstChild();
6742 child;
6743 child = child->GetNextSibling()) {
6745 Element* matchedElement =
6746 GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch);
6747 if (matchedElement)
6748 return matchedElement;
6751 return nullptr;
6754 Element*
6755 nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
6756 nsIAtom* aAttrName,
6757 const nsAString& aAttrValue) const
6759 nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
6760 if (!nodeList)
6761 return nullptr;
6763 uint32_t length = 0;
6764 nodeList->GetLength(&length);
6766 bool universalMatch = aAttrValue.EqualsLiteral("*");
6768 for (uint32_t i = 0; i < length; ++i) {
6769 nsIContent* current = nodeList->Item(i);
6770 Element* matchedElm =
6771 GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
6772 if (matchedElm)
6773 return matchedElm;
6776 return nullptr;
6779 NS_IMETHODIMP
6780 nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
6781 const nsAString& aAttrName,
6782 const nsAString& aAttrValue,
6783 nsIDOMElement** aResult)
6785 nsCOMPtr<Element> element = do_QueryInterface(aElement);
6786 NS_ENSURE_ARG_POINTER(element);
6788 Element* anonEl =
6789 nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName,
6790 aAttrValue);
6791 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl);
6792 retval.forget(aResult);
6793 return NS_OK;
6796 Element*
6797 nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
6798 const nsAString& aAttrName,
6799 const nsAString& aAttrValue)
6801 nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
6803 return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
6807 NS_IMETHODIMP
6808 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
6809 nsIDOMNodeList** aResult)
6811 *aResult = nullptr;
6813 nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
6814 return BindingManager()->GetAnonymousNodesFor(content, aResult);
6817 nsINodeList*
6818 nsIDocument::GetAnonymousNodes(Element& aElement)
6820 return BindingManager()->GetAnonymousNodesFor(&aElement);
6823 NS_IMETHODIMP
6824 nsDocument::CreateRange(nsIDOMRange** aReturn)
6826 ErrorResult rv;
6827 *aReturn = nsIDocument::CreateRange(rv).take();
6828 return rv.ErrorCode();
6831 already_AddRefed<nsRange>
6832 nsIDocument::CreateRange(ErrorResult& rv)
6834 nsRefPtr<nsRange> range = new nsRange(this);
6835 nsresult res = range->Set(this, 0, this, 0);
6836 if (NS_FAILED(res)) {
6837 rv.Throw(res);
6838 return nullptr;
6841 return range.forget();
6844 NS_IMETHODIMP
6845 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
6846 uint32_t aWhatToShow,
6847 nsIDOMNodeFilter *aFilter,
6848 uint8_t aOptionalArgc,
6849 nsIDOMNodeIterator **_retval)
6851 *_retval = nullptr;
6853 if (!aOptionalArgc) {
6854 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6857 if (!aRoot) {
6858 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
6861 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6862 NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
6864 ErrorResult rv;
6865 NodeFilterHolder holder(aFilter);
6866 *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow, holder,
6867 rv).take();
6868 return rv.ErrorCode();
6871 already_AddRefed<NodeIterator>
6872 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6873 NodeFilter* aFilter,
6874 ErrorResult& rv) const
6876 NodeFilterHolder holder(aFilter);
6877 return CreateNodeIterator(aRoot, aWhatToShow, holder, rv);
6880 already_AddRefed<NodeIterator>
6881 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6882 const NodeFilterHolder& aFilter,
6883 ErrorResult& rv) const
6885 nsINode* root = &aRoot;
6886 nsRefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow,
6887 aFilter);
6888 return iterator.forget();
6891 NS_IMETHODIMP
6892 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
6893 uint32_t aWhatToShow,
6894 nsIDOMNodeFilter *aFilter,
6895 uint8_t aOptionalArgc,
6896 nsIDOMTreeWalker **_retval)
6898 *_retval = nullptr;
6900 if (!aOptionalArgc) {
6901 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6904 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6905 NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6907 ErrorResult rv;
6908 NodeFilterHolder holder(aFilter);
6909 *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow, holder,
6910 rv).take();
6911 return rv.ErrorCode();
6914 already_AddRefed<TreeWalker>
6915 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6916 NodeFilter* aFilter,
6917 ErrorResult& rv) const
6919 NodeFilterHolder holder(aFilter);
6920 return CreateTreeWalker(aRoot, aWhatToShow, holder, rv);
6923 already_AddRefed<TreeWalker>
6924 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6925 const NodeFilterHolder& aFilter,
6926 ErrorResult& rv) const
6928 nsINode* root = &aRoot;
6929 nsRefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, aFilter);
6930 return walker.forget();
6934 NS_IMETHODIMP
6935 nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView)
6937 *aDefaultView = nullptr;
6938 nsCOMPtr<nsPIDOMWindow> win = GetWindow();
6939 win.forget(aDefaultView);
6940 return NS_OK;
6943 NS_IMETHODIMP
6944 nsDocument::GetLocation(nsIDOMLocation **_retval)
6946 *_retval = nsIDocument::GetLocation().take();
6947 return NS_OK;
6950 already_AddRefed<nsLocation>
6951 nsIDocument::GetLocation() const
6953 nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject);
6955 if (!w) {
6956 return nullptr;
6959 nsCOMPtr<nsIDOMLocation> loc;
6960 w->GetLocation(getter_AddRefs(loc));
6961 return loc.forget().downcast<nsLocation>();
6964 Element*
6965 nsIDocument::GetHtmlElement() const
6967 Element* rootElement = GetRootElement();
6968 if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
6969 return rootElement;
6970 return nullptr;
6973 Element*
6974 nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
6976 Element* html = GetHtmlElement();
6977 if (!html)
6978 return nullptr;
6980 // Look for the element with aTag inside html. This needs to run
6981 // forwards to find the first such element.
6982 for (nsIContent* child = html->GetFirstChild();
6983 child;
6984 child = child->GetNextSibling()) {
6985 if (child->IsHTML(aTag))
6986 return child->AsElement();
6988 return nullptr;
6991 nsIContent*
6992 nsDocument::GetTitleContent(uint32_t aNamespace)
6994 // mMayHaveTitleElement will have been set to true if any HTML or SVG
6995 // <title> element has been bound to this document. So if it's false,
6996 // we know there is nothing to do here. This avoids us having to search
6997 // the whole DOM if someone calls document.title on a large document
6998 // without a title.
6999 if (!mMayHaveTitleElement)
7000 return nullptr;
7002 nsRefPtr<nsContentList> list =
7003 NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title"));
7005 return list->Item(0, false);
7008 void
7009 nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle)
7011 nsIContent* title = GetTitleContent(aNamespace);
7012 if (!title)
7013 return;
7014 if(!nsContentUtils::GetNodeTextContent(title, false, aTitle))
7015 NS_RUNTIMEABORT("OOM");
7018 NS_IMETHODIMP
7019 nsDocument::GetTitle(nsAString& aTitle)
7021 nsString title;
7022 GetTitle(title);
7023 aTitle = title;
7024 return NS_OK;
7027 void
7028 nsDocument::GetTitle(nsString& aTitle)
7030 aTitle.Truncate();
7032 nsIContent *rootElement = GetRootElement();
7033 if (!rootElement)
7034 return;
7036 nsAutoString tmp;
7038 switch (rootElement->GetNameSpaceID()) {
7039 #ifdef MOZ_XUL
7040 case kNameSpaceID_XUL:
7041 rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
7042 break;
7043 #endif
7044 case kNameSpaceID_SVG:
7045 if (rootElement->Tag() == nsGkAtoms::svg) {
7046 GetTitleFromElement(kNameSpaceID_SVG, tmp);
7047 break;
7048 } // else fall through
7049 default:
7050 GetTitleFromElement(kNameSpaceID_XHTML, tmp);
7051 break;
7054 tmp.CompressWhitespace();
7055 aTitle = tmp;
7058 NS_IMETHODIMP
7059 nsDocument::SetTitle(const nsAString& aTitle)
7061 Element *rootElement = GetRootElement();
7062 if (!rootElement)
7063 return NS_OK;
7065 switch (rootElement->GetNameSpaceID()) {
7066 case kNameSpaceID_SVG:
7067 return NS_OK; // SVG doesn't support setting a title
7068 #ifdef MOZ_XUL
7069 case kNameSpaceID_XUL:
7070 return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
7071 aTitle, true);
7072 #endif
7075 // Batch updates so that mutation events don't change "the title
7076 // element" under us
7077 mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
7079 nsIContent* title = GetTitleContent(kNameSpaceID_XHTML);
7080 if (!title) {
7081 Element *head = GetHeadElement();
7082 if (!head)
7083 return NS_OK;
7086 nsRefPtr<mozilla::dom::NodeInfo> titleInfo;
7087 titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
7088 kNameSpaceID_XHTML,
7089 nsIDOMNode::ELEMENT_NODE);
7090 title = NS_NewHTMLTitleElement(titleInfo.forget());
7091 if (!title)
7092 return NS_OK;
7095 head->AppendChildTo(title, true);
7098 return nsContentUtils::SetNodeTextContent(title, aTitle, false);
7101 void
7102 nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv)
7104 rv = SetTitle(aTitle);
7107 void
7108 nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
7110 NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
7111 "Setting a title while unlinking or destroying the element?");
7112 if (mInUnlinkOrDeletion) {
7113 return;
7116 if (aBoundTitleElement) {
7117 mMayHaveTitleElement = true;
7119 if (mPendingTitleChangeEvent.IsPending())
7120 return;
7122 nsRefPtr<nsRunnableMethod<nsDocument, void, false> > event =
7123 NS_NewNonOwningRunnableMethod(this,
7124 &nsDocument::DoNotifyPossibleTitleChange);
7125 nsresult rv = NS_DispatchToCurrentThread(event);
7126 if (NS_SUCCEEDED(rv)) {
7127 mPendingTitleChangeEvent = event;
7131 void
7132 nsDocument::DoNotifyPossibleTitleChange()
7134 mPendingTitleChangeEvent.Forget();
7135 mHaveFiredTitleChange = true;
7137 nsAutoString title;
7138 GetTitle(title);
7140 nsCOMPtr<nsIPresShell> shell = GetShell();
7141 if (shell) {
7142 nsCOMPtr<nsISupports> container =
7143 shell->GetPresContext()->GetContainerWeak();
7144 if (container) {
7145 nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
7146 if (docShellWin) {
7147 docShellWin->SetTitle(title.get());
7152 // Fire a DOM event for the title change.
7153 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
7154 NS_LITERAL_STRING("DOMTitleChanged"),
7155 true, true);
7158 already_AddRefed<BoxObject>
7159 nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
7161 if (!aElement) {
7162 aRv.Throw(NS_ERROR_UNEXPECTED);
7163 return nullptr;
7166 nsIDocument* doc = aElement->OwnerDoc();
7167 if (doc != this) {
7168 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
7169 return nullptr;
7172 if (!mHasWarnedAboutBoxObjects && !aElement->IsXUL()) {
7173 mHasWarnedAboutBoxObjects = true;
7174 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
7175 NS_LITERAL_CSTRING("BoxObjects"), this,
7176 nsContentUtils::eDOM_PROPERTIES,
7177 "UseOfGetBoxObjectForWarning");
7180 if (!mBoxObjectTable) {
7181 mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(6);
7182 } else {
7183 nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement);
7184 if (boxObject) {
7185 return boxObject.forget().downcast<BoxObject>();
7189 int32_t namespaceID;
7190 nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
7192 nsAutoCString contractID("@mozilla.org/layout/xul-boxobject");
7193 if (namespaceID == kNameSpaceID_XUL) {
7194 if (tag == nsGkAtoms::browser ||
7195 tag == nsGkAtoms::editor ||
7196 tag == nsGkAtoms::iframe)
7197 contractID += "-container";
7198 else if (tag == nsGkAtoms::menu)
7199 contractID += "-menu";
7200 else if (tag == nsGkAtoms::popup ||
7201 tag == nsGkAtoms::menupopup ||
7202 tag == nsGkAtoms::panel ||
7203 tag == nsGkAtoms::tooltip)
7204 contractID += "-popup";
7205 else if (tag == nsGkAtoms::tree)
7206 contractID += "-tree";
7207 else if (tag == nsGkAtoms::listbox)
7208 contractID += "-listbox";
7209 else if (tag == nsGkAtoms::scrollbox)
7210 contractID += "-scrollbox";
7212 contractID += ";1";
7214 nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get()));
7215 if (!boxObject) {
7216 aRv.Throw(NS_ERROR_FAILURE);
7217 return nullptr;
7220 boxObject->Init(aElement);
7222 if (mBoxObjectTable) {
7223 mBoxObjectTable->Put(aElement, boxObject.get());
7226 return boxObject.forget().downcast<BoxObject>();
7229 void
7230 nsDocument::ClearBoxObjectFor(nsIContent* aContent)
7232 if (mBoxObjectTable) {
7233 nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
7234 if (boxObject) {
7235 boxObject->Clear();
7236 mBoxObjectTable->Remove(aContent);
7241 already_AddRefed<MediaQueryList>
7242 nsIDocument::MatchMedia(const nsAString& aMediaQueryList)
7244 nsRefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
7246 // Insert the new item at the end of the linked list.
7247 PR_INSERT_BEFORE(result, &mDOMMediaQueryLists);
7249 return result.forget();
7252 void
7253 nsDocument::FlushSkinBindings()
7255 BindingManager()->FlushSkinBindings();
7258 nsresult
7259 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
7261 mInitializableFrameLoaders.RemoveElement(aLoader);
7262 // Don't even try to initialize.
7263 if (mInDestructor) {
7264 NS_WARNING("Trying to initialize a frame loader while"
7265 "document is being deleted");
7266 return NS_ERROR_FAILURE;
7269 mInitializableFrameLoaders.AppendElement(aLoader);
7270 if (!mFrameLoaderRunner) {
7271 mFrameLoaderRunner =
7272 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7273 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
7274 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7276 return NS_OK;
7279 nsresult
7280 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
7282 mInitializableFrameLoaders.RemoveElement(aLoader);
7283 if (mInDestructor) {
7284 return NS_ERROR_FAILURE;
7287 mFinalizableFrameLoaders.AppendElement(aLoader);
7288 if (!mFrameLoaderRunner) {
7289 mFrameLoaderRunner =
7290 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7291 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
7292 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7294 return NS_OK;
7297 void
7298 nsDocument::MaybeInitializeFinalizeFrameLoaders()
7300 if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
7301 // This method will be recalled when mUpdateNestLevel drops to 0,
7302 // or when !mDelayFrameLoaderInitialization.
7303 mFrameLoaderRunner = nullptr;
7304 return;
7307 // We're not in an update, but it is not safe to run scripts, so
7308 // postpone frameloader initialization and finalization.
7309 if (!nsContentUtils::IsSafeToRunScript()) {
7310 if (!mInDestructor && !mFrameLoaderRunner &&
7311 (mInitializableFrameLoaders.Length() ||
7312 mFinalizableFrameLoaders.Length())) {
7313 mFrameLoaderRunner =
7314 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7315 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7317 return;
7319 mFrameLoaderRunner = nullptr;
7321 // Don't use a temporary array for mInitializableFrameLoaders, because
7322 // loading a frame may cause some other frameloader to be removed from the
7323 // array. But be careful to keep the loader alive when starting the load!
7324 while (mInitializableFrameLoaders.Length()) {
7325 nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
7326 mInitializableFrameLoaders.RemoveElementAt(0);
7327 NS_ASSERTION(loader, "null frameloader in the array?");
7328 loader->ReallyStartLoading();
7331 uint32_t length = mFinalizableFrameLoaders.Length();
7332 if (length > 0) {
7333 nsTArray<nsRefPtr<nsFrameLoader> > loaders;
7334 mFinalizableFrameLoaders.SwapElements(loaders);
7335 for (uint32_t i = 0; i < length; ++i) {
7336 loaders[i]->Finalize();
7341 void
7342 nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
7344 uint32_t length = mInitializableFrameLoaders.Length();
7345 for (uint32_t i = 0; i < length; ++i) {
7346 if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
7347 mInitializableFrameLoaders.RemoveElementAt(i);
7348 return;
7353 bool
7354 nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
7356 if (aShell) {
7357 uint32_t length = mFinalizableFrameLoaders.Length();
7358 for (uint32_t i = 0; i < length; ++i) {
7359 if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
7360 return true;
7364 return false;
7367 nsIDocument*
7368 nsDocument::RequestExternalResource(nsIURI* aURI,
7369 nsINode* aRequestingNode,
7370 ExternalResourceLoad** aPendingLoad)
7372 NS_PRECONDITION(aURI, "Must have a URI");
7373 NS_PRECONDITION(aRequestingNode, "Must have a node");
7374 if (mDisplayDocument) {
7375 return mDisplayDocument->RequestExternalResource(aURI,
7376 aRequestingNode,
7377 aPendingLoad);
7380 return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
7381 this, aPendingLoad);
7384 void
7385 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
7387 mExternalResourceMap.EnumerateResources(aCallback, aData);
7390 nsSMILAnimationController*
7391 nsDocument::GetAnimationController()
7393 // We create the animation controller lazily because most documents won't want
7394 // one and only SVG documents and the like will call this
7395 if (mAnimationController)
7396 return mAnimationController;
7397 // Refuse to create an Animation Controller for data documents.
7398 if (mLoadedAsData || mLoadedAsInteractiveData)
7399 return nullptr;
7401 mAnimationController = new nsSMILAnimationController(this);
7403 // If there's a presContext then check the animation mode and pause if
7404 // necessary.
7405 nsIPresShell *shell = GetShell();
7406 if (mAnimationController && shell) {
7407 nsPresContext *context = shell->GetPresContext();
7408 if (context &&
7409 context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
7410 mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
7414 // If we're hidden (or being hidden), notify the newly-created animation
7415 // controller. (Skip this check for SVG-as-an-image documents, though,
7416 // because they don't get OnPageShow / OnPageHide calls).
7417 if (!mIsShowing && !mIsBeingUsedAsImage) {
7418 mAnimationController->OnPageHide();
7421 return mAnimationController;
7424 PendingPlayerTracker*
7425 nsDocument::GetOrCreatePendingPlayerTracker()
7427 if (!mPendingPlayerTracker) {
7428 mPendingPlayerTracker = new PendingPlayerTracker(this);
7431 return mPendingPlayerTracker;
7435 * Retrieve the "direction" property of the document.
7437 * @lina 01/09/2001
7439 NS_IMETHODIMP
7440 nsDocument::GetDir(nsAString& aDirection)
7442 nsIDocument::GetDir(aDirection);
7443 return NS_OK;
7446 void
7447 nsIDocument::GetDir(nsAString& aDirection) const
7449 aDirection.Truncate();
7450 Element* rootElement = GetHtmlElement();
7451 if (rootElement) {
7452 static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
7457 * Set the "direction" property of the document.
7459 * @lina 01/09/2001
7461 NS_IMETHODIMP
7462 nsDocument::SetDir(const nsAString& aDirection)
7464 nsIDocument::SetDir(aDirection);
7465 return NS_OK;
7468 void
7469 nsIDocument::SetDir(const nsAString& aDirection)
7471 Element* rootElement = GetHtmlElement();
7472 if (rootElement) {
7473 rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
7474 aDirection, true);
7478 NS_IMETHODIMP
7479 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
7481 nsIDocument::GetInputEncoding(aInputEncoding);
7482 return NS_OK;
7485 void
7486 nsIDocument::GetInputEncoding(nsAString& aInputEncoding)
7488 // Not const function, because WarnOnceAbout is not a const method
7489 WarnOnceAbout(eInputEncoding);
7490 if (mHaveInputEncoding) {
7491 return GetCharacterSet(aInputEncoding);
7494 SetDOMStringToNull(aInputEncoding);
7497 NS_IMETHODIMP
7498 nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument)
7500 *aSyntheticDocument = mIsSyntheticDocument;
7501 return NS_OK;
7504 NS_IMETHODIMP
7505 nsDocument::GetDocumentURI(nsAString& aDocumentURI)
7507 nsString temp;
7508 nsIDocument::GetDocumentURI(temp);
7509 aDocumentURI = temp;
7510 return NS_OK;
7513 void
7514 nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
7516 if (mDocumentURI) {
7517 nsAutoCString uri;
7518 mDocumentURI->GetSpec(uri);
7519 CopyUTF8toUTF16(uri, aDocumentURI);
7520 } else {
7521 aDocumentURI.Truncate();
7525 // Alias of above
7526 NS_IMETHODIMP
7527 nsDocument::GetURL(nsAString& aURL)
7529 return GetDocumentURI(aURL);
7532 void
7533 nsIDocument::GetURL(nsString& aURL) const
7535 return GetDocumentURI(aURL);
7538 void
7539 nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI) const
7541 if (!mChromeXHRDocURI || !nsContentUtils::IsCallerChrome()) {
7542 return GetDocumentURI(aDocumentURI);
7545 nsAutoCString uri;
7546 mChromeXHRDocURI->GetSpec(uri);
7547 CopyUTF8toUTF16(uri, aDocumentURI);
7550 nsIURI*
7551 nsIDocument::GetDocumentURIObject() const
7553 if (!mChromeXHRDocURI) {
7554 return GetDocumentURI();
7557 return mChromeXHRDocURI;
7561 // readonly attribute DOMString compatMode;
7562 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
7563 // in almost standards or full standards mode. See bug 105640. This was
7564 // implemented to match MSIE's compatMode property.
7565 NS_IMETHODIMP
7566 nsDocument::GetCompatMode(nsAString& aCompatMode)
7568 nsString temp;
7569 nsIDocument::GetCompatMode(temp);
7570 aCompatMode = temp;
7571 return NS_OK;
7574 void
7575 nsIDocument::GetCompatMode(nsString& aCompatMode) const
7577 NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
7578 mCompatMode == eCompatibility_AlmostStandards ||
7579 mCompatMode == eCompatibility_FullStandards,
7580 "mCompatMode is neither quirks nor strict for this document");
7582 if (mCompatMode == eCompatibility_NavQuirks) {
7583 aCompatMode.AssignLiteral("BackCompat");
7584 } else {
7585 aCompatMode.AssignLiteral("CSS1Compat");
7589 static void BlastSubtreeToPieces(nsINode *aNode);
7591 PLDHashOperator
7592 BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg)
7594 nsCOMPtr<nsIAttribute> *attr =
7595 static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
7597 *attr = aData;
7599 NS_ASSERTION(attr->get(),
7600 "non-nsIAttribute somehow made it into the hashmap?!");
7602 return PL_DHASH_STOP;
7605 static void
7606 BlastSubtreeToPieces(nsINode *aNode)
7608 if (aNode->IsElement()) {
7609 Element *element = aNode->AsElement();
7610 const nsDOMAttributeMap *map = element->GetAttributeMap();
7611 if (map) {
7612 nsCOMPtr<nsIAttribute> attr;
7613 while (map->Enumerate(BlastFunc, &attr) > 0) {
7614 BlastSubtreeToPieces(attr);
7616 #ifdef DEBUG
7617 nsresult rv =
7618 #endif
7619 element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
7620 attr->NodeInfo()->NameAtom(),
7621 false);
7623 // XXX Should we abort here?
7624 NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!");
7629 uint32_t count = aNode->GetChildCount();
7630 for (uint32_t i = 0; i < count; ++i) {
7631 BlastSubtreeToPieces(aNode->GetFirstChild());
7632 aNode->RemoveChildAt(0, false);
7636 NS_IMETHODIMP
7637 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
7639 *aResult = nullptr;
7641 nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
7642 NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED);
7644 ErrorResult rv;
7645 nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv);
7646 if (rv.Failed()) {
7647 return rv.ErrorCode();
7650 NS_ADDREF(*aResult = result->AsDOMNode());
7651 return NS_OK;
7654 nsINode*
7655 nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
7657 nsINode* adoptedNode = &aAdoptedNode;
7659 // Scope firing mutation events so that we don't carry any state that
7660 // might be stale
7662 nsINode* parent = adoptedNode->GetParentNode();
7663 if (parent) {
7664 nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
7665 adoptedNode->OwnerDoc());
7669 nsAutoScriptBlocker scriptBlocker;
7671 switch (adoptedNode->NodeType()) {
7672 case nsIDOMNode::ATTRIBUTE_NODE:
7674 // Remove from ownerElement.
7675 nsRefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
7677 nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
7678 if (rv.Failed()) {
7679 return nullptr;
7682 if (ownerElement) {
7683 nsRefPtr<Attr> newAttr =
7684 ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
7685 if (rv.Failed()) {
7686 return nullptr;
7689 newAttr.swap(adoptedAttr);
7692 break;
7694 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
7695 case nsIDOMNode::ELEMENT_NODE:
7696 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
7697 case nsIDOMNode::TEXT_NODE:
7698 case nsIDOMNode::CDATA_SECTION_NODE:
7699 case nsIDOMNode::COMMENT_NODE:
7700 case nsIDOMNode::DOCUMENT_TYPE_NODE:
7702 // Don't allow adopting a node's anonymous subtree out from under it.
7703 if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
7704 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7705 return nullptr;
7708 // We don't want to adopt an element into its own contentDocument or into
7709 // a descendant contentDocument, so we check if the frameElement of this
7710 // document or any of its parents is the adopted node or one of its
7711 // descendants.
7712 nsIDocument *doc = this;
7713 do {
7714 nsPIDOMWindow *win = doc->GetWindow();
7715 if (win) {
7716 nsCOMPtr<nsINode> node = win->GetFrameElementInternal();
7717 if (node &&
7718 nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
7719 rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7720 return nullptr;
7723 } while ((doc = doc->GetParentDocument()));
7725 // Remove from parent.
7726 nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
7727 if (parent) {
7728 int32_t idx = parent->IndexOf(adoptedNode);
7729 MOZ_ASSERT(idx >= 0);
7730 parent->RemoveChildAt(idx, true);
7731 } else {
7732 MOZ_ASSERT(!adoptedNode->IsInDoc());
7734 // If we're adopting a node that's not in a document, it might still
7735 // have a binding applied. Remove the binding from the element now
7736 // that it's getting adopted into a new document.
7737 // TODO Fully tear down the binding.
7738 adoptedNode->AsContent()->SetXBLBinding(nullptr);
7741 break;
7743 case nsIDOMNode::DOCUMENT_NODE:
7745 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7746 return nullptr;
7748 default:
7750 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
7752 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7753 return nullptr;
7757 nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
7758 bool sameDocument = oldDocument == this;
7760 AutoJSContext cx;
7761 JS::Rooted<JSObject*> newScope(cx, nullptr);
7762 if (!sameDocument) {
7763 newScope = GetWrapper();
7764 if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
7765 // Make sure cx is in a semi-sane compartment before we call WrapNative.
7766 // It's kind of irrelevant, given that we're passing aAllowWrapping =
7767 // false, and documents should always insist on being wrapped in an
7768 // canonical scope. But we try to pass something sane anyway.
7769 JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject());
7770 JS::Rooted<JS::Value> v(cx);
7771 rv = nsContentUtils::WrapNative(cx, this, this, &v,
7772 /* aAllowWrapping = */ false);
7773 if (rv.Failed())
7774 return nullptr;
7775 newScope = &v.toObject();
7779 nsCOMArray<nsINode> nodesWithProperties;
7780 rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
7781 newScope, nodesWithProperties);
7782 if (rv.Failed()) {
7783 // Disconnect all nodes from their parents, since some have the old document
7784 // as their ownerDocument and some have this as their ownerDocument.
7785 BlastSubtreeToPieces(adoptedNode);
7787 if (!sameDocument && oldDocument) {
7788 uint32_t count = nodesWithProperties.Count();
7789 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7790 for (uint32_t i = 0; i < count; ++i) {
7791 // Remove all properties.
7792 oldDocument->PropertyTable(j)->
7793 DeleteAllPropertiesFor(nodesWithProperties[i]);
7798 return nullptr;
7801 uint32_t count = nodesWithProperties.Count();
7802 if (!sameDocument && oldDocument) {
7803 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7804 nsPropertyTable *oldTable = oldDocument->PropertyTable(j);
7805 nsPropertyTable *newTable = PropertyTable(j);
7806 for (uint32_t i = 0; i < count; ++i) {
7807 rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
7808 newTable);
7812 if (rv.Failed()) {
7813 // Disconnect all nodes from their parents.
7814 BlastSubtreeToPieces(adoptedNode);
7816 return nullptr;
7820 NS_ASSERTION(adoptedNode->OwnerDoc() == this,
7821 "Should still be in the document we just got adopted into");
7823 return adoptedNode;
7826 nsViewportInfo
7827 nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
7829 // Compute the CSS-to-LayoutDevice pixel scale as the product of the
7830 // widget scale and the full zoom.
7831 nsPresContext* context = mPresShell->GetPresContext();
7832 float fullZoom = context ? context->GetFullZoom() : 1.0;
7833 fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
7834 nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
7835 float widgetScale = widget ? widget->GetDefaultScale().scale : 1.0f;
7836 CSSToLayoutDeviceScale layoutDeviceScale(widgetScale * fullZoom);
7838 CSSToScreenScale defaultScale = layoutDeviceScale
7839 * LayoutDeviceToScreenScale(1.0);
7841 if (!Preferences::GetBool("dom.meta-viewport.enabled", false)) {
7842 return nsViewportInfo(aDisplaySize,
7843 defaultScale,
7844 /*allowZoom*/ false,
7845 /*allowDoubleTapZoom*/ true);
7848 // In cases where the width of the CSS viewport is less than or equal to the width
7849 // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
7850 // behaviour. See bug 941995 for details.
7852 switch (mViewportType) {
7853 case DisplayWidthHeight:
7854 return nsViewportInfo(aDisplaySize,
7855 defaultScale,
7856 /*allowZoom*/ true,
7857 /*allowDoubleTapZoom*/ true);
7858 case DisplayWidthHeightNoZoom:
7859 return nsViewportInfo(aDisplaySize,
7860 defaultScale,
7861 /*allowZoom*/ false,
7862 /*allowDoubleTapZoom*/ false);
7863 case Unknown:
7865 nsAutoString viewport;
7866 GetHeaderData(nsGkAtoms::viewport, viewport);
7867 if (viewport.IsEmpty()) {
7868 // If the docType specifies that we are on a site optimized for mobile,
7869 // then we want to return specially crafted defaults for the viewport info.
7870 nsCOMPtr<nsIDOMDocumentType> docType;
7871 nsresult rv = GetDoctype(getter_AddRefs(docType));
7872 if (NS_SUCCEEDED(rv) && docType) {
7873 nsAutoString docId;
7874 rv = docType->GetPublicId(docId);
7875 if (NS_SUCCEEDED(rv)) {
7876 if ((docId.Find("WAP") != -1) ||
7877 (docId.Find("Mobile") != -1) ||
7878 (docId.Find("WML") != -1))
7880 // We're making an assumption that the docType can't change here
7881 mViewportType = DisplayWidthHeight;
7882 return nsViewportInfo(aDisplaySize,
7883 defaultScale,
7884 /*allowZoom*/true,
7885 /*allowDoubleTapZoom*/false);
7890 nsAutoString handheldFriendly;
7891 GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
7892 if (handheldFriendly.EqualsLiteral("true")) {
7893 mViewportType = DisplayWidthHeight;
7894 return nsViewportInfo(aDisplaySize,
7895 defaultScale,
7896 /*allowZoom*/true,
7897 /*allowDoubleTapZoom*/false);
7901 nsAutoString minScaleStr;
7902 GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
7904 nsresult errorCode;
7905 mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
7907 if (NS_FAILED(errorCode)) {
7908 mScaleMinFloat = kViewportMinScale;
7911 mScaleMinFloat = mozilla::clamped(
7912 mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
7914 nsAutoString maxScaleStr;
7915 GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
7917 // We define a special error code variable for the scale and max scale,
7918 // because they are used later (see the width calculations).
7919 nsresult scaleMaxErrorCode;
7920 mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
7922 if (NS_FAILED(scaleMaxErrorCode)) {
7923 mScaleMaxFloat = kViewportMaxScale;
7926 mScaleMaxFloat = mozilla::clamped(
7927 mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
7929 nsAutoString scaleStr;
7930 GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
7932 nsresult scaleErrorCode;
7933 mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
7935 nsAutoString widthStr, heightStr;
7937 GetHeaderData(nsGkAtoms::viewport_height, heightStr);
7938 GetHeaderData(nsGkAtoms::viewport_width, widthStr);
7940 mAutoSize = false;
7942 if (widthStr.EqualsLiteral("device-width")) {
7943 mAutoSize = true;
7946 if (widthStr.IsEmpty() &&
7947 (heightStr.EqualsLiteral("device-height") ||
7948 (mScaleFloat.scale == 1.0)))
7950 mAutoSize = true;
7953 nsresult widthErrorCode, heightErrorCode;
7954 mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
7955 mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
7957 // If width or height has not been set to a valid number by this point,
7958 // fall back to a default value.
7959 mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
7960 mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
7962 mAllowZoom = true;
7963 nsAutoString userScalable;
7964 GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
7966 if ((userScalable.EqualsLiteral("0")) ||
7967 (userScalable.EqualsLiteral("no")) ||
7968 (userScalable.EqualsLiteral("false"))) {
7969 mAllowZoom = false;
7971 mAllowDoubleTapZoom = mAllowZoom;
7973 mScaleStrEmpty = scaleStr.IsEmpty();
7974 mWidthStrEmpty = widthStr.IsEmpty();
7975 mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
7976 mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
7978 mViewportType = Specified;
7980 case Specified:
7981 default:
7982 CSSSize size = mViewportSize;
7984 if (!mValidWidth) {
7985 if (mValidHeight && !aDisplaySize.IsEmpty()) {
7986 size.width = size.height * aDisplaySize.width / aDisplaySize.height;
7987 } else {
7988 // Stretch CSS pixel size of viewport to keep device pixel size
7989 // unchanged after full zoom applied.
7990 // See bug 974242.
7991 size.width = Preferences::GetInt("browser.viewport.desktopWidth",
7992 kViewportDefaultScreenWidth) / fullZoom;
7996 if (!mValidHeight) {
7997 if (!aDisplaySize.IsEmpty()) {
7998 size.height = size.width * aDisplaySize.height / aDisplaySize.width;
7999 } else {
8000 size.height = size.width;
8004 CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
8005 CSSToScreenScale scaleMinFloat = mScaleMinFloat * layoutDeviceScale;
8006 CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * layoutDeviceScale;
8008 if (mAutoSize) {
8009 // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
8010 CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f);
8011 size = ScreenSize(aDisplaySize) / defaultPixelScale;
8014 size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width));
8016 // Also recalculate the default zoom, if it wasn't specified in the metadata,
8017 // and the width is specified.
8018 if (mScaleStrEmpty && !mWidthStrEmpty) {
8019 CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width);
8020 scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
8023 size.height = clamped(size.height, float(kViewportMinSize.height), float(kViewportMaxSize.height));
8025 // We need to perform a conversion, but only if the initial or maximum
8026 // scale were set explicitly by the user.
8027 if (mValidScaleFloat) {
8028 CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
8029 size.width = std::max(size.width, displaySize.width);
8030 size.height = std::max(size.height, displaySize.height);
8031 } else if (mValidMaxScale) {
8032 CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
8033 size.width = std::max(size.width, displaySize.width);
8034 size.height = std::max(size.height, displaySize.height);
8037 return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
8038 mAutoSize, mAllowZoom, mAllowDoubleTapZoom);
8042 EventListenerManager*
8043 nsDocument::GetOrCreateListenerManager()
8045 if (!mListenerManager) {
8046 mListenerManager =
8047 new EventListenerManager(static_cast<EventTarget*>(this));
8048 SetFlags(NODE_HAS_LISTENERMANAGER);
8051 return mListenerManager;
8054 EventListenerManager*
8055 nsDocument::GetExistingListenerManager() const
8057 return mListenerManager;
8060 nsresult
8061 nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor)
8063 aVisitor.mCanHandle = true;
8064 // FIXME! This is a hack to make middle mouse paste working also in Editor.
8065 // Bug 329119
8066 aVisitor.mForceContentDispatch = true;
8068 // Load events must not propagate to |window| object, see bug 335251.
8069 if (aVisitor.mEvent->message != NS_LOAD) {
8070 nsGlobalWindow* window = static_cast<nsGlobalWindow*>(GetWindow());
8071 aVisitor.mParentTarget =
8072 window ? window->GetTargetForEventTargetChain() : nullptr;
8074 return NS_OK;
8077 NS_IMETHODIMP
8078 nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
8080 NS_ENSURE_ARG_POINTER(aReturn);
8081 ErrorResult rv;
8082 *aReturn = nsIDocument::CreateEvent(aEventType, rv).take();
8083 return rv.ErrorCode();
8086 already_AddRefed<Event>
8087 nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
8089 nsIPresShell *shell = GetShell();
8091 nsPresContext *presContext = nullptr;
8093 if (shell) {
8094 // Retrieve the context
8095 presContext = shell->GetPresContext();
8098 // Create event even without presContext.
8099 nsCOMPtr<nsIDOMEvent> ev;
8100 rv = EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this),
8101 presContext, nullptr, aEventType,
8102 getter_AddRefs(ev));
8103 if (!ev) {
8104 return nullptr;
8106 WidgetEvent* e = ev->GetInternalNSEvent();
8107 e->mFlags.mBubbles = false;
8108 e->mFlags.mCancelable = false;
8109 return dont_AddRef(ev.forget().take()->InternalDOMEvent());
8112 void
8113 nsDocument::FlushPendingNotifications(mozFlushType aType)
8115 nsDocumentOnStack dos(this);
8117 // We need to flush the sink for non-HTML documents (because the XML
8118 // parser still does insertion with deferred notifications). We
8119 // also need to flush the sink if this is a layout-related flush, to
8120 // make sure that layout is started as needed. But we can skip that
8121 // part if we have no presshell or if it's already done an initial
8122 // reflow.
8123 if ((!IsHTML() ||
8124 (aType > Flush_ContentAndNotify && mPresShell &&
8125 !mPresShell->DidInitialize())) &&
8126 (mParser || mWeakSink)) {
8127 nsCOMPtr<nsIContentSink> sink;
8128 if (mParser) {
8129 sink = mParser->GetContentSink();
8130 } else {
8131 sink = do_QueryReferent(mWeakSink);
8132 if (!sink) {
8133 mWeakSink = nullptr;
8136 // Determine if it is safe to flush the sink notifications
8137 // by determining if it safe to flush all the presshells.
8138 if (sink && (aType == Flush_Content || IsSafeToFlush())) {
8139 sink->FlushPendingNotifications(aType);
8143 // Should we be flushing pending binding constructors in here?
8145 if (aType <= Flush_ContentAndNotify) {
8146 // Nothing to do here
8147 return;
8150 // If we have a parent we must flush the parent too to ensure that our
8151 // container is reflowed if its size was changed. But if it's not safe to
8152 // flush ourselves, then don't flush the parent, since that can cause things
8153 // like resizes of our frame's widget, which we can't handle while flushing
8154 // is unsafe.
8155 // Since media queries mean that a size change of our container can
8156 // affect style, we need to promote a style flush on ourself to a
8157 // layout flush on our parent, since we need our container to be the
8158 // correct size to determine the correct style.
8159 if (mParentDocument && IsSafeToFlush()) {
8160 mozFlushType parentType = aType;
8161 if (aType >= Flush_Style)
8162 parentType = std::max(Flush_Layout, aType);
8163 mParentDocument->FlushPendingNotifications(parentType);
8166 // We can optimize away getting our presshell and calling
8167 // FlushPendingNotifications on it if we don't need a flush of the sort we're
8168 // looking at. The one exception is if mInFlush is true, because in that
8169 // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false
8170 // already but the presshell hasn't actually done the corresponding work yet.
8171 // So if mInFlush and reentering this code, we need to flush the presshell.
8172 if (mNeedStyleFlush ||
8173 (mNeedLayoutFlush && aType >= Flush_InterruptibleLayout) ||
8174 aType >= Flush_Display ||
8175 mInFlush) {
8176 nsCOMPtr<nsIPresShell> shell = GetShell();
8177 if (shell) {
8178 mNeedStyleFlush = false;
8179 mNeedLayoutFlush = mNeedLayoutFlush && (aType < Flush_InterruptibleLayout);
8180 // mInFlush is a bitfield, so can't us AutoRestore here. But we
8181 // need to keep track of multi-level reentry correctly, so need
8182 // to restore the old mInFlush value.
8183 bool oldInFlush = mInFlush;
8184 mInFlush = true;
8185 shell->FlushPendingNotifications(aType);
8186 mInFlush = oldInFlush;
8191 static bool
8192 Copy(nsIDocument* aDocument, void* aData)
8194 nsTArray<nsCOMPtr<nsIDocument> >* resources =
8195 static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
8196 resources->AppendElement(aDocument);
8197 return true;
8200 void
8201 nsDocument::FlushExternalResources(mozFlushType aType)
8203 NS_ASSERTION(aType >= Flush_Style,
8204 "should only need to flush for style or higher in external resources");
8205 if (GetDisplayDocument()) {
8206 return;
8208 nsTArray<nsCOMPtr<nsIDocument> > resources;
8209 EnumerateExternalResources(Copy, &resources);
8211 for (uint32_t i = 0; i < resources.Length(); i++) {
8212 resources[i]->FlushPendingNotifications(aType);
8216 void
8217 nsDocument::SetXMLDeclaration(const char16_t *aVersion,
8218 const char16_t *aEncoding,
8219 const int32_t aStandalone)
8221 if (!aVersion || *aVersion == '\0') {
8222 mXMLDeclarationBits = 0;
8223 return;
8226 mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
8228 if (aEncoding && *aEncoding != '\0') {
8229 mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
8232 if (aStandalone == 1) {
8233 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
8234 XML_DECLARATION_BITS_STANDALONE_YES;
8236 else if (aStandalone == 0) {
8237 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
8241 void
8242 nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
8243 nsAString& aStandalone)
8245 aVersion.Truncate();
8246 aEncoding.Truncate();
8247 aStandalone.Truncate();
8249 if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
8250 return;
8253 // always until we start supporting 1.1 etc.
8254 aVersion.AssignLiteral("1.0");
8256 if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
8257 // This is what we have stored, not necessarily what was written
8258 // in the original
8259 GetCharacterSet(aEncoding);
8262 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
8263 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
8264 aStandalone.AssignLiteral("yes");
8265 } else {
8266 aStandalone.AssignLiteral("no");
8271 bool
8272 nsDocument::IsScriptEnabled()
8274 // If this document is sandboxed without 'allow-scripts'
8275 // script is not enabled
8276 if (mSandboxFlags & SANDBOXED_SCRIPTS) {
8277 return false;
8280 nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
8281 NS_ENSURE_TRUE(sm, false);
8283 nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
8284 if (!globalObject && mMasterDocument) {
8285 globalObject = do_QueryInterface(mMasterDocument->GetInnerWindow());
8287 NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false);
8289 return sm->ScriptAllowed(globalObject->GetGlobalJSObject());
8292 nsRadioGroupStruct*
8293 nsDocument::GetRadioGroupInternal(const nsAString& aName) const
8295 #ifdef DEBUG
8296 if (IsHTML()) {
8297 nsAutoString lcName;
8298 ToLowerCase(aName, lcName);
8299 MOZ_ASSERT(aName == lcName);
8301 #endif
8303 nsRadioGroupStruct* radioGroup;
8304 if (!mRadioGroups.Get(aName, &radioGroup)) {
8305 return nullptr;
8308 return radioGroup;
8311 nsRadioGroupStruct*
8312 nsDocument::GetRadioGroup(const nsAString& aName) const
8314 nsAutoString tmKey(aName);
8315 if (IsHTML()) {
8316 ToLowerCase(tmKey); //should case-insensitive.
8319 return GetRadioGroupInternal(tmKey);
8322 nsRadioGroupStruct*
8323 nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
8325 nsAutoString tmKey(aName);
8326 if (IsHTML()) {
8327 ToLowerCase(tmKey); //should case-insensitive.
8330 if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) {
8331 return radioGroup;
8334 nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct());
8335 mRadioGroups.Put(tmKey, newRadioGroup);
8337 return newRadioGroup.forget();
8340 void
8341 nsDocument::SetCurrentRadioButton(const nsAString& aName,
8342 HTMLInputElement* aRadio)
8344 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8345 radioGroup->mSelectedRadioButton = aRadio;
8348 HTMLInputElement*
8349 nsDocument::GetCurrentRadioButton(const nsAString& aName)
8351 return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
8354 NS_IMETHODIMP
8355 nsDocument::GetNextRadioButton(const nsAString& aName,
8356 const bool aPrevious,
8357 HTMLInputElement* aFocusedRadio,
8358 HTMLInputElement** aRadioOut)
8360 // XXX Can we combine the HTML radio button method impls of
8361 // nsDocument and nsHTMLFormControl?
8362 // XXX Why is HTML radio button stuff in nsDocument, as
8363 // opposed to nsHTMLDocument?
8364 *aRadioOut = nullptr;
8366 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8368 // Return the radio button relative to the focused radio button.
8369 // If no radio is focused, get the radio relative to the selected one.
8370 nsRefPtr<HTMLInputElement> currentRadio;
8371 if (aFocusedRadio) {
8372 currentRadio = aFocusedRadio;
8374 else {
8375 currentRadio = radioGroup->mSelectedRadioButton;
8376 if (!currentRadio) {
8377 return NS_ERROR_FAILURE;
8380 int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
8381 if (index < 0) {
8382 return NS_ERROR_FAILURE;
8385 int32_t numRadios = radioGroup->mRadioButtons.Count();
8386 nsRefPtr<HTMLInputElement> radio;
8387 do {
8388 if (aPrevious) {
8389 if (--index < 0) {
8390 index = numRadios -1;
8393 else if (++index >= numRadios) {
8394 index = 0;
8396 NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTML(nsGkAtoms::input),
8397 "mRadioButtons holding a non-radio button");
8398 radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
8399 } while (radio->Disabled() && radio != currentRadio);
8401 radio.forget(aRadioOut);
8402 return NS_OK;
8405 void
8406 nsDocument::AddToRadioGroup(const nsAString& aName,
8407 nsIFormControl* aRadio)
8409 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8410 radioGroup->mRadioButtons.AppendObject(aRadio);
8412 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8413 NS_ASSERTION(element, "radio controls have to be content elements");
8414 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8415 radioGroup->mRequiredRadioCount++;
8419 void
8420 nsDocument::RemoveFromRadioGroup(const nsAString& aName,
8421 nsIFormControl* aRadio)
8423 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8424 radioGroup->mRadioButtons.RemoveObject(aRadio);
8426 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8427 NS_ASSERTION(element, "radio controls have to be content elements");
8428 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8429 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8430 "mRequiredRadioCount about to wrap below 0!");
8431 radioGroup->mRequiredRadioCount--;
8435 NS_IMETHODIMP
8436 nsDocument::WalkRadioGroup(const nsAString& aName,
8437 nsIRadioVisitor* aVisitor,
8438 bool aFlushContent)
8440 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8442 for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
8443 if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
8444 return NS_OK;
8448 return NS_OK;
8451 uint32_t
8452 nsDocument::GetRequiredRadioCount(const nsAString& aName) const
8454 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8455 return radioGroup ? radioGroup->mRequiredRadioCount : 0;
8458 void
8459 nsDocument::RadioRequiredWillChange(const nsAString& aName, bool aRequiredAdded)
8461 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8463 if (aRequiredAdded) {
8464 radioGroup->mRequiredRadioCount++;
8465 } else {
8466 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8467 "mRequiredRadioCount about to wrap below 0!");
8468 radioGroup->mRequiredRadioCount--;
8472 bool
8473 nsDocument::GetValueMissingState(const nsAString& aName) const
8475 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8476 return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
8479 void
8480 nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
8482 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8483 radioGroup->mGroupSuffersFromValueMissing = aValue;
8486 void
8487 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
8489 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8490 PRTime modDate = 0;
8491 nsresult rv;
8493 if (httpChannel) {
8494 nsAutoCString tmp;
8495 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
8496 tmp);
8498 if (NS_SUCCEEDED(rv)) {
8499 PRTime time;
8500 PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
8501 if (st == PR_SUCCESS) {
8502 modDate = time;
8506 // The misspelled key 'referer' is as per the HTTP spec
8507 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
8508 mReferrer);
8509 if (NS_FAILED(rv)) {
8510 mReferrer.Truncate();
8513 static const char *const headers[] = {
8514 "default-style",
8515 "content-style-type",
8516 "content-language",
8517 "content-disposition",
8518 "refresh",
8519 "x-dns-prefetch-control",
8520 "x-frame-options",
8521 // add more http headers if you need
8522 // XXXbz don't add content-location support without reading bug
8523 // 238654 and its dependencies/dups first.
8527 nsAutoCString headerVal;
8528 const char *const *name = headers;
8529 while (*name) {
8530 rv =
8531 httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
8532 if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
8533 nsCOMPtr<nsIAtom> key = do_GetAtom(*name);
8534 SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
8536 ++name;
8538 } else {
8539 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
8540 if (fileChannel) {
8541 nsCOMPtr<nsIFile> file;
8542 fileChannel->GetFile(getter_AddRefs(file));
8543 if (file) {
8544 PRTime msecs;
8545 rv = file->GetLastModifiedTime(&msecs);
8547 if (NS_SUCCEEDED(rv)) {
8548 modDate = msecs * int64_t(PR_USEC_PER_MSEC);
8551 } else {
8552 nsAutoCString contentDisp;
8553 rv = aChannel->GetContentDispositionHeader(contentDisp);
8554 if (NS_SUCCEEDED(rv)) {
8555 SetHeaderData(nsGkAtoms::headerContentDisposition,
8556 NS_ConvertASCIItoUTF16(contentDisp));
8561 if (modDate == 0) {
8562 // We got nothing from our attempt to ask nsIFileChannel and
8563 // nsIHttpChannel for the last modified time. Return the current
8564 // time.
8565 modDate = PR_Now();
8568 mLastModified.Truncate();
8569 if (modDate != 0) {
8570 PRExplodedTime prtime;
8571 PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime);
8572 // "MM/DD/YYYY hh:mm:ss"
8573 char formatedTime[24];
8574 if (PR_snprintf(formatedTime, sizeof(formatedTime),
8575 "%02ld/%02ld/%04hd %02ld:%02ld:%02ld",
8576 prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year,
8577 prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
8578 CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified);
8583 nsresult
8584 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix, int32_t aNamespaceID,
8585 nsIContent **aResult)
8587 #ifdef DEBUG
8588 nsAutoString qName;
8589 if (aPrefix) {
8590 aPrefix->ToString(qName);
8591 qName.Append(':');
8593 qName.Append(aName);
8595 // Note: "a:b:c" is a valid name in non-namespaces XML, and
8596 // nsDocument::CreateElement can call us with such a name and no prefix,
8597 // which would cause an error if we just used true here.
8598 bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
8599 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
8600 "Don't pass invalid prefixes to nsDocument::CreateElem, "
8601 "check caller.");
8602 #endif
8604 *aResult = nullptr;
8606 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
8607 mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
8608 nsIDOMNode::ELEMENT_NODE,
8609 getter_AddRefs(nodeInfo));
8610 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
8612 nsCOMPtr<Element> element;
8613 nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
8614 NOT_FROM_PARSER);
8615 element.forget(aResult);
8616 return rv;
8619 bool
8620 nsDocument::IsSafeToFlush() const
8622 nsIPresShell* shell = GetShell();
8623 if (!shell)
8624 return true;
8626 return shell->IsSafeToFlush();
8629 void
8630 nsDocument::Sanitize()
8632 // Sanitize the document by resetting all password fields and any form
8633 // fields with autocomplete=off to their default values. We do this now,
8634 // instead of when the presentation is restored, to offer some protection
8635 // in case there is ever an exploit that allows a cached document to be
8636 // accessed from a different document.
8638 // First locate all input elements, regardless of whether they are
8639 // in a form, and reset the password and autocomplete=off elements.
8641 nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
8643 nsCOMPtr<nsIContent> item;
8644 nsAutoString value;
8646 uint32_t length = nodes->Length(true);
8647 for (uint32_t i = 0; i < length; ++i) {
8648 NS_ASSERTION(nodes->Item(i), "null item in node list!");
8650 nsRefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i));
8651 if (!input)
8652 continue;
8654 bool resetValue = false;
8656 input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
8657 if (value.LowerCaseEqualsLiteral("off")) {
8658 resetValue = true;
8659 } else {
8660 input->GetType(value);
8661 if (value.LowerCaseEqualsLiteral("password"))
8662 resetValue = true;
8665 if (resetValue) {
8666 input->Reset();
8670 // Now locate all _form_ elements that have autocomplete=off and reset them
8671 nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
8673 length = nodes->Length(true);
8674 for (uint32_t i = 0; i < length; ++i) {
8675 NS_ASSERTION(nodes->Item(i), "null item in nodelist");
8677 nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i));
8678 if (!form)
8679 continue;
8681 nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None,
8682 nsGkAtoms::autocomplete, value);
8683 if (value.LowerCaseEqualsLiteral("off"))
8684 form->Reset();
8688 struct SubDocEnumArgs
8690 nsIDocument::nsSubDocEnumFunc callback;
8691 void *data;
8694 static PLDHashOperator
8695 SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr,
8696 uint32_t number, void *arg)
8698 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
8699 SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg);
8701 nsIDocument *subdoc = entry->mSubDocument;
8702 bool next = subdoc ? args->callback(subdoc, args->data) : true;
8704 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
8707 void
8708 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
8710 if (mSubDocuments) {
8711 SubDocEnumArgs args = { aCallback, aData };
8712 PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args);
8716 static PLDHashOperator
8717 CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr,
8718 uint32_t number, void *arg)
8720 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
8721 bool *canCacheArg = static_cast<bool*>(arg);
8723 nsIDocument *subdoc = entry->mSubDocument;
8725 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
8726 bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
8727 if (!canCache) {
8728 *canCacheArg = false;
8729 return PL_DHASH_STOP;
8732 return PL_DHASH_NEXT;
8735 #ifdef DEBUG_bryner
8736 #define DEBUG_PAGE_CACHE
8737 #endif
8739 bool
8740 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
8742 if (EventHandlingSuppressed()) {
8743 return false;
8746 nsPIDOMWindow* win = GetInnerWindow();
8747 if (win && win->TimeoutSuspendCount()) {
8748 return false;
8751 // Check our event listener manager for unload/beforeunload listeners.
8752 nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
8753 if (piTarget) {
8754 EventListenerManager* manager = piTarget->GetExistingListenerManager();
8755 if (manager && manager->HasUnloadListeners()) {
8756 return false;
8760 // Check if we have pending network requests
8761 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8762 if (loadGroup) {
8763 nsCOMPtr<nsISimpleEnumerator> requests;
8764 loadGroup->GetRequests(getter_AddRefs(requests));
8766 bool hasMore = false;
8768 // We want to bail out if we have any requests other than aNewRequest (or
8769 // in the case when aNewRequest is a part of a multipart response the base
8770 // channel the multipart response is coming in on).
8771 nsCOMPtr<nsIChannel> baseChannel;
8772 nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
8773 if (part) {
8774 part->GetBaseChannel(getter_AddRefs(baseChannel));
8777 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8778 nsCOMPtr<nsISupports> elem;
8779 requests->GetNext(getter_AddRefs(elem));
8781 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8782 if (request && request != aNewRequest && request != baseChannel) {
8783 #ifdef DEBUG_PAGE_CACHE
8784 nsAutoCString requestName, docSpec;
8785 request->GetName(requestName);
8786 if (mDocumentURI)
8787 mDocumentURI->GetSpec(docSpec);
8789 printf("document %s has request %s\n",
8790 docSpec.get(), requestName.get());
8791 #endif
8792 return false;
8797 #ifdef MOZ_MEDIA_NAVIGATOR
8798 // Check if we have active GetUserMedia use
8799 if (MediaManager::Exists() && win &&
8800 MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
8801 return false;
8803 #endif // MOZ_MEDIA_NAVIGATOR
8805 #ifdef MOZ_WEBRTC
8806 // Check if we have active PeerConnections
8807 nsCOMPtr<IPeerConnectionManager> pcManager =
8808 do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
8810 if (pcManager && win) {
8811 bool active;
8812 pcManager->HasActivePeerConnection(win->WindowID(), &active);
8813 if (active) {
8814 return false;
8817 #endif // MOZ_WEBRTC
8819 #ifdef MOZ_EME
8820 // Don't save presentations for documents containing EME content, so that
8821 // CDMs reliably shutdown upon user navigation.
8822 if (ContainsEMEContent()) {
8823 return false;
8825 #endif
8827 bool canCache = true;
8828 if (mSubDocuments)
8829 PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache);
8831 return canCache;
8834 void
8835 nsDocument::Destroy()
8837 // The ContentViewer wants to release the document now. So, tell our content
8838 // to drop any references to the document so that it can be destroyed.
8839 if (mIsGoingAway)
8840 return;
8842 mIsGoingAway = true;
8844 RemovedFromDocShell();
8846 bool oldVal = mInUnlinkOrDeletion;
8847 mInUnlinkOrDeletion = true;
8848 uint32_t i, count = mChildren.ChildCount();
8849 for (i = 0; i < count; ++i) {
8850 mChildren.ChildAt(i)->DestroyContent();
8852 mInUnlinkOrDeletion = oldVal;
8854 mLayoutHistoryState = nullptr;
8856 // Shut down our external resource map. We might not need this for
8857 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
8858 // tearing down all those frame trees right now is the right thing to do.
8859 mExternalResourceMap.Shutdown();
8861 mRegistry = nullptr;
8863 nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
8864 if (swm) {
8865 swm->MaybeStopControlling(this);
8868 // XXX We really should let cycle collection do this, but that currently still
8869 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
8870 ReleaseWrapper(static_cast<nsINode*>(this));
8873 void
8874 nsDocument::RemovedFromDocShell()
8876 if (mRemovedFromDocShell)
8877 return;
8879 mRemovedFromDocShell = true;
8880 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8882 uint32_t i, count = mChildren.ChildCount();
8883 for (i = 0; i < count; ++i) {
8884 mChildren.ChildAt(i)->SaveSubtreeState();
8888 already_AddRefed<nsILayoutHistoryState>
8889 nsDocument::GetLayoutHistoryState() const
8891 nsCOMPtr<nsILayoutHistoryState> state;
8892 if (!mScriptGlobalObject) {
8893 state = mLayoutHistoryState;
8894 } else {
8895 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
8896 if (docShell) {
8897 docShell->GetLayoutHistoryState(getter_AddRefs(state));
8901 return state.forget();
8904 void
8905 nsDocument::EnsureOnloadBlocker()
8907 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8908 // -- it's not ours.
8909 if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
8910 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8911 if (loadGroup) {
8912 // Check first to see if mOnloadBlocker is in the loadgroup.
8913 nsCOMPtr<nsISimpleEnumerator> requests;
8914 loadGroup->GetRequests(getter_AddRefs(requests));
8916 bool hasMore = false;
8917 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8918 nsCOMPtr<nsISupports> elem;
8919 requests->GetNext(getter_AddRefs(elem));
8920 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8921 if (request && request == mOnloadBlocker) {
8922 return;
8926 // Not in the loadgroup, so add it.
8927 loadGroup->AddRequest(mOnloadBlocker, nullptr);
8932 void
8933 nsDocument::AsyncBlockOnload()
8935 while (mAsyncOnloadBlockCount) {
8936 --mAsyncOnloadBlockCount;
8937 BlockOnload();
8941 void
8942 nsDocument::BlockOnload()
8944 if (mDisplayDocument) {
8945 mDisplayDocument->BlockOnload();
8946 return;
8949 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8950 // -- it's not ours.
8951 if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
8952 if (!nsContentUtils::IsSafeToRunScript()) {
8953 // Because AddRequest may lead to OnStateChange calls in chrome,
8954 // block onload only when there are no script blockers.
8955 ++mAsyncOnloadBlockCount;
8956 if (mAsyncOnloadBlockCount == 1) {
8957 bool success = nsContentUtils::AddScriptRunner(
8958 NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
8960 // The script runner shouldn't fail to add. But if somebody broke
8961 // something and it does, we'll thrash at 100% cpu forever. The best
8962 // response is just to ignore the onload blocking request. See bug 579535.
8963 if (!success) {
8964 NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
8965 mAsyncOnloadBlockCount = 0;
8968 return;
8970 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8971 if (loadGroup) {
8972 loadGroup->AddRequest(mOnloadBlocker, nullptr);
8975 ++mOnloadBlockCount;
8978 void
8979 nsDocument::UnblockOnload(bool aFireSync)
8981 if (mDisplayDocument) {
8982 mDisplayDocument->UnblockOnload(aFireSync);
8983 return;
8986 if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
8987 NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
8988 return;
8991 --mOnloadBlockCount;
8993 if (mOnloadBlockCount == 0) {
8994 if (mScriptGlobalObject) {
8995 // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
8996 // is null, it's not ours.
8997 if (aFireSync && mAsyncOnloadBlockCount == 0) {
8998 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
8999 ++mOnloadBlockCount;
9000 DoUnblockOnload();
9001 } else {
9002 PostUnblockOnloadEvent();
9004 } else if (mIsBeingUsedAsImage) {
9005 // To correctly unblock onload for a document that contains an SVG
9006 // image, we need to know when all of the SVG document's resources are
9007 // done loading, in a way comparable to |window.onload|. We fire this
9008 // event to indicate that the SVG should be considered fully loaded.
9009 // Because scripting is disabled on SVG-as-image documents, this event
9010 // is not accessible to content authors. (See bug 837135.)
9011 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
9012 new AsyncEventDispatcher(this,
9013 NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
9014 false,
9015 false);
9016 asyncDispatcher->PostDOMEvent();
9021 class nsUnblockOnloadEvent : public nsRunnable {
9022 public:
9023 explicit nsUnblockOnloadEvent(nsDocument* aDoc) : mDoc(aDoc) {}
9024 NS_IMETHOD Run() {
9025 mDoc->DoUnblockOnload();
9026 return NS_OK;
9028 private:
9029 nsRefPtr<nsDocument> mDoc;
9032 void
9033 nsDocument::PostUnblockOnloadEvent()
9035 nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
9036 nsresult rv = NS_DispatchToCurrentThread(evt);
9037 if (NS_SUCCEEDED(rv)) {
9038 // Stabilize block count so we don't post more events while this one is up
9039 ++mOnloadBlockCount;
9040 } else {
9041 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
9045 void
9046 nsDocument::DoUnblockOnload()
9048 NS_PRECONDITION(!mDisplayDocument,
9049 "Shouldn't get here for resource document");
9050 NS_PRECONDITION(mOnloadBlockCount != 0,
9051 "Shouldn't have a count of zero here, since we stabilized in "
9052 "PostUnblockOnloadEvent");
9054 --mOnloadBlockCount;
9056 if (mOnloadBlockCount != 0) {
9057 // We blocked again after the last unblock. Nothing to do here. We'll
9058 // post a new event when we unblock again.
9059 return;
9062 if (mAsyncOnloadBlockCount != 0) {
9063 // We need to wait until the async onload block has been handled.
9064 PostUnblockOnloadEvent();
9067 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
9068 // -- it's not ours.
9069 if (mScriptGlobalObject) {
9070 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
9071 if (loadGroup) {
9072 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
9077 nsIContent*
9078 nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
9080 for (nsIFrame* f = aFrame; f;
9081 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
9082 nsIContent* content = f->GetContent();
9083 if (!content || content->IsInAnonymousSubtree())
9084 continue;
9086 if (content->OwnerDoc() == this) {
9087 return content;
9089 // We must be in a subdocument so jump directly to the root frame.
9090 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
9091 // the containing document.
9092 f = f->PresContext()->GetPresShell()->GetRootFrame();
9095 return nullptr;
9098 void
9099 nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
9100 const nsAString& aType,
9101 bool aPersisted)
9103 if (!aDispatchTarget) {
9104 return;
9107 PageTransitionEventInit init;
9108 init.mBubbles = true;
9109 init.mCancelable = true;
9110 init.mPersisted = aPersisted;
9112 nsRefPtr<PageTransitionEvent> event =
9113 PageTransitionEvent::Constructor(this, aType, init);
9115 event->SetTrusted(true);
9116 event->SetTarget(this);
9117 EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
9118 nullptr, nullptr);
9121 static bool
9122 NotifyPageShow(nsIDocument* aDocument, void* aData)
9124 const bool* aPersistedPtr = static_cast<const bool*>(aData);
9125 aDocument->OnPageShow(*aPersistedPtr, nullptr);
9126 return true;
9129 void
9130 nsDocument::OnPageShow(bool aPersisted,
9131 EventTarget* aDispatchStartTarget)
9133 mVisible = true;
9135 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
9136 EnumerateExternalResources(NotifyPageShow, &aPersisted);
9138 Element* root = GetRootElement();
9139 if (aPersisted && root) {
9140 // Send out notifications that our <link> elements are attached.
9141 nsRefPtr<nsContentList> links = NS_GetContentList(root,
9142 kNameSpaceID_XHTML,
9143 NS_LITERAL_STRING("link"));
9145 uint32_t linkCount = links->Length(true);
9146 for (uint32_t i = 0; i < linkCount; ++i) {
9147 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
9151 // See nsIDocument
9152 if (!aDispatchStartTarget) {
9153 // Set mIsShowing before firing events, in case those event handlers
9154 // move us around.
9155 mIsShowing = true;
9158 if (mAnimationController) {
9159 mAnimationController->OnPageShow();
9162 if (aPersisted) {
9163 SetImagesNeedAnimating(true);
9166 UpdateVisibilityState();
9168 nsCOMPtr<EventTarget> target = aDispatchStartTarget;
9169 if (!target) {
9170 target = do_QueryInterface(GetWindow());
9173 // Dispatch observer notification to notify observers page is shown.
9174 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9175 nsIPrincipal *principal = GetPrincipal();
9176 os->NotifyObservers(static_cast<nsIDocument*>(this),
9177 nsContentUtils::IsSystemPrincipal(principal) ?
9178 "chrome-page-shown" :
9179 "content-page-shown",
9180 nullptr);
9181 if (!mObservingAppThemeChanged) {
9182 os->AddObserver(this, "app-theme-changed", /* ownsWeak */ false);
9183 mObservingAppThemeChanged = true;
9186 DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
9189 static bool
9190 NotifyPageHide(nsIDocument* aDocument, void* aData)
9192 const bool* aPersistedPtr = static_cast<const bool*>(aData);
9193 aDocument->OnPageHide(*aPersistedPtr, nullptr);
9194 return true;
9197 static void
9198 DispatchFullScreenChange(nsIDocument* aTarget)
9200 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
9201 new AsyncEventDispatcher(aTarget,
9202 NS_LITERAL_STRING("mozfullscreenchange"),
9203 true,
9204 false);
9205 asyncDispatcher->PostDOMEvent();
9208 void
9209 nsDocument::OnPageHide(bool aPersisted,
9210 EventTarget* aDispatchStartTarget)
9212 // Send out notifications that our <link> elements are detached,
9213 // but only if this is not a full unload.
9214 Element* root = GetRootElement();
9215 if (aPersisted && root) {
9216 nsRefPtr<nsContentList> links = NS_GetContentList(root,
9217 kNameSpaceID_XHTML,
9218 NS_LITERAL_STRING("link"));
9220 uint32_t linkCount = links->Length(true);
9221 for (uint32_t i = 0; i < linkCount; ++i) {
9222 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
9226 // See nsIDocument
9227 if (!aDispatchStartTarget) {
9228 // Set mIsShowing before firing events, in case those event handlers
9229 // move us around.
9230 mIsShowing = false;
9233 if (mAnimationController) {
9234 mAnimationController->OnPageHide();
9237 if (aPersisted) {
9238 SetImagesNeedAnimating(false);
9241 MozExitPointerLock();
9243 // Now send out a PageHide event.
9244 nsCOMPtr<EventTarget> target = aDispatchStartTarget;
9245 if (!target) {
9246 target = do_QueryInterface(GetWindow());
9249 // Dispatch observer notification to notify observers page is hidden.
9250 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
9251 if (os) {
9252 nsIPrincipal* principal = GetPrincipal();
9253 os->NotifyObservers(static_cast<nsIDocument*>(this),
9254 nsContentUtils::IsSystemPrincipal(principal) ?
9255 "chrome-page-hidden" :
9256 "content-page-hidden",
9257 nullptr);
9259 os->RemoveObserver(this, "app-theme-changed");
9260 mObservingAppThemeChanged = false;
9263 DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
9265 mVisible = false;
9267 UpdateVisibilityState();
9269 EnumerateExternalResources(NotifyPageHide, &aPersisted);
9270 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
9272 if (IsFullScreenDoc()) {
9273 // If this document was fullscreen, we should exit fullscreen in this
9274 // doctree branch. This ensures that if the user navigates while in
9275 // fullscreen mode we don't leave its still visible ancestor documents
9276 // in fullscreen mode. So exit fullscreen in the document's fullscreen
9277 // root document, as this will exit fullscreen in all the root's
9278 // descendant documents. Note that documents are removed from the
9279 // doctree by the time OnPageHide() is called, so we must store a
9280 // reference to the root (in nsDocument::mFullscreenRoot) since we can't
9281 // just traverse the doctree to get the root.
9282 nsIDocument::ExitFullscreen(this, /* async */ false);
9284 // Since the document is removed from the doctree before OnPageHide() is
9285 // called, ExitFullscreen() can't traverse from the root down to *this*
9286 // document, so we must manually call CleanupFullscreenState() below too.
9287 // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
9288 // so we *must* call it after ExitFullscreen(), not before.
9289 // OnPageHide() is called in every hidden (i.e. descendant) document,
9290 // so calling CleanupFullscreenState() here will ensure all hidden
9291 // documents have their fullscreen state reset.
9292 CleanupFullscreenState();
9294 // If anyone was listening to this document's state, advertizing the state
9295 // change would be the least of the politeness.
9296 DispatchFullScreenChange(this);
9300 void
9301 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
9303 NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
9304 mSubtreeModifiedTargets.Count() == 0,
9305 "mSubtreeModifiedTargets not cleared after dispatching?");
9306 ++mSubtreeModifiedDepth;
9307 if (aTarget) {
9308 // MayDispatchMutationEvent is often called just before this method,
9309 // so it has already appended the node to mSubtreeModifiedTargets.
9310 int32_t count = mSubtreeModifiedTargets.Count();
9311 if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
9312 mSubtreeModifiedTargets.AppendObject(aTarget);
9317 void
9318 nsDocument::MutationEventDispatched(nsINode* aTarget)
9320 --mSubtreeModifiedDepth;
9321 if (mSubtreeModifiedDepth == 0) {
9322 int32_t count = mSubtreeModifiedTargets.Count();
9323 if (!count) {
9324 return;
9327 nsPIDOMWindow* window = GetInnerWindow();
9328 if (window &&
9329 !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
9330 mSubtreeModifiedTargets.Clear();
9331 return;
9334 nsCOMArray<nsINode> realTargets;
9335 for (int32_t i = 0; i < count; ++i) {
9336 nsINode* possibleTarget = mSubtreeModifiedTargets[i];
9337 nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
9338 if (content && content->ChromeOnlyAccess()) {
9339 continue;
9342 nsINode* commonAncestor = nullptr;
9343 int32_t realTargetCount = realTargets.Count();
9344 for (int32_t j = 0; j < realTargetCount; ++j) {
9345 commonAncestor =
9346 nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
9347 if (commonAncestor) {
9348 realTargets.ReplaceObjectAt(commonAncestor, j);
9349 break;
9352 if (!commonAncestor) {
9353 realTargets.AppendObject(possibleTarget);
9357 mSubtreeModifiedTargets.Clear();
9359 int32_t realTargetCount = realTargets.Count();
9360 for (int32_t k = 0; k < realTargetCount; ++k) {
9361 InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED);
9362 (new AsyncEventDispatcher(realTargets[k], mutation))->
9363 RunDOMEventWhenSafe();
9368 void
9369 nsDocument::AddStyleRelevantLink(Link* aLink)
9371 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9372 #ifdef DEBUG
9373 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9374 NS_ASSERTION(!entry, "Document already knows about this Link!");
9375 mStyledLinksCleared = false;
9376 #endif
9377 (void)mStyledLinks.PutEntry(aLink);
9380 void
9381 nsDocument::ForgetLink(Link* aLink)
9383 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9384 #ifdef DEBUG
9385 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9386 NS_ASSERTION(entry || mStyledLinksCleared,
9387 "Document knows nothing about this Link!");
9388 #endif
9389 (void)mStyledLinks.RemoveEntry(aLink);
9392 void
9393 nsDocument::DestroyElementMaps()
9395 #ifdef DEBUG
9396 mStyledLinksCleared = true;
9397 #endif
9398 mStyledLinks.Clear();
9399 mIdentifierMap.Clear();
9400 ++mExpandoAndGeneration.generation;
9403 static
9404 PLDHashOperator
9405 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
9407 LinkArray* array = static_cast<LinkArray*>(aArray);
9408 (void)array->AppendElement(aEntry->GetKey());
9409 return PL_DHASH_NEXT;
9412 void
9413 nsDocument::RefreshLinkHrefs()
9415 // Get a list of all links we know about. We will reset them, which will
9416 // remove them from the document, so we need a copy of what is in the
9417 // hashtable.
9418 LinkArray linksToNotify(mStyledLinks.Count());
9419 (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
9421 // Reset all of our styled links.
9422 nsAutoScriptBlocker scriptBlocker;
9423 for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
9424 linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
9428 nsresult
9429 nsDocument::CloneDocHelper(nsDocument* clone) const
9431 clone->mIsStaticDocument = mCreatingStaticClone;
9433 // Init document
9434 nsresult rv = clone->Init();
9435 NS_ENSURE_SUCCESS(rv, rv);
9437 if (mCreatingStaticClone) {
9438 nsCOMPtr<nsILoadGroup> loadGroup;
9440 // |mDocumentContainer| is the container of the document that is being
9441 // created and not the original container. See CreateStaticClone function().
9442 nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
9443 if (docLoader) {
9444 docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
9446 nsCOMPtr<nsIChannel> channel = GetChannel();
9447 nsCOMPtr<nsIURI> uri;
9448 if (channel) {
9449 NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
9450 } else {
9451 uri = nsIDocument::GetDocumentURI();
9453 clone->mChannel = channel;
9454 if (uri) {
9455 clone->ResetToURI(uri, loadGroup, NodePrincipal());
9458 clone->SetContainer(mDocumentContainer);
9461 // Now ensure that our clone has the same URI, base URI, and principal as us.
9462 // We do this after the mCreatingStaticClone block above, because that block
9463 // can set the base URI to an incorrect value in cases when base URI
9464 // information came from the channel. So we override explicitly, and do it
9465 // for all these properties, in case ResetToURI messes with any of the rest of
9466 // them.
9467 clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
9468 clone->SetChromeXHRDocURI(mChromeXHRDocURI);
9469 clone->SetPrincipal(NodePrincipal());
9470 clone->mDocumentBaseURI = mDocumentBaseURI;
9471 clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
9473 // Set scripting object
9474 bool hasHadScriptObject = true;
9475 nsIScriptGlobalObject* scriptObject =
9476 GetScriptHandlingObject(hasHadScriptObject);
9477 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
9478 if (scriptObject) {
9479 clone->SetScriptHandlingObject(scriptObject);
9480 } else {
9481 clone->SetScopeObject(GetScopeObject());
9483 // Make the clone a data document
9484 clone->SetLoadedAsData(true);
9486 // Misc state
9488 // State from nsIDocument
9489 clone->mCharacterSet = mCharacterSet;
9490 clone->mCharacterSetSource = mCharacterSetSource;
9491 clone->mCompatMode = mCompatMode;
9492 clone->mBidiOptions = mBidiOptions;
9493 clone->mContentLanguage = mContentLanguage;
9494 clone->SetContentTypeInternal(GetContentTypeInternal());
9495 clone->mSecurityInfo = mSecurityInfo;
9497 // State from nsDocument
9498 clone->mType = mType;
9499 clone->mXMLDeclarationBits = mXMLDeclarationBits;
9500 clone->mBaseTarget = mBaseTarget;
9501 return NS_OK;
9504 void
9505 nsDocument::SetReadyStateInternal(ReadyState rs)
9507 mReadyState = rs;
9508 if (rs == READYSTATE_UNINITIALIZED) {
9509 // Transition back to uninitialized happens only to keep assertions happy
9510 // right before readyState transitions to something else. Make this
9511 // transition undetectable by Web content.
9512 return;
9514 if (mTiming) {
9515 switch (rs) {
9516 case READYSTATE_LOADING:
9517 mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
9518 break;
9519 case READYSTATE_INTERACTIVE:
9520 mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
9521 break;
9522 case READYSTATE_COMPLETE:
9523 mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
9524 break;
9525 default:
9526 NS_WARNING("Unexpected ReadyState value");
9527 break;
9530 // At the time of loading start, we don't have timing object, record time.
9531 if (READYSTATE_LOADING == rs) {
9532 mLoadingTimeStamp = mozilla::TimeStamp::Now();
9535 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
9536 new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
9537 false, false);
9538 asyncDispatcher->RunDOMEventWhenSafe();
9541 NS_IMETHODIMP
9542 nsDocument::GetReadyState(nsAString& aReadyState)
9544 nsIDocument::GetReadyState(aReadyState);
9545 return NS_OK;
9548 void
9549 nsIDocument::GetReadyState(nsAString& aReadyState) const
9551 switch(mReadyState) {
9552 case READYSTATE_LOADING :
9553 aReadyState.AssignLiteral(MOZ_UTF16("loading"));
9554 break;
9555 case READYSTATE_INTERACTIVE :
9556 aReadyState.AssignLiteral(MOZ_UTF16("interactive"));
9557 break;
9558 case READYSTATE_COMPLETE :
9559 aReadyState.AssignLiteral(MOZ_UTF16("complete"));
9560 break;
9561 default:
9562 aReadyState.AssignLiteral(MOZ_UTF16("uninitialized"));
9566 namespace {
9568 struct SuppressArgs
9570 nsIDocument::SuppressionType mWhat;
9571 uint32_t mIncrease;
9576 static bool
9577 SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
9579 SuppressArgs* args = static_cast<SuppressArgs*>(aData);
9580 aDocument->SuppressEventHandling(args->mWhat, args->mIncrease);
9581 return true;
9584 void
9585 nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
9586 uint32_t aIncrease)
9588 if (mEventsSuppressed == 0 && mAnimationsPaused == 0 &&
9589 aIncrease != 0 && mPresShell && mScriptGlobalObject) {
9590 RevokeAnimationFrameNotifications();
9593 if (aWhat == eAnimationsOnly) {
9594 mAnimationsPaused += aIncrease;
9595 } else {
9596 mEventsSuppressed += aIncrease;
9599 SuppressArgs args = { aWhat, aIncrease };
9600 EnumerateSubDocuments(SuppressEventHandlingInDocument, &args);
9603 static void
9604 FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments,
9605 bool aFireEvents)
9607 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9608 if (!fm)
9609 return;
9611 for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
9612 // NB: Don't bother trying to fire delayed events on documents that were
9613 // closed before this event ran.
9614 if (!aDocuments[i]->EventHandlingSuppressed()) {
9615 fm->FireDelayedEvents(aDocuments[i]);
9616 nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
9617 if (shell) {
9618 // Only fire events for active documents.
9619 bool fire = aFireEvents &&
9620 aDocuments[i]->GetInnerWindow() &&
9621 aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
9622 shell->FireOrClearDelayedEvents(fire);
9628 void
9629 nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
9630 ReferrerPolicy aReferrerPolicy)
9632 // Early exit if the img is already present in the img-cache
9633 // which indicates that the "real" load has already started and
9634 // that we shouldn't preload it.
9635 int16_t blockingStatus;
9636 if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
9637 !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
9638 this, NodePrincipal(), &blockingStatus)) {
9639 return;
9642 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
9643 switch (Element::StringToCORSMode(aCrossOriginAttr)) {
9644 case CORS_NONE:
9645 // Nothing to do
9646 break;
9647 case CORS_ANONYMOUS:
9648 loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
9649 break;
9650 case CORS_USE_CREDENTIALS:
9651 loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
9652 break;
9653 default:
9654 MOZ_CRASH("Unknown CORS mode!");
9657 // Image not in cache - trigger preload
9658 nsRefPtr<imgRequestProxy> request;
9659 nsresult rv =
9660 nsContentUtils::LoadImage(uri,
9661 this,
9662 NodePrincipal(),
9663 mDocumentURI, // uri of document used as referrer
9664 aReferrerPolicy,
9665 nullptr, // no observer
9666 loadFlags,
9667 NS_LITERAL_STRING("img"),
9668 getter_AddRefs(request));
9670 // Pin image-reference to avoid evicting it from the img-cache before
9671 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
9672 // unlink
9673 if (NS_SUCCEEDED(rv)) {
9674 mPreloadingImages.Put(uri, request.forget());
9678 void
9679 nsDocument::ForgetImagePreload(nsIURI* aURI)
9681 // Checking count is faster than hashing the URI in the common
9682 // case of empty table.
9683 if (mPreloadingImages.Count() != 0) {
9684 nsCOMPtr<imgIRequest> req;
9685 mPreloadingImages.Remove(aURI, getter_AddRefs(req));
9686 if (req) {
9687 // Make sure to cancel the request so imagelib knows it's gone.
9688 req->CancelAndForgetObserver(NS_BINDING_ABORTED);
9693 EventStates
9694 nsDocument::GetDocumentState()
9696 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
9697 if (IsDocumentRightToLeft()) {
9698 mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9700 mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9702 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
9703 nsIPresShell* shell = GetShell();
9704 if (shell && shell->GetPresContext() &&
9705 shell->GetPresContext()->IsTopLevelWindowInactive()) {
9706 mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9708 mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9710 return mDocumentState;
9713 namespace {
9716 * Stub for LoadSheet(), since all we want is to get the sheet into
9717 * the CSSLoader's style cache
9719 class StubCSSLoaderObserver MOZ_FINAL : public nsICSSLoaderObserver {
9720 ~StubCSSLoaderObserver() {}
9721 public:
9722 NS_IMETHOD
9723 StyleSheetLoaded(CSSStyleSheet*, bool, nsresult) MOZ_OVERRIDE
9725 return NS_OK;
9727 NS_DECL_ISUPPORTS
9729 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
9733 void
9734 nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
9735 const nsAString& aCrossOriginAttr,
9736 const ReferrerPolicy aReferrerPolicy)
9738 // The CSSLoader will retain this object after we return.
9739 nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
9741 // Charset names are always ASCII.
9742 CSSLoader()->LoadSheet(uri, NodePrincipal(),
9743 NS_LossyConvertUTF16toASCII(charset),
9744 obs,
9745 Element::StringToCORSMode(aCrossOriginAttr),
9746 aReferrerPolicy);
9749 nsresult
9750 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
9751 CSSStyleSheet** sheet)
9753 return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet);
9756 class nsDelayedEventDispatcher : public nsRunnable
9758 public:
9759 explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
9761 mDocuments.SwapElements(aDocuments);
9763 virtual ~nsDelayedEventDispatcher() {}
9765 NS_IMETHOD Run()
9767 FireOrClearDelayedEvents(mDocuments, true);
9768 return NS_OK;
9771 private:
9772 nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
9775 namespace {
9777 struct UnsuppressArgs
9779 explicit UnsuppressArgs(nsIDocument::SuppressionType aWhat)
9780 : mWhat(aWhat)
9784 nsIDocument::SuppressionType mWhat;
9785 nsTArray<nsCOMPtr<nsIDocument>> mDocs;
9790 static bool
9791 GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
9792 void* aData)
9794 UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData);
9795 if (args->mWhat != nsIDocument::eAnimationsOnly &&
9796 aDocument->EventHandlingSuppressed() > 0) {
9797 static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
9798 } else if (args->mWhat == nsIDocument::eAnimationsOnly &&
9799 aDocument->AnimationsPaused()) {
9800 static_cast<nsDocument*>(aDocument)->ResumeAnimations();
9803 if (args->mWhat != nsIDocument::eAnimationsOnly) {
9804 // No need to remember documents if we only care about animation frames.
9805 args->mDocs.AppendElement(aDocument);
9808 aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
9809 return true;
9812 void
9813 nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat,
9814 bool aFireEvents)
9816 UnsuppressArgs args(aWhat);
9817 GetAndUnsuppressSubDocuments(this, &args);
9819 if (aWhat == nsIDocument::eAnimationsOnly) {
9820 // No need to fire events if we only care about animations here.
9821 return;
9824 if (aFireEvents) {
9825 NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs));
9826 } else {
9827 FireOrClearDelayedEvents(args.mDocs, false);
9831 nsISupports*
9832 nsDocument::GetCurrentContentSink()
9834 return mParser ? mParser->GetContentSink() : nullptr;
9837 nsIDocument*
9838 nsDocument::GetTemplateContentsOwner()
9840 if (!mTemplateContentsOwner) {
9841 bool hasHadScriptObject = true;
9842 nsIScriptGlobalObject* scriptObject =
9843 GetScriptHandlingObject(hasHadScriptObject);
9845 nsCOMPtr<nsIDOMDocument> domDocument;
9846 nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
9847 EmptyString(), // aNamespaceURI
9848 EmptyString(), // aQualifiedName
9849 nullptr, // aDoctype
9850 nsIDocument::GetDocumentURI(),
9851 nsIDocument::GetDocBaseURI(),
9852 NodePrincipal(),
9853 true, // aLoadedAsData
9854 scriptObject, // aEventObject
9855 DocumentFlavorHTML);
9856 NS_ENSURE_SUCCESS(rv, nullptr);
9858 mTemplateContentsOwner = do_QueryInterface(domDocument);
9859 NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
9861 nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
9863 if (!scriptObject) {
9864 mTemplateContentsOwner->SetScopeObject(GetScopeObject());
9867 doc->mHasHadScriptHandlingObject = hasHadScriptObject;
9869 // Set |doc| as the template contents owner of itself so that
9870 // |doc| is the template contents owner of template elements created
9871 // by |doc|.
9872 doc->mTemplateContentsOwner = doc;
9875 return mTemplateContentsOwner;
9878 void
9879 nsDocument::RegisterHostObjectUri(const nsACString& aUri)
9881 mHostObjectURIs.AppendElement(aUri);
9884 void
9885 nsDocument::UnregisterHostObjectUri(const nsACString& aUri)
9887 mHostObjectURIs.RemoveElement(aUri);
9890 void
9891 nsDocument::SetScrollToRef(nsIURI *aDocumentURI)
9893 if (!aDocumentURI) {
9894 return;
9897 nsAutoCString ref;
9899 // Since all URI's that pass through here aren't URL's we can't
9900 // rely on the nsIURI implementation for providing a way for
9901 // finding the 'ref' part of the URI, we'll haveto revert to
9902 // string routines for finding the data past '#'
9904 aDocumentURI->GetSpec(ref);
9906 nsReadingIterator<char> start, end;
9908 ref.BeginReading(start);
9909 ref.EndReading(end);
9911 if (FindCharInReadable('#', start, end)) {
9912 ++start; // Skip over the '#'
9914 mScrollToRef = Substring(start, end);
9918 void
9919 nsDocument::ScrollToRef()
9921 if (mScrolledToRefAlready) {
9922 nsCOMPtr<nsIPresShell> shell = GetShell();
9923 if (shell) {
9924 shell->ScrollToAnchor();
9926 return;
9929 if (mScrollToRef.IsEmpty()) {
9930 return;
9933 char* tmpstr = ToNewCString(mScrollToRef);
9934 if (!tmpstr) {
9935 return;
9938 nsUnescape(tmpstr);
9939 nsAutoCString unescapedRef;
9940 unescapedRef.Assign(tmpstr);
9941 nsMemory::Free(tmpstr);
9943 nsresult rv = NS_ERROR_FAILURE;
9944 // We assume that the bytes are in UTF-8, as it says in the spec:
9945 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9946 NS_ConvertUTF8toUTF16 ref(unescapedRef);
9948 nsCOMPtr<nsIPresShell> shell = GetShell();
9949 if (shell) {
9950 // Check an empty string which might be caused by the UTF-8 conversion
9951 if (!ref.IsEmpty()) {
9952 // Note that GoToAnchor will handle flushing layout as needed.
9953 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9954 } else {
9955 rv = NS_ERROR_FAILURE;
9958 // If UTF-8 URI failed then try to assume the string as a
9959 // document's charset.
9961 if (NS_FAILED(rv)) {
9962 const nsACString &docCharset = GetDocumentCharacterSet();
9964 rv = nsContentUtils::ConvertStringFromEncoding(docCharset,
9965 unescapedRef,
9966 ref);
9968 if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
9969 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9972 if (NS_SUCCEEDED(rv)) {
9973 mScrolledToRefAlready = true;
9978 void
9979 nsDocument::ResetScrolledToRefAlready()
9981 mScrolledToRefAlready = false;
9984 void
9985 nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue)
9987 mChangeScrollPosWhenScrollingToRef = aValue;
9990 void
9991 nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
9993 if (!mActivityObservers) {
9994 mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports> >();
9995 if (!mActivityObservers)
9996 return;
9998 mActivityObservers->PutEntry(aSupports);
10001 bool
10002 nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
10004 if (!mActivityObservers)
10005 return false;
10006 if (!mActivityObservers->GetEntry(aSupports))
10007 return false;
10008 mActivityObservers->RemoveEntry(aSupports);
10009 return true;
10012 struct EnumerateActivityObserversData {
10013 nsIDocument::ActivityObserverEnumerator mEnumerator;
10014 void* mData;
10017 static PLDHashOperator
10018 EnumerateObservers(nsPtrHashKey<nsISupports>* aEntry, void* aData)
10020 EnumerateActivityObserversData* data = static_cast<EnumerateActivityObserversData*>(aData);
10021 data->mEnumerator(aEntry->GetKey(), data->mData);
10022 return PL_DHASH_NEXT;
10025 void
10026 nsIDocument::EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
10027 void* aData)
10029 if (!mActivityObservers)
10030 return;
10031 EnumerateActivityObserversData data = { aEnumerator, aData };
10032 mActivityObservers->EnumerateEntries(EnumerateObservers, &data);
10035 void
10036 nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
10038 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
10039 mLinksToUpdate.PutEntry(aLink);
10040 mHasLinksToUpdate = true;
10043 void
10044 nsIDocument::UnregisterPendingLinkUpdate(Link* aLink)
10046 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
10047 if (!mHasLinksToUpdate)
10048 return;
10050 mLinksToUpdate.RemoveEntry(aLink);
10053 static PLDHashOperator
10054 EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData)
10056 aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState());
10057 return PL_DHASH_NEXT;
10060 void
10061 nsIDocument::FlushPendingLinkUpdates()
10063 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
10064 if (!mHasLinksToUpdate)
10065 return;
10067 #ifdef DEBUG
10068 AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden);
10069 mIsLinkUpdateRegistrationsForbidden = true;
10070 #endif
10071 mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nullptr);
10072 mLinksToUpdate.Clear();
10073 mHasLinksToUpdate = false;
10076 already_AddRefed<nsIDocument>
10077 nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
10079 nsDocument* thisAsDoc = static_cast<nsDocument*>(this);
10080 mCreatingStaticClone = true;
10082 // Make document use different container during cloning.
10083 nsRefPtr<nsDocShell> originalShell = mDocumentContainer.get();
10084 SetContainer(static_cast<nsDocShell*>(aCloneContainer));
10085 nsCOMPtr<nsIDOMNode> clonedNode;
10086 nsresult rv = thisAsDoc->CloneNode(true, 1, getter_AddRefs(clonedNode));
10087 SetContainer(originalShell);
10089 nsRefPtr<nsDocument> clonedDoc;
10090 if (NS_SUCCEEDED(rv)) {
10091 nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode);
10092 if (tmp) {
10093 clonedDoc = static_cast<nsDocument*>(tmp.get());
10094 if (IsStaticDocument()) {
10095 clonedDoc->mOriginalDocument = mOriginalDocument;
10096 } else {
10097 clonedDoc->mOriginalDocument = this;
10099 int32_t sheetsCount = GetNumberOfStyleSheets();
10100 for (int32_t i = 0; i < sheetsCount; ++i) {
10101 nsRefPtr<CSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
10102 if (sheet) {
10103 if (sheet->IsApplicable()) {
10104 nsRefPtr<CSSStyleSheet> clonedSheet =
10105 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
10106 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
10107 if (clonedSheet) {
10108 clonedDoc->AddStyleSheet(clonedSheet);
10114 sheetsCount = thisAsDoc->mOnDemandBuiltInUASheets.Count();
10115 // Iterate backwards to maintain order
10116 for (int32_t i = sheetsCount - 1; i >= 0; --i) {
10117 nsRefPtr<CSSStyleSheet> sheet =
10118 do_QueryObject(thisAsDoc->mOnDemandBuiltInUASheets[i]);
10119 if (sheet) {
10120 if (sheet->IsApplicable()) {
10121 nsRefPtr<CSSStyleSheet> clonedSheet =
10122 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
10123 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
10124 if (clonedSheet) {
10125 clonedDoc->AddOnDemandBuiltInUASheet(clonedSheet);
10132 mCreatingStaticClone = false;
10133 return clonedDoc.forget();
10136 nsresult
10137 nsIDocument::ScheduleFrameRequestCallback(const FrameRequestCallbackHolder& aCallback,
10138 int32_t *aHandle)
10140 if (mFrameRequestCallbackCounter == INT32_MAX) {
10141 // Can't increment without overflowing; bail out
10142 return NS_ERROR_NOT_AVAILABLE;
10144 int32_t newHandle = ++mFrameRequestCallbackCounter;
10146 bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty();
10147 DebugOnly<FrameRequest*> request =
10148 mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
10149 NS_ASSERTION(request, "This is supposed to be infallible!");
10150 if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
10151 mPresShell->GetPresContext()->RefreshDriver()->
10152 ScheduleFrameRequestCallbacks(this);
10155 *aHandle = newHandle;
10156 return NS_OK;
10159 void
10160 nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
10162 // mFrameRequestCallbacks is stored sorted by handle
10163 if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) &&
10164 mFrameRequestCallbacks.IsEmpty() &&
10165 mPresShell && IsEventHandlingEnabled()) {
10166 mPresShell->GetPresContext()->RefreshDriver()->
10167 RevokeFrameRequestCallbacks(this);
10171 nsresult
10172 nsDocument::GetStateObject(nsIVariant** aState)
10174 // Get the document's current state object. This is the object backing both
10175 // history.state and popStateEvent.state.
10177 // mStateObjectContainer may be null; this just means that there's no
10178 // current state object.
10180 nsCOMPtr<nsIVariant> stateObj;
10181 if (!mStateObjectCached && mStateObjectContainer) {
10182 AutoJSContext cx;
10183 nsIGlobalObject* sgo = GetScopeObject();
10184 NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
10185 JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
10186 NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
10187 JSAutoCompartment ac(cx, global);
10189 mStateObjectContainer->
10190 DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
10193 NS_IF_ADDREF(*aState = mStateObjectCached);
10195 return NS_OK;
10198 nsDOMNavigationTiming*
10199 nsDocument::GetNavigationTiming() const
10201 return mTiming;
10204 nsresult
10205 nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
10207 mTiming = aTiming;
10208 if (!mLoadingTimeStamp.IsNull() && mTiming) {
10209 mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp);
10211 return NS_OK;
10214 Element*
10215 nsDocument::FindImageMap(const nsAString& aUseMapValue)
10217 if (aUseMapValue.IsEmpty()) {
10218 return nullptr;
10221 nsAString::const_iterator start, end;
10222 aUseMapValue.BeginReading(start);
10223 aUseMapValue.EndReading(end);
10225 int32_t hash = aUseMapValue.FindChar('#');
10226 if (hash < 0) {
10227 return nullptr;
10229 // aUsemap contains a '#', set start to point right after the '#'
10230 start.advance(hash + 1);
10232 if (start == end) {
10233 return nullptr; // aUsemap == "#"
10236 const nsAString& mapName = Substring(start, end);
10238 if (!mImageMaps) {
10239 mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
10242 uint32_t i, n = mImageMaps->Length(true);
10243 nsString name;
10244 for (i = 0; i < n; ++i) {
10245 nsIContent* map = mImageMaps->Item(i);
10246 if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
10247 eCaseMatters) ||
10248 (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) &&
10249 mapName.Equals(name, nsCaseInsensitiveStringComparator()))) {
10250 return map->AsElement();
10254 return nullptr;
10257 #define DEPRECATED_OPERATION(_op) #_op "Warning",
10258 static const char* kDeprecationWarnings[] = {
10259 #include "nsDeprecatedOperationList.h"
10260 nullptr
10262 #undef DEPRECATED_OPERATION
10264 #define DOCUMENT_WARNING(_op) #_op "Warning",
10265 static const char* kDocumentWarnings[] = {
10266 #include "nsDocumentWarningList.h"
10267 nullptr
10269 #undef DOCUMENT_WARNING
10271 bool
10272 nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation)
10274 static_assert(eDeprecatedOperationCount <= 64,
10275 "Too many deprecated operations");
10276 return mDeprecationWarnedAbout & (1ull << aOperation);
10279 void
10280 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
10281 bool asError /* = false */)
10283 if (HasWarnedAbout(aOperation)) {
10284 return;
10286 mDeprecationWarnedAbout |= (1ull << aOperation);
10287 uint32_t flags = asError ? nsIScriptError::errorFlag
10288 : nsIScriptError::warningFlag;
10289 nsContentUtils::ReportToConsole(flags,
10290 NS_LITERAL_CSTRING("DOM Core"), this,
10291 nsContentUtils::eDOM_PROPERTIES,
10292 kDeprecationWarnings[aOperation]);
10295 bool
10296 nsIDocument::HasWarnedAbout(DocumentWarnings aWarning)
10298 static_assert(eDocumentWarningCount <= 64,
10299 "Too many document warnings");
10300 return mDocWarningWarnedAbout & (1ull << aWarning);
10303 void
10304 nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
10305 bool asError /* = false */,
10306 const char16_t **aParams /* = nullptr */,
10307 uint32_t aParamsLength /* = 0 */)
10309 if (HasWarnedAbout(aWarning)) {
10310 return;
10312 mDocWarningWarnedAbout |= (1ull << aWarning);
10313 uint32_t flags = asError ? nsIScriptError::errorFlag
10314 : nsIScriptError::warningFlag;
10315 nsContentUtils::ReportToConsole(flags,
10316 NS_LITERAL_CSTRING("DOM Core"), this,
10317 nsContentUtils::eDOM_PROPERTIES,
10318 kDocumentWarnings[aWarning],
10319 aParams,
10320 aParamsLength);
10323 nsresult
10324 nsDocument::AddImage(imgIRequest* aImage)
10326 NS_ENSURE_ARG_POINTER(aImage);
10328 // See if the image is already in the hashtable. If it is, get the old count.
10329 uint32_t oldCount = 0;
10330 mImageTracker.Get(aImage, &oldCount);
10332 // Put the image in the hashtable, with the proper count.
10333 mImageTracker.Put(aImage, oldCount + 1);
10335 nsresult rv = NS_OK;
10337 // If this is the first insertion and we're locking images, lock this image
10338 // too.
10339 if (oldCount == 0) {
10340 if (mLockingImages)
10341 rv = aImage->LockImage();
10342 if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit ||
10343 mImageTracker.Count() < sOnloadDecodeLimit))
10344 rv = aImage->StartDecoding();
10347 // If this is the first insertion and we're animating images, request
10348 // that this image be animated too.
10349 if (oldCount == 0 && mAnimatingImages) {
10350 nsresult rv2 = aImage->IncrementAnimationConsumers();
10351 rv = NS_SUCCEEDED(rv) ? rv2 : rv;
10354 return rv;
10357 nsresult
10358 nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags)
10360 NS_ENSURE_ARG_POINTER(aImage);
10362 // Get the old count. It should exist and be > 0.
10363 uint32_t count = 0;
10364 DebugOnly<bool> found = mImageTracker.Get(aImage, &count);
10365 NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!");
10366 NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!");
10368 // We're removing, so decrement the count.
10369 count--;
10371 // If the count is now zero, remove from the tracker.
10372 // Otherwise, set the new value.
10373 if (count != 0) {
10374 mImageTracker.Put(aImage, count);
10375 return NS_OK;
10378 mImageTracker.Remove(aImage);
10380 nsresult rv = NS_OK;
10382 // Now that we're no longer tracking this image, unlock it if we'd
10383 // previously locked it.
10384 if (mLockingImages) {
10385 rv = aImage->UnlockImage();
10388 // If we're animating images, remove our request to animate this one.
10389 if (mAnimatingImages) {
10390 nsresult rv2 = aImage->DecrementAnimationConsumers();
10391 rv = NS_SUCCEEDED(rv) ? rv2 : rv;
10394 if (aFlags & REQUEST_DISCARD) {
10395 // Request that the image be discarded if nobody else holds a lock on it.
10396 // Do this even if !mLockingImages, because even if we didn't just unlock
10397 // this image, it might still be a candidate for discarding.
10398 aImage->RequestDiscard();
10401 return rv;
10404 nsresult
10405 nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
10407 MOZ_ASSERT(aPlugin);
10408 if (!mPlugins.PutEntry(aPlugin)) {
10409 return NS_ERROR_OUT_OF_MEMORY;
10411 return NS_OK;
10414 void
10415 nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin)
10417 MOZ_ASSERT(aPlugin);
10418 mPlugins.RemoveEntry(aPlugin);
10421 static bool
10422 AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
10424 nsTArray<nsIObjectLoadingContent*>* plugins =
10425 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
10426 MOZ_ASSERT(plugins);
10427 aDocument->GetPlugins(*plugins);
10428 return true;
10431 static PLDHashOperator
10432 AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg)
10434 nsTArray<nsIObjectLoadingContent*>* allPlugins =
10435 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
10436 MOZ_ASSERT(allPlugins);
10437 allPlugins->AppendElement(aPlugin->GetKey());
10438 return PL_DHASH_NEXT;
10441 void
10442 nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
10444 aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
10445 mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins);
10446 EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
10449 PLDHashOperator LockEnumerator(imgIRequest* aKey,
10450 uint32_t aData,
10451 void* userArg)
10453 aKey->LockImage();
10454 aKey->RequestDecode();
10455 return PL_DHASH_NEXT;
10458 PLDHashOperator UnlockEnumerator(imgIRequest* aKey,
10459 uint32_t aData,
10460 void* userArg)
10462 aKey->UnlockImage();
10463 return PL_DHASH_NEXT;
10467 nsresult
10468 nsDocument::SetImageLockingState(bool aLocked)
10470 if (XRE_GetProcessType() == GeckoProcessType_Content &&
10471 !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) {
10472 return NS_OK;
10475 // If there's no change, there's nothing to do.
10476 if (mLockingImages == aLocked)
10477 return NS_OK;
10479 // Otherwise, iterate over our images and perform the appropriate action.
10480 mImageTracker.EnumerateRead(aLocked ? LockEnumerator
10481 : UnlockEnumerator,
10482 nullptr);
10484 // Update state.
10485 mLockingImages = aLocked;
10487 return NS_OK;
10490 PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
10491 uint32_t aData,
10492 void* userArg)
10494 aKey->IncrementAnimationConsumers();
10495 return PL_DHASH_NEXT;
10498 PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
10499 uint32_t aData,
10500 void* userArg)
10502 aKey->DecrementAnimationConsumers();
10503 return PL_DHASH_NEXT;
10506 void
10507 nsDocument::SetImagesNeedAnimating(bool aAnimating)
10509 // If there's no change, there's nothing to do.
10510 if (mAnimatingImages == aAnimating)
10511 return;
10513 // Otherwise, iterate over our images and perform the appropriate action.
10514 mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
10515 : DecrementAnimationEnumerator,
10516 nullptr);
10518 // Update state.
10519 mAnimatingImages = aAnimating;
10522 already_AddRefed<Touch>
10523 nsIDocument::CreateTouch(nsIDOMWindow* aView,
10524 EventTarget* aTarget,
10525 int32_t aIdentifier,
10526 int32_t aPageX, int32_t aPageY,
10527 int32_t aScreenX, int32_t aScreenY,
10528 int32_t aClientX, int32_t aClientY,
10529 int32_t aRadiusX, int32_t aRadiusY,
10530 float aRotationAngle,
10531 float aForce)
10533 nsRefPtr<Touch> touch = new Touch(aTarget,
10534 aIdentifier,
10535 aPageX, aPageY,
10536 aScreenX, aScreenY,
10537 aClientX, aClientY,
10538 aRadiusX, aRadiusY,
10539 aRotationAngle,
10540 aForce);
10541 return touch.forget();
10544 already_AddRefed<TouchList>
10545 nsIDocument::CreateTouchList()
10547 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10548 return retval.forget();
10551 already_AddRefed<TouchList>
10552 nsIDocument::CreateTouchList(Touch& aTouch,
10553 const Sequence<OwningNonNull<Touch> >& aTouches)
10555 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10556 retval->Append(&aTouch);
10557 for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10558 retval->Append(aTouches[i].get());
10560 return retval.forget();
10563 already_AddRefed<TouchList>
10564 nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
10566 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10567 for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10568 retval->Append(aTouches[i].get());
10570 return retval.forget();
10573 already_AddRefed<nsDOMCaretPosition>
10574 nsIDocument::CaretPositionFromPoint(float aX, float aY)
10576 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
10577 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
10578 nsPoint pt(x, y);
10580 FlushPendingNotifications(Flush_Layout);
10582 nsIPresShell *ps = GetShell();
10583 if (!ps) {
10584 return nullptr;
10587 nsIFrame *rootFrame = ps->GetRootFrame();
10589 // XUL docs, unlike HTML, have no frame tree until everything's done loading
10590 if (!rootFrame) {
10591 return nullptr;
10594 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
10595 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
10596 if (!ptFrame) {
10597 return nullptr;
10600 // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
10601 // to adjust to frame-relative coordinates before we can perform this call.
10602 // It should also not take into account the padding of the frame.
10603 nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
10605 nsFrame::ContentOffsets offsets =
10606 ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
10608 nsCOMPtr<nsIContent> node = offsets.content;
10609 uint32_t offset = offsets.offset;
10610 nsCOMPtr<nsIContent> anonNode = node;
10611 bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
10612 if (nodeIsAnonymous) {
10613 node = ptFrame->GetContent();
10614 nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
10615 nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
10616 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
10617 bool isText;
10618 if (textArea || (input &&
10619 NS_SUCCEEDED(input->MozIsTextField(false, &isText)) &&
10620 isText)) {
10621 // If the anonymous content node has a child, then we need to make sure
10622 // that we get the appropriate child, as otherwise the offset may not be
10623 // correct when we construct a range for it.
10624 nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
10625 if (firstChild) {
10626 anonNode = firstChild;
10629 if (textArea) {
10630 offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
10633 node = nonanon;
10634 } else {
10635 node = nullptr;
10636 offset = 0;
10640 nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
10641 if (nodeIsAnonymous) {
10642 aCaretPos->SetAnonymousContentNode(anonNode);
10644 return aCaretPos.forget();
10647 NS_IMETHODIMP
10648 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
10650 NS_ENSURE_ARG_POINTER(aCaretPos);
10651 *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take();
10652 return NS_OK;
10655 void
10656 nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
10658 nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
10659 if (NS_FAILED(res)) {
10660 rv.Throw(res);
10664 void
10665 nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
10667 nsCOMPtr<nsIURI> uri;
10668 nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
10669 if (NS_FAILED(res)) {
10670 rv.Throw(res);
10671 return;
10673 res = CSSLoader()->ObsoleteSheet(uri);
10674 if (NS_FAILED(res)) {
10675 rv.Throw(res);
10679 nsIHTMLCollection*
10680 nsIDocument::Children()
10682 if (!mChildrenCollection) {
10683 mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
10684 nsGkAtoms::_asterix,
10685 nsGkAtoms::_asterix,
10686 false);
10689 return mChildrenCollection;
10692 uint32_t
10693 nsIDocument::ChildElementCount()
10695 return Children()->Length();
10698 namespace mozilla {
10700 // Singleton class to manage the list of fullscreen documents which are the
10701 // root of a branch which contains fullscreen documents. We maintain this list
10702 // so that we can easily exit all windows from fullscreen when the user
10703 // presses the escape key.
10704 class FullscreenRoots {
10705 public:
10706 // Adds a root to the manager. Adding a root multiple times does not result
10707 // in duplicate entries for that item, only one.
10708 static void Add(nsIDocument* aRoot);
10710 // Iterates over every root in the root list, and calls aFunction, passing
10711 // each root once to aFunction. It is safe to call Add() and Remove() while
10712 // iterating over the list (i.e. in aFunction). Documents that are removed
10713 // from the manager during traversal are not traversed, and documents that
10714 // are added to the manager during traversal are also not traversed.
10715 static void ForEach(void(*aFunction)(nsIDocument* aDoc));
10717 // Removes a specific root from the manager.
10718 static void Remove(nsIDocument* aRoot);
10720 // Returns true if all roots added to the list have been removed.
10721 static bool IsEmpty();
10723 private:
10725 FullscreenRoots() {
10726 MOZ_COUNT_CTOR(FullscreenRoots);
10728 ~FullscreenRoots() {
10729 MOZ_COUNT_DTOR(FullscreenRoots);
10732 enum {
10733 NotFound = uint32_t(-1)
10735 // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
10736 static uint32_t Find(nsIDocument* aRoot);
10738 // Returns true if aRoot is in the list of fullscreen roots.
10739 static bool Contains(nsIDocument* aRoot);
10741 // Singleton instance of the FullscreenRoots. This is instantiated when a
10742 // root is added, and it is deleted when the last root is removed.
10743 static FullscreenRoots* sInstance;
10745 // List of weak pointers to roots.
10746 nsTArray<nsWeakPtr> mRoots;
10749 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
10751 /* static */
10752 void
10753 FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
10755 if (!sInstance) {
10756 return;
10758 // Create a copy of the roots array, and iterate over the copy. This is so
10759 // that if an element is removed from mRoots we don't mess up our iteration.
10760 nsTArray<nsWeakPtr> roots(sInstance->mRoots);
10761 // Call aFunction on all entries.
10762 for (uint32_t i = 0; i < roots.Length(); i++) {
10763 nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
10764 // Check that the root isn't in the manager. This is so that new additions
10765 // while we were running don't get traversed.
10766 if (root && FullscreenRoots::Contains(root)) {
10767 aFunction(root);
10772 /* static */
10773 bool
10774 FullscreenRoots::Contains(nsIDocument* aRoot)
10776 return FullscreenRoots::Find(aRoot) != NotFound;
10779 /* static */
10780 void
10781 FullscreenRoots::Add(nsIDocument* aRoot)
10783 if (!FullscreenRoots::Contains(aRoot)) {
10784 if (!sInstance) {
10785 sInstance = new FullscreenRoots();
10787 sInstance->mRoots.AppendElement(do_GetWeakReference(aRoot));
10791 /* static */
10792 uint32_t
10793 FullscreenRoots::Find(nsIDocument* aRoot)
10795 if (!sInstance) {
10796 return NotFound;
10798 nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
10799 for (uint32_t i = 0; i < roots.Length(); i++) {
10800 nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
10801 if (otherRoot == aRoot) {
10802 return i;
10805 return NotFound;
10808 /* static */
10809 void
10810 FullscreenRoots::Remove(nsIDocument* aRoot)
10812 uint32_t index = Find(aRoot);
10813 NS_ASSERTION(index != NotFound,
10814 "Should only try to remove roots which are still added!");
10815 if (index == NotFound || !sInstance) {
10816 return;
10818 sInstance->mRoots.RemoveElementAt(index);
10819 if (sInstance->mRoots.IsEmpty()) {
10820 delete sInstance;
10821 sInstance = nullptr;
10825 /* static */
10826 bool
10827 FullscreenRoots::IsEmpty()
10829 return !sInstance;
10832 } // end namespace mozilla.
10833 using mozilla::FullscreenRoots;
10835 nsIDocument*
10836 nsDocument::GetFullscreenRoot()
10838 nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
10839 return root;
10842 void
10843 nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
10845 mFullscreenRoot = do_GetWeakReference(aRoot);
10848 NS_IMETHODIMP
10849 nsDocument::MozCancelFullScreen()
10851 nsIDocument::MozCancelFullScreen();
10852 return NS_OK;
10855 void
10856 nsIDocument::MozCancelFullScreen()
10858 RestorePreviousFullScreenState();
10861 // Runnable to set window full-screen mode. Used as a script runner
10862 // to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
10863 // run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
10864 // (handled in chome code) which is unsafe to run if this is called in
10865 // Element::UnbindFromTree().
10866 class nsSetWindowFullScreen : public nsRunnable {
10867 public:
10868 nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo* aHMD = nullptr)
10869 : mDoc(aDoc), mValue(aValue), mHMD(aHMD) {}
10871 NS_IMETHOD Run()
10873 if (mDoc->GetWindow()) {
10874 mDoc->GetWindow()->SetFullScreenInternal(mValue, false, mHMD);
10876 return NS_OK;
10879 private:
10880 nsCOMPtr<nsIDocument> mDoc;
10881 bool mValue;
10882 nsRefPtr<gfx::VRHMDInfo> mHMD;
10885 static nsIDocument*
10886 GetFullscreenRootDocument(nsIDocument* aDoc)
10888 if (!aDoc) {
10889 return nullptr;
10891 nsIDocument* doc = aDoc;
10892 nsIDocument* parent;
10893 while ((parent = doc->GetParentDocument()) &&
10894 (!nsContentUtils::IsFullscreenApiContentOnly() ||
10895 !nsContentUtils::IsChromeDoc(parent))) {
10896 doc = parent;
10898 return doc;
10901 static void
10902 SetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo *aVRHMD = nullptr)
10904 // Maintain list of fullscreen root documents.
10905 nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc);
10906 if (aValue) {
10907 FullscreenRoots::Add(root);
10908 } else {
10909 FullscreenRoots::Remove(root);
10911 if (!nsContentUtils::IsFullscreenApiContentOnly()) {
10912 nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue, aVRHMD));
10916 class nsCallExitFullscreen : public nsRunnable {
10917 public:
10918 explicit nsCallExitFullscreen(nsIDocument* aDoc)
10919 : mDoc(aDoc) {}
10920 NS_IMETHOD Run()
10922 nsDocument::ExitFullscreen(mDoc);
10923 return NS_OK;
10925 private:
10926 nsCOMPtr<nsIDocument> mDoc;
10929 /* static */
10930 void
10931 nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
10933 if (aDoc && !aDoc->IsFullScreenDoc()) {
10934 return;
10936 if (aRunAsync) {
10937 NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
10938 return;
10940 nsDocument::ExitFullscreen(aDoc);
10943 // Returns true if the document is a direct child of a cross process parent
10944 // mozbrowser iframe or TabParent. This is the case when the document has
10945 // a null parent and its DocShell reports that it is a browser frame, or
10946 // we can get a TabChild from it.
10947 static bool
10948 HasCrossProcessParent(nsIDocument* aDocument)
10950 if (XRE_GetProcessType() != GeckoProcessType_Content) {
10951 return false;
10953 if (aDocument->GetParentDocument() != nullptr) {
10954 return false;
10956 nsPIDOMWindow* win = aDocument->GetWindow();
10957 if (!win) {
10958 return false;
10960 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
10961 if (!docShell) {
10962 return false;
10964 TabChild* tabChild(TabChild::GetFrom(docShell));
10965 if (!tabChild) {
10966 return false;
10969 return true;
10972 static bool
10973 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
10975 if (aDoc->IsFullScreenDoc()) {
10976 uint32_t* count = static_cast<uint32_t*>(aData);
10977 (*count)++;
10979 return true;
10982 static uint32_t
10983 CountFullscreenSubDocuments(nsIDocument* aDoc)
10985 uint32_t count = 0;
10986 aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
10987 return count;
10990 bool
10991 nsDocument::IsFullscreenLeaf()
10993 // A fullscreen leaf document is fullscreen, and has no fullscreen
10994 // subdocuments.
10995 if (!IsFullScreenDoc()) {
10996 return false;
10998 return CountFullscreenSubDocuments(this) == 0;
11001 static bool
11002 ResetFullScreen(nsIDocument* aDocument, void* aData)
11004 if (aDocument->IsFullScreenDoc()) {
11005 NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
11006 "Should have at most 1 fullscreen subdocument.");
11007 static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
11008 NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
11009 nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
11010 changed->AppendElement(aDocument);
11012 if (HasCrossProcessParent(aDocument)) {
11013 // We're at the top of the content-process side doc tree. Ask the parent
11014 // process to exit fullscreen.
11015 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11016 os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr);
11019 // Dispatch a notification so that if this document has any
11020 // cross-process subdocuments, they'll be notified to exit fullscreen.
11021 // The BrowserElementParent listens for this event and performs the
11022 // cross process notification if it has a remote child process.
11023 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11024 os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
11026 aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
11028 return true;
11031 static void
11032 ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
11034 MOZ_ASSERT(aMaybeNotARootDoc);
11035 nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
11036 NS_ASSERTION(root, "Should have root when in fullscreen!");
11037 if (!root) {
11038 return;
11040 NS_ASSERTION(root->IsFullScreenDoc(),
11041 "Fullscreen root should be a fullscreen doc...");
11043 // Stores a list of documents to which we must dispatch "mozfullscreenchange".
11044 // We're required by the spec to dispatch the events in leaf-to-root
11045 // order when exiting fullscreen, but we traverse the doctree in a
11046 // root-to-leaf order, so we save references to the documents we must
11047 // dispatch to so that we dispatch in the specified order.
11048 nsAutoTArray<nsIDocument*, 8> changed;
11050 // Walk the tree of fullscreen documents, and reset their fullscreen state.
11051 ResetFullScreen(root, static_cast<void*>(&changed));
11053 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
11054 // order so that the events for the leaf document arrives before the root
11055 // document, as required by the spec.
11056 for (uint32_t i = 0; i < changed.Length(); ++i) {
11057 DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11060 NS_ASSERTION(!root->IsFullScreenDoc(),
11061 "Fullscreen root should no longer be a fullscreen doc...");
11063 // Move the top-level window out of fullscreen mode.
11064 SetWindowFullScreen(root, false);
11067 /* static */
11068 void
11069 nsDocument::ExitFullscreen(nsIDocument* aDoc)
11071 // Unlock the pointer, if it's locked.
11072 nsCOMPtr<Element> pointerLockedElement =
11073 do_QueryReferent(EventStateManager::sPointerLockedElement);
11074 if (pointerLockedElement) {
11075 UnlockPointer();
11078 if (aDoc) {
11079 ExitFullscreenInDocTree(aDoc);
11080 return;
11083 // Clear fullscreen stacks in all fullscreen roots' descendant documents.
11084 FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
11085 NS_ASSERTION(FullscreenRoots::IsEmpty(),
11086 "Should have exited all fullscreen roots from fullscreen");
11089 bool
11090 GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
11092 if (aDoc->IsFullscreenLeaf()) {
11093 nsIDocument** result = static_cast<nsIDocument**>(aData);
11094 *result = aDoc;
11095 return false;
11096 } else if (aDoc->IsFullScreenDoc()) {
11097 aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
11099 return true;
11102 static nsIDocument*
11103 GetFullscreenLeaf(nsIDocument* aDoc)
11105 nsIDocument* leaf = nullptr;
11106 GetFullscreenLeaf(aDoc, &leaf);
11107 if (leaf) {
11108 return leaf;
11110 // Otherwise we could be either in a non-fullscreen doc tree, or we're
11111 // below the fullscreen doc. Start the search from the root.
11112 nsIDocument* root = GetFullscreenRootDocument(aDoc);
11113 // Check that the root is actually fullscreen so we don't waste time walking
11114 // around its descendants.
11115 if (!root->IsFullScreenDoc()) {
11116 return nullptr;
11118 GetFullscreenLeaf(root, &leaf);
11119 return leaf;
11122 void
11123 nsDocument::RestorePreviousFullScreenState()
11125 NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(),
11126 "Should have at least 1 fullscreen root when fullscreen!");
11127 NS_ASSERTION(!nsContentUtils::IsFullscreenApiContentOnly() ||
11128 !nsContentUtils::IsChromeDoc(this),
11129 "Should not run RestorePreviousFullScreenState() on "
11130 "chrome document when fullscreen is content only");
11132 if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) {
11133 return;
11136 // If fullscreen mode is updated the pointer should be unlocked
11137 nsCOMPtr<Element> pointerLockedElement =
11138 do_QueryReferent(EventStateManager::sPointerLockedElement);
11139 if (pointerLockedElement) {
11140 UnlockPointer();
11143 nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
11145 // The fullscreen document may contain a <iframe mozbrowser> element which
11146 // has a cross process child. So send a notification so that its browser
11147 // parent will send a message to its child process to also exit fullscreen.
11148 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11149 os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr);
11151 // Clear full-screen stacks in all descendant in process documents, bottom up.
11152 nsIDocument* doc = fullScreenDoc;
11153 while (doc != this) {
11154 NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc");
11155 static_cast<nsDocument*>(doc)->CleanupFullscreenState();
11156 UnlockPointer();
11157 DispatchFullScreenChange(doc);
11158 doc = doc->GetParentDocument();
11161 // Roll-back full-screen state to previous full-screen element.
11162 NS_ASSERTION(doc == this, "Must have reached this doc.");
11163 while (doc != nullptr) {
11164 static_cast<nsDocument*>(doc)->FullScreenStackPop();
11165 UnlockPointer();
11166 DispatchFullScreenChange(doc);
11167 if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) {
11168 if (HasCrossProcessParent(doc)) {
11169 // Send notification to the parent process to tell it to rollback to
11170 // the previous fullscreen elements in its fullscreen element stacks.
11171 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11172 os->NotifyObservers(doc, "ask-parent-to-rollback-fullscreen", nullptr);
11174 // Full-screen stack in document is empty. Go back up to the parent
11175 // document. We'll pop the containing element off its stack, and use
11176 // its next full-screen element as the full-screen element.
11177 static_cast<nsDocument*>(doc)->CleanupFullscreenState();
11178 doc = doc->GetParentDocument();
11179 } else {
11180 // Else we popped the top of the stack, and there's still another
11181 // element in there, so that will become the full-screen element.
11182 if (fullScreenDoc != doc) {
11183 // We've popped so enough off the stack that we've rolled back to
11184 // a fullscreen element in a parent document. If this document isn't
11185 // approved for fullscreen, or if it's cross origin, dispatch an
11186 // event to chrome so it knows to show the authorization/warning UI.
11187 if (!nsContentUtils::HaveEqualPrincipals(fullScreenDoc, doc) ||
11188 (!nsContentUtils::IsSitePermAllow(doc->NodePrincipal(), "fullscreen") &&
11189 !static_cast<nsDocument*>(doc)->mIsApprovedForFullscreen)) {
11190 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11191 new AsyncEventDispatcher(doc,
11192 NS_LITERAL_STRING("MozEnteredDomFullscreen"),
11193 true,
11194 true);
11195 asyncDispatcher->PostDOMEvent();
11199 if (!nsContentUtils::HaveEqualPrincipals(doc, fullScreenDoc)) {
11200 // The origin which is fullscreen changed. Send a notification to
11201 // the root process so that a warning or approval UI can be shown
11202 // as necessary.
11203 nsAutoString origin;
11204 nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin);
11205 nsIDocument* root = GetFullscreenRootDocument(doc);
11206 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11207 os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
11210 break;
11214 if (doc == nullptr) {
11215 // We moved all documents in this doctree out of fullscreen mode,
11216 // move the top-level window out of fullscreen mode.
11217 NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(),
11218 "Should have cleared all docs' stacks");
11219 SetWindowFullScreen(this, false);
11223 bool
11224 nsDocument::IsFullScreenDoc()
11226 return GetFullScreenElement() != nullptr;
11229 class nsCallRequestFullScreen : public nsRunnable
11231 public:
11232 explicit nsCallRequestFullScreen(Element* aElement, FullScreenOptions& aOptions)
11233 : mElement(aElement),
11234 mDoc(aElement->OwnerDoc()),
11235 mWasCallerChrome(nsContentUtils::IsCallerChrome()),
11236 mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
11237 mAsyncFullscreenPending),
11238 mOptions(aOptions)
11240 static_cast<nsDocument*>(mDoc.get())->
11241 mAsyncFullscreenPending = true;
11244 NS_IMETHOD Run()
11246 static_cast<nsDocument*>(mDoc.get())->
11247 mAsyncFullscreenPending = mHadRequestPending;
11248 nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
11249 doc->RequestFullScreen(mElement,
11250 mOptions,
11251 mWasCallerChrome,
11252 /* aNotifyOnOriginChange */ true);
11253 return NS_OK;
11256 nsRefPtr<Element> mElement;
11257 nsCOMPtr<nsIDocument> mDoc;
11258 bool mWasCallerChrome;
11259 bool mHadRequestPending;
11260 FullScreenOptions mOptions;
11263 void
11264 nsDocument::AsyncRequestFullScreen(Element* aElement,
11265 FullScreenOptions& aOptions)
11267 NS_ASSERTION(aElement,
11268 "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
11269 if (!aElement) {
11270 return;
11272 // Request full-screen asynchronously.
11273 nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement, aOptions));
11274 NS_DispatchToCurrentThread(event);
11277 static void
11278 LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc)
11280 if (!aLogFailure) {
11281 return;
11283 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11284 new AsyncEventDispatcher(aDoc,
11285 NS_LITERAL_STRING("mozfullscreenerror"),
11286 true,
11287 false);
11288 asyncDispatcher->PostDOMEvent();
11289 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
11290 NS_LITERAL_CSTRING("DOM"), aDoc,
11291 nsContentUtils::eDOM_PROPERTIES,
11292 aMessage);
11295 nsresult
11296 nsDocument::AddFullscreenApprovedObserver()
11298 if (mHasFullscreenApprovedObserver ||
11299 !Preferences::GetBool("full-screen-api.approval-required")) {
11300 return NS_OK;
11303 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11304 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
11306 nsresult res = os->AddObserver(this, "fullscreen-approved", true);
11307 NS_ENSURE_SUCCESS(res, res);
11309 mHasFullscreenApprovedObserver = true;
11311 return NS_OK;
11314 nsresult
11315 nsDocument::RemoveFullscreenApprovedObserver()
11317 if (!mHasFullscreenApprovedObserver) {
11318 return NS_OK;
11320 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11321 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
11323 nsresult res = os->RemoveObserver(this, "fullscreen-approved");
11324 NS_ENSURE_SUCCESS(res, res);
11326 mHasFullscreenApprovedObserver = false;
11328 return NS_OK;
11331 void
11332 nsDocument::CleanupFullscreenState()
11334 if (!mFullScreenStack.IsEmpty()) {
11335 // The top element in the full-screen stack will have full-screen
11336 // style bits set on it and its ancestors. Remove the style bits.
11337 // Note the non-top elements won't have the style bits set.
11338 Element* top = FullScreenStackTop();
11339 NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
11340 if (top) {
11341 // Remove any VR state properties
11342 top->DeleteProperty(nsGkAtoms::vr_state);
11344 EventStateManager::SetFullScreenState(top, false);
11346 mFullScreenStack.Clear();
11348 SetApprovedForFullscreen(false);
11349 RemoveFullscreenApprovedObserver();
11350 mFullscreenRoot = nullptr;
11353 bool
11354 nsDocument::FullScreenStackPush(Element* aElement)
11356 NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
11357 Element* top = FullScreenStackTop();
11358 if (top == aElement || !aElement) {
11359 return false;
11361 if (top) {
11362 // We're pushing a new element onto the full-screen stack, so we must
11363 // remove the ancestor and full-screen styles from the former top of the
11364 // stack.
11365 EventStateManager::SetFullScreenState(top, false);
11367 EventStateManager::SetFullScreenState(aElement, true);
11368 mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
11369 NS_ASSERTION(GetFullScreenElement() == aElement, "Should match");
11370 return true;
11373 void
11374 nsDocument::FullScreenStackPop()
11376 if (mFullScreenStack.IsEmpty()) {
11377 return;
11380 Element* top = FullScreenStackTop();
11382 // Remove any VR state properties
11383 top->DeleteProperty(nsGkAtoms::vr_state);
11385 // Remove styles from existing top element.
11386 EventStateManager::SetFullScreenState(top, false);
11388 // Remove top element. Note the remaining top element in the stack
11389 // will not have full-screen style bits set, so we will need to restore
11390 // them on the new top element before returning.
11391 uint32_t last = mFullScreenStack.Length() - 1;
11392 mFullScreenStack.RemoveElementAt(last);
11394 // Pop from the stack null elements (references to elements which have
11395 // been GC'd since they were added to the stack) and elements which are
11396 // no longer in this document.
11397 while (!mFullScreenStack.IsEmpty()) {
11398 Element* element = FullScreenStackTop();
11399 if (!element || !element->IsInDoc() || element->OwnerDoc() != this) {
11400 NS_ASSERTION(!element->IsFullScreenAncestor(),
11401 "Should have already removed full-screen styles");
11402 uint32_t last = mFullScreenStack.Length() - 1;
11403 mFullScreenStack.RemoveElementAt(last);
11404 } else {
11405 // The top element of the stack is now an in-doc element. Apply the
11406 // full-screen styles and return.
11407 EventStateManager::SetFullScreenState(element, true);
11408 break;
11413 Element*
11414 nsDocument::FullScreenStackTop()
11416 if (mFullScreenStack.IsEmpty()) {
11417 return nullptr;
11419 uint32_t last = mFullScreenStack.Length() - 1;
11420 nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
11421 NS_ASSERTION(element, "Should have full-screen element!");
11422 NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc");
11423 NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
11424 return element;
11427 // Returns true if aDoc is in the focused tab in the active window.
11428 static bool
11429 IsInActiveTab(nsIDocument* aDoc)
11431 nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
11432 if (!docshell) {
11433 return false;
11436 bool isActive = false;
11437 docshell->GetIsActive(&isActive);
11438 if (!isActive) {
11439 return false;
11442 nsCOMPtr<nsIDocShellTreeItem> rootItem;
11443 docshell->GetRootTreeItem(getter_AddRefs(rootItem));
11444 if (!rootItem) {
11445 return false;
11447 nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
11448 if (!rootWin) {
11449 return false;
11452 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11453 if (!fm) {
11454 return false;
11457 nsCOMPtr<nsIDOMWindow> activeWindow;
11458 fm->GetActiveWindow(getter_AddRefs(activeWindow));
11459 if (!activeWindow) {
11460 return false;
11463 return activeWindow == rootWin;
11466 nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
11467 const nsAString& aOrigin)
11469 // Ensure the frame element is the fullscreen element in this document.
11470 // If the frame element is already the fullscreen element in this document,
11471 // this has no effect.
11472 nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
11473 FullScreenOptions opts;
11474 RequestFullScreen(content->AsElement(),
11475 opts,
11476 /* aWasCallerChrome */ false,
11477 /* aNotifyOnOriginChange */ false);
11479 // Origin changed in child process, send notifiction, so that chrome can
11480 // update the UI to reflect the fullscreen origin change if necessary.
11481 // The BrowserElementChild listens on this, and forwards it over its
11482 // parent process, where it is redispatched. Chrome (in the root process,
11483 // which could be *this* process) listens for this notification so that
11484 // it can show a warning or approval UI.
11485 if (!aOrigin.IsEmpty()) {
11486 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11487 os->NotifyObservers(GetFullscreenRootDocument(this),
11488 "fullscreen-origin-change",
11489 PromiseFlatString(aOrigin).get());
11492 return NS_OK;
11495 nsresult nsDocument::RemoteFrameFullscreenReverted()
11497 RestorePreviousFullScreenState();
11498 return NS_OK;
11501 static void
11502 ReleaseHMDInfoRef(void *, nsIAtom*, void *aPropertyValue, void *)
11504 if (aPropertyValue) {
11505 static_cast<gfx::VRHMDInfo*>(aPropertyValue)->Release();
11509 void
11510 nsDocument::RequestFullScreen(Element* aElement,
11511 FullScreenOptions& aOptions,
11512 bool aWasCallerChrome,
11513 bool aNotifyOnOriginChange)
11515 NS_ASSERTION(aElement,
11516 "Must pass non-null element to nsDocument::RequestFullScreen");
11517 if (!aElement || aElement == GetFullScreenElement()) {
11518 return;
11520 if (!aElement->IsInDoc()) {
11521 LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this);
11522 return;
11524 if (aElement->OwnerDoc() != this) {
11525 LogFullScreenDenied(true, "FullScreenDeniedMovedDocument", this);
11526 return;
11528 if (!GetWindow()) {
11529 LogFullScreenDenied(true, "FullScreenDeniedLostWindow", this);
11530 return;
11532 if (nsContentUtils::IsFullscreenApiContentOnly() &&
11533 nsContentUtils::IsChromeDoc(this)) {
11534 // Block fullscreen requests in the chrome document when the fullscreen API
11535 // is configured for content only.
11536 LogFullScreenDenied(true, "FullScreenDeniedContentOnly", this);
11537 return;
11539 if (!IsFullScreenEnabled(aWasCallerChrome, true)) {
11540 // IsFullScreenEnabled calls LogFullScreenDenied, no need to log.
11541 return;
11543 if (GetFullScreenElement() &&
11544 !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) {
11545 // If this document is full-screen, only grant full-screen requests from
11546 // a descendant of the current full-screen element.
11547 LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this);
11548 return;
11550 if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
11551 LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this);
11552 return;
11554 // Deny requests when a windowed plugin is focused.
11555 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11556 if (!fm) {
11557 NS_WARNING("Failed to retrieve focus manager in full-screen request.");
11558 return;
11560 nsCOMPtr<nsIDOMElement> focusedElement;
11561 fm->GetFocusedElement(getter_AddRefs(focusedElement));
11562 if (focusedElement) {
11563 nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
11564 if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
11565 LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this);
11566 return;
11570 // Stash a reference to any existing fullscreen doc, we'll use this later
11571 // to detect if the origin which is fullscreen has changed.
11572 nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
11574 AddFullscreenApprovedObserver();
11576 // Stores a list of documents which we must dispatch "mozfullscreenchange"
11577 // too. We're required by the spec to dispatch the events in root-to-leaf
11578 // order, but we traverse the doctree in a leaf-to-root order, so we save
11579 // references to the documents we must dispatch to so that we get the order
11580 // as specified.
11581 nsAutoTArray<nsIDocument*, 8> changed;
11583 // Remember the root document, so that if a full-screen document is hidden
11584 // we can reset full-screen state in the remaining visible full-screen documents.
11585 nsIDocument* fullScreenRootDoc = GetFullscreenRootDocument(this);
11586 if (fullScreenRootDoc->IsFullScreenDoc()) {
11587 // A document is already in fullscreen, unlock the mouse pointer
11588 // before setting a new document to fullscreen
11589 UnlockPointer();
11592 // If a document is already in fullscreen, then unlock the mouse pointer
11593 // before setting a new document to fullscreen
11594 nsCOMPtr<Element> pointerLockedElement =
11595 do_QueryReferent(EventStateManager::sPointerLockedElement);
11596 if (pointerLockedElement) {
11597 UnlockPointer();
11600 // Process options -- in this case, just HMD
11601 if (aOptions.mVRHMDDevice) {
11602 nsRefPtr<gfx::VRHMDInfo> hmdRef = aOptions.mVRHMDDevice;
11603 aElement->SetProperty(nsGkAtoms::vr_state, hmdRef.forget().take(),
11604 ReleaseHMDInfoRef,
11605 true);
11608 // Set the full-screen element. This sets the full-screen style on the
11609 // element, and the full-screen-ancestor styles on ancestors of the element
11610 // in this document.
11611 DebugOnly<bool> x = FullScreenStackPush(aElement);
11612 NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
11613 changed.AppendElement(this);
11615 // Propagate up the document hierarchy, setting the full-screen element as
11616 // the element's container in ancestor documents. This also sets the
11617 // appropriate css styles as well. Note we don't propagate down the
11618 // document hierarchy, the full-screen element (or its container) is not
11619 // visible there. Stop when we reach the root document.
11620 nsIDocument* child = this;
11621 while (true) {
11622 child->SetFullscreenRoot(fullScreenRootDoc);
11623 NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
11624 "Fullscreen root should be set!");
11625 if (child == fullScreenRootDoc) {
11626 break;
11628 nsIDocument* parent = child->GetParentDocument();
11629 Element* element = parent->FindContentForSubDocument(child)->AsElement();
11630 if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
11631 changed.AppendElement(parent);
11632 child = parent;
11633 } else {
11634 // We've reached either the root, or a point in the doctree where the
11635 // new full-screen element container is the same as the previous
11636 // full-screen element's container. No more changes need to be made
11637 // to the full-screen stacks of documents further up the tree.
11638 break;
11642 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
11643 // order so that the events for the root document arrives before the leaf
11644 // document, as required by the spec.
11645 for (uint32_t i = 0; i < changed.Length(); ++i) {
11646 DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11649 // If this document hasn't already been approved in this session,
11650 // check to see if the user has granted the fullscreen access
11651 // to the document's principal's host, if it has one. Note that documents
11652 // in web apps which are the same origin as the web app are considered
11653 // trusted and so are automatically approved.
11654 if (!mIsApprovedForFullscreen) {
11655 mIsApprovedForFullscreen =
11656 !Preferences::GetBool("full-screen-api.approval-required") ||
11657 NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
11658 nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen");
11661 // If this document, or a document with the same principal has not
11662 // already been approved for fullscreen this fullscreen-session, dispatch
11663 // an event so that chrome knows to pop up a warning/approval UI.
11664 // Note previousFullscreenDoc=nullptr upon first entry, so we always
11665 // take this path on the first time we enter fullscreen in a fullscreen
11666 // session.
11667 if (!mIsApprovedForFullscreen ||
11668 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11669 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11670 new AsyncEventDispatcher(this,
11671 NS_LITERAL_STRING("MozEnteredDomFullscreen"),
11672 true,
11673 true);
11674 asyncDispatcher->PostDOMEvent();
11677 #ifdef DEBUG
11678 // Note assertions must run before SetWindowFullScreen() as that does
11679 // synchronous event dispatch which can run script which exits full-screen!
11680 NS_ASSERTION(GetFullScreenElement() == aElement,
11681 "Full-screen element should be the requested element!");
11682 NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
11683 nsCOMPtr<nsIDOMElement> fse;
11684 GetMozFullScreenElement(getter_AddRefs(fse));
11685 nsCOMPtr<nsIContent> c(do_QueryInterface(fse));
11686 NS_ASSERTION(c->AsElement() == aElement,
11687 "GetMozFullScreenElement should match GetFullScreenElement()");
11688 #endif
11690 // The origin which is fullscreen changed, send a notifiction so that the
11691 // root document knows the origin of the document which requested fullscreen.
11692 // This is used for the fullscreen approval UI. If we're in a child
11693 // process, the root BrowserElementChild listens for this notification,
11694 // and forwards it across to its BrowserElementParent, which
11695 // re-broadcasts the message for the root document in its process.
11696 if (aNotifyOnOriginChange &&
11697 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11698 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11699 nsIDocument* root = GetFullscreenRootDocument(this);
11700 nsAutoString origin;
11701 nsContentUtils::GetUTFOrigin(NodePrincipal(), origin);
11702 os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
11705 // Make the window full-screen. Note we must make the state changes above
11706 // before making the window full-screen, as then the document reports as
11707 // being in full-screen mode when the chrome "fullscreen" event fires,
11708 // enabling chrome to distinguish between browser and dom full-screen
11709 // modes. Also note that nsGlobalWindow::SetFullScreen() (which
11710 // SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
11711 // and does not operate on the a per-nsIDOMWindow basis.
11712 SetWindowFullScreen(this, true, aOptions.mVRHMDDevice);
11715 NS_IMETHODIMP
11716 nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
11718 ErrorResult rv;
11719 Element* el = GetMozFullScreenElement(rv);
11720 if (rv.Failed()) {
11721 return rv.ErrorCode();
11723 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
11724 retval.forget(aFullScreenElement);
11725 return NS_OK;
11728 Element*
11729 nsDocument::GetMozFullScreenElement(ErrorResult& rv)
11731 if (IsFullScreenDoc()) {
11732 // Must have a full-screen element while in full-screen mode.
11733 Element* el = GetFullScreenElement();
11734 if (!el) {
11735 rv.Throw(NS_ERROR_UNEXPECTED);
11737 return el;
11739 return nullptr;
11742 Element*
11743 nsDocument::GetFullScreenElement()
11745 Element* element = FullScreenStackTop();
11746 NS_ASSERTION(!element ||
11747 element->IsFullScreenAncestor(),
11748 "Fullscreen element should have fullscreen styles applied");
11749 return element;
11752 NS_IMETHODIMP
11753 nsDocument::GetMozFullScreen(bool *aFullScreen)
11755 *aFullScreen = MozFullScreen();
11756 return NS_OK;
11759 NS_IMETHODIMP
11760 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
11762 NS_ENSURE_ARG_POINTER(aFullScreen);
11763 *aFullScreen = MozFullScreenEnabled();
11764 return NS_OK;
11767 bool
11768 nsDocument::MozFullScreenEnabled()
11770 return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false);
11773 static bool
11774 HasFullScreenSubDocument(nsIDocument* aDoc)
11776 uint32_t count = CountFullscreenSubDocuments(aDoc);
11777 NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
11778 return count >= 1;
11781 bool
11782 nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
11784 if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) {
11785 // Chrome code can always use the full-screen API, provided it's not
11786 // explicitly disabled. Note IsCallerChrome() returns true when running
11787 // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an
11788 // nsRunnable!
11789 return true;
11792 if (!nsContentUtils::IsFullScreenApiEnabled()) {
11793 LogFullScreenDenied(aLogFailure, "FullScreenDeniedDisabled", this);
11794 return false;
11796 if (!IsVisible()) {
11797 LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this);
11798 return false;
11800 if (HasFullScreenSubDocument(this)) {
11801 LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this);
11802 return false;
11805 // Ensure that all ancestor <iframe> elements have the allowfullscreen
11806 // boolean attribute set.
11807 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
11808 bool allowed = false;
11809 if (docShell) {
11810 docShell->GetFullscreenAllowed(&allowed);
11812 if (!allowed) {
11813 LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this);
11816 return allowed;
11819 static void
11820 DispatchPointerLockChange(nsIDocument* aTarget)
11822 if (!aTarget) {
11823 return;
11826 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11827 new AsyncEventDispatcher(aTarget,
11828 NS_LITERAL_STRING("mozpointerlockchange"),
11829 true,
11830 false);
11831 asyncDispatcher->PostDOMEvent();
11834 static void
11835 DispatchPointerLockError(nsIDocument* aTarget)
11837 if (!aTarget) {
11838 return;
11841 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11842 new AsyncEventDispatcher(aTarget,
11843 NS_LITERAL_STRING("mozpointerlockerror"),
11844 true,
11845 false);
11846 asyncDispatcher->PostDOMEvent();
11849 mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest;
11851 class nsPointerLockPermissionRequest : public nsRunnable,
11852 public nsIContentPermissionRequest
11854 public:
11855 nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller)
11856 : mElement(do_GetWeakReference(aElement)),
11857 mDocument(do_GetWeakReference(aElement->OwnerDoc())),
11858 mUserInputOrChromeCaller(aUserInputOrChromeCaller) {}
11860 NS_DECL_ISUPPORTS_INHERITED
11861 NS_DECL_NSICONTENTPERMISSIONREQUEST
11863 NS_IMETHOD Run() MOZ_OVERRIDE
11865 nsCOMPtr<Element> e = do_QueryReferent(mElement);
11866 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11867 if (!e || !d || gPendingPointerLockRequest != this ||
11868 e->GetUncomposedDoc() != d) {
11869 Handled();
11870 DispatchPointerLockError(d);
11871 return NS_OK;
11874 // We're about to enter fullscreen mode.
11875 nsDocument* doc = static_cast<nsDocument*>(d.get());
11876 if (doc->mAsyncFullscreenPending ||
11877 (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) {
11878 // We're still waiting for approval.
11879 return NS_OK;
11882 if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) {
11883 Allow(JS::UndefinedHandleValue);
11884 return NS_OK;
11887 // In non-fullscreen mode user input (or chrome caller) is required!
11888 // Also, don't let the page to try to get the permission too many times.
11889 if (!mUserInputOrChromeCaller ||
11890 doc->mCancelledPointerLockRequests > 2) {
11891 Handled();
11892 DispatchPointerLockError(d);
11893 return NS_OK;
11896 // Handling a request from user input in non-fullscreen mode.
11897 // Do a normal permission check.
11898 nsCOMPtr<nsPIDOMWindow> window = doc->GetInnerWindow();
11899 nsContentPermissionUtils::AskPermission(this, window);
11900 return NS_OK;
11903 void Handled()
11905 mElement = nullptr;
11906 mDocument = nullptr;
11907 if (gPendingPointerLockRequest == this) {
11908 gPendingPointerLockRequest = nullptr;
11912 nsWeakPtr mElement;
11913 nsWeakPtr mDocument;
11914 bool mUserInputOrChromeCaller;
11916 protected:
11917 virtual ~nsPointerLockPermissionRequest() {}
11920 NS_IMPL_ISUPPORTS_INHERITED(nsPointerLockPermissionRequest,
11921 nsRunnable,
11922 nsIContentPermissionRequest)
11924 NS_IMETHODIMP
11925 nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
11927 nsTArray<nsString> emptyOptions;
11928 return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
11929 NS_LITERAL_CSTRING("unused"),
11930 emptyOptions,
11931 aTypes);
11934 NS_IMETHODIMP
11935 nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
11937 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11938 if (d) {
11939 NS_ADDREF(*aPrincipal = d->NodePrincipal());
11941 return NS_OK;
11944 NS_IMETHODIMP
11945 nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow)
11947 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11948 if (d) {
11949 NS_IF_ADDREF(*aWindow = d->GetInnerWindow());
11951 return NS_OK;
11954 NS_IMETHODIMP
11955 nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement)
11957 // It is enough to implement GetWindow.
11958 *aElement = nullptr;
11959 return NS_OK;
11962 NS_IMETHODIMP
11963 nsPointerLockPermissionRequest::Cancel()
11965 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11966 Handled();
11967 if (d) {
11968 static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++;
11969 DispatchPointerLockError(d);
11971 return NS_OK;
11974 NS_IMETHODIMP
11975 nsPointerLockPermissionRequest::Allow(JS::HandleValue aChoices)
11977 MOZ_ASSERT(aChoices.isUndefined());
11979 nsCOMPtr<Element> e = do_QueryReferent(mElement);
11980 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
11981 nsDocument* d = static_cast<nsDocument*>(doc.get());
11982 if (!e || !d || gPendingPointerLockRequest != this ||
11983 e->GetUncomposedDoc() != d ||
11984 (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) {
11985 Handled();
11986 DispatchPointerLockError(d);
11987 return NS_OK;
11990 // Mark handled here so that we don't need to call it everywhere below.
11991 Handled();
11993 nsCOMPtr<Element> pointerLockedElement =
11994 do_QueryReferent(EventStateManager::sPointerLockedElement);
11995 if (e == pointerLockedElement) {
11996 DispatchPointerLockChange(d);
11997 return NS_OK;
12000 // Note, we must bypass focus change, so pass true as the last parameter!
12001 if (!d->ShouldLockPointer(e, pointerLockedElement, true)) {
12002 DispatchPointerLockError(d);
12003 return NS_OK;
12006 if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
12007 DispatchPointerLockError(d);
12008 return NS_OK;
12011 d->mCancelledPointerLockRequests = 0;
12012 e->SetPointerLock();
12013 EventStateManager::sPointerLockedElement = do_GetWeakReference(e);
12014 EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
12015 NS_ASSERTION(EventStateManager::sPointerLockedElement &&
12016 EventStateManager::sPointerLockedDoc,
12017 "aElement and this should support weak references!");
12019 DispatchPointerLockChange(d);
12020 return NS_OK;
12023 void
12024 nsDocument::SetApprovedForFullscreen(bool aIsApproved)
12026 mIsApprovedForFullscreen = aIsApproved;
12029 nsresult
12030 nsDocument::Observe(nsISupports *aSubject,
12031 const char *aTopic,
12032 const char16_t *aData)
12034 if (strcmp("fullscreen-approved", aTopic) == 0) {
12035 nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
12036 if (subject != this) {
12037 return NS_OK;
12039 SetApprovedForFullscreen(true);
12040 if (gPendingPointerLockRequest) {
12041 // We have a request pending. Create a clone of it and re-dispatch so that
12042 // Run() method gets called again.
12043 nsCOMPtr<Element> el =
12044 do_QueryReferent(gPendingPointerLockRequest->mElement);
12045 nsCOMPtr<nsIDocument> doc =
12046 do_QueryReferent(gPendingPointerLockRequest->mDocument);
12047 bool userInputOrChromeCaller =
12048 gPendingPointerLockRequest->mUserInputOrChromeCaller;
12049 gPendingPointerLockRequest->Handled();
12050 if (doc == this && el && el->GetUncomposedDoc() == doc) {
12051 nsPointerLockPermissionRequest* clone =
12052 new nsPointerLockPermissionRequest(el, userInputOrChromeCaller);
12053 gPendingPointerLockRequest = clone;
12054 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
12055 NS_DispatchToMainThread(r);
12058 } else if (strcmp("app-theme-changed", aTopic) == 0) {
12059 if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()) &&
12060 !IsUnstyledDocument()) {
12061 // We don't want to style the chrome window, only app ones.
12062 OnAppThemeChanged();
12065 return NS_OK;
12068 void
12069 nsDocument::OnAppThemeChanged()
12071 // Bail out if there is no theme support set up properly.
12072 auto themeOrigin = Preferences::GetString("b2g.theme.origin");
12073 if (!themeOrigin || !Preferences::GetBool("dom.mozApps.themable")) {
12074 return;
12077 for (int32_t i = 0; i < GetNumberOfStyleSheets(); i++) {
12078 nsRefPtr<CSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
12079 if (!sheet) {
12080 continue;
12083 nsINode* owningNode = sheet->GetOwnerNode();
12084 if (!owningNode) {
12085 continue;
12087 // Get a DOM stylesheet link to check the href against the theme origin.
12088 nsIURI* sheetURI = sheet->GetOriginalURI();
12089 if (!sheetURI) {
12090 continue;
12092 nsAutoString sheetOrigin;
12093 nsContentUtils::GetUTFOrigin(sheetURI, sheetOrigin);
12094 if (!sheetOrigin.Equals(themeOrigin)) {
12095 continue;
12098 // Finally getting a Stylesheet link.
12099 nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(owningNode);
12100 if (!link) {
12101 continue;
12103 bool willNotify;
12104 bool isAlternate;
12105 link->UpdateStyleSheet(nullptr, &willNotify, &isAlternate, true);
12109 void
12110 nsDocument::RequestPointerLock(Element* aElement)
12112 NS_ASSERTION(aElement,
12113 "Must pass non-null element to nsDocument::RequestPointerLock");
12115 nsCOMPtr<Element> pointerLockedElement =
12116 do_QueryReferent(EventStateManager::sPointerLockedElement);
12117 if (aElement == pointerLockedElement) {
12118 DispatchPointerLockChange(this);
12119 return;
12122 if (!ShouldLockPointer(aElement, pointerLockedElement)) {
12123 DispatchPointerLockError(this);
12124 return;
12127 bool userInputOrChromeCaller = EventStateManager::IsHandlingUserInput() ||
12128 nsContentUtils::IsCallerChrome();
12130 gPendingPointerLockRequest =
12131 new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller);
12132 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
12133 NS_DispatchToMainThread(r);
12136 bool
12137 nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock,
12138 bool aNoFocusCheck)
12140 // Check if pointer lock pref is enabled
12141 if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
12142 NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled");
12143 return false;
12146 if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) {
12147 NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document");
12148 return false;
12151 if (!aElement->IsInDoc()) {
12152 NS_WARNING("ShouldLockPointer(): Element without Document");
12153 return false;
12156 if (mSandboxFlags & SANDBOXED_POINTER_LOCK) {
12157 NS_WARNING("ShouldLockPointer(): Document is sandboxed and doesn't allow pointer-lock");
12158 return false;
12161 // Check if the element is in a document with a docshell.
12162 nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
12163 if (!ownerDoc->GetContainer()) {
12164 return false;
12166 nsCOMPtr<nsPIDOMWindow> ownerWindow = ownerDoc->GetWindow();
12167 if (!ownerWindow) {
12168 return false;
12170 nsCOMPtr<nsPIDOMWindow> ownerInnerWindow = ownerDoc->GetInnerWindow();
12171 if (!ownerInnerWindow) {
12172 return false;
12174 if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
12175 return false;
12178 nsCOMPtr<nsIDOMWindow> top;
12179 ownerWindow->GetScriptableTop(getter_AddRefs(top));
12180 nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top);
12181 if (!piTop || !piTop->GetExtantDoc() ||
12182 piTop->GetExtantDoc()->Hidden()) {
12183 NS_WARNING("ShouldLockPointer(): Top document isn't visible.");
12184 return false;
12187 if (!aNoFocusCheck) {
12188 mozilla::ErrorResult rv;
12189 if (!piTop->GetExtantDoc()->HasFocus(rv)) {
12190 NS_WARNING("ShouldLockPointer(): Top document isn't focused.");
12191 return false;
12195 return true;
12198 bool
12199 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
12201 // NOTE: aElement will be nullptr when unlocking.
12202 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
12203 if (!window) {
12204 NS_WARNING("SetPointerLock(): No Window");
12205 return false;
12208 nsIDocShell *docShell = window->GetDocShell();
12209 if (!docShell) {
12210 NS_WARNING("SetPointerLock(): No DocShell (window already closed?)");
12211 return false;
12214 nsRefPtr<nsPresContext> presContext;
12215 docShell->GetPresContext(getter_AddRefs(presContext));
12216 if (!presContext) {
12217 NS_WARNING("SetPointerLock(): Unable to get presContext in \
12218 domWindow->GetDocShell()->GetPresContext()");
12219 return false;
12222 nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
12223 if (!shell) {
12224 NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()");
12225 return false;
12228 nsIFrame* rootFrame = shell->GetRootFrame();
12229 if (!rootFrame) {
12230 NS_WARNING("SetPointerLock(): Unable to get root frame");
12231 return false;
12234 nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
12235 if (!widget) {
12236 NS_WARNING("SetPointerLock(): Unable to find widget in \
12237 shell->GetRootFrame()->GetNearestWidget();");
12238 return false;
12241 if (aElement && (aElement->OwnerDoc() != this)) {
12242 NS_WARNING("SetPointerLock(): Element not in this document.");
12243 return false;
12246 // Hide the cursor and set pointer lock for future mouse events
12247 nsRefPtr<EventStateManager> esm = presContext->EventStateManager();
12248 esm->SetCursor(aCursorStyle, nullptr, false,
12249 0.0f, 0.0f, widget, true);
12250 esm->SetPointerLock(widget, aElement);
12252 return true;
12255 void
12256 nsDocument::UnlockPointer(nsIDocument* aDoc)
12258 if (!EventStateManager::sIsPointerLocked) {
12259 return;
12262 nsCOMPtr<nsIDocument> pointerLockedDoc =
12263 do_QueryReferent(EventStateManager::sPointerLockedDoc);
12264 if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
12265 return;
12267 nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
12268 if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
12269 return;
12272 nsCOMPtr<Element> pointerLockedElement =
12273 do_QueryReferent(EventStateManager::sPointerLockedElement);
12274 if (pointerLockedElement) {
12275 pointerLockedElement->ClearPointerLock();
12278 EventStateManager::sPointerLockedElement = nullptr;
12279 EventStateManager::sPointerLockedDoc = nullptr;
12280 static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc;
12281 gPendingPointerLockRequest = nullptr;
12282 DispatchPointerLockChange(pointerLockedDoc);
12285 void
12286 nsIDocument::UnlockPointer(nsIDocument* aDoc)
12288 nsDocument::UnlockPointer(aDoc);
12291 NS_IMETHODIMP
12292 nsDocument::MozExitPointerLock()
12294 nsIDocument::MozExitPointerLock();
12295 return NS_OK;
12298 NS_IMETHODIMP
12299 nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
12301 Element* el = nsIDocument::GetMozPointerLockElement();
12302 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
12303 retval.forget(aPointerLockedElement);
12304 return NS_OK;
12307 Element*
12308 nsIDocument::GetMozPointerLockElement()
12310 nsCOMPtr<Element> pointerLockedElement =
12311 do_QueryReferent(EventStateManager::sPointerLockedElement);
12312 if (!pointerLockedElement) {
12313 return nullptr;
12316 // Make sure pointer locked element is in the same document.
12317 nsCOMPtr<nsIDocument> pointerLockedDoc =
12318 do_QueryReferent(EventStateManager::sPointerLockedDoc);
12319 if (pointerLockedDoc != this) {
12320 return nullptr;
12323 return pointerLockedElement;
12326 void
12327 nsDocument::XPCOMShutdown()
12329 gPendingPointerLockRequest = nullptr;
12330 sProcessingStack.reset();
12333 void
12334 nsDocument::UpdateVisibilityState()
12336 dom::VisibilityState oldState = mVisibilityState;
12337 mVisibilityState = GetVisibilityState();
12338 if (oldState != mVisibilityState) {
12339 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
12340 NS_LITERAL_STRING("visibilitychange"),
12341 /* bubbles = */ true,
12342 /* cancelable = */ false);
12343 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
12344 NS_LITERAL_STRING("mozvisibilitychange"),
12345 /* bubbles = */ true,
12346 /* cancelable = */ false);
12348 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
12352 VisibilityState
12353 nsDocument::GetVisibilityState() const
12355 // We have to check a few pieces of information here:
12356 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
12357 // 2) Do we have an outer window? If not, we're hidden. Note that we don't
12358 // want to use GetWindow here because it does weird groveling for windows
12359 // in some cases.
12360 // 3) Is our outer window background? If so, we're hidden.
12361 // Otherwise, we're visible.
12362 if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
12363 mWindow->GetOuterWindow()->IsBackground()) {
12364 return dom::VisibilityState::Hidden;
12367 return dom::VisibilityState::Visible;
12370 /* virtual */ void
12371 nsDocument::PostVisibilityUpdateEvent()
12373 nsCOMPtr<nsIRunnable> event =
12374 NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState);
12375 NS_DispatchToMainThread(event);
12378 NS_IMETHODIMP
12379 nsDocument::GetMozHidden(bool* aHidden)
12381 *aHidden = MozHidden();
12382 return NS_OK;
12385 NS_IMETHODIMP
12386 nsDocument::GetHidden(bool* aHidden)
12388 *aHidden = Hidden();
12389 return NS_OK;
12392 NS_IMETHODIMP
12393 nsDocument::GetMozVisibilityState(nsAString& aState)
12395 WarnOnceAbout(ePrefixedVisibilityAPI);
12396 return GetVisibilityState(aState);
12399 NS_IMETHODIMP
12400 nsDocument::GetVisibilityState(nsAString& aState)
12402 const EnumEntry& entry =
12403 VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
12404 aState.AssignASCII(entry.value, entry.length);
12405 return NS_OK;
12408 /* virtual */ void
12409 nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
12411 aWindowSizes->mDOMOtherSize +=
12412 nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12414 if (mPresShell) {
12415 mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
12416 &aWindowSizes->mArenaStats,
12417 &aWindowSizes->mLayoutPresShellSize,
12418 &aWindowSizes->mLayoutStyleSetsSize,
12419 &aWindowSizes->mLayoutTextRunsSize,
12420 &aWindowSizes->mLayoutPresContextSize);
12423 aWindowSizes->mPropertyTablesSize +=
12424 mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12425 for (uint32_t i = 0, count = mExtraPropertyTables.Length();
12426 i < count; ++i) {
12427 aWindowSizes->mPropertyTablesSize +=
12428 mExtraPropertyTables[i]->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12431 if (EventListenerManager* elm = GetExistingListenerManager()) {
12432 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
12435 // Measurement of the following members may be added later if DMD finds it
12436 // is worthwhile:
12437 // - many!
12440 void
12441 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
12443 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
12444 DocAddSizeOfExcludingThis(aWindowSizes);
12447 static size_t
12448 SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
12449 MallocSizeOf aMallocSizeOf,
12450 void* aData)
12452 if (!aStyleSheet->GetOwningDocument()) {
12453 // Avoid over-reporting shared sheets.
12454 return 0;
12456 return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
12459 size_t
12460 nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
12462 // This SizeOfExcludingThis() overrides the one from nsINode. But
12463 // nsDocuments can only appear at the top of the DOM tree, and we use the
12464 // specialized DocAddSizeOfExcludingThis() in that case. So this should never
12465 // be called.
12466 MOZ_CRASH();
12469 void
12470 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
12472 nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
12474 for (nsIContent* node = nsINode::GetFirstChild();
12475 node;
12476 node = node->GetNextNode(this))
12478 size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12479 size_t* p;
12481 switch (node->NodeType()) {
12482 case nsIDOMNode::ELEMENT_NODE:
12483 p = &aWindowSizes->mDOMElementNodesSize;
12484 break;
12485 case nsIDOMNode::TEXT_NODE:
12486 p = &aWindowSizes->mDOMTextNodesSize;
12487 break;
12488 case nsIDOMNode::CDATA_SECTION_NODE:
12489 p = &aWindowSizes->mDOMCDATANodesSize;
12490 break;
12491 case nsIDOMNode::COMMENT_NODE:
12492 p = &aWindowSizes->mDOMCommentNodesSize;
12493 break;
12494 default:
12495 p = &aWindowSizes->mDOMOtherSize;
12496 break;
12499 *p += nodeSize;
12501 if (EventListenerManager* elm = node->GetExistingListenerManager()) {
12502 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
12506 aWindowSizes->mStyleSheetsSize +=
12507 mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12508 aWindowSizes->mMallocSizeOf);
12509 // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
12510 // (the nsLayoutStyleSheetCache singleton does) so pass nullptr as the
12511 // aSizeOfElementIncludingThis callback argument.
12512 aWindowSizes->mStyleSheetsSize +=
12513 mOnDemandBuiltInUASheets.SizeOfExcludingThis(nullptr,
12514 aWindowSizes->mMallocSizeOf);
12515 aWindowSizes->mStyleSheetsSize +=
12516 mAdditionalSheets[eAgentSheet].
12517 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12518 aWindowSizes->mMallocSizeOf);
12519 aWindowSizes->mStyleSheetsSize +=
12520 mAdditionalSheets[eUserSheet].
12521 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12522 aWindowSizes->mMallocSizeOf);
12523 aWindowSizes->mStyleSheetsSize +=
12524 mAdditionalSheets[eAuthorSheet].
12525 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12526 aWindowSizes->mMallocSizeOf);
12527 // Lumping in the loader with the style-sheets size is not ideal,
12528 // but most of the things in there are in fact stylesheets, so it
12529 // doesn't seem worthwhile to separate it out.
12530 aWindowSizes->mStyleSheetsSize +=
12531 CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12533 aWindowSizes->mDOMOtherSize +=
12534 mAttrStyleSheet ?
12535 mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
12538 aWindowSizes->mDOMOtherSize +=
12539 mSVGAttrAnimationRuleProcessor ?
12540 mSVGAttrAnimationRuleProcessor->DOMSizeOfIncludingThis(
12541 aWindowSizes->mMallocSizeOf) :
12544 aWindowSizes->mDOMOtherSize +=
12545 mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf);
12547 aWindowSizes->mDOMOtherSize +=
12548 mIdentifierMap.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12550 // Measurement of the following members may be added later if DMD finds it
12551 // is worthwhile:
12552 // - many!
12555 NS_IMETHODIMP
12556 nsDocument::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn)
12558 return nsINode::QuerySelector(aSelector, aReturn);
12561 NS_IMETHODIMP
12562 nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn)
12564 return nsINode::QuerySelectorAll(aSelector, aReturn);
12567 already_AddRefed<nsIDocument>
12568 nsIDocument::Constructor(const GlobalObject& aGlobal,
12569 ErrorResult& rv)
12571 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
12572 if (!global) {
12573 rv.Throw(NS_ERROR_UNEXPECTED);
12574 return nullptr;
12577 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
12578 if (!prin) {
12579 rv.Throw(NS_ERROR_UNEXPECTED);
12580 return nullptr;
12583 nsCOMPtr<nsIURI> uri;
12584 NS_NewURI(getter_AddRefs(uri), "about:blank");
12585 if (!uri) {
12586 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
12587 return nullptr;
12590 nsCOMPtr<nsIDOMDocument> document;
12591 nsresult res =
12592 NS_NewDOMDocument(getter_AddRefs(document),
12593 NullString(),
12594 EmptyString(),
12595 nullptr,
12596 uri,
12597 uri,
12598 prin->GetPrincipal(),
12599 true,
12600 global,
12601 DocumentFlavorPlain);
12602 if (NS_FAILED(res)) {
12603 rv.Throw(res);
12604 return nullptr;
12607 nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
12608 doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
12610 return doc.forget();
12613 XPathExpression*
12614 nsIDocument::CreateExpression(const nsAString& aExpression,
12615 XPathNSResolver* aResolver,
12616 ErrorResult& rv)
12618 return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
12621 nsINode*
12622 nsIDocument::CreateNSResolver(nsINode& aNodeResolver)
12624 return XPathEvaluator()->CreateNSResolver(aNodeResolver);
12627 already_AddRefed<XPathResult>
12628 nsIDocument::Evaluate(JSContext* aCx, const nsAString& aExpression,
12629 nsINode& aContextNode, XPathNSResolver* aResolver,
12630 uint16_t aType, JS::Handle<JSObject*> aResult,
12631 ErrorResult& rv)
12633 return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
12634 aType, aResult, rv);
12637 NS_IMETHODIMP
12638 nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode,
12639 nsIDOMNode* aResolver, uint16_t aType,
12640 nsISupports* aInResult, nsISupports** aResult)
12642 return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
12643 aInResult, aResult);
12646 XPathEvaluator*
12647 nsIDocument::XPathEvaluator()
12649 if (!mXPathEvaluator) {
12650 mXPathEvaluator = new dom::XPathEvaluator(this);
12652 return mXPathEvaluator;
12655 already_AddRefed<nsIDocumentEncoder>
12656 nsIDocument::GetCachedEncoder()
12658 return mCachedEncoder.forget();
12661 void
12662 nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
12664 mCachedEncoder = aEncoder;
12667 void
12668 nsIDocument::SetContentTypeInternal(const nsACString& aType)
12670 mCachedEncoder = nullptr;
12671 mContentType = aType;
12674 nsILoadContext*
12675 nsIDocument::GetLoadContext() const
12677 return mDocumentContainer;
12680 nsIDocShell*
12681 nsIDocument::GetDocShell() const
12683 return mDocumentContainer;
12686 void
12687 nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
12689 mStateObjectContainer = scContainer;
12690 mStateObjectCached = nullptr;
12693 already_AddRefed<Element>
12694 nsIDocument::CreateHTMLElement(nsIAtom* aTag)
12696 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
12697 nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
12698 nsIDOMNode::ELEMENT_NODE);
12699 MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
12701 nsCOMPtr<Element> element;
12702 DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
12703 nodeInfo.forget(),
12704 mozilla::dom::NOT_FROM_PARSER);
12706 MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
12707 return element.forget();
12710 bool
12711 MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
12713 nsCOMArray<nsIDocument>* documents =
12714 static_cast<nsCOMArray<nsIDocument>*>(aData);
12715 if (aDoc) {
12716 aDoc->SetIsInSyncOperation(true);
12717 documents->AppendObject(aDoc);
12718 aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
12720 return true;
12723 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
12725 mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
12726 nsContentUtils::SetMicroTaskLevel(0);
12727 if (aDoc) {
12728 nsPIDOMWindow* win = aDoc->GetWindow();
12729 if (win) {
12730 nsCOMPtr<nsIDOMWindow> topWindow;
12731 win->GetTop(getter_AddRefs(topWindow));
12732 nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
12733 if (top) {
12734 nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
12735 MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
12741 nsAutoSyncOperation::~nsAutoSyncOperation()
12743 for (int32_t i = 0; i < mDocuments.Count(); ++i) {
12744 mDocuments[i]->SetIsInSyncOperation(false);
12746 nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
12749 FontFaceSet*
12750 nsIDocument::GetFonts(ErrorResult& aRv)
12752 nsIPresShell* shell = GetShell();
12753 if (!shell) {
12754 aRv.Throw(NS_ERROR_FAILURE);
12755 return nullptr;
12758 nsPresContext* presContext = shell->GetPresContext();
12759 if (!presContext) {
12760 aRv.Throw(NS_ERROR_FAILURE);
12761 return nullptr;
12764 return presContext->Fonts();