Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / content / base / src / nsDocument.cpp
blobda9536be7a0ac1aea5f553a63d294d63e86402e4
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/DebugOnly.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/Likely.h"
18 #include <algorithm>
20 #ifdef MOZ_LOGGING
21 // so we can get logging even in release builds
22 #define FORCE_PR_LOG 1
23 #endif
24 #include "prlog.h"
25 #include "plstr.h"
26 #include "prprf.h"
28 #include "mozilla/Telemetry.h"
29 #include "nsIInterfaceRequestor.h"
30 #include "nsIInterfaceRequestorUtils.h"
31 #include "nsILoadContext.h"
32 #include "nsUnicharUtils.h"
33 #include "nsContentList.h"
34 #include "nsIObserver.h"
35 #include "nsIBaseWindow.h"
36 #include "mozilla/css/Loader.h"
37 #include "mozilla/css/ImageLoader.h"
38 #include "nsDocShell.h"
39 #include "nsIDocShellTreeItem.h"
40 #include "nsCOMArray.h"
41 #include "nsDOMClassInfo.h"
42 #include "mozilla/Services.h"
44 #include "mozilla/AsyncEventDispatcher.h"
45 #include "mozilla/BasicEvents.h"
46 #include "mozilla/EventListenerManager.h"
47 #include "mozilla/EventStateManager.h"
48 #include "nsIDOMNodeFilter.h"
50 #include "nsIDOMStyleSheet.h"
51 #include "mozilla/dom/Attr.h"
52 #include "nsIDOMDOMImplementation.h"
53 #include "nsIDOMDocumentXBL.h"
54 #include "mozilla/dom/Element.h"
55 #include "nsGenericHTMLElement.h"
56 #include "mozilla/dom/CDATASection.h"
57 #include "mozilla/dom/ProcessingInstruction.h"
58 #include "nsDOMString.h"
59 #include "nsNodeUtils.h"
60 #include "nsLayoutUtils.h" // for GetFrameForPoint
61 #include "nsIFrame.h"
62 #include "nsITabChild.h"
64 #include "nsRange.h"
65 #include "nsIDOMText.h"
66 #include "nsIDOMComment.h"
67 #include "mozilla/dom/DocumentType.h"
68 #include "mozilla/dom/NodeIterator.h"
69 #include "mozilla/dom/TreeWalker.h"
71 #include "nsIServiceManager.h"
72 #include "nsIServiceWorkerManager.h"
74 #include "nsContentCID.h"
75 #include "nsError.h"
76 #include "nsPresShell.h"
77 #include "nsPresContext.h"
78 #include "nsIJSON.h"
79 #include "nsThreadUtils.h"
80 #include "nsNodeInfoManager.h"
81 #include "nsIFileChannel.h"
82 #include "nsIMultiPartChannel.h"
83 #include "nsIRefreshURI.h"
84 #include "nsIWebNavigation.h"
85 #include "nsIScriptError.h"
86 #include "nsStyleSheetService.h"
88 #include "nsNetUtil.h" // for NS_MakeAbsoluteURI
90 #include "nsIScriptSecurityManager.h"
91 #include "nsIPrincipal.h"
93 #include "nsIDOMWindow.h"
94 #include "nsPIDOMWindow.h"
95 #include "nsIDOMElement.h"
96 #include "nsFocusManager.h"
98 // for radio group stuff
99 #include "nsIDOMHTMLInputElement.h"
100 #include "nsIRadioVisitor.h"
101 #include "nsIFormControl.h"
103 #include "nsBidiUtils.h"
105 #include "nsIDOMUserDataHandler.h"
106 #include "nsIDOMXPathNSResolver.h"
107 #include "nsIParserService.h"
108 #include "nsContentCreatorFunctions.h"
110 #include "nsIScriptContext.h"
111 #include "nsBindingManager.h"
112 #include "nsIDOMHTMLDocument.h"
113 #include "nsHTMLDocument.h"
114 #include "nsIDOMHTMLFormElement.h"
115 #include "nsIRequest.h"
116 #include "nsHostObjectProtocolHandler.h"
118 #include "nsCharsetSource.h"
119 #include "nsIParser.h"
120 #include "nsIContentSink.h"
122 #include "nsDateTimeFormatCID.h"
123 #include "nsIDateTimeFormat.h"
124 #include "mozilla/EventDispatcher.h"
125 #include "mozilla/EventStates.h"
126 #include "mozilla/InternalMutationEvent.h"
127 #include "nsDOMCID.h"
129 #include "jsapi.h"
130 #include "nsIXPConnect.h"
131 #include "nsCCUncollectableMarker.h"
132 #include "nsIContentPolicy.h"
133 #include "nsContentPolicyUtils.h"
134 #include "nsICategoryManager.h"
135 #include "nsIDocumentLoaderFactory.h"
136 #include "nsIDocumentLoader.h"
137 #include "nsIContentViewer.h"
138 #include "nsIXMLContentSink.h"
139 #include "nsIXULDocument.h"
140 #include "nsIPrompt.h"
141 #include "nsIPropertyBag2.h"
142 #include "mozilla/dom/PageTransitionEvent.h"
143 #include "mozilla/dom/StyleRuleChangeEvent.h"
144 #include "mozilla/dom/StyleSheetChangeEvent.h"
145 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
146 #include "nsJSUtils.h"
147 #include "nsFrameLoader.h"
148 #include "nsEscape.h"
149 #include "nsObjectLoadingContent.h"
150 #include "nsHtml5TreeOpExecutor.h"
151 #include "mozilla/dom/HTMLLinkElement.h"
152 #include "mozilla/dom/HTMLMediaElement.h"
154 #include "mozAutoDocUpdate.h"
155 #include "nsGlobalWindow.h"
156 #include "mozilla/dom/EncodingUtils.h"
157 #include "mozilla/dom/quota/QuotaManager.h"
158 #include "nsDOMNavigationTiming.h"
160 #include "nsSMILAnimationController.h"
161 #include "imgIContainer.h"
162 #include "nsSVGUtils.h"
163 #include "SVGElementFactory.h"
165 #include "nsRefreshDriver.h"
167 // FOR CSP (autogenerated by xpidl)
168 #include "nsIContentSecurityPolicy.h"
169 #include "nsCSPService.h"
170 #include "nsHTMLStyleSheet.h"
171 #include "nsHTMLCSSStyleSheet.h"
172 #include "mozilla/dom/DOMImplementation.h"
173 #include "mozilla/dom/ShadowRoot.h"
174 #include "mozilla/dom/Comment.h"
175 #include "nsTextNode.h"
176 #include "mozilla/dom/Link.h"
177 #include "mozilla/dom/HTMLElementBinding.h"
178 #include "mozilla/dom/SVGElementBinding.h"
179 #include "nsXULAppAPI.h"
180 #include "mozilla/dom/Touch.h"
181 #include "mozilla/dom/TouchEvent.h"
183 #include "mozilla/Preferences.h"
185 #include "imgILoader.h"
186 #include "imgRequestProxy.h"
187 #include "nsWrapperCacheInlines.h"
188 #include "nsSandboxFlags.h"
189 #include "nsIAppsService.h"
190 #include "mozilla/dom/AnimationTimeline.h"
191 #include "mozilla/dom/BindingUtils.h"
192 #include "mozilla/dom/DocumentFragment.h"
193 #include "mozilla/dom/Event.h"
194 #include "mozilla/dom/HTMLBodyElement.h"
195 #include "mozilla/dom/HTMLInputElement.h"
196 #include "mozilla/dom/NodeFilterBinding.h"
197 #include "mozilla/dom/OwningNonNull.h"
198 #include "mozilla/dom/TabChild.h"
199 #include "mozilla/dom/UndoManager.h"
200 #include "mozilla/dom/WebComponentsBinding.h"
201 #include "nsFrame.h"
202 #include "nsDOMCaretPosition.h"
203 #include "nsIDOMHTMLTextAreaElement.h"
204 #include "nsViewportInfo.h"
205 #include "nsIContentPermissionPrompt.h"
206 #include "mozilla/StaticPtr.h"
207 #include "nsITextControlElement.h"
208 #include "nsIDOMNSEditableElement.h"
209 #include "nsIEditor.h"
210 #include "nsIDOMCSSStyleRule.h"
211 #include "mozilla/css/Rule.h"
212 #include "nsIDOMLocation.h"
213 #include "nsIHttpChannelInternal.h"
214 #include "nsISecurityConsoleMessage.h"
215 #include "nsCharSeparatedTokenizer.h"
216 #include "mozilla/dom/XPathEvaluator.h"
217 #include "mozilla/dom/XPathResult.h"
218 #include "nsIDocumentEncoder.h"
219 #include "nsIDocumentActivity.h"
220 #include "nsIStructuredCloneContainer.h"
221 #include "nsIMutableArray.h"
222 #include "nsContentPermissionHelper.h"
223 #include "mozilla/dom/DOMStringList.h"
224 #include "nsWindowMemoryReporter.h"
225 #include "nsLocation.h"
227 #ifdef MOZ_MEDIA_NAVIGATOR
228 #include "mozilla/MediaManager.h"
229 #endif // MOZ_MEDIA_NAVIGATOR
230 #ifdef MOZ_WEBRTC
231 #include "IPeerConnection.h"
232 #endif // MOZ_WEBRTC
234 using namespace mozilla;
235 using namespace mozilla::dom;
237 typedef nsTArray<Link*> LinkArray;
239 #ifdef PR_LOGGING
240 static PRLogModuleInfo* gDocumentLeakPRLog;
241 static PRLogModuleInfo* gCspPRLog;
242 #endif
244 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
246 nsIdentifierMapEntry::~nsIdentifierMapEntry()
250 void
251 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
253 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
254 "mIdentifierMap mNameContentList");
255 aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
257 if (mImageElement) {
258 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
259 "mIdentifierMap mImageElement element");
260 nsIContent* imageElement = mImageElement;
261 aCallback->NoteXPCOMChild(imageElement);
265 bool
266 nsIdentifierMapEntry::IsEmpty()
268 return mIdContentList.Count() == 0 && !mNameContentList &&
269 !mChangeCallbacks && !mImageElement;
272 Element*
273 nsIdentifierMapEntry::GetIdElement()
275 return static_cast<Element*>(mIdContentList.SafeElementAt(0));
278 Element*
279 nsIdentifierMapEntry::GetImageIdElement()
281 return mImageElement ? mImageElement.get() : GetIdElement();
284 void
285 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
287 for (int32_t i = 0; i < mIdContentList.Count(); ++i) {
288 aElements->AppendObject(static_cast<Element*>(mIdContentList[i]));
292 void
293 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
294 void* aData, bool aForImage)
296 if (!mChangeCallbacks) {
297 mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
298 if (!mChangeCallbacks)
299 return;
302 ChangeCallback cc = { aCallback, aData, aForImage };
303 mChangeCallbacks->PutEntry(cc);
306 void
307 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
308 void* aData, bool aForImage)
310 if (!mChangeCallbacks)
311 return;
312 ChangeCallback cc = { aCallback, aData, aForImage };
313 mChangeCallbacks->RemoveEntry(cc);
314 if (mChangeCallbacks->Count() == 0) {
315 mChangeCallbacks = nullptr;
319 struct FireChangeArgs {
320 Element* mFrom;
321 Element* mTo;
322 bool mImageOnly;
323 bool mHaveImageOverride;
326 // XXX Workaround for bug 980560 to maintain the existing broken semantics
327 template<>
328 struct nsIStyleRule::COMTypeInfo<css::Rule, void> {
329 static const nsIID kIID;
331 const nsIID nsIStyleRule::COMTypeInfo<css::Rule, void>::kIID = NS_ISTYLE_RULE_IID;
333 namespace mozilla {
334 namespace dom {
336 static PLDHashOperator
337 CustomDefinitionsTraverse(CustomElementHashKey* aKey,
338 CustomElementDefinition* aDefinition,
339 void* aArg)
341 nsCycleCollectionTraversalCallback* cb =
342 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
344 nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks;
346 if (callbacks->mAttributeChangedCallback.WasPassed()) {
347 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
348 "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
349 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value());
352 if (callbacks->mCreatedCallback.WasPassed()) {
353 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
354 "mCustomDefinitions->mCallbacks->mCreatedCallback");
355 cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
358 if (callbacks->mAttachedCallback.WasPassed()) {
359 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
360 "mCustomDefinitions->mCallbacks->mAttachedCallback");
361 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value());
364 if (callbacks->mDetachedCallback.WasPassed()) {
365 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
366 "mCustomDefinitions->mCallbacks->mDetachedCallback");
367 cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value());
370 return PL_DHASH_NEXT;
373 static PLDHashOperator
374 CandidatesTraverse(CustomElementHashKey* aKey,
375 nsTArray<nsRefPtr<Element>>* aData,
376 void* aArg)
378 nsCycleCollectionTraversalCallback *cb =
379 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
380 for (size_t i = 0; i < aData->Length(); ++i) {
381 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element");
382 cb->NoteXPCOMChild(aData->ElementAt(i));
384 return PL_DHASH_NEXT;
387 struct CustomDefinitionTraceArgs
389 const TraceCallbacks& callbacks;
390 void* closure;
393 static PLDHashOperator
394 CustomDefinitionTrace(CustomElementHashKey *aKey,
395 CustomElementDefinition *aData,
396 void *aArg)
398 CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg);
399 MOZ_ASSERT(aData, "Definition must not be null");
400 traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype",
401 traceArgs->closure);
402 return PL_DHASH_NEXT;
405 NS_IMPL_CYCLE_COLLECTION_CLASS(Registry)
407 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry)
408 CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure };
409 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace,
410 &customDefinitionArgs);
411 NS_IMPL_CYCLE_COLLECTION_TRACE_END
413 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry)
414 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb);
415 tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb);
416 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
417 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
419 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry)
420 tmp->mCustomDefinitions.Clear();
421 tmp->mCandidatesMap.Clear();
422 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
424 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry)
425 NS_INTERFACE_MAP_ENTRY(nsISupports)
426 NS_INTERFACE_MAP_END
428 NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry)
429 NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry)
431 Registry::Registry()
433 mozilla::HoldJSObjects(this);
436 Registry::~Registry()
438 mozilla::DropJSObjects(this);
441 void
442 CustomElementCallback::Call()
444 ErrorResult rv;
445 switch (mType) {
446 case nsIDocument::eCreated:
447 // For the duration of this callback invocation, the element is being created
448 // flag must be set to true.
449 mOwnerData->mElementIsBeingCreated = true;
450 mOwnerData->mCreatedCallbackInvoked = true;
451 static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
452 mOwnerData->mElementIsBeingCreated = false;
453 break;
454 case nsIDocument::eAttached:
455 static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
456 break;
457 case nsIDocument::eDetached:
458 static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
459 break;
460 case nsIDocument::eAttributeChanged:
461 static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
462 mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
463 break;
467 void
468 CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
470 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
471 aCb.NoteXPCOMChild(mThisObject);
473 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
474 aCb.NoteXPCOMChild(mCallback);
477 CustomElementCallback::CustomElementCallback(Element* aThisObject,
478 nsIDocument::ElementCallbackType aCallbackType,
479 mozilla::dom::CallbackFunction* aCallback,
480 CustomElementData* aOwnerData)
481 : mThisObject(aThisObject),
482 mCallback(aCallback),
483 mType(aCallbackType),
484 mOwnerData(aOwnerData)
488 CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
489 nsIAtom* aType,
490 nsIAtom* aLocalName,
491 LifecycleCallbacks* aCallbacks,
492 uint32_t aNamespaceID,
493 uint32_t aDocOrder)
494 : mPrototype(aPrototype),
495 mType(aType),
496 mLocalName(aLocalName),
497 mCallbacks(aCallbacks),
498 mNamespaceID(aNamespaceID),
499 mDocOrder(aDocOrder)
503 CustomElementData::CustomElementData(nsIAtom* aType)
504 : mType(aType),
505 mCurrentCallback(-1),
506 mElementIsBeingCreated(false),
507 mCreatedCallbackInvoked(true),
508 mAssociatedMicroTask(-1)
512 void
513 CustomElementData::RunCallbackQueue()
515 // Note: It's possible to re-enter this method.
516 while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
517 mCallbackQueue[mCurrentCallback]->Call();
520 mCallbackQueue.Clear();
521 mCurrentCallback = -1;
524 } // namespace dom
525 } // namespace mozilla
527 static PLDHashOperator
528 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
530 FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
531 // Don't fire image changes for non-image observers, and don't fire element
532 // changes for image observers when an image override is active.
533 if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) :
534 args->mImageOnly)
535 return PL_DHASH_NEXT;
536 return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
537 ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
540 void
541 nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
542 Element* aNewElement,
543 bool aImageOnly)
545 if (!mChangeCallbacks)
546 return;
548 FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement };
549 mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
552 bool
553 nsIdentifierMapEntry::AddIdElement(Element* aElement)
555 NS_PRECONDITION(aElement, "Must have element");
556 NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0,
557 "Why is null in our list?");
559 #ifdef DEBUG
560 Element* currentElement =
561 static_cast<Element*>(mIdContentList.SafeElementAt(0));
562 #endif
564 // Common case
565 if (mIdContentList.Count() == 0) {
566 if (!mIdContentList.AppendElement(aElement))
567 return false;
568 NS_ASSERTION(currentElement == nullptr, "How did that happen?");
569 FireChangeCallbacks(nullptr, aElement);
570 return true;
573 // We seem to have multiple content nodes for the same id, or XUL is messing
574 // with us. Search for the right place to insert the content.
575 int32_t start = 0;
576 int32_t end = mIdContentList.Count();
577 do {
578 NS_ASSERTION(start < end, "Bogus start/end");
580 int32_t cur = (start + end) / 2;
581 NS_ASSERTION(cur >= start && cur < end, "What happened here?");
583 Element* curElement = static_cast<Element*>(mIdContentList[cur]);
584 if (curElement == aElement) {
585 // Already in the list, so already in the right spot. Get out of here.
586 // XXXbz this only happens because XUL does all sorts of random
587 // UpdateIdTableEntry calls. Hate, hate, hate!
588 return true;
591 if (nsContentUtils::PositionIsBefore(aElement, curElement)) {
592 end = cur;
593 } else {
594 start = cur + 1;
596 } while (start != end);
598 if (!mIdContentList.InsertElementAt(aElement, start))
599 return false;
601 if (start == 0) {
602 Element* oldElement =
603 static_cast<Element*>(mIdContentList.SafeElementAt(1));
604 NS_ASSERTION(currentElement == oldElement, "How did that happen?");
605 FireChangeCallbacks(oldElement, aElement);
607 return true;
610 void
611 nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
613 NS_PRECONDITION(aElement, "Missing element");
615 // This should only be called while the document is in an update.
616 // Assertions near the call to this method guarantee this.
618 // This could fire in OOM situations
619 // Only assert this in HTML documents for now as XUL does all sorts of weird
620 // crap.
621 NS_ASSERTION(!aElement->OwnerDoc()->IsHTML() ||
622 mIdContentList.IndexOf(aElement) >= 0,
623 "Removing id entry that doesn't exist");
625 // XXXbz should this ever Compact() I guess when all the content is gone
626 // we'll just get cleaned up in the natural order of things...
627 Element* currentElement =
628 static_cast<Element*>(mIdContentList.SafeElementAt(0));
629 mIdContentList.RemoveElement(aElement);
630 if (currentElement == aElement) {
631 FireChangeCallbacks(currentElement,
632 static_cast<Element*>(mIdContentList.SafeElementAt(0)));
636 void
637 nsIdentifierMapEntry::SetImageElement(Element* aElement)
639 Element* oldElement = GetImageIdElement();
640 mImageElement = aElement;
641 Element* newElement = GetImageIdElement();
642 if (oldElement != newElement) {
643 FireChangeCallbacks(oldElement, newElement, true);
647 void
648 nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
650 if (!mNameContentList) {
651 mNameContentList = new nsSimpleContentList(aNode);
654 mNameContentList->AppendElement(aElement);
657 void
658 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
660 if (mNameContentList) {
661 mNameContentList->RemoveElement(aElement);
665 bool
666 nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
668 Element* idElement = GetIdElement();
669 return idElement &&
670 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
673 size_t
674 nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
676 return nsStringHashKey::SizeOfExcludingThis(aMallocSizeOf);
679 // Helper structs for the content->subdoc map
681 class SubDocMapEntry : public PLDHashEntryHdr
683 public:
684 // Both of these are strong references
685 Element *mKey; // must be first, to look like PLDHashEntryStub
686 nsIDocument *mSubDocument;
689 struct FindContentData
691 explicit FindContentData(nsIDocument* aSubDoc)
692 : mSubDocument(aSubDoc), mResult(nullptr)
696 nsISupports *mSubDocument;
697 Element *mResult;
702 * A struct that holds all the information about a radio group.
704 struct nsRadioGroupStruct
706 nsRadioGroupStruct()
707 : mRequiredRadioCount(0)
708 , mGroupSuffersFromValueMissing(false)
712 * A strong pointer to the currently selected radio button.
714 nsRefPtr<HTMLInputElement> mSelectedRadioButton;
715 nsCOMArray<nsIFormControl> mRadioButtons;
716 uint32_t mRequiredRadioCount;
717 bool mGroupSuffersFromValueMissing;
721 nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument)
723 mLength = -1;
724 // Not reference counted to avoid circular references.
725 // The document will tell us when its going away.
726 mDocument = aDocument;
727 mDocument->AddObserver(this);
730 nsDOMStyleSheetList::~nsDOMStyleSheetList()
732 if (mDocument) {
733 mDocument->RemoveObserver(this);
737 NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList,
738 nsIDocumentObserver,
739 nsIMutationObserver)
741 uint32_t
742 nsDOMStyleSheetList::Length()
744 if (!mDocument) {
745 return 0;
748 // XXX Find the number and then cache it. We'll use the
749 // observer notification to figure out if new ones have
750 // been added or removed.
751 if (-1 == mLength) {
752 mLength = mDocument->GetNumberOfStyleSheets();
754 #ifdef DEBUG
755 int32_t i;
756 for (i = 0; i < mLength; i++) {
757 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
758 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
759 NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
761 #endif
763 return mLength;
766 CSSStyleSheet*
767 nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
769 if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
770 aFound = false;
771 return nullptr;
774 aFound = true;
775 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
776 NS_ASSERTION(sheet, "Must have a sheet");
778 return static_cast<CSSStyleSheet*>(sheet);
781 void
782 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
784 mDocument = nullptr;
787 void
788 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
789 nsIStyleSheet* aStyleSheet,
790 bool aDocumentSheet)
792 if (aDocumentSheet && -1 != mLength) {
793 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
794 if (domss) {
795 mLength++;
800 void
801 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
802 nsIStyleSheet* aStyleSheet,
803 bool aDocumentSheet)
805 if (aDocumentSheet && -1 != mLength) {
806 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
807 if (domss) {
808 mLength--;
813 // nsOnloadBlocker implementation
814 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
816 NS_IMETHODIMP
817 nsOnloadBlocker::GetName(nsACString &aResult)
819 aResult.AssignLiteral("about:document-onload-blocker");
820 return NS_OK;
823 NS_IMETHODIMP
824 nsOnloadBlocker::IsPending(bool *_retval)
826 *_retval = true;
827 return NS_OK;
830 NS_IMETHODIMP
831 nsOnloadBlocker::GetStatus(nsresult *status)
833 *status = NS_OK;
834 return NS_OK;
837 NS_IMETHODIMP
838 nsOnloadBlocker::Cancel(nsresult status)
840 return NS_OK;
842 NS_IMETHODIMP
843 nsOnloadBlocker::Suspend(void)
845 return NS_OK;
847 NS_IMETHODIMP
848 nsOnloadBlocker::Resume(void)
850 return NS_OK;
853 NS_IMETHODIMP
854 nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
856 *aLoadGroup = nullptr;
857 return NS_OK;
860 NS_IMETHODIMP
861 nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
863 return NS_OK;
866 NS_IMETHODIMP
867 nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
869 *aLoadFlags = nsIRequest::LOAD_NORMAL;
870 return NS_OK;
873 NS_IMETHODIMP
874 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
876 return NS_OK;
879 // ==================================================================
881 nsExternalResourceMap::nsExternalResourceMap()
882 : mHaveShutDown(false)
886 nsIDocument*
887 nsExternalResourceMap::RequestResource(nsIURI* aURI,
888 nsINode* aRequestingNode,
889 nsDocument* aDisplayDocument,
890 ExternalResourceLoad** aPendingLoad)
892 // If we ever start allowing non-same-origin loads here, we might need to do
893 // something interesting with aRequestingPrincipal even for the hashtable
894 // gets.
895 NS_PRECONDITION(aURI, "Must have a URI");
896 NS_PRECONDITION(aRequestingNode, "Must have a node");
897 *aPendingLoad = nullptr;
898 if (mHaveShutDown) {
899 return nullptr;
902 // First, make sure we strip the ref from aURI.
903 nsCOMPtr<nsIURI> clone;
904 nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
905 if (NS_FAILED(rv) || !clone) {
906 return nullptr;
909 ExternalResource* resource;
910 mMap.Get(clone, &resource);
911 if (resource) {
912 return resource->mDocument;
915 nsRefPtr<PendingLoad> load;
916 mPendingLoads.Get(clone, getter_AddRefs(load));
917 if (load) {
918 load.forget(aPendingLoad);
919 return nullptr;
922 load = new PendingLoad(aDisplayDocument);
924 mPendingLoads.Put(clone, load);
926 if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
927 // Make sure we don't thrash things by trying this load again, since
928 // chances are it failed for good reasons (security check, etc).
929 AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
930 } else {
931 load.forget(aPendingLoad);
934 return nullptr;
937 struct
938 nsExternalResourceEnumArgs
940 nsIDocument::nsSubDocEnumFunc callback;
941 void *data;
944 static PLDHashOperator
945 ExternalResourceEnumerator(nsIURI* aKey,
946 nsExternalResourceMap::ExternalResource* aData,
947 void* aClosure)
949 nsExternalResourceEnumArgs* args =
950 static_cast<nsExternalResourceEnumArgs*>(aClosure);
951 bool next =
952 aData->mDocument ? args->callback(aData->mDocument, args->data) : true;
953 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
956 void
957 nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
958 void* aData)
960 nsExternalResourceEnumArgs args = { aCallback, aData };
961 mMap.EnumerateRead(ExternalResourceEnumerator, &args);
964 static PLDHashOperator
965 ExternalResourceTraverser(nsIURI* aKey,
966 nsExternalResourceMap::ExternalResource* aData,
967 void* aClosure)
969 nsCycleCollectionTraversalCallback *cb =
970 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
972 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
973 "mExternalResourceMap.mMap entry"
974 "->mDocument");
975 cb->NoteXPCOMChild(aData->mDocument);
977 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
978 "mExternalResourceMap.mMap entry"
979 "->mViewer");
980 cb->NoteXPCOMChild(aData->mViewer);
982 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
983 "mExternalResourceMap.mMap entry"
984 "->mLoadGroup");
985 cb->NoteXPCOMChild(aData->mLoadGroup);
987 return PL_DHASH_NEXT;
990 void
991 nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
993 // mPendingLoads will get cleared out as the requests complete, so
994 // no need to worry about those here.
995 mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
998 static PLDHashOperator
999 ExternalResourceHider(nsIURI* aKey,
1000 nsExternalResourceMap::ExternalResource* aData,
1001 void* aClosure)
1003 if (aData->mViewer) {
1004 aData->mViewer->Hide();
1006 return PL_DHASH_NEXT;
1009 void
1010 nsExternalResourceMap::HideViewers()
1012 mMap.EnumerateRead(ExternalResourceHider, nullptr);
1015 static PLDHashOperator
1016 ExternalResourceShower(nsIURI* aKey,
1017 nsExternalResourceMap::ExternalResource* aData,
1018 void* aClosure)
1020 if (aData->mViewer) {
1021 aData->mViewer->Show();
1023 return PL_DHASH_NEXT;
1026 void
1027 nsExternalResourceMap::ShowViewers()
1029 mMap.EnumerateRead(ExternalResourceShower, nullptr);
1032 void
1033 TransferZoomLevels(nsIDocument* aFromDoc,
1034 nsIDocument* aToDoc)
1036 NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
1037 "transferring zoom levels from/to null doc");
1039 nsIPresShell* fromShell = aFromDoc->GetShell();
1040 if (!fromShell)
1041 return;
1043 nsPresContext* fromCtxt = fromShell->GetPresContext();
1044 if (!fromCtxt)
1045 return;
1047 nsIPresShell* toShell = aToDoc->GetShell();
1048 if (!toShell)
1049 return;
1051 nsPresContext* toCtxt = toShell->GetPresContext();
1052 if (!toCtxt)
1053 return;
1055 toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
1056 toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
1057 toCtxt->SetTextZoom(fromCtxt->TextZoom());
1060 void
1061 TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
1063 NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
1064 "transferring showing state from/to null doc");
1066 if (aFromDoc->IsShowing()) {
1067 aToDoc->OnPageShow(true, nullptr);
1071 nsresult
1072 nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
1073 nsIContentViewer* aViewer,
1074 nsILoadGroup* aLoadGroup,
1075 nsIDocument* aDisplayDocument)
1077 NS_PRECONDITION(aURI, "Unexpected call");
1078 NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
1079 "Must have both or neither");
1081 nsRefPtr<PendingLoad> load;
1082 mPendingLoads.Get(aURI, getter_AddRefs(load));
1083 mPendingLoads.Remove(aURI);
1085 nsresult rv = NS_OK;
1087 nsCOMPtr<nsIDocument> doc;
1088 if (aViewer) {
1089 doc = aViewer->GetDocument();
1090 NS_ASSERTION(doc, "Must have a document");
1092 nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
1093 if (xulDoc) {
1094 // We don't handle XUL stuff here yet.
1095 rv = NS_ERROR_NOT_AVAILABLE;
1096 } else {
1097 doc->SetDisplayDocument(aDisplayDocument);
1099 // Make sure that hiding our viewer will tear down its presentation.
1100 aViewer->SetSticky(false);
1102 rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
1103 if (NS_SUCCEEDED(rv)) {
1104 rv = aViewer->Open(nullptr, nullptr);
1108 if (NS_FAILED(rv)) {
1109 doc = nullptr;
1110 aViewer = nullptr;
1111 aLoadGroup = nullptr;
1115 ExternalResource* newResource = new ExternalResource();
1116 mMap.Put(aURI, newResource);
1118 newResource->mDocument = doc;
1119 newResource->mViewer = aViewer;
1120 newResource->mLoadGroup = aLoadGroup;
1121 if (doc) {
1122 TransferZoomLevels(aDisplayDocument, doc);
1123 TransferShowingState(aDisplayDocument, doc);
1126 const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
1127 for (uint32_t i = 0; i < obs.Length(); ++i) {
1128 obs[i]->Observe(doc, "external-resource-document-created", nullptr);
1131 return rv;
1134 NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
1135 nsIStreamListener,
1136 nsIRequestObserver)
1138 NS_IMETHODIMP
1139 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
1140 nsISupports *aContext)
1142 nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
1143 if (map.HaveShutDown()) {
1144 return NS_BINDING_ABORTED;
1147 nsCOMPtr<nsIContentViewer> viewer;
1148 nsCOMPtr<nsILoadGroup> loadGroup;
1149 nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
1150 getter_AddRefs(loadGroup));
1152 // Make sure to do this no matter what
1153 nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
1154 mDisplayDocument);
1155 if (NS_FAILED(rv)) {
1156 return rv;
1158 if (NS_FAILED(rv2)) {
1159 mTargetListener = nullptr;
1160 return rv2;
1163 return mTargetListener->OnStartRequest(aRequest, aContext);
1166 nsresult
1167 nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
1168 nsIContentViewer** aViewer,
1169 nsILoadGroup** aLoadGroup)
1171 NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
1172 *aViewer = nullptr;
1173 *aLoadGroup = nullptr;
1175 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1176 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
1178 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
1179 if (httpChannel) {
1180 bool requestSucceeded;
1181 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
1182 !requestSucceeded) {
1183 // Bail out on this load, since it looks like we have an HTTP error page
1184 return NS_BINDING_ABORTED;
1188 nsAutoCString type;
1189 chan->GetContentType(type);
1191 nsCOMPtr<nsILoadGroup> loadGroup;
1192 chan->GetLoadGroup(getter_AddRefs(loadGroup));
1194 // Give this document its own loadgroup
1195 nsCOMPtr<nsILoadGroup> newLoadGroup =
1196 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1197 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
1198 newLoadGroup->SetLoadGroup(loadGroup);
1200 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1201 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1203 nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1204 new LoadgroupCallbacks(callbacks);
1205 newLoadGroup->SetNotificationCallbacks(newCallbacks);
1207 // This is some serious hackery cribbed from docshell
1208 nsCOMPtr<nsICategoryManager> catMan =
1209 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1210 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1211 nsXPIDLCString contractId;
1212 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1213 getter_Copies(contractId));
1214 NS_ENSURE_SUCCESS(rv, rv);
1215 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1216 do_GetService(contractId);
1217 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1219 nsCOMPtr<nsIContentViewer> viewer;
1220 nsCOMPtr<nsIStreamListener> listener;
1221 rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1222 type.get(), nullptr, nullptr,
1223 getter_AddRefs(listener),
1224 getter_AddRefs(viewer));
1225 NS_ENSURE_SUCCESS(rv, rv);
1226 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1228 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1229 if (!parser) {
1230 /// We don't want to deal with the various fake documents yet
1231 return NS_ERROR_NOT_IMPLEMENTED;
1234 // We can't handle HTML and other weird things here yet.
1235 nsIContentSink* sink = parser->GetContentSink();
1236 nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1237 if (!xmlSink) {
1238 return NS_ERROR_NOT_IMPLEMENTED;
1241 listener.swap(mTargetListener);
1242 viewer.forget(aViewer);
1243 newLoadGroup.forget(aLoadGroup);
1244 return NS_OK;
1247 NS_IMETHODIMP
1248 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1249 nsISupports* aContext,
1250 nsIInputStream* aStream,
1251 uint64_t aOffset,
1252 uint32_t aCount)
1254 NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
1255 if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1256 return NS_BINDING_ABORTED;
1258 return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1259 aCount);
1262 NS_IMETHODIMP
1263 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1264 nsISupports* aContext,
1265 nsresult aStatus)
1267 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1268 if (mTargetListener) {
1269 nsCOMPtr<nsIStreamListener> listener;
1270 mTargetListener.swap(listener);
1271 return listener->OnStopRequest(aRequest, aContext, aStatus);
1274 return NS_OK;
1277 nsresult
1278 nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1279 nsINode* aRequestingNode)
1281 NS_PRECONDITION(aURI, "Must have a URI");
1282 NS_PRECONDITION(aRequestingNode, "Must have a node");
1284 // Time to start a load. First, the security checks.
1286 nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal();
1288 nsresult rv = nsContentUtils::GetSecurityManager()->
1289 CheckLoadURIWithPrincipal(requestingPrincipal, aURI,
1290 nsIScriptSecurityManager::STANDARD);
1291 NS_ENSURE_SUCCESS(rv, rv);
1293 // Allow data URIs and other URI's that inherit their principal by passing
1294 // true as the 3rd argument of CheckMayLoad, since we want
1295 // to allow external resources from data URIs regardless of the difference
1296 // in URI scheme.
1297 rv = requestingPrincipal->CheckMayLoad(aURI, true, true);
1298 NS_ENSURE_SUCCESS(rv, rv);
1300 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1301 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
1302 aURI,
1303 requestingPrincipal,
1304 aRequestingNode,
1305 EmptyCString(), //mime guess
1306 nullptr, //extra
1307 &shouldLoad,
1308 nsContentUtils::GetContentPolicy(),
1309 nsContentUtils::GetSecurityManager());
1310 if (NS_FAILED(rv)) return rv;
1311 if (NS_CP_REJECTED(shouldLoad)) {
1312 // Disallowed by content policy
1313 return NS_ERROR_CONTENT_BLOCKED;
1316 nsIDocument* doc = aRequestingNode->OwnerDoc();
1318 nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
1319 NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
1321 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
1322 nsCOMPtr<nsIChannel> channel;
1323 rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, loadGroup, req);
1324 NS_ENSURE_SUCCESS(rv, rv);
1326 mURI = aURI;
1328 return channel->AsyncOpen(this, nullptr);
1331 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1332 nsIInterfaceRequestor)
1334 #define IMPL_SHIM(_i) \
1335 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1337 IMPL_SHIM(nsILoadContext)
1338 IMPL_SHIM(nsIProgressEventSink)
1339 IMPL_SHIM(nsIChannelEventSink)
1340 IMPL_SHIM(nsISecurityEventSink)
1341 IMPL_SHIM(nsIApplicationCacheContainer)
1343 #undef IMPL_SHIM
1345 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1347 #define TRY_SHIM(_i) \
1348 PR_BEGIN_MACRO \
1349 if (IID_IS(_i)) { \
1350 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1351 if (!real) { \
1352 return NS_NOINTERFACE; \
1354 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1355 if (!shim) { \
1356 return NS_ERROR_OUT_OF_MEMORY; \
1358 shim.forget(aSink); \
1359 return NS_OK; \
1361 PR_END_MACRO
1363 NS_IMETHODIMP
1364 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1365 void **aSink)
1367 if (mCallbacks &&
1368 (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
1369 IID_IS(nsITabChild))) {
1370 return mCallbacks->GetInterface(aIID, aSink);
1373 *aSink = nullptr;
1375 TRY_SHIM(nsILoadContext);
1376 TRY_SHIM(nsIProgressEventSink);
1377 TRY_SHIM(nsIChannelEventSink);
1378 TRY_SHIM(nsISecurityEventSink);
1379 TRY_SHIM(nsIApplicationCacheContainer);
1381 return NS_NOINTERFACE;
1384 #undef TRY_SHIM
1385 #undef IID_IS
1387 nsExternalResourceMap::ExternalResource::~ExternalResource()
1389 if (mViewer) {
1390 mViewer->Close(nullptr);
1391 mViewer->Destroy();
1395 // ==================================================================
1396 // =
1397 // ==================================================================
1399 // If we ever have an nsIDocumentObserver notification for stylesheet title
1400 // changes we should update the list from that instead of overriding
1401 // EnsureFresh.
1402 class nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList
1404 public:
1405 explicit nsDOMStyleSheetSetList(nsIDocument* aDocument);
1407 void Disconnect()
1409 mDocument = nullptr;
1412 virtual void EnsureFresh() MOZ_OVERRIDE;
1414 protected:
1415 nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it
1416 // dies.
1419 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1420 : mDocument(aDocument)
1422 NS_ASSERTION(mDocument, "Must have document!");
1425 void
1426 nsDOMStyleSheetSetList::EnsureFresh()
1428 mNames.Clear();
1430 if (!mDocument) {
1431 return; // Spec says "no exceptions", and we have no style sets if we have
1432 // no document, for sure
1435 int32_t count = mDocument->GetNumberOfStyleSheets();
1436 nsAutoString title;
1437 for (int32_t index = 0; index < count; index++) {
1438 nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
1439 NS_ASSERTION(sheet, "Null sheet in sheet list!");
1440 sheet->GetTitle(title);
1441 if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1442 return;
1447 // ==================================================================
1448 nsIDocument::SelectorCache::SelectorCache()
1449 : nsExpirationTracker<SelectorCacheKey, 4>(1000) { }
1451 // CacheList takes ownership of aSelectorList.
1452 void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector,
1453 nsCSSSelectorList* aSelectorList)
1455 SelectorCacheKey* key = new SelectorCacheKey(aSelector);
1456 mTable.Put(key->mKey, aSelectorList);
1457 AddObject(key);
1460 class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable
1462 public:
1463 explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete)
1464 : mSelector(aToDelete)
1466 MOZ_COUNT_CTOR(SelectorCacheKeyDeleter);
1469 protected:
1470 ~SelectorCacheKeyDeleter()
1472 MOZ_COUNT_DTOR(SelectorCacheKeyDeleter);
1475 public:
1476 NS_IMETHOD Run()
1478 return NS_OK;
1481 private:
1482 nsAutoPtr<SelectorCacheKey> mSelector;
1485 void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
1487 RemoveObject(aSelector);
1488 mTable.Remove(aSelector->mKey);
1489 nsCOMPtr<nsIRunnable> runnable = new SelectorCacheKeyDeleter(aSelector);
1490 NS_DispatchToCurrentThread(runnable);
1494 struct nsIDocument::FrameRequest
1496 FrameRequest(const FrameRequestCallbackHolder& aCallback,
1497 int32_t aHandle) :
1498 mCallback(aCallback),
1499 mHandle(aHandle)
1502 // Conversion operator so that we can append these to a
1503 // FrameRequestCallbackList
1504 operator const FrameRequestCallbackHolder& () const {
1505 return mCallback;
1508 // Comparator operators to allow RemoveElementSorted with an
1509 // integer argument on arrays of FrameRequest
1510 bool operator==(int32_t aHandle) const {
1511 return mHandle == aHandle;
1513 bool operator<(int32_t aHandle) const {
1514 return mHandle < aHandle;
1517 FrameRequestCallbackHolder mCallback;
1518 int32_t mHandle;
1521 static already_AddRefed<mozilla::dom::NodeInfo> nullNodeInfo;
1523 // ==================================================================
1524 // =
1525 // ==================================================================
1526 nsIDocument::nsIDocument()
1527 : nsINode(nullNodeInfo),
1528 mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
1529 mNodeInfoManager(nullptr),
1530 mCompatMode(eCompatibility_FullStandards),
1531 mVisibilityState(dom::VisibilityState::Hidden),
1532 mIsInitialDocumentInWindow(false),
1533 mMayStartLayout(true),
1534 mVisible(true),
1535 mRemovedFromDocShell(false),
1536 // mAllowDNSPrefetch starts true, so that we can always reliably && it
1537 // with various values that might disable it. Since we never prefetch
1538 // unless we get a window, and in that case the docshell value will get
1539 // &&-ed in, this is safe.
1540 mAllowDNSPrefetch(true),
1541 mIsBeingUsedAsImage(false),
1542 mHasLinksToUpdate(false),
1543 mPartID(0),
1544 mDidFireDOMContentLoaded(true)
1546 SetInDocument();
1549 // NOTE! nsDocument::operator new() zeroes out all members, so don't
1550 // bother initializing members to 0.
1552 nsDocument::nsDocument(const char* aContentType)
1553 : nsIDocument()
1554 , mAnimatingImages(true)
1555 , mViewportType(Unknown)
1557 SetContentTypeInternal(nsDependentCString(aContentType));
1559 #ifdef PR_LOGGING
1560 if (!gDocumentLeakPRLog)
1561 gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
1563 if (gDocumentLeakPRLog)
1564 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1565 ("DOCUMENT %p created", this));
1567 if (!gCspPRLog)
1568 gCspPRLog = PR_NewLogModule("CSP");
1569 #endif
1571 // Start out mLastStyleSheetSet as null, per spec
1572 SetDOMStringToNull(mLastStyleSheetSet);
1574 if (!sProcessingStack) {
1575 sProcessingStack.emplace();
1576 // Add the base queue sentinel to the processing stack.
1577 sProcessingStack->AppendElement((CustomElementData*) nullptr);
1581 static PLDHashOperator
1582 ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
1584 if (aBoxObject) {
1585 aBoxObject->Clear();
1587 return PL_DHASH_NEXT;
1590 nsIDocument::~nsIDocument()
1592 if (mNodeInfoManager) {
1593 mNodeInfoManager->DropDocumentReference();
1598 nsDocument::~nsDocument()
1600 #ifdef PR_LOGGING
1601 if (gDocumentLeakPRLog)
1602 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1603 ("DOCUMENT %p destroyed", this));
1604 #endif
1606 NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1608 // Note: This assert is only non-fatal because mochitest-bc triggers
1609 // it... as well as the preceding assert about !mIsShowing.
1610 NS_ASSERTION(!mObservingAppThemeChanged,
1611 "Document leaked to shutdown, then the observer service dropped "
1612 "its ref to us so we were able to go away.");
1614 if (IsTopLevelContentDocument()) {
1615 //don't report for about: pages
1616 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1617 nsCOMPtr<nsIURI> uri;
1618 principal->GetURI(getter_AddRefs(uri));
1619 bool isAboutScheme = true;
1620 if (uri) {
1621 uri->SchemeIs("about", &isAboutScheme);
1624 if (!isAboutScheme) {
1625 // Record the page load
1626 uint32_t pageLoaded = 1;
1627 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1628 // Record the mixed content status of the docshell in Telemetry
1629 enum {
1630 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1631 MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
1632 MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
1633 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
1636 bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1637 bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1639 bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1640 bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1642 bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1643 bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1645 uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1646 if (hasMixedDisplay && hasMixedActive) {
1647 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1648 } else if (hasMixedActive){
1649 mixedContentLevel = MIXED_ACTIVE_CONTENT;
1650 } else if (hasMixedDisplay) {
1651 mixedContentLevel = MIXED_DISPLAY_CONTENT;
1653 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1657 mInDestructor = true;
1658 mInUnlinkOrDeletion = true;
1660 mRegistry = nullptr;
1662 mozilla::DropJSObjects(this);
1664 // Clear mObservers to keep it in sync with the mutationobserver list
1665 mObservers.Clear();
1667 if (mStyleSheetSetList) {
1668 mStyleSheetSetList->Disconnect();
1671 if (mAnimationController) {
1672 mAnimationController->Disconnect();
1675 mParentDocument = nullptr;
1677 // Kill the subdocument map, doing this will release its strong
1678 // references, if any.
1679 if (mSubDocuments) {
1680 PL_DHashTableDestroy(mSubDocuments);
1682 mSubDocuments = nullptr;
1685 // Destroy link map now so we don't waste time removing
1686 // links one by one
1687 DestroyElementMaps();
1689 nsAutoScriptBlocker scriptBlocker;
1691 int32_t indx; // must be signed
1692 uint32_t count = mChildren.ChildCount();
1693 for (indx = int32_t(count) - 1; indx >= 0; --indx) {
1694 mChildren.ChildAt(indx)->UnbindFromTree();
1695 mChildren.RemoveChildAt(indx);
1697 mFirstChild = nullptr;
1698 mCachedRootElement = nullptr;
1700 // Let the stylesheets know we're going away
1701 indx = mStyleSheets.Count();
1702 while (--indx >= 0) {
1703 mStyleSheets[indx]->SetOwningDocument(nullptr);
1705 if (mAttrStyleSheet) {
1706 mAttrStyleSheet->SetOwningDocument(nullptr);
1708 // We don't own the mOnDemandBuiltInUASheets, so we don't need to reset them.
1710 if (mListenerManager) {
1711 mListenerManager->Disconnect();
1712 UnsetFlags(NODE_HAS_LISTENERMANAGER);
1715 if (mScriptLoader) {
1716 mScriptLoader->DropDocumentReference();
1719 if (mCSSLoader) {
1720 // Could be null here if Init() failed or if we have been unlinked.
1721 mCSSLoader->DropDocumentReference();
1724 if (mStyleImageLoader) {
1725 mStyleImageLoader->DropDocumentReference();
1728 delete mHeaderData;
1730 if (mBoxObjectTable) {
1731 mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
1732 delete mBoxObjectTable;
1735 mPendingTitleChangeEvent.Revoke();
1737 for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) {
1738 nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]);
1741 // We don't want to leave residual locks on images. Make sure we're in an
1742 // unlocked state, and then clear the table.
1743 SetImageLockingState(false);
1744 mImageTracker.Clear();
1746 mPlugins.Clear();
1749 NS_INTERFACE_TABLE_HEAD(nsDocument)
1750 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
1751 NS_INTERFACE_TABLE_BEGIN
1752 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1753 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1754 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1755 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument)
1756 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode)
1757 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
1758 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1759 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
1760 NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1761 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1762 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1763 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1764 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1765 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver)
1766 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator)
1767 NS_INTERFACE_TABLE_END
1768 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1769 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
1770 new nsNode3Tearoff(this))
1771 NS_INTERFACE_MAP_END
1774 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1775 NS_IMETHODIMP_(MozExternalRefCountType)
1776 nsDocument::Release()
1778 NS_PRECONDITION(0 != mRefCnt, "dup release");
1779 NS_ASSERT_OWNINGTHREAD(nsDocument);
1780 nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1781 bool shouldDelete = false;
1782 nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1783 NS_LOG_RELEASE(this, count, "nsDocument");
1784 if (count == 0) {
1785 if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1786 mNeedsReleaseAfterStackRefCntRelease = true;
1787 NS_ADDREF_THIS();
1788 return mRefCnt.get();
1790 mRefCnt.incr(base);
1791 nsNodeUtils::LastRelease(this);
1792 mRefCnt.decr(base);
1793 if (shouldDelete) {
1794 mRefCnt.stabilizeForDeletion();
1795 DeleteCycleCollectable();
1798 return count;
1801 NS_IMETHODIMP_(void)
1802 nsDocument::DeleteCycleCollectable()
1804 delete this;
1807 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1808 if (Element::CanSkip(tmp, aRemovingAllowed)) {
1809 EventListenerManager* elm = tmp->GetExistingListenerManager();
1810 if (elm) {
1811 elm->MarkForCC();
1813 return true;
1815 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1817 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1818 return Element::CanSkipInCC(tmp);
1819 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1821 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1822 return Element::CanSkipThis(tmp);
1823 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1825 static PLDHashOperator
1826 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
1827 void *arg)
1829 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
1830 nsCycleCollectionTraversalCallback *cb =
1831 static_cast<nsCycleCollectionTraversalCallback*>(arg);
1833 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
1834 cb->NoteXPCOMChild(entry->mKey);
1835 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
1836 cb->NoteXPCOMChild(entry->mSubDocument);
1838 return PL_DHASH_NEXT;
1841 static PLDHashOperator
1842 RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
1843 void* aClosure)
1845 nsCycleCollectionTraversalCallback *cb =
1846 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
1848 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1849 "mRadioGroups entry->mSelectedRadioButton");
1850 cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton));
1852 uint32_t i, count = aData->mRadioButtons.Count();
1853 for (i = 0; i < count; ++i) {
1854 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1855 "mRadioGroups entry->mRadioButtons[i]");
1856 cb->NoteXPCOMChild(aData->mRadioButtons[i]);
1859 return PL_DHASH_NEXT;
1862 static PLDHashOperator
1863 BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg)
1865 nsCycleCollectionTraversalCallback *cb =
1866 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
1868 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
1869 cb->NoteXPCOMChild(boxObject);
1871 return PL_DHASH_NEXT;
1874 static PLDHashOperator
1875 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
1877 nsCycleCollectionTraversalCallback *cb =
1878 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
1879 aEntry->Traverse(cb);
1880 return PL_DHASH_NEXT;
1883 static const char* kNSURIs[] = {
1884 "([none])",
1885 "(xmlns)",
1886 "(xml)",
1887 "(xhtml)",
1888 "(XLink)",
1889 "(XSLT)",
1890 "(XBL)",
1891 "(MathML)",
1892 "(RDF)",
1893 "(XUL)"
1896 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1897 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1898 char name[512];
1899 nsAutoCString loadedAsData;
1900 if (tmp->IsLoadedAsData()) {
1901 loadedAsData.AssignLiteral("data");
1902 } else {
1903 loadedAsData.AssignLiteral("normal");
1905 uint32_t nsid = tmp->GetDefaultNamespaceID();
1906 nsAutoCString uri;
1907 if (tmp->mDocumentURI)
1908 tmp->mDocumentURI->GetSpec(uri);
1909 if (nsid < ArrayLength(kNSURIs)) {
1910 PR_snprintf(name, sizeof(name), "nsDocument %s %s %s",
1911 loadedAsData.get(), kNSURIs[nsid], uri.get());
1913 else {
1914 PR_snprintf(name, sizeof(name), "nsDocument %s %s",
1915 loadedAsData.get(), uri.get());
1917 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1919 else {
1920 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1923 // Always need to traverse script objects, so do that before we check
1924 // if we're uncollectable.
1925 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1927 if (!nsINode::Traverse(tmp, cb)) {
1928 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1931 tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
1933 tmp->mExternalResourceMap.Traverse(&cb);
1935 // Traverse the mChildren nsAttrAndChildArray.
1936 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1937 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1938 cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1941 // Traverse all nsIDocument pointer members.
1942 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1943 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1945 // Traverse all nsDocument nsCOMPtrs.
1946 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1947 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1948 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1949 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1950 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1951 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1952 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterDocument)
1953 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImportManager)
1955 tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
1957 // The boxobject for an element will only exist as long as it's in the
1958 // document, so we'll traverse the table here instead of from the element.
1959 if (tmp->mBoxObjectTable) {
1960 tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
1963 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
1964 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
1965 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator)
1966 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
1967 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
1968 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
1969 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
1970 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
1971 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
1972 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
1973 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
1974 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
1975 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationTimeline)
1976 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
1977 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
1978 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
1980 // Traverse all our nsCOMArrays.
1981 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
1982 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
1983 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
1985 for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
1986 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
1987 cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
1990 // Traverse animation components
1991 if (tmp->mAnimationController) {
1992 tmp->mAnimationController->Traverse(&cb);
1995 if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
1996 PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
1999 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
2001 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
2002 nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb);
2004 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2006 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
2008 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
2009 if (tmp->PreservingWrapper()) {
2010 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
2012 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2013 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2016 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
2017 tmp->mInUnlinkOrDeletion = true;
2019 // Clear out our external resources
2020 tmp->mExternalResourceMap.Shutdown();
2022 nsAutoScriptBlocker scriptBlocker;
2024 nsINode::Unlink(tmp);
2026 // Unlink the mChildren nsAttrAndChildArray.
2027 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1;
2028 indx >= 0; --indx) {
2029 tmp->mChildren.ChildAt(indx)->UnbindFromTree();
2030 tmp->mChildren.RemoveChildAt(indx);
2032 tmp->mFirstChild = nullptr;
2034 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator)
2035 tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
2036 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
2037 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
2038 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
2039 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
2040 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
2041 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
2042 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
2043 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline)
2044 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
2045 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
2046 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
2047 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
2048 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
2050 tmp->mParentDocument = nullptr;
2052 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
2055 if (tmp->mBoxObjectTable) {
2056 tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
2057 delete tmp->mBoxObjectTable;
2058 tmp->mBoxObjectTable = nullptr;
2061 if (tmp->mListenerManager) {
2062 tmp->mListenerManager->Disconnect();
2063 tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
2064 tmp->mListenerManager = nullptr;
2067 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
2069 if (tmp->mStyleSheetSetList) {
2070 tmp->mStyleSheetSetList->Disconnect();
2071 tmp->mStyleSheetSetList = nullptr;
2074 if (tmp->mSubDocuments) {
2075 PL_DHashTableDestroy(tmp->mSubDocuments);
2076 tmp->mSubDocuments = nullptr;
2079 tmp->mFrameRequestCallbacks.Clear();
2081 tmp->mRadioGroups.Clear();
2083 // nsDocument has a pretty complex destructor, so we're going to
2084 // assume that *most* cycles you actually want to break somewhere
2085 // else, and not unlink an awful lot here.
2087 tmp->mIdentifierMap.Clear();
2088 tmp->mExpandoAndGeneration.Unlink();
2090 if (tmp->mAnimationController) {
2091 tmp->mAnimationController->Unlink();
2094 tmp->mPendingTitleChangeEvent.Revoke();
2096 if (tmp->mCSSLoader) {
2097 tmp->mCSSLoader->DropDocumentReference();
2098 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
2101 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
2102 nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]);
2105 tmp->mInUnlinkOrDeletion = false;
2106 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2108 static bool sPrefsInitialized = false;
2109 static uint32_t sOnloadDecodeLimit = 0;
2111 nsresult
2112 nsDocument::Init()
2114 if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
2115 return NS_ERROR_ALREADY_INITIALIZED;
2118 if (!sPrefsInitialized) {
2119 sPrefsInitialized = true;
2120 Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0);
2123 // Force initialization.
2124 nsINode::nsSlots* slots = Slots();
2126 // Prepend self as mutation-observer whether we need it or not (some
2127 // subclasses currently do, other don't). This is because the code in
2128 // nsNodeUtils always notifies the first observer first, expecting the
2129 // first observer to be the document.
2130 NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
2131 NS_ERROR_OUT_OF_MEMORY);
2134 mOnloadBlocker = new nsOnloadBlocker();
2135 mCSSLoader = new mozilla::css::Loader(this);
2136 // Assume we're not quirky, until we know otherwise
2137 mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2139 mStyleImageLoader = new mozilla::css::ImageLoader(this);
2141 mNodeInfoManager = new nsNodeInfoManager();
2142 nsresult rv = mNodeInfoManager->Init(this);
2143 NS_ENSURE_SUCCESS(rv, rv);
2145 // mNodeInfo keeps NodeInfoManager alive!
2146 mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2147 NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2148 NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
2149 "Bad NodeType in aNodeInfo");
2151 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2153 // If after creation the owner js global is not set for a document
2154 // we use the default compartment for this document, instead of creating
2155 // wrapper in some random compartment when the document is exposed to js
2156 // via some events.
2157 nsCOMPtr<nsIGlobalObject> global = xpc::GetNativeForGlobal(xpc::PrivilegedJunkScope());
2158 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2159 mScopeObject = do_GetWeakReference(global);
2160 MOZ_ASSERT(mScopeObject);
2162 mScriptLoader = new nsScriptLoader(this);
2164 mozilla::HoldJSObjects(this);
2166 return NS_OK;
2169 void
2170 nsIDocument::DeleteAllProperties()
2172 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2173 PropertyTable(i)->DeleteAllProperties();
2177 void
2178 nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
2180 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2181 PropertyTable(i)->DeleteAllPropertiesFor(aNode);
2185 nsPropertyTable*
2186 nsIDocument::GetExtraPropertyTable(uint16_t aCategory)
2188 NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
2189 while (aCategory >= mExtraPropertyTables.Length() + 1) {
2190 mExtraPropertyTables.AppendElement(new nsPropertyTable());
2192 return mExtraPropertyTables[aCategory - 1];
2195 bool
2196 nsIDocument::IsVisibleConsideringAncestors() const
2198 const nsIDocument *parent = this;
2199 do {
2200 if (!parent->IsVisible()) {
2201 return false;
2203 } while ((parent = parent->GetParentDocument()));
2205 return true;
2208 void
2209 nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
2211 nsCOMPtr<nsIURI> uri;
2212 nsCOMPtr<nsIPrincipal> principal;
2213 if (aChannel) {
2214 // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2215 // nsScriptSecurityManager::GetChannelPrincipal.
2216 // Note: this should match nsDocShell::OnLoadingSite
2217 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2219 nsIScriptSecurityManager *securityManager =
2220 nsContentUtils::GetSecurityManager();
2221 if (securityManager) {
2222 securityManager->GetChannelPrincipal(aChannel,
2223 getter_AddRefs(principal));
2227 ResetToURI(uri, aLoadGroup, principal);
2229 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2230 if (bag) {
2231 nsCOMPtr<nsIURI> baseURI;
2232 bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2233 NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2234 if (baseURI) {
2235 mDocumentBaseURI = baseURI;
2236 mChromeXHRDocBaseURI = baseURI;
2240 mChannel = aChannel;
2243 void
2244 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
2245 nsIPrincipal* aPrincipal)
2247 NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
2249 #ifdef PR_LOGGING
2250 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2251 nsAutoCString spec;
2252 aURI->GetSpec(spec);
2253 PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
2255 #endif
2257 mSecurityInfo = nullptr;
2259 mDocumentLoadGroup = nullptr;
2261 // Delete references to sub-documents and kill the subdocument map,
2262 // if any. It holds strong references
2263 if (mSubDocuments) {
2264 PL_DHashTableDestroy(mSubDocuments);
2266 mSubDocuments = nullptr;
2269 // Destroy link map now so we don't waste time removing
2270 // links one by one
2271 DestroyElementMaps();
2273 bool oldVal = mInUnlinkOrDeletion;
2274 mInUnlinkOrDeletion = true;
2275 uint32_t count = mChildren.ChildCount();
2276 { // Scope for update
2277 MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true);
2278 for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
2279 nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
2281 nsIContent* previousSibling = content->GetPreviousSibling();
2283 if (nsINode::GetFirstChild() == content) {
2284 mFirstChild = content->GetNextSibling();
2286 mChildren.RemoveChildAt(i);
2287 nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
2288 content->UnbindFromTree();
2290 mCachedRootElement = nullptr;
2292 mInUnlinkOrDeletion = oldVal;
2294 mRegistry = nullptr;
2296 // Reset our stylesheets
2297 ResetStylesheetsToURI(aURI);
2299 // Release the listener manager
2300 if (mListenerManager) {
2301 mListenerManager->Disconnect();
2302 mListenerManager = nullptr;
2305 // Release the stylesheets list.
2306 mDOMStyleSheets = nullptr;
2308 // Release our principal after tearing down the document, rather than before.
2309 // This ensures that, during teardown, the document and the dying window (which
2310 // already nulled out its document pointer and cached the principal) have
2311 // matching principals.
2312 SetPrincipal(nullptr);
2314 // Clear the original URI so SetDocumentURI sets it.
2315 mOriginalURI = nullptr;
2317 SetDocumentURI(aURI);
2318 mChromeXHRDocURI = aURI;
2319 // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2320 // mDocumentURI.
2321 mDocumentBaseURI = nullptr;
2322 mChromeXHRDocBaseURI = nullptr;
2324 if (aLoadGroup) {
2325 mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2326 // there was an assertion here that aLoadGroup was not null. This
2327 // is no longer valid: nsDocShell::SetDocument does not create a
2328 // load group, and it works just fine
2330 // XXXbz what does "just fine" mean exactly? And given that there
2331 // is no nsDocShell::SetDocument, what is this talking about?
2334 mLastModified.Truncate();
2335 // XXXbz I guess we're assuming that the caller will either pass in
2336 // a channel with a useful type or call SetContentType?
2337 SetContentTypeInternal(EmptyCString());
2338 mContentLanguage.Truncate();
2339 mBaseTarget.Truncate();
2340 mReferrer.Truncate();
2342 mXMLDeclarationBits = 0;
2344 // Now get our new principal
2345 if (aPrincipal) {
2346 SetPrincipal(aPrincipal);
2347 } else {
2348 nsIScriptSecurityManager *securityManager =
2349 nsContentUtils::GetSecurityManager();
2350 if (securityManager) {
2351 nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2353 if (!loadContext && aLoadGroup) {
2354 nsCOMPtr<nsIInterfaceRequestor> cbs;
2355 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2356 loadContext = do_GetInterface(cbs);
2359 MOZ_ASSERT(loadContext,
2360 "must have a load context or pass in an explicit principal");
2362 nsCOMPtr<nsIPrincipal> principal;
2363 nsresult rv = securityManager->
2364 GetLoadContextCodebasePrincipal(mDocumentURI, loadContext,
2365 getter_AddRefs(principal));
2366 if (NS_SUCCEEDED(rv)) {
2367 SetPrincipal(principal);
2372 // Refresh the principal on the compartment.
2373 nsPIDOMWindow* win = GetInnerWindow();
2374 if (win) {
2375 win->RefreshCompartmentPrincipal();
2379 void
2380 nsDocument::RemoveDocStyleSheetsFromStyleSets()
2382 // The stylesheets should forget us
2383 int32_t indx = mStyleSheets.Count();
2384 while (--indx >= 0) {
2385 nsIStyleSheet* sheet = mStyleSheets[indx];
2386 sheet->SetOwningDocument(nullptr);
2388 if (sheet->IsApplicable()) {
2389 nsCOMPtr<nsIPresShell> shell = GetShell();
2390 if (shell) {
2391 shell->StyleSet()->RemoveDocStyleSheet(sheet);
2394 // XXX Tell observers?
2398 void
2399 nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType)
2401 // The stylesheets should forget us
2402 int32_t indx = aSheets.Count();
2403 while (--indx >= 0) {
2404 nsIStyleSheet* sheet = aSheets[indx];
2405 sheet->SetOwningDocument(nullptr);
2407 if (sheet->IsApplicable()) {
2408 nsCOMPtr<nsIPresShell> shell = GetShell();
2409 if (shell) {
2410 shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2414 // XXX Tell observers?
2419 void
2420 nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
2422 MOZ_ASSERT(aURI);
2424 mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
2425 RemoveDocStyleSheetsFromStyleSets();
2426 RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, nsStyleSet::eAgentSheet);
2427 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet);
2428 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet);
2429 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet);
2431 // Release all the sheets
2432 mStyleSheets.Clear();
2433 mOnDemandBuiltInUASheets.Clear();
2434 for (uint32_t i = 0; i < SheetTypeCount; ++i)
2435 mAdditionalSheets[i].Clear();
2437 // NOTE: We don't release the catalog sheets. It doesn't really matter
2438 // now, but it could in the future -- in which case not releasing them
2439 // is probably the right thing to do.
2441 // Now reset our inline style and attribute sheets.
2442 if (mAttrStyleSheet) {
2443 mAttrStyleSheet->Reset();
2444 mAttrStyleSheet->SetOwningDocument(this);
2445 } else {
2446 mAttrStyleSheet = new nsHTMLStyleSheet(this);
2449 if (!mStyleAttrStyleSheet) {
2450 mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2453 // Now set up our style sets
2454 nsCOMPtr<nsIPresShell> shell = GetShell();
2455 if (shell) {
2456 FillStyleSet(shell->StyleSet());
2460 static bool
2461 AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
2463 nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
2464 styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet);
2465 return true;
2468 static void
2469 AppendSheetsToStyleSet(nsStyleSet* aStyleSet,
2470 const nsCOMArray<nsIStyleSheet>& aSheets,
2471 nsStyleSet::sheetType aType)
2473 for (int32_t i = aSheets.Count() - 1; i >= 0; --i) {
2474 aStyleSet->AppendStyleSheet(aType, aSheets[i]);
2479 void
2480 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
2482 NS_PRECONDITION(aStyleSet, "Must have a style set");
2483 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
2484 "Style set already has document sheets?");
2486 // We could consider moving this to nsStyleSet::Init, to match its
2487 // handling of the eAnimationSheet and eTransitionSheet levels.
2488 aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet);
2489 aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet);
2491 int32_t i;
2492 for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
2493 nsIStyleSheet* sheet = mStyleSheets[i];
2494 if (sheet->IsApplicable()) {
2495 aStyleSet->AddDocStyleSheet(sheet, this);
2499 nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
2500 if (sheetService) {
2501 sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet,
2502 aStyleSet);
2505 // Iterate backwards to maintain order
2506 for (i = mOnDemandBuiltInUASheets.Count() - 1; i >= 0; --i) {
2507 nsIStyleSheet* sheet = mOnDemandBuiltInUASheets[i];
2508 if (sheet->IsApplicable()) {
2509 aStyleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
2513 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2514 nsStyleSet::eAgentSheet);
2515 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2516 nsStyleSet::eUserSheet);
2517 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2518 nsStyleSet::eDocSheet);
2521 nsresult
2522 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2523 nsILoadGroup* aLoadGroup,
2524 nsISupports* aContainer,
2525 nsIStreamListener **aDocListener,
2526 bool aReset, nsIContentSink* aSink)
2528 #ifdef PR_LOGGING
2529 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2530 nsCOMPtr<nsIURI> uri;
2531 aChannel->GetURI(getter_AddRefs(uri));
2532 nsAutoCString spec;
2533 if (uri)
2534 uri->GetSpec(spec);
2535 PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get());
2537 #endif
2539 #ifdef DEBUG
2541 uint32_t appId;
2542 nsresult rv = NodePrincipal()->GetAppId(&appId);
2543 NS_ENSURE_SUCCESS(rv, rv);
2544 MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2545 "Document should never have UNKNOWN_APP_ID");
2547 #endif
2549 MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2550 "Bad readyState");
2551 SetReadyStateInternal(READYSTATE_LOADING);
2553 if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2554 mLoadedAsData = true;
2555 // We need to disable script & style loading in this case.
2556 // We leave them disabled even in EndLoad(), and let anyone
2557 // who puts the document on display to worry about enabling.
2559 // Do not load/process scripts when loading as data
2560 ScriptLoader()->SetEnabled(false);
2562 // styles
2563 CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
2564 } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2565 // Allow CSS, but not scripts
2566 ScriptLoader()->SetEnabled(false);
2569 mMayStartLayout = false;
2571 mHaveInputEncoding = true;
2573 if (aReset) {
2574 Reset(aChannel, aLoadGroup);
2577 nsAutoCString contentType;
2578 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2579 if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2580 NS_LITERAL_STRING("contentType"), contentType))) ||
2581 NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2582 // XXX this is only necessary for viewsource:
2583 nsACString::const_iterator start, end, semicolon;
2584 contentType.BeginReading(start);
2585 contentType.EndReading(end);
2586 semicolon = start;
2587 FindCharInReadable(';', semicolon, end);
2588 SetContentTypeInternal(Substring(start, semicolon));
2591 RetrieveRelevantHeaders(aChannel);
2593 mChannel = aChannel;
2594 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2595 if (inStrmChan) {
2596 bool isSrcdocChannel;
2597 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2598 if (isSrcdocChannel) {
2599 mIsSrcdocDocument = true;
2603 // If this document is being loaded by a docshell, copy its sandbox flags
2604 // to the document. These are immutable after being set here.
2605 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2607 if (docShell) {
2608 nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2609 NS_ENSURE_SUCCESS(rv, rv);
2612 // If this is not a data document, set CSP.
2613 if (!mLoadedAsData) {
2614 nsresult rv = InitCSP(aChannel);
2615 NS_ENSURE_SUCCESS(rv, rv);
2618 return NS_OK;
2621 void
2622 CSPErrorQueue::Add(const char* aMessageName)
2624 mErrors.AppendElement(aMessageName);
2627 void
2628 CSPErrorQueue::Flush(nsIDocument* aDocument)
2630 for (uint32_t i = 0; i < mErrors.Length(); i++) {
2631 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2632 NS_LITERAL_CSTRING("CSP"), aDocument,
2633 nsContentUtils::eSECURITY_PROPERTIES,
2634 mErrors[i]);
2636 mErrors.Clear();
2639 void
2640 nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
2642 for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2643 nsAutoString messageTag;
2644 aMessages[i]->GetTag(messageTag);
2646 nsAutoString category;
2647 aMessages[i]->GetCategory(category);
2649 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2650 NS_ConvertUTF16toUTF8(category),
2651 this, nsContentUtils::eSECURITY_PROPERTIES,
2652 NS_ConvertUTF16toUTF8(messageTag).get());
2656 static nsresult
2657 AppendCSPFromHeader(nsIContentSecurityPolicy* csp,
2658 const nsAString& aHeaderValue,
2659 bool aReportOnly)
2661 // Need to tokenize the header value since multiple headers could be
2662 // concatenated into one comma-separated list of policies.
2663 // See RFC2616 section 4.2 (last paragraph)
2664 nsresult rv = NS_OK;
2665 nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
2666 while (tokenizer.hasMoreTokens()) {
2667 const nsSubstring& policy = tokenizer.nextToken();
2668 rv = csp->AppendPolicy(policy, aReportOnly);
2669 NS_ENSURE_SUCCESS(rv, rv);
2670 #ifdef PR_LOGGING
2672 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2673 ("CSP refined with policy: \"%s\"",
2674 NS_ConvertUTF16toUTF8(policy).get()));
2676 #endif
2678 return NS_OK;
2681 bool
2682 nsDocument::IsLoopDocument(nsIChannel *aChannel)
2684 nsCOMPtr<nsIURI> chanURI;
2685 nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(chanURI));
2686 NS_ENSURE_SUCCESS(rv, false);
2688 bool isAbout = false;
2689 bool isLoop = false;
2690 rv = chanURI->SchemeIs("about", &isAbout);
2691 NS_ENSURE_SUCCESS(rv, false);
2692 if (isAbout) {
2693 nsCOMPtr<nsIURI> loopURI;
2694 rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
2695 NS_ENSURE_SUCCESS(rv, false);
2696 rv = chanURI->EqualsExceptRef(loopURI, &isLoop);
2697 NS_ENSURE_SUCCESS(rv, false);
2698 if (!isLoop) {
2699 rv = NS_NewURI(getter_AddRefs(loopURI), "about:looppanel");
2700 NS_ENSURE_SUCCESS(rv, false);
2701 rv = chanURI->EqualsExceptRef(loopURI, &isLoop);
2702 NS_ENSURE_SUCCESS(rv, false);
2705 return isLoop;
2708 nsresult
2709 nsDocument::InitCSP(nsIChannel* aChannel)
2711 nsCOMPtr<nsIContentSecurityPolicy> csp;
2712 if (!CSPService::sCSPEnabled) {
2713 #ifdef PR_LOGGING
2714 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2715 ("CSP is disabled, skipping CSP init for document %p", this));
2716 #endif
2717 return NS_OK;
2720 nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2722 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
2723 if (httpChannel) {
2724 httpChannel->GetResponseHeader(
2725 NS_LITERAL_CSTRING("content-security-policy"),
2726 tCspHeaderValue);
2728 httpChannel->GetResponseHeader(
2729 NS_LITERAL_CSTRING("content-security-policy-report-only"),
2730 tCspROHeaderValue);
2732 NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2733 NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2735 // Figure out if we need to apply an app default CSP or a CSP from an app manifest
2736 nsIPrincipal* principal = NodePrincipal();
2738 uint16_t appStatus = principal->GetAppStatus();
2739 bool applyAppDefaultCSP = false;
2740 bool applyAppManifestCSP = false;
2742 nsAutoString appManifestCSP;
2743 nsAutoString appDefaultCSP;
2744 if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
2745 nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
2746 if (appsService) {
2747 uint32_t appId = 0;
2748 if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
2749 appsService->GetManifestCSPByLocalId(appId, appManifestCSP);
2750 if (!appManifestCSP.IsEmpty()) {
2751 applyAppManifestCSP = true;
2753 appsService->GetDefaultCSPByLocalId(appId, appDefaultCSP);
2754 if (!appDefaultCSP.IsEmpty()) {
2755 applyAppDefaultCSP = true;
2761 // Check if this is part of the Loop/Hello service
2762 bool applyLoopCSP = IsLoopDocument(aChannel);
2764 // If there's no CSP to apply, go ahead and return early
2765 if (!applyAppDefaultCSP &&
2766 !applyAppManifestCSP &&
2767 !applyLoopCSP &&
2768 cspHeaderValue.IsEmpty() &&
2769 cspROHeaderValue.IsEmpty()) {
2770 #ifdef PR_LOGGING
2771 nsCOMPtr<nsIURI> chanURI;
2772 aChannel->GetURI(getter_AddRefs(chanURI));
2773 nsAutoCString aspec;
2774 chanURI->GetAsciiSpec(aspec);
2775 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2776 ("no CSP for document, %s, %s",
2777 aspec.get(),
2778 applyAppDefaultCSP ? "is app" : "not an app"));
2779 #endif
2780 return NS_OK;
2783 #ifdef PR_LOGGING
2784 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
2785 #endif
2787 nsresult rv;
2789 // If Document is an app check to see if we already set CSP and return early
2790 // if that is indeed the case.
2792 // In general (see bug 947831), we should not be setting CSP on a principal
2793 // that aliases another document. For non-app code this is not a problem
2794 // since we only share the underlying principal with nested browsing
2795 // contexts for which a header cannot be set (e.g., about:blank and
2796 // about:srcodoc iframes) and thus won't try to set the CSP again. This
2797 // check ensures that we do not try to set CSP for an app.
2798 if (applyAppDefaultCSP || applyAppManifestCSP) {
2799 nsCOMPtr<nsIContentSecurityPolicy> csp;
2800 rv = principal->GetCsp(getter_AddRefs(csp));
2801 NS_ENSURE_SUCCESS(rv, rv);
2803 if (csp) {
2804 #ifdef PR_LOGGING
2805 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s",
2806 "This document is sharing principal with another document.",
2807 "Since the document is an app, CSP was already set.",
2808 "Skipping attempt to set CSP."));
2809 #endif
2810 return NS_OK;
2814 csp = do_CreateInstance("@mozilla.org/cspcontext;1", &rv);
2816 if (NS_FAILED(rv)) {
2817 #ifdef PR_LOGGING
2818 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
2819 #endif
2820 return rv;
2823 // used as a "self" identifier for the CSP.
2824 nsCOMPtr<nsIURI> selfURI;
2825 aChannel->GetURI(getter_AddRefs(selfURI));
2827 // Store the request context for violation reports
2828 csp->SetRequestContext(nullptr, nullptr, aChannel);
2830 // ----- if the doc is an app and we want a default CSP, apply it.
2831 if (applyAppDefaultCSP) {
2832 csp->AppendPolicy(appDefaultCSP, false);
2835 // ----- if the doc is an app and specifies a CSP in its manifest, apply it.
2836 if (applyAppManifestCSP) {
2837 csp->AppendPolicy(appManifestCSP, false);
2840 // ----- if the doc is part of Loop, apply the loop CSP
2841 if (applyLoopCSP) {
2842 nsAdoptingString loopCSP;
2843 loopCSP = Preferences::GetString("loop.CSP");
2844 NS_ASSERTION(loopCSP, "Missing loop.CSP preference");
2845 // If the pref has been removed, we continue without setting a CSP
2846 if (loopCSP) {
2847 csp->AppendPolicy(loopCSP, false);
2851 // ----- if there's a full-strength CSP header, apply it.
2852 if (!cspHeaderValue.IsEmpty()) {
2853 rv = AppendCSPFromHeader(csp, cspHeaderValue, false);
2854 NS_ENSURE_SUCCESS(rv, rv);
2857 // ----- if there's a report-only CSP header, apply it.
2858 if (!cspROHeaderValue.IsEmpty()) {
2859 rv = AppendCSPFromHeader(csp, cspROHeaderValue, true);
2860 NS_ENSURE_SUCCESS(rv, rv);
2863 // ----- Enforce frame-ancestor policy on any applied policies
2864 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2865 if (docShell) {
2866 bool safeAncestry = false;
2868 // PermitsAncestry sends violation reports when necessary
2869 rv = csp->PermitsAncestry(docShell, &safeAncestry);
2871 if (NS_FAILED(rv) || !safeAncestry) {
2872 #ifdef PR_LOGGING
2873 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2874 ("CSP doesn't like frame's ancestry, not loading."));
2875 #endif
2876 // stop! ERROR page!
2877 aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2881 rv = principal->SetCsp(csp);
2882 NS_ENSURE_SUCCESS(rv, rv);
2883 #ifdef PR_LOGGING
2884 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2885 ("Inserted CSP into principal %p", principal));
2886 #endif
2888 return NS_OK;
2891 void
2892 nsDocument::StopDocumentLoad()
2894 if (mParser) {
2895 mParserAborted = true;
2896 mParser->Terminate();
2900 void
2901 nsDocument::SetDocumentURI(nsIURI* aURI)
2903 nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
2904 mDocumentURI = NS_TryToMakeImmutable(aURI);
2905 nsIURI* newBase = GetDocBaseURI();
2907 bool equalBases = false;
2908 // Changing just the ref of a URI does not change how relative URIs would
2909 // resolve wrt to it, so we can treat the bases as equal as long as they're
2910 // equal ignoring the ref.
2911 if (oldBase && newBase) {
2912 oldBase->EqualsExceptRef(newBase, &equalBases);
2914 else {
2915 equalBases = !oldBase && !newBase;
2918 // If this is the first time we're setting the document's URI, set the
2919 // document's original URI.
2920 if (!mOriginalURI)
2921 mOriginalURI = mDocumentURI;
2923 // If changing the document's URI changed the base URI of the document, we
2924 // need to refresh the hrefs of all the links on the page.
2925 if (!equalBases) {
2926 RefreshLinkHrefs();
2930 void
2931 nsDocument::SetChromeXHRDocURI(nsIURI* aURI)
2933 mChromeXHRDocURI = aURI;
2936 void
2937 nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI)
2939 mChromeXHRDocBaseURI = aURI;
2942 NS_IMETHODIMP
2943 nsDocument::GetLastModified(nsAString& aLastModified)
2945 nsIDocument::GetLastModified(aLastModified);
2946 return NS_OK;
2949 void
2950 nsIDocument::GetLastModified(nsAString& aLastModified) const
2952 if (!mLastModified.IsEmpty()) {
2953 aLastModified.Assign(mLastModified);
2954 } else {
2955 // If we for whatever reason failed to find the last modified time
2956 // (or even the current time), fall back to what NS4.x returned.
2957 aLastModified.AssignLiteral(MOZ_UTF16("01/01/1970 00:00:00"));
2961 void
2962 nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
2964 MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
2965 "Only put elements that need to be exposed as document['name'] in "
2966 "the named table.");
2968 nsIdentifierMapEntry *entry =
2969 mIdentifierMap.PutEntry(nsDependentAtomString(aName));
2971 // Null for out-of-memory
2972 if (entry) {
2973 if (!entry->HasNameElement() &&
2974 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2975 ++mExpandoAndGeneration.generation;
2977 entry->AddNameElement(this, aElement);
2981 void
2982 nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
2984 // Speed up document teardown
2985 if (mIdentifierMap.Count() == 0)
2986 return;
2988 nsIdentifierMapEntry *entry =
2989 mIdentifierMap.GetEntry(nsDependentAtomString(aName));
2990 if (!entry) // Could be false if the element was anonymous, hence never added
2991 return;
2993 entry->RemoveNameElement(aElement);
2994 if (!entry->HasNameElement() &&
2995 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2996 ++mExpandoAndGeneration.generation;
3000 void
3001 nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
3003 nsIdentifierMapEntry *entry =
3004 mIdentifierMap.PutEntry(nsDependentAtomString(aId));
3006 if (entry) { /* True except on OOM */
3007 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3008 !entry->HasNameElement() &&
3009 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3010 ++mExpandoAndGeneration.generation;
3012 entry->AddIdElement(aElement);
3016 void
3017 nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
3019 NS_ASSERTION(aId, "huhwhatnow?");
3021 // Speed up document teardown
3022 if (mIdentifierMap.Count() == 0) {
3023 return;
3026 nsIdentifierMapEntry *entry =
3027 mIdentifierMap.GetEntry(nsDependentAtomString(aId));
3028 if (!entry) // Can be null for XML elements with changing ids.
3029 return;
3031 entry->RemoveIdElement(aElement);
3032 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3033 !entry->HasNameElement() &&
3034 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3035 ++mExpandoAndGeneration.generation;
3037 if (entry->IsEmpty()) {
3038 mIdentifierMap.RawRemoveEntry(entry);
3042 nsIPrincipal*
3043 nsDocument::GetPrincipal()
3045 return NodePrincipal();
3048 extern bool sDisablePrefetchHTTPSPref;
3050 void
3051 nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
3053 if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3054 nsCOMPtr<nsIURI> uri;
3055 aNewPrincipal->GetURI(getter_AddRefs(uri));
3056 bool isHTTPS;
3057 if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
3058 isHTTPS) {
3059 mAllowDNSPrefetch = false;
3062 mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3065 NS_IMETHODIMP
3066 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
3068 NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3070 return NS_OK;
3073 NS_IMETHODIMP
3074 nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
3076 mApplicationCache = aApplicationCache;
3078 return NS_OK;
3081 NS_IMETHODIMP
3082 nsDocument::GetContentType(nsAString& aContentType)
3084 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3086 return NS_OK;
3089 void
3090 nsDocument::SetContentType(const nsAString& aContentType)
3092 NS_ASSERTION(GetContentTypeInternal().IsEmpty() ||
3093 GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)),
3094 "Do you really want to change the content-type?");
3096 SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3099 nsresult
3100 nsDocument::GetAllowPlugins(bool * aAllowPlugins)
3102 // First, we ask our docshell if it allows plugins.
3103 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3105 if (docShell) {
3106 docShell->GetAllowPlugins(aAllowPlugins);
3108 // If the docshell allows plugins, we check whether
3109 // we are sandboxed and plugins should not be allowed.
3110 if (*aAllowPlugins)
3111 *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS);
3114 return NS_OK;
3117 already_AddRefed<UndoManager>
3118 nsDocument::GetUndoManager()
3120 Element* rootElement = GetRootElement();
3121 if (!rootElement) {
3122 return nullptr;
3125 if (!mUndoManager) {
3126 mUndoManager = new UndoManager(rootElement);
3129 nsRefPtr<UndoManager> undoManager = mUndoManager;
3130 return undoManager.forget();
3133 AnimationTimeline*
3134 nsDocument::Timeline()
3136 if (!mAnimationTimeline) {
3137 mAnimationTimeline = new AnimationTimeline(this);
3140 return mAnimationTimeline;
3143 /* Return true if the document is in the focused top-level window, and is an
3144 * ancestor of the focused DOMWindow. */
3145 NS_IMETHODIMP
3146 nsDocument::HasFocus(bool* aResult)
3148 ErrorResult rv;
3149 *aResult = nsIDocument::HasFocus(rv);
3150 return rv.ErrorCode();
3153 bool
3154 nsIDocument::HasFocus(ErrorResult& rv) const
3156 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3157 if (!fm) {
3158 rv.Throw(NS_ERROR_NOT_AVAILABLE);
3159 return false;
3162 // Is there a focused DOMWindow?
3163 nsCOMPtr<nsIDOMWindow> focusedWindow;
3164 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3165 if (!focusedWindow) {
3166 return false;
3169 // Are we an ancestor of the focused DOMWindow?
3170 nsCOMPtr<nsIDOMDocument> domDocument;
3171 focusedWindow->GetDocument(getter_AddRefs(domDocument));
3172 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
3174 for (nsIDocument* currentDoc = document; currentDoc;
3175 currentDoc = currentDoc->GetParentDocument()) {
3176 if (currentDoc == this) {
3177 // Yes, we are an ancestor
3178 return true;
3182 return false;
3185 NS_IMETHODIMP
3186 nsDocument::GetReferrer(nsAString& aReferrer)
3188 nsIDocument::GetReferrer(aReferrer);
3189 return NS_OK;
3192 void
3193 nsIDocument::GetReferrer(nsAString& aReferrer) const
3195 if (mIsSrcdocDocument && mParentDocument)
3196 mParentDocument->GetReferrer(aReferrer);
3197 else
3198 CopyUTF8toUTF16(mReferrer, aReferrer);
3201 nsresult
3202 nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
3204 if (mIsSrcdocDocument) {
3205 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3206 if (inStrmChan) {
3207 return inStrmChan->GetSrcdocData(aSrcdocData);
3210 aSrcdocData = NullString();
3211 return NS_OK;
3214 NS_IMETHODIMP
3215 nsDocument::GetActiveElement(nsIDOMElement **aElement)
3217 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement()));
3218 el.forget(aElement);
3219 return NS_OK;
3222 Element*
3223 nsIDocument::GetActiveElement()
3225 // Get the focused element.
3226 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
3227 if (window) {
3228 nsCOMPtr<nsPIDOMWindow> focusedWindow;
3229 nsIContent* focusedContent =
3230 nsFocusManager::GetFocusedDescendant(window, false,
3231 getter_AddRefs(focusedWindow));
3232 // be safe and make sure the element is from this document
3233 if (focusedContent && focusedContent->OwnerDoc() == this) {
3234 if (focusedContent->ChromeOnlyAccess()) {
3235 focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
3237 if (focusedContent) {
3238 return focusedContent->AsElement();
3243 // No focused element anywhere in this document. Try to get the BODY.
3244 nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
3245 if (htmlDoc) {
3246 // Because of IE compatibility, return null when html document doesn't have
3247 // a body.
3248 return htmlDoc->GetBody();
3251 // If we couldn't get a BODY, return the root element.
3252 return GetDocumentElement();
3255 NS_IMETHODIMP
3256 nsDocument::GetCurrentScript(nsIDOMElement **aElement)
3258 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript()));
3259 el.forget(aElement);
3260 return NS_OK;
3263 Element*
3264 nsIDocument::GetCurrentScript()
3266 nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3267 return el;
3270 NS_IMETHODIMP
3271 nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
3273 Element* el = nsIDocument::ElementFromPoint(aX, aY);
3274 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
3275 retval.forget(aReturn);
3276 return NS_OK;
3279 Element*
3280 nsIDocument::ElementFromPoint(float aX, float aY)
3282 return ElementFromPointHelper(aX, aY, false, true);
3285 Element*
3286 nsDocument::ElementFromPointHelper(float aX, float aY,
3287 bool aIgnoreRootScrollFrame,
3288 bool aFlushLayout)
3290 // As per the the spec, we return null if either coord is negative
3291 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
3292 return nullptr;
3295 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
3296 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
3297 nsPoint pt(x, y);
3299 // Make sure the layout information we get is up-to-date, and
3300 // ensure we get a root frame (for everything but XUL)
3301 if (aFlushLayout)
3302 FlushPendingNotifications(Flush_Layout);
3304 nsIPresShell *ps = GetShell();
3305 if (!ps) {
3306 return nullptr;
3308 nsIFrame *rootFrame = ps->GetRootFrame();
3310 // XUL docs, unlike HTML, have no frame tree until everything's done loading
3311 if (!rootFrame) {
3312 return nullptr; // return null to premature XUL callers as a reminder to wait
3315 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
3316 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3317 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3318 if (!ptFrame) {
3319 return nullptr;
3322 nsIContent* elem = GetContentInThisDocument(ptFrame);
3323 if (elem && !elem->IsElement()) {
3324 elem = elem->GetParent();
3326 return elem ? elem->AsElement() : nullptr;
3329 nsresult
3330 nsDocument::NodesFromRectHelper(float aX, float aY,
3331 float aTopSize, float aRightSize,
3332 float aBottomSize, float aLeftSize,
3333 bool aIgnoreRootScrollFrame,
3334 bool aFlushLayout,
3335 nsIDOMNodeList** aReturn)
3337 NS_ENSURE_ARG_POINTER(aReturn);
3339 nsSimpleContentList* elements = new nsSimpleContentList(this);
3340 NS_ADDREF(elements);
3341 *aReturn = elements;
3343 // Following the same behavior of elementFromPoint,
3344 // we don't return anything if either coord is negative
3345 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
3346 return NS_OK;
3348 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3349 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3350 nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3351 nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3353 nsRect rect(x, y, w, h);
3355 // Make sure the layout information we get is up-to-date, and
3356 // ensure we get a root frame (for everything but XUL)
3357 if (aFlushLayout) {
3358 FlushPendingNotifications(Flush_Layout);
3361 nsIPresShell *ps = GetShell();
3362 NS_ENSURE_STATE(ps);
3363 nsIFrame *rootFrame = ps->GetRootFrame();
3365 // XUL docs, unlike HTML, have no frame tree until everything's done loading
3366 if (!rootFrame)
3367 return NS_OK; // return nothing to premature XUL callers as a reminder to wait
3369 nsAutoTArray<nsIFrame*,8> outFrames;
3370 nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
3371 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3372 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3374 // Used to filter out repeated elements in sequence.
3375 nsIContent* lastAdded = nullptr;
3377 for (uint32_t i = 0; i < outFrames.Length(); i++) {
3378 nsIContent* node = GetContentInThisDocument(outFrames[i]);
3380 if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
3381 // We have a node that isn't an element or a text node,
3382 // use its parent content instead.
3383 node = node->GetParent();
3385 if (node && node != lastAdded) {
3386 elements->AppendElement(node);
3387 lastAdded = node;
3391 return NS_OK;
3394 NS_IMETHODIMP
3395 nsDocument::GetElementsByClassName(const nsAString& aClasses,
3396 nsIDOMNodeList** aReturn)
3398 *aReturn = nsIDocument::GetElementsByClassName(aClasses).take();
3399 return NS_OK;
3402 already_AddRefed<nsContentList>
3403 nsIDocument::GetElementsByClassName(const nsAString& aClasses)
3405 return nsContentUtils::GetElementsByClassName(this, aClasses);
3408 NS_IMETHODIMP
3409 nsDocument::ReleaseCapture()
3411 nsIDocument::ReleaseCapture();
3412 return NS_OK;
3415 void
3416 nsIDocument::ReleaseCapture() const
3418 // only release the capture if the caller can access it. This prevents a
3419 // page from stopping a scrollbar grab for example.
3420 nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3421 if (node && nsContentUtils::CanCallerAccess(node)) {
3422 nsIPresShell::SetCapturingContent(nullptr, 0);
3426 already_AddRefed<nsIURI>
3427 nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
3429 nsCOMPtr<nsIURI> uri;
3430 if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3431 uri = mChromeXHRDocBaseURI;
3432 } else {
3433 uri = GetDocBaseURI();
3436 return uri.forget();
3439 nsresult
3440 nsDocument::SetBaseURI(nsIURI* aURI)
3442 if (!aURI && !mDocumentBaseURI) {
3443 return NS_OK;
3446 // Don't do anything if the URI wasn't actually changed.
3447 if (aURI && mDocumentBaseURI) {
3448 bool equalBases = false;
3449 mDocumentBaseURI->Equals(aURI, &equalBases);
3450 if (equalBases) {
3451 return NS_OK;
3455 if (aURI) {
3456 mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
3457 } else {
3458 mDocumentBaseURI = nullptr;
3460 RefreshLinkHrefs();
3462 return NS_OK;
3465 void
3466 nsDocument::GetBaseTarget(nsAString &aBaseTarget)
3468 aBaseTarget = mBaseTarget;
3471 void
3472 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
3474 // XXX it would be a good idea to assert the sanity of the argument,
3475 // but before we figure out what to do about non-Encoding Standard
3476 // encodings in the charset menu and in mailnews, assertions are futile.
3477 if (!mCharacterSet.Equals(aCharSetID)) {
3478 mCharacterSet = aCharSetID;
3480 int32_t n = mCharSetObservers.Length();
3482 for (int32_t i = 0; i < n; i++) {
3483 nsIObserver* observer = mCharSetObservers.ElementAt(i);
3485 observer->Observe(static_cast<nsIDocument *>(this), "charset",
3486 NS_ConvertASCIItoUTF16(aCharSetID).get());
3491 nsresult
3492 nsDocument::AddCharSetObserver(nsIObserver* aObserver)
3494 NS_ENSURE_ARG_POINTER(aObserver);
3496 NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE);
3498 return NS_OK;
3501 void
3502 nsDocument::RemoveCharSetObserver(nsIObserver* aObserver)
3504 mCharSetObservers.RemoveElement(aObserver);
3507 void
3508 nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const
3510 aData.Truncate();
3511 const nsDocHeaderData* data = mHeaderData;
3512 while (data) {
3513 if (data->mField == aHeaderField) {
3514 aData = data->mData;
3516 break;
3518 data = data->mNext;
3522 void
3523 nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
3525 if (!aHeaderField) {
3526 NS_ERROR("null headerField");
3527 return;
3530 if (!mHeaderData) {
3531 if (!aData.IsEmpty()) { // don't bother storing empty string
3532 mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3535 else {
3536 nsDocHeaderData* data = mHeaderData;
3537 nsDocHeaderData** lastPtr = &mHeaderData;
3538 bool found = false;
3539 do { // look for existing and replace
3540 if (data->mField == aHeaderField) {
3541 if (!aData.IsEmpty()) {
3542 data->mData.Assign(aData);
3544 else { // don't store empty string
3545 *lastPtr = data->mNext;
3546 data->mNext = nullptr;
3547 delete data;
3549 found = true;
3551 break;
3553 lastPtr = &(data->mNext);
3554 data = *lastPtr;
3555 } while (data);
3557 if (!aData.IsEmpty() && !found) {
3558 // didn't find, append
3559 *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3563 if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3564 CopyUTF16toUTF8(aData, mContentLanguage);
3567 if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3568 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
3569 // spec.
3570 if (DOMStringIsNull(mLastStyleSheetSet)) {
3571 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
3572 // per spec. The idea here is that we're changing our preferred set and
3573 // that shouldn't change the value of lastStyleSheetSet. Also, we're
3574 // using the Internal version so we can update the CSSLoader and not have
3575 // to worry about null strings.
3576 EnableStyleSheetsForSetInternal(aData, true);
3580 if (aHeaderField == nsGkAtoms::refresh) {
3581 // We get into this code before we have a script global yet, so get to
3582 // our container via mDocumentContainer.
3583 nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3584 if (refresher) {
3585 // Note: using mDocumentURI instead of mBaseURI here, for consistency
3586 // (used to just use the current URI of our webnavigation, but that
3587 // should really be the same thing). Note that this code can run
3588 // before the current URI of the webnavigation has been updated, so we
3589 // can't assert equality here.
3590 refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3591 NS_ConvertUTF16toUTF8(aData));
3595 if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3596 mAllowDNSPrefetch) {
3597 // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3598 mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3601 if (aHeaderField == nsGkAtoms::viewport ||
3602 aHeaderField == nsGkAtoms::handheldFriendly ||
3603 aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3604 aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3605 aHeaderField == nsGkAtoms::viewport_initial_scale ||
3606 aHeaderField == nsGkAtoms::viewport_height ||
3607 aHeaderField == nsGkAtoms::viewport_width ||
3608 aHeaderField == nsGkAtoms::viewport_user_scalable) {
3609 mViewportType = Unknown;
3613 void
3614 nsDocument::TryChannelCharset(nsIChannel *aChannel,
3615 int32_t& aCharsetSource,
3616 nsACString& aCharset,
3617 nsHtml5TreeOpExecutor* aExecutor)
3619 if (aChannel) {
3620 nsAutoCString charsetVal;
3621 nsresult rv = aChannel->GetContentCharset(charsetVal);
3622 if (NS_SUCCEEDED(rv)) {
3623 nsAutoCString preferred;
3624 if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) {
3625 aCharset = preferred;
3626 aCharsetSource = kCharsetFromChannel;
3627 return;
3628 } else if (aExecutor && !charsetVal.IsEmpty()) {
3629 aExecutor->ComplainAboutBogusProtocolCharset(this);
3635 already_AddRefed<nsIPresShell>
3636 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
3637 nsStyleSet* aStyleSet)
3639 // Don't add anything here. Add it to |doCreateShell| instead.
3640 // This exists so that subclasses can pass other values for the 4th
3641 // parameter some of the time.
3642 return doCreateShell(aContext, aViewManager, aStyleSet,
3643 eCompatibility_FullStandards);
3646 already_AddRefed<nsIPresShell>
3647 nsDocument::doCreateShell(nsPresContext* aContext,
3648 nsViewManager* aViewManager, nsStyleSet* aStyleSet,
3649 nsCompatibility aCompatMode)
3651 NS_ASSERTION(!mPresShell, "We have a presshell already!");
3653 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3655 FillStyleSet(aStyleSet);
3657 nsRefPtr<PresShell> shell = new PresShell;
3658 shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
3660 // Note: we don't hold a ref to the shell (it holds a ref to us)
3661 mPresShell = shell;
3663 // Make sure to never paint if we belong to an invisible DocShell.
3664 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3665 if (docShell && docShell->IsInvisible())
3666 shell->SetNeverPainting(true);
3668 mExternalResourceMap.ShowViewers();
3670 MaybeRescheduleAnimationFrameNotifications();
3672 return shell.forget();
3675 void
3676 nsDocument::MaybeRescheduleAnimationFrameNotifications()
3678 if (!mPresShell || !IsEventHandlingEnabled()) {
3679 // bail out for now, until one of those conditions changes
3680 return;
3683 nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver();
3684 if (!mFrameRequestCallbacks.IsEmpty()) {
3685 rd->ScheduleFrameRequestCallbacks(this);
3689 void
3690 nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
3692 aCallbacks.AppendElements(mFrameRequestCallbacks);
3693 mFrameRequestCallbacks.Clear();
3696 PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey,
3697 uint32_t aData,
3698 void* userArg)
3700 aKey->RequestDiscard();
3701 return PL_DHASH_NEXT;
3704 void
3705 nsDocument::DeleteShell()
3707 mExternalResourceMap.HideViewers();
3708 if (IsEventHandlingEnabled()) {
3709 RevokeAnimationFrameNotifications();
3712 // When our shell goes away, request that all our images be immediately
3713 // discarded, so we don't carry around decoded image data for a document we
3714 // no longer intend to paint.
3715 mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr);
3717 mPresShell = nullptr;
3720 void
3721 nsDocument::RevokeAnimationFrameNotifications()
3723 if (!mFrameRequestCallbacks.IsEmpty()) {
3724 mPresShell->GetPresContext()->RefreshDriver()->
3725 RevokeFrameRequestCallbacks(this);
3729 static void
3730 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
3732 SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
3734 NS_RELEASE(e->mKey);
3735 if (e->mSubDocument) {
3736 e->mSubDocument->SetParentDocument(nullptr);
3737 NS_RELEASE(e->mSubDocument);
3741 static bool
3742 SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key)
3744 SubDocMapEntry *e =
3745 const_cast<SubDocMapEntry *>
3746 (static_cast<const SubDocMapEntry *>(entry));
3748 e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
3749 NS_ADDREF(e->mKey);
3751 e->mSubDocument = nullptr;
3752 return true;
3755 nsresult
3756 nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
3758 NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
3760 if (!aSubDoc) {
3761 // aSubDoc is nullptr, remove the mapping
3763 if (mSubDocuments) {
3764 SubDocMapEntry *entry =
3765 static_cast<SubDocMapEntry*>
3766 (PL_DHashTableOperate(mSubDocuments, aElement,
3767 PL_DHASH_LOOKUP));
3769 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3770 PL_DHashTableRawRemove(mSubDocuments, entry);
3773 } else {
3774 if (!mSubDocuments) {
3775 // Create a new hashtable
3777 static const PLDHashTableOps hash_table_ops =
3779 PL_DHashAllocTable,
3780 PL_DHashFreeTable,
3781 PL_DHashVoidPtrKeyStub,
3782 PL_DHashMatchEntryStub,
3783 PL_DHashMoveEntryStub,
3784 SubDocClearEntry,
3785 PL_DHashFinalizeStub,
3786 SubDocInitEntry
3789 mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr,
3790 sizeof(SubDocMapEntry));
3791 if (!mSubDocuments) {
3792 return NS_ERROR_OUT_OF_MEMORY;
3796 // Add a mapping to the hash table
3797 SubDocMapEntry *entry =
3798 static_cast<SubDocMapEntry*>
3799 (PL_DHashTableOperate(mSubDocuments, aElement,
3800 PL_DHASH_ADD));
3802 if (!entry) {
3803 return NS_ERROR_OUT_OF_MEMORY;
3806 if (entry->mSubDocument) {
3807 entry->mSubDocument->SetParentDocument(nullptr);
3809 // Release the old sub document
3810 NS_RELEASE(entry->mSubDocument);
3813 entry->mSubDocument = aSubDoc;
3814 NS_ADDREF(entry->mSubDocument);
3816 aSubDoc->SetParentDocument(this);
3819 return NS_OK;
3822 nsIDocument*
3823 nsDocument::GetSubDocumentFor(nsIContent *aContent) const
3825 if (mSubDocuments && aContent->IsElement()) {
3826 SubDocMapEntry *entry =
3827 static_cast<SubDocMapEntry*>
3828 (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(),
3829 PL_DHASH_LOOKUP));
3831 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3832 return entry->mSubDocument;
3836 return nullptr;
3839 static PLDHashOperator
3840 FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
3841 uint32_t number, void *arg)
3843 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
3844 FindContentData *data = static_cast<FindContentData*>(arg);
3846 if (entry->mSubDocument == data->mSubDocument) {
3847 data->mResult = entry->mKey;
3849 return PL_DHASH_STOP;
3852 return PL_DHASH_NEXT;
3855 Element*
3856 nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
3858 NS_ENSURE_TRUE(aDocument, nullptr);
3860 if (!mSubDocuments) {
3861 return nullptr;
3864 FindContentData data(aDocument);
3865 PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data);
3867 return data.mResult;
3870 bool
3871 nsDocument::IsNodeOfType(uint32_t aFlags) const
3873 return !(aFlags & ~eDOCUMENT);
3876 Element*
3877 nsIDocument::GetRootElement() const
3879 return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
3880 mCachedRootElement : GetRootElementInternal();
3883 Element*
3884 nsDocument::GetRootElementInternal() const
3886 // Loop backwards because any non-elements, such as doctypes and PIs
3887 // are likely to appear before the root element.
3888 uint32_t i;
3889 for (i = mChildren.ChildCount(); i > 0; --i) {
3890 nsIContent* child = mChildren.ChildAt(i - 1);
3891 if (child->IsElement()) {
3892 const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
3893 return child->AsElement();
3897 const_cast<nsDocument*>(this)->mCachedRootElement = nullptr;
3898 return nullptr;
3901 nsIContent *
3902 nsDocument::GetChildAt(uint32_t aIndex) const
3904 return mChildren.GetSafeChildAt(aIndex);
3907 int32_t
3908 nsDocument::IndexOf(const nsINode* aPossibleChild) const
3910 return mChildren.IndexOfChild(aPossibleChild);
3913 uint32_t
3914 nsDocument::GetChildCount() const
3916 return mChildren.ChildCount();
3919 nsIContent * const *
3920 nsDocument::GetChildArray(uint32_t* aChildCount) const
3922 return mChildren.GetChildArray(aChildCount);
3926 nsresult
3927 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
3928 bool aNotify)
3930 if (aKid->IsElement() && GetRootElement()) {
3931 NS_WARNING("Inserting root element when we already have one");
3932 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
3935 return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
3938 void
3939 nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
3941 nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
3942 if (!oldKid) {
3943 return;
3946 if (oldKid->IsElement()) {
3947 // Destroy the link map up front before we mess with the child list.
3948 DestroyElementMaps();
3951 doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
3952 mCachedRootElement = nullptr;
3955 void
3956 nsDocument::EnsureOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
3958 // Contains() takes nsISupport*, so annoyingly we have to cast here
3959 if (mOnDemandBuiltInUASheets.Contains(static_cast<nsIStyleSheet*>(aSheet))) {
3960 return;
3962 BeginUpdate(UPDATE_STYLE);
3963 AddOnDemandBuiltInUASheet(aSheet);
3964 EndUpdate(UPDATE_STYLE);
3967 void
3968 nsDocument::AddOnDemandBuiltInUASheet(CSSStyleSheet* aSheet)
3970 // Contains() takes nsISupport*, so annoyingly we have to cast here
3971 MOZ_ASSERT(!mOnDemandBuiltInUASheets.Contains(static_cast<nsIStyleSheet*>(aSheet)));
3973 // Prepend here so that we store the sheets in mOnDemandBuiltInUASheets in
3974 // the same order that they should end up in the style set.
3975 mOnDemandBuiltInUASheets.InsertElementAt(0, aSheet);
3977 if (aSheet->IsApplicable()) {
3978 // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
3979 nsCOMPtr<nsIPresShell> shell = GetShell();
3980 if (shell) {
3981 // Note that prepending here is necessary to make sure that html.css etc.
3982 // do not override Firefox OS/Mobile's content.css sheet. Maybe we should
3983 // have an insertion point to match the order of
3984 // nsDocumentViewer::CreateStyleSet though?
3985 shell->StyleSet()->PrependStyleSheet(nsStyleSet::eAgentSheet, aSheet);
3989 NotifyStyleSheetAdded(aSheet, false);
3992 int32_t
3993 nsDocument::GetNumberOfStyleSheets() const
3995 return mStyleSheets.Count();
3998 nsIStyleSheet*
3999 nsDocument::GetStyleSheetAt(int32_t aIndex) const
4001 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr);
4002 return mStyleSheets[aIndex];
4005 int32_t
4006 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
4008 return mStyleSheets.IndexOf(aSheet);
4011 void
4012 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
4014 nsCOMPtr<nsIPresShell> shell = GetShell();
4015 if (shell) {
4016 shell->StyleSet()->AddDocStyleSheet(aSheet, this);
4020 #define DO_STYLESHEET_NOTIFICATION(className, type, memberName, argName) \
4021 do { \
4022 nsRefPtr<CSSStyleSheet> cssSheet = do_QueryObject(aSheet); \
4023 if (!cssSheet) { \
4024 return; \
4027 className##Init init; \
4028 init.mBubbles = true; \
4029 init.mCancelable = true; \
4030 init.mStylesheet = cssSheet; \
4031 init.memberName = argName; \
4033 nsRefPtr<className> event = \
4034 className::Constructor(this, NS_LITERAL_STRING(type), init); \
4035 event->SetTrusted(true); \
4036 event->SetTarget(this); \
4037 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = \
4038 new AsyncEventDispatcher(this, event); \
4039 asyncDispatcher->mDispatchChromeOnly = true; \
4040 asyncDispatcher->PostDOMEvent(); \
4041 } while (0);
4043 void
4044 nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
4046 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
4048 if (StyleSheetChangeEventsEnabled()) {
4049 DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4050 "StyleSheetAdded",
4051 mDocumentSheet,
4052 aDocumentSheet);
4056 void
4057 nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet)
4059 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
4061 if (StyleSheetChangeEventsEnabled()) {
4062 DO_STYLESHEET_NOTIFICATION(StyleSheetChangeEvent,
4063 "StyleSheetRemoved",
4064 mDocumentSheet,
4065 aDocumentSheet);
4069 void
4070 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
4072 NS_PRECONDITION(aSheet, "null arg");
4073 mStyleSheets.AppendObject(aSheet);
4074 aSheet->SetOwningDocument(this);
4076 if (aSheet->IsApplicable()) {
4077 AddStyleSheetToStyleSets(aSheet);
4080 NotifyStyleSheetAdded(aSheet, true);
4083 void
4084 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
4086 nsCOMPtr<nsIPresShell> shell = GetShell();
4087 if (shell) {
4088 shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4092 void
4093 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
4095 NS_PRECONDITION(aSheet, "null arg");
4096 nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
4098 if (!mStyleSheets.RemoveObject(aSheet)) {
4099 NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4100 return;
4103 if (!mIsGoingAway) {
4104 if (aSheet->IsApplicable()) {
4105 RemoveStyleSheetFromStyleSets(aSheet);
4108 NotifyStyleSheetRemoved(aSheet, true);
4111 aSheet->SetOwningDocument(nullptr);
4114 void
4115 nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
4116 nsCOMArray<nsIStyleSheet>& aNewSheets)
4118 BeginUpdate(UPDATE_STYLE);
4120 // XXX Need to set the sheet on the ownernode, if any
4121 NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(),
4122 "The lists must be the same length!");
4123 int32_t count = aOldSheets.Count();
4125 nsCOMPtr<nsIStyleSheet> oldSheet;
4126 int32_t i;
4127 for (i = 0; i < count; ++i) {
4128 oldSheet = aOldSheets[i];
4130 // First remove the old sheet.
4131 NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4132 int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4133 RemoveStyleSheet(oldSheet); // This does the right notifications
4135 // Now put the new one in its place. If it's null, just ignore it.
4136 nsIStyleSheet* newSheet = aNewSheets[i];
4137 if (newSheet) {
4138 mStyleSheets.InsertObjectAt(newSheet, oldIndex);
4139 newSheet->SetOwningDocument(this);
4140 if (newSheet->IsApplicable()) {
4141 AddStyleSheetToStyleSets(newSheet);
4144 NotifyStyleSheetAdded(newSheet, true);
4148 EndUpdate(UPDATE_STYLE);
4151 void
4152 nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex)
4154 NS_PRECONDITION(aSheet, "null ptr");
4155 mStyleSheets.InsertObjectAt(aSheet, aIndex);
4157 aSheet->SetOwningDocument(this);
4159 if (aSheet->IsApplicable()) {
4160 AddStyleSheetToStyleSets(aSheet);
4163 NotifyStyleSheetAdded(aSheet, true);
4167 void
4168 nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
4169 bool aApplicable)
4171 NS_PRECONDITION(aSheet, "null arg");
4173 // If we're actually in the document style sheet list
4174 if (-1 != mStyleSheets.IndexOf(aSheet)) {
4175 if (aApplicable) {
4176 AddStyleSheetToStyleSets(aSheet);
4177 } else {
4178 RemoveStyleSheetFromStyleSets(aSheet);
4182 // We have to always notify, since this will be called for sheets
4183 // that are children of sheets in our style set, as well as some
4184 // sheets for nsHTMLEditor.
4186 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
4187 (this, aSheet, aApplicable));
4189 if (StyleSheetChangeEventsEnabled()) {
4190 DO_STYLESHEET_NOTIFICATION(StyleSheetApplicableStateChangeEvent,
4191 "StyleSheetApplicableStateChanged",
4192 mApplicable,
4193 aApplicable);
4196 if (!mSSApplicableStateNotificationPending) {
4197 nsRefPtr<nsIRunnable> notification = NS_NewRunnableMethod(this,
4198 &nsDocument::NotifyStyleSheetApplicableStateChanged);
4199 mSSApplicableStateNotificationPending =
4200 NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
4204 void
4205 nsDocument::NotifyStyleSheetApplicableStateChanged()
4207 mSSApplicableStateNotificationPending = false;
4208 nsCOMPtr<nsIObserverService> observerService =
4209 mozilla::services::GetObserverService();
4210 if (observerService) {
4211 observerService->NotifyObservers(static_cast<nsIDocument*>(this),
4212 "style-sheet-applicable-state-changed",
4213 nullptr);
4217 static nsStyleSet::sheetType
4218 ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
4220 switch(aType) {
4221 case nsIDocument::eAgentSheet:
4222 return nsStyleSet::eAgentSheet;
4223 case nsIDocument::eUserSheet:
4224 return nsStyleSet::eUserSheet;
4225 case nsIDocument::eAuthorSheet:
4226 return nsStyleSet::eDocSheet;
4227 default:
4228 NS_ASSERTION(false, "wrong type");
4229 // we must return something although this should never happen
4230 return nsStyleSet::eSheetTypeCount;
4234 static int32_t
4235 FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI)
4237 for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) {
4238 bool bEqual;
4239 nsIURI* uri = aSheets[i]->GetSheetURI();
4241 if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4242 return i;
4245 return -1;
4248 nsresult
4249 nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4251 NS_PRECONDITION(aSheetURI, "null arg");
4253 // Checking if we have loaded this one already.
4254 if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4255 return NS_ERROR_INVALID_ARG;
4257 // Loading the sheet sync.
4258 nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader();
4260 nsRefPtr<CSSStyleSheet> sheet;
4261 nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet,
4262 true, getter_AddRefs(sheet));
4263 NS_ENSURE_SUCCESS(rv, rv);
4265 sheet->SetOwningDocument(this);
4266 MOZ_ASSERT(sheet->IsApplicable());
4268 return AddAdditionalStyleSheet(aType, sheet);
4271 nsresult
4272 nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aSheet)
4274 if (mAdditionalSheets[aType].Contains(aSheet))
4275 return NS_ERROR_INVALID_ARG;
4277 if (!aSheet->IsApplicable())
4278 return NS_ERROR_INVALID_ARG;
4280 mAdditionalSheets[aType].AppendObject(aSheet);
4282 BeginUpdate(UPDATE_STYLE);
4283 nsCOMPtr<nsIPresShell> shell = GetShell();
4284 if (shell) {
4285 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
4286 shell->StyleSet()->AppendStyleSheet(type, aSheet);
4289 // Passing false, so documet.styleSheets.length will not be affected by
4290 // these additional sheets.
4291 NotifyStyleSheetAdded(aSheet, false);
4292 EndUpdate(UPDATE_STYLE);
4293 return NS_OK;
4296 void
4297 nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4299 MOZ_ASSERT(aSheetURI);
4301 nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType];
4303 int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4304 if (i >= 0) {
4305 nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i];
4306 sheets.RemoveObjectAt(i);
4308 BeginUpdate(UPDATE_STYLE);
4309 if (!mIsGoingAway) {
4310 MOZ_ASSERT(sheetRef->IsApplicable());
4311 nsCOMPtr<nsIPresShell> shell = GetShell();
4312 if (shell) {
4313 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
4314 shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4318 // Passing false, so documet.styleSheets.length will not be affected by
4319 // these additional sheets.
4320 NotifyStyleSheetRemoved(sheetRef, false);
4321 EndUpdate(UPDATE_STYLE);
4323 sheetRef->SetOwningDocument(nullptr);
4327 nsIStyleSheet*
4328 nsDocument::FirstAdditionalAuthorSheet()
4330 return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0);
4333 nsIGlobalObject*
4334 nsDocument::GetScopeObject() const
4336 nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4337 return scope;
4340 void
4341 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
4343 mScopeObject = do_GetWeakReference(aGlobal);
4344 if (aGlobal) {
4345 mHasHadScriptHandlingObject = true;
4349 #ifdef MOZ_EME
4350 static void
4351 CheckIfContainsEMEContent(nsISupports* aSupports, void* aContainsEME)
4353 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4354 if (domMediaElem) {
4355 nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4356 MOZ_ASSERT(content, "aSupports is not a content");
4357 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4358 bool* contains = static_cast<bool*>(aContainsEME);
4359 if (mediaElem->GetMediaKeys()) {
4360 *contains = true;
4365 bool
4366 nsDocument::ContainsEMEContent()
4368 bool containsEME = false;
4369 EnumerateActivityObservers(CheckIfContainsEMEContent,
4370 static_cast<void*>(&containsEME));
4371 return containsEME;
4373 #endif // MOZ_EME
4375 static void
4376 NotifyActivityChanged(nsISupports *aSupports, void *aUnused)
4378 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aSupports));
4379 if (domMediaElem) {
4380 nsCOMPtr<nsIContent> content(do_QueryInterface(domMediaElem));
4381 MOZ_ASSERT(content, "aSupports is not a content");
4382 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(content.get());
4383 mediaElem->NotifyOwnerDocumentActivityChanged();
4385 nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aSupports));
4386 if (objectLoadingContent) {
4387 nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4388 olc->NotifyOwnerDocumentActivityChanged();
4390 nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(do_QueryInterface(aSupports));
4391 if (objectDocumentActivity) {
4392 objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
4396 void
4397 nsIDocument::SetContainer(nsDocShell* aContainer)
4399 if (aContainer) {
4400 mDocumentContainer = aContainer;
4401 } else {
4402 mDocumentContainer = WeakPtr<nsDocShell>();
4405 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
4406 if (!aContainer) {
4407 return;
4410 // Get the Docshell
4411 if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4412 // check if same type root
4413 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4414 aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4415 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
4417 if (sameTypeRoot == aContainer) {
4418 static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4423 nsISupports*
4424 nsIDocument::GetContainer() const
4426 return static_cast<nsIDocShell*>(mDocumentContainer);
4429 void
4430 nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
4432 #ifdef DEBUG
4434 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject));
4436 NS_ASSERTION(!win || win->IsInnerWindow(),
4437 "Script global object must be an inner window!");
4439 #endif
4440 NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController ||
4441 mAnimationController->IsPausedByType(
4442 nsSMILTimeContainer::PAUSE_PAGEHIDE |
4443 nsSMILTimeContainer::PAUSE_BEGIN),
4444 "Clearing window pointer while animations are unpaused");
4446 if (mScriptGlobalObject && !aScriptGlobalObject) {
4447 // We're detaching from the window. We need to grab a pointer to
4448 // our layout history state now.
4449 mLayoutHistoryState = GetLayoutHistoryState();
4451 if (mPresShell && !EventHandlingSuppressed()) {
4452 RevokeAnimationFrameNotifications();
4455 // Also make sure to remove our onload blocker now if we haven't done it yet
4456 if (mOnloadBlockCount != 0) {
4457 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4458 if (loadGroup) {
4459 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4464 mScriptGlobalObject = aScriptGlobalObject;
4466 if (aScriptGlobalObject) {
4467 mHasHadScriptHandlingObject = true;
4468 mHasHadDefaultView = true;
4469 // Go back to using the docshell for the layout history state
4470 mLayoutHistoryState = nullptr;
4471 mScopeObject = do_GetWeakReference(aScriptGlobalObject);
4472 #ifdef DEBUG
4473 if (!mWillReparent) {
4474 // We really shouldn't have a wrapper here but if we do we need to make sure
4475 // it has the correct parent.
4476 JSObject *obj = GetWrapperPreserveColor();
4477 if (obj) {
4478 JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
4479 NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
4480 "Wrong scope, this is really bad!");
4483 #endif
4485 if (mAllowDNSPrefetch) {
4486 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4487 if (docShell) {
4488 #ifdef DEBUG
4489 nsCOMPtr<nsIWebNavigation> webNav =
4490 do_GetInterface(aScriptGlobalObject);
4491 NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4492 "Unexpected container or script global?");
4493 #endif
4494 bool allowDNSPrefetch;
4495 docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4496 mAllowDNSPrefetch = allowDNSPrefetch;
4500 MaybeRescheduleAnimationFrameNotifications();
4501 mRegistry = new Registry();
4504 // Remember the pointer to our window (or lack there of), to avoid
4505 // having to QI every time it's asked for.
4506 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject);
4507 mWindow = window;
4509 // Now that we know what our window is, we can flush the CSP errors to the
4510 // Web Console. We are flushing all messages that occured and were stored
4511 // in the queue prior to this point.
4512 FlushCSPWebConsoleErrorQueue();
4513 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4514 do_QueryInterface(GetChannel());
4515 if (internalChannel) {
4516 nsCOMArray<nsISecurityConsoleMessage> messages;
4517 internalChannel->TakeAllSecurityMessages(messages);
4518 SendToConsole(messages);
4521 // Set our visibility state, but do not fire the event. This is correct
4522 // because either we're coming out of bfcache (in which case IsVisible() will
4523 // still test false at this point and no state change will happen) or we're
4524 // doing the initial document load and don't want to fire the event for this
4525 // change.
4526 mVisibilityState = GetVisibilityState();
4528 // The global in the template contents owner document should be the same.
4529 if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
4530 mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
4533 nsCOMPtr<nsIChannel> channel = GetChannel();
4534 if (!mMaybeServiceWorkerControlled && channel) {
4535 nsLoadFlags loadFlags = 0;
4536 channel->GetLoadFlags(&loadFlags);
4537 // If we are shift-reloaded, don't associate with a ServiceWorker.
4538 // FIXME(nsm): Bug 1041339.
4539 if (loadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
4540 NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
4541 return;
4544 nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
4545 if (swm) {
4546 swm->MaybeStartControlling(this);
4547 mMaybeServiceWorkerControlled = true;
4552 nsIScriptGlobalObject*
4553 nsDocument::GetScriptHandlingObjectInternal() const
4555 MOZ_ASSERT(!mScriptGlobalObject,
4556 "Do not call this when mScriptGlobalObject is set!");
4557 if (mHasHadDefaultView) {
4558 return nullptr;
4561 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4562 do_QueryReferent(mScopeObject);
4563 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject);
4564 if (win) {
4565 NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!");
4566 nsPIDOMWindow* outer = win->GetOuterWindow();
4567 if (!outer || outer->GetCurrentInnerWindow() != win) {
4568 NS_WARNING("Wrong inner/outer window combination!");
4569 return nullptr;
4572 return scriptHandlingObject;
4574 void
4575 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
4577 NS_ASSERTION(!mScriptGlobalObject ||
4578 mScriptGlobalObject == aScriptObject,
4579 "Wrong script object!");
4580 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject);
4581 NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!");
4582 if (aScriptObject) {
4583 mScopeObject = do_GetWeakReference(aScriptObject);
4584 mHasHadScriptHandlingObject = true;
4585 mHasHadDefaultView = false;
4589 bool
4590 nsDocument::IsTopLevelContentDocument()
4592 return mIsTopLevelContentDocument;
4595 void
4596 nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument)
4598 mIsTopLevelContentDocument = aIsTopLevelContentDocument;
4601 nsPIDOMWindow *
4602 nsDocument::GetWindowInternal() const
4604 MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4605 // Let's use mScriptGlobalObject. Even if the document is already removed from
4606 // the docshell, the outer window might be still obtainable from the it.
4607 nsCOMPtr<nsPIDOMWindow> win;
4608 if (mRemovedFromDocShell) {
4609 // The docshell returns the outer window we are done.
4610 nsCOMPtr<nsIDocShell> kungfuDeathGrip(mDocumentContainer);
4611 if (mDocumentContainer) {
4612 win = mDocumentContainer->GetWindow();
4614 } else {
4615 win = do_QueryInterface(mScriptGlobalObject);
4616 if (win) {
4617 // mScriptGlobalObject is always the inner window, let's get the outer.
4618 win = win->GetOuterWindow();
4619 } else if (mMasterDocument) {
4620 // For script execution in the imported document we need the window of
4621 // the master document.
4622 win = mMasterDocument->GetWindow();
4626 return win;
4629 nsScriptLoader*
4630 nsDocument::ScriptLoader()
4632 return mScriptLoader;
4635 bool
4636 nsDocument::InternalAllowXULXBL()
4638 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
4639 mAllowXULXBL = eTriTrue;
4640 return true;
4643 mAllowXULXBL = eTriFalse;
4644 return false;
4647 // Note: We don't hold a reference to the document observer; we assume
4648 // that it has a live reference to the document.
4649 void
4650 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
4652 NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
4653 "Observer already in the list");
4654 mObservers.AppendElement(aObserver);
4655 AddMutationObserver(aObserver);
4658 bool
4659 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
4661 // If we're in the process of destroying the document (and we're
4662 // informing the observers of the destruction), don't remove the
4663 // observers from the list. This is not a big deal, since we
4664 // don't hold a live reference to the observers.
4665 if (!mInDestructor) {
4666 RemoveMutationObserver(aObserver);
4667 return mObservers.RemoveElement(aObserver);
4670 return mObservers.Contains(aObserver);
4673 void
4674 nsDocument::MaybeEndOutermostXBLUpdate()
4676 // Only call BindingManager()->EndOutermostUpdate() when
4677 // we're not in an update and it is safe to run scripts.
4678 if (mUpdateNestLevel == 0 && mInXBLUpdate) {
4679 if (nsContentUtils::IsSafeToRunScript()) {
4680 mInXBLUpdate = false;
4681 BindingManager()->EndOutermostUpdate();
4682 } else if (!mInDestructor) {
4683 nsContentUtils::AddScriptRunner(
4684 NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate));
4689 void
4690 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
4692 if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
4693 mInXBLUpdate = true;
4694 BindingManager()->BeginOutermostUpdate();
4697 ++mUpdateNestLevel;
4698 nsContentUtils::AddScriptBlocker();
4699 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
4702 void
4703 nsDocument::EndUpdate(nsUpdateType aUpdateType)
4705 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
4707 nsContentUtils::RemoveScriptBlocker();
4709 --mUpdateNestLevel;
4711 // This set of updates may have created XBL bindings. Let the
4712 // binding manager know we're done.
4713 MaybeEndOutermostXBLUpdate();
4715 MaybeInitializeFinalizeFrameLoaders();
4718 void
4719 nsDocument::BeginLoad()
4721 // Block onload here to prevent having to deal with blocking and
4722 // unblocking it while we know the document is loading.
4723 BlockOnload();
4724 mDidFireDOMContentLoaded = false;
4725 BlockDOMContentLoaded();
4727 if (mScriptLoader) {
4728 mScriptLoader->BeginDeferringScripts();
4731 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
4734 void
4735 nsDocument::ReportEmptyGetElementByIdArg()
4737 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
4738 NS_LITERAL_CSTRING("DOM"), this,
4739 nsContentUtils::eDOM_PROPERTIES,
4740 "EmptyGetElementByIdParam");
4743 Element*
4744 nsDocument::GetElementById(const nsAString& aElementId)
4746 if (!CheckGetElementByIdArg(aElementId)) {
4747 return nullptr;
4750 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
4751 return entry ? entry->GetIdElement() : nullptr;
4754 const nsSmallVoidArray*
4755 nsDocument::GetAllElementsForId(const nsAString& aElementId) const
4757 if (aElementId.IsEmpty()) {
4758 return nullptr;
4761 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
4762 return entry ? entry->GetIdElements() : nullptr;
4765 NS_IMETHODIMP
4766 nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
4768 Element *content = GetElementById(aId);
4769 if (content) {
4770 return CallQueryInterface(content, aReturn);
4773 *aReturn = nullptr;
4775 return NS_OK;
4778 Element*
4779 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
4780 void* aData, bool aForImage)
4782 nsDependentAtomString id(aID);
4784 if (!CheckGetElementByIdArg(id))
4785 return nullptr;
4787 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
4788 NS_ENSURE_TRUE(entry, nullptr);
4790 entry->AddContentChangeCallback(aObserver, aData, aForImage);
4791 return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
4794 void
4795 nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
4796 void* aData, bool aForImage)
4798 nsDependentAtomString id(aID);
4800 if (!CheckGetElementByIdArg(id))
4801 return;
4803 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
4804 if (!entry) {
4805 return;
4808 entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
4811 NS_IMETHODIMP
4812 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
4813 nsIDOMElement* aImageElement)
4815 nsCOMPtr<Element> el = do_QueryInterface(aImageElement);
4816 MozSetImageElement(aImageElementId, el);
4817 return NS_OK;
4820 void
4821 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
4822 Element* aElement)
4824 if (aImageElementId.IsEmpty())
4825 return;
4827 // Hold a script blocker while calling SetImageElement since that can call
4828 // out to id-observers
4829 nsAutoScriptBlocker scriptBlocker;
4831 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId);
4832 if (entry) {
4833 entry->SetImageElement(aElement);
4834 if (entry->IsEmpty()) {
4835 mIdentifierMap.RemoveEntry(aImageElementId);
4840 Element*
4841 nsDocument::LookupImageElement(const nsAString& aId)
4843 if (aId.IsEmpty())
4844 return nullptr;
4846 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
4847 return entry ? entry->GetImageIdElement() : nullptr;
4850 void
4851 nsDocument::DispatchContentLoadedEvents()
4853 // If you add early returns from this method, make sure you're
4854 // calling UnblockOnload properly.
4856 // Unpin references to preloaded images
4857 mPreloadingImages.Clear();
4859 if (mTiming) {
4860 mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
4863 // Dispatch observer notification to notify observers document is interactive.
4864 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
4865 nsIPrincipal *principal = GetPrincipal();
4866 os->NotifyObservers(static_cast<nsIDocument*>(this),
4867 nsContentUtils::IsSystemPrincipal(principal) ?
4868 "chrome-document-interactive" :
4869 "content-document-interactive",
4870 nullptr);
4872 // Fire a DOM event notifying listeners that this document has been
4873 // loaded (excluding images and other loads initiated by this
4874 // document).
4875 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
4876 NS_LITERAL_STRING("DOMContentLoaded"),
4877 true, true);
4879 if (mTiming) {
4880 mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
4883 // If this document is a [i]frame, fire a DOMFrameContentLoaded
4884 // event on all parent documents notifying that the HTML (excluding
4885 // other external files such as images and stylesheets) in a frame
4886 // has finished loading.
4888 // target_frame is the [i]frame element that will be used as the
4889 // target for the event. It's the [i]frame whose content is done
4890 // loading.
4891 nsCOMPtr<EventTarget> target_frame;
4893 if (mParentDocument) {
4894 target_frame = mParentDocument->FindContentForSubDocument(this);
4897 if (target_frame) {
4898 nsCOMPtr<nsIDocument> parent = mParentDocument;
4899 do {
4900 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent);
4902 nsCOMPtr<nsIDOMEvent> event;
4903 if (domDoc) {
4904 domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
4905 getter_AddRefs(event));
4909 if (event) {
4910 event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
4911 true);
4913 event->SetTarget(target_frame);
4914 event->SetTrusted(true);
4916 // To dispatch this event we must manually call
4917 // EventDispatcher::Dispatch() on the ancestor document since the
4918 // target is not in the same document, so the event would never reach
4919 // the ancestor document if we used the normal event
4920 // dispatching code.
4922 WidgetEvent* innerEvent = event->GetInternalNSEvent();
4923 if (innerEvent) {
4924 nsEventStatus status = nsEventStatus_eIgnore;
4926 nsIPresShell *shell = parent->GetShell();
4927 if (shell) {
4928 nsRefPtr<nsPresContext> context = shell->GetPresContext();
4930 if (context) {
4931 EventDispatcher::Dispatch(parent, context, innerEvent, event,
4932 &status);
4938 parent = parent->GetParentDocument();
4939 } while (parent);
4942 // If the document has a manifest attribute, fire a MozApplicationManifest
4943 // event.
4944 Element* root = GetRootElement();
4945 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
4946 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
4947 NS_LITERAL_STRING("MozApplicationManifest"),
4948 true, true);
4951 UnblockOnload(true);
4954 void
4955 nsDocument::EndLoad()
4957 // Drop the ref to our parser, if any, but keep hold of the sink so that we
4958 // can flush it from FlushPendingNotifications as needed. We might have to
4959 // do that to get a StartLayout() to happen.
4960 if (mParser) {
4961 mWeakSink = do_GetWeakReference(mParser->GetContentSink());
4962 mParser = nullptr;
4965 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
4967 UnblockDOMContentLoaded();
4970 void
4971 nsDocument::UnblockDOMContentLoaded()
4973 MOZ_ASSERT(mBlockDOMContentLoaded);
4974 if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
4975 return;
4977 mDidFireDOMContentLoaded = true;
4979 MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
4980 if (!mSynchronousDOMContentLoaded) {
4981 nsRefPtr<nsIRunnable> ev =
4982 NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
4983 NS_DispatchToCurrentThread(ev);
4984 } else {
4985 DispatchContentLoadedEvents();
4989 void
4990 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
4992 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
4993 "Someone forgot a scriptblocker");
4994 NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
4995 (this, aContent, aStateMask));
4998 void
4999 nsDocument::DocumentStatesChanged(EventStates aStateMask)
5001 // Invalidate our cached state.
5002 mGotDocumentState &= ~aStateMask;
5003 mDocumentState &= ~aStateMask;
5005 NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
5008 void
5009 nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
5010 nsIStyleRule* aOldStyleRule,
5011 nsIStyleRule* aNewStyleRule)
5013 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
5014 (this, aSheet,
5015 aOldStyleRule, aNewStyleRule));
5017 if (StyleSheetChangeEventsEnabled()) {
5018 nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule);
5019 DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5020 "StyleRuleChanged",
5021 mRule,
5022 rule ? rule->GetDOMRule() : nullptr);
5026 void
5027 nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
5028 nsIStyleRule* aStyleRule)
5030 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
5031 (this, aSheet, aStyleRule));
5033 if (StyleSheetChangeEventsEnabled()) {
5034 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
5035 DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5036 "StyleRuleAdded",
5037 mRule,
5038 rule ? rule->GetDOMRule() : nullptr);
5042 void
5043 nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
5044 nsIStyleRule* aStyleRule)
5046 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
5047 (this, aSheet, aStyleRule));
5049 if (StyleSheetChangeEventsEnabled()) {
5050 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
5051 DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent,
5052 "StyleRuleRemoved",
5053 mRule,
5054 rule ? rule->GetDOMRule() : nullptr);
5058 #undef DO_STYLESHEET_NOTIFICATION
5062 // nsIDOMDocument interface
5064 DocumentType*
5065 nsIDocument::GetDoctype() const
5067 for (nsIContent* child = GetFirstChild();
5068 child;
5069 child = child->GetNextSibling()) {
5070 if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
5071 return static_cast<DocumentType*>(child);
5074 return nullptr;
5077 NS_IMETHODIMP
5078 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
5080 MOZ_ASSERT(aDoctype);
5081 nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype();
5082 doctype.forget(aDoctype);
5083 return NS_OK;
5086 NS_IMETHODIMP
5087 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
5089 ErrorResult rv;
5090 *aImplementation = GetImplementation(rv);
5091 if (rv.Failed()) {
5092 MOZ_ASSERT(!*aImplementation);
5093 return rv.ErrorCode();
5095 NS_ADDREF(*aImplementation);
5096 return NS_OK;
5099 DOMImplementation*
5100 nsDocument::GetImplementation(ErrorResult& rv)
5102 if (!mDOMImplementation) {
5103 nsCOMPtr<nsIURI> uri;
5104 NS_NewURI(getter_AddRefs(uri), "about:blank");
5105 if (!uri) {
5106 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5107 return nullptr;
5109 bool hasHadScriptObject = true;
5110 nsIScriptGlobalObject* scriptObject =
5111 GetScriptHandlingObject(hasHadScriptObject);
5112 if (!scriptObject && hasHadScriptObject) {
5113 rv.Throw(NS_ERROR_UNEXPECTED);
5114 return nullptr;
5116 mDOMImplementation = new DOMImplementation(this,
5117 scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5120 return mDOMImplementation;
5123 NS_IMETHODIMP
5124 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
5126 NS_ENSURE_ARG_POINTER(aDocumentElement);
5128 Element* root = GetRootElement();
5129 if (root) {
5130 return CallQueryInterface(root, aDocumentElement);
5133 *aDocumentElement = nullptr;
5135 return NS_OK;
5138 NS_IMETHODIMP
5139 nsDocument::CreateElement(const nsAString& aTagName,
5140 nsIDOMElement** aReturn)
5142 *aReturn = nullptr;
5143 ErrorResult rv;
5144 nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
5145 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
5146 return CallQueryInterface(element, aReturn);
5149 bool IsLowercaseASCII(const nsAString& aValue)
5151 int32_t len = aValue.Length();
5152 for (int32_t i = 0; i < len; ++i) {
5153 char16_t c = aValue[i];
5154 if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5155 return false;
5158 return true;
5161 already_AddRefed<Element>
5162 nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
5164 rv = nsContentUtils::CheckQName(aTagName, false);
5165 if (rv.Failed()) {
5166 return nullptr;
5169 bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName);
5170 nsAutoString lcTagName;
5171 if (needsLowercase) {
5172 nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5175 nsCOMPtr<nsIContent> content;
5176 rv = CreateElem(needsLowercase ? lcTagName : aTagName,
5177 nullptr, mDefaultElementType, getter_AddRefs(content));
5178 if (rv.Failed()) {
5179 return nullptr;
5181 return dont_AddRef(content.forget().take()->AsElement());
5184 void
5185 nsDocument::SwizzleCustomElement(Element* aElement,
5186 const nsAString& aTypeExtension,
5187 uint32_t aNamespaceID,
5188 ErrorResult& rv)
5190 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension));
5191 nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
5192 if (!mRegistry || tagAtom == typeAtom) {
5193 return;
5196 CustomElementDefinition* data;
5197 CustomElementHashKey key(aNamespaceID, typeAtom);
5198 if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
5199 // The type extension doesn't exist in the registry,
5200 // thus we don't need to swizzle, but it is possibly
5201 // an upgrade candidate.
5202 RegisterUnresolvedElement(aElement, typeAtom);
5203 return;
5206 if (data->mLocalName != tagAtom) {
5207 // The element doesn't match the local name for the
5208 // definition, thus the element isn't a custom element
5209 // and we don't need to do anything more.
5210 return;
5213 if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
5214 // Swizzling in the parser happens after the "is" attribute is added.
5215 aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true);
5218 // Enqueuing the created callback will set the CustomElementData on the
5219 // element, causing prototype swizzling to occur in Element::WrapObject.
5220 EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
5223 already_AddRefed<Element>
5224 nsDocument::CreateElement(const nsAString& aTagName,
5225 const nsAString& aTypeExtension,
5226 ErrorResult& rv)
5228 nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
5229 if (rv.Failed()) {
5230 return nullptr;
5233 SwizzleCustomElement(elem, aTypeExtension,
5234 GetDefaultNamespaceID(), rv);
5235 if (rv.Failed()) {
5236 return nullptr;
5239 return elem.forget();
5242 NS_IMETHODIMP
5243 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5244 const nsAString& aQualifiedName,
5245 nsIDOMElement** aReturn)
5247 *aReturn = nullptr;
5248 ErrorResult rv;
5249 nsCOMPtr<Element> element =
5250 nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
5251 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
5252 return CallQueryInterface(element, aReturn);
5255 already_AddRefed<Element>
5256 nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
5257 const nsAString& aQualifiedName,
5258 ErrorResult& rv)
5260 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
5261 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5262 aQualifiedName,
5263 mNodeInfoManager,
5264 nsIDOMNode::ELEMENT_NODE,
5265 getter_AddRefs(nodeInfo));
5266 if (rv.Failed()) {
5267 return nullptr;
5270 nsCOMPtr<Element> element;
5271 rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5272 NOT_FROM_PARSER);
5273 if (rv.Failed()) {
5274 return nullptr;
5276 return element.forget();
5279 already_AddRefed<Element>
5280 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5281 const nsAString& aQualifiedName,
5282 const nsAString& aTypeExtension,
5283 ErrorResult& rv)
5285 nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
5286 aQualifiedName,
5287 rv);
5288 if (rv.Failed()) {
5289 return nullptr;
5292 int32_t nameSpaceId = kNameSpaceID_Wildcard;
5293 if (!aNamespaceURI.EqualsLiteral("*")) {
5294 rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
5295 nameSpaceId);
5296 if (rv.Failed()) {
5297 return nullptr;
5301 SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv);
5302 if (rv.Failed()) {
5303 return nullptr;
5306 return elem.forget();
5309 NS_IMETHODIMP
5310 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
5312 *aReturn = nsIDocument::CreateTextNode(aData).take();
5313 return NS_OK;
5316 already_AddRefed<nsTextNode>
5317 nsIDocument::CreateTextNode(const nsAString& aData) const
5319 nsRefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5320 // Don't notify; this node is still being created.
5321 text->SetText(aData, false);
5322 return text.forget();
5325 NS_IMETHODIMP
5326 nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
5328 *aReturn = nsIDocument::CreateDocumentFragment().take();
5329 return NS_OK;
5332 already_AddRefed<DocumentFragment>
5333 nsIDocument::CreateDocumentFragment() const
5335 nsRefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5336 return frag.forget();
5339 NS_IMETHODIMP
5340 nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
5342 *aReturn = nsIDocument::CreateComment(aData).take();
5343 return NS_OK;
5346 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
5347 already_AddRefed<dom::Comment>
5348 nsIDocument::CreateComment(const nsAString& aData) const
5350 nsRefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5352 // Don't notify; this node is still being created.
5353 comment->SetText(aData, false);
5354 return comment.forget();
5357 NS_IMETHODIMP
5358 nsDocument::CreateCDATASection(const nsAString& aData,
5359 nsIDOMCDATASection** aReturn)
5361 NS_ENSURE_ARG_POINTER(aReturn);
5362 ErrorResult rv;
5363 *aReturn = nsIDocument::CreateCDATASection(aData, rv).take();
5364 return rv.ErrorCode();
5367 already_AddRefed<CDATASection>
5368 nsIDocument::CreateCDATASection(const nsAString& aData,
5369 ErrorResult& rv)
5371 if (IsHTML()) {
5372 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5373 return nullptr;
5376 if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5377 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5378 return nullptr;
5381 nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5383 // Don't notify; this node is still being created.
5384 cdata->SetText(aData, false);
5386 return cdata.forget();
5389 NS_IMETHODIMP
5390 nsDocument::CreateProcessingInstruction(const nsAString& aTarget,
5391 const nsAString& aData,
5392 nsIDOMProcessingInstruction** aReturn)
5394 ErrorResult rv;
5395 *aReturn =
5396 nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take();
5397 return rv.ErrorCode();
5400 already_AddRefed<ProcessingInstruction>
5401 nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5402 const nsAString& aData,
5403 ErrorResult& rv) const
5405 nsresult res = nsContentUtils::CheckQName(aTarget, false);
5406 if (NS_FAILED(res)) {
5407 rv.Throw(res);
5408 return nullptr;
5411 if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5412 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5413 return nullptr;
5416 nsRefPtr<ProcessingInstruction> pi =
5417 NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5419 return pi.forget();
5422 NS_IMETHODIMP
5423 nsDocument::CreateAttribute(const nsAString& aName,
5424 nsIDOMAttr** aReturn)
5426 ErrorResult rv;
5427 *aReturn = nsIDocument::CreateAttribute(aName, rv).take();
5428 return rv.ErrorCode();
5431 already_AddRefed<Attr>
5432 nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
5434 WarnOnceAbout(eCreateAttribute);
5436 if (!mNodeInfoManager) {
5437 rv.Throw(NS_ERROR_NOT_INITIALIZED);
5438 return nullptr;
5441 nsresult res = nsContentUtils::CheckQName(aName, false);
5442 if (NS_FAILED(res)) {
5443 rv.Throw(res);
5444 return nullptr;
5447 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
5448 res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None,
5449 nsIDOMNode::ATTRIBUTE_NODE,
5450 getter_AddRefs(nodeInfo));
5451 if (NS_FAILED(res)) {
5452 rv.Throw(res);
5453 return nullptr;
5456 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5457 EmptyString(), false);
5458 return attribute.forget();
5461 NS_IMETHODIMP
5462 nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI,
5463 const nsAString & aQualifiedName,
5464 nsIDOMAttr **aResult)
5466 ErrorResult rv;
5467 *aResult =
5468 nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take();
5469 return rv.ErrorCode();
5472 already_AddRefed<Attr>
5473 nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
5474 const nsAString& aQualifiedName,
5475 ErrorResult& rv)
5477 WarnOnceAbout(eCreateAttributeNS);
5479 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
5480 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5481 aQualifiedName,
5482 mNodeInfoManager,
5483 nsIDOMNode::ATTRIBUTE_NODE,
5484 getter_AddRefs(nodeInfo));
5485 if (rv.Failed()) {
5486 return nullptr;
5489 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5490 EmptyString(), true);
5491 return attribute.forget();
5494 bool
5495 nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
5497 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
5499 JS::Rooted<JSObject*> global(aCx,
5500 JS_GetGlobalForObject(aCx, &args.callee()));
5501 nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
5502 MOZ_ASSERT(window, "Should have a non-null window");
5504 nsDocument* document = static_cast<nsDocument*>(window->GetDoc());
5506 // Function name is the type of the custom element.
5507 JSString* jsFunName =
5508 JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
5509 nsAutoJSString elemName;
5510 if (!elemName.init(aCx, jsFunName)) {
5511 return true;
5514 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName));
5515 CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom);
5516 CustomElementDefinition* definition;
5517 if (!document->mRegistry ||
5518 !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
5519 return true;
5522 nsDependentAtomString localName(definition->mLocalName);
5524 nsCOMPtr<nsIContent> newElement;
5525 nsresult rv = document->CreateElem(localName, nullptr,
5526 definition->mNamespaceID,
5527 getter_AddRefs(newElement));
5528 NS_ENSURE_SUCCESS(rv, true);
5530 ErrorResult errorResult;
5531 nsCOMPtr<Element> element = do_QueryInterface(newElement);
5532 document->SwizzleCustomElement(element, elemName, definition->mNamespaceID,
5533 errorResult);
5534 if (errorResult.Failed()) {
5535 return true;
5538 rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval());
5539 NS_ENSURE_SUCCESS(rv, true);
5541 return true;
5544 bool
5545 nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
5547 JS::Rooted<JSObject*> obj(aCx, aObject);
5548 return Preferences::GetBool("dom.webcomponents.enabled") ||
5549 IsInCertifiedApp(aCx, obj);
5552 nsresult
5553 nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
5555 if (!mRegistry) {
5556 return NS_OK;
5559 mozilla::dom::NodeInfo* info = aElement->NodeInfo();
5561 // Candidate may be a custom element through extension,
5562 // in which case the custom element type name will not
5563 // match the element tag name. e.g. <button is="x-button">.
5564 nsCOMPtr<nsIAtom> typeName = aTypeName;
5565 if (!typeName) {
5566 typeName = info->NameAtom();
5569 CustomElementHashKey key(info->NamespaceID(), typeName);
5570 if (mRegistry->mCustomDefinitions.Get(&key)) {
5571 return NS_OK;
5574 nsTArray<nsRefPtr<Element>>* unresolved;
5575 mRegistry->mCandidatesMap.Get(&key, &unresolved);
5576 if (!unresolved) {
5577 unresolved = new nsTArray<nsRefPtr<Element>>();
5578 // Ownership of unresolved is taken by mCandidatesMap.
5579 mRegistry->mCandidatesMap.Put(&key, unresolved);
5582 nsRefPtr<Element>* elem = unresolved->AppendElement();
5583 *elem = aElement;
5585 return NS_OK;
5588 namespace {
5590 class ProcessStackRunner MOZ_FINAL : public nsIRunnable
5592 ~ProcessStackRunner() {}
5593 public:
5594 explicit ProcessStackRunner(bool aIsBaseQueue = false)
5595 : mIsBaseQueue(aIsBaseQueue)
5598 NS_DECL_ISUPPORTS
5599 NS_IMETHOD Run() MOZ_OVERRIDE
5601 nsDocument::ProcessTopElementQueue(mIsBaseQueue);
5602 return NS_OK;
5604 bool mIsBaseQueue;
5607 NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable);
5609 } // anonymous namespace
5611 void
5612 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
5613 Element* aCustomElement,
5614 LifecycleCallbackArgs* aArgs,
5615 CustomElementDefinition* aDefinition)
5617 if (!mRegistry) {
5618 // The element might not belong to a document that
5619 // has a browsing context, and thus no registry.
5620 return;
5623 CustomElementData* elementData = aCustomElement->GetCustomElementData();
5625 // Let DEFINITION be ELEMENT's definition
5626 CustomElementDefinition* definition = aDefinition;
5627 if (!definition) {
5628 mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
5630 // Make sure we get the correct definition in case the element
5631 // is a extended custom element e.g. <button is="x-button">.
5632 nsCOMPtr<nsIAtom> typeAtom = elementData ?
5633 elementData->mType.get() : info->NameAtom();
5635 CustomElementHashKey key(info->NamespaceID(), typeAtom);
5636 if (!mRegistry->mCustomDefinitions.Get(&key, &definition) ||
5637 definition->mLocalName != info->NameAtom()) {
5638 // Trying to enqueue a callback for an element that is not
5639 // a custom element. We are done, nothing to do.
5640 return;
5644 if (!elementData) {
5645 // Create the custom element data the first time
5646 // that we try to enqueue a callback.
5647 elementData = new CustomElementData(definition->mType);
5648 // aCustomElement takes ownership of elementData
5649 aCustomElement->SetCustomElementData(elementData);
5650 MOZ_ASSERT(aType == nsIDocument::eCreated,
5651 "First callback should be the created callback");
5654 // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
5655 CallbackFunction* func = nullptr;
5656 switch (aType) {
5657 case nsIDocument::eCreated:
5658 if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
5659 func = definition->mCallbacks->mCreatedCallback.Value();
5661 break;
5663 case nsIDocument::eAttached:
5664 if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
5665 func = definition->mCallbacks->mAttachedCallback.Value();
5667 break;
5669 case nsIDocument::eDetached:
5670 if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
5671 func = definition->mCallbacks->mDetachedCallback.Value();
5673 break;
5675 case nsIDocument::eAttributeChanged:
5676 if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
5677 func = definition->mCallbacks->mAttributeChangedCallback.Value();
5679 break;
5682 // If there is no such callback, stop.
5683 if (!func) {
5684 return;
5687 if (aType == nsIDocument::eCreated) {
5688 elementData->mCreatedCallbackInvoked = false;
5689 } else if (!elementData->mCreatedCallbackInvoked) {
5690 // Callbacks other than created callback must not be enqueued
5691 // until after the created callback has been invoked.
5692 return;
5695 // Add CALLBACK to ELEMENT's callback queue.
5696 CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
5697 aType,
5698 func,
5699 elementData);
5700 // Ownership of callback is taken by mCallbackQueue.
5701 elementData->mCallbackQueue.AppendElement(callback);
5702 if (aArgs) {
5703 callback->SetArgs(*aArgs);
5706 if (!elementData->mElementIsBeingCreated) {
5707 CustomElementData* lastData =
5708 sProcessingStack->SafeLastElement(nullptr);
5710 // A new element queue needs to be pushed if the queue at the
5711 // top of the stack is associated with another microtask level.
5712 // Don't push a queue for the level 0 microtask (base element queue)
5713 // because we don't want to process the queue until the
5714 // microtask checkpoint.
5715 bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 &&
5716 (!lastData || lastData->mAssociatedMicroTask <
5717 static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
5719 // Push a new element queue onto the processing stack when appropriate
5720 // (when we enter a new microtask).
5721 if (shouldPushElementQueue) {
5722 // Push a sentinel value on the processing stack to mark the
5723 // boundary between the element queues.
5724 sProcessingStack->AppendElement((CustomElementData*) nullptr);
5727 sProcessingStack->AppendElement(elementData);
5728 elementData->mAssociatedMicroTask =
5729 static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
5731 // Add a script runner to pop and process the element queue at
5732 // the top of the processing stack.
5733 if (shouldPushElementQueue) {
5734 // Lifecycle callbacks enqueued by user agent implementation
5735 // should be invoked prior to returning control back to script.
5736 // Create a script runner to process the top of the processing
5737 // stack as soon as it is safe to run script.
5738 nsContentUtils::AddScriptRunner(new ProcessStackRunner());
5743 // static
5744 void
5745 nsDocument::ProcessBaseElementQueue()
5747 // Prevent re-entrance. Also, if a microtask checkpoint is reached
5748 // and there is no processing stack to process, then we are done.
5749 if (sProcessingBaseElementQueue || !sProcessingStack) {
5750 return;
5753 MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0);
5754 sProcessingBaseElementQueue = true;
5755 nsContentUtils::AddScriptRunner(new ProcessStackRunner(true));
5758 // static
5759 void
5760 nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
5762 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
5764 nsTArray<nsRefPtr<CustomElementData>>& stack = *sProcessingStack;
5765 uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
5767 if (aIsBaseQueue && firstQueue != 0) {
5768 return;
5771 for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
5772 // Callback queue may have already been processed in an earlier
5773 // element queue or in an element queue that was popped
5774 // off more recently.
5775 if (stack[i]->mAssociatedMicroTask != -1) {
5776 stack[i]->RunCallbackQueue();
5777 stack[i]->mAssociatedMicroTask = -1;
5781 // If this was actually the base element queue, don't bother trying to pop
5782 // the first "queue" marker (sentinel).
5783 if (firstQueue != 0) {
5784 stack.SetLength(firstQueue);
5785 } else {
5786 // Don't pop sentinel for base element queue.
5787 stack.SetLength(1);
5788 sProcessingBaseElementQueue = false;
5792 bool
5793 nsDocument::RegisterEnabled()
5795 static bool sPrefValue =
5796 Preferences::GetBool("dom.webcomponents.enabled", false);
5797 return sPrefValue;
5800 // static
5801 Maybe<nsTArray<nsRefPtr<mozilla::dom::CustomElementData>>>
5802 nsDocument::sProcessingStack;
5804 // static
5805 bool
5806 nsDocument::sProcessingBaseElementQueue;
5808 void
5809 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
5810 const ElementRegistrationOptions& aOptions,
5811 JS::MutableHandle<JSObject*> aRetval,
5812 ErrorResult& rv)
5814 if (!mRegistry) {
5815 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5816 return;
5819 Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
5821 // Unconditionally convert TYPE to lowercase.
5822 nsAutoString lcType;
5823 nsContentUtils::ASCIIToLower(aType, lcType);
5825 // Only convert NAME to lowercase in HTML documents. Note that NAME is
5826 // options.extends.
5827 nsAutoString lcName;
5828 if (IsHTML()) {
5829 nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
5830 } else {
5831 lcName.Assign(aOptions.mExtends);
5834 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
5835 if (!nsContentUtils::IsCustomElementName(typeAtom)) {
5836 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5837 return;
5840 // If there already exists a definition with the same TYPE, set ERROR to
5841 // DuplicateDefinition and stop.
5842 // Note that we need to find existing custom elements from either namespace.
5843 CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom);
5844 if (definitions.Get(&duplicateFinder)) {
5845 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5846 return;
5849 nsIGlobalObject* sgo = GetScopeObject();
5850 if (!sgo) {
5851 rv.Throw(NS_ERROR_UNEXPECTED);
5852 return;
5854 JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
5856 JSAutoCompartment ac(aCx, global);
5858 JS::Handle<JSObject*> htmlProto(
5859 HTMLElementBinding::GetProtoObject(aCx, global));
5860 if (!htmlProto) {
5861 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5862 return;
5865 int32_t namespaceID = kNameSpaceID_XHTML;
5866 JS::Rooted<JSObject*> protoObject(aCx);
5867 if (!aOptions.mPrototype) {
5868 protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr());
5869 if (!protoObject) {
5870 rv.Throw(NS_ERROR_UNEXPECTED);
5871 return;
5873 } else {
5874 // If a prototype is provided, we must check to ensure that it is from the
5875 // same browsing context as us.
5876 protoObject = aOptions.mPrototype;
5877 if (JS_GetGlobalForObject(aCx, protoObject) != global) {
5878 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5879 return;
5882 // If PROTOTYPE is already an interface prototype object for any interface
5883 // object or PROTOTYPE has a non-configurable property named constructor,
5884 // throw a NotSupportedError and stop.
5885 const js::Class* clasp = js::GetObjectClass(protoObject);
5886 if (IsDOMIfaceAndProtoClass(clasp)) {
5887 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5888 return;
5891 JS::Rooted<JSPropertyDescriptor> descRoot(aCx);
5892 JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot);
5893 if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
5894 rv.Throw(NS_ERROR_UNEXPECTED);
5895 return;
5898 // Check if non-configurable
5899 if (desc.isPermanent()) {
5900 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5901 return;
5904 JS::Handle<JSObject*> svgProto(
5905 SVGElementBinding::GetProtoObject(aCx, global));
5906 if (!svgProto) {
5907 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5908 return;
5911 JS::Rooted<JSObject*> protoProto(aCx, protoObject);
5913 // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
5914 // Namespace.
5915 while (protoProto) {
5916 if (protoProto == htmlProto) {
5917 break;
5920 if (protoProto == svgProto) {
5921 namespaceID = kNameSpaceID_SVG;
5922 break;
5925 if (!JS_GetPrototype(aCx, protoProto, &protoProto)) {
5926 rv.Throw(NS_ERROR_UNEXPECTED);
5927 return;
5932 // If name was provided and not null...
5933 nsCOMPtr<nsIAtom> nameAtom;
5934 if (!lcName.IsEmpty()) {
5935 // Let BASE be the element interface for NAME and NAMESPACE.
5936 bool known = false;
5937 nameAtom = do_GetAtom(lcName);
5938 if (namespaceID == kNameSpaceID_XHTML) {
5939 nsIParserService* ps = nsContentUtils::GetParserService();
5940 if (!ps) {
5941 rv.Throw(NS_ERROR_UNEXPECTED);
5942 return;
5945 known =
5946 ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined;
5947 } else {
5948 known = SVGElementFactory::Exists(nameAtom);
5951 // If BASE does not exist or is an interface for a custom element, set ERROR
5952 // to InvalidName and stop.
5953 // If BASE exists, then it cannot be an interface for a custom element.
5954 if (!known) {
5955 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5956 return;
5958 } else {
5959 // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
5960 if (namespaceID == kNameSpaceID_SVG) {
5961 rv.Throw(NS_ERROR_UNEXPECTED);
5962 return;
5965 nameAtom = typeAtom;
5968 nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
5969 JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject));
5970 if (!callbacksHolder->Init(aCx, rootedv)) {
5971 rv.Throw(NS_ERROR_FAILURE);
5972 return;
5975 // Associate the definition with the custom element.
5976 CustomElementHashKey key(namespaceID, typeAtom);
5977 LifecycleCallbacks* callbacks = callbacksHolder.forget();
5978 CustomElementDefinition* definition =
5979 new CustomElementDefinition(protoObject,
5980 typeAtom,
5981 nameAtom,
5982 callbacks,
5983 namespaceID,
5984 0 /* TODO dependent on HTML imports. Bug 877072 */);
5985 definitions.Put(&key, definition);
5987 // Do element upgrade.
5988 nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates;
5989 mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
5990 if (candidates) {
5991 for (size_t i = 0; i < candidates->Length(); ++i) {
5992 Element *elem = candidates->ElementAt(i);
5994 // Make sure that the element name matches the name in the definition.
5995 // (e.g. a definition for x-button extending button should match
5996 // <button is="x-button"> but not <x-button>.
5997 if (elem->NodeInfo()->NameAtom() != nameAtom) {
5998 // Skip over this element because definition does not apply.
5999 continue;
6002 nsWrapperCache* cache;
6003 CallQueryInterface(elem, &cache);
6004 MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
6006 JS::RootedObject wrapper(aCx);
6007 if ((wrapper = cache->GetWrapper())) {
6008 if (!JS_SetPrototype(aCx, wrapper, protoObject)) {
6009 continue;
6013 EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
6014 if (elem->GetCurrentDoc()) {
6015 // Normally callbacks can not be enqueued until the created
6016 // callback has been invoked, however, the attached callback
6017 // in element upgrade is an exception so pretend the created
6018 // callback has been invoked.
6019 elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
6021 EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition);
6026 // Create constructor to return. Store the name of the custom element as the
6027 // name of the function.
6028 JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
6029 JSFUN_CONSTRUCTOR, JS::NullPtr(),
6030 NS_ConvertUTF16toUTF8(lcType).get());
6031 if (!constructor) {
6032 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
6033 return;
6036 aRetval.set(JS_GetFunctionObject(constructor));
6039 void
6040 nsDocument::UseRegistryFromDocument(nsIDocument* aDocument)
6042 nsDocument* doc = static_cast<nsDocument*>(aDocument);
6043 MOZ_ASSERT(!mRegistry, "There should be no existing registry.");
6044 mRegistry = doc->mRegistry;
6047 NS_IMETHODIMP
6048 nsDocument::GetElementsByTagName(const nsAString& aTagname,
6049 nsIDOMNodeList** aReturn)
6051 nsRefPtr<nsContentList> list = GetElementsByTagName(aTagname);
6052 NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
6054 // transfer ref to aReturn
6055 list.forget(aReturn);
6056 return NS_OK;
6059 long
6060 nsDocument::BlockedTrackingNodeCount() const
6062 return mBlockedTrackingNodes.Length();
6065 already_AddRefed<nsSimpleContentList>
6066 nsDocument::BlockedTrackingNodes() const
6068 nsRefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
6070 nsTArray<nsWeakPtr> blockedTrackingNodes;
6071 blockedTrackingNodes = mBlockedTrackingNodes;
6073 for (unsigned long i = 0; i < blockedTrackingNodes.Length(); i++) {
6074 nsWeakPtr weakNode = blockedTrackingNodes[i];
6075 nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
6076 // Consider only nodes to which we have managed to get strong references.
6077 // Coping with nullptrs since it's expected for nodes to disappear when
6078 // nobody else is referring to them.
6079 if (node) {
6080 list->AppendElement(node);
6084 return list.forget();
6087 already_AddRefed<nsContentList>
6088 nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6089 const nsAString& aLocalName,
6090 ErrorResult& aResult)
6092 int32_t nameSpaceId = kNameSpaceID_Wildcard;
6094 if (!aNamespaceURI.EqualsLiteral("*")) {
6095 aResult =
6096 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
6097 nameSpaceId);
6098 if (aResult.Failed()) {
6099 return nullptr;
6103 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
6105 return NS_GetContentList(this, nameSpaceId, aLocalName);
6108 NS_IMETHODIMP
6109 nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6110 const nsAString& aLocalName,
6111 nsIDOMNodeList** aReturn)
6113 ErrorResult rv;
6114 nsRefPtr<nsContentList> list =
6115 nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
6116 if (rv.Failed()) {
6117 return rv.ErrorCode();
6120 // transfer ref to aReturn
6121 list.forget(aReturn);
6122 return NS_OK;
6125 NS_IMETHODIMP
6126 nsDocument::GetAsync(bool *aAsync)
6128 NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
6130 return NS_ERROR_NOT_IMPLEMENTED;
6133 NS_IMETHODIMP
6134 nsDocument::SetAsync(bool aAsync)
6136 NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!");
6138 return NS_ERROR_NOT_IMPLEMENTED;
6141 NS_IMETHODIMP
6142 nsDocument::Load(const nsAString& aUrl, bool *aReturn)
6144 NS_ERROR("nsDocument::Load() should be overriden by subclass!");
6146 return NS_ERROR_NOT_IMPLEMENTED;
6149 NS_IMETHODIMP
6150 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
6152 NS_ADDREF(*aStyleSheets = StyleSheets());
6153 return NS_OK;
6156 StyleSheetList*
6157 nsDocument::StyleSheets()
6159 if (!mDOMStyleSheets) {
6160 mDOMStyleSheets = new nsDOMStyleSheetList(this);
6162 return mDOMStyleSheets;
6165 NS_IMETHODIMP
6166 nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet)
6168 nsIDocument::GetSelectedStyleSheetSet(aSheetSet);
6169 return NS_OK;
6172 void
6173 nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
6175 aSheetSet.Truncate();
6177 // Look through our sheets, find the selected set title
6178 int32_t count = GetNumberOfStyleSheets();
6179 nsAutoString title;
6180 for (int32_t index = 0; index < count; index++) {
6181 nsIStyleSheet* sheet = GetStyleSheetAt(index);
6182 NS_ASSERTION(sheet, "Null sheet in sheet list!");
6184 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet);
6185 NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet");
6186 bool disabled;
6187 domSheet->GetDisabled(&disabled);
6188 if (disabled) {
6189 // Disabled sheets don't affect the currently selected set
6190 continue;
6193 sheet->GetTitle(title);
6195 if (aSheetSet.IsEmpty()) {
6196 aSheetSet = title;
6197 } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
6198 // Sheets from multiple sets enabled; return null string, per spec.
6199 SetDOMStringToNull(aSheetSet);
6200 return;
6205 NS_IMETHODIMP
6206 nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet)
6208 SetSelectedStyleSheetSet(aSheetSet);
6209 return NS_OK;
6212 void
6213 nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
6215 if (DOMStringIsNull(aSheetSet)) {
6216 return;
6219 // Must update mLastStyleSheetSet before doing anything else with stylesheets
6220 // or CSSLoaders.
6221 mLastStyleSheetSet = aSheetSet;
6222 EnableStyleSheetsForSetInternal(aSheetSet, true);
6225 NS_IMETHODIMP
6226 nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet)
6228 nsString sheetSet;
6229 GetLastStyleSheetSet(sheetSet);
6230 aSheetSet = sheetSet;
6231 return NS_OK;
6234 void
6235 nsDocument::GetLastStyleSheetSet(nsString& aSheetSet)
6237 aSheetSet = mLastStyleSheetSet;
6240 NS_IMETHODIMP
6241 nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6243 nsIDocument::GetPreferredStyleSheetSet(aSheetSet);
6244 return NS_OK;
6247 void
6248 nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6250 GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
6253 NS_IMETHODIMP
6254 nsDocument::GetStyleSheetSets(nsISupports** aList)
6256 NS_ADDREF(*aList = StyleSheetSets());
6257 return NS_OK;
6260 DOMStringList*
6261 nsDocument::StyleSheetSets()
6263 if (!mStyleSheetSetList) {
6264 mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
6266 return mStyleSheetSetList;
6269 NS_IMETHODIMP
6270 nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet)
6272 EnableStyleSheetsForSet(aSheetSet);
6273 return NS_OK;
6276 void
6277 nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
6279 // Per spec, passing in null is a no-op.
6280 if (!DOMStringIsNull(aSheetSet)) {
6281 // Note: must make sure to not change the CSSLoader's preferred sheet --
6282 // that value should be equal to either our lastStyleSheetSet (if that's
6283 // non-null) or to our preferredStyleSheetSet. And this method doesn't
6284 // change either of those.
6285 EnableStyleSheetsForSetInternal(aSheetSet, false);
6289 void
6290 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
6291 bool aUpdateCSSLoader)
6293 BeginUpdate(UPDATE_STYLE);
6294 int32_t count = GetNumberOfStyleSheets();
6295 nsAutoString title;
6296 for (int32_t index = 0; index < count; index++) {
6297 nsIStyleSheet* sheet = GetStyleSheetAt(index);
6298 NS_ASSERTION(sheet, "Null sheet in sheet list!");
6299 sheet->GetTitle(title);
6300 if (!title.IsEmpty()) {
6301 sheet->SetEnabled(title.Equals(aSheetSet));
6304 if (aUpdateCSSLoader) {
6305 CSSLoader()->SetPreferredSheet(aSheetSet);
6307 EndUpdate(UPDATE_STYLE);
6310 NS_IMETHODIMP
6311 nsDocument::GetCharacterSet(nsAString& aCharacterSet)
6313 nsIDocument::GetCharacterSet(aCharacterSet);
6314 return NS_OK;
6317 void
6318 nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
6320 CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet);
6323 NS_IMETHODIMP
6324 nsDocument::ImportNode(nsIDOMNode* aImportedNode,
6325 bool aDeep,
6326 uint8_t aArgc,
6327 nsIDOMNode** aResult)
6329 if (aArgc == 0) {
6330 aDeep = true;
6333 *aResult = nullptr;
6335 nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode);
6336 NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED);
6338 ErrorResult rv;
6339 nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv);
6340 if (rv.Failed()) {
6341 return rv.ErrorCode();
6344 NS_ADDREF(*aResult = result->AsDOMNode());
6345 return NS_OK;
6348 already_AddRefed<nsINode>
6349 nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
6351 nsINode* imported = &aNode;
6353 switch (imported->NodeType()) {
6354 case nsIDOMNode::ATTRIBUTE_NODE:
6355 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
6356 case nsIDOMNode::ELEMENT_NODE:
6357 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
6358 case nsIDOMNode::TEXT_NODE:
6359 case nsIDOMNode::CDATA_SECTION_NODE:
6360 case nsIDOMNode::COMMENT_NODE:
6361 case nsIDOMNode::DOCUMENT_TYPE_NODE:
6363 nsCOMPtr<nsINode> newNode;
6364 nsCOMArray<nsINode> nodesWithProperties;
6365 rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
6366 nodesWithProperties, getter_AddRefs(newNode));
6367 if (rv.Failed()) {
6368 return nullptr;
6371 nsIDocument *ownerDoc = imported->OwnerDoc();
6372 rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, ownerDoc,
6373 nsIDOMUserDataHandler::NODE_IMPORTED,
6374 true);
6375 if (rv.Failed()) {
6376 return nullptr;
6379 return newNode.forget();
6381 default:
6383 NS_WARNING("Don't know how to clone this nodetype for importNode.");
6385 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6389 return nullptr;
6392 NS_IMETHODIMP
6393 nsDocument::LoadBindingDocument(const nsAString& aURI)
6395 ErrorResult rv;
6396 nsIDocument::LoadBindingDocument(aURI, rv);
6397 return rv.ErrorCode();
6400 void
6401 nsIDocument::LoadBindingDocument(const nsAString& aURI, ErrorResult& rv)
6403 nsCOMPtr<nsIURI> uri;
6404 rv = NS_NewURI(getter_AddRefs(uri), aURI,
6405 mCharacterSet.get(),
6406 GetDocBaseURI());
6407 if (rv.Failed()) {
6408 return;
6411 // Note - This computation of subjectPrincipal isn't necessarily sensical.
6412 // It's just designed to preserve the old semantics during a mass-conversion
6413 // patch.
6414 nsCOMPtr<nsIPrincipal> subjectPrincipal =
6415 nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal()
6416 : NodePrincipal();
6417 BindingManager()->LoadBindingDocument(this, uri, subjectPrincipal);
6420 NS_IMETHODIMP
6421 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
6423 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
6424 NS_ENSURE_ARG_POINTER(node);
6426 Element* bindingParent = nsIDocument::GetBindingParent(*node);
6427 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent);
6428 retval.forget(aResult);
6429 return NS_OK;
6432 Element*
6433 nsIDocument::GetBindingParent(nsINode& aNode)
6435 nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
6436 if (!content)
6437 return nullptr;
6439 nsIContent* bindingParent = content->GetBindingParent();
6440 return bindingParent ? bindingParent->AsElement() : nullptr;
6443 static Element*
6444 GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
6445 const nsAString& aAttrValue, bool aUniversalMatch)
6447 if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
6448 aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
6449 aAttrValue, eCaseMatters)) {
6450 return aContent->AsElement();
6453 for (nsIContent* child = aContent->GetFirstChild();
6454 child;
6455 child = child->GetNextSibling()) {
6457 Element* matchedElement =
6458 GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch);
6459 if (matchedElement)
6460 return matchedElement;
6463 return nullptr;
6466 Element*
6467 nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
6468 nsIAtom* aAttrName,
6469 const nsAString& aAttrValue) const
6471 nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
6472 if (!nodeList)
6473 return nullptr;
6475 uint32_t length = 0;
6476 nodeList->GetLength(&length);
6478 bool universalMatch = aAttrValue.EqualsLiteral("*");
6480 for (uint32_t i = 0; i < length; ++i) {
6481 nsIContent* current = nodeList->Item(i);
6482 Element* matchedElm =
6483 GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
6484 if (matchedElm)
6485 return matchedElm;
6488 return nullptr;
6491 NS_IMETHODIMP
6492 nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
6493 const nsAString& aAttrName,
6494 const nsAString& aAttrValue,
6495 nsIDOMElement** aResult)
6497 nsCOMPtr<Element> element = do_QueryInterface(aElement);
6498 NS_ENSURE_ARG_POINTER(element);
6500 Element* anonEl =
6501 nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName,
6502 aAttrValue);
6503 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl);
6504 retval.forget(aResult);
6505 return NS_OK;
6508 Element*
6509 nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
6510 const nsAString& aAttrName,
6511 const nsAString& aAttrValue)
6513 nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
6515 return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
6519 NS_IMETHODIMP
6520 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
6521 nsIDOMNodeList** aResult)
6523 *aResult = nullptr;
6525 nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
6526 return BindingManager()->GetAnonymousNodesFor(content, aResult);
6529 nsINodeList*
6530 nsIDocument::GetAnonymousNodes(Element& aElement)
6532 return BindingManager()->GetAnonymousNodesFor(&aElement);
6535 NS_IMETHODIMP
6536 nsDocument::CreateRange(nsIDOMRange** aReturn)
6538 ErrorResult rv;
6539 *aReturn = nsIDocument::CreateRange(rv).take();
6540 return rv.ErrorCode();
6543 already_AddRefed<nsRange>
6544 nsIDocument::CreateRange(ErrorResult& rv)
6546 nsRefPtr<nsRange> range = new nsRange(this);
6547 nsresult res = range->Set(this, 0, this, 0);
6548 if (NS_FAILED(res)) {
6549 rv.Throw(res);
6550 return nullptr;
6553 return range.forget();
6556 NS_IMETHODIMP
6557 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
6558 uint32_t aWhatToShow,
6559 nsIDOMNodeFilter *aFilter,
6560 uint8_t aOptionalArgc,
6561 nsIDOMNodeIterator **_retval)
6563 *_retval = nullptr;
6565 if (!aOptionalArgc) {
6566 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6569 if (!aRoot) {
6570 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
6573 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6574 NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
6576 ErrorResult rv;
6577 NodeFilterHolder holder(aFilter);
6578 *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow, holder,
6579 rv).take();
6580 return rv.ErrorCode();
6583 already_AddRefed<NodeIterator>
6584 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6585 NodeFilter* aFilter,
6586 ErrorResult& rv) const
6588 NodeFilterHolder holder(aFilter);
6589 return CreateNodeIterator(aRoot, aWhatToShow, holder, rv);
6592 already_AddRefed<NodeIterator>
6593 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6594 const NodeFilterHolder& aFilter,
6595 ErrorResult& rv) const
6597 nsINode* root = &aRoot;
6598 nsRefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow,
6599 aFilter);
6600 return iterator.forget();
6603 NS_IMETHODIMP
6604 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
6605 uint32_t aWhatToShow,
6606 nsIDOMNodeFilter *aFilter,
6607 uint8_t aOptionalArgc,
6608 nsIDOMTreeWalker **_retval)
6610 *_retval = nullptr;
6612 if (!aOptionalArgc) {
6613 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6616 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6617 NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6619 ErrorResult rv;
6620 NodeFilterHolder holder(aFilter);
6621 *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow, holder,
6622 rv).take();
6623 return rv.ErrorCode();
6626 already_AddRefed<TreeWalker>
6627 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6628 NodeFilter* aFilter,
6629 ErrorResult& rv) const
6631 NodeFilterHolder holder(aFilter);
6632 return CreateTreeWalker(aRoot, aWhatToShow, holder, rv);
6635 already_AddRefed<TreeWalker>
6636 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6637 const NodeFilterHolder& aFilter,
6638 ErrorResult& rv) const
6640 nsINode* root = &aRoot;
6641 nsRefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, aFilter);
6642 return walker.forget();
6646 NS_IMETHODIMP
6647 nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView)
6649 *aDefaultView = nullptr;
6650 nsCOMPtr<nsPIDOMWindow> win = GetWindow();
6651 win.forget(aDefaultView);
6652 return NS_OK;
6655 NS_IMETHODIMP
6656 nsDocument::GetLocation(nsIDOMLocation **_retval)
6658 *_retval = nsIDocument::GetLocation().take();
6659 return NS_OK;
6662 already_AddRefed<nsLocation>
6663 nsIDocument::GetLocation() const
6665 nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject);
6667 if (!w) {
6668 return nullptr;
6671 nsCOMPtr<nsIDOMLocation> loc;
6672 w->GetLocation(getter_AddRefs(loc));
6673 return loc.forget().downcast<nsLocation>();
6676 Element*
6677 nsIDocument::GetHtmlElement() const
6679 Element* rootElement = GetRootElement();
6680 if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
6681 return rootElement;
6682 return nullptr;
6685 Element*
6686 nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
6688 Element* html = GetHtmlElement();
6689 if (!html)
6690 return nullptr;
6692 // Look for the element with aTag inside html. This needs to run
6693 // forwards to find the first such element.
6694 for (nsIContent* child = html->GetFirstChild();
6695 child;
6696 child = child->GetNextSibling()) {
6697 if (child->IsHTML(aTag))
6698 return child->AsElement();
6700 return nullptr;
6703 nsIContent*
6704 nsDocument::GetTitleContent(uint32_t aNamespace)
6706 // mMayHaveTitleElement will have been set to true if any HTML or SVG
6707 // <title> element has been bound to this document. So if it's false,
6708 // we know there is nothing to do here. This avoids us having to search
6709 // the whole DOM if someone calls document.title on a large document
6710 // without a title.
6711 if (!mMayHaveTitleElement)
6712 return nullptr;
6714 nsRefPtr<nsContentList> list =
6715 NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title"));
6717 return list->Item(0, false);
6720 void
6721 nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle)
6723 nsIContent* title = GetTitleContent(aNamespace);
6724 if (!title)
6725 return;
6726 if(!nsContentUtils::GetNodeTextContent(title, false, aTitle))
6727 NS_RUNTIMEABORT("OOM");
6730 NS_IMETHODIMP
6731 nsDocument::GetTitle(nsAString& aTitle)
6733 nsString title;
6734 GetTitle(title);
6735 aTitle = title;
6736 return NS_OK;
6739 void
6740 nsDocument::GetTitle(nsString& aTitle)
6742 aTitle.Truncate();
6744 nsIContent *rootElement = GetRootElement();
6745 if (!rootElement)
6746 return;
6748 nsAutoString tmp;
6750 switch (rootElement->GetNameSpaceID()) {
6751 #ifdef MOZ_XUL
6752 case kNameSpaceID_XUL:
6753 rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
6754 break;
6755 #endif
6756 case kNameSpaceID_SVG:
6757 if (rootElement->Tag() == nsGkAtoms::svg) {
6758 GetTitleFromElement(kNameSpaceID_SVG, tmp);
6759 break;
6760 } // else fall through
6761 default:
6762 GetTitleFromElement(kNameSpaceID_XHTML, tmp);
6763 break;
6766 tmp.CompressWhitespace();
6767 aTitle = tmp;
6770 NS_IMETHODIMP
6771 nsDocument::SetTitle(const nsAString& aTitle)
6773 Element *rootElement = GetRootElement();
6774 if (!rootElement)
6775 return NS_OK;
6777 switch (rootElement->GetNameSpaceID()) {
6778 case kNameSpaceID_SVG:
6779 return NS_OK; // SVG doesn't support setting a title
6780 #ifdef MOZ_XUL
6781 case kNameSpaceID_XUL:
6782 return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
6783 aTitle, true);
6784 #endif
6787 // Batch updates so that mutation events don't change "the title
6788 // element" under us
6789 mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
6791 nsIContent* title = GetTitleContent(kNameSpaceID_XHTML);
6792 if (!title) {
6793 Element *head = GetHeadElement();
6794 if (!head)
6795 return NS_OK;
6798 nsRefPtr<mozilla::dom::NodeInfo> titleInfo;
6799 titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6800 kNameSpaceID_XHTML,
6801 nsIDOMNode::ELEMENT_NODE);
6802 title = NS_NewHTMLTitleElement(titleInfo.forget());
6803 if (!title)
6804 return NS_OK;
6807 head->AppendChildTo(title, true);
6810 return nsContentUtils::SetNodeTextContent(title, aTitle, false);
6813 void
6814 nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv)
6816 rv = SetTitle(aTitle);
6819 void
6820 nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
6822 NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
6823 "Setting a title while unlinking or destroying the element?");
6824 if (mInUnlinkOrDeletion) {
6825 return;
6828 if (aBoundTitleElement) {
6829 mMayHaveTitleElement = true;
6831 if (mPendingTitleChangeEvent.IsPending())
6832 return;
6834 nsRefPtr<nsRunnableMethod<nsDocument, void, false> > event =
6835 NS_NewNonOwningRunnableMethod(this,
6836 &nsDocument::DoNotifyPossibleTitleChange);
6837 nsresult rv = NS_DispatchToCurrentThread(event);
6838 if (NS_SUCCEEDED(rv)) {
6839 mPendingTitleChangeEvent = event;
6843 void
6844 nsDocument::DoNotifyPossibleTitleChange()
6846 mPendingTitleChangeEvent.Forget();
6847 mHaveFiredTitleChange = true;
6849 nsAutoString title;
6850 GetTitle(title);
6852 nsCOMPtr<nsIPresShell> shell = GetShell();
6853 if (shell) {
6854 nsCOMPtr<nsISupports> container =
6855 shell->GetPresContext()->GetContainerWeak();
6856 if (container) {
6857 nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
6858 if (docShellWin) {
6859 docShellWin->SetTitle(title.get());
6864 // Fire a DOM event for the title change.
6865 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
6866 NS_LITERAL_STRING("DOMTitleChanged"),
6867 true, true);
6870 already_AddRefed<nsIBoxObject>
6871 nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
6873 if (!aElement) {
6874 aRv.Throw(NS_ERROR_UNEXPECTED);
6875 return nullptr;
6878 nsIDocument* doc = aElement->OwnerDoc();
6879 if (doc != this) {
6880 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
6881 return nullptr;
6884 if (!mHasWarnedAboutBoxObjects && !aElement->IsXUL()) {
6885 mHasWarnedAboutBoxObjects = true;
6886 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
6887 NS_LITERAL_CSTRING("BoxObjects"), this,
6888 nsContentUtils::eDOM_PROPERTIES,
6889 "UseOfGetBoxObjectForWarning");
6892 if (!mBoxObjectTable) {
6893 mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(6);
6894 } else {
6895 nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement);
6896 if (boxObject) {
6897 return boxObject.forget();
6901 int32_t namespaceID;
6902 nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
6904 nsAutoCString contractID("@mozilla.org/layout/xul-boxobject");
6905 if (namespaceID == kNameSpaceID_XUL) {
6906 if (tag == nsGkAtoms::browser ||
6907 tag == nsGkAtoms::editor ||
6908 tag == nsGkAtoms::iframe)
6909 contractID += "-container";
6910 else if (tag == nsGkAtoms::menu)
6911 contractID += "-menu";
6912 else if (tag == nsGkAtoms::popup ||
6913 tag == nsGkAtoms::menupopup ||
6914 tag == nsGkAtoms::panel ||
6915 tag == nsGkAtoms::tooltip)
6916 contractID += "-popup";
6917 else if (tag == nsGkAtoms::tree)
6918 contractID += "-tree";
6919 else if (tag == nsGkAtoms::listbox)
6920 contractID += "-listbox";
6921 else if (tag == nsGkAtoms::scrollbox)
6922 contractID += "-scrollbox";
6924 contractID += ";1";
6926 nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get()));
6927 if (!boxObject) {
6928 aRv.Throw(NS_ERROR_FAILURE);
6929 return nullptr;
6932 boxObject->Init(aElement);
6934 if (mBoxObjectTable) {
6935 mBoxObjectTable->Put(aElement, boxObject.get());
6938 return boxObject.forget();
6941 void
6942 nsDocument::ClearBoxObjectFor(nsIContent* aContent)
6944 if (mBoxObjectTable) {
6945 nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
6946 if (boxObject) {
6947 boxObject->Clear();
6948 mBoxObjectTable->Remove(aContent);
6953 void
6954 nsDocument::FlushSkinBindings()
6956 BindingManager()->FlushSkinBindings();
6959 nsresult
6960 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
6962 mInitializableFrameLoaders.RemoveElement(aLoader);
6963 // Don't even try to initialize.
6964 if (mInDestructor) {
6965 NS_WARNING("Trying to initialize a frame loader while"
6966 "document is being deleted");
6967 return NS_ERROR_FAILURE;
6970 mInitializableFrameLoaders.AppendElement(aLoader);
6971 if (!mFrameLoaderRunner) {
6972 mFrameLoaderRunner =
6973 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6974 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6975 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6977 return NS_OK;
6980 nsresult
6981 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
6983 mInitializableFrameLoaders.RemoveElement(aLoader);
6984 if (mInDestructor) {
6985 return NS_ERROR_FAILURE;
6988 mFinalizableFrameLoaders.AppendElement(aLoader);
6989 if (!mFrameLoaderRunner) {
6990 mFrameLoaderRunner =
6991 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6992 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6993 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6995 return NS_OK;
6998 void
6999 nsDocument::MaybeInitializeFinalizeFrameLoaders()
7001 if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
7002 // This method will be recalled when mUpdateNestLevel drops to 0,
7003 // or when !mDelayFrameLoaderInitialization.
7004 mFrameLoaderRunner = nullptr;
7005 return;
7008 // We're not in an update, but it is not safe to run scripts, so
7009 // postpone frameloader initialization and finalization.
7010 if (!nsContentUtils::IsSafeToRunScript()) {
7011 if (!mInDestructor && !mFrameLoaderRunner &&
7012 (mInitializableFrameLoaders.Length() ||
7013 mFinalizableFrameLoaders.Length())) {
7014 mFrameLoaderRunner =
7015 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
7016 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
7018 return;
7020 mFrameLoaderRunner = nullptr;
7022 // Don't use a temporary array for mInitializableFrameLoaders, because
7023 // loading a frame may cause some other frameloader to be removed from the
7024 // array. But be careful to keep the loader alive when starting the load!
7025 while (mInitializableFrameLoaders.Length()) {
7026 nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
7027 mInitializableFrameLoaders.RemoveElementAt(0);
7028 NS_ASSERTION(loader, "null frameloader in the array?");
7029 loader->ReallyStartLoading();
7032 uint32_t length = mFinalizableFrameLoaders.Length();
7033 if (length > 0) {
7034 nsTArray<nsRefPtr<nsFrameLoader> > loaders;
7035 mFinalizableFrameLoaders.SwapElements(loaders);
7036 for (uint32_t i = 0; i < length; ++i) {
7037 loaders[i]->Finalize();
7042 void
7043 nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
7045 uint32_t length = mInitializableFrameLoaders.Length();
7046 for (uint32_t i = 0; i < length; ++i) {
7047 if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
7048 mInitializableFrameLoaders.RemoveElementAt(i);
7049 return;
7054 bool
7055 nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
7057 if (aShell) {
7058 uint32_t length = mFinalizableFrameLoaders.Length();
7059 for (uint32_t i = 0; i < length; ++i) {
7060 if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
7061 return true;
7065 return false;
7068 nsIDocument*
7069 nsDocument::RequestExternalResource(nsIURI* aURI,
7070 nsINode* aRequestingNode,
7071 ExternalResourceLoad** aPendingLoad)
7073 NS_PRECONDITION(aURI, "Must have a URI");
7074 NS_PRECONDITION(aRequestingNode, "Must have a node");
7075 if (mDisplayDocument) {
7076 return mDisplayDocument->RequestExternalResource(aURI,
7077 aRequestingNode,
7078 aPendingLoad);
7081 return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
7082 this, aPendingLoad);
7085 void
7086 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
7088 mExternalResourceMap.EnumerateResources(aCallback, aData);
7091 nsSMILAnimationController*
7092 nsDocument::GetAnimationController()
7094 // We create the animation controller lazily because most documents won't want
7095 // one and only SVG documents and the like will call this
7096 if (mAnimationController)
7097 return mAnimationController;
7098 // Refuse to create an Animation Controller for data documents.
7099 if (mLoadedAsData || mLoadedAsInteractiveData)
7100 return nullptr;
7102 mAnimationController = new nsSMILAnimationController(this);
7104 // If there's a presContext then check the animation mode and pause if
7105 // necessary.
7106 nsIPresShell *shell = GetShell();
7107 if (mAnimationController && shell) {
7108 nsPresContext *context = shell->GetPresContext();
7109 if (context &&
7110 context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
7111 mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
7115 // If we're hidden (or being hidden), notify the newly-created animation
7116 // controller. (Skip this check for SVG-as-an-image documents, though,
7117 // because they don't get OnPageShow / OnPageHide calls).
7118 if (!mIsShowing && !mIsBeingUsedAsImage) {
7119 mAnimationController->OnPageHide();
7122 return mAnimationController;
7126 * Retrieve the "direction" property of the document.
7128 * @lina 01/09/2001
7130 NS_IMETHODIMP
7131 nsDocument::GetDir(nsAString& aDirection)
7133 nsIDocument::GetDir(aDirection);
7134 return NS_OK;
7137 void
7138 nsIDocument::GetDir(nsAString& aDirection) const
7140 aDirection.Truncate();
7141 Element* rootElement = GetHtmlElement();
7142 if (rootElement) {
7143 static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
7148 * Set the "direction" property of the document.
7150 * @lina 01/09/2001
7152 NS_IMETHODIMP
7153 nsDocument::SetDir(const nsAString& aDirection)
7155 nsIDocument::SetDir(aDirection);
7156 return NS_OK;
7159 void
7160 nsIDocument::SetDir(const nsAString& aDirection)
7162 Element* rootElement = GetHtmlElement();
7163 if (rootElement) {
7164 rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
7165 aDirection, true);
7169 NS_IMETHODIMP
7170 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
7172 nsIDocument::GetInputEncoding(aInputEncoding);
7173 return NS_OK;
7176 void
7177 nsIDocument::GetInputEncoding(nsAString& aInputEncoding)
7179 // Not const function, because WarnOnceAbout is not a const method
7180 WarnOnceAbout(eInputEncoding);
7181 if (mHaveInputEncoding) {
7182 return GetCharacterSet(aInputEncoding);
7185 SetDOMStringToNull(aInputEncoding);
7188 NS_IMETHODIMP
7189 nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument)
7191 *aSyntheticDocument = mIsSyntheticDocument;
7192 return NS_OK;
7195 NS_IMETHODIMP
7196 nsDocument::GetDocumentURI(nsAString& aDocumentURI)
7198 nsString temp;
7199 nsIDocument::GetDocumentURI(temp);
7200 aDocumentURI = temp;
7201 return NS_OK;
7204 void
7205 nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
7207 if (mDocumentURI) {
7208 nsAutoCString uri;
7209 mDocumentURI->GetSpec(uri);
7210 CopyUTF8toUTF16(uri, aDocumentURI);
7211 } else {
7212 aDocumentURI.Truncate();
7216 // Alias of above
7217 NS_IMETHODIMP
7218 nsDocument::GetURL(nsAString& aURL)
7220 return GetDocumentURI(aURL);
7223 void
7224 nsIDocument::GetURL(nsString& aURL) const
7226 return GetDocumentURI(aURL);
7229 void
7230 nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI) const
7232 if (!mChromeXHRDocURI || !nsContentUtils::IsCallerChrome()) {
7233 return GetDocumentURI(aDocumentURI);
7236 nsAutoCString uri;
7237 mChromeXHRDocURI->GetSpec(uri);
7238 CopyUTF8toUTF16(uri, aDocumentURI);
7241 nsIURI*
7242 nsIDocument::GetDocumentURIObject() const
7244 if (!mChromeXHRDocURI) {
7245 return GetDocumentURI();
7248 return mChromeXHRDocURI;
7252 // readonly attribute DOMString compatMode;
7253 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
7254 // in almost standards or full standards mode. See bug 105640. This was
7255 // implemented to match MSIE's compatMode property.
7256 NS_IMETHODIMP
7257 nsDocument::GetCompatMode(nsAString& aCompatMode)
7259 nsString temp;
7260 nsIDocument::GetCompatMode(temp);
7261 aCompatMode = temp;
7262 return NS_OK;
7265 void
7266 nsIDocument::GetCompatMode(nsString& aCompatMode) const
7268 NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
7269 mCompatMode == eCompatibility_AlmostStandards ||
7270 mCompatMode == eCompatibility_FullStandards,
7271 "mCompatMode is neither quirks nor strict for this document");
7273 if (mCompatMode == eCompatibility_NavQuirks) {
7274 aCompatMode.AssignLiteral("BackCompat");
7275 } else {
7276 aCompatMode.AssignLiteral("CSS1Compat");
7280 static void BlastSubtreeToPieces(nsINode *aNode);
7282 PLDHashOperator
7283 BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg)
7285 nsCOMPtr<nsIAttribute> *attr =
7286 static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
7288 *attr = aData;
7290 NS_ASSERTION(attr->get(),
7291 "non-nsIAttribute somehow made it into the hashmap?!");
7293 return PL_DHASH_STOP;
7296 static void
7297 BlastSubtreeToPieces(nsINode *aNode)
7299 if (aNode->IsElement()) {
7300 Element *element = aNode->AsElement();
7301 const nsDOMAttributeMap *map = element->GetAttributeMap();
7302 if (map) {
7303 nsCOMPtr<nsIAttribute> attr;
7304 while (map->Enumerate(BlastFunc, &attr) > 0) {
7305 BlastSubtreeToPieces(attr);
7307 #ifdef DEBUG
7308 nsresult rv =
7309 #endif
7310 element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
7311 attr->NodeInfo()->NameAtom(),
7312 false);
7314 // XXX Should we abort here?
7315 NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!");
7320 uint32_t count = aNode->GetChildCount();
7321 for (uint32_t i = 0; i < count; ++i) {
7322 BlastSubtreeToPieces(aNode->GetFirstChild());
7323 aNode->RemoveChildAt(0, false);
7328 class nsUserDataCaller : public nsRunnable
7330 public:
7331 nsUserDataCaller(nsCOMArray<nsINode>& aNodesWithProperties,
7332 nsIDocument* aOwnerDoc)
7333 : mNodesWithProperties(aNodesWithProperties),
7334 mOwnerDoc(aOwnerDoc)
7338 NS_IMETHOD Run()
7340 nsNodeUtils::CallUserDataHandlers(mNodesWithProperties, mOwnerDoc,
7341 nsIDOMUserDataHandler::NODE_ADOPTED,
7342 false);
7343 return NS_OK;
7346 private:
7347 nsCOMArray<nsINode> mNodesWithProperties;
7348 nsCOMPtr<nsIDocument> mOwnerDoc;
7351 NS_IMETHODIMP
7352 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
7354 *aResult = nullptr;
7356 nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
7357 NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED);
7359 ErrorResult rv;
7360 nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv);
7361 if (rv.Failed()) {
7362 return rv.ErrorCode();
7365 NS_ADDREF(*aResult = result->AsDOMNode());
7366 return NS_OK;
7369 nsINode*
7370 nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
7372 nsINode* adoptedNode = &aAdoptedNode;
7374 // Scope firing mutation events so that we don't carry any state that
7375 // might be stale
7377 nsINode* parent = adoptedNode->GetParentNode();
7378 if (parent) {
7379 nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
7380 adoptedNode->OwnerDoc());
7384 nsAutoScriptBlocker scriptBlocker;
7386 switch (adoptedNode->NodeType()) {
7387 case nsIDOMNode::ATTRIBUTE_NODE:
7389 // Remove from ownerElement.
7390 nsRefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
7392 nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
7393 if (rv.Failed()) {
7394 return nullptr;
7397 if (ownerElement) {
7398 nsRefPtr<Attr> newAttr =
7399 ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
7400 if (rv.Failed()) {
7401 return nullptr;
7404 newAttr.swap(adoptedAttr);
7407 break;
7409 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
7410 case nsIDOMNode::ELEMENT_NODE:
7411 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
7412 case nsIDOMNode::TEXT_NODE:
7413 case nsIDOMNode::CDATA_SECTION_NODE:
7414 case nsIDOMNode::COMMENT_NODE:
7415 case nsIDOMNode::DOCUMENT_TYPE_NODE:
7417 // Don't allow adopting a node's anonymous subtree out from under it.
7418 if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
7419 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7420 return nullptr;
7423 // We don't want to adopt an element into its own contentDocument or into
7424 // a descendant contentDocument, so we check if the frameElement of this
7425 // document or any of its parents is the adopted node or one of its
7426 // descendants.
7427 nsIDocument *doc = this;
7428 do {
7429 nsPIDOMWindow *win = doc->GetWindow();
7430 if (win) {
7431 nsCOMPtr<nsINode> node = win->GetFrameElementInternal();
7432 if (node &&
7433 nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
7434 rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7435 return nullptr;
7438 } while ((doc = doc->GetParentDocument()));
7440 // Remove from parent.
7441 nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
7442 if (parent) {
7443 int32_t idx = parent->IndexOf(adoptedNode);
7444 MOZ_ASSERT(idx >= 0);
7445 parent->RemoveChildAt(idx, true);
7446 } else {
7447 MOZ_ASSERT(!adoptedNode->IsInDoc());
7449 // If we're adopting a node that's not in a document, it might still
7450 // have a binding applied. Remove the binding from the element now
7451 // that it's getting adopted into a new document.
7452 // TODO Fully tear down the binding.
7453 adoptedNode->AsContent()->SetXBLBinding(nullptr);
7456 break;
7458 case nsIDOMNode::DOCUMENT_NODE:
7460 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7461 return nullptr;
7463 default:
7465 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
7467 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7468 return nullptr;
7472 nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
7473 bool sameDocument = oldDocument == this;
7475 AutoJSContext cx;
7476 JS::Rooted<JSObject*> newScope(cx, nullptr);
7477 if (!sameDocument) {
7478 newScope = GetWrapper();
7479 if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
7480 // Make sure cx is in a semi-sane compartment before we call WrapNative.
7481 // It's kind of irrelevant, given that we're passing aAllowWrapping =
7482 // false, and documents should always insist on being wrapped in an
7483 // canonical scope. But we try to pass something sane anyway.
7484 JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject());
7485 JS::Rooted<JS::Value> v(cx);
7486 rv = nsContentUtils::WrapNative(cx, this, this, &v,
7487 /* aAllowWrapping = */ false);
7488 if (rv.Failed())
7489 return nullptr;
7490 newScope = &v.toObject();
7494 nsCOMArray<nsINode> nodesWithProperties;
7495 rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
7496 newScope, nodesWithProperties);
7497 if (rv.Failed()) {
7498 // Disconnect all nodes from their parents, since some have the old document
7499 // as their ownerDocument and some have this as their ownerDocument.
7500 BlastSubtreeToPieces(adoptedNode);
7502 if (!sameDocument && oldDocument) {
7503 uint32_t count = nodesWithProperties.Count();
7504 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7505 for (uint32_t i = 0; i < count; ++i) {
7506 // Remove all properties.
7507 oldDocument->PropertyTable(j)->
7508 DeleteAllPropertiesFor(nodesWithProperties[i]);
7513 return nullptr;
7516 uint32_t count = nodesWithProperties.Count();
7517 if (!sameDocument && oldDocument) {
7518 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7519 nsPropertyTable *oldTable = oldDocument->PropertyTable(j);
7520 nsPropertyTable *newTable = PropertyTable(j);
7521 for (uint32_t i = 0; i < count; ++i) {
7522 rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
7523 newTable);
7527 if (rv.Failed()) {
7528 // Disconnect all nodes from their parents.
7529 BlastSubtreeToPieces(adoptedNode);
7531 return nullptr;
7535 if (nodesWithProperties.Count()) {
7536 nsContentUtils::AddScriptRunner(new nsUserDataCaller(nodesWithProperties,
7537 this));
7540 NS_ASSERTION(adoptedNode->OwnerDoc() == this,
7541 "Should still be in the document we just got adopted into");
7543 return adoptedNode;
7546 nsViewportInfo
7547 nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
7549 // Compute the CSS-to-LayoutDevice pixel scale as the product of the
7550 // widget scale and the full zoom.
7551 nsPresContext* context = mPresShell->GetPresContext();
7552 float fullZoom = context ? context->GetFullZoom() : 1.0;
7553 fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
7554 nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
7555 float widgetScale = widget ? widget->GetDefaultScale().scale : 1.0f;
7556 CSSToLayoutDeviceScale layoutDeviceScale(widgetScale * fullZoom);
7558 CSSToScreenScale defaultScale = layoutDeviceScale
7559 * LayoutDeviceToScreenScale(1.0);
7561 // In cases where the width of the CSS viewport is less than or equal to the width
7562 // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
7563 // behaviour. See bug 941995 for details.
7565 switch (mViewportType) {
7566 case DisplayWidthHeight:
7567 return nsViewportInfo(aDisplaySize,
7568 defaultScale,
7569 /*allowZoom*/ true,
7570 /*allowDoubleTapZoom*/ true);
7571 case DisplayWidthHeightNoZoom:
7572 return nsViewportInfo(aDisplaySize,
7573 defaultScale,
7574 /*allowZoom*/ false,
7575 /*allowDoubleTapZoom*/ false);
7576 case Unknown:
7578 nsAutoString viewport;
7579 GetHeaderData(nsGkAtoms::viewport, viewport);
7580 if (viewport.IsEmpty()) {
7581 // If the docType specifies that we are on a site optimized for mobile,
7582 // then we want to return specially crafted defaults for the viewport info.
7583 nsCOMPtr<nsIDOMDocumentType> docType;
7584 nsresult rv = GetDoctype(getter_AddRefs(docType));
7585 if (NS_SUCCEEDED(rv) && docType) {
7586 nsAutoString docId;
7587 rv = docType->GetPublicId(docId);
7588 if (NS_SUCCEEDED(rv)) {
7589 if ((docId.Find("WAP") != -1) ||
7590 (docId.Find("Mobile") != -1) ||
7591 (docId.Find("WML") != -1))
7593 // We're making an assumption that the docType can't change here
7594 mViewportType = DisplayWidthHeight;
7595 return nsViewportInfo(aDisplaySize,
7596 defaultScale,
7597 /*allowZoom*/true,
7598 /*allowDoubleTapZoom*/false);
7603 nsAutoString handheldFriendly;
7604 GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
7605 if (handheldFriendly.EqualsLiteral("true")) {
7606 mViewportType = DisplayWidthHeight;
7607 return nsViewportInfo(aDisplaySize,
7608 defaultScale,
7609 /*allowZoom*/true,
7610 /*allowDoubleTapZoom*/false);
7614 nsAutoString minScaleStr;
7615 GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
7617 nsresult errorCode;
7618 mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
7620 if (NS_FAILED(errorCode)) {
7621 mScaleMinFloat = kViewportMinScale;
7624 mScaleMinFloat = mozilla::clamped(
7625 mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
7627 nsAutoString maxScaleStr;
7628 GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
7630 // We define a special error code variable for the scale and max scale,
7631 // because they are used later (see the width calculations).
7632 nsresult scaleMaxErrorCode;
7633 mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
7635 if (NS_FAILED(scaleMaxErrorCode)) {
7636 mScaleMaxFloat = kViewportMaxScale;
7639 mScaleMaxFloat = mozilla::clamped(
7640 mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
7642 nsAutoString scaleStr;
7643 GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
7645 nsresult scaleErrorCode;
7646 mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
7648 nsAutoString widthStr, heightStr;
7650 GetHeaderData(nsGkAtoms::viewport_height, heightStr);
7651 GetHeaderData(nsGkAtoms::viewport_width, widthStr);
7653 mAutoSize = false;
7655 if (widthStr.EqualsLiteral("device-width")) {
7656 mAutoSize = true;
7659 if (widthStr.IsEmpty() &&
7660 (heightStr.EqualsLiteral("device-height") ||
7661 (mScaleFloat.scale == 1.0)))
7663 mAutoSize = true;
7666 nsresult widthErrorCode, heightErrorCode;
7667 mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
7668 mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
7670 // If width or height has not been set to a valid number by this point,
7671 // fall back to a default value.
7672 mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
7673 mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
7675 mAllowZoom = true;
7676 nsAutoString userScalable;
7677 GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
7679 if ((userScalable.EqualsLiteral("0")) ||
7680 (userScalable.EqualsLiteral("no")) ||
7681 (userScalable.EqualsLiteral("false"))) {
7682 mAllowZoom = false;
7684 mAllowDoubleTapZoom = mAllowZoom;
7686 mScaleStrEmpty = scaleStr.IsEmpty();
7687 mWidthStrEmpty = widthStr.IsEmpty();
7688 mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
7689 mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
7691 mViewportType = Specified;
7693 case Specified:
7694 default:
7695 CSSSize size = mViewportSize;
7697 if (!mValidWidth) {
7698 if (mValidHeight && !aDisplaySize.IsEmpty()) {
7699 size.width = size.height * aDisplaySize.width / aDisplaySize.height;
7700 } else {
7701 // Stretch CSS pixel size of viewport to keep device pixel size
7702 // unchanged after full zoom applied.
7703 // See bug 974242.
7704 size.width = Preferences::GetInt("browser.viewport.desktopWidth",
7705 kViewportDefaultScreenWidth) / fullZoom;
7709 if (!mValidHeight) {
7710 if (!aDisplaySize.IsEmpty()) {
7711 size.height = size.width * aDisplaySize.height / aDisplaySize.width;
7712 } else {
7713 size.height = size.width;
7717 CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
7718 CSSToScreenScale scaleMinFloat = mScaleMinFloat * layoutDeviceScale;
7719 CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * layoutDeviceScale;
7721 if (mAutoSize) {
7722 // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
7723 CSSToScreenScale defaultPixelScale = layoutDeviceScale * LayoutDeviceToScreenScale(1.0f);
7724 size = ScreenSize(aDisplaySize) / defaultPixelScale;
7727 size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width));
7729 // Also recalculate the default zoom, if it wasn't specified in the metadata,
7730 // and the width is specified.
7731 if (mScaleStrEmpty && !mWidthStrEmpty) {
7732 CSSToScreenScale defaultScale(float(aDisplaySize.width) / size.width);
7733 scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
7736 size.height = clamped(size.height, float(kViewportMinSize.height), float(kViewportMaxSize.height));
7738 // We need to perform a conversion, but only if the initial or maximum
7739 // scale were set explicitly by the user.
7740 if (mValidScaleFloat) {
7741 CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
7742 size.width = std::max(size.width, displaySize.width);
7743 size.height = std::max(size.height, displaySize.height);
7744 } else if (mValidMaxScale) {
7745 CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
7746 size.width = std::max(size.width, displaySize.width);
7747 size.height = std::max(size.height, displaySize.height);
7750 return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
7751 mAutoSize, mAllowZoom, mAllowDoubleTapZoom);
7755 EventListenerManager*
7756 nsDocument::GetOrCreateListenerManager()
7758 if (!mListenerManager) {
7759 mListenerManager =
7760 new EventListenerManager(static_cast<EventTarget*>(this));
7761 SetFlags(NODE_HAS_LISTENERMANAGER);
7764 return mListenerManager;
7767 EventListenerManager*
7768 nsDocument::GetExistingListenerManager() const
7770 return mListenerManager;
7773 nsresult
7774 nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor)
7776 aVisitor.mCanHandle = true;
7777 // FIXME! This is a hack to make middle mouse paste working also in Editor.
7778 // Bug 329119
7779 aVisitor.mForceContentDispatch = true;
7781 // Load events must not propagate to |window| object, see bug 335251.
7782 if (aVisitor.mEvent->message != NS_LOAD) {
7783 nsGlobalWindow* window = static_cast<nsGlobalWindow*>(GetWindow());
7784 aVisitor.mParentTarget =
7785 window ? window->GetTargetForEventTargetChain() : nullptr;
7787 return NS_OK;
7790 NS_IMETHODIMP
7791 nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
7793 NS_ENSURE_ARG_POINTER(aReturn);
7794 ErrorResult rv;
7795 *aReturn = nsIDocument::CreateEvent(aEventType, rv).take();
7796 return rv.ErrorCode();
7799 already_AddRefed<Event>
7800 nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
7802 nsIPresShell *shell = GetShell();
7804 nsPresContext *presContext = nullptr;
7806 if (shell) {
7807 // Retrieve the context
7808 presContext = shell->GetPresContext();
7811 // Create event even without presContext.
7812 nsCOMPtr<nsIDOMEvent> ev;
7813 rv = EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this),
7814 presContext, nullptr, aEventType,
7815 getter_AddRefs(ev));
7816 if (!ev) {
7817 return nullptr;
7819 WidgetEvent* e = ev->GetInternalNSEvent();
7820 e->mFlags.mBubbles = false;
7821 e->mFlags.mCancelable = false;
7822 return dont_AddRef(ev.forget().take()->InternalDOMEvent());
7825 void
7826 nsDocument::FlushPendingNotifications(mozFlushType aType)
7828 nsDocumentOnStack dos(this);
7830 // We need to flush the sink for non-HTML documents (because the XML
7831 // parser still does insertion with deferred notifications). We
7832 // also need to flush the sink if this is a layout-related flush, to
7833 // make sure that layout is started as needed. But we can skip that
7834 // part if we have no presshell or if it's already done an initial
7835 // reflow.
7836 if ((!IsHTML() ||
7837 (aType > Flush_ContentAndNotify && mPresShell &&
7838 !mPresShell->DidInitialize())) &&
7839 (mParser || mWeakSink)) {
7840 nsCOMPtr<nsIContentSink> sink;
7841 if (mParser) {
7842 sink = mParser->GetContentSink();
7843 } else {
7844 sink = do_QueryReferent(mWeakSink);
7845 if (!sink) {
7846 mWeakSink = nullptr;
7849 // Determine if it is safe to flush the sink notifications
7850 // by determining if it safe to flush all the presshells.
7851 if (sink && (aType == Flush_Content || IsSafeToFlush())) {
7852 sink->FlushPendingNotifications(aType);
7856 // Should we be flushing pending binding constructors in here?
7858 if (aType <= Flush_ContentAndNotify) {
7859 // Nothing to do here
7860 return;
7863 // If we have a parent we must flush the parent too to ensure that our
7864 // container is reflowed if its size was changed. But if it's not safe to
7865 // flush ourselves, then don't flush the parent, since that can cause things
7866 // like resizes of our frame's widget, which we can't handle while flushing
7867 // is unsafe.
7868 // Since media queries mean that a size change of our container can
7869 // affect style, we need to promote a style flush on ourself to a
7870 // layout flush on our parent, since we need our container to be the
7871 // correct size to determine the correct style.
7872 if (mParentDocument && IsSafeToFlush()) {
7873 mozFlushType parentType = aType;
7874 if (aType >= Flush_Style)
7875 parentType = std::max(Flush_Layout, aType);
7876 mParentDocument->FlushPendingNotifications(parentType);
7879 // We can optimize away getting our presshell and calling
7880 // FlushPendingNotifications on it if we don't need a flush of the sort we're
7881 // looking at. The one exception is if mInFlush is true, because in that
7882 // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false
7883 // already but the presshell hasn't actually done the corresponding work yet.
7884 // So if mInFlush and reentering this code, we need to flush the presshell.
7885 if (mNeedStyleFlush ||
7886 (mNeedLayoutFlush && aType >= Flush_InterruptibleLayout) ||
7887 aType >= Flush_Display ||
7888 mInFlush) {
7889 nsCOMPtr<nsIPresShell> shell = GetShell();
7890 if (shell) {
7891 mNeedStyleFlush = false;
7892 mNeedLayoutFlush = mNeedLayoutFlush && (aType < Flush_InterruptibleLayout);
7893 // mInFlush is a bitfield, so can't us AutoRestore here. But we
7894 // need to keep track of multi-level reentry correctly, so need
7895 // to restore the old mInFlush value.
7896 bool oldInFlush = mInFlush;
7897 mInFlush = true;
7898 shell->FlushPendingNotifications(aType);
7899 mInFlush = oldInFlush;
7904 static bool
7905 Copy(nsIDocument* aDocument, void* aData)
7907 nsTArray<nsCOMPtr<nsIDocument> >* resources =
7908 static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
7909 resources->AppendElement(aDocument);
7910 return true;
7913 void
7914 nsDocument::FlushExternalResources(mozFlushType aType)
7916 NS_ASSERTION(aType >= Flush_Style,
7917 "should only need to flush for style or higher in external resources");
7918 if (GetDisplayDocument()) {
7919 return;
7921 nsTArray<nsCOMPtr<nsIDocument> > resources;
7922 EnumerateExternalResources(Copy, &resources);
7924 for (uint32_t i = 0; i < resources.Length(); i++) {
7925 resources[i]->FlushPendingNotifications(aType);
7929 void
7930 nsDocument::SetXMLDeclaration(const char16_t *aVersion,
7931 const char16_t *aEncoding,
7932 const int32_t aStandalone)
7934 if (!aVersion || *aVersion == '\0') {
7935 mXMLDeclarationBits = 0;
7936 return;
7939 mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
7941 if (aEncoding && *aEncoding != '\0') {
7942 mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
7945 if (aStandalone == 1) {
7946 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
7947 XML_DECLARATION_BITS_STANDALONE_YES;
7949 else if (aStandalone == 0) {
7950 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
7954 void
7955 nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
7956 nsAString& aStandalone)
7958 aVersion.Truncate();
7959 aEncoding.Truncate();
7960 aStandalone.Truncate();
7962 if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
7963 return;
7966 // always until we start supporting 1.1 etc.
7967 aVersion.AssignLiteral("1.0");
7969 if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
7970 // This is what we have stored, not necessarily what was written
7971 // in the original
7972 GetCharacterSet(aEncoding);
7975 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
7976 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
7977 aStandalone.AssignLiteral("yes");
7978 } else {
7979 aStandalone.AssignLiteral("no");
7984 bool
7985 nsDocument::IsScriptEnabled()
7987 // If this document is sandboxed without 'allow-scripts'
7988 // script is not enabled
7989 if (mSandboxFlags & SANDBOXED_SCRIPTS) {
7990 return false;
7993 nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
7994 NS_ENSURE_TRUE(sm, false);
7996 nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
7997 if (!globalObject && mMasterDocument) {
7998 globalObject = do_QueryInterface(mMasterDocument->GetInnerWindow());
8000 NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false);
8002 return sm->ScriptAllowed(globalObject->GetGlobalJSObject());
8005 nsRadioGroupStruct*
8006 nsDocument::GetRadioGroupInternal(const nsAString& aName) const
8008 #ifdef DEBUG
8009 if (IsHTML()) {
8010 nsAutoString lcName;
8011 ToLowerCase(aName, lcName);
8012 MOZ_ASSERT(aName == lcName);
8014 #endif
8016 nsRadioGroupStruct* radioGroup;
8017 if (!mRadioGroups.Get(aName, &radioGroup)) {
8018 return nullptr;
8021 return radioGroup;
8024 nsRadioGroupStruct*
8025 nsDocument::GetRadioGroup(const nsAString& aName) const
8027 nsAutoString tmKey(aName);
8028 if (IsHTML()) {
8029 ToLowerCase(tmKey); //should case-insensitive.
8032 return GetRadioGroupInternal(tmKey);
8035 nsRadioGroupStruct*
8036 nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
8038 nsAutoString tmKey(aName);
8039 if (IsHTML()) {
8040 ToLowerCase(tmKey); //should case-insensitive.
8043 if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) {
8044 return radioGroup;
8047 nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct());
8048 mRadioGroups.Put(tmKey, newRadioGroup);
8050 return newRadioGroup.forget();
8053 void
8054 nsDocument::SetCurrentRadioButton(const nsAString& aName,
8055 HTMLInputElement* aRadio)
8057 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8058 radioGroup->mSelectedRadioButton = aRadio;
8061 HTMLInputElement*
8062 nsDocument::GetCurrentRadioButton(const nsAString& aName)
8064 return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
8067 NS_IMETHODIMP
8068 nsDocument::GetNextRadioButton(const nsAString& aName,
8069 const bool aPrevious,
8070 HTMLInputElement* aFocusedRadio,
8071 HTMLInputElement** aRadioOut)
8073 // XXX Can we combine the HTML radio button method impls of
8074 // nsDocument and nsHTMLFormControl?
8075 // XXX Why is HTML radio button stuff in nsDocument, as
8076 // opposed to nsHTMLDocument?
8077 *aRadioOut = nullptr;
8079 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8081 // Return the radio button relative to the focused radio button.
8082 // If no radio is focused, get the radio relative to the selected one.
8083 nsRefPtr<HTMLInputElement> currentRadio;
8084 if (aFocusedRadio) {
8085 currentRadio = aFocusedRadio;
8087 else {
8088 currentRadio = radioGroup->mSelectedRadioButton;
8089 if (!currentRadio) {
8090 return NS_ERROR_FAILURE;
8093 int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
8094 if (index < 0) {
8095 return NS_ERROR_FAILURE;
8098 int32_t numRadios = radioGroup->mRadioButtons.Count();
8099 nsRefPtr<HTMLInputElement> radio;
8100 do {
8101 if (aPrevious) {
8102 if (--index < 0) {
8103 index = numRadios -1;
8106 else if (++index >= numRadios) {
8107 index = 0;
8109 NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTML(nsGkAtoms::input),
8110 "mRadioButtons holding a non-radio button");
8111 radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
8112 } while (radio->Disabled() && radio != currentRadio);
8114 radio.forget(aRadioOut);
8115 return NS_OK;
8118 void
8119 nsDocument::AddToRadioGroup(const nsAString& aName,
8120 nsIFormControl* aRadio)
8122 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8123 radioGroup->mRadioButtons.AppendObject(aRadio);
8125 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8126 NS_ASSERTION(element, "radio controls have to be content elements");
8127 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8128 radioGroup->mRequiredRadioCount++;
8132 void
8133 nsDocument::RemoveFromRadioGroup(const nsAString& aName,
8134 nsIFormControl* aRadio)
8136 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8137 radioGroup->mRadioButtons.RemoveObject(aRadio);
8139 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8140 NS_ASSERTION(element, "radio controls have to be content elements");
8141 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8142 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8143 "mRequiredRadioCount about to wrap below 0!");
8144 radioGroup->mRequiredRadioCount--;
8148 NS_IMETHODIMP
8149 nsDocument::WalkRadioGroup(const nsAString& aName,
8150 nsIRadioVisitor* aVisitor,
8151 bool aFlushContent)
8153 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8155 for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
8156 if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
8157 return NS_OK;
8161 return NS_OK;
8164 uint32_t
8165 nsDocument::GetRequiredRadioCount(const nsAString& aName) const
8167 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8168 return radioGroup ? radioGroup->mRequiredRadioCount : 0;
8171 void
8172 nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio)
8174 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8176 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8177 NS_ASSERTION(element, "radio controls have to be content elements");
8178 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8179 radioGroup->mRequiredRadioCount++;
8180 } else {
8181 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8182 "mRequiredRadioCount about to wrap below 0!");
8183 radioGroup->mRequiredRadioCount--;
8187 bool
8188 nsDocument::GetValueMissingState(const nsAString& aName) const
8190 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8191 return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
8194 void
8195 nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
8197 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8198 radioGroup->mGroupSuffersFromValueMissing = aValue;
8201 void
8202 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
8204 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8205 PRTime modDate = 0;
8206 nsresult rv;
8208 if (httpChannel) {
8209 nsAutoCString tmp;
8210 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
8211 tmp);
8213 if (NS_SUCCEEDED(rv)) {
8214 PRTime time;
8215 PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
8216 if (st == PR_SUCCESS) {
8217 modDate = time;
8221 // The misspelled key 'referer' is as per the HTTP spec
8222 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
8223 mReferrer);
8224 if (NS_FAILED(rv)) {
8225 mReferrer.Truncate();
8228 static const char *const headers[] = {
8229 "default-style",
8230 "content-style-type",
8231 "content-language",
8232 "content-disposition",
8233 "refresh",
8234 "x-dns-prefetch-control",
8235 "x-frame-options",
8236 // add more http headers if you need
8237 // XXXbz don't add content-location support without reading bug
8238 // 238654 and its dependencies/dups first.
8242 nsAutoCString headerVal;
8243 const char *const *name = headers;
8244 while (*name) {
8245 rv =
8246 httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
8247 if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
8248 nsCOMPtr<nsIAtom> key = do_GetAtom(*name);
8249 SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
8251 ++name;
8253 } else {
8254 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
8255 if (fileChannel) {
8256 nsCOMPtr<nsIFile> file;
8257 fileChannel->GetFile(getter_AddRefs(file));
8258 if (file) {
8259 PRTime msecs;
8260 rv = file->GetLastModifiedTime(&msecs);
8262 if (NS_SUCCEEDED(rv)) {
8263 modDate = msecs * int64_t(PR_USEC_PER_MSEC);
8266 } else {
8267 nsAutoCString contentDisp;
8268 rv = aChannel->GetContentDispositionHeader(contentDisp);
8269 if (NS_SUCCEEDED(rv)) {
8270 SetHeaderData(nsGkAtoms::headerContentDisposition,
8271 NS_ConvertASCIItoUTF16(contentDisp));
8276 if (modDate == 0) {
8277 // We got nothing from our attempt to ask nsIFileChannel and
8278 // nsIHttpChannel for the last modified time. Return the current
8279 // time.
8280 modDate = PR_Now();
8283 mLastModified.Truncate();
8284 if (modDate != 0) {
8285 PRExplodedTime prtime;
8286 PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime);
8287 // "MM/DD/YYYY hh:mm:ss"
8288 char formatedTime[24];
8289 if (PR_snprintf(formatedTime, sizeof(formatedTime),
8290 "%02ld/%02ld/%04hd %02ld:%02ld:%02ld",
8291 prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year,
8292 prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
8293 CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified);
8298 nsresult
8299 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix, int32_t aNamespaceID,
8300 nsIContent **aResult)
8302 #ifdef DEBUG
8303 nsAutoString qName;
8304 if (aPrefix) {
8305 aPrefix->ToString(qName);
8306 qName.Append(':');
8308 qName.Append(aName);
8310 // Note: "a:b:c" is a valid name in non-namespaces XML, and
8311 // nsDocument::CreateElement can call us with such a name and no prefix,
8312 // which would cause an error if we just used true here.
8313 bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
8314 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
8315 "Don't pass invalid prefixes to nsDocument::CreateElem, "
8316 "check caller.");
8317 #endif
8319 *aResult = nullptr;
8321 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
8322 mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
8323 nsIDOMNode::ELEMENT_NODE,
8324 getter_AddRefs(nodeInfo));
8325 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
8327 nsCOMPtr<Element> element;
8328 nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
8329 NOT_FROM_PARSER);
8330 element.forget(aResult);
8331 return rv;
8334 bool
8335 nsDocument::IsSafeToFlush() const
8337 nsIPresShell* shell = GetShell();
8338 if (!shell)
8339 return true;
8341 return shell->IsSafeToFlush();
8344 void
8345 nsDocument::Sanitize()
8347 // Sanitize the document by resetting all password fields and any form
8348 // fields with autocomplete=off to their default values. We do this now,
8349 // instead of when the presentation is restored, to offer some protection
8350 // in case there is ever an exploit that allows a cached document to be
8351 // accessed from a different document.
8353 // First locate all input elements, regardless of whether they are
8354 // in a form, and reset the password and autocomplete=off elements.
8356 nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
8358 nsCOMPtr<nsIContent> item;
8359 nsAutoString value;
8361 uint32_t length = nodes->Length(true);
8362 for (uint32_t i = 0; i < length; ++i) {
8363 NS_ASSERTION(nodes->Item(i), "null item in node list!");
8365 nsRefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i));
8366 if (!input)
8367 continue;
8369 bool resetValue = false;
8371 input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
8372 if (value.LowerCaseEqualsLiteral("off")) {
8373 resetValue = true;
8374 } else {
8375 input->GetType(value);
8376 if (value.LowerCaseEqualsLiteral("password"))
8377 resetValue = true;
8380 if (resetValue) {
8381 input->Reset();
8385 // Now locate all _form_ elements that have autocomplete=off and reset them
8386 nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
8388 length = nodes->Length(true);
8389 for (uint32_t i = 0; i < length; ++i) {
8390 NS_ASSERTION(nodes->Item(i), "null item in nodelist");
8392 nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i));
8393 if (!form)
8394 continue;
8396 nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None,
8397 nsGkAtoms::autocomplete, value);
8398 if (value.LowerCaseEqualsLiteral("off"))
8399 form->Reset();
8403 struct SubDocEnumArgs
8405 nsIDocument::nsSubDocEnumFunc callback;
8406 void *data;
8409 static PLDHashOperator
8410 SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr,
8411 uint32_t number, void *arg)
8413 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
8414 SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg);
8416 nsIDocument *subdoc = entry->mSubDocument;
8417 bool next = subdoc ? args->callback(subdoc, args->data) : true;
8419 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
8422 void
8423 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
8425 if (mSubDocuments) {
8426 SubDocEnumArgs args = { aCallback, aData };
8427 PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args);
8431 static PLDHashOperator
8432 CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr,
8433 uint32_t number, void *arg)
8435 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
8436 bool *canCacheArg = static_cast<bool*>(arg);
8438 nsIDocument *subdoc = entry->mSubDocument;
8440 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
8441 bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
8442 if (!canCache) {
8443 *canCacheArg = false;
8444 return PL_DHASH_STOP;
8447 return PL_DHASH_NEXT;
8450 #ifdef DEBUG_bryner
8451 #define DEBUG_PAGE_CACHE
8452 #endif
8454 bool
8455 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
8457 if (EventHandlingSuppressed()) {
8458 return false;
8461 nsPIDOMWindow* win = GetInnerWindow();
8462 if (win && win->TimeoutSuspendCount()) {
8463 return false;
8466 // Check our event listener manager for unload/beforeunload listeners.
8467 nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
8468 if (piTarget) {
8469 EventListenerManager* manager = piTarget->GetExistingListenerManager();
8470 if (manager && manager->HasUnloadListeners()) {
8471 return false;
8475 // Check if we have pending network requests
8476 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8477 if (loadGroup) {
8478 nsCOMPtr<nsISimpleEnumerator> requests;
8479 loadGroup->GetRequests(getter_AddRefs(requests));
8481 bool hasMore = false;
8483 // We want to bail out if we have any requests other than aNewRequest (or
8484 // in the case when aNewRequest is a part of a multipart response the base
8485 // channel the multipart response is coming in on).
8486 nsCOMPtr<nsIChannel> baseChannel;
8487 nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
8488 if (part) {
8489 part->GetBaseChannel(getter_AddRefs(baseChannel));
8492 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8493 nsCOMPtr<nsISupports> elem;
8494 requests->GetNext(getter_AddRefs(elem));
8496 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8497 if (request && request != aNewRequest && request != baseChannel) {
8498 #ifdef DEBUG_PAGE_CACHE
8499 nsAutoCString requestName, docSpec;
8500 request->GetName(requestName);
8501 if (mDocumentURI)
8502 mDocumentURI->GetSpec(docSpec);
8504 printf("document %s has request %s\n",
8505 docSpec.get(), requestName.get());
8506 #endif
8507 return false;
8512 // Check if we have running offline storage transactions
8513 quota::QuotaManager* quotaManager =
8514 win ? quota::QuotaManager::Get() : nullptr;
8515 if (quotaManager && quotaManager->HasOpenTransactions(win)) {
8516 return false;
8519 #ifdef MOZ_MEDIA_NAVIGATOR
8520 // Check if we have active GetUserMedia use
8521 if (MediaManager::Exists() && win &&
8522 MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
8523 return false;
8525 #endif // MOZ_MEDIA_NAVIGATOR
8527 #ifdef MOZ_WEBRTC
8528 // Check if we have active PeerConnections
8529 nsCOMPtr<IPeerConnectionManager> pcManager =
8530 do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
8532 if (pcManager && win) {
8533 bool active;
8534 pcManager->HasActivePeerConnection(win->WindowID(), &active);
8535 if (active) {
8536 return false;
8539 #endif // MOZ_WEBRTC
8541 #ifdef MOZ_EME
8542 // Don't save presentations for documents containing EME content, so that
8543 // CDMs reliably shutdown upon user navigation.
8544 if (ContainsEMEContent()) {
8545 return false;
8547 #endif
8549 bool canCache = true;
8550 if (mSubDocuments)
8551 PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache);
8553 return canCache;
8556 void
8557 nsDocument::Destroy()
8559 // The ContentViewer wants to release the document now. So, tell our content
8560 // to drop any references to the document so that it can be destroyed.
8561 if (mIsGoingAway)
8562 return;
8564 mIsGoingAway = true;
8566 RemovedFromDocShell();
8568 bool oldVal = mInUnlinkOrDeletion;
8569 mInUnlinkOrDeletion = true;
8570 uint32_t i, count = mChildren.ChildCount();
8571 for (i = 0; i < count; ++i) {
8572 mChildren.ChildAt(i)->DestroyContent();
8574 mInUnlinkOrDeletion = oldVal;
8576 mLayoutHistoryState = nullptr;
8578 // Shut down our external resource map. We might not need this for
8579 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
8580 // tearing down all those frame trees right now is the right thing to do.
8581 mExternalResourceMap.Shutdown();
8583 mRegistry = nullptr;
8585 nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
8586 if (swm) {
8587 swm->MaybeStopControlling(this);
8590 // XXX We really should let cycle collection do this, but that currently still
8591 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
8592 ReleaseWrapper(static_cast<nsINode*>(this));
8595 void
8596 nsDocument::RemovedFromDocShell()
8598 if (mRemovedFromDocShell)
8599 return;
8601 mRemovedFromDocShell = true;
8602 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8604 uint32_t i, count = mChildren.ChildCount();
8605 for (i = 0; i < count; ++i) {
8606 mChildren.ChildAt(i)->SaveSubtreeState();
8610 already_AddRefed<nsILayoutHistoryState>
8611 nsDocument::GetLayoutHistoryState() const
8613 nsCOMPtr<nsILayoutHistoryState> state;
8614 if (!mScriptGlobalObject) {
8615 state = mLayoutHistoryState;
8616 } else {
8617 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
8618 if (docShell) {
8619 docShell->GetLayoutHistoryState(getter_AddRefs(state));
8623 return state.forget();
8626 void
8627 nsDocument::EnsureOnloadBlocker()
8629 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8630 // -- it's not ours.
8631 if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
8632 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8633 if (loadGroup) {
8634 // Check first to see if mOnloadBlocker is in the loadgroup.
8635 nsCOMPtr<nsISimpleEnumerator> requests;
8636 loadGroup->GetRequests(getter_AddRefs(requests));
8638 bool hasMore = false;
8639 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8640 nsCOMPtr<nsISupports> elem;
8641 requests->GetNext(getter_AddRefs(elem));
8642 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8643 if (request && request == mOnloadBlocker) {
8644 return;
8648 // Not in the loadgroup, so add it.
8649 loadGroup->AddRequest(mOnloadBlocker, nullptr);
8654 void
8655 nsDocument::AsyncBlockOnload()
8657 while (mAsyncOnloadBlockCount) {
8658 --mAsyncOnloadBlockCount;
8659 BlockOnload();
8663 void
8664 nsDocument::BlockOnload()
8666 if (mDisplayDocument) {
8667 mDisplayDocument->BlockOnload();
8668 return;
8671 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8672 // -- it's not ours.
8673 if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
8674 if (!nsContentUtils::IsSafeToRunScript()) {
8675 // Because AddRequest may lead to OnStateChange calls in chrome,
8676 // block onload only when there are no script blockers.
8677 ++mAsyncOnloadBlockCount;
8678 if (mAsyncOnloadBlockCount == 1) {
8679 bool success = nsContentUtils::AddScriptRunner(
8680 NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
8682 // The script runner shouldn't fail to add. But if somebody broke
8683 // something and it does, we'll thrash at 100% cpu forever. The best
8684 // response is just to ignore the onload blocking request. See bug 579535.
8685 if (!success) {
8686 NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
8687 mAsyncOnloadBlockCount = 0;
8690 return;
8692 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8693 if (loadGroup) {
8694 loadGroup->AddRequest(mOnloadBlocker, nullptr);
8697 ++mOnloadBlockCount;
8700 void
8701 nsDocument::UnblockOnload(bool aFireSync)
8703 if (mDisplayDocument) {
8704 mDisplayDocument->UnblockOnload(aFireSync);
8705 return;
8708 if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
8709 NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
8710 return;
8713 --mOnloadBlockCount;
8715 if (mOnloadBlockCount == 0) {
8716 if (mScriptGlobalObject) {
8717 // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
8718 // is null, it's not ours.
8719 if (aFireSync && mAsyncOnloadBlockCount == 0) {
8720 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
8721 ++mOnloadBlockCount;
8722 DoUnblockOnload();
8723 } else {
8724 PostUnblockOnloadEvent();
8726 } else if (mIsBeingUsedAsImage) {
8727 // To correctly unblock onload for a document that contains an SVG
8728 // image, we need to know when all of the SVG document's resources are
8729 // done loading, in a way comparable to |window.onload|. We fire this
8730 // event to indicate that the SVG should be considered fully loaded.
8731 // Because scripting is disabled on SVG-as-image documents, this event
8732 // is not accessible to content authors. (See bug 837135.)
8733 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
8734 new AsyncEventDispatcher(this,
8735 NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
8736 false,
8737 false);
8738 asyncDispatcher->PostDOMEvent();
8743 class nsUnblockOnloadEvent : public nsRunnable {
8744 public:
8745 explicit nsUnblockOnloadEvent(nsDocument* aDoc) : mDoc(aDoc) {}
8746 NS_IMETHOD Run() {
8747 mDoc->DoUnblockOnload();
8748 return NS_OK;
8750 private:
8751 nsRefPtr<nsDocument> mDoc;
8754 void
8755 nsDocument::PostUnblockOnloadEvent()
8757 nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
8758 nsresult rv = NS_DispatchToCurrentThread(evt);
8759 if (NS_SUCCEEDED(rv)) {
8760 // Stabilize block count so we don't post more events while this one is up
8761 ++mOnloadBlockCount;
8762 } else {
8763 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
8767 void
8768 nsDocument::DoUnblockOnload()
8770 NS_PRECONDITION(!mDisplayDocument,
8771 "Shouldn't get here for resource document");
8772 NS_PRECONDITION(mOnloadBlockCount != 0,
8773 "Shouldn't have a count of zero here, since we stabilized in "
8774 "PostUnblockOnloadEvent");
8776 --mOnloadBlockCount;
8778 if (mOnloadBlockCount != 0) {
8779 // We blocked again after the last unblock. Nothing to do here. We'll
8780 // post a new event when we unblock again.
8781 return;
8784 if (mAsyncOnloadBlockCount != 0) {
8785 // We need to wait until the async onload block has been handled.
8786 PostUnblockOnloadEvent();
8789 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8790 // -- it's not ours.
8791 if (mScriptGlobalObject) {
8792 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8793 if (loadGroup) {
8794 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
8799 nsIContent*
8800 nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
8802 for (nsIFrame* f = aFrame; f;
8803 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
8804 nsIContent* content = f->GetContent();
8805 if (!content || content->IsInAnonymousSubtree())
8806 continue;
8808 if (content->OwnerDoc() == this) {
8809 return content;
8811 // We must be in a subdocument so jump directly to the root frame.
8812 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
8813 // the containing document.
8814 f = f->PresContext()->GetPresShell()->GetRootFrame();
8817 return nullptr;
8820 void
8821 nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
8822 const nsAString& aType,
8823 bool aPersisted)
8825 if (!aDispatchTarget) {
8826 return;
8829 PageTransitionEventInit init;
8830 init.mBubbles = true;
8831 init.mCancelable = true;
8832 init.mPersisted = aPersisted;
8834 nsRefPtr<PageTransitionEvent> event =
8835 PageTransitionEvent::Constructor(this, aType, init);
8837 event->SetTrusted(true);
8838 event->SetTarget(this);
8839 EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
8840 nullptr, nullptr);
8843 static bool
8844 NotifyPageShow(nsIDocument* aDocument, void* aData)
8846 const bool* aPersistedPtr = static_cast<const bool*>(aData);
8847 aDocument->OnPageShow(*aPersistedPtr, nullptr);
8848 return true;
8851 void
8852 nsDocument::OnPageShow(bool aPersisted,
8853 EventTarget* aDispatchStartTarget)
8855 mVisible = true;
8857 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8858 EnumerateExternalResources(NotifyPageShow, &aPersisted);
8860 Element* root = GetRootElement();
8861 if (aPersisted && root) {
8862 // Send out notifications that our <link> elements are attached.
8863 nsRefPtr<nsContentList> links = NS_GetContentList(root,
8864 kNameSpaceID_XHTML,
8865 NS_LITERAL_STRING("link"));
8867 uint32_t linkCount = links->Length(true);
8868 for (uint32_t i = 0; i < linkCount; ++i) {
8869 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
8873 // See nsIDocument
8874 if (!aDispatchStartTarget) {
8875 // Set mIsShowing before firing events, in case those event handlers
8876 // move us around.
8877 mIsShowing = true;
8880 if (mAnimationController) {
8881 mAnimationController->OnPageShow();
8884 if (aPersisted) {
8885 SetImagesNeedAnimating(true);
8888 UpdateVisibilityState();
8890 nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8891 if (!target) {
8892 target = do_QueryInterface(GetWindow());
8895 // Dispatch observer notification to notify observers page is shown.
8896 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8897 nsIPrincipal *principal = GetPrincipal();
8898 os->NotifyObservers(static_cast<nsIDocument*>(this),
8899 nsContentUtils::IsSystemPrincipal(principal) ?
8900 "chrome-page-shown" :
8901 "content-page-shown",
8902 nullptr);
8903 if (!mObservingAppThemeChanged) {
8904 os->AddObserver(this, "app-theme-changed", /* ownsWeak */ false);
8905 mObservingAppThemeChanged = true;
8908 DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
8911 static bool
8912 NotifyPageHide(nsIDocument* aDocument, void* aData)
8914 const bool* aPersistedPtr = static_cast<const bool*>(aData);
8915 aDocument->OnPageHide(*aPersistedPtr, nullptr);
8916 return true;
8919 static void
8920 DispatchFullScreenChange(nsIDocument* aTarget)
8922 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
8923 new AsyncEventDispatcher(aTarget,
8924 NS_LITERAL_STRING("mozfullscreenchange"),
8925 true,
8926 false);
8927 asyncDispatcher->PostDOMEvent();
8930 void
8931 nsDocument::OnPageHide(bool aPersisted,
8932 EventTarget* aDispatchStartTarget)
8934 // Send out notifications that our <link> elements are detached,
8935 // but only if this is not a full unload.
8936 Element* root = GetRootElement();
8937 if (aPersisted && root) {
8938 nsRefPtr<nsContentList> links = NS_GetContentList(root,
8939 kNameSpaceID_XHTML,
8940 NS_LITERAL_STRING("link"));
8942 uint32_t linkCount = links->Length(true);
8943 for (uint32_t i = 0; i < linkCount; ++i) {
8944 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
8948 // See nsIDocument
8949 if (!aDispatchStartTarget) {
8950 // Set mIsShowing before firing events, in case those event handlers
8951 // move us around.
8952 mIsShowing = false;
8955 if (mAnimationController) {
8956 mAnimationController->OnPageHide();
8959 if (aPersisted) {
8960 SetImagesNeedAnimating(false);
8963 MozExitPointerLock();
8965 // Now send out a PageHide event.
8966 nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8967 if (!target) {
8968 target = do_QueryInterface(GetWindow());
8971 // Dispatch observer notification to notify observers page is hidden.
8972 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8973 if (os) {
8974 nsIPrincipal* principal = GetPrincipal();
8975 os->NotifyObservers(static_cast<nsIDocument*>(this),
8976 nsContentUtils::IsSystemPrincipal(principal) ?
8977 "chrome-page-hidden" :
8978 "content-page-hidden",
8979 nullptr);
8981 os->RemoveObserver(this, "app-theme-changed");
8982 mObservingAppThemeChanged = false;
8985 DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
8987 mVisible = false;
8989 UpdateVisibilityState();
8991 EnumerateExternalResources(NotifyPageHide, &aPersisted);
8992 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
8994 if (IsFullScreenDoc()) {
8995 // If this document was fullscreen, we should exit fullscreen in this
8996 // doctree branch. This ensures that if the user navigates while in
8997 // fullscreen mode we don't leave its still visible ancestor documents
8998 // in fullscreen mode. So exit fullscreen in the document's fullscreen
8999 // root document, as this will exit fullscreen in all the root's
9000 // descendant documents. Note that documents are removed from the
9001 // doctree by the time OnPageHide() is called, so we must store a
9002 // reference to the root (in nsDocument::mFullscreenRoot) since we can't
9003 // just traverse the doctree to get the root.
9004 nsIDocument::ExitFullscreen(this, /* async */ false);
9006 // Since the document is removed from the doctree before OnPageHide() is
9007 // called, ExitFullscreen() can't traverse from the root down to *this*
9008 // document, so we must manually call CleanupFullscreenState() below too.
9009 // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
9010 // so we *must* call it after ExitFullscreen(), not before.
9011 // OnPageHide() is called in every hidden (i.e. descendant) document,
9012 // so calling CleanupFullscreenState() here will ensure all hidden
9013 // documents have their fullscreen state reset.
9014 CleanupFullscreenState();
9016 // If anyone was listening to this document's state, advertizing the state
9017 // change would be the least of the politeness.
9018 DispatchFullScreenChange(this);
9022 void
9023 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
9025 NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
9026 mSubtreeModifiedTargets.Count() == 0,
9027 "mSubtreeModifiedTargets not cleared after dispatching?");
9028 ++mSubtreeModifiedDepth;
9029 if (aTarget) {
9030 // MayDispatchMutationEvent is often called just before this method,
9031 // so it has already appended the node to mSubtreeModifiedTargets.
9032 int32_t count = mSubtreeModifiedTargets.Count();
9033 if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
9034 mSubtreeModifiedTargets.AppendObject(aTarget);
9039 void
9040 nsDocument::MutationEventDispatched(nsINode* aTarget)
9042 --mSubtreeModifiedDepth;
9043 if (mSubtreeModifiedDepth == 0) {
9044 int32_t count = mSubtreeModifiedTargets.Count();
9045 if (!count) {
9046 return;
9049 nsPIDOMWindow* window = GetInnerWindow();
9050 if (window &&
9051 !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
9052 mSubtreeModifiedTargets.Clear();
9053 return;
9056 nsCOMArray<nsINode> realTargets;
9057 for (int32_t i = 0; i < count; ++i) {
9058 nsINode* possibleTarget = mSubtreeModifiedTargets[i];
9059 nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
9060 if (content && content->ChromeOnlyAccess()) {
9061 continue;
9064 nsINode* commonAncestor = nullptr;
9065 int32_t realTargetCount = realTargets.Count();
9066 for (int32_t j = 0; j < realTargetCount; ++j) {
9067 commonAncestor =
9068 nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
9069 if (commonAncestor) {
9070 realTargets.ReplaceObjectAt(commonAncestor, j);
9071 break;
9074 if (!commonAncestor) {
9075 realTargets.AppendObject(possibleTarget);
9079 mSubtreeModifiedTargets.Clear();
9081 int32_t realTargetCount = realTargets.Count();
9082 for (int32_t k = 0; k < realTargetCount; ++k) {
9083 InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED);
9084 (new AsyncEventDispatcher(realTargets[k], mutation))->
9085 RunDOMEventWhenSafe();
9090 void
9091 nsDocument::AddStyleRelevantLink(Link* aLink)
9093 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9094 #ifdef DEBUG
9095 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9096 NS_ASSERTION(!entry, "Document already knows about this Link!");
9097 mStyledLinksCleared = false;
9098 #endif
9099 (void)mStyledLinks.PutEntry(aLink);
9102 void
9103 nsDocument::ForgetLink(Link* aLink)
9105 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9106 #ifdef DEBUG
9107 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9108 NS_ASSERTION(entry || mStyledLinksCleared,
9109 "Document knows nothing about this Link!");
9110 #endif
9111 (void)mStyledLinks.RemoveEntry(aLink);
9114 void
9115 nsDocument::DestroyElementMaps()
9117 #ifdef DEBUG
9118 mStyledLinksCleared = true;
9119 #endif
9120 mStyledLinks.Clear();
9121 mIdentifierMap.Clear();
9122 ++mExpandoAndGeneration.generation;
9125 static
9126 PLDHashOperator
9127 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
9129 LinkArray* array = static_cast<LinkArray*>(aArray);
9130 (void)array->AppendElement(aEntry->GetKey());
9131 return PL_DHASH_NEXT;
9134 void
9135 nsDocument::RefreshLinkHrefs()
9137 // Get a list of all links we know about. We will reset them, which will
9138 // remove them from the document, so we need a copy of what is in the
9139 // hashtable.
9140 LinkArray linksToNotify(mStyledLinks.Count());
9141 (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
9143 // Reset all of our styled links.
9144 nsAutoScriptBlocker scriptBlocker;
9145 for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
9146 linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
9150 nsresult
9151 nsDocument::CloneDocHelper(nsDocument* clone) const
9153 clone->mIsStaticDocument = mCreatingStaticClone;
9155 // Init document
9156 nsresult rv = clone->Init();
9157 NS_ENSURE_SUCCESS(rv, rv);
9159 if (mCreatingStaticClone) {
9160 nsCOMPtr<nsILoadGroup> loadGroup;
9162 // |mDocumentContainer| is the container of the document that is being
9163 // created and not the original container. See CreateStaticClone function().
9164 nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
9165 if (docLoader) {
9166 docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
9168 nsCOMPtr<nsIChannel> channel = GetChannel();
9169 nsCOMPtr<nsIURI> uri;
9170 if (channel) {
9171 NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
9172 } else {
9173 uri = nsIDocument::GetDocumentURI();
9175 clone->mChannel = channel;
9176 if (uri) {
9177 clone->ResetToURI(uri, loadGroup, NodePrincipal());
9180 clone->SetContainer(mDocumentContainer);
9183 // Now ensure that our clone has the same URI, base URI, and principal as us.
9184 // We do this after the mCreatingStaticClone block above, because that block
9185 // can set the base URI to an incorrect value in cases when base URI
9186 // information came from the channel. So we override explicitly, and do it
9187 // for all these properties, in case ResetToURI messes with any of the rest of
9188 // them.
9189 clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
9190 clone->SetChromeXHRDocURI(mChromeXHRDocURI);
9191 clone->SetPrincipal(NodePrincipal());
9192 clone->mDocumentBaseURI = mDocumentBaseURI;
9193 clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
9195 // Set scripting object
9196 bool hasHadScriptObject = true;
9197 nsIScriptGlobalObject* scriptObject =
9198 GetScriptHandlingObject(hasHadScriptObject);
9199 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
9200 if (scriptObject) {
9201 clone->SetScriptHandlingObject(scriptObject);
9202 } else {
9203 clone->SetScopeObject(GetScopeObject());
9205 // Make the clone a data document
9206 clone->SetLoadedAsData(true);
9208 // Misc state
9210 // State from nsIDocument
9211 clone->mCharacterSet = mCharacterSet;
9212 clone->mCharacterSetSource = mCharacterSetSource;
9213 clone->mCompatMode = mCompatMode;
9214 clone->mBidiOptions = mBidiOptions;
9215 clone->mContentLanguage = mContentLanguage;
9216 clone->SetContentTypeInternal(GetContentTypeInternal());
9217 clone->mSecurityInfo = mSecurityInfo;
9219 // State from nsDocument
9220 clone->mType = mType;
9221 clone->mXMLDeclarationBits = mXMLDeclarationBits;
9222 clone->mBaseTarget = mBaseTarget;
9223 return NS_OK;
9226 void
9227 nsDocument::SetReadyStateInternal(ReadyState rs)
9229 mReadyState = rs;
9230 if (rs == READYSTATE_UNINITIALIZED) {
9231 // Transition back to uninitialized happens only to keep assertions happy
9232 // right before readyState transitions to something else. Make this
9233 // transition undetectable by Web content.
9234 return;
9236 if (mTiming) {
9237 switch (rs) {
9238 case READYSTATE_LOADING:
9239 mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
9240 break;
9241 case READYSTATE_INTERACTIVE:
9242 mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
9243 break;
9244 case READYSTATE_COMPLETE:
9245 mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
9246 break;
9247 default:
9248 NS_WARNING("Unexpected ReadyState value");
9249 break;
9252 // At the time of loading start, we don't have timing object, record time.
9253 if (READYSTATE_LOADING == rs) {
9254 mLoadingTimeStamp = mozilla::TimeStamp::Now();
9257 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
9258 new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
9259 false, false);
9260 asyncDispatcher->RunDOMEventWhenSafe();
9263 NS_IMETHODIMP
9264 nsDocument::GetReadyState(nsAString& aReadyState)
9266 nsIDocument::GetReadyState(aReadyState);
9267 return NS_OK;
9270 void
9271 nsIDocument::GetReadyState(nsAString& aReadyState) const
9273 switch(mReadyState) {
9274 case READYSTATE_LOADING :
9275 aReadyState.AssignLiteral(MOZ_UTF16("loading"));
9276 break;
9277 case READYSTATE_INTERACTIVE :
9278 aReadyState.AssignLiteral(MOZ_UTF16("interactive"));
9279 break;
9280 case READYSTATE_COMPLETE :
9281 aReadyState.AssignLiteral(MOZ_UTF16("complete"));
9282 break;
9283 default:
9284 aReadyState.AssignLiteral(MOZ_UTF16("uninitialized"));
9288 namespace {
9290 struct SuppressArgs
9292 nsIDocument::SuppressionType mWhat;
9293 uint32_t mIncrease;
9298 static bool
9299 SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
9301 SuppressArgs* args = static_cast<SuppressArgs*>(aData);
9302 aDocument->SuppressEventHandling(args->mWhat, args->mIncrease);
9303 return true;
9306 void
9307 nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
9308 uint32_t aIncrease)
9310 if (mEventsSuppressed == 0 && mAnimationsPaused == 0 &&
9311 aIncrease != 0 && mPresShell && mScriptGlobalObject) {
9312 RevokeAnimationFrameNotifications();
9315 if (aWhat == eAnimationsOnly) {
9316 mAnimationsPaused += aIncrease;
9317 } else {
9318 mEventsSuppressed += aIncrease;
9321 SuppressArgs args = { aWhat, aIncrease };
9322 EnumerateSubDocuments(SuppressEventHandlingInDocument, &args);
9325 static void
9326 FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments,
9327 bool aFireEvents)
9329 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9330 if (!fm)
9331 return;
9333 for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
9334 // NB: Don't bother trying to fire delayed events on documents that were
9335 // closed before this event ran.
9336 if (!aDocuments[i]->EventHandlingSuppressed()) {
9337 fm->FireDelayedEvents(aDocuments[i]);
9338 nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
9339 if (shell) {
9340 // Only fire events for active documents.
9341 bool fire = aFireEvents &&
9342 aDocuments[i]->GetInnerWindow() &&
9343 aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
9344 shell->FireOrClearDelayedEvents(fire);
9350 void
9351 nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr)
9353 // Early exit if the img is already present in the img-cache
9354 // which indicates that the "real" load has already started and
9355 // that we shouldn't preload it.
9356 int16_t blockingStatus;
9357 if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
9358 !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
9359 this, NodePrincipal(), &blockingStatus)) {
9360 return;
9363 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
9364 switch (Element::StringToCORSMode(aCrossOriginAttr)) {
9365 case CORS_NONE:
9366 // Nothing to do
9367 break;
9368 case CORS_ANONYMOUS:
9369 loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
9370 break;
9371 case CORS_USE_CREDENTIALS:
9372 loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
9373 break;
9374 default:
9375 MOZ_CRASH("Unknown CORS mode!");
9378 // Image not in cache - trigger preload
9379 nsRefPtr<imgRequestProxy> request;
9380 nsresult rv =
9381 nsContentUtils::LoadImage(uri,
9382 this,
9383 NodePrincipal(),
9384 mDocumentURI, // uri of document used as referrer
9385 nullptr, // no observer
9386 loadFlags,
9387 NS_LITERAL_STRING("img"),
9388 getter_AddRefs(request));
9390 // Pin image-reference to avoid evicting it from the img-cache before
9391 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
9392 // unlink
9393 if (NS_SUCCEEDED(rv)) {
9394 mPreloadingImages.AppendObject(request);
9398 EventStates
9399 nsDocument::GetDocumentState()
9401 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
9402 if (IsDocumentRightToLeft()) {
9403 mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9405 mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9407 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
9408 nsIPresShell* shell = GetShell();
9409 if (shell && shell->GetPresContext() &&
9410 shell->GetPresContext()->IsTopLevelWindowInactive()) {
9411 mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9413 mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9415 return mDocumentState;
9418 namespace {
9421 * Stub for LoadSheet(), since all we want is to get the sheet into
9422 * the CSSLoader's style cache
9424 class StubCSSLoaderObserver MOZ_FINAL : public nsICSSLoaderObserver {
9425 ~StubCSSLoaderObserver() {}
9426 public:
9427 NS_IMETHOD
9428 StyleSheetLoaded(CSSStyleSheet*, bool, nsresult)
9430 return NS_OK;
9432 NS_DECL_ISUPPORTS
9434 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
9438 void
9439 nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
9440 const nsAString& aCrossOriginAttr)
9442 // The CSSLoader will retain this object after we return.
9443 nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
9445 // Charset names are always ASCII.
9446 CSSLoader()->LoadSheet(uri, NodePrincipal(),
9447 NS_LossyConvertUTF16toASCII(charset),
9448 obs,
9449 Element::StringToCORSMode(aCrossOriginAttr));
9452 nsresult
9453 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
9454 CSSStyleSheet** sheet)
9456 return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet);
9459 class nsDelayedEventDispatcher : public nsRunnable
9461 public:
9462 explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument>>& aDocuments)
9464 mDocuments.SwapElements(aDocuments);
9466 virtual ~nsDelayedEventDispatcher() {}
9468 NS_IMETHOD Run()
9470 FireOrClearDelayedEvents(mDocuments, true);
9471 return NS_OK;
9474 private:
9475 nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
9478 namespace {
9480 struct UnsuppressArgs
9482 explicit UnsuppressArgs(nsIDocument::SuppressionType aWhat)
9483 : mWhat(aWhat)
9487 nsIDocument::SuppressionType mWhat;
9488 nsTArray<nsCOMPtr<nsIDocument>> mDocs;
9493 static bool
9494 GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
9495 void* aData)
9497 UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData);
9498 if (args->mWhat != nsIDocument::eAnimationsOnly &&
9499 aDocument->EventHandlingSuppressed() > 0) {
9500 static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
9501 } else if (args->mWhat == nsIDocument::eAnimationsOnly &&
9502 aDocument->AnimationsPaused()) {
9503 static_cast<nsDocument*>(aDocument)->ResumeAnimations();
9506 if (args->mWhat != nsIDocument::eAnimationsOnly) {
9507 // No need to remember documents if we only care about animation frames.
9508 args->mDocs.AppendElement(aDocument);
9511 aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
9512 return true;
9515 void
9516 nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat,
9517 bool aFireEvents)
9519 UnsuppressArgs args(aWhat);
9520 GetAndUnsuppressSubDocuments(this, &args);
9522 if (aWhat == nsIDocument::eAnimationsOnly) {
9523 // No need to fire events if we only care about animations here.
9524 return;
9527 if (aFireEvents) {
9528 NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs));
9529 } else {
9530 FireOrClearDelayedEvents(args.mDocs, false);
9534 nsISupports*
9535 nsDocument::GetCurrentContentSink()
9537 return mParser ? mParser->GetContentSink() : nullptr;
9540 nsIDocument*
9541 nsDocument::GetTemplateContentsOwner()
9543 if (!mTemplateContentsOwner) {
9544 bool hasHadScriptObject = true;
9545 nsIScriptGlobalObject* scriptObject =
9546 GetScriptHandlingObject(hasHadScriptObject);
9548 nsCOMPtr<nsIDOMDocument> domDocument;
9549 nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
9550 EmptyString(), // aNamespaceURI
9551 EmptyString(), // aQualifiedName
9552 nullptr, // aDoctype
9553 nsIDocument::GetDocumentURI(),
9554 nsIDocument::GetDocBaseURI(),
9555 NodePrincipal(),
9556 true, // aLoadedAsData
9557 scriptObject, // aEventObject
9558 DocumentFlavorHTML);
9559 NS_ENSURE_SUCCESS(rv, nullptr);
9561 mTemplateContentsOwner = do_QueryInterface(domDocument);
9562 NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
9564 nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
9565 doc->mHasHadScriptHandlingObject = hasHadScriptObject;
9567 if (!scriptObject) {
9568 mTemplateContentsOwner->SetScopeObject(GetScopeObject());
9571 // Set |doc| as the template contents owner of itself so that
9572 // |doc| is the template contents owner of template elements created
9573 // by |doc|.
9574 doc->mTemplateContentsOwner = doc;
9577 return mTemplateContentsOwner;
9580 void
9581 nsDocument::RegisterHostObjectUri(const nsACString& aUri)
9583 mHostObjectURIs.AppendElement(aUri);
9586 void
9587 nsDocument::UnregisterHostObjectUri(const nsACString& aUri)
9589 mHostObjectURIs.RemoveElement(aUri);
9592 void
9593 nsDocument::SetScrollToRef(nsIURI *aDocumentURI)
9595 if (!aDocumentURI) {
9596 return;
9599 nsAutoCString ref;
9601 // Since all URI's that pass through here aren't URL's we can't
9602 // rely on the nsIURI implementation for providing a way for
9603 // finding the 'ref' part of the URI, we'll haveto revert to
9604 // string routines for finding the data past '#'
9606 aDocumentURI->GetSpec(ref);
9608 nsReadingIterator<char> start, end;
9610 ref.BeginReading(start);
9611 ref.EndReading(end);
9613 if (FindCharInReadable('#', start, end)) {
9614 ++start; // Skip over the '#'
9616 mScrollToRef = Substring(start, end);
9620 void
9621 nsDocument::ScrollToRef()
9623 if (mScrolledToRefAlready) {
9624 nsCOMPtr<nsIPresShell> shell = GetShell();
9625 if (shell) {
9626 shell->ScrollToAnchor();
9628 return;
9631 if (mScrollToRef.IsEmpty()) {
9632 return;
9635 char* tmpstr = ToNewCString(mScrollToRef);
9636 if (!tmpstr) {
9637 return;
9640 nsUnescape(tmpstr);
9641 nsAutoCString unescapedRef;
9642 unescapedRef.Assign(tmpstr);
9643 nsMemory::Free(tmpstr);
9645 nsresult rv = NS_ERROR_FAILURE;
9646 // We assume that the bytes are in UTF-8, as it says in the spec:
9647 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9648 NS_ConvertUTF8toUTF16 ref(unescapedRef);
9650 nsCOMPtr<nsIPresShell> shell = GetShell();
9651 if (shell) {
9652 // Check an empty string which might be caused by the UTF-8 conversion
9653 if (!ref.IsEmpty()) {
9654 // Note that GoToAnchor will handle flushing layout as needed.
9655 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9656 } else {
9657 rv = NS_ERROR_FAILURE;
9660 // If UTF-8 URI failed then try to assume the string as a
9661 // document's charset.
9663 if (NS_FAILED(rv)) {
9664 const nsACString &docCharset = GetDocumentCharacterSet();
9666 rv = nsContentUtils::ConvertStringFromEncoding(docCharset,
9667 unescapedRef,
9668 ref);
9670 if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
9671 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9674 if (NS_SUCCEEDED(rv)) {
9675 mScrolledToRefAlready = true;
9680 void
9681 nsDocument::ResetScrolledToRefAlready()
9683 mScrolledToRefAlready = false;
9686 void
9687 nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue)
9689 mChangeScrollPosWhenScrollingToRef = aValue;
9692 void
9693 nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
9695 if (!mActivityObservers) {
9696 mActivityObservers = new nsTHashtable<nsPtrHashKey<nsISupports> >();
9697 if (!mActivityObservers)
9698 return;
9700 mActivityObservers->PutEntry(aSupports);
9703 bool
9704 nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
9706 if (!mActivityObservers)
9707 return false;
9708 if (!mActivityObservers->GetEntry(aSupports))
9709 return false;
9710 mActivityObservers->RemoveEntry(aSupports);
9711 return true;
9714 struct EnumerateActivityObserversData {
9715 nsIDocument::ActivityObserverEnumerator mEnumerator;
9716 void* mData;
9719 static PLDHashOperator
9720 EnumerateObservers(nsPtrHashKey<nsISupports>* aEntry, void* aData)
9722 EnumerateActivityObserversData* data = static_cast<EnumerateActivityObserversData*>(aData);
9723 data->mEnumerator(aEntry->GetKey(), data->mData);
9724 return PL_DHASH_NEXT;
9727 void
9728 nsIDocument::EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator,
9729 void* aData)
9731 if (!mActivityObservers)
9732 return;
9733 EnumerateActivityObserversData data = { aEnumerator, aData };
9734 mActivityObservers->EnumerateEntries(EnumerateObservers, &data);
9737 void
9738 nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
9740 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9741 mLinksToUpdate.PutEntry(aLink);
9742 mHasLinksToUpdate = true;
9745 void
9746 nsIDocument::UnregisterPendingLinkUpdate(Link* aLink)
9748 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9749 if (!mHasLinksToUpdate)
9750 return;
9752 mLinksToUpdate.RemoveEntry(aLink);
9755 static PLDHashOperator
9756 EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData)
9758 aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState());
9759 return PL_DHASH_NEXT;
9762 void
9763 nsIDocument::FlushPendingLinkUpdates()
9765 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9766 if (!mHasLinksToUpdate)
9767 return;
9769 #ifdef DEBUG
9770 AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden);
9771 mIsLinkUpdateRegistrationsForbidden = true;
9772 #endif
9773 mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nullptr);
9774 mLinksToUpdate.Clear();
9775 mHasLinksToUpdate = false;
9778 already_AddRefed<nsIDocument>
9779 nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
9781 nsDocument* thisAsDoc = static_cast<nsDocument*>(this);
9782 mCreatingStaticClone = true;
9784 // Make document use different container during cloning.
9785 nsRefPtr<nsDocShell> originalShell = mDocumentContainer.get();
9786 SetContainer(static_cast<nsDocShell*>(aCloneContainer));
9787 nsCOMPtr<nsIDOMNode> clonedNode;
9788 nsresult rv = thisAsDoc->CloneNode(true, 1, getter_AddRefs(clonedNode));
9789 SetContainer(originalShell);
9791 nsRefPtr<nsDocument> clonedDoc;
9792 if (NS_SUCCEEDED(rv)) {
9793 nsCOMPtr<nsIDocument> tmp = do_QueryInterface(clonedNode);
9794 if (tmp) {
9795 clonedDoc = static_cast<nsDocument*>(tmp.get());
9796 if (IsStaticDocument()) {
9797 clonedDoc->mOriginalDocument = mOriginalDocument;
9798 } else {
9799 clonedDoc->mOriginalDocument = this;
9801 int32_t sheetsCount = GetNumberOfStyleSheets();
9802 for (int32_t i = 0; i < sheetsCount; ++i) {
9803 nsRefPtr<CSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
9804 if (sheet) {
9805 if (sheet->IsApplicable()) {
9806 nsRefPtr<CSSStyleSheet> clonedSheet =
9807 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9808 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
9809 if (clonedSheet) {
9810 clonedDoc->AddStyleSheet(clonedSheet);
9816 sheetsCount = thisAsDoc->mOnDemandBuiltInUASheets.Count();
9817 // Iterate backwards to maintain order
9818 for (int32_t i = sheetsCount - 1; i >= 0; --i) {
9819 nsRefPtr<CSSStyleSheet> sheet =
9820 do_QueryObject(thisAsDoc->mOnDemandBuiltInUASheets[i]);
9821 if (sheet) {
9822 if (sheet->IsApplicable()) {
9823 nsRefPtr<CSSStyleSheet> clonedSheet =
9824 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9825 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
9826 if (clonedSheet) {
9827 clonedDoc->AddOnDemandBuiltInUASheet(clonedSheet);
9834 mCreatingStaticClone = false;
9835 return clonedDoc.forget();
9838 nsresult
9839 nsIDocument::ScheduleFrameRequestCallback(const FrameRequestCallbackHolder& aCallback,
9840 int32_t *aHandle)
9842 if (mFrameRequestCallbackCounter == INT32_MAX) {
9843 // Can't increment without overflowing; bail out
9844 return NS_ERROR_NOT_AVAILABLE;
9846 int32_t newHandle = ++mFrameRequestCallbackCounter;
9848 bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty();
9849 DebugOnly<FrameRequest*> request =
9850 mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
9851 NS_ASSERTION(request, "This is supposed to be infallible!");
9852 if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
9853 mPresShell->GetPresContext()->RefreshDriver()->
9854 ScheduleFrameRequestCallbacks(this);
9857 *aHandle = newHandle;
9858 return NS_OK;
9861 void
9862 nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
9864 // mFrameRequestCallbacks is stored sorted by handle
9865 if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) &&
9866 mFrameRequestCallbacks.IsEmpty() &&
9867 mPresShell && IsEventHandlingEnabled()) {
9868 mPresShell->GetPresContext()->RefreshDriver()->
9869 RevokeFrameRequestCallbacks(this);
9873 nsresult
9874 nsDocument::GetStateObject(nsIVariant** aState)
9876 // Get the document's current state object. This is the object backing both
9877 // history.state and popStateEvent.state.
9879 // mStateObjectContainer may be null; this just means that there's no
9880 // current state object.
9882 nsCOMPtr<nsIVariant> stateObj;
9883 if (!mStateObjectCached && mStateObjectContainer) {
9884 AutoJSContext cx;
9885 nsIGlobalObject* sgo = GetScopeObject();
9886 NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
9887 JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
9888 NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
9889 JSAutoCompartment ac(cx, global);
9891 mStateObjectContainer->
9892 DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
9895 NS_IF_ADDREF(*aState = mStateObjectCached);
9897 return NS_OK;
9900 nsDOMNavigationTiming*
9901 nsDocument::GetNavigationTiming() const
9903 return mTiming;
9906 nsresult
9907 nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
9909 mTiming = aTiming;
9910 if (!mLoadingTimeStamp.IsNull() && mTiming) {
9911 mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp);
9913 return NS_OK;
9916 Element*
9917 nsDocument::FindImageMap(const nsAString& aUseMapValue)
9919 if (aUseMapValue.IsEmpty()) {
9920 return nullptr;
9923 nsAString::const_iterator start, end;
9924 aUseMapValue.BeginReading(start);
9925 aUseMapValue.EndReading(end);
9927 int32_t hash = aUseMapValue.FindChar('#');
9928 if (hash < 0) {
9929 return nullptr;
9931 // aUsemap contains a '#', set start to point right after the '#'
9932 start.advance(hash + 1);
9934 if (start == end) {
9935 return nullptr; // aUsemap == "#"
9938 const nsAString& mapName = Substring(start, end);
9940 if (!mImageMaps) {
9941 mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
9944 uint32_t i, n = mImageMaps->Length(true);
9945 nsString name;
9946 for (i = 0; i < n; ++i) {
9947 nsIContent* map = mImageMaps->Item(i);
9948 if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
9949 eCaseMatters) ||
9950 (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) &&
9951 mapName.Equals(name, nsCaseInsensitiveStringComparator()))) {
9952 return map->AsElement();
9956 return nullptr;
9959 #define DEPRECATED_OPERATION(_op) #_op "Warning",
9960 static const char* kWarnings[] = {
9961 #include "nsDeprecatedOperationList.h"
9962 nullptr
9964 #undef DEPRECATED_OPERATION
9966 bool
9967 nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation)
9969 static_assert(eDeprecatedOperationCount <= 64,
9970 "Too many deprecated operations");
9971 return mWarnedAbout & (1ull << aOperation);
9974 void
9975 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
9976 bool asError /* = false */)
9978 if (HasWarnedAbout(aOperation)) {
9979 return;
9981 mWarnedAbout |= (1ull << aOperation);
9982 uint32_t flags = asError ? nsIScriptError::errorFlag
9983 : nsIScriptError::warningFlag;
9984 nsContentUtils::ReportToConsole(flags,
9985 NS_LITERAL_CSTRING("DOM Core"), this,
9986 nsContentUtils::eDOM_PROPERTIES,
9987 kWarnings[aOperation]);
9990 nsresult
9991 nsDocument::AddImage(imgIRequest* aImage)
9993 NS_ENSURE_ARG_POINTER(aImage);
9995 // See if the image is already in the hashtable. If it is, get the old count.
9996 uint32_t oldCount = 0;
9997 mImageTracker.Get(aImage, &oldCount);
9999 // Put the image in the hashtable, with the proper count.
10000 mImageTracker.Put(aImage, oldCount + 1);
10002 nsresult rv = NS_OK;
10004 // If this is the first insertion and we're locking images, lock this image
10005 // too.
10006 if (oldCount == 0) {
10007 if (mLockingImages)
10008 rv = aImage->LockImage();
10009 if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit ||
10010 mImageTracker.Count() < sOnloadDecodeLimit))
10011 rv = aImage->StartDecoding();
10014 // If this is the first insertion and we're animating images, request
10015 // that this image be animated too.
10016 if (oldCount == 0 && mAnimatingImages) {
10017 nsresult rv2 = aImage->IncrementAnimationConsumers();
10018 rv = NS_SUCCEEDED(rv) ? rv2 : rv;
10021 return rv;
10024 nsresult
10025 nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags)
10027 NS_ENSURE_ARG_POINTER(aImage);
10029 // Get the old count. It should exist and be > 0.
10030 uint32_t count = 0;
10031 DebugOnly<bool> found = mImageTracker.Get(aImage, &count);
10032 NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!");
10033 NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!");
10035 // We're removing, so decrement the count.
10036 count--;
10038 // If the count is now zero, remove from the tracker.
10039 // Otherwise, set the new value.
10040 if (count != 0) {
10041 mImageTracker.Put(aImage, count);
10042 return NS_OK;
10045 mImageTracker.Remove(aImage);
10047 nsresult rv = NS_OK;
10049 // Now that we're no longer tracking this image, unlock it if we'd
10050 // previously locked it.
10051 if (mLockingImages) {
10052 rv = aImage->UnlockImage();
10055 // If we're animating images, remove our request to animate this one.
10056 if (mAnimatingImages) {
10057 nsresult rv2 = aImage->DecrementAnimationConsumers();
10058 rv = NS_SUCCEEDED(rv) ? rv2 : rv;
10061 if (aFlags & REQUEST_DISCARD) {
10062 // Request that the image be discarded if nobody else holds a lock on it.
10063 // Do this even if !mLockingImages, because even if we didn't just unlock
10064 // this image, it might still be a candidate for discarding.
10065 aImage->RequestDiscard();
10068 return rv;
10071 nsresult
10072 nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
10074 MOZ_ASSERT(aPlugin);
10075 if (!mPlugins.PutEntry(aPlugin)) {
10076 return NS_ERROR_OUT_OF_MEMORY;
10078 return NS_OK;
10081 void
10082 nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin)
10084 MOZ_ASSERT(aPlugin);
10085 mPlugins.RemoveEntry(aPlugin);
10088 static bool
10089 AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
10091 nsTArray<nsIObjectLoadingContent*>* plugins =
10092 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
10093 MOZ_ASSERT(plugins);
10094 aDocument->GetPlugins(*plugins);
10095 return true;
10098 static PLDHashOperator
10099 AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg)
10101 nsTArray<nsIObjectLoadingContent*>* allPlugins =
10102 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
10103 MOZ_ASSERT(allPlugins);
10104 allPlugins->AppendElement(aPlugin->GetKey());
10105 return PL_DHASH_NEXT;
10108 void
10109 nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
10111 aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
10112 mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins);
10113 EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
10116 PLDHashOperator LockEnumerator(imgIRequest* aKey,
10117 uint32_t aData,
10118 void* userArg)
10120 aKey->LockImage();
10121 aKey->RequestDecode();
10122 return PL_DHASH_NEXT;
10125 PLDHashOperator UnlockEnumerator(imgIRequest* aKey,
10126 uint32_t aData,
10127 void* userArg)
10129 aKey->UnlockImage();
10130 return PL_DHASH_NEXT;
10134 nsresult
10135 nsDocument::SetImageLockingState(bool aLocked)
10137 if (XRE_GetProcessType() == GeckoProcessType_Content &&
10138 !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) {
10139 return NS_OK;
10142 // If there's no change, there's nothing to do.
10143 if (mLockingImages == aLocked)
10144 return NS_OK;
10146 // Otherwise, iterate over our images and perform the appropriate action.
10147 mImageTracker.EnumerateRead(aLocked ? LockEnumerator
10148 : UnlockEnumerator,
10149 nullptr);
10151 // Update state.
10152 mLockingImages = aLocked;
10154 return NS_OK;
10157 PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
10158 uint32_t aData,
10159 void* userArg)
10161 aKey->IncrementAnimationConsumers();
10162 return PL_DHASH_NEXT;
10165 PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
10166 uint32_t aData,
10167 void* userArg)
10169 aKey->DecrementAnimationConsumers();
10170 return PL_DHASH_NEXT;
10173 void
10174 nsDocument::SetImagesNeedAnimating(bool aAnimating)
10176 // If there's no change, there's nothing to do.
10177 if (mAnimatingImages == aAnimating)
10178 return;
10180 // Otherwise, iterate over our images and perform the appropriate action.
10181 mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
10182 : DecrementAnimationEnumerator,
10183 nullptr);
10185 // Update state.
10186 mAnimatingImages = aAnimating;
10189 already_AddRefed<Touch>
10190 nsIDocument::CreateTouch(nsIDOMWindow* aView,
10191 EventTarget* aTarget,
10192 int32_t aIdentifier,
10193 int32_t aPageX, int32_t aPageY,
10194 int32_t aScreenX, int32_t aScreenY,
10195 int32_t aClientX, int32_t aClientY,
10196 int32_t aRadiusX, int32_t aRadiusY,
10197 float aRotationAngle,
10198 float aForce)
10200 nsRefPtr<Touch> touch = new Touch(aTarget,
10201 aIdentifier,
10202 aPageX, aPageY,
10203 aScreenX, aScreenY,
10204 aClientX, aClientY,
10205 aRadiusX, aRadiusY,
10206 aRotationAngle,
10207 aForce);
10208 return touch.forget();
10211 already_AddRefed<TouchList>
10212 nsIDocument::CreateTouchList()
10214 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10215 return retval.forget();
10218 already_AddRefed<TouchList>
10219 nsIDocument::CreateTouchList(Touch& aTouch,
10220 const Sequence<OwningNonNull<Touch> >& aTouches)
10222 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10223 retval->Append(&aTouch);
10224 for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10225 retval->Append(aTouches[i].get());
10227 return retval.forget();
10230 already_AddRefed<TouchList>
10231 nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
10233 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10234 for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10235 retval->Append(aTouches[i].get());
10237 return retval.forget();
10240 already_AddRefed<nsDOMCaretPosition>
10241 nsIDocument::CaretPositionFromPoint(float aX, float aY)
10243 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
10244 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
10245 nsPoint pt(x, y);
10247 FlushPendingNotifications(Flush_Layout);
10249 nsIPresShell *ps = GetShell();
10250 if (!ps) {
10251 return nullptr;
10254 nsIFrame *rootFrame = ps->GetRootFrame();
10256 // XUL docs, unlike HTML, have no frame tree until everything's done loading
10257 if (!rootFrame) {
10258 return nullptr;
10261 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
10262 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
10263 if (!ptFrame) {
10264 return nullptr;
10267 // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
10268 // to adjust to frame-relative coordinates before we can perform this call.
10269 // It should also not take into account the padding of the frame.
10270 nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
10272 nsFrame::ContentOffsets offsets =
10273 ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
10275 nsCOMPtr<nsIContent> node = offsets.content;
10276 uint32_t offset = offsets.offset;
10277 nsCOMPtr<nsIContent> anonNode = node;
10278 bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
10279 if (nodeIsAnonymous) {
10280 node = ptFrame->GetContent();
10281 nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
10282 nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
10283 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
10284 bool isText;
10285 if (textArea || (input &&
10286 NS_SUCCEEDED(input->MozIsTextField(false, &isText)) &&
10287 isText)) {
10288 // If the anonymous content node has a child, then we need to make sure
10289 // that we get the appropriate child, as otherwise the offset may not be
10290 // correct when we construct a range for it.
10291 nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
10292 if (firstChild) {
10293 anonNode = firstChild;
10296 if (textArea) {
10297 offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
10300 node = nonanon;
10301 } else {
10302 node = nullptr;
10303 offset = 0;
10307 nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
10308 if (nodeIsAnonymous) {
10309 aCaretPos->SetAnonymousContentNode(anonNode);
10311 return aCaretPos.forget();
10314 NS_IMETHODIMP
10315 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
10317 NS_ENSURE_ARG_POINTER(aCaretPos);
10318 *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take();
10319 return NS_OK;
10322 void
10323 nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
10325 nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
10326 if (NS_FAILED(res)) {
10327 rv.Throw(res);
10331 void
10332 nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
10334 nsCOMPtr<nsIURI> uri;
10335 nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
10336 if (NS_FAILED(res)) {
10337 rv.Throw(res);
10338 return;
10340 res = CSSLoader()->ObsoleteSheet(uri);
10341 if (NS_FAILED(res)) {
10342 rv.Throw(res);
10346 nsIHTMLCollection*
10347 nsIDocument::Children()
10349 if (!mChildrenCollection) {
10350 mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
10351 nsGkAtoms::_asterix,
10352 nsGkAtoms::_asterix,
10353 false);
10356 return mChildrenCollection;
10359 uint32_t
10360 nsIDocument::ChildElementCount()
10362 return Children()->Length();
10365 namespace mozilla {
10367 // Singleton class to manage the list of fullscreen documents which are the
10368 // root of a branch which contains fullscreen documents. We maintain this list
10369 // so that we can easily exit all windows from fullscreen when the user
10370 // presses the escape key.
10371 class FullscreenRoots {
10372 public:
10373 // Adds a root to the manager. Adding a root multiple times does not result
10374 // in duplicate entries for that item, only one.
10375 static void Add(nsIDocument* aRoot);
10377 // Iterates over every root in the root list, and calls aFunction, passing
10378 // each root once to aFunction. It is safe to call Add() and Remove() while
10379 // iterating over the list (i.e. in aFunction). Documents that are removed
10380 // from the manager during traversal are not traversed, and documents that
10381 // are added to the manager during traversal are also not traversed.
10382 static void ForEach(void(*aFunction)(nsIDocument* aDoc));
10384 // Removes a specific root from the manager.
10385 static void Remove(nsIDocument* aRoot);
10387 // Returns true if all roots added to the list have been removed.
10388 static bool IsEmpty();
10390 private:
10392 FullscreenRoots() {
10393 MOZ_COUNT_CTOR(FullscreenRoots);
10395 ~FullscreenRoots() {
10396 MOZ_COUNT_DTOR(FullscreenRoots);
10399 enum {
10400 NotFound = uint32_t(-1)
10402 // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
10403 static uint32_t Find(nsIDocument* aRoot);
10405 // Returns true if aRoot is in the list of fullscreen roots.
10406 static bool Contains(nsIDocument* aRoot);
10408 // Singleton instance of the FullscreenRoots. This is instantiated when a
10409 // root is added, and it is deleted when the last root is removed.
10410 static FullscreenRoots* sInstance;
10412 // List of weak pointers to roots.
10413 nsTArray<nsWeakPtr> mRoots;
10416 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
10418 /* static */
10419 void
10420 FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
10422 if (!sInstance) {
10423 return;
10425 // Create a copy of the roots array, and iterate over the copy. This is so
10426 // that if an element is removed from mRoots we don't mess up our iteration.
10427 nsTArray<nsWeakPtr> roots(sInstance->mRoots);
10428 // Call aFunction on all entries.
10429 for (uint32_t i = 0; i < roots.Length(); i++) {
10430 nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
10431 // Check that the root isn't in the manager. This is so that new additions
10432 // while we were running don't get traversed.
10433 if (root && FullscreenRoots::Contains(root)) {
10434 aFunction(root);
10439 /* static */
10440 bool
10441 FullscreenRoots::Contains(nsIDocument* aRoot)
10443 return FullscreenRoots::Find(aRoot) != NotFound;
10446 /* static */
10447 void
10448 FullscreenRoots::Add(nsIDocument* aRoot)
10450 if (!FullscreenRoots::Contains(aRoot)) {
10451 if (!sInstance) {
10452 sInstance = new FullscreenRoots();
10454 nsWeakPtr weakRoot = do_GetWeakReference(aRoot);
10455 sInstance->mRoots.AppendElement(weakRoot);
10459 /* static */
10460 uint32_t
10461 FullscreenRoots::Find(nsIDocument* aRoot)
10463 if (!sInstance) {
10464 return NotFound;
10466 nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
10467 for (uint32_t i = 0; i < roots.Length(); i++) {
10468 nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
10469 if (otherRoot == aRoot) {
10470 return i;
10473 return NotFound;
10476 /* static */
10477 void
10478 FullscreenRoots::Remove(nsIDocument* aRoot)
10480 uint32_t index = Find(aRoot);
10481 NS_ASSERTION(index != NotFound,
10482 "Should only try to remove roots which are still added!");
10483 if (index == NotFound || !sInstance) {
10484 return;
10486 sInstance->mRoots.RemoveElementAt(index);
10487 if (sInstance->mRoots.IsEmpty()) {
10488 delete sInstance;
10489 sInstance = nullptr;
10493 /* static */
10494 bool
10495 FullscreenRoots::IsEmpty()
10497 return !sInstance;
10500 } // end namespace mozilla.
10501 using mozilla::FullscreenRoots;
10503 nsIDocument*
10504 nsDocument::GetFullscreenRoot()
10506 nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
10507 return root;
10510 void
10511 nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
10513 mFullscreenRoot = do_GetWeakReference(aRoot);
10516 NS_IMETHODIMP
10517 nsDocument::MozCancelFullScreen()
10519 nsIDocument::MozCancelFullScreen();
10520 return NS_OK;
10523 void
10524 nsIDocument::MozCancelFullScreen()
10526 RestorePreviousFullScreenState();
10529 // Runnable to set window full-screen mode. Used as a script runner
10530 // to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
10531 // run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
10532 // (handled in chome code) which is unsafe to run if this is called in
10533 // Element::UnbindFromTree().
10534 class nsSetWindowFullScreen : public nsRunnable {
10535 public:
10536 nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
10537 : mDoc(aDoc), mValue(aValue) {}
10539 NS_IMETHOD Run()
10541 if (mDoc->GetWindow()) {
10542 mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
10544 return NS_OK;
10547 private:
10548 nsCOMPtr<nsIDocument> mDoc;
10549 bool mValue;
10552 static nsIDocument*
10553 GetFullscreenRootDocument(nsIDocument* aDoc)
10555 if (!aDoc) {
10556 return nullptr;
10558 nsIDocument* doc = aDoc;
10559 nsIDocument* parent;
10560 while ((parent = doc->GetParentDocument()) &&
10561 (!nsContentUtils::IsFullscreenApiContentOnly() ||
10562 !nsContentUtils::IsChromeDoc(parent))) {
10563 doc = parent;
10565 return doc;
10568 static void
10569 SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
10571 // Maintain list of fullscreen root documents.
10572 nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc);
10573 if (aValue) {
10574 FullscreenRoots::Add(root);
10575 } else {
10576 FullscreenRoots::Remove(root);
10578 if (!nsContentUtils::IsFullscreenApiContentOnly()) {
10579 nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
10583 class nsCallExitFullscreen : public nsRunnable {
10584 public:
10585 explicit nsCallExitFullscreen(nsIDocument* aDoc)
10586 : mDoc(aDoc) {}
10587 NS_IMETHOD Run()
10589 nsDocument::ExitFullscreen(mDoc);
10590 return NS_OK;
10592 private:
10593 nsCOMPtr<nsIDocument> mDoc;
10596 /* static */
10597 void
10598 nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
10600 if (aDoc && !aDoc->IsFullScreenDoc()) {
10601 return;
10603 if (aRunAsync) {
10604 NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
10605 return;
10607 nsDocument::ExitFullscreen(aDoc);
10610 // Returns true if the document is a direct child of a cross process parent
10611 // mozbrowser iframe or TabParent. This is the case when the document has
10612 // a null parent and its DocShell reports that it is a browser frame, or
10613 // we can get a TabChild from it.
10614 static bool
10615 HasCrossProcessParent(nsIDocument* aDocument)
10617 if (XRE_GetProcessType() != GeckoProcessType_Content) {
10618 return false;
10620 if (aDocument->GetParentDocument() != nullptr) {
10621 return false;
10623 nsPIDOMWindow* win = aDocument->GetWindow();
10624 if (!win) {
10625 return false;
10627 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
10628 if (!docShell) {
10629 return false;
10631 TabChild* tabChild(TabChild::GetFrom(docShell));
10632 if (!tabChild) {
10633 return false;
10636 return true;
10639 static bool
10640 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
10642 if (aDoc->IsFullScreenDoc()) {
10643 uint32_t* count = static_cast<uint32_t*>(aData);
10644 (*count)++;
10646 return true;
10649 static uint32_t
10650 CountFullscreenSubDocuments(nsIDocument* aDoc)
10652 uint32_t count = 0;
10653 aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
10654 return count;
10657 bool
10658 nsDocument::IsFullscreenLeaf()
10660 // A fullscreen leaf document is fullscreen, and has no fullscreen
10661 // subdocuments.
10662 if (!IsFullScreenDoc()) {
10663 return false;
10665 return CountFullscreenSubDocuments(this) == 0;
10668 static bool
10669 ResetFullScreen(nsIDocument* aDocument, void* aData)
10671 if (aDocument->IsFullScreenDoc()) {
10672 NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
10673 "Should have at most 1 fullscreen subdocument.");
10674 static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
10675 NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
10676 nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
10677 changed->AppendElement(aDocument);
10679 if (HasCrossProcessParent(aDocument)) {
10680 // We're at the top of the content-process side doc tree. Ask the parent
10681 // process to exit fullscreen.
10682 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10683 os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr);
10686 // Dispatch a notification so that if this document has any
10687 // cross-process subdocuments, they'll be notified to exit fullscreen.
10688 // The BrowserElementParent listens for this event and performs the
10689 // cross process notification if it has a remote child process.
10690 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10691 os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
10693 aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
10695 return true;
10698 static void
10699 ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
10701 MOZ_ASSERT(aMaybeNotARootDoc);
10702 nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
10703 NS_ASSERTION(root, "Should have root when in fullscreen!");
10704 if (!root) {
10705 return;
10707 NS_ASSERTION(root->IsFullScreenDoc(),
10708 "Fullscreen root should be a fullscreen doc...");
10710 // Stores a list of documents to which we must dispatch "mozfullscreenchange".
10711 // We're required by the spec to dispatch the events in leaf-to-root
10712 // order when exiting fullscreen, but we traverse the doctree in a
10713 // root-to-leaf order, so we save references to the documents we must
10714 // dispatch to so that we dispatch in the specified order.
10715 nsAutoTArray<nsIDocument*, 8> changed;
10717 // Walk the tree of fullscreen documents, and reset their fullscreen state.
10718 ResetFullScreen(root, static_cast<void*>(&changed));
10720 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
10721 // order so that the events for the leaf document arrives before the root
10722 // document, as required by the spec.
10723 for (uint32_t i = 0; i < changed.Length(); ++i) {
10724 DispatchFullScreenChange(changed[changed.Length() - i - 1]);
10727 NS_ASSERTION(!root->IsFullScreenDoc(),
10728 "Fullscreen root should no longer be a fullscreen doc...");
10730 // Move the top-level window out of fullscreen mode.
10731 SetWindowFullScreen(root, false);
10734 /* static */
10735 void
10736 nsDocument::ExitFullscreen(nsIDocument* aDoc)
10738 // Unlock the pointer, if it's locked.
10739 nsCOMPtr<Element> pointerLockedElement =
10740 do_QueryReferent(EventStateManager::sPointerLockedElement);
10741 if (pointerLockedElement) {
10742 UnlockPointer();
10745 if (aDoc) {
10746 ExitFullscreenInDocTree(aDoc);
10747 return;
10750 // Clear fullscreen stacks in all fullscreen roots' descendant documents.
10751 FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
10752 NS_ASSERTION(FullscreenRoots::IsEmpty(),
10753 "Should have exited all fullscreen roots from fullscreen");
10756 bool
10757 GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
10759 if (aDoc->IsFullscreenLeaf()) {
10760 nsIDocument** result = static_cast<nsIDocument**>(aData);
10761 *result = aDoc;
10762 return false;
10763 } else if (aDoc->IsFullScreenDoc()) {
10764 aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
10766 return true;
10769 static nsIDocument*
10770 GetFullscreenLeaf(nsIDocument* aDoc)
10772 nsIDocument* leaf = nullptr;
10773 GetFullscreenLeaf(aDoc, &leaf);
10774 if (leaf) {
10775 return leaf;
10777 // Otherwise we could be either in a non-fullscreen doc tree, or we're
10778 // below the fullscreen doc. Start the search from the root.
10779 nsIDocument* root = GetFullscreenRootDocument(aDoc);
10780 // Check that the root is actually fullscreen so we don't waste time walking
10781 // around its descendants.
10782 if (!root->IsFullScreenDoc()) {
10783 return nullptr;
10785 GetFullscreenLeaf(root, &leaf);
10786 return leaf;
10789 void
10790 nsDocument::RestorePreviousFullScreenState()
10792 NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(),
10793 "Should have at least 1 fullscreen root when fullscreen!");
10794 NS_ASSERTION(!nsContentUtils::IsFullscreenApiContentOnly() ||
10795 !nsContentUtils::IsChromeDoc(this),
10796 "Should not run RestorePreviousFullScreenState() on "
10797 "chrome document when fullscreen is content only");
10799 if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) {
10800 return;
10803 // If fullscreen mode is updated the pointer should be unlocked
10804 nsCOMPtr<Element> pointerLockedElement =
10805 do_QueryReferent(EventStateManager::sPointerLockedElement);
10806 if (pointerLockedElement) {
10807 UnlockPointer();
10810 nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
10812 // The fullscreen document may contain a <iframe mozbrowser> element which
10813 // has a cross process child. So send a notification so that its browser
10814 // parent will send a message to its child process to also exit fullscreen.
10815 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10816 os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr);
10818 // Clear full-screen stacks in all descendant in process documents, bottom up.
10819 nsIDocument* doc = fullScreenDoc;
10820 while (doc != this) {
10821 NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc");
10822 static_cast<nsDocument*>(doc)->CleanupFullscreenState();
10823 UnlockPointer();
10824 DispatchFullScreenChange(doc);
10825 doc = doc->GetParentDocument();
10828 // Roll-back full-screen state to previous full-screen element.
10829 NS_ASSERTION(doc == this, "Must have reached this doc.");
10830 while (doc != nullptr) {
10831 static_cast<nsDocument*>(doc)->FullScreenStackPop();
10832 UnlockPointer();
10833 DispatchFullScreenChange(doc);
10834 if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) {
10835 if (HasCrossProcessParent(doc)) {
10836 // Send notification to the parent process to tell it to rollback to
10837 // the previous fullscreen elements in its fullscreen element stacks.
10838 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10839 os->NotifyObservers(doc, "ask-parent-to-rollback-fullscreen", nullptr);
10841 // Full-screen stack in document is empty. Go back up to the parent
10842 // document. We'll pop the containing element off its stack, and use
10843 // its next full-screen element as the full-screen element.
10844 static_cast<nsDocument*>(doc)->CleanupFullscreenState();
10845 doc = doc->GetParentDocument();
10846 } else {
10847 // Else we popped the top of the stack, and there's still another
10848 // element in there, so that will become the full-screen element.
10849 if (fullScreenDoc != doc) {
10850 // We've popped so enough off the stack that we've rolled back to
10851 // a fullscreen element in a parent document. If this document isn't
10852 // approved for fullscreen, or if it's cross origin, dispatch an
10853 // event to chrome so it knows to show the authorization/warning UI.
10854 if (!nsContentUtils::HaveEqualPrincipals(fullScreenDoc, doc) ||
10855 (!nsContentUtils::IsSitePermAllow(doc->NodePrincipal(), "fullscreen") &&
10856 !static_cast<nsDocument*>(doc)->mIsApprovedForFullscreen)) {
10857 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
10858 new AsyncEventDispatcher(doc,
10859 NS_LITERAL_STRING("MozEnteredDomFullscreen"),
10860 true,
10861 true);
10862 asyncDispatcher->PostDOMEvent();
10866 if (!nsContentUtils::HaveEqualPrincipals(doc, fullScreenDoc)) {
10867 // The origin which is fullscreen changed. Send a notification to
10868 // the root process so that a warning or approval UI can be shown
10869 // as necessary.
10870 nsAutoString origin;
10871 nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin);
10872 nsIDocument* root = GetFullscreenRootDocument(doc);
10873 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10874 os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
10877 break;
10881 if (doc == nullptr) {
10882 // We moved all documents in this doctree out of fullscreen mode,
10883 // move the top-level window out of fullscreen mode.
10884 NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(),
10885 "Should have cleared all docs' stacks");
10886 SetWindowFullScreen(this, false);
10890 bool
10891 nsDocument::IsFullScreenDoc()
10893 return GetFullScreenElement() != nullptr;
10896 class nsCallRequestFullScreen : public nsRunnable
10898 public:
10899 explicit nsCallRequestFullScreen(Element* aElement)
10900 : mElement(aElement),
10901 mDoc(aElement->OwnerDoc()),
10902 mWasCallerChrome(nsContentUtils::IsCallerChrome()),
10903 mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
10904 mAsyncFullscreenPending)
10906 static_cast<nsDocument*>(mDoc.get())->
10907 mAsyncFullscreenPending = true;
10910 NS_IMETHOD Run()
10912 static_cast<nsDocument*>(mDoc.get())->
10913 mAsyncFullscreenPending = mHadRequestPending;
10914 nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
10915 doc->RequestFullScreen(mElement,
10916 mWasCallerChrome,
10917 /* aNotifyOnOriginChange */ true);
10918 return NS_OK;
10921 nsRefPtr<Element> mElement;
10922 nsCOMPtr<nsIDocument> mDoc;
10923 bool mWasCallerChrome;
10924 bool mHadRequestPending;
10927 void
10928 nsDocument::AsyncRequestFullScreen(Element* aElement)
10930 NS_ASSERTION(aElement,
10931 "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
10932 if (!aElement) {
10933 return;
10935 // Request full-screen asynchronously.
10936 nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement));
10937 NS_DispatchToCurrentThread(event);
10940 static void
10941 LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc)
10943 if (!aLogFailure) {
10944 return;
10946 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
10947 new AsyncEventDispatcher(aDoc,
10948 NS_LITERAL_STRING("mozfullscreenerror"),
10949 true,
10950 false);
10951 asyncDispatcher->PostDOMEvent();
10952 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
10953 NS_LITERAL_CSTRING("DOM"), aDoc,
10954 nsContentUtils::eDOM_PROPERTIES,
10955 aMessage);
10958 nsresult
10959 nsDocument::AddFullscreenApprovedObserver()
10961 if (mHasFullscreenApprovedObserver ||
10962 !Preferences::GetBool("full-screen-api.approval-required")) {
10963 return NS_OK;
10966 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10967 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
10969 nsresult res = os->AddObserver(this, "fullscreen-approved", true);
10970 NS_ENSURE_SUCCESS(res, res);
10972 mHasFullscreenApprovedObserver = true;
10974 return NS_OK;
10977 nsresult
10978 nsDocument::RemoveFullscreenApprovedObserver()
10980 if (!mHasFullscreenApprovedObserver) {
10981 return NS_OK;
10983 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10984 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
10986 nsresult res = os->RemoveObserver(this, "fullscreen-approved");
10987 NS_ENSURE_SUCCESS(res, res);
10989 mHasFullscreenApprovedObserver = false;
10991 return NS_OK;
10994 void
10995 nsDocument::CleanupFullscreenState()
10997 if (!mFullScreenStack.IsEmpty()) {
10998 // The top element in the full-screen stack will have full-screen
10999 // style bits set on it and its ancestors. Remove the style bits.
11000 // Note the non-top elements won't have the style bits set.
11001 Element* top = FullScreenStackTop();
11002 NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
11003 if (top) {
11004 EventStateManager::SetFullScreenState(top, false);
11006 mFullScreenStack.Clear();
11008 SetApprovedForFullscreen(false);
11009 RemoveFullscreenApprovedObserver();
11010 mFullscreenRoot = nullptr;
11013 bool
11014 nsDocument::FullScreenStackPush(Element* aElement)
11016 NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
11017 Element* top = FullScreenStackTop();
11018 if (top == aElement || !aElement) {
11019 return false;
11021 if (top) {
11022 // We're pushing a new element onto the full-screen stack, so we must
11023 // remove the ancestor and full-screen styles from the former top of the
11024 // stack.
11025 EventStateManager::SetFullScreenState(top, false);
11027 EventStateManager::SetFullScreenState(aElement, true);
11028 nsWeakPtr weakElement = do_GetWeakReference(aElement);
11029 mFullScreenStack.AppendElement(weakElement);
11030 NS_ASSERTION(GetFullScreenElement() == aElement, "Should match");
11031 return true;
11034 void
11035 nsDocument::FullScreenStackPop()
11037 if (mFullScreenStack.IsEmpty()) {
11038 return;
11041 // Remove styles from existing top element.
11042 Element* top = FullScreenStackTop();
11043 EventStateManager::SetFullScreenState(top, false);
11045 // Remove top element. Note the remaining top element in the stack
11046 // will not have full-screen style bits set, so we will need to restore
11047 // them on the new top element before returning.
11048 uint32_t last = mFullScreenStack.Length() - 1;
11049 mFullScreenStack.RemoveElementAt(last);
11051 // Pop from the stack null elements (references to elements which have
11052 // been GC'd since they were added to the stack) and elements which are
11053 // no longer in this document.
11054 while (!mFullScreenStack.IsEmpty()) {
11055 Element* element = FullScreenStackTop();
11056 if (!element || !element->IsInDoc() || element->OwnerDoc() != this) {
11057 NS_ASSERTION(!element->IsFullScreenAncestor(),
11058 "Should have already removed full-screen styles");
11059 uint32_t last = mFullScreenStack.Length() - 1;
11060 mFullScreenStack.RemoveElementAt(last);
11061 } else {
11062 // The top element of the stack is now an in-doc element. Apply the
11063 // full-screen styles and return.
11064 EventStateManager::SetFullScreenState(element, true);
11065 break;
11070 Element*
11071 nsDocument::FullScreenStackTop()
11073 if (mFullScreenStack.IsEmpty()) {
11074 return nullptr;
11076 uint32_t last = mFullScreenStack.Length() - 1;
11077 nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
11078 NS_ASSERTION(element, "Should have full-screen element!");
11079 NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc");
11080 NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
11081 return element;
11084 // Returns true if aDoc is in the focused tab in the active window.
11085 static bool
11086 IsInActiveTab(nsIDocument* aDoc)
11088 nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
11089 if (!docshell) {
11090 return false;
11093 bool isActive = false;
11094 docshell->GetIsActive(&isActive);
11095 if (!isActive) {
11096 return false;
11099 nsCOMPtr<nsIDocShellTreeItem> rootItem;
11100 docshell->GetRootTreeItem(getter_AddRefs(rootItem));
11101 if (!rootItem) {
11102 return false;
11104 nsCOMPtr<nsIDOMWindow> rootWin = rootItem->GetWindow();
11105 if (!rootWin) {
11106 return false;
11109 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11110 if (!fm) {
11111 return false;
11114 nsCOMPtr<nsIDOMWindow> activeWindow;
11115 fm->GetActiveWindow(getter_AddRefs(activeWindow));
11116 if (!activeWindow) {
11117 return false;
11120 return activeWindow == rootWin;
11123 nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
11124 const nsAString& aOrigin)
11126 // Ensure the frame element is the fullscreen element in this document.
11127 // If the frame element is already the fullscreen element in this document,
11128 // this has no effect.
11129 nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
11130 RequestFullScreen(content->AsElement(),
11131 /* aWasCallerChrome */ false,
11132 /* aNotifyOnOriginChange */ false);
11134 // Origin changed in child process, send notifiction, so that chrome can
11135 // update the UI to reflect the fullscreen origin change if necessary.
11136 // The BrowserElementChild listens on this, and forwards it over its
11137 // parent process, where it is redispatched. Chrome (in the root process,
11138 // which could be *this* process) listens for this notification so that
11139 // it can show a warning or approval UI.
11140 if (!aOrigin.IsEmpty()) {
11141 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11142 os->NotifyObservers(GetFullscreenRootDocument(this),
11143 "fullscreen-origin-change",
11144 PromiseFlatString(aOrigin).get());
11147 return NS_OK;
11150 nsresult nsDocument::RemoteFrameFullscreenReverted()
11152 RestorePreviousFullScreenState();
11153 return NS_OK;
11156 void
11157 nsDocument::RequestFullScreen(Element* aElement,
11158 bool aWasCallerChrome,
11159 bool aNotifyOnOriginChange)
11161 NS_ASSERTION(aElement,
11162 "Must pass non-null element to nsDocument::RequestFullScreen");
11163 if (!aElement || aElement == GetFullScreenElement()) {
11164 return;
11166 if (!aElement->IsInDoc()) {
11167 LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this);
11168 return;
11170 if (aElement->OwnerDoc() != this) {
11171 LogFullScreenDenied(true, "FullScreenDeniedMovedDocument", this);
11172 return;
11174 if (!GetWindow()) {
11175 LogFullScreenDenied(true, "FullScreenDeniedLostWindow", this);
11176 return;
11178 if (nsContentUtils::IsFullscreenApiContentOnly() &&
11179 nsContentUtils::IsChromeDoc(this)) {
11180 // Block fullscreen requests in the chrome document when the fullscreen API
11181 // is configured for content only.
11182 LogFullScreenDenied(true, "FullScreenDeniedContentOnly", this);
11183 return;
11185 if (!IsFullScreenEnabled(aWasCallerChrome, true)) {
11186 // IsFullScreenEnabled calls LogFullScreenDenied, no need to log.
11187 return;
11189 if (GetFullScreenElement() &&
11190 !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) {
11191 // If this document is full-screen, only grant full-screen requests from
11192 // a descendant of the current full-screen element.
11193 LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this);
11194 return;
11196 if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
11197 LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this);
11198 return;
11200 // Deny requests when a windowed plugin is focused.
11201 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11202 if (!fm) {
11203 NS_WARNING("Failed to retrieve focus manager in full-screen request.");
11204 return;
11206 nsCOMPtr<nsIDOMElement> focusedElement;
11207 fm->GetFocusedElement(getter_AddRefs(focusedElement));
11208 if (focusedElement) {
11209 nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
11210 if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
11211 LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this);
11212 return;
11216 // Stash a reference to any existing fullscreen doc, we'll use this later
11217 // to detect if the origin which is fullscreen has changed.
11218 nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
11220 AddFullscreenApprovedObserver();
11222 // Stores a list of documents which we must dispatch "mozfullscreenchange"
11223 // too. We're required by the spec to dispatch the events in root-to-leaf
11224 // order, but we traverse the doctree in a leaf-to-root order, so we save
11225 // references to the documents we must dispatch to so that we get the order
11226 // as specified.
11227 nsAutoTArray<nsIDocument*, 8> changed;
11229 // Remember the root document, so that if a full-screen document is hidden
11230 // we can reset full-screen state in the remaining visible full-screen documents.
11231 nsIDocument* fullScreenRootDoc = GetFullscreenRootDocument(this);
11232 if (fullScreenRootDoc->IsFullScreenDoc()) {
11233 // A document is already in fullscreen, unlock the mouse pointer
11234 // before setting a new document to fullscreen
11235 UnlockPointer();
11238 // If a document is already in fullscreen, then unlock the mouse pointer
11239 // before setting a new document to fullscreen
11240 nsCOMPtr<Element> pointerLockedElement =
11241 do_QueryReferent(EventStateManager::sPointerLockedElement);
11242 if (pointerLockedElement) {
11243 UnlockPointer();
11246 // Set the full-screen element. This sets the full-screen style on the
11247 // element, and the full-screen-ancestor styles on ancestors of the element
11248 // in this document.
11249 DebugOnly<bool> x = FullScreenStackPush(aElement);
11250 NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
11251 changed.AppendElement(this);
11253 // Propagate up the document hierarchy, setting the full-screen element as
11254 // the element's container in ancestor documents. This also sets the
11255 // appropriate css styles as well. Note we don't propagate down the
11256 // document hierarchy, the full-screen element (or its container) is not
11257 // visible there. Stop when we reach the root document.
11258 nsIDocument* child = this;
11259 while (true) {
11260 child->SetFullscreenRoot(fullScreenRootDoc);
11261 NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
11262 "Fullscreen root should be set!");
11263 if (child == fullScreenRootDoc) {
11264 break;
11266 nsIDocument* parent = child->GetParentDocument();
11267 Element* element = parent->FindContentForSubDocument(child)->AsElement();
11268 if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
11269 changed.AppendElement(parent);
11270 child = parent;
11271 } else {
11272 // We've reached either the root, or a point in the doctree where the
11273 // new full-screen element container is the same as the previous
11274 // full-screen element's container. No more changes need to be made
11275 // to the full-screen stacks of documents further up the tree.
11276 break;
11280 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
11281 // order so that the events for the root document arrives before the leaf
11282 // document, as required by the spec.
11283 for (uint32_t i = 0; i < changed.Length(); ++i) {
11284 DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11287 // If this document hasn't already been approved in this session,
11288 // check to see if the user has granted the fullscreen access
11289 // to the document's principal's host, if it has one. Note that documents
11290 // in web apps which are the same origin as the web app are considered
11291 // trusted and so are automatically approved.
11292 if (!mIsApprovedForFullscreen) {
11293 mIsApprovedForFullscreen =
11294 !Preferences::GetBool("full-screen-api.approval-required") ||
11295 NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
11296 nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen");
11299 // If this document, or a document with the same principal has not
11300 // already been approved for fullscreen this fullscreen-session, dispatch
11301 // an event so that chrome knows to pop up a warning/approval UI.
11302 // Note previousFullscreenDoc=nullptr upon first entry, so we always
11303 // take this path on the first time we enter fullscreen in a fullscreen
11304 // session.
11305 if (!mIsApprovedForFullscreen ||
11306 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11307 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11308 new AsyncEventDispatcher(this,
11309 NS_LITERAL_STRING("MozEnteredDomFullscreen"),
11310 true,
11311 true);
11312 asyncDispatcher->PostDOMEvent();
11315 #ifdef DEBUG
11316 // Note assertions must run before SetWindowFullScreen() as that does
11317 // synchronous event dispatch which can run script which exits full-screen!
11318 NS_ASSERTION(GetFullScreenElement() == aElement,
11319 "Full-screen element should be the requested element!");
11320 NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
11321 nsCOMPtr<nsIDOMElement> fse;
11322 GetMozFullScreenElement(getter_AddRefs(fse));
11323 nsCOMPtr<nsIContent> c(do_QueryInterface(fse));
11324 NS_ASSERTION(c->AsElement() == aElement,
11325 "GetMozFullScreenElement should match GetFullScreenElement()");
11326 #endif
11328 // The origin which is fullscreen changed, send a notifiction so that the
11329 // root document knows the origin of the document which requested fullscreen.
11330 // This is used for the fullscreen approval UI. If we're in a child
11331 // process, the root BrowserElementChild listens for this notification,
11332 // and forwards it across to its BrowserElementParent, which
11333 // re-broadcasts the message for the root document in its process.
11334 if (aNotifyOnOriginChange &&
11335 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11336 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11337 nsIDocument* root = GetFullscreenRootDocument(this);
11338 nsAutoString origin;
11339 nsContentUtils::GetUTFOrigin(NodePrincipal(), origin);
11340 os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
11343 // Make the window full-screen. Note we must make the state changes above
11344 // before making the window full-screen, as then the document reports as
11345 // being in full-screen mode when the chrome "fullscreen" event fires,
11346 // enabling chrome to distinguish between browser and dom full-screen
11347 // modes. Also note that nsGlobalWindow::SetFullScreen() (which
11348 // SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
11349 // and does not operate on the a per-nsIDOMWindow basis.
11350 SetWindowFullScreen(this, true);
11353 NS_IMETHODIMP
11354 nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
11356 ErrorResult rv;
11357 Element* el = GetMozFullScreenElement(rv);
11358 if (rv.Failed()) {
11359 return rv.ErrorCode();
11361 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
11362 retval.forget(aFullScreenElement);
11363 return NS_OK;
11366 Element*
11367 nsDocument::GetMozFullScreenElement(ErrorResult& rv)
11369 if (IsFullScreenDoc()) {
11370 // Must have a full-screen element while in full-screen mode.
11371 Element* el = GetFullScreenElement();
11372 if (!el) {
11373 rv.Throw(NS_ERROR_UNEXPECTED);
11375 return el;
11377 return nullptr;
11380 Element*
11381 nsDocument::GetFullScreenElement()
11383 Element* element = FullScreenStackTop();
11384 NS_ASSERTION(!element ||
11385 element->IsFullScreenAncestor(),
11386 "Fullscreen element should have fullscreen styles applied");
11387 return element;
11390 NS_IMETHODIMP
11391 nsDocument::GetMozFullScreen(bool *aFullScreen)
11393 *aFullScreen = MozFullScreen();
11394 return NS_OK;
11397 NS_IMETHODIMP
11398 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
11400 NS_ENSURE_ARG_POINTER(aFullScreen);
11401 *aFullScreen = MozFullScreenEnabled();
11402 return NS_OK;
11405 bool
11406 nsDocument::MozFullScreenEnabled()
11408 return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false);
11411 static bool
11412 HasFullScreenSubDocument(nsIDocument* aDoc)
11414 uint32_t count = CountFullscreenSubDocuments(aDoc);
11415 NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
11416 return count >= 1;
11419 bool
11420 nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
11422 if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) {
11423 // Chrome code can always use the full-screen API, provided it's not
11424 // explicitly disabled. Note IsCallerChrome() returns true when running
11425 // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an
11426 // nsRunnable!
11427 return true;
11430 if (!nsContentUtils::IsFullScreenApiEnabled()) {
11431 LogFullScreenDenied(aLogFailure, "FullScreenDeniedDisabled", this);
11432 return false;
11434 if (!IsVisible()) {
11435 LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this);
11436 return false;
11438 if (HasFullScreenSubDocument(this)) {
11439 LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this);
11440 return false;
11443 // Ensure that all ancestor <iframe> elements have the allowfullscreen
11444 // boolean attribute set.
11445 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
11446 bool allowed = false;
11447 if (docShell) {
11448 docShell->GetFullscreenAllowed(&allowed);
11450 if (!allowed) {
11451 LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this);
11454 return allowed;
11457 static void
11458 DispatchPointerLockChange(nsIDocument* aTarget)
11460 if (!aTarget) {
11461 return;
11464 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11465 new AsyncEventDispatcher(aTarget,
11466 NS_LITERAL_STRING("mozpointerlockchange"),
11467 true,
11468 false);
11469 asyncDispatcher->PostDOMEvent();
11472 static void
11473 DispatchPointerLockError(nsIDocument* aTarget)
11475 if (!aTarget) {
11476 return;
11479 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11480 new AsyncEventDispatcher(aTarget,
11481 NS_LITERAL_STRING("mozpointerlockerror"),
11482 true,
11483 false);
11484 asyncDispatcher->PostDOMEvent();
11487 mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest;
11489 class nsPointerLockPermissionRequest : public nsRunnable,
11490 public nsIContentPermissionRequest
11492 public:
11493 nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller)
11494 : mElement(do_GetWeakReference(aElement)),
11495 mDocument(do_GetWeakReference(aElement->OwnerDoc())),
11496 mUserInputOrChromeCaller(aUserInputOrChromeCaller) {}
11498 NS_DECL_ISUPPORTS_INHERITED
11499 NS_DECL_NSICONTENTPERMISSIONREQUEST
11501 NS_IMETHOD Run()
11503 nsCOMPtr<Element> e = do_QueryReferent(mElement);
11504 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11505 if (!e || !d || gPendingPointerLockRequest != this ||
11506 e->GetCurrentDoc() != d) {
11507 Handled();
11508 DispatchPointerLockError(d);
11509 return NS_OK;
11512 // We're about to enter fullscreen mode.
11513 nsDocument* doc = static_cast<nsDocument*>(d.get());
11514 if (doc->mAsyncFullscreenPending ||
11515 (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) {
11516 // We're still waiting for approval.
11517 return NS_OK;
11520 if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) {
11521 Allow(JS::UndefinedHandleValue);
11522 return NS_OK;
11525 // In non-fullscreen mode user input (or chrome caller) is required!
11526 // Also, don't let the page to try to get the permission too many times.
11527 if (!mUserInputOrChromeCaller ||
11528 doc->mCancelledPointerLockRequests > 2) {
11529 Handled();
11530 DispatchPointerLockError(d);
11531 return NS_OK;
11534 // Handling a request from user input in non-fullscreen mode.
11535 // Do a normal permission check.
11536 nsCOMPtr<nsPIDOMWindow> window = doc->GetInnerWindow();
11537 nsContentPermissionUtils::AskPermission(this, window);
11538 return NS_OK;
11541 void Handled()
11543 mElement = nullptr;
11544 mDocument = nullptr;
11545 if (gPendingPointerLockRequest == this) {
11546 gPendingPointerLockRequest = nullptr;
11550 nsWeakPtr mElement;
11551 nsWeakPtr mDocument;
11552 bool mUserInputOrChromeCaller;
11554 protected:
11555 virtual ~nsPointerLockPermissionRequest() {}
11558 NS_IMPL_ISUPPORTS_INHERITED(nsPointerLockPermissionRequest,
11559 nsRunnable,
11560 nsIContentPermissionRequest)
11562 NS_IMETHODIMP
11563 nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
11565 nsTArray<nsString> emptyOptions;
11566 return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
11567 NS_LITERAL_CSTRING("unused"),
11568 emptyOptions,
11569 aTypes);
11572 NS_IMETHODIMP
11573 nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
11575 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11576 if (d) {
11577 NS_ADDREF(*aPrincipal = d->NodePrincipal());
11579 return NS_OK;
11582 NS_IMETHODIMP
11583 nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow)
11585 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11586 if (d) {
11587 NS_IF_ADDREF(*aWindow = d->GetInnerWindow());
11589 return NS_OK;
11592 NS_IMETHODIMP
11593 nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement)
11595 // It is enough to implement GetWindow.
11596 *aElement = nullptr;
11597 return NS_OK;
11600 NS_IMETHODIMP
11601 nsPointerLockPermissionRequest::Cancel()
11603 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11604 Handled();
11605 if (d) {
11606 static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++;
11607 DispatchPointerLockError(d);
11609 return NS_OK;
11612 NS_IMETHODIMP
11613 nsPointerLockPermissionRequest::Allow(JS::HandleValue aChoices)
11615 MOZ_ASSERT(aChoices.isUndefined());
11617 nsCOMPtr<Element> e = do_QueryReferent(mElement);
11618 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
11619 nsDocument* d = static_cast<nsDocument*>(doc.get());
11620 if (!e || !d || gPendingPointerLockRequest != this ||
11621 e->GetCurrentDoc() != d ||
11622 (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) {
11623 Handled();
11624 DispatchPointerLockError(d);
11625 return NS_OK;
11628 // Mark handled here so that we don't need to call it everywhere below.
11629 Handled();
11631 nsCOMPtr<Element> pointerLockedElement =
11632 do_QueryReferent(EventStateManager::sPointerLockedElement);
11633 if (e == pointerLockedElement) {
11634 DispatchPointerLockChange(d);
11635 return NS_OK;
11638 // Note, we must bypass focus change, so pass true as the last parameter!
11639 if (!d->ShouldLockPointer(e, pointerLockedElement, true)) {
11640 DispatchPointerLockError(d);
11641 return NS_OK;
11644 if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
11645 DispatchPointerLockError(d);
11646 return NS_OK;
11649 d->mCancelledPointerLockRequests = 0;
11650 e->SetPointerLock();
11651 EventStateManager::sPointerLockedElement = do_GetWeakReference(e);
11652 EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
11653 NS_ASSERTION(EventStateManager::sPointerLockedElement &&
11654 EventStateManager::sPointerLockedDoc,
11655 "aElement and this should support weak references!");
11657 DispatchPointerLockChange(d);
11658 return NS_OK;
11661 void
11662 nsDocument::SetApprovedForFullscreen(bool aIsApproved)
11664 mIsApprovedForFullscreen = aIsApproved;
11667 nsresult
11668 nsDocument::Observe(nsISupports *aSubject,
11669 const char *aTopic,
11670 const char16_t *aData)
11672 if (strcmp("fullscreen-approved", aTopic) == 0) {
11673 nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
11674 if (subject != this) {
11675 return NS_OK;
11677 SetApprovedForFullscreen(true);
11678 if (gPendingPointerLockRequest) {
11679 // We have a request pending. Create a clone of it and re-dispatch so that
11680 // Run() method gets called again.
11681 nsCOMPtr<Element> el =
11682 do_QueryReferent(gPendingPointerLockRequest->mElement);
11683 nsCOMPtr<nsIDocument> doc =
11684 do_QueryReferent(gPendingPointerLockRequest->mDocument);
11685 bool userInputOrChromeCaller =
11686 gPendingPointerLockRequest->mUserInputOrChromeCaller;
11687 gPendingPointerLockRequest->Handled();
11688 if (doc == this && el && el->GetCurrentDoc() == doc) {
11689 nsPointerLockPermissionRequest* clone =
11690 new nsPointerLockPermissionRequest(el, userInputOrChromeCaller);
11691 gPendingPointerLockRequest = clone;
11692 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
11693 NS_DispatchToMainThread(r);
11696 } else if (strcmp("app-theme-changed", aTopic) == 0) {
11697 if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()) &&
11698 !IsUnstyledDocument()) {
11699 // We don't want to style the chrome window, only app ones.
11700 OnAppThemeChanged();
11703 return NS_OK;
11706 void
11707 nsDocument::OnAppThemeChanged()
11709 // Bail out if there is no theme support set up properly.
11710 auto themeOrigin = Preferences::GetString("b2g.theme.origin");
11711 if (!themeOrigin || !Preferences::GetBool("dom.mozApps.themable")) {
11712 return;
11715 for (int32_t i = 0; i < GetNumberOfStyleSheets(); i++) {
11716 nsRefPtr<CSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
11717 if (!sheet) {
11718 continue;
11721 nsINode* owningNode = sheet->GetOwnerNode();
11722 if (!owningNode) {
11723 continue;
11725 // Get a DOM stylesheet link to check the href against the theme origin.
11726 nsIURI* sheetURI = sheet->GetOriginalURI();
11727 if (!sheetURI) {
11728 continue;
11730 nsAutoString sheetOrigin;
11731 nsContentUtils::GetUTFOrigin(sheetURI, sheetOrigin);
11732 if (!sheetOrigin.Equals(themeOrigin)) {
11733 continue;
11736 // Finally getting a Stylesheet link.
11737 nsCOMPtr<nsIStyleSheetLinkingElement> link = do_QueryInterface(owningNode);
11738 if (!link) {
11739 continue;
11741 bool willNotify;
11742 bool isAlternate;
11743 link->UpdateStyleSheet(nullptr, &willNotify, &isAlternate, true);
11747 void
11748 nsDocument::RequestPointerLock(Element* aElement)
11750 NS_ASSERTION(aElement,
11751 "Must pass non-null element to nsDocument::RequestPointerLock");
11753 nsCOMPtr<Element> pointerLockedElement =
11754 do_QueryReferent(EventStateManager::sPointerLockedElement);
11755 if (aElement == pointerLockedElement) {
11756 DispatchPointerLockChange(this);
11757 return;
11760 if (!ShouldLockPointer(aElement, pointerLockedElement)) {
11761 DispatchPointerLockError(this);
11762 return;
11765 bool userInputOrChromeCaller = EventStateManager::IsHandlingUserInput() ||
11766 nsContentUtils::IsCallerChrome();
11768 gPendingPointerLockRequest =
11769 new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller);
11770 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
11771 NS_DispatchToMainThread(r);
11774 bool
11775 nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock,
11776 bool aNoFocusCheck)
11778 // Check if pointer lock pref is enabled
11779 if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
11780 NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled");
11781 return false;
11784 if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) {
11785 NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document");
11786 return false;
11789 if (!aElement->IsInDoc()) {
11790 NS_WARNING("ShouldLockPointer(): Element without Document");
11791 return false;
11794 if (mSandboxFlags & SANDBOXED_POINTER_LOCK) {
11795 NS_WARNING("ShouldLockPointer(): Document is sandboxed and doesn't allow pointer-lock");
11796 return false;
11799 // Check if the element is in a document with a docshell.
11800 nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
11801 if (!ownerDoc->GetContainer()) {
11802 return false;
11804 nsCOMPtr<nsPIDOMWindow> ownerWindow = ownerDoc->GetWindow();
11805 if (!ownerWindow) {
11806 return false;
11808 nsCOMPtr<nsPIDOMWindow> ownerInnerWindow = ownerDoc->GetInnerWindow();
11809 if (!ownerInnerWindow) {
11810 return false;
11812 if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
11813 return false;
11816 nsCOMPtr<nsIDOMWindow> top;
11817 ownerWindow->GetScriptableTop(getter_AddRefs(top));
11818 nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top);
11819 if (!piTop || !piTop->GetExtantDoc() ||
11820 piTop->GetExtantDoc()->Hidden()) {
11821 NS_WARNING("ShouldLockPointer(): Top document isn't visible.");
11822 return false;
11825 if (!aNoFocusCheck) {
11826 mozilla::ErrorResult rv;
11827 if (!piTop->GetExtantDoc()->HasFocus(rv)) {
11828 NS_WARNING("ShouldLockPointer(): Top document isn't focused.");
11829 return false;
11833 return true;
11836 bool
11837 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
11839 // NOTE: aElement will be nullptr when unlocking.
11840 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
11841 if (!window) {
11842 NS_WARNING("SetPointerLock(): No Window");
11843 return false;
11846 nsIDocShell *docShell = window->GetDocShell();
11847 if (!docShell) {
11848 NS_WARNING("SetPointerLock(): No DocShell (window already closed?)");
11849 return false;
11852 nsRefPtr<nsPresContext> presContext;
11853 docShell->GetPresContext(getter_AddRefs(presContext));
11854 if (!presContext) {
11855 NS_WARNING("SetPointerLock(): Unable to get presContext in \
11856 domWindow->GetDocShell()->GetPresContext()");
11857 return false;
11860 nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
11861 if (!shell) {
11862 NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()");
11863 return false;
11866 nsIFrame* rootFrame = shell->GetRootFrame();
11867 if (!rootFrame) {
11868 NS_WARNING("SetPointerLock(): Unable to get root frame");
11869 return false;
11872 nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
11873 if (!widget) {
11874 NS_WARNING("SetPointerLock(): Unable to find widget in \
11875 shell->GetRootFrame()->GetNearestWidget();");
11876 return false;
11879 if (aElement && (aElement->OwnerDoc() != this)) {
11880 NS_WARNING("SetPointerLock(): Element not in this document.");
11881 return false;
11884 // Hide the cursor and set pointer lock for future mouse events
11885 nsRefPtr<EventStateManager> esm = presContext->EventStateManager();
11886 esm->SetCursor(aCursorStyle, nullptr, false,
11887 0.0f, 0.0f, widget, true);
11888 esm->SetPointerLock(widget, aElement);
11890 return true;
11893 void
11894 nsDocument::UnlockPointer(nsIDocument* aDoc)
11896 if (!EventStateManager::sIsPointerLocked) {
11897 return;
11900 nsCOMPtr<nsIDocument> pointerLockedDoc =
11901 do_QueryReferent(EventStateManager::sPointerLockedDoc);
11902 if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
11903 return;
11905 nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
11906 if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
11907 return;
11910 nsCOMPtr<Element> pointerLockedElement =
11911 do_QueryReferent(EventStateManager::sPointerLockedElement);
11912 if (pointerLockedElement) {
11913 pointerLockedElement->ClearPointerLock();
11916 EventStateManager::sPointerLockedElement = nullptr;
11917 EventStateManager::sPointerLockedDoc = nullptr;
11918 static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc;
11919 gPendingPointerLockRequest = nullptr;
11920 DispatchPointerLockChange(pointerLockedDoc);
11923 void
11924 nsIDocument::UnlockPointer(nsIDocument* aDoc)
11926 nsDocument::UnlockPointer(aDoc);
11929 NS_IMETHODIMP
11930 nsDocument::MozExitPointerLock()
11932 nsIDocument::MozExitPointerLock();
11933 return NS_OK;
11936 NS_IMETHODIMP
11937 nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
11939 Element* el = nsIDocument::GetMozPointerLockElement();
11940 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
11941 retval.forget(aPointerLockedElement);
11942 return NS_OK;
11945 Element*
11946 nsIDocument::GetMozPointerLockElement()
11948 nsCOMPtr<Element> pointerLockedElement =
11949 do_QueryReferent(EventStateManager::sPointerLockedElement);
11950 if (!pointerLockedElement) {
11951 return nullptr;
11954 // Make sure pointer locked element is in the same document.
11955 nsCOMPtr<nsIDocument> pointerLockedDoc =
11956 do_QueryReferent(EventStateManager::sPointerLockedDoc);
11957 if (pointerLockedDoc != this) {
11958 return nullptr;
11961 return pointerLockedElement;
11964 void
11965 nsDocument::XPCOMShutdown()
11967 gPendingPointerLockRequest = nullptr;
11968 sProcessingStack.reset();
11971 void
11972 nsDocument::UpdateVisibilityState()
11974 dom::VisibilityState oldState = mVisibilityState;
11975 mVisibilityState = GetVisibilityState();
11976 if (oldState != mVisibilityState) {
11977 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
11978 NS_LITERAL_STRING("visibilitychange"),
11979 /* bubbles = */ true,
11980 /* cancelable = */ false);
11981 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
11982 NS_LITERAL_STRING("mozvisibilitychange"),
11983 /* bubbles = */ true,
11984 /* cancelable = */ false);
11986 EnumerateActivityObservers(NotifyActivityChanged, nullptr);
11990 VisibilityState
11991 nsDocument::GetVisibilityState() const
11993 // We have to check a few pieces of information here:
11994 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
11995 // 2) Do we have an outer window? If not, we're hidden. Note that we don't
11996 // want to use GetWindow here because it does weird groveling for windows
11997 // in some cases.
11998 // 3) Is our outer window background? If so, we're hidden.
11999 // Otherwise, we're visible.
12000 if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
12001 mWindow->GetOuterWindow()->IsBackground()) {
12002 return dom::VisibilityState::Hidden;
12005 return dom::VisibilityState::Visible;
12008 /* virtual */ void
12009 nsDocument::PostVisibilityUpdateEvent()
12011 nsCOMPtr<nsIRunnable> event =
12012 NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState);
12013 NS_DispatchToMainThread(event);
12016 NS_IMETHODIMP
12017 nsDocument::GetMozHidden(bool* aHidden)
12019 *aHidden = MozHidden();
12020 return NS_OK;
12023 NS_IMETHODIMP
12024 nsDocument::GetHidden(bool* aHidden)
12026 *aHidden = Hidden();
12027 return NS_OK;
12030 NS_IMETHODIMP
12031 nsDocument::GetMozVisibilityState(nsAString& aState)
12033 WarnOnceAbout(ePrefixedVisibilityAPI);
12034 return GetVisibilityState(aState);
12037 NS_IMETHODIMP
12038 nsDocument::GetVisibilityState(nsAString& aState)
12040 const EnumEntry& entry =
12041 VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
12042 aState.AssignASCII(entry.value, entry.length);
12043 return NS_OK;
12046 /* virtual */ void
12047 nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
12049 aWindowSizes->mDOMOtherSize +=
12050 nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12052 if (mPresShell) {
12053 mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
12054 &aWindowSizes->mArenaStats,
12055 &aWindowSizes->mLayoutPresShellSize,
12056 &aWindowSizes->mLayoutStyleSetsSize,
12057 &aWindowSizes->mLayoutTextRunsSize,
12058 &aWindowSizes->mLayoutPresContextSize);
12061 aWindowSizes->mPropertyTablesSize +=
12062 mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12063 for (uint32_t i = 0, count = mExtraPropertyTables.Length();
12064 i < count; ++i) {
12065 aWindowSizes->mPropertyTablesSize +=
12066 mExtraPropertyTables[i]->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12069 if (EventListenerManager* elm = GetExistingListenerManager()) {
12070 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
12073 // Measurement of the following members may be added later if DMD finds it
12074 // is worthwhile:
12075 // - many!
12078 void
12079 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
12081 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
12082 DocAddSizeOfExcludingThis(aWindowSizes);
12085 static size_t
12086 SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
12087 MallocSizeOf aMallocSizeOf,
12088 void* aData)
12090 if (!aStyleSheet->GetOwningDocument()) {
12091 // Avoid over-reporting shared sheets.
12092 return 0;
12094 return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
12097 size_t
12098 nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
12100 // This SizeOfExcludingThis() overrides the one from nsINode. But
12101 // nsDocuments can only appear at the top of the DOM tree, and we use the
12102 // specialized DocAddSizeOfExcludingThis() in that case. So this should never
12103 // be called.
12104 MOZ_CRASH();
12107 void
12108 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
12110 nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
12112 for (nsIContent* node = nsINode::GetFirstChild();
12113 node;
12114 node = node->GetNextNode(this))
12116 size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12117 size_t* p;
12119 switch (node->NodeType()) {
12120 case nsIDOMNode::ELEMENT_NODE:
12121 p = &aWindowSizes->mDOMElementNodesSize;
12122 break;
12123 case nsIDOMNode::TEXT_NODE:
12124 p = &aWindowSizes->mDOMTextNodesSize;
12125 break;
12126 case nsIDOMNode::CDATA_SECTION_NODE:
12127 p = &aWindowSizes->mDOMCDATANodesSize;
12128 break;
12129 case nsIDOMNode::COMMENT_NODE:
12130 p = &aWindowSizes->mDOMCommentNodesSize;
12131 break;
12132 default:
12133 p = &aWindowSizes->mDOMOtherSize;
12134 break;
12137 *p += nodeSize;
12139 if (EventListenerManager* elm = node->GetExistingListenerManager()) {
12140 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
12144 aWindowSizes->mStyleSheetsSize +=
12145 mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12146 aWindowSizes->mMallocSizeOf);
12147 // Note that we do not own the sheets pointed to by mOnDemandBuiltInUASheets
12148 // (the nsLayoutStyleSheetCache singleton does) so pass nullptr as the
12149 // aSizeOfElementIncludingThis callback argument.
12150 aWindowSizes->mStyleSheetsSize +=
12151 mOnDemandBuiltInUASheets.SizeOfExcludingThis(nullptr,
12152 aWindowSizes->mMallocSizeOf);
12153 aWindowSizes->mStyleSheetsSize +=
12154 mAdditionalSheets[eAgentSheet].
12155 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12156 aWindowSizes->mMallocSizeOf);
12157 aWindowSizes->mStyleSheetsSize +=
12158 mAdditionalSheets[eUserSheet].
12159 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12160 aWindowSizes->mMallocSizeOf);
12161 aWindowSizes->mStyleSheetsSize +=
12162 mAdditionalSheets[eAuthorSheet].
12163 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12164 aWindowSizes->mMallocSizeOf);
12165 // Lumping in the loader with the style-sheets size is not ideal,
12166 // but most of the things in there are in fact stylesheets, so it
12167 // doesn't seem worthwhile to separate it out.
12168 aWindowSizes->mStyleSheetsSize +=
12169 CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12171 aWindowSizes->mDOMOtherSize +=
12172 mAttrStyleSheet ?
12173 mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
12176 aWindowSizes->mDOMOtherSize +=
12177 mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf);
12179 aWindowSizes->mDOMOtherSize +=
12180 mIdentifierMap.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
12182 // Measurement of the following members may be added later if DMD finds it
12183 // is worthwhile:
12184 // - many!
12187 NS_IMETHODIMP
12188 nsDocument::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn)
12190 return nsINode::QuerySelector(aSelector, aReturn);
12193 NS_IMETHODIMP
12194 nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn)
12196 return nsINode::QuerySelectorAll(aSelector, aReturn);
12199 already_AddRefed<nsIDocument>
12200 nsIDocument::Constructor(const GlobalObject& aGlobal,
12201 ErrorResult& rv)
12203 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
12204 if (!global) {
12205 rv.Throw(NS_ERROR_UNEXPECTED);
12206 return nullptr;
12209 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
12210 if (!prin) {
12211 rv.Throw(NS_ERROR_UNEXPECTED);
12212 return nullptr;
12215 nsCOMPtr<nsIURI> uri;
12216 NS_NewURI(getter_AddRefs(uri), "about:blank");
12217 if (!uri) {
12218 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
12219 return nullptr;
12222 nsCOMPtr<nsIDOMDocument> document;
12223 nsresult res =
12224 NS_NewDOMDocument(getter_AddRefs(document),
12225 NullString(),
12226 EmptyString(),
12227 nullptr,
12228 uri,
12229 uri,
12230 prin->GetPrincipal(),
12231 true,
12232 global,
12233 DocumentFlavorPlain);
12234 if (NS_FAILED(res)) {
12235 rv.Throw(res);
12236 return nullptr;
12239 nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
12240 doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
12242 return doc.forget();
12245 XPathExpression*
12246 nsIDocument::CreateExpression(const nsAString& aExpression,
12247 nsIDOMXPathNSResolver* aResolver,
12248 ErrorResult& rv)
12250 return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
12253 already_AddRefed<nsIDOMXPathNSResolver>
12254 nsIDocument::CreateNSResolver(nsINode* aNodeResolver,
12255 ErrorResult& rv)
12257 return XPathEvaluator()->CreateNSResolver(aNodeResolver, rv);
12260 already_AddRefed<XPathResult>
12261 nsIDocument::Evaluate(JSContext* aCx, const nsAString& aExpression,
12262 nsINode* aContextNode, nsIDOMXPathNSResolver* aResolver,
12263 uint16_t aType, JS::Handle<JSObject*> aResult,
12264 ErrorResult& rv)
12266 return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
12267 aType, aResult, rv);
12270 NS_IMETHODIMP
12271 nsDocument::CreateNSResolver(nsIDOMNode* aNodeResolver,
12272 nsIDOMXPathNSResolver** aResult)
12274 return XPathEvaluator()->CreateNSResolver(aNodeResolver, aResult);
12277 NS_IMETHODIMP
12278 nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode,
12279 nsIDOMXPathNSResolver* aResolver, uint16_t aType,
12280 nsISupports* aInResult, nsISupports** aResult)
12282 return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
12283 aInResult, aResult);
12286 // This is just a hack around the fact that window.document is not
12287 // [Unforgeable] yet.
12288 JSObject*
12289 nsIDocument::WrapObject(JSContext *aCx)
12291 MOZ_ASSERT(IsDOMBinding());
12293 JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx));
12294 if (!obj) {
12295 return nullptr;
12298 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(GetInnerWindow());
12299 if (!win ||
12300 static_cast<nsGlobalWindow*>(win.get())->IsDOMBinding()) {
12301 // No window or window on new DOM binding, nothing else to do here.
12302 return obj;
12305 if (this != win->GetExtantDoc()) {
12306 // We're not the current document; we're also done here
12307 return obj;
12310 JSAutoCompartment ac(aCx, obj);
12312 JS::Rooted<JS::Value> winVal(aCx);
12313 nsresult rv = nsContentUtils::WrapNative(aCx, win, &NS_GET_IID(nsIDOMWindow),
12314 &winVal,
12315 false);
12316 if (NS_FAILED(rv)) {
12317 Throw(aCx, rv);
12318 return nullptr;
12321 NS_NAMED_LITERAL_STRING(doc_str, "document");
12323 JS::Rooted<JSObject*> winObj(aCx, &winVal.toObject());
12324 if (!JS_DefineUCProperty(aCx, winObj, doc_str.get(),
12325 doc_str.Length(), obj,
12326 JSPROP_READONLY | JSPROP_ENUMERATE,
12327 JS_PropertyStub, JS_StrictPropertyStub)) {
12328 return nullptr;
12331 return obj;
12334 XPathEvaluator*
12335 nsIDocument::XPathEvaluator()
12337 if (!mXPathEvaluator) {
12338 mXPathEvaluator = new dom::XPathEvaluator(this);
12340 return mXPathEvaluator;
12343 already_AddRefed<nsIDocumentEncoder>
12344 nsIDocument::GetCachedEncoder()
12346 return mCachedEncoder.forget();
12349 void
12350 nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
12352 mCachedEncoder = aEncoder;
12355 void
12356 nsIDocument::SetContentTypeInternal(const nsACString& aType)
12358 mCachedEncoder = nullptr;
12359 mContentType = aType;
12362 nsILoadContext*
12363 nsIDocument::GetLoadContext() const
12365 return mDocumentContainer;
12368 nsIDocShell*
12369 nsIDocument::GetDocShell() const
12371 return mDocumentContainer;
12374 void
12375 nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
12377 mStateObjectContainer = scContainer;
12378 mStateObjectCached = nullptr;
12381 already_AddRefed<Element>
12382 nsIDocument::CreateHTMLElement(nsIAtom* aTag)
12384 nsRefPtr<mozilla::dom::NodeInfo> nodeInfo;
12385 nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
12386 nsIDOMNode::ELEMENT_NODE);
12387 MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
12389 nsCOMPtr<Element> element;
12390 DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
12391 nodeInfo.forget(),
12392 mozilla::dom::NOT_FROM_PARSER);
12394 MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
12395 return element.forget();
12398 bool
12399 MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
12401 nsCOMArray<nsIDocument>* documents =
12402 static_cast<nsCOMArray<nsIDocument>*>(aData);
12403 if (aDoc) {
12404 aDoc->SetIsInSyncOperation(true);
12405 documents->AppendObject(aDoc);
12406 aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
12408 return true;
12411 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
12413 mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
12414 nsContentUtils::SetMicroTaskLevel(0);
12415 if (aDoc) {
12416 nsPIDOMWindow* win = aDoc->GetWindow();
12417 if (win) {
12418 nsCOMPtr<nsIDOMWindow> topWindow;
12419 win->GetTop(getter_AddRefs(topWindow));
12420 nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
12421 if (top) {
12422 nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
12423 MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
12429 nsAutoSyncOperation::~nsAutoSyncOperation()
12431 for (int32_t i = 0; i < mDocuments.Count(); ++i) {
12432 mDocuments[i]->SetIsInSyncOperation(false);
12434 nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);