Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsUnicharUtils.h"
32 #include "nsContentList.h"
33 #include "nsIObserver.h"
34 #include "nsIBaseWindow.h"
35 #include "mozilla/css/Loader.h"
36 #include "mozilla/css/ImageLoader.h"
37 #include "nsDocShell.h"
38 #include "nsIDocShellTreeItem.h"
39 #include "nsCOMArray.h"
40 #include "nsDOMClassInfo.h"
41 #include "nsCxPusher.h"
43 #include "mozilla/AsyncEventDispatcher.h"
44 #include "mozilla/BasicEvents.h"
45 #include "mozilla/EventListenerManager.h"
46 #include "mozilla/EventStateManager.h"
47 #include "nsIDOMNodeFilter.h"
49 #include "nsIDOMStyleSheet.h"
50 #include "mozilla/dom/Attr.h"
51 #include "nsIDOMDOMImplementation.h"
52 #include "nsIDOMDocumentXBL.h"
53 #include "mozilla/dom/Element.h"
54 #include "nsGenericHTMLElement.h"
55 #include "mozilla/dom/CDATASection.h"
56 #include "mozilla/dom/ProcessingInstruction.h"
57 #include "nsDOMString.h"
58 #include "nsNodeUtils.h"
59 #include "nsLayoutUtils.h" // for GetFrameForPoint
60 #include "nsIFrame.h"
61 #include "nsITabChild.h"
63 #include "nsRange.h"
64 #include "nsIDOMText.h"
65 #include "nsIDOMComment.h"
66 #include "mozilla/dom/DocumentType.h"
67 #include "mozilla/dom/NodeIterator.h"
68 #include "mozilla/dom/TreeWalker.h"
70 #include "nsIServiceManager.h"
72 #include "nsContentCID.h"
73 #include "nsError.h"
74 #include "nsPresShell.h"
75 #include "nsPresContext.h"
76 #include "nsIJSON.h"
77 #include "nsThreadUtils.h"
78 #include "nsNodeInfoManager.h"
79 #include "nsIFileChannel.h"
80 #include "nsIMultiPartChannel.h"
81 #include "nsIRefreshURI.h"
82 #include "nsIWebNavigation.h"
83 #include "nsIScriptError.h"
84 #include "nsStyleSheetService.h"
86 #include "nsNetUtil.h" // for NS_MakeAbsoluteURI
88 #include "nsIScriptSecurityManager.h"
89 #include "nsIPrincipal.h"
91 #include "nsIDOMWindow.h"
92 #include "nsPIDOMWindow.h"
93 #include "nsIDOMElement.h"
94 #include "nsFocusManager.h"
96 // for radio group stuff
97 #include "nsIDOMHTMLInputElement.h"
98 #include "nsIRadioVisitor.h"
99 #include "nsIFormControl.h"
101 #include "nsBidiUtils.h"
103 #include "nsIDOMUserDataHandler.h"
104 #include "nsIDOMXPathExpression.h"
105 #include "nsIDOMXPathNSResolver.h"
106 #include "nsIParserService.h"
107 #include "nsContentCreatorFunctions.h"
109 #include "nsIScriptContext.h"
110 #include "nsBindingManager.h"
111 #include "nsIDOMHTMLDocument.h"
112 #include "nsHTMLDocument.h"
113 #include "nsIDOMHTMLFormElement.h"
114 #include "nsIRequest.h"
115 #include "nsHostObjectProtocolHandler.h"
117 #include "nsCharsetAlias.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 "nsIDOMPageTransitionEvent.h"
143 #include "nsIDOMStyleRuleChangeEvent.h"
144 #include "nsIDOMStyleSheetChangeEvent.h"
145 #include "nsIDOMStyleSheetApplicableStateChangeEvent.h"
146 #include "nsJSUtils.h"
147 #include "nsFrameLoader.h"
148 #include "nsEscape.h"
149 #include "nsObjectLoadingContent.h"
150 #include "nsHtml5TreeOpExecutor.h"
151 #include "nsIDOMElementReplaceEvent.h"
152 #include "mozilla/dom/HTMLLinkElement.h"
153 #include "mozilla/dom/HTMLMediaElement.h"
154 #ifdef MOZ_MEDIA_NAVIGATOR
155 #include "mozilla/MediaManager.h"
156 #endif // MOZ_MEDIA_NAVIGATOR
157 #ifdef MOZ_WEBRTC
158 #include "IPeerConnection.h"
159 #endif // MOZ_WEBRTC
161 #include "mozAutoDocUpdate.h"
162 #include "nsGlobalWindow.h"
163 #include "mozilla/dom/EncodingUtils.h"
164 #include "mozilla/dom/quota/QuotaManager.h"
165 #include "nsDOMNavigationTiming.h"
167 #include "nsSMILAnimationController.h"
168 #include "imgIContainer.h"
169 #include "nsSVGUtils.h"
170 #include "SVGElementFactory.h"
172 #include "nsRefreshDriver.h"
174 // FOR CSP (autogenerated by xpidl)
175 #include "nsIContentSecurityPolicy.h"
176 #include "nsCSPService.h"
177 #include "nsHTMLStyleSheet.h"
178 #include "nsHTMLCSSStyleSheet.h"
179 #include "mozilla/dom/DOMImplementation.h"
180 #include "mozilla/dom/ShadowRoot.h"
181 #include "mozilla/dom/Comment.h"
182 #include "nsTextNode.h"
183 #include "mozilla/dom/Link.h"
184 #include "mozilla/dom/HTMLElementBinding.h"
185 #include "mozilla/dom/SVGElementBinding.h"
186 #include "nsXULAppAPI.h"
187 #include "mozilla/dom/Touch.h"
188 #include "mozilla/dom/TouchEvent.h"
189 #include "GeneratedEvents.h"
191 #include "mozilla/Preferences.h"
193 #include "imgILoader.h"
194 #include "imgRequestProxy.h"
195 #include "nsWrapperCacheInlines.h"
196 #include "nsSandboxFlags.h"
197 #include "nsIAppsService.h"
198 #include "mozilla/dom/BindingUtils.h"
199 #include "mozilla/dom/DocumentFragment.h"
200 #include "mozilla/dom/Event.h"
201 #include "mozilla/dom/HTMLBodyElement.h"
202 #include "mozilla/dom/HTMLInputElement.h"
203 #include "mozilla/dom/NodeFilterBinding.h"
204 #include "mozilla/dom/OwningNonNull.h"
205 #include "mozilla/dom/UndoManager.h"
206 #include "mozilla/dom/WebComponentsBinding.h"
207 #include "nsFrame.h"
208 #include "nsDOMCaretPosition.h"
209 #include "nsIDOMHTMLTextAreaElement.h"
210 #include "nsViewportInfo.h"
211 #include "nsIContentPermissionPrompt.h"
212 #include "mozilla/StaticPtr.h"
213 #include "nsITextControlElement.h"
214 #include "nsIDOMNSEditableElement.h"
215 #include "nsIEditor.h"
216 #include "nsIDOMCSSStyleRule.h"
217 #include "mozilla/css/Rule.h"
218 #include "nsIDOMLocation.h"
219 #include "nsIHttpChannelInternal.h"
220 #include "nsISecurityConsoleMessage.h"
221 #include "nsCharSeparatedTokenizer.h"
222 #include "mozilla/dom/XPathEvaluator.h"
223 #include "nsIDocumentEncoder.h"
224 #include "nsIStructuredCloneContainer.h"
225 #include "nsIMutableArray.h"
226 #include "nsContentPermissionHelper.h"
227 #include "mozilla/dom/DOMStringList.h"
228 #include "nsWindowMemoryReporter.h"
230 using namespace mozilla;
231 using namespace mozilla::dom;
233 typedef nsTArray<Link*> LinkArray;
235 #ifdef PR_LOGGING
236 static PRLogModuleInfo* gDocumentLeakPRLog;
237 static PRLogModuleInfo* gCspPRLog;
238 #endif
240 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
242 nsIdentifierMapEntry::~nsIdentifierMapEntry()
243 {
244 }
246 void
247 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
248 {
249 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
250 "mIdentifierMap mNameContentList");
251 aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
253 if (mImageElement) {
254 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
255 "mIdentifierMap mImageElement element");
256 nsIContent* imageElement = mImageElement;
257 aCallback->NoteXPCOMChild(imageElement);
258 }
259 }
261 bool
262 nsIdentifierMapEntry::IsEmpty()
263 {
264 return mIdContentList.Count() == 0 && !mNameContentList &&
265 !mChangeCallbacks && !mImageElement;
266 }
268 Element*
269 nsIdentifierMapEntry::GetIdElement()
270 {
271 return static_cast<Element*>(mIdContentList.SafeElementAt(0));
272 }
274 Element*
275 nsIdentifierMapEntry::GetImageIdElement()
276 {
277 return mImageElement ? mImageElement.get() : GetIdElement();
278 }
280 void
281 nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
282 {
283 for (int32_t i = 0; i < mIdContentList.Count(); ++i) {
284 aElements->AppendObject(static_cast<Element*>(mIdContentList[i]));
285 }
286 }
288 void
289 nsIdentifierMapEntry::AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
290 void* aData, bool aForImage)
291 {
292 if (!mChangeCallbacks) {
293 mChangeCallbacks = new nsTHashtable<ChangeCallbackEntry>;
294 if (!mChangeCallbacks)
295 return;
296 }
298 ChangeCallback cc = { aCallback, aData, aForImage };
299 mChangeCallbacks->PutEntry(cc);
300 }
302 void
303 nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
304 void* aData, bool aForImage)
305 {
306 if (!mChangeCallbacks)
307 return;
308 ChangeCallback cc = { aCallback, aData, aForImage };
309 mChangeCallbacks->RemoveEntry(cc);
310 if (mChangeCallbacks->Count() == 0) {
311 mChangeCallbacks = nullptr;
312 }
313 }
315 struct FireChangeArgs {
316 Element* mFrom;
317 Element* mTo;
318 bool mImageOnly;
319 bool mHaveImageOverride;
320 };
322 namespace mozilla {
323 namespace dom {
325 static PLDHashOperator
326 CustomDefinitionsTraverse(CustomElementHashKey* aKey,
327 CustomElementDefinition* aDefinition,
328 void* aArg)
329 {
330 nsCycleCollectionTraversalCallback* cb =
331 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
333 nsAutoPtr<LifecycleCallbacks>& callbacks = aDefinition->mCallbacks;
335 if (callbacks->mAttributeChangedCallback.WasPassed()) {
336 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
337 "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
338 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttributeChangedCallback.Value());
339 }
341 if (callbacks->mCreatedCallback.WasPassed()) {
342 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
343 "mCustomDefinitions->mCallbacks->mCreatedCallback");
344 cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
345 }
347 if (callbacks->mAttachedCallback.WasPassed()) {
348 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
349 "mCustomDefinitions->mCallbacks->mAttachedCallback");
350 cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value());
351 }
353 if (callbacks->mDetachedCallback.WasPassed()) {
354 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
355 "mCustomDefinitions->mCallbacks->mDetachedCallback");
356 cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value());
357 }
359 return PL_DHASH_NEXT;
360 }
362 static PLDHashOperator
363 CandidatesTraverse(CustomElementHashKey* aKey,
364 nsTArray<nsRefPtr<Element>>* aData,
365 void* aArg)
366 {
367 nsCycleCollectionTraversalCallback *cb =
368 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
369 for (size_t i = 0; i < aData->Length(); ++i) {
370 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mCandidatesMap->Element");
371 cb->NoteXPCOMChild(aData->ElementAt(i));
372 }
373 return PL_DHASH_NEXT;
374 }
376 struct CustomDefinitionTraceArgs
377 {
378 const TraceCallbacks& callbacks;
379 void* closure;
380 };
382 static PLDHashOperator
383 CustomDefinitionTrace(CustomElementHashKey *aKey,
384 CustomElementDefinition *aData,
385 void *aArg)
386 {
387 CustomDefinitionTraceArgs* traceArgs = static_cast<CustomDefinitionTraceArgs*>(aArg);
388 MOZ_ASSERT(aData, "Definition must not be null");
389 traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype",
390 traceArgs->closure);
391 return PL_DHASH_NEXT;
392 }
394 NS_IMPL_CYCLE_COLLECTION_CLASS(Registry)
396 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry)
397 CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure };
398 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace,
399 &customDefinitionArgs);
400 NS_IMPL_CYCLE_COLLECTION_TRACE_END
402 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry)
403 tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionsTraverse, &cb);
404 tmp->mCandidatesMap.EnumerateRead(CandidatesTraverse, &cb);
405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
406 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
408 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry)
409 tmp->mCustomDefinitions.Clear();
410 tmp->mCandidatesMap.Clear();
411 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
413 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry)
414 NS_INTERFACE_MAP_ENTRY(nsISupports)
415 NS_INTERFACE_MAP_END
417 NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry)
418 NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry)
420 Registry::Registry()
421 {
422 mozilla::HoldJSObjects(this);
423 }
425 Registry::~Registry()
426 {
427 mozilla::DropJSObjects(this);
428 }
430 void
431 CustomElementCallback::Call()
432 {
433 ErrorResult rv;
434 switch (mType) {
435 case nsIDocument::eCreated:
436 // For the duration of this callback invocation, the element is being created
437 // flag must be set to true.
438 mOwnerData->mElementIsBeingCreated = true;
439 mOwnerData->mCreatedCallbackInvoked = true;
440 static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
441 mOwnerData->mElementIsBeingCreated = false;
442 break;
443 case nsIDocument::eAttached:
444 static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
445 break;
446 case nsIDocument::eDetached:
447 static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
448 break;
449 case nsIDocument::eAttributeChanged:
450 static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
451 mArgs.name, mArgs.oldValue, mArgs.newValue, rv);
452 break;
453 }
454 }
456 void
457 CustomElementCallback::Traverse(nsCycleCollectionTraversalCallback& aCb) const
458 {
459 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
460 aCb.NoteXPCOMChild(mThisObject);
462 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
463 aCb.NoteXPCOMChild(mCallback);
464 }
466 CustomElementCallback::CustomElementCallback(Element* aThisObject,
467 nsIDocument::ElementCallbackType aCallbackType,
468 mozilla::dom::CallbackFunction* aCallback,
469 CustomElementData* aOwnerData)
470 : mThisObject(aThisObject),
471 mCallback(aCallback),
472 mType(aCallbackType),
473 mOwnerData(aOwnerData)
474 {
475 }
477 CustomElementDefinition::CustomElementDefinition(JSObject* aPrototype,
478 nsIAtom* aType,
479 nsIAtom* aLocalName,
480 LifecycleCallbacks* aCallbacks,
481 uint32_t aNamespaceID,
482 uint32_t aDocOrder)
483 : mPrototype(aPrototype),
484 mType(aType),
485 mLocalName(aLocalName),
486 mCallbacks(aCallbacks),
487 mNamespaceID(aNamespaceID),
488 mDocOrder(aDocOrder)
489 {
490 }
492 CustomElementData::CustomElementData(nsIAtom* aType)
493 : mType(aType),
494 mCurrentCallback(-1),
495 mElementIsBeingCreated(false),
496 mCreatedCallbackInvoked(true),
497 mAssociatedMicroTask(-1)
498 {
499 }
501 void
502 CustomElementData::RunCallbackQueue()
503 {
504 // Note: It's possible to re-enter this method.
505 while (static_cast<uint32_t>(++mCurrentCallback) < mCallbackQueue.Length()) {
506 mCallbackQueue[mCurrentCallback]->Call();
507 }
509 mCallbackQueue.Clear();
510 mCurrentCallback = -1;
511 }
513 } // namespace dom
514 } // namespace mozilla
516 static PLDHashOperator
517 FireChangeEnumerator(nsIdentifierMapEntry::ChangeCallbackEntry *aEntry, void *aArg)
518 {
519 FireChangeArgs* args = static_cast<FireChangeArgs*>(aArg);
520 // Don't fire image changes for non-image observers, and don't fire element
521 // changes for image observers when an image override is active.
522 if (aEntry->mKey.mForImage ? (args->mHaveImageOverride && !args->mImageOnly) :
523 args->mImageOnly)
524 return PL_DHASH_NEXT;
525 return aEntry->mKey.mCallback(args->mFrom, args->mTo, aEntry->mKey.mData)
526 ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
527 }
529 void
530 nsIdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
531 Element* aNewElement,
532 bool aImageOnly)
533 {
534 if (!mChangeCallbacks)
535 return;
537 FireChangeArgs args = { aOldElement, aNewElement, aImageOnly, !!mImageElement };
538 mChangeCallbacks->EnumerateEntries(FireChangeEnumerator, &args);
539 }
541 bool
542 nsIdentifierMapEntry::AddIdElement(Element* aElement)
543 {
544 NS_PRECONDITION(aElement, "Must have element");
545 NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0,
546 "Why is null in our list?");
548 #ifdef DEBUG
549 Element* currentElement =
550 static_cast<Element*>(mIdContentList.SafeElementAt(0));
551 #endif
553 // Common case
554 if (mIdContentList.Count() == 0) {
555 if (!mIdContentList.AppendElement(aElement))
556 return false;
557 NS_ASSERTION(currentElement == nullptr, "How did that happen?");
558 FireChangeCallbacks(nullptr, aElement);
559 return true;
560 }
562 // We seem to have multiple content nodes for the same id, or XUL is messing
563 // with us. Search for the right place to insert the content.
564 int32_t start = 0;
565 int32_t end = mIdContentList.Count();
566 do {
567 NS_ASSERTION(start < end, "Bogus start/end");
569 int32_t cur = (start + end) / 2;
570 NS_ASSERTION(cur >= start && cur < end, "What happened here?");
572 Element* curElement = static_cast<Element*>(mIdContentList[cur]);
573 if (curElement == aElement) {
574 // Already in the list, so already in the right spot. Get out of here.
575 // XXXbz this only happens because XUL does all sorts of random
576 // UpdateIdTableEntry calls. Hate, hate, hate!
577 return true;
578 }
580 if (nsContentUtils::PositionIsBefore(aElement, curElement)) {
581 end = cur;
582 } else {
583 start = cur + 1;
584 }
585 } while (start != end);
587 if (!mIdContentList.InsertElementAt(aElement, start))
588 return false;
590 if (start == 0) {
591 Element* oldElement =
592 static_cast<Element*>(mIdContentList.SafeElementAt(1));
593 NS_ASSERTION(currentElement == oldElement, "How did that happen?");
594 FireChangeCallbacks(oldElement, aElement);
595 }
596 return true;
597 }
599 void
600 nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
601 {
602 NS_PRECONDITION(aElement, "Missing element");
604 // This should only be called while the document is in an update.
605 // Assertions near the call to this method guarantee this.
607 // This could fire in OOM situations
608 // Only assert this in HTML documents for now as XUL does all sorts of weird
609 // crap.
610 NS_ASSERTION(!aElement->OwnerDoc()->IsHTML() ||
611 mIdContentList.IndexOf(aElement) >= 0,
612 "Removing id entry that doesn't exist");
614 // XXXbz should this ever Compact() I guess when all the content is gone
615 // we'll just get cleaned up in the natural order of things...
616 Element* currentElement =
617 static_cast<Element*>(mIdContentList.SafeElementAt(0));
618 mIdContentList.RemoveElement(aElement);
619 if (currentElement == aElement) {
620 FireChangeCallbacks(currentElement,
621 static_cast<Element*>(mIdContentList.SafeElementAt(0)));
622 }
623 }
625 void
626 nsIdentifierMapEntry::SetImageElement(Element* aElement)
627 {
628 Element* oldElement = GetImageIdElement();
629 mImageElement = aElement;
630 Element* newElement = GetImageIdElement();
631 if (oldElement != newElement) {
632 FireChangeCallbacks(oldElement, newElement, true);
633 }
634 }
636 void
637 nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
638 {
639 if (!mNameContentList) {
640 mNameContentList = new nsSimpleContentList(aNode);
641 }
643 mNameContentList->AppendElement(aElement);
644 }
646 void
647 nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
648 {
649 if (mNameContentList) {
650 mNameContentList->RemoveElement(aElement);
651 }
652 }
654 bool
655 nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
656 {
657 Element* idElement = GetIdElement();
658 return idElement &&
659 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
660 }
662 // static
663 size_t
664 nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
665 MallocSizeOf aMallocSizeOf,
666 void*)
667 {
668 return aEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
669 }
671 // Helper structs for the content->subdoc map
673 class SubDocMapEntry : public PLDHashEntryHdr
674 {
675 public:
676 // Both of these are strong references
677 Element *mKey; // must be first, to look like PLDHashEntryStub
678 nsIDocument *mSubDocument;
679 };
681 struct FindContentData
682 {
683 FindContentData(nsIDocument *aSubDoc)
684 : mSubDocument(aSubDoc), mResult(nullptr)
685 {
686 }
688 nsISupports *mSubDocument;
689 Element *mResult;
690 };
693 /**
694 * A struct that holds all the information about a radio group.
695 */
696 struct nsRadioGroupStruct
697 {
698 nsRadioGroupStruct()
699 : mRequiredRadioCount(0)
700 , mGroupSuffersFromValueMissing(false)
701 {}
703 /**
704 * A strong pointer to the currently selected radio button.
705 */
706 nsRefPtr<HTMLInputElement> mSelectedRadioButton;
707 nsCOMArray<nsIFormControl> mRadioButtons;
708 uint32_t mRequiredRadioCount;
709 bool mGroupSuffersFromValueMissing;
710 };
713 nsDOMStyleSheetList::nsDOMStyleSheetList(nsIDocument *aDocument)
714 {
715 mLength = -1;
716 // Not reference counted to avoid circular references.
717 // The document will tell us when its going away.
718 mDocument = aDocument;
719 mDocument->AddObserver(this);
720 }
722 nsDOMStyleSheetList::~nsDOMStyleSheetList()
723 {
724 if (mDocument) {
725 mDocument->RemoveObserver(this);
726 }
727 }
729 NS_IMPL_ISUPPORTS_INHERITED(nsDOMStyleSheetList, StyleSheetList,
730 nsIDocumentObserver,
731 nsIMutationObserver)
733 uint32_t
734 nsDOMStyleSheetList::Length()
735 {
736 if (!mDocument) {
737 return 0;
738 }
740 // XXX Find the number and then cache it. We'll use the
741 // observer notification to figure out if new ones have
742 // been added or removed.
743 if (-1 == mLength) {
744 mLength = mDocument->GetNumberOfStyleSheets();
746 #ifdef DEBUG
747 int32_t i;
748 for (i = 0; i < mLength; i++) {
749 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
750 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
751 NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
752 }
753 #endif
754 }
755 return mLength;
756 }
758 nsCSSStyleSheet*
759 nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
760 {
761 if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
762 aFound = false;
763 return nullptr;
764 }
766 aFound = true;
767 nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
768 NS_ASSERTION(sheet, "Must have a sheet");
770 return static_cast<nsCSSStyleSheet*>(sheet);
771 }
773 void
774 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
775 {
776 mDocument = nullptr;
777 }
779 void
780 nsDOMStyleSheetList::StyleSheetAdded(nsIDocument *aDocument,
781 nsIStyleSheet* aStyleSheet,
782 bool aDocumentSheet)
783 {
784 if (aDocumentSheet && -1 != mLength) {
785 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
786 if (domss) {
787 mLength++;
788 }
789 }
790 }
792 void
793 nsDOMStyleSheetList::StyleSheetRemoved(nsIDocument *aDocument,
794 nsIStyleSheet* aStyleSheet,
795 bool aDocumentSheet)
796 {
797 if (aDocumentSheet && -1 != mLength) {
798 nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(aStyleSheet));
799 if (domss) {
800 mLength--;
801 }
802 }
803 }
805 // nsOnloadBlocker implementation
806 NS_IMPL_ISUPPORTS(nsOnloadBlocker, nsIRequest)
808 NS_IMETHODIMP
809 nsOnloadBlocker::GetName(nsACString &aResult)
810 {
811 aResult.AssignLiteral("about:document-onload-blocker");
812 return NS_OK;
813 }
815 NS_IMETHODIMP
816 nsOnloadBlocker::IsPending(bool *_retval)
817 {
818 *_retval = true;
819 return NS_OK;
820 }
822 NS_IMETHODIMP
823 nsOnloadBlocker::GetStatus(nsresult *status)
824 {
825 *status = NS_OK;
826 return NS_OK;
827 }
829 NS_IMETHODIMP
830 nsOnloadBlocker::Cancel(nsresult status)
831 {
832 return NS_OK;
833 }
834 NS_IMETHODIMP
835 nsOnloadBlocker::Suspend(void)
836 {
837 return NS_OK;
838 }
839 NS_IMETHODIMP
840 nsOnloadBlocker::Resume(void)
841 {
842 return NS_OK;
843 }
845 NS_IMETHODIMP
846 nsOnloadBlocker::GetLoadGroup(nsILoadGroup * *aLoadGroup)
847 {
848 *aLoadGroup = nullptr;
849 return NS_OK;
850 }
852 NS_IMETHODIMP
853 nsOnloadBlocker::SetLoadGroup(nsILoadGroup * aLoadGroup)
854 {
855 return NS_OK;
856 }
858 NS_IMETHODIMP
859 nsOnloadBlocker::GetLoadFlags(nsLoadFlags *aLoadFlags)
860 {
861 *aLoadFlags = nsIRequest::LOAD_NORMAL;
862 return NS_OK;
863 }
865 NS_IMETHODIMP
866 nsOnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags)
867 {
868 return NS_OK;
869 }
871 // ==================================================================
873 nsExternalResourceMap::nsExternalResourceMap()
874 : mHaveShutDown(false)
875 {
876 }
878 nsIDocument*
879 nsExternalResourceMap::RequestResource(nsIURI* aURI,
880 nsINode* aRequestingNode,
881 nsDocument* aDisplayDocument,
882 ExternalResourceLoad** aPendingLoad)
883 {
884 // If we ever start allowing non-same-origin loads here, we might need to do
885 // something interesting with aRequestingPrincipal even for the hashtable
886 // gets.
887 NS_PRECONDITION(aURI, "Must have a URI");
888 NS_PRECONDITION(aRequestingNode, "Must have a node");
889 *aPendingLoad = nullptr;
890 if (mHaveShutDown) {
891 return nullptr;
892 }
894 // First, make sure we strip the ref from aURI.
895 nsCOMPtr<nsIURI> clone;
896 nsresult rv = aURI->CloneIgnoringRef(getter_AddRefs(clone));
897 if (NS_FAILED(rv) || !clone) {
898 return nullptr;
899 }
901 ExternalResource* resource;
902 mMap.Get(clone, &resource);
903 if (resource) {
904 return resource->mDocument;
905 }
907 nsRefPtr<PendingLoad> load;
908 mPendingLoads.Get(clone, getter_AddRefs(load));
909 if (load) {
910 load.forget(aPendingLoad);
911 return nullptr;
912 }
914 load = new PendingLoad(aDisplayDocument);
916 mPendingLoads.Put(clone, load);
918 if (NS_FAILED(load->StartLoad(clone, aRequestingNode))) {
919 // Make sure we don't thrash things by trying this load again, since
920 // chances are it failed for good reasons (security check, etc).
921 AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
922 } else {
923 load.forget(aPendingLoad);
924 }
926 return nullptr;
927 }
929 struct
930 nsExternalResourceEnumArgs
931 {
932 nsIDocument::nsSubDocEnumFunc callback;
933 void *data;
934 };
936 static PLDHashOperator
937 ExternalResourceEnumerator(nsIURI* aKey,
938 nsExternalResourceMap::ExternalResource* aData,
939 void* aClosure)
940 {
941 nsExternalResourceEnumArgs* args =
942 static_cast<nsExternalResourceEnumArgs*>(aClosure);
943 bool next =
944 aData->mDocument ? args->callback(aData->mDocument, args->data) : true;
945 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
946 }
948 void
949 nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback,
950 void* aData)
951 {
952 nsExternalResourceEnumArgs args = { aCallback, aData };
953 mMap.EnumerateRead(ExternalResourceEnumerator, &args);
954 }
956 static PLDHashOperator
957 ExternalResourceTraverser(nsIURI* aKey,
958 nsExternalResourceMap::ExternalResource* aData,
959 void* aClosure)
960 {
961 nsCycleCollectionTraversalCallback *cb =
962 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
964 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
965 "mExternalResourceMap.mMap entry"
966 "->mDocument");
967 cb->NoteXPCOMChild(aData->mDocument);
969 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
970 "mExternalResourceMap.mMap entry"
971 "->mViewer");
972 cb->NoteXPCOMChild(aData->mViewer);
974 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
975 "mExternalResourceMap.mMap entry"
976 "->mLoadGroup");
977 cb->NoteXPCOMChild(aData->mLoadGroup);
979 return PL_DHASH_NEXT;
980 }
982 void
983 nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) const
984 {
985 // mPendingLoads will get cleared out as the requests complete, so
986 // no need to worry about those here.
987 mMap.EnumerateRead(ExternalResourceTraverser, aCallback);
988 }
990 static PLDHashOperator
991 ExternalResourceHider(nsIURI* aKey,
992 nsExternalResourceMap::ExternalResource* aData,
993 void* aClosure)
994 {
995 if (aData->mViewer) {
996 aData->mViewer->Hide();
997 }
998 return PL_DHASH_NEXT;
999 }
1001 void
1002 nsExternalResourceMap::HideViewers()
1003 {
1004 mMap.EnumerateRead(ExternalResourceHider, nullptr);
1005 }
1007 static PLDHashOperator
1008 ExternalResourceShower(nsIURI* aKey,
1009 nsExternalResourceMap::ExternalResource* aData,
1010 void* aClosure)
1011 {
1012 if (aData->mViewer) {
1013 aData->mViewer->Show();
1014 }
1015 return PL_DHASH_NEXT;
1016 }
1018 void
1019 nsExternalResourceMap::ShowViewers()
1020 {
1021 mMap.EnumerateRead(ExternalResourceShower, nullptr);
1022 }
1024 void
1025 TransferZoomLevels(nsIDocument* aFromDoc,
1026 nsIDocument* aToDoc)
1027 {
1028 NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
1029 "transferring zoom levels from/to null doc");
1031 nsIPresShell* fromShell = aFromDoc->GetShell();
1032 if (!fromShell)
1033 return;
1035 nsPresContext* fromCtxt = fromShell->GetPresContext();
1036 if (!fromCtxt)
1037 return;
1039 nsIPresShell* toShell = aToDoc->GetShell();
1040 if (!toShell)
1041 return;
1043 nsPresContext* toCtxt = toShell->GetPresContext();
1044 if (!toCtxt)
1045 return;
1047 toCtxt->SetFullZoom(fromCtxt->GetFullZoom());
1048 toCtxt->SetBaseMinFontSize(fromCtxt->BaseMinFontSize());
1049 toCtxt->SetTextZoom(fromCtxt->TextZoom());
1050 }
1052 void
1053 TransferShowingState(nsIDocument* aFromDoc, nsIDocument* aToDoc)
1054 {
1055 NS_ABORT_IF_FALSE(aFromDoc && aToDoc,
1056 "transferring showing state from/to null doc");
1058 if (aFromDoc->IsShowing()) {
1059 aToDoc->OnPageShow(true, nullptr);
1060 }
1061 }
1063 nsresult
1064 nsExternalResourceMap::AddExternalResource(nsIURI* aURI,
1065 nsIContentViewer* aViewer,
1066 nsILoadGroup* aLoadGroup,
1067 nsIDocument* aDisplayDocument)
1068 {
1069 NS_PRECONDITION(aURI, "Unexpected call");
1070 NS_PRECONDITION((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
1071 "Must have both or neither");
1073 nsRefPtr<PendingLoad> load;
1074 mPendingLoads.Get(aURI, getter_AddRefs(load));
1075 mPendingLoads.Remove(aURI);
1077 nsresult rv = NS_OK;
1079 nsCOMPtr<nsIDocument> doc;
1080 if (aViewer) {
1081 doc = aViewer->GetDocument();
1082 NS_ASSERTION(doc, "Must have a document");
1084 nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(doc);
1085 if (xulDoc) {
1086 // We don't handle XUL stuff here yet.
1087 rv = NS_ERROR_NOT_AVAILABLE;
1088 } else {
1089 doc->SetDisplayDocument(aDisplayDocument);
1091 // Make sure that hiding our viewer will tear down its presentation.
1092 aViewer->SetSticky(false);
1094 rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0));
1095 if (NS_SUCCEEDED(rv)) {
1096 rv = aViewer->Open(nullptr, nullptr);
1097 }
1098 }
1100 if (NS_FAILED(rv)) {
1101 doc = nullptr;
1102 aViewer = nullptr;
1103 aLoadGroup = nullptr;
1104 }
1105 }
1107 ExternalResource* newResource = new ExternalResource();
1108 mMap.Put(aURI, newResource);
1110 newResource->mDocument = doc;
1111 newResource->mViewer = aViewer;
1112 newResource->mLoadGroup = aLoadGroup;
1113 if (doc) {
1114 TransferZoomLevels(aDisplayDocument, doc);
1115 TransferShowingState(aDisplayDocument, doc);
1116 }
1118 const nsTArray< nsCOMPtr<nsIObserver> > & obs = load->Observers();
1119 for (uint32_t i = 0; i < obs.Length(); ++i) {
1120 obs[i]->Observe(doc, "external-resource-document-created", nullptr);
1121 }
1123 return rv;
1124 }
1126 NS_IMPL_ISUPPORTS(nsExternalResourceMap::PendingLoad,
1127 nsIStreamListener,
1128 nsIRequestObserver)
1130 NS_IMETHODIMP
1131 nsExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest *aRequest,
1132 nsISupports *aContext)
1133 {
1134 nsExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
1135 if (map.HaveShutDown()) {
1136 return NS_BINDING_ABORTED;
1137 }
1139 nsCOMPtr<nsIContentViewer> viewer;
1140 nsCOMPtr<nsILoadGroup> loadGroup;
1141 nsresult rv = SetupViewer(aRequest, getter_AddRefs(viewer),
1142 getter_AddRefs(loadGroup));
1144 // Make sure to do this no matter what
1145 nsresult rv2 = map.AddExternalResource(mURI, viewer, loadGroup,
1146 mDisplayDocument);
1147 if (NS_FAILED(rv)) {
1148 return rv;
1149 }
1150 if (NS_FAILED(rv2)) {
1151 mTargetListener = nullptr;
1152 return rv2;
1153 }
1155 return mTargetListener->OnStartRequest(aRequest, aContext);
1156 }
1158 nsresult
1159 nsExternalResourceMap::PendingLoad::SetupViewer(nsIRequest* aRequest,
1160 nsIContentViewer** aViewer,
1161 nsILoadGroup** aLoadGroup)
1162 {
1163 NS_PRECONDITION(!mTargetListener, "Unexpected call to OnStartRequest");
1164 *aViewer = nullptr;
1165 *aLoadGroup = nullptr;
1167 nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1168 NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
1170 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
1171 if (httpChannel) {
1172 bool requestSucceeded;
1173 if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
1174 !requestSucceeded) {
1175 // Bail out on this load, since it looks like we have an HTTP error page
1176 return NS_BINDING_ABORTED;
1177 }
1178 }
1180 nsAutoCString type;
1181 chan->GetContentType(type);
1183 nsCOMPtr<nsILoadGroup> loadGroup;
1184 chan->GetLoadGroup(getter_AddRefs(loadGroup));
1186 // Give this document its own loadgroup
1187 nsCOMPtr<nsILoadGroup> newLoadGroup =
1188 do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1189 NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
1190 newLoadGroup->SetLoadGroup(loadGroup);
1192 nsCOMPtr<nsIInterfaceRequestor> callbacks;
1193 loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1195 nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1196 new LoadgroupCallbacks(callbacks);
1197 newLoadGroup->SetNotificationCallbacks(newCallbacks);
1199 // This is some serious hackery cribbed from docshell
1200 nsCOMPtr<nsICategoryManager> catMan =
1201 do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1202 NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1203 nsXPIDLCString contractId;
1204 nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
1205 getter_Copies(contractId));
1206 NS_ENSURE_SUCCESS(rv, rv);
1207 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1208 do_GetService(contractId);
1209 NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1211 nsCOMPtr<nsIContentViewer> viewer;
1212 nsCOMPtr<nsIStreamListener> listener;
1213 rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
1214 type.get(), nullptr, nullptr,
1215 getter_AddRefs(listener),
1216 getter_AddRefs(viewer));
1217 NS_ENSURE_SUCCESS(rv, rv);
1218 NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1220 nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1221 if (!parser) {
1222 /// We don't want to deal with the various fake documents yet
1223 return NS_ERROR_NOT_IMPLEMENTED;
1224 }
1226 // We can't handle HTML and other weird things here yet.
1227 nsIContentSink* sink = parser->GetContentSink();
1228 nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1229 if (!xmlSink) {
1230 return NS_ERROR_NOT_IMPLEMENTED;
1231 }
1233 listener.swap(mTargetListener);
1234 viewer.forget(aViewer);
1235 newLoadGroup.forget(aLoadGroup);
1236 return NS_OK;
1237 }
1239 NS_IMETHODIMP
1240 nsExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1241 nsISupports* aContext,
1242 nsIInputStream* aStream,
1243 uint64_t aOffset,
1244 uint32_t aCount)
1245 {
1246 NS_PRECONDITION(mTargetListener, "Shouldn't be getting called!");
1247 if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1248 return NS_BINDING_ABORTED;
1249 }
1250 return mTargetListener->OnDataAvailable(aRequest, aContext, aStream, aOffset,
1251 aCount);
1252 }
1254 NS_IMETHODIMP
1255 nsExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1256 nsISupports* aContext,
1257 nsresult aStatus)
1258 {
1259 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1260 if (mTargetListener) {
1261 nsCOMPtr<nsIStreamListener> listener;
1262 mTargetListener.swap(listener);
1263 return listener->OnStopRequest(aRequest, aContext, aStatus);
1264 }
1266 return NS_OK;
1267 }
1269 nsresult
1270 nsExternalResourceMap::PendingLoad::StartLoad(nsIURI* aURI,
1271 nsINode* aRequestingNode)
1272 {
1273 NS_PRECONDITION(aURI, "Must have a URI");
1274 NS_PRECONDITION(aRequestingNode, "Must have a node");
1276 // Time to start a load. First, the security checks.
1278 nsIPrincipal* requestingPrincipal = aRequestingNode->NodePrincipal();
1280 nsresult rv = nsContentUtils::GetSecurityManager()->
1281 CheckLoadURIWithPrincipal(requestingPrincipal, aURI,
1282 nsIScriptSecurityManager::STANDARD);
1283 NS_ENSURE_SUCCESS(rv, rv);
1285 // Allow data URIs and other URI's that inherit their principal by passing
1286 // true as the 3rd argument of CheckMayLoad, since we want
1287 // to allow external resources from data URIs regardless of the difference
1288 // in URI scheme.
1289 rv = requestingPrincipal->CheckMayLoad(aURI, true, true);
1290 NS_ENSURE_SUCCESS(rv, rv);
1292 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
1293 rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
1294 aURI,
1295 requestingPrincipal,
1296 aRequestingNode,
1297 EmptyCString(), //mime guess
1298 nullptr, //extra
1299 &shouldLoad,
1300 nsContentUtils::GetContentPolicy(),
1301 nsContentUtils::GetSecurityManager());
1302 if (NS_FAILED(rv)) return rv;
1303 if (NS_CP_REJECTED(shouldLoad)) {
1304 // Disallowed by content policy
1305 return NS_ERROR_CONTENT_BLOCKED;
1306 }
1308 nsIDocument* doc = aRequestingNode->OwnerDoc();
1310 nsCOMPtr<nsIInterfaceRequestor> req = nsContentUtils::GetSameOriginChecker();
1311 NS_ENSURE_TRUE(req, NS_ERROR_OUT_OF_MEMORY);
1313 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
1314 nsCOMPtr<nsIChannel> channel;
1315 rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, loadGroup, req);
1316 NS_ENSURE_SUCCESS(rv, rv);
1318 mURI = aURI;
1320 return channel->AsyncOpen(this, nullptr);
1321 }
1323 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks,
1324 nsIInterfaceRequestor)
1326 #define IMPL_SHIM(_i) \
1327 NS_IMPL_ISUPPORTS(nsExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1329 IMPL_SHIM(nsILoadContext)
1330 IMPL_SHIM(nsIProgressEventSink)
1331 IMPL_SHIM(nsIChannelEventSink)
1332 IMPL_SHIM(nsISecurityEventSink)
1333 IMPL_SHIM(nsIApplicationCacheContainer)
1335 #undef IMPL_SHIM
1337 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1339 #define TRY_SHIM(_i) \
1340 PR_BEGIN_MACRO \
1341 if (IID_IS(_i)) { \
1342 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1343 if (!real) { \
1344 return NS_NOINTERFACE; \
1345 } \
1346 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1347 if (!shim) { \
1348 return NS_ERROR_OUT_OF_MEMORY; \
1349 } \
1350 shim.forget(aSink); \
1351 return NS_OK; \
1352 } \
1353 PR_END_MACRO
1355 NS_IMETHODIMP
1356 nsExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID & aIID,
1357 void **aSink)
1358 {
1359 if (mCallbacks &&
1360 (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) || IID_IS(nsIAuthPrompt2) ||
1361 IID_IS(nsITabChild))) {
1362 return mCallbacks->GetInterface(aIID, aSink);
1363 }
1365 *aSink = nullptr;
1367 TRY_SHIM(nsILoadContext);
1368 TRY_SHIM(nsIProgressEventSink);
1369 TRY_SHIM(nsIChannelEventSink);
1370 TRY_SHIM(nsISecurityEventSink);
1371 TRY_SHIM(nsIApplicationCacheContainer);
1373 return NS_NOINTERFACE;
1374 }
1376 #undef TRY_SHIM
1377 #undef IID_IS
1379 nsExternalResourceMap::ExternalResource::~ExternalResource()
1380 {
1381 if (mViewer) {
1382 mViewer->Close(nullptr);
1383 mViewer->Destroy();
1384 }
1385 }
1387 // ==================================================================
1388 // =
1389 // ==================================================================
1391 // If we ever have an nsIDocumentObserver notification for stylesheet title
1392 // changes we should update the list from that instead of overriding
1393 // EnsureFresh.
1394 class nsDOMStyleSheetSetList MOZ_FINAL : public DOMStringList
1395 {
1396 public:
1397 nsDOMStyleSheetSetList(nsIDocument* aDocument);
1399 void Disconnect()
1400 {
1401 mDocument = nullptr;
1402 }
1404 virtual void EnsureFresh() MOZ_OVERRIDE;
1406 protected:
1407 nsIDocument* mDocument; // Our document; weak ref. It'll let us know if it
1408 // dies.
1409 };
1411 nsDOMStyleSheetSetList::nsDOMStyleSheetSetList(nsIDocument* aDocument)
1412 : mDocument(aDocument)
1413 {
1414 NS_ASSERTION(mDocument, "Must have document!");
1415 }
1417 void
1418 nsDOMStyleSheetSetList::EnsureFresh()
1419 {
1420 mNames.Clear();
1422 if (!mDocument) {
1423 return; // Spec says "no exceptions", and we have no style sets if we have
1424 // no document, for sure
1425 }
1427 int32_t count = mDocument->GetNumberOfStyleSheets();
1428 nsAutoString title;
1429 for (int32_t index = 0; index < count; index++) {
1430 nsIStyleSheet* sheet = mDocument->GetStyleSheetAt(index);
1431 NS_ASSERTION(sheet, "Null sheet in sheet list!");
1432 sheet->GetTitle(title);
1433 if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1434 return;
1435 }
1436 }
1437 }
1439 // ==================================================================
1440 nsIDocument::SelectorCache::SelectorCache()
1441 : nsExpirationTracker<SelectorCacheKey, 4>(1000) { }
1443 // CacheList takes ownership of aSelectorList.
1444 void nsIDocument::SelectorCache::CacheList(const nsAString& aSelector,
1445 nsCSSSelectorList* aSelectorList)
1446 {
1447 SelectorCacheKey* key = new SelectorCacheKey(aSelector);
1448 mTable.Put(key->mKey, aSelectorList);
1449 AddObject(key);
1450 }
1452 class nsIDocument::SelectorCacheKeyDeleter MOZ_FINAL : public nsRunnable
1453 {
1454 public:
1455 explicit SelectorCacheKeyDeleter(SelectorCacheKey* aToDelete)
1456 : mSelector(aToDelete)
1457 {
1458 MOZ_COUNT_CTOR(SelectorCacheKeyDeleter);
1459 }
1461 ~SelectorCacheKeyDeleter()
1462 {
1463 MOZ_COUNT_DTOR(SelectorCacheKeyDeleter);
1464 }
1466 NS_IMETHOD Run()
1467 {
1468 return NS_OK;
1469 }
1471 private:
1472 nsAutoPtr<SelectorCacheKey> mSelector;
1473 };
1475 void nsIDocument::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector)
1476 {
1477 RemoveObject(aSelector);
1478 mTable.Remove(aSelector->mKey);
1479 nsCOMPtr<nsIRunnable> runnable = new SelectorCacheKeyDeleter(aSelector);
1480 NS_DispatchToCurrentThread(runnable);
1481 }
1484 struct nsIDocument::FrameRequest
1485 {
1486 FrameRequest(const FrameRequestCallbackHolder& aCallback,
1487 int32_t aHandle) :
1488 mCallback(aCallback),
1489 mHandle(aHandle)
1490 {}
1492 // Conversion operator so that we can append these to a
1493 // FrameRequestCallbackList
1494 operator const FrameRequestCallbackHolder& () const {
1495 return mCallback;
1496 }
1498 // Comparator operators to allow RemoveElementSorted with an
1499 // integer argument on arrays of FrameRequest
1500 bool operator==(int32_t aHandle) const {
1501 return mHandle == aHandle;
1502 }
1503 bool operator<(int32_t aHandle) const {
1504 return mHandle < aHandle;
1505 }
1507 FrameRequestCallbackHolder mCallback;
1508 int32_t mHandle;
1509 };
1511 static already_AddRefed<nsINodeInfo> nullNodeInfo(nullptr);
1513 // ==================================================================
1514 // =
1515 // ==================================================================
1516 nsIDocument::nsIDocument()
1517 : nsINode(nullNodeInfo),
1518 mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
1519 mNodeInfoManager(nullptr),
1520 mCompatMode(eCompatibility_FullStandards),
1521 mVisibilityState(dom::VisibilityState::Hidden),
1522 mIsInitialDocumentInWindow(false),
1523 mMayStartLayout(true),
1524 mVisible(true),
1525 mRemovedFromDocShell(false),
1526 // mAllowDNSPrefetch starts true, so that we can always reliably && it
1527 // with various values that might disable it. Since we never prefetch
1528 // unless we get a window, and in that case the docshell value will get
1529 // &&-ed in, this is safe.
1530 mAllowDNSPrefetch(true),
1531 mIsBeingUsedAsImage(false),
1532 mHasLinksToUpdate(false),
1533 mPartID(0),
1534 mDidFireDOMContentLoaded(true)
1535 {
1536 SetInDocument();
1537 }
1539 // NOTE! nsDocument::operator new() zeroes out all members, so don't
1540 // bother initializing members to 0.
1542 nsDocument::nsDocument(const char* aContentType)
1543 : nsIDocument()
1544 , mAnimatingImages(true)
1545 , mViewportType(Unknown)
1546 {
1547 SetContentTypeInternal(nsDependentCString(aContentType));
1549 #ifdef PR_LOGGING
1550 if (!gDocumentLeakPRLog)
1551 gDocumentLeakPRLog = PR_NewLogModule("DocumentLeak");
1553 if (gDocumentLeakPRLog)
1554 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1555 ("DOCUMENT %p created", this));
1557 if (!gCspPRLog)
1558 gCspPRLog = PR_NewLogModule("CSP");
1559 #endif
1561 // Start out mLastStyleSheetSet as null, per spec
1562 SetDOMStringToNull(mLastStyleSheetSet);
1564 if (sProcessingStack.empty()) {
1565 sProcessingStack.construct();
1566 // Add the base queue sentinel to the processing stack.
1567 sProcessingStack.ref().AppendElement((CustomElementData*) nullptr);
1568 }
1569 }
1571 static PLDHashOperator
1572 ClearAllBoxObjects(nsIContent* aKey, nsPIBoxObject* aBoxObject, void* aUserArg)
1573 {
1574 if (aBoxObject) {
1575 aBoxObject->Clear();
1576 }
1577 return PL_DHASH_NEXT;
1578 }
1580 nsIDocument::~nsIDocument()
1581 {
1582 if (mNodeInfoManager) {
1583 mNodeInfoManager->DropDocumentReference();
1584 }
1585 }
1588 nsDocument::~nsDocument()
1589 {
1590 #ifdef PR_LOGGING
1591 if (gDocumentLeakPRLog)
1592 PR_LOG(gDocumentLeakPRLog, PR_LOG_DEBUG,
1593 ("DOCUMENT %p destroyed", this));
1594 #endif
1596 NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
1598 if (IsTopLevelContentDocument()) {
1599 //don't report for about: pages
1600 nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
1601 nsCOMPtr<nsIURI> uri;
1602 principal->GetURI(getter_AddRefs(uri));
1603 bool isAboutScheme = true;
1604 if (uri) {
1605 uri->SchemeIs("about", &isAboutScheme);
1606 }
1608 if (!isAboutScheme) {
1609 // Record the page load
1610 uint32_t pageLoaded = 1;
1611 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1612 // Record the mixed content status of the docshell in Telemetry
1613 enum {
1614 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1615 MIXED_DISPLAY_CONTENT = 1, // The page attempted to load Mixed Display Content
1616 MIXED_ACTIVE_CONTENT = 2, // The page attempted to load Mixed Active Content
1617 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed Display & Mixed Active Content
1618 };
1620 bool mixedActiveLoaded = GetHasMixedActiveContentLoaded();
1621 bool mixedActiveBlocked = GetHasMixedActiveContentBlocked();
1623 bool mixedDisplayLoaded = GetHasMixedDisplayContentLoaded();
1624 bool mixedDisplayBlocked = GetHasMixedDisplayContentBlocked();
1626 bool hasMixedDisplay = (mixedDisplayBlocked || mixedDisplayLoaded);
1627 bool hasMixedActive = (mixedActiveBlocked || mixedActiveLoaded);
1629 uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1630 if (hasMixedDisplay && hasMixedActive) {
1631 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1632 } else if (hasMixedActive){
1633 mixedContentLevel = MIXED_ACTIVE_CONTENT;
1634 } else if (hasMixedDisplay) {
1635 mixedContentLevel = MIXED_DISPLAY_CONTENT;
1636 }
1637 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1638 }
1639 }
1641 mInDestructor = true;
1642 mInUnlinkOrDeletion = true;
1644 mRegistry = nullptr;
1646 mozilla::DropJSObjects(this);
1648 // Clear mObservers to keep it in sync with the mutationobserver list
1649 mObservers.Clear();
1651 if (mStyleSheetSetList) {
1652 mStyleSheetSetList->Disconnect();
1653 }
1655 if (mAnimationController) {
1656 mAnimationController->Disconnect();
1657 }
1659 mParentDocument = nullptr;
1661 // Kill the subdocument map, doing this will release its strong
1662 // references, if any.
1663 if (mSubDocuments) {
1664 PL_DHashTableDestroy(mSubDocuments);
1666 mSubDocuments = nullptr;
1667 }
1669 // Destroy link map now so we don't waste time removing
1670 // links one by one
1671 DestroyElementMaps();
1673 nsAutoScriptBlocker scriptBlocker;
1675 int32_t indx; // must be signed
1676 uint32_t count = mChildren.ChildCount();
1677 for (indx = int32_t(count) - 1; indx >= 0; --indx) {
1678 mChildren.ChildAt(indx)->UnbindFromTree();
1679 mChildren.RemoveChildAt(indx);
1680 }
1681 mFirstChild = nullptr;
1682 mCachedRootElement = nullptr;
1684 // Let the stylesheets know we're going away
1685 indx = mStyleSheets.Count();
1686 while (--indx >= 0) {
1687 mStyleSheets[indx]->SetOwningDocument(nullptr);
1688 }
1689 indx = mCatalogSheets.Count();
1690 while (--indx >= 0) {
1691 static_cast<nsCSSStyleSheet*>(mCatalogSheets[indx])->SetOwningNode(nullptr);
1692 mCatalogSheets[indx]->SetOwningDocument(nullptr);
1693 }
1694 if (mAttrStyleSheet) {
1695 mAttrStyleSheet->SetOwningDocument(nullptr);
1696 }
1698 if (mListenerManager) {
1699 mListenerManager->Disconnect();
1700 UnsetFlags(NODE_HAS_LISTENERMANAGER);
1701 }
1703 if (mScriptLoader) {
1704 mScriptLoader->DropDocumentReference();
1705 }
1707 if (mCSSLoader) {
1708 // Could be null here if Init() failed
1709 mCSSLoader->DropDocumentReference();
1710 }
1712 if (mStyleImageLoader) {
1713 mStyleImageLoader->DropDocumentReference();
1714 }
1716 delete mHeaderData;
1718 if (mBoxObjectTable) {
1719 mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
1720 delete mBoxObjectTable;
1721 }
1723 mPendingTitleChangeEvent.Revoke();
1725 for (uint32_t i = 0; i < mHostObjectURIs.Length(); ++i) {
1726 nsHostObjectProtocolHandler::RemoveDataEntry(mHostObjectURIs[i]);
1727 }
1729 // We don't want to leave residual locks on images. Make sure we're in an
1730 // unlocked state, and then clear the table.
1731 SetImageLockingState(false);
1732 mImageTracker.Clear();
1734 mPlugins.Clear();
1735 }
1737 NS_INTERFACE_TABLE_HEAD(nsDocument)
1738 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1739 NS_INTERFACE_TABLE_BEGIN
1740 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsDocument, nsISupports, nsINode)
1741 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsINode)
1742 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDocument)
1743 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocument)
1744 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNode)
1745 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMDocumentXBL)
1746 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIScriptObjectPrincipal)
1747 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMEventTarget)
1748 NS_INTERFACE_TABLE_ENTRY(nsDocument, mozilla::dom::EventTarget)
1749 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
1750 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
1751 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
1752 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
1753 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIObserver)
1754 NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMXPathEvaluator)
1755 NS_INTERFACE_TABLE_END
1756 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsDocument)
1757 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver,
1758 new nsNode3Tearoff(this))
1759 NS_INTERFACE_MAP_END
1762 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDocument)
1763 NS_IMETHODIMP_(MozExternalRefCountType)
1764 nsDocument::Release()
1765 {
1766 NS_PRECONDITION(0 != mRefCnt, "dup release");
1767 NS_ASSERT_OWNINGTHREAD(nsDocument);
1768 nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsDocument)::Upcast(this);
1769 bool shouldDelete = false;
1770 nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
1771 NS_LOG_RELEASE(this, count, "nsDocument");
1772 if (count == 0) {
1773 if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
1774 mNeedsReleaseAfterStackRefCntRelease = true;
1775 NS_ADDREF_THIS();
1776 return mRefCnt.get();
1777 }
1778 mRefCnt.incr(base);
1779 nsNodeUtils::LastRelease(this);
1780 mRefCnt.decr(base);
1781 if (shouldDelete) {
1782 mRefCnt.stabilizeForDeletion();
1783 DeleteCycleCollectable();
1784 }
1785 }
1786 return count;
1787 }
1789 NS_IMETHODIMP_(void)
1790 nsDocument::DeleteCycleCollectable()
1791 {
1792 delete this;
1793 }
1795 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDocument)
1796 if (Element::CanSkip(tmp, aRemovingAllowed)) {
1797 EventListenerManager* elm = tmp->GetExistingListenerManager();
1798 if (elm) {
1799 elm->MarkForCC();
1800 }
1801 return true;
1802 }
1803 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1805 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDocument)
1806 return Element::CanSkipInCC(tmp);
1807 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1809 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
1810 return Element::CanSkipThis(tmp);
1811 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1813 static PLDHashOperator
1814 SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
1815 void *arg)
1816 {
1817 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
1818 nsCycleCollectionTraversalCallback *cb =
1819 static_cast<nsCycleCollectionTraversalCallback*>(arg);
1821 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
1822 cb->NoteXPCOMChild(entry->mKey);
1823 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
1824 cb->NoteXPCOMChild(entry->mSubDocument);
1826 return PL_DHASH_NEXT;
1827 }
1829 static PLDHashOperator
1830 RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
1831 void* aClosure)
1832 {
1833 nsCycleCollectionTraversalCallback *cb =
1834 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
1836 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1837 "mRadioGroups entry->mSelectedRadioButton");
1838 cb->NoteXPCOMChild(ToSupports(aData->mSelectedRadioButton));
1840 uint32_t i, count = aData->mRadioButtons.Count();
1841 for (i = 0; i < count; ++i) {
1842 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
1843 "mRadioGroups entry->mRadioButtons[i]");
1844 cb->NoteXPCOMChild(aData->mRadioButtons[i]);
1845 }
1847 return PL_DHASH_NEXT;
1848 }
1850 static PLDHashOperator
1851 BoxObjectTraverser(nsIContent* key, nsPIBoxObject* boxObject, void* userArg)
1852 {
1853 nsCycleCollectionTraversalCallback *cb =
1854 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
1856 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mBoxObjectTable entry");
1857 cb->NoteXPCOMChild(boxObject);
1859 return PL_DHASH_NEXT;
1860 }
1862 static PLDHashOperator
1863 IdentifierMapEntryTraverse(nsIdentifierMapEntry *aEntry, void *aArg)
1864 {
1865 nsCycleCollectionTraversalCallback *cb =
1866 static_cast<nsCycleCollectionTraversalCallback*>(aArg);
1867 aEntry->Traverse(cb);
1868 return PL_DHASH_NEXT;
1869 }
1871 static const char* kNSURIs[] = {
1872 "([none])",
1873 "(xmlns)",
1874 "(xml)",
1875 "(xhtml)",
1876 "(XLink)",
1877 "(XSLT)",
1878 "(XBL)",
1879 "(MathML)",
1880 "(RDF)",
1881 "(XUL)"
1882 };
1884 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
1885 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1886 char name[512];
1887 nsAutoCString loadedAsData;
1888 if (tmp->IsLoadedAsData()) {
1889 loadedAsData.AssignLiteral("data");
1890 } else {
1891 loadedAsData.AssignLiteral("normal");
1892 }
1893 uint32_t nsid = tmp->GetDefaultNamespaceID();
1894 nsAutoCString uri;
1895 if (tmp->mDocumentURI)
1896 tmp->mDocumentURI->GetSpec(uri);
1897 if (nsid < ArrayLength(kNSURIs)) {
1898 PR_snprintf(name, sizeof(name), "nsDocument %s %s %s",
1899 loadedAsData.get(), kNSURIs[nsid], uri.get());
1900 }
1901 else {
1902 PR_snprintf(name, sizeof(name), "nsDocument %s %s",
1903 loadedAsData.get(), uri.get());
1904 }
1905 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1906 }
1907 else {
1908 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsDocument, tmp->mRefCnt.get())
1909 }
1911 // Always need to traverse script objects, so do that before we check
1912 // if we're uncollectable.
1913 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1915 if (!nsINode::Traverse(tmp, cb)) {
1916 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1917 }
1919 tmp->mIdentifierMap.EnumerateEntries(IdentifierMapEntryTraverse, &cb);
1921 tmp->mExternalResourceMap.Traverse(&cb);
1923 // Traverse the mChildren nsAttrAndChildArray.
1924 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()); indx > 0; --indx) {
1925 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
1926 cb.NoteXPCOMChild(tmp->mChildren.ChildAt(indx - 1));
1927 }
1929 // Traverse all nsIDocument pointer members.
1930 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
1931 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
1933 // Traverse all nsDocument nsCOMPtrs.
1934 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
1935 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
1936 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
1937 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
1938 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
1939 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
1941 tmp->mRadioGroups.EnumerateRead(RadioGroupsTraverser, &cb);
1943 // The boxobject for an element will only exist as long as it's in the
1944 // document, so we'll traverse the table here instead of from the element.
1945 if (tmp->mBoxObjectTable) {
1946 tmp->mBoxObjectTable->EnumerateRead(BoxObjectTraverser, &cb);
1947 }
1949 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
1950 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleAttrStyleSheet)
1951 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXPathEvaluator)
1952 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLayoutHistoryState)
1953 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
1954 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstBaseNodeWithHref)
1955 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
1956 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
1957 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
1958 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
1959 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
1960 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
1961 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
1962 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
1963 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
1965 // Traverse all our nsCOMArrays.
1966 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
1967 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCatalogSheets)
1968 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
1970 for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
1971 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
1972 cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
1973 }
1975 // Traverse animation components
1976 if (tmp->mAnimationController) {
1977 tmp->mAnimationController->Traverse(&cb);
1978 }
1980 if (tmp->mSubDocuments && tmp->mSubDocuments->ops) {
1981 PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
1982 }
1984 if (tmp->mCSSLoader) {
1985 tmp->mCSSLoader->TraverseCachedSheets(cb);
1986 }
1988 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
1989 nsHostObjectProtocolHandler::Traverse(tmp->mHostObjectURIs[i], cb);
1990 }
1991 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1993 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
1995 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
1996 if (tmp->PreservingWrapper()) {
1997 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
1998 }
1999 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2000 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2003 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
2004 tmp->mInUnlinkOrDeletion = true;
2006 // Clear out our external resources
2007 tmp->mExternalResourceMap.Shutdown();
2009 nsAutoScriptBlocker scriptBlocker;
2011 nsINode::Unlink(tmp);
2013 // Unlink the mChildren nsAttrAndChildArray.
2014 for (int32_t indx = int32_t(tmp->mChildren.ChildCount()) - 1;
2015 indx >= 0; --indx) {
2016 tmp->mChildren.ChildAt(indx)->UnbindFromTree();
2017 tmp->mChildren.RemoveChildAt(indx);
2018 }
2019 tmp->mFirstChild = nullptr;
2021 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXPathEvaluator)
2022 tmp->mCachedRootElement = nullptr; // Avoid a dangling pointer
2023 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
2024 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFirstBaseNodeWithHref)
2025 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
2026 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
2027 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
2028 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
2029 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
2030 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
2031 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
2032 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
2034 tmp->mParentDocument = nullptr;
2036 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
2039 if (tmp->mBoxObjectTable) {
2040 tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
2041 delete tmp->mBoxObjectTable;
2042 tmp->mBoxObjectTable = nullptr;
2043 }
2045 if (tmp->mListenerManager) {
2046 tmp->mListenerManager->Disconnect();
2047 tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
2048 tmp->mListenerManager = nullptr;
2049 }
2051 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
2053 if (tmp->mStyleSheetSetList) {
2054 tmp->mStyleSheetSetList->Disconnect();
2055 tmp->mStyleSheetSetList = nullptr;
2056 }
2058 if (tmp->mSubDocuments) {
2059 PL_DHashTableDestroy(tmp->mSubDocuments);
2060 tmp->mSubDocuments = nullptr;
2061 }
2063 tmp->mFrameRequestCallbacks.Clear();
2065 tmp->mRadioGroups.Clear();
2067 // nsDocument has a pretty complex destructor, so we're going to
2068 // assume that *most* cycles you actually want to break somewhere
2069 // else, and not unlink an awful lot here.
2071 tmp->mIdentifierMap.Clear();
2072 tmp->mExpandoAndGeneration.Unlink();
2074 if (tmp->mAnimationController) {
2075 tmp->mAnimationController->Unlink();
2076 }
2078 tmp->mPendingTitleChangeEvent.Revoke();
2080 if (tmp->mCSSLoader) {
2081 tmp->mCSSLoader->UnlinkCachedSheets();
2082 }
2084 for (uint32_t i = 0; i < tmp->mHostObjectURIs.Length(); ++i) {
2085 nsHostObjectProtocolHandler::RemoveDataEntry(tmp->mHostObjectURIs[i]);
2086 }
2088 tmp->mInUnlinkOrDeletion = false;
2089 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2091 static bool sPrefsInitialized = false;
2092 static uint32_t sOnloadDecodeLimit = 0;
2094 nsresult
2095 nsDocument::Init()
2096 {
2097 if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
2098 return NS_ERROR_ALREADY_INITIALIZED;
2099 }
2101 if (!sPrefsInitialized) {
2102 sPrefsInitialized = true;
2103 Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0);
2104 }
2106 // Force initialization.
2107 nsINode::nsSlots* slots = Slots();
2109 // Prepend self as mutation-observer whether we need it or not (some
2110 // subclasses currently do, other don't). This is because the code in
2111 // nsNodeUtils always notifies the first observer first, expecting the
2112 // first observer to be the document.
2113 NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
2114 NS_ERROR_OUT_OF_MEMORY);
2117 mOnloadBlocker = new nsOnloadBlocker();
2118 mCSSLoader = new mozilla::css::Loader(this);
2119 // Assume we're not quirky, until we know otherwise
2120 mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2122 mStyleImageLoader = new mozilla::css::ImageLoader(this);
2124 mNodeInfoManager = new nsNodeInfoManager();
2125 nsresult rv = mNodeInfoManager->Init(this);
2126 NS_ENSURE_SUCCESS(rv, rv);
2128 // mNodeInfo keeps NodeInfoManager alive!
2129 mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2130 NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2131 NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_NODE,
2132 "Bad NodeType in aNodeInfo");
2134 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2136 // If after creation the owner js global is not set for a document
2137 // we use the default compartment for this document, instead of creating
2138 // wrapper in some random compartment when the document is exposed to js
2139 // via some events.
2140 nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
2141 NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2142 mScopeObject = do_GetWeakReference(global);
2143 MOZ_ASSERT(mScopeObject);
2145 mScriptLoader = new nsScriptLoader(this);
2147 mozilla::HoldJSObjects(this);
2149 return NS_OK;
2150 }
2152 void
2153 nsIDocument::DeleteAllProperties()
2154 {
2155 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2156 PropertyTable(i)->DeleteAllProperties();
2157 }
2158 }
2160 void
2161 nsIDocument::DeleteAllPropertiesFor(nsINode* aNode)
2162 {
2163 for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
2164 PropertyTable(i)->DeleteAllPropertiesFor(aNode);
2165 }
2166 }
2168 nsPropertyTable*
2169 nsIDocument::GetExtraPropertyTable(uint16_t aCategory)
2170 {
2171 NS_ASSERTION(aCategory > 0, "Category 0 should have already been handled");
2172 while (aCategory >= mExtraPropertyTables.Length() + 1) {
2173 mExtraPropertyTables.AppendElement(new nsPropertyTable());
2174 }
2175 return mExtraPropertyTables[aCategory - 1];
2176 }
2178 bool
2179 nsIDocument::IsVisibleConsideringAncestors() const
2180 {
2181 const nsIDocument *parent = this;
2182 do {
2183 if (!parent->IsVisible()) {
2184 return false;
2185 }
2186 } while ((parent = parent->GetParentDocument()));
2188 return true;
2189 }
2191 void
2192 nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
2193 {
2194 nsCOMPtr<nsIURI> uri;
2195 nsCOMPtr<nsIPrincipal> principal;
2196 if (aChannel) {
2197 // Note: this code is duplicated in XULDocument::StartDocumentLoad and
2198 // nsScriptSecurityManager::GetChannelPrincipal.
2199 // Note: this should match nsDocShell::OnLoadingSite
2200 NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2202 nsIScriptSecurityManager *securityManager =
2203 nsContentUtils::GetSecurityManager();
2204 if (securityManager) {
2205 securityManager->GetChannelPrincipal(aChannel,
2206 getter_AddRefs(principal));
2207 }
2208 }
2210 ResetToURI(uri, aLoadGroup, principal);
2212 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2213 if (bag) {
2214 nsCOMPtr<nsIURI> baseURI;
2215 bag->GetPropertyAsInterface(NS_LITERAL_STRING("baseURI"),
2216 NS_GET_IID(nsIURI), getter_AddRefs(baseURI));
2217 if (baseURI) {
2218 mDocumentBaseURI = baseURI;
2219 mChromeXHRDocBaseURI = baseURI;
2220 }
2221 }
2223 mChannel = aChannel;
2224 }
2226 void
2227 nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
2228 nsIPrincipal* aPrincipal)
2229 {
2230 NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
2232 #ifdef PR_LOGGING
2233 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2234 nsAutoCString spec;
2235 aURI->GetSpec(spec);
2236 PR_LogPrint("DOCUMENT %p ResetToURI %s", this, spec.get());
2237 }
2238 #endif
2240 mSecurityInfo = nullptr;
2242 mDocumentLoadGroup = nullptr;
2244 // Delete references to sub-documents and kill the subdocument map,
2245 // if any. It holds strong references
2246 if (mSubDocuments) {
2247 PL_DHashTableDestroy(mSubDocuments);
2249 mSubDocuments = nullptr;
2250 }
2252 // Destroy link map now so we don't waste time removing
2253 // links one by one
2254 DestroyElementMaps();
2256 bool oldVal = mInUnlinkOrDeletion;
2257 mInUnlinkOrDeletion = true;
2258 uint32_t count = mChildren.ChildCount();
2259 { // Scope for update
2260 MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, true);
2261 for (int32_t i = int32_t(count) - 1; i >= 0; i--) {
2262 nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
2264 nsIContent* previousSibling = content->GetPreviousSibling();
2266 if (nsINode::GetFirstChild() == content) {
2267 mFirstChild = content->GetNextSibling();
2268 }
2269 mChildren.RemoveChildAt(i);
2270 nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
2271 content->UnbindFromTree();
2272 }
2273 mCachedRootElement = nullptr;
2274 }
2275 mInUnlinkOrDeletion = oldVal;
2277 mRegistry = nullptr;
2279 // Reset our stylesheets
2280 ResetStylesheetsToURI(aURI);
2282 // Release the listener manager
2283 if (mListenerManager) {
2284 mListenerManager->Disconnect();
2285 mListenerManager = nullptr;
2286 }
2288 // Release the stylesheets list.
2289 mDOMStyleSheets = nullptr;
2291 // Release our principal after tearing down the document, rather than before.
2292 // This ensures that, during teardown, the document and the dying window (which
2293 // already nulled out its document pointer and cached the principal) have
2294 // matching principals.
2295 SetPrincipal(nullptr);
2297 // Clear the original URI so SetDocumentURI sets it.
2298 mOriginalURI = nullptr;
2300 SetDocumentURI(aURI);
2301 mChromeXHRDocURI = aURI;
2302 // If mDocumentBaseURI is null, nsIDocument::GetBaseURI() returns
2303 // mDocumentURI.
2304 mDocumentBaseURI = nullptr;
2305 mChromeXHRDocBaseURI = nullptr;
2307 if (aLoadGroup) {
2308 mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2309 // there was an assertion here that aLoadGroup was not null. This
2310 // is no longer valid: nsDocShell::SetDocument does not create a
2311 // load group, and it works just fine
2313 // XXXbz what does "just fine" mean exactly? And given that there
2314 // is no nsDocShell::SetDocument, what is this talking about?
2315 }
2317 mLastModified.Truncate();
2318 // XXXbz I guess we're assuming that the caller will either pass in
2319 // a channel with a useful type or call SetContentType?
2320 SetContentTypeInternal(EmptyCString());
2321 mContentLanguage.Truncate();
2322 mBaseTarget.Truncate();
2323 mReferrer.Truncate();
2325 mXMLDeclarationBits = 0;
2327 // Now get our new principal
2328 if (aPrincipal) {
2329 SetPrincipal(aPrincipal);
2330 } else {
2331 nsIScriptSecurityManager *securityManager =
2332 nsContentUtils::GetSecurityManager();
2333 if (securityManager) {
2334 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2336 if (!docShell && aLoadGroup) {
2337 nsCOMPtr<nsIInterfaceRequestor> cbs;
2338 aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2339 docShell = do_GetInterface(cbs);
2340 }
2342 MOZ_ASSERT(docShell,
2343 "must be in a docshell or pass in an explicit principal");
2345 nsCOMPtr<nsIPrincipal> principal;
2346 nsresult rv = securityManager->
2347 GetDocShellCodebasePrincipal(mDocumentURI, docShell,
2348 getter_AddRefs(principal));
2349 if (NS_SUCCEEDED(rv)) {
2350 SetPrincipal(principal);
2351 }
2352 }
2353 }
2355 // Refresh the principal on the compartment.
2356 nsPIDOMWindow* win = GetInnerWindow();
2357 if (win) {
2358 win->RefreshCompartmentPrincipal();
2359 }
2360 }
2362 void
2363 nsDocument::RemoveDocStyleSheetsFromStyleSets()
2364 {
2365 // The stylesheets should forget us
2366 int32_t indx = mStyleSheets.Count();
2367 while (--indx >= 0) {
2368 nsIStyleSheet* sheet = mStyleSheets[indx];
2369 sheet->SetOwningDocument(nullptr);
2371 if (sheet->IsApplicable()) {
2372 nsCOMPtr<nsIPresShell> shell = GetShell();
2373 if (shell) {
2374 shell->StyleSet()->RemoveDocStyleSheet(sheet);
2375 }
2376 }
2377 // XXX Tell observers?
2378 }
2379 }
2381 void
2382 nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, nsStyleSet::sheetType aType)
2383 {
2384 // The stylesheets should forget us
2385 int32_t indx = aSheets.Count();
2386 while (--indx >= 0) {
2387 nsIStyleSheet* sheet = aSheets[indx];
2388 sheet->SetOwningDocument(nullptr);
2390 if (sheet->IsApplicable()) {
2391 nsCOMPtr<nsIPresShell> shell = GetShell();
2392 if (shell) {
2393 shell->StyleSet()->RemoveStyleSheet(aType, sheet);
2394 }
2395 }
2397 // XXX Tell observers?
2398 }
2400 }
2402 void
2403 nsDocument::ResetStylesheetsToURI(nsIURI* aURI)
2404 {
2405 MOZ_ASSERT(aURI);
2407 mozAutoDocUpdate upd(this, UPDATE_STYLE, true);
2408 RemoveDocStyleSheetsFromStyleSets();
2409 RemoveStyleSheetsFromStyleSets(mCatalogSheets, nsStyleSet::eAgentSheet);
2410 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet);
2411 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet);
2412 RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet);
2414 // Release all the sheets
2415 mStyleSheets.Clear();
2416 for (uint32_t i = 0; i < SheetTypeCount; ++i)
2417 mAdditionalSheets[i].Clear();
2419 // NOTE: We don't release the catalog sheets. It doesn't really matter
2420 // now, but it could in the future -- in which case not releasing them
2421 // is probably the right thing to do.
2423 // Now reset our inline style and attribute sheets.
2424 if (mAttrStyleSheet) {
2425 mAttrStyleSheet->Reset();
2426 mAttrStyleSheet->SetOwningDocument(this);
2427 } else {
2428 mAttrStyleSheet = new nsHTMLStyleSheet(this);
2429 }
2431 if (!mStyleAttrStyleSheet) {
2432 mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
2433 }
2435 // Now set up our style sets
2436 nsCOMPtr<nsIPresShell> shell = GetShell();
2437 if (shell) {
2438 FillStyleSet(shell->StyleSet());
2439 }
2440 }
2442 static bool
2443 AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData)
2444 {
2445 nsStyleSet *styleSet = static_cast<nsStyleSet*>(aData);
2446 styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet);
2447 return true;
2448 }
2450 static void
2451 AppendSheetsToStyleSet(nsStyleSet* aStyleSet,
2452 const nsCOMArray<nsIStyleSheet>& aSheets,
2453 nsStyleSet::sheetType aType)
2454 {
2455 for (int32_t i = aSheets.Count() - 1; i >= 0; --i) {
2456 aStyleSet->AppendStyleSheet(aType, aSheets[i]);
2457 }
2458 }
2461 void
2462 nsDocument::FillStyleSet(nsStyleSet* aStyleSet)
2463 {
2464 NS_PRECONDITION(aStyleSet, "Must have a style set");
2465 NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0,
2466 "Style set already has document sheets?");
2468 // We could consider moving this to nsStyleSet::Init, to match its
2469 // handling of the eAnimationSheet and eTransitionSheet levels.
2470 aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet);
2471 aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet);
2473 int32_t i;
2474 for (i = mStyleSheets.Count() - 1; i >= 0; --i) {
2475 nsIStyleSheet* sheet = mStyleSheets[i];
2476 if (sheet->IsApplicable()) {
2477 aStyleSet->AddDocStyleSheet(sheet, this);
2478 }
2479 }
2481 nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
2482 if (sheetService) {
2483 sheetService->AuthorStyleSheets()->EnumerateForwards(AppendAuthorSheet,
2484 aStyleSet);
2485 }
2487 for (i = mCatalogSheets.Count() - 1; i >= 0; --i) {
2488 nsIStyleSheet* sheet = mCatalogSheets[i];
2489 if (sheet->IsApplicable()) {
2490 aStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
2491 }
2492 }
2494 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
2495 nsStyleSet::eAgentSheet);
2496 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
2497 nsStyleSet::eUserSheet);
2498 AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
2499 nsStyleSet::eDocSheet);
2500 }
2502 nsresult
2503 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
2504 nsILoadGroup* aLoadGroup,
2505 nsISupports* aContainer,
2506 nsIStreamListener **aDocListener,
2507 bool aReset, nsIContentSink* aSink)
2508 {
2509 #ifdef PR_LOGGING
2510 if (gDocumentLeakPRLog && PR_LOG_TEST(gDocumentLeakPRLog, PR_LOG_DEBUG)) {
2511 nsCOMPtr<nsIURI> uri;
2512 aChannel->GetURI(getter_AddRefs(uri));
2513 nsAutoCString spec;
2514 if (uri)
2515 uri->GetSpec(spec);
2516 PR_LogPrint("DOCUMENT %p StartDocumentLoad %s", this, spec.get());
2517 }
2518 #endif
2520 #ifdef DEBUG
2521 {
2522 uint32_t appId;
2523 nsresult rv = NodePrincipal()->GetAppId(&appId);
2524 NS_ENSURE_SUCCESS(rv, rv);
2525 MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
2526 "Document should never have UNKNOWN_APP_ID");
2527 }
2528 #endif
2530 MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_UNINITIALIZED,
2531 "Bad readyState");
2532 SetReadyStateInternal(READYSTATE_LOADING);
2534 if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
2535 mLoadedAsData = true;
2536 // We need to disable script & style loading in this case.
2537 // We leave them disabled even in EndLoad(), and let anyone
2538 // who puts the document on display to worry about enabling.
2540 // Do not load/process scripts when loading as data
2541 ScriptLoader()->SetEnabled(false);
2543 // styles
2544 CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
2545 } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
2546 // Allow CSS, but not scripts
2547 ScriptLoader()->SetEnabled(false);
2548 }
2550 mMayStartLayout = false;
2552 mHaveInputEncoding = true;
2554 if (aReset) {
2555 Reset(aChannel, aLoadGroup);
2556 }
2558 nsAutoCString contentType;
2559 nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
2560 if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
2561 NS_LITERAL_STRING("contentType"), contentType))) ||
2562 NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
2563 // XXX this is only necessary for viewsource:
2564 nsACString::const_iterator start, end, semicolon;
2565 contentType.BeginReading(start);
2566 contentType.EndReading(end);
2567 semicolon = start;
2568 FindCharInReadable(';', semicolon, end);
2569 SetContentTypeInternal(Substring(start, semicolon));
2570 }
2572 RetrieveRelevantHeaders(aChannel);
2574 mChannel = aChannel;
2575 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
2576 if (inStrmChan) {
2577 bool isSrcdocChannel;
2578 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
2579 if (isSrcdocChannel) {
2580 mIsSrcdocDocument = true;
2581 }
2582 }
2584 // If this document is being loaded by a docshell, copy its sandbox flags
2585 // to the document. These are immutable after being set here.
2586 nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
2588 if (docShell) {
2589 nsresult rv = docShell->GetSandboxFlags(&mSandboxFlags);
2590 NS_ENSURE_SUCCESS(rv, rv);
2591 }
2593 // If this is not a data document, set CSP.
2594 if (!mLoadedAsData) {
2595 nsresult rv = InitCSP(aChannel);
2596 NS_ENSURE_SUCCESS(rv, rv);
2597 }
2599 return NS_OK;
2600 }
2602 void
2603 CSPErrorQueue::Add(const char* aMessageName)
2604 {
2605 mErrors.AppendElement(aMessageName);
2606 }
2608 void
2609 CSPErrorQueue::Flush(nsIDocument* aDocument)
2610 {
2611 for (uint32_t i = 0; i < mErrors.Length(); i++) {
2612 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2613 NS_LITERAL_CSTRING("CSP"), aDocument,
2614 nsContentUtils::eSECURITY_PROPERTIES,
2615 mErrors[i]);
2616 }
2617 mErrors.Clear();
2618 }
2620 void
2621 nsDocument::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages)
2622 {
2623 for (uint32_t i = 0; i < aMessages.Length(); ++i) {
2624 nsAutoString messageTag;
2625 aMessages[i]->GetTag(messageTag);
2627 nsAutoString category;
2628 aMessages[i]->GetCategory(category);
2630 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
2631 NS_ConvertUTF16toUTF8(category),
2632 this, nsContentUtils::eSECURITY_PROPERTIES,
2633 NS_ConvertUTF16toUTF8(messageTag).get());
2634 }
2635 }
2637 static nsresult
2638 AppendCSPFromHeader(nsIContentSecurityPolicy* csp, const nsAString& aHeaderValue,
2639 nsIURI* aSelfURI, bool aReportOnly, bool aSpecCompliant)
2640 {
2641 // Need to tokenize the header value since multiple headers could be
2642 // concatenated into one comma-separated list of policies.
2643 // See RFC2616 section 4.2 (last paragraph)
2644 nsresult rv = NS_OK;
2645 nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
2646 while (tokenizer.hasMoreTokens()) {
2647 const nsSubstring& policy = tokenizer.nextToken();
2648 rv = csp->AppendPolicy(policy, aSelfURI, aReportOnly, aSpecCompliant);
2649 NS_ENSURE_SUCCESS(rv, rv);
2650 #ifdef PR_LOGGING
2651 {
2652 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2653 ("CSP refined with policy: \"%s\"",
2654 NS_ConvertUTF16toUTF8(policy).get()));
2655 }
2656 #endif
2657 }
2658 return NS_OK;
2659 }
2661 nsresult
2662 nsDocument::InitCSP(nsIChannel* aChannel)
2663 {
2664 nsCOMPtr<nsIContentSecurityPolicy> csp;
2665 if (!CSPService::sCSPEnabled) {
2666 #ifdef PR_LOGGING
2667 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2668 ("CSP is disabled, skipping CSP init for document %p", this));
2669 #endif
2670 return NS_OK;
2671 }
2673 nsAutoCString tCspHeaderValue, tCspROHeaderValue;
2674 nsAutoCString tCspOldHeaderValue, tCspOldROHeaderValue;
2676 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
2677 if (httpChannel) {
2678 httpChannel->GetResponseHeader(
2679 NS_LITERAL_CSTRING("x-content-security-policy"),
2680 tCspOldHeaderValue);
2682 httpChannel->GetResponseHeader(
2683 NS_LITERAL_CSTRING("x-content-security-policy-report-only"),
2684 tCspOldROHeaderValue);
2686 httpChannel->GetResponseHeader(
2687 NS_LITERAL_CSTRING("content-security-policy"),
2688 tCspHeaderValue);
2690 httpChannel->GetResponseHeader(
2691 NS_LITERAL_CSTRING("content-security-policy-report-only"),
2692 tCspROHeaderValue);
2693 }
2694 NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
2695 NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
2696 NS_ConvertASCIItoUTF16 cspOldHeaderValue(tCspOldHeaderValue);
2697 NS_ConvertASCIItoUTF16 cspOldROHeaderValue(tCspOldROHeaderValue);
2699 // Only use the CSP 1.0 spec compliant headers if a pref to do so
2700 // is set. This lets us turn on the 1.0 parser per platform. This
2701 // pref is also set by the tests for 1.0 spec compliant CSP.
2702 bool specCompliantEnabled =
2703 Preferences::GetBool("security.csp.speccompliant");
2705 // If spec compliant pref isn't set, pretend we never got these headers.
2706 if ((!cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty()) &&
2707 !specCompliantEnabled) {
2708 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2709 ("Got spec compliant CSP headers but pref was not set"));
2710 cspHeaderValue.Truncate();
2711 cspROHeaderValue.Truncate();
2712 }
2714 // If both the new header AND the old header are present, warn that
2715 // the old header will be ignored. Otherwise, if the old header is
2716 // present, warn that it will be deprecated.
2717 bool oldHeaderIsPresent = !cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty();
2718 bool newHeaderIsPresent = !cspHeaderValue.IsEmpty() || !cspROHeaderValue.IsEmpty();
2720 if (oldHeaderIsPresent && newHeaderIsPresent) {
2721 mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent");
2722 } else if (oldHeaderIsPresent) {
2723 mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated");
2724 }
2726 // Figure out if we need to apply an app default CSP or a CSP from an app manifest
2727 nsIPrincipal* principal = NodePrincipal();
2729 uint16_t appStatus = principal->GetAppStatus();
2730 bool applyAppDefaultCSP = appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
2731 appStatus == nsIPrincipal::APP_STATUS_CERTIFIED;
2732 bool applyAppManifestCSP = false;
2734 nsAutoString appManifestCSP;
2735 if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
2736 nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
2737 if (appsService) {
2738 uint32_t appId = 0;
2739 if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
2740 appsService->GetCSPByLocalId(appId, appManifestCSP);
2741 if (!appManifestCSP.IsEmpty()) {
2742 applyAppManifestCSP = true;
2743 }
2744 }
2745 }
2746 }
2748 // If there's no CSP to apply, go ahead and return early
2749 if (!applyAppDefaultCSP &&
2750 !applyAppManifestCSP &&
2751 cspHeaderValue.IsEmpty() &&
2752 cspROHeaderValue.IsEmpty() &&
2753 cspOldHeaderValue.IsEmpty() &&
2754 cspOldROHeaderValue.IsEmpty()) {
2755 #ifdef PR_LOGGING
2756 nsCOMPtr<nsIURI> chanURI;
2757 aChannel->GetURI(getter_AddRefs(chanURI));
2758 nsAutoCString aspec;
2759 chanURI->GetAsciiSpec(aspec);
2760 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2761 ("no CSP for document, %s, %s",
2762 aspec.get(),
2763 applyAppDefaultCSP ? "is app" : "not an app"));
2764 #endif
2765 return NS_OK;
2766 }
2768 #ifdef PR_LOGGING
2769 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Document is an app or CSP header specified %p", this));
2770 #endif
2772 nsresult rv;
2774 // If Document is an app check to see if we already set CSP and return early
2775 // if that is indeed the case.
2776 //
2777 // In general (see bug 947831), we should not be setting CSP on a principal
2778 // that aliases another document. For non-app code this is not a problem
2779 // since we only share the underlying principal with nested browsing
2780 // contexts for which a header cannot be set (e.g., about:blank and
2781 // about:srcodoc iframes) and thus won't try to set the CSP again. This
2782 // check ensures that we do not try to set CSP for an app.
2783 if (applyAppDefaultCSP || applyAppManifestCSP) {
2784 nsCOMPtr<nsIContentSecurityPolicy> csp;
2785 rv = principal->GetCsp(getter_AddRefs(csp));
2786 NS_ENSURE_SUCCESS(rv, rv);
2788 if (csp) {
2789 #ifdef PR_LOGGING
2790 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("%s %s %s",
2791 "This document is sharing principal with another document.",
2792 "Since the document is an app, CSP was already set.",
2793 "Skipping attempt to set CSP."));
2794 #endif
2795 return NS_OK;
2796 }
2797 }
2799 // create new CSP object
2800 csp = do_CreateInstance("@mozilla.org/contentsecuritypolicy;1", &rv);
2802 if (NS_FAILED(rv)) {
2803 #ifdef PR_LOGGING
2804 PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("Failed to create CSP object: %x", rv));
2805 #endif
2806 return rv;
2807 }
2809 // used as a "self" identifier for the CSP.
2810 nsCOMPtr<nsIURI> selfURI;
2811 aChannel->GetURI(getter_AddRefs(selfURI));
2813 // Store the request context for violation reports
2814 csp->SetRequestContext(nullptr, nullptr, nullptr, aChannel);
2816 // ----- if the doc is an app and we want a default CSP, apply it.
2817 if (applyAppDefaultCSP) {
2818 nsAdoptingString appCSP;
2819 if (appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) {
2820 appCSP = Preferences::GetString("security.apps.privileged.CSP.default");
2821 NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.privileged.CSP.default");
2822 } else if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
2823 appCSP = Preferences::GetString("security.apps.certified.CSP.default");
2824 NS_ASSERTION(appCSP, "App, but no default CSP in security.apps.certified.CSP.default");
2825 }
2827 if (appCSP) {
2828 // Use the 1.0 CSP parser for apps if the pref to do so is set.
2829 csp->AppendPolicy(appCSP, selfURI, false, specCompliantEnabled);
2830 }
2831 }
2833 // ----- if the doc is an app and specifies a CSP in its manifest, apply it.
2834 if (applyAppManifestCSP) {
2835 // Use the 1.0 CSP parser for apps if the pref to do so is set.
2836 csp->AppendPolicy(appManifestCSP, selfURI, false, specCompliantEnabled);
2837 }
2839 // While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
2840 // take priority. If both are present, the x-* headers are ignored.
2842 // ----- if there's a full-strength CSP header, apply it.
2843 if (!cspHeaderValue.IsEmpty()) {
2844 rv = AppendCSPFromHeader(csp, cspHeaderValue, selfURI, false, true);
2845 NS_ENSURE_SUCCESS(rv, rv);
2846 } else if (!cspOldHeaderValue.IsEmpty()) {
2847 rv = AppendCSPFromHeader(csp, cspOldHeaderValue, selfURI, false, false);
2848 NS_ENSURE_SUCCESS(rv, rv);
2849 }
2851 // ----- if there's a report-only CSP header, apply it.
2852 if (!cspROHeaderValue.IsEmpty()) {
2853 rv = AppendCSPFromHeader(csp, cspROHeaderValue, selfURI, true, true);
2854 NS_ENSURE_SUCCESS(rv, rv);
2855 } else if (!cspOldROHeaderValue.IsEmpty()) {
2856 rv = AppendCSPFromHeader(csp, cspOldROHeaderValue, selfURI, true, false);
2857 NS_ENSURE_SUCCESS(rv, rv);
2858 }
2860 // ----- Enforce frame-ancestor policy on any applied policies
2861 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
2862 if (docShell) {
2863 bool safeAncestry = false;
2865 // PermitsAncestry sends violation reports when necessary
2866 rv = csp->PermitsAncestry(docShell, &safeAncestry);
2867 NS_ENSURE_SUCCESS(rv, rv);
2869 if (!safeAncestry) {
2870 #ifdef PR_LOGGING
2871 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2872 ("CSP doesn't like frame's ancestry, not loading."));
2873 #endif
2874 // stop! ERROR page!
2875 aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
2876 }
2877 }
2879 rv = principal->SetCsp(csp);
2880 NS_ENSURE_SUCCESS(rv, rv);
2881 #ifdef PR_LOGGING
2882 PR_LOG(gCspPRLog, PR_LOG_DEBUG,
2883 ("Inserted CSP into principal %p", principal));
2884 #endif
2886 return NS_OK;
2887 }
2889 void
2890 nsDocument::StopDocumentLoad()
2891 {
2892 if (mParser) {
2893 mParserAborted = true;
2894 mParser->Terminate();
2895 }
2896 }
2898 void
2899 nsDocument::SetDocumentURI(nsIURI* aURI)
2900 {
2901 nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
2902 mDocumentURI = NS_TryToMakeImmutable(aURI);
2903 nsIURI* newBase = GetDocBaseURI();
2905 bool equalBases = false;
2906 // Changing just the ref of a URI does not change how relative URIs would
2907 // resolve wrt to it, so we can treat the bases as equal as long as they're
2908 // equal ignoring the ref.
2909 if (oldBase && newBase) {
2910 oldBase->EqualsExceptRef(newBase, &equalBases);
2911 }
2912 else {
2913 equalBases = !oldBase && !newBase;
2914 }
2916 // If this is the first time we're setting the document's URI, set the
2917 // document's original URI.
2918 if (!mOriginalURI)
2919 mOriginalURI = mDocumentURI;
2921 // If changing the document's URI changed the base URI of the document, we
2922 // need to refresh the hrefs of all the links on the page.
2923 if (!equalBases) {
2924 RefreshLinkHrefs();
2925 }
2926 }
2928 void
2929 nsDocument::SetChromeXHRDocURI(nsIURI* aURI)
2930 {
2931 mChromeXHRDocURI = aURI;
2932 }
2934 void
2935 nsDocument::SetChromeXHRDocBaseURI(nsIURI* aURI)
2936 {
2937 mChromeXHRDocBaseURI = aURI;
2938 }
2940 NS_IMETHODIMP
2941 nsDocument::GetLastModified(nsAString& aLastModified)
2942 {
2943 nsIDocument::GetLastModified(aLastModified);
2944 return NS_OK;
2945 }
2947 void
2948 nsIDocument::GetLastModified(nsAString& aLastModified) const
2949 {
2950 if (!mLastModified.IsEmpty()) {
2951 aLastModified.Assign(mLastModified);
2952 } else {
2953 // If we for whatever reason failed to find the last modified time
2954 // (or even the current time), fall back to what NS4.x returned.
2955 aLastModified.Assign(NS_LITERAL_STRING("01/01/1970 00:00:00"));
2956 }
2957 }
2959 void
2960 nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
2961 {
2962 MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
2963 "Only put elements that need to be exposed as document['name'] in "
2964 "the named table.");
2966 nsIdentifierMapEntry *entry =
2967 mIdentifierMap.PutEntry(nsDependentAtomString(aName));
2969 // Null for out-of-memory
2970 if (entry) {
2971 if (!entry->HasNameElement() &&
2972 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2973 ++mExpandoAndGeneration.generation;
2974 }
2975 entry->AddNameElement(this, aElement);
2976 }
2977 }
2979 void
2980 nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
2981 {
2982 // Speed up document teardown
2983 if (mIdentifierMap.Count() == 0)
2984 return;
2986 nsIdentifierMapEntry *entry =
2987 mIdentifierMap.GetEntry(nsDependentAtomString(aName));
2988 if (!entry) // Could be false if the element was anonymous, hence never added
2989 return;
2991 entry->RemoveNameElement(aElement);
2992 if (!entry->HasNameElement() &&
2993 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
2994 ++mExpandoAndGeneration.generation;
2995 }
2996 }
2998 void
2999 nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
3000 {
3001 nsIdentifierMapEntry *entry =
3002 mIdentifierMap.PutEntry(nsDependentAtomString(aId));
3004 if (entry) { /* True except on OOM */
3005 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3006 !entry->HasNameElement() &&
3007 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3008 ++mExpandoAndGeneration.generation;
3009 }
3010 entry->AddIdElement(aElement);
3011 }
3012 }
3014 void
3015 nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
3016 {
3017 NS_ASSERTION(aId, "huhwhatnow?");
3019 // Speed up document teardown
3020 if (mIdentifierMap.Count() == 0) {
3021 return;
3022 }
3024 nsIdentifierMapEntry *entry =
3025 mIdentifierMap.GetEntry(nsDependentAtomString(aId));
3026 if (!entry) // Can be null for XML elements with changing ids.
3027 return;
3029 entry->RemoveIdElement(aElement);
3030 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
3031 !entry->HasNameElement() &&
3032 !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3033 ++mExpandoAndGeneration.generation;
3034 }
3035 if (entry->IsEmpty()) {
3036 mIdentifierMap.RawRemoveEntry(entry);
3037 }
3038 }
3040 nsIPrincipal*
3041 nsDocument::GetPrincipal()
3042 {
3043 return NodePrincipal();
3044 }
3046 extern bool sDisablePrefetchHTTPSPref;
3048 void
3049 nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
3050 {
3051 if (aNewPrincipal && mAllowDNSPrefetch && sDisablePrefetchHTTPSPref) {
3052 nsCOMPtr<nsIURI> uri;
3053 aNewPrincipal->GetURI(getter_AddRefs(uri));
3054 bool isHTTPS;
3055 if (!uri || NS_FAILED(uri->SchemeIs("https", &isHTTPS)) ||
3056 isHTTPS) {
3057 mAllowDNSPrefetch = false;
3058 }
3059 }
3060 mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
3061 }
3063 NS_IMETHODIMP
3064 nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache)
3065 {
3066 NS_IF_ADDREF(*aApplicationCache = mApplicationCache);
3068 return NS_OK;
3069 }
3071 NS_IMETHODIMP
3072 nsDocument::SetApplicationCache(nsIApplicationCache *aApplicationCache)
3073 {
3074 mApplicationCache = aApplicationCache;
3076 return NS_OK;
3077 }
3079 NS_IMETHODIMP
3080 nsDocument::GetContentType(nsAString& aContentType)
3081 {
3082 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
3084 return NS_OK;
3085 }
3087 void
3088 nsDocument::SetContentType(const nsAString& aContentType)
3089 {
3090 NS_ASSERTION(GetContentTypeInternal().IsEmpty() ||
3091 GetContentTypeInternal().Equals(NS_ConvertUTF16toUTF8(aContentType)),
3092 "Do you really want to change the content-type?");
3094 SetContentTypeInternal(NS_ConvertUTF16toUTF8(aContentType));
3095 }
3097 nsresult
3098 nsDocument::GetAllowPlugins(bool * aAllowPlugins)
3099 {
3100 // First, we ask our docshell if it allows plugins.
3101 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3103 if (docShell) {
3104 docShell->GetAllowPlugins(aAllowPlugins);
3106 // If the docshell allows plugins, we check whether
3107 // we are sandboxed and plugins should not be allowed.
3108 if (*aAllowPlugins)
3109 *aAllowPlugins = !(mSandboxFlags & SANDBOXED_PLUGINS);
3110 }
3112 return NS_OK;
3113 }
3115 already_AddRefed<UndoManager>
3116 nsDocument::GetUndoManager()
3117 {
3118 Element* rootElement = GetRootElement();
3119 if (!rootElement) {
3120 return nullptr;
3121 }
3123 if (!mUndoManager) {
3124 mUndoManager = new UndoManager(rootElement);
3125 }
3127 nsRefPtr<UndoManager> undoManager = mUndoManager;
3128 return undoManager.forget();
3129 }
3131 /* Return true if the document is in the focused top-level window, and is an
3132 * ancestor of the focused DOMWindow. */
3133 NS_IMETHODIMP
3134 nsDocument::HasFocus(bool* aResult)
3135 {
3136 ErrorResult rv;
3137 *aResult = nsIDocument::HasFocus(rv);
3138 return rv.ErrorCode();
3139 }
3141 bool
3142 nsIDocument::HasFocus(ErrorResult& rv) const
3143 {
3144 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3145 if (!fm) {
3146 rv.Throw(NS_ERROR_NOT_AVAILABLE);
3147 return false;
3148 }
3150 // Is there a focused DOMWindow?
3151 nsCOMPtr<nsIDOMWindow> focusedWindow;
3152 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3153 if (!focusedWindow) {
3154 return false;
3155 }
3157 // Are we an ancestor of the focused DOMWindow?
3158 nsCOMPtr<nsIDOMDocument> domDocument;
3159 focusedWindow->GetDocument(getter_AddRefs(domDocument));
3160 nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
3162 for (nsIDocument* currentDoc = document; currentDoc;
3163 currentDoc = currentDoc->GetParentDocument()) {
3164 if (currentDoc == this) {
3165 // Yes, we are an ancestor
3166 return true;
3167 }
3168 }
3170 return false;
3171 }
3173 NS_IMETHODIMP
3174 nsDocument::GetReferrer(nsAString& aReferrer)
3175 {
3176 nsIDocument::GetReferrer(aReferrer);
3177 return NS_OK;
3178 }
3180 void
3181 nsIDocument::GetReferrer(nsAString& aReferrer) const
3182 {
3183 if (mIsSrcdocDocument && mParentDocument)
3184 mParentDocument->GetReferrer(aReferrer);
3185 else
3186 CopyUTF8toUTF16(mReferrer, aReferrer);
3187 }
3189 nsresult
3190 nsIDocument::GetSrcdocData(nsAString &aSrcdocData)
3191 {
3192 if (mIsSrcdocDocument) {
3193 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3194 if (inStrmChan) {
3195 return inStrmChan->GetSrcdocData(aSrcdocData);
3196 }
3197 }
3198 aSrcdocData = NullString();
3199 return NS_OK;
3200 }
3202 NS_IMETHODIMP
3203 nsDocument::GetActiveElement(nsIDOMElement **aElement)
3204 {
3205 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetActiveElement()));
3206 el.forget(aElement);
3207 return NS_OK;
3208 }
3210 Element*
3211 nsIDocument::GetActiveElement()
3212 {
3213 // Get the focused element.
3214 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
3215 if (window) {
3216 nsCOMPtr<nsPIDOMWindow> focusedWindow;
3217 nsIContent* focusedContent =
3218 nsFocusManager::GetFocusedDescendant(window, false,
3219 getter_AddRefs(focusedWindow));
3220 // be safe and make sure the element is from this document
3221 if (focusedContent && focusedContent->OwnerDoc() == this) {
3222 if (focusedContent->ChromeOnlyAccess()) {
3223 focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
3224 }
3225 if (focusedContent) {
3226 return focusedContent->AsElement();
3227 }
3228 }
3229 }
3231 // No focused element anywhere in this document. Try to get the BODY.
3232 nsRefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
3233 if (htmlDoc) {
3234 // Because of IE compatibility, return null when html document doesn't have
3235 // a body.
3236 return htmlDoc->GetBody();
3237 }
3239 // If we couldn't get a BODY, return the root element.
3240 return GetDocumentElement();
3241 }
3243 NS_IMETHODIMP
3244 nsDocument::GetCurrentScript(nsIDOMElement **aElement)
3245 {
3246 nsCOMPtr<nsIDOMElement> el(do_QueryInterface(nsIDocument::GetCurrentScript()));
3247 el.forget(aElement);
3248 return NS_OK;
3249 }
3251 Element*
3252 nsIDocument::GetCurrentScript()
3253 {
3254 nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
3255 return el;
3256 }
3258 NS_IMETHODIMP
3259 nsDocument::ElementFromPoint(float aX, float aY, nsIDOMElement** aReturn)
3260 {
3261 Element* el = nsIDocument::ElementFromPoint(aX, aY);
3262 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
3263 retval.forget(aReturn);
3264 return NS_OK;
3265 }
3267 Element*
3268 nsIDocument::ElementFromPoint(float aX, float aY)
3269 {
3270 return ElementFromPointHelper(aX, aY, false, true);
3271 }
3273 Element*
3274 nsDocument::ElementFromPointHelper(float aX, float aY,
3275 bool aIgnoreRootScrollFrame,
3276 bool aFlushLayout)
3277 {
3278 // As per the the spec, we return null if either coord is negative
3279 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0)) {
3280 return nullptr;
3281 }
3283 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
3284 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
3285 nsPoint pt(x, y);
3287 // Make sure the layout information we get is up-to-date, and
3288 // ensure we get a root frame (for everything but XUL)
3289 if (aFlushLayout)
3290 FlushPendingNotifications(Flush_Layout);
3292 nsIPresShell *ps = GetShell();
3293 if (!ps) {
3294 return nullptr;
3295 }
3296 nsIFrame *rootFrame = ps->GetRootFrame();
3298 // XUL docs, unlike HTML, have no frame tree until everything's done loading
3299 if (!rootFrame) {
3300 return nullptr; // return null to premature XUL callers as a reminder to wait
3301 }
3303 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
3304 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3305 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3306 if (!ptFrame) {
3307 return nullptr;
3308 }
3310 nsIContent* elem = GetContentInThisDocument(ptFrame);
3311 if (elem && !elem->IsElement()) {
3312 elem = elem->GetParent();
3313 }
3314 return elem ? elem->AsElement() : nullptr;
3315 }
3317 nsresult
3318 nsDocument::NodesFromRectHelper(float aX, float aY,
3319 float aTopSize, float aRightSize,
3320 float aBottomSize, float aLeftSize,
3321 bool aIgnoreRootScrollFrame,
3322 bool aFlushLayout,
3323 nsIDOMNodeList** aReturn)
3324 {
3325 NS_ENSURE_ARG_POINTER(aReturn);
3327 nsSimpleContentList* elements = new nsSimpleContentList(this);
3328 NS_ADDREF(elements);
3329 *aReturn = elements;
3331 // Following the same behavior of elementFromPoint,
3332 // we don't return anything if either coord is negative
3333 if (!aIgnoreRootScrollFrame && (aX < 0 || aY < 0))
3334 return NS_OK;
3336 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX - aLeftSize);
3337 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY - aTopSize);
3338 nscoord w = nsPresContext::CSSPixelsToAppUnits(aLeftSize + aRightSize) + 1;
3339 nscoord h = nsPresContext::CSSPixelsToAppUnits(aTopSize + aBottomSize) + 1;
3341 nsRect rect(x, y, w, h);
3343 // Make sure the layout information we get is up-to-date, and
3344 // ensure we get a root frame (for everything but XUL)
3345 if (aFlushLayout) {
3346 FlushPendingNotifications(Flush_Layout);
3347 }
3349 nsIPresShell *ps = GetShell();
3350 NS_ENSURE_STATE(ps);
3351 nsIFrame *rootFrame = ps->GetRootFrame();
3353 // XUL docs, unlike HTML, have no frame tree until everything's done loading
3354 if (!rootFrame)
3355 return NS_OK; // return nothing to premature XUL callers as a reminder to wait
3357 nsAutoTArray<nsIFrame*,8> outFrames;
3358 nsLayoutUtils::GetFramesForArea(rootFrame, rect, outFrames,
3359 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC |
3360 (aIgnoreRootScrollFrame ? nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0));
3362 // Used to filter out repeated elements in sequence.
3363 nsIContent* lastAdded = nullptr;
3365 for (uint32_t i = 0; i < outFrames.Length(); i++) {
3366 nsIContent* node = GetContentInThisDocument(outFrames[i]);
3368 if (node && !node->IsElement() && !node->IsNodeOfType(nsINode::eTEXT)) {
3369 // We have a node that isn't an element or a text node,
3370 // use its parent content instead.
3371 node = node->GetParent();
3372 }
3373 if (node && node != lastAdded) {
3374 elements->AppendElement(node);
3375 lastAdded = node;
3376 }
3377 }
3379 return NS_OK;
3380 }
3382 NS_IMETHODIMP
3383 nsDocument::GetElementsByClassName(const nsAString& aClasses,
3384 nsIDOMNodeList** aReturn)
3385 {
3386 *aReturn = nsIDocument::GetElementsByClassName(aClasses).take();
3387 return NS_OK;
3388 }
3390 already_AddRefed<nsContentList>
3391 nsIDocument::GetElementsByClassName(const nsAString& aClasses)
3392 {
3393 return nsContentUtils::GetElementsByClassName(this, aClasses);
3394 }
3396 NS_IMETHODIMP
3397 nsDocument::ReleaseCapture()
3398 {
3399 nsIDocument::ReleaseCapture();
3400 return NS_OK;
3401 }
3403 void
3404 nsIDocument::ReleaseCapture() const
3405 {
3406 // only release the capture if the caller can access it. This prevents a
3407 // page from stopping a scrollbar grab for example.
3408 nsCOMPtr<nsINode> node = nsIPresShell::GetCapturingContent();
3409 if (node && nsContentUtils::CanCallerAccess(node)) {
3410 nsIPresShell::SetCapturingContent(nullptr, 0);
3411 }
3412 }
3414 already_AddRefed<nsIURI>
3415 nsIDocument::GetBaseURI(bool aTryUseXHRDocBaseURI) const
3416 {
3417 nsCOMPtr<nsIURI> uri;
3418 if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
3419 uri = mChromeXHRDocBaseURI;
3420 } else {
3421 uri = GetDocBaseURI();
3422 }
3424 return uri.forget();
3425 }
3427 nsresult
3428 nsDocument::SetBaseURI(nsIURI* aURI)
3429 {
3430 if (!aURI && !mDocumentBaseURI) {
3431 return NS_OK;
3432 }
3434 // Don't do anything if the URI wasn't actually changed.
3435 if (aURI && mDocumentBaseURI) {
3436 bool equalBases = false;
3437 mDocumentBaseURI->Equals(aURI, &equalBases);
3438 if (equalBases) {
3439 return NS_OK;
3440 }
3441 }
3443 if (aURI) {
3444 mDocumentBaseURI = NS_TryToMakeImmutable(aURI);
3445 } else {
3446 mDocumentBaseURI = nullptr;
3447 }
3448 RefreshLinkHrefs();
3450 return NS_OK;
3451 }
3453 void
3454 nsDocument::GetBaseTarget(nsAString &aBaseTarget)
3455 {
3456 aBaseTarget = mBaseTarget;
3457 }
3459 void
3460 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
3461 {
3462 // XXX it would be a good idea to assert the sanity of the argument,
3463 // but before we figure out what to do about non-Encoding Standard
3464 // encodings in the charset menu and in mailnews, assertions are futile.
3465 if (!mCharacterSet.Equals(aCharSetID)) {
3466 mCharacterSet = aCharSetID;
3468 int32_t n = mCharSetObservers.Length();
3470 for (int32_t i = 0; i < n; i++) {
3471 nsIObserver* observer = mCharSetObservers.ElementAt(i);
3473 observer->Observe(static_cast<nsIDocument *>(this), "charset",
3474 NS_ConvertASCIItoUTF16(aCharSetID).get());
3475 }
3476 }
3477 }
3479 nsresult
3480 nsDocument::AddCharSetObserver(nsIObserver* aObserver)
3481 {
3482 NS_ENSURE_ARG_POINTER(aObserver);
3484 NS_ENSURE_TRUE(mCharSetObservers.AppendElement(aObserver), NS_ERROR_FAILURE);
3486 return NS_OK;
3487 }
3489 void
3490 nsDocument::RemoveCharSetObserver(nsIObserver* aObserver)
3491 {
3492 mCharSetObservers.RemoveElement(aObserver);
3493 }
3495 void
3496 nsDocument::GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const
3497 {
3498 aData.Truncate();
3499 const nsDocHeaderData* data = mHeaderData;
3500 while (data) {
3501 if (data->mField == aHeaderField) {
3502 aData = data->mData;
3504 break;
3505 }
3506 data = data->mNext;
3507 }
3508 }
3510 void
3511 nsDocument::SetHeaderData(nsIAtom* aHeaderField, const nsAString& aData)
3512 {
3513 if (!aHeaderField) {
3514 NS_ERROR("null headerField");
3515 return;
3516 }
3518 if (!mHeaderData) {
3519 if (!aData.IsEmpty()) { // don't bother storing empty string
3520 mHeaderData = new nsDocHeaderData(aHeaderField, aData);
3521 }
3522 }
3523 else {
3524 nsDocHeaderData* data = mHeaderData;
3525 nsDocHeaderData** lastPtr = &mHeaderData;
3526 bool found = false;
3527 do { // look for existing and replace
3528 if (data->mField == aHeaderField) {
3529 if (!aData.IsEmpty()) {
3530 data->mData.Assign(aData);
3531 }
3532 else { // don't store empty string
3533 *lastPtr = data->mNext;
3534 data->mNext = nullptr;
3535 delete data;
3536 }
3537 found = true;
3539 break;
3540 }
3541 lastPtr = &(data->mNext);
3542 data = *lastPtr;
3543 } while (data);
3545 if (!aData.IsEmpty() && !found) {
3546 // didn't find, append
3547 *lastPtr = new nsDocHeaderData(aHeaderField, aData);
3548 }
3549 }
3551 if (aHeaderField == nsGkAtoms::headerContentLanguage) {
3552 CopyUTF16toUTF8(aData, mContentLanguage);
3553 }
3555 if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
3556 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
3557 // spec.
3558 if (DOMStringIsNull(mLastStyleSheetSet)) {
3559 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
3560 // per spec. The idea here is that we're changing our preferred set and
3561 // that shouldn't change the value of lastStyleSheetSet. Also, we're
3562 // using the Internal version so we can update the CSSLoader and not have
3563 // to worry about null strings.
3564 EnableStyleSheetsForSetInternal(aData, true);
3565 }
3566 }
3568 if (aHeaderField == nsGkAtoms::refresh) {
3569 // We get into this code before we have a script global yet, so get to
3570 // our container via mDocumentContainer.
3571 nsCOMPtr<nsIRefreshURI> refresher(mDocumentContainer);
3572 if (refresher) {
3573 // Note: using mDocumentURI instead of mBaseURI here, for consistency
3574 // (used to just use the current URI of our webnavigation, but that
3575 // should really be the same thing). Note that this code can run
3576 // before the current URI of the webnavigation has been updated, so we
3577 // can't assert equality here.
3578 refresher->SetupRefreshURIFromHeader(mDocumentURI, NodePrincipal(),
3579 NS_ConvertUTF16toUTF8(aData));
3580 }
3581 }
3583 if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
3584 mAllowDNSPrefetch) {
3585 // Chromium treats any value other than 'on' (case insensitive) as 'off'.
3586 mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
3587 }
3589 if (aHeaderField == nsGkAtoms::viewport ||
3590 aHeaderField == nsGkAtoms::handheldFriendly ||
3591 aHeaderField == nsGkAtoms::viewport_minimum_scale ||
3592 aHeaderField == nsGkAtoms::viewport_maximum_scale ||
3593 aHeaderField == nsGkAtoms::viewport_initial_scale ||
3594 aHeaderField == nsGkAtoms::viewport_height ||
3595 aHeaderField == nsGkAtoms::viewport_width ||
3596 aHeaderField == nsGkAtoms::viewport_user_scalable) {
3597 mViewportType = Unknown;
3598 }
3599 }
3601 void
3602 nsDocument::TryChannelCharset(nsIChannel *aChannel,
3603 int32_t& aCharsetSource,
3604 nsACString& aCharset,
3605 nsHtml5TreeOpExecutor* aExecutor)
3606 {
3607 if (aChannel) {
3608 nsAutoCString charsetVal;
3609 nsresult rv = aChannel->GetContentCharset(charsetVal);
3610 if (NS_SUCCEEDED(rv)) {
3611 nsAutoCString preferred;
3612 if(EncodingUtils::FindEncodingForLabel(charsetVal, preferred)) {
3613 aCharset = preferred;
3614 aCharsetSource = kCharsetFromChannel;
3615 return;
3616 } else if (aExecutor && !charsetVal.IsEmpty()) {
3617 aExecutor->ComplainAboutBogusProtocolCharset(this);
3618 }
3619 }
3620 }
3621 }
3623 already_AddRefed<nsIPresShell>
3624 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
3625 nsStyleSet* aStyleSet)
3626 {
3627 // Don't add anything here. Add it to |doCreateShell| instead.
3628 // This exists so that subclasses can pass other values for the 4th
3629 // parameter some of the time.
3630 return doCreateShell(aContext, aViewManager, aStyleSet,
3631 eCompatibility_FullStandards);
3632 }
3634 already_AddRefed<nsIPresShell>
3635 nsDocument::doCreateShell(nsPresContext* aContext,
3636 nsViewManager* aViewManager, nsStyleSet* aStyleSet,
3637 nsCompatibility aCompatMode)
3638 {
3639 NS_ASSERTION(!mPresShell, "We have a presshell already!");
3641 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
3643 FillStyleSet(aStyleSet);
3645 nsRefPtr<PresShell> shell = new PresShell;
3646 shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
3648 // Note: we don't hold a ref to the shell (it holds a ref to us)
3649 mPresShell = shell;
3651 // Make sure to never paint if we belong to an invisible DocShell.
3652 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
3653 if (docShell && docShell->IsInvisible())
3654 shell->SetNeverPainting(true);
3656 mExternalResourceMap.ShowViewers();
3658 MaybeRescheduleAnimationFrameNotifications();
3660 return shell.forget();
3661 }
3663 void
3664 nsDocument::MaybeRescheduleAnimationFrameNotifications()
3665 {
3666 if (!mPresShell || !IsEventHandlingEnabled()) {
3667 // bail out for now, until one of those conditions changes
3668 return;
3669 }
3671 nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver();
3672 if (!mFrameRequestCallbacks.IsEmpty()) {
3673 rd->ScheduleFrameRequestCallbacks(this);
3674 }
3675 }
3677 void
3678 nsIDocument::TakeFrameRequestCallbacks(FrameRequestCallbackList& aCallbacks)
3679 {
3680 aCallbacks.AppendElements(mFrameRequestCallbacks);
3681 mFrameRequestCallbacks.Clear();
3682 }
3684 PLDHashOperator RequestDiscardEnumerator(imgIRequest* aKey,
3685 uint32_t aData,
3686 void* userArg)
3687 {
3688 aKey->RequestDiscard();
3689 return PL_DHASH_NEXT;
3690 }
3692 void
3693 nsDocument::DeleteShell()
3694 {
3695 mExternalResourceMap.HideViewers();
3696 if (IsEventHandlingEnabled()) {
3697 RevokeAnimationFrameNotifications();
3698 }
3700 // When our shell goes away, request that all our images be immediately
3701 // discarded, so we don't carry around decoded image data for a document we
3702 // no longer intend to paint.
3703 mImageTracker.EnumerateRead(RequestDiscardEnumerator, nullptr);
3705 mPresShell = nullptr;
3706 }
3708 void
3709 nsDocument::RevokeAnimationFrameNotifications()
3710 {
3711 if (!mFrameRequestCallbacks.IsEmpty()) {
3712 mPresShell->GetPresContext()->RefreshDriver()->
3713 RevokeFrameRequestCallbacks(this);
3714 }
3715 }
3717 static void
3718 SubDocClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
3719 {
3720 SubDocMapEntry *e = static_cast<SubDocMapEntry *>(entry);
3722 NS_RELEASE(e->mKey);
3723 if (e->mSubDocument) {
3724 e->mSubDocument->SetParentDocument(nullptr);
3725 NS_RELEASE(e->mSubDocument);
3726 }
3727 }
3729 static bool
3730 SubDocInitEntry(PLDHashTable *table, PLDHashEntryHdr *entry, const void *key)
3731 {
3732 SubDocMapEntry *e =
3733 const_cast<SubDocMapEntry *>
3734 (static_cast<const SubDocMapEntry *>(entry));
3736 e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
3737 NS_ADDREF(e->mKey);
3739 e->mSubDocument = nullptr;
3740 return true;
3741 }
3743 nsresult
3744 nsDocument::SetSubDocumentFor(Element* aElement, nsIDocument* aSubDoc)
3745 {
3746 NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
3748 if (!aSubDoc) {
3749 // aSubDoc is nullptr, remove the mapping
3751 if (mSubDocuments) {
3752 SubDocMapEntry *entry =
3753 static_cast<SubDocMapEntry*>
3754 (PL_DHashTableOperate(mSubDocuments, aElement,
3755 PL_DHASH_LOOKUP));
3757 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3758 PL_DHashTableRawRemove(mSubDocuments, entry);
3759 }
3760 }
3761 } else {
3762 if (!mSubDocuments) {
3763 // Create a new hashtable
3765 static const PLDHashTableOps hash_table_ops =
3766 {
3767 PL_DHashAllocTable,
3768 PL_DHashFreeTable,
3769 PL_DHashVoidPtrKeyStub,
3770 PL_DHashMatchEntryStub,
3771 PL_DHashMoveEntryStub,
3772 SubDocClearEntry,
3773 PL_DHashFinalizeStub,
3774 SubDocInitEntry
3775 };
3777 mSubDocuments = PL_NewDHashTable(&hash_table_ops, nullptr,
3778 sizeof(SubDocMapEntry), 16);
3779 if (!mSubDocuments) {
3780 return NS_ERROR_OUT_OF_MEMORY;
3781 }
3782 }
3784 // Add a mapping to the hash table
3785 SubDocMapEntry *entry =
3786 static_cast<SubDocMapEntry*>
3787 (PL_DHashTableOperate(mSubDocuments, aElement,
3788 PL_DHASH_ADD));
3790 if (!entry) {
3791 return NS_ERROR_OUT_OF_MEMORY;
3792 }
3794 if (entry->mSubDocument) {
3795 entry->mSubDocument->SetParentDocument(nullptr);
3797 // Release the old sub document
3798 NS_RELEASE(entry->mSubDocument);
3799 }
3801 entry->mSubDocument = aSubDoc;
3802 NS_ADDREF(entry->mSubDocument);
3804 aSubDoc->SetParentDocument(this);
3805 }
3807 return NS_OK;
3808 }
3810 nsIDocument*
3811 nsDocument::GetSubDocumentFor(nsIContent *aContent) const
3812 {
3813 if (mSubDocuments && aContent->IsElement()) {
3814 SubDocMapEntry *entry =
3815 static_cast<SubDocMapEntry*>
3816 (PL_DHashTableOperate(mSubDocuments, aContent->AsElement(),
3817 PL_DHASH_LOOKUP));
3819 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
3820 return entry->mSubDocument;
3821 }
3822 }
3824 return nullptr;
3825 }
3827 static PLDHashOperator
3828 FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
3829 uint32_t number, void *arg)
3830 {
3831 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
3832 FindContentData *data = static_cast<FindContentData*>(arg);
3834 if (entry->mSubDocument == data->mSubDocument) {
3835 data->mResult = entry->mKey;
3837 return PL_DHASH_STOP;
3838 }
3840 return PL_DHASH_NEXT;
3841 }
3843 Element*
3844 nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
3845 {
3846 NS_ENSURE_TRUE(aDocument, nullptr);
3848 if (!mSubDocuments) {
3849 return nullptr;
3850 }
3852 FindContentData data(aDocument);
3853 PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data);
3855 return data.mResult;
3856 }
3858 bool
3859 nsDocument::IsNodeOfType(uint32_t aFlags) const
3860 {
3861 return !(aFlags & ~eDOCUMENT);
3862 }
3864 Element*
3865 nsIDocument::GetRootElement() const
3866 {
3867 return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
3868 mCachedRootElement : GetRootElementInternal();
3869 }
3871 Element*
3872 nsDocument::GetRootElementInternal() const
3873 {
3874 // Loop backwards because any non-elements, such as doctypes and PIs
3875 // are likely to appear before the root element.
3876 uint32_t i;
3877 for (i = mChildren.ChildCount(); i > 0; --i) {
3878 nsIContent* child = mChildren.ChildAt(i - 1);
3879 if (child->IsElement()) {
3880 const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
3881 return child->AsElement();
3882 }
3883 }
3885 const_cast<nsDocument*>(this)->mCachedRootElement = nullptr;
3886 return nullptr;
3887 }
3889 nsIContent *
3890 nsDocument::GetChildAt(uint32_t aIndex) const
3891 {
3892 return mChildren.GetSafeChildAt(aIndex);
3893 }
3895 int32_t
3896 nsDocument::IndexOf(const nsINode* aPossibleChild) const
3897 {
3898 return mChildren.IndexOfChild(aPossibleChild);
3899 }
3901 uint32_t
3902 nsDocument::GetChildCount() const
3903 {
3904 return mChildren.ChildCount();
3905 }
3907 nsIContent * const *
3908 nsDocument::GetChildArray(uint32_t* aChildCount) const
3909 {
3910 return mChildren.GetChildArray(aChildCount);
3911 }
3914 nsresult
3915 nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
3916 bool aNotify)
3917 {
3918 if (aKid->IsElement() && GetRootElement()) {
3919 NS_WARNING("Inserting root element when we already have one");
3920 return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
3921 }
3923 return doInsertChildAt(aKid, aIndex, aNotify, mChildren);
3924 }
3926 nsresult
3927 nsDocument::AppendChildTo(nsIContent* aKid, bool aNotify)
3928 {
3929 // Make sure to _not_ call the subclass InsertChildAt here. If
3930 // subclasses wanted to hook into this stuff, they would have
3931 // overridden AppendChildTo.
3932 // XXXbz maybe this should just be a non-virtual method on nsINode?
3933 // Feels that way to me...
3934 return nsDocument::InsertChildAt(aKid, GetChildCount(), aNotify);
3935 }
3937 void
3938 nsDocument::RemoveChildAt(uint32_t aIndex, bool aNotify)
3939 {
3940 nsCOMPtr<nsIContent> oldKid = GetChildAt(aIndex);
3941 if (!oldKid) {
3942 return;
3943 }
3945 if (oldKid->IsElement()) {
3946 // Destroy the link map up front before we mess with the child list.
3947 DestroyElementMaps();
3948 }
3950 doRemoveChildAt(aIndex, aNotify, oldKid, mChildren);
3951 mCachedRootElement = nullptr;
3952 }
3954 int32_t
3955 nsDocument::GetNumberOfStyleSheets() const
3956 {
3957 return mStyleSheets.Count();
3958 }
3960 nsIStyleSheet*
3961 nsDocument::GetStyleSheetAt(int32_t aIndex) const
3962 {
3963 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mStyleSheets.Count(), nullptr);
3964 return mStyleSheets[aIndex];
3965 }
3967 int32_t
3968 nsDocument::GetIndexOfStyleSheet(nsIStyleSheet* aSheet) const
3969 {
3970 return mStyleSheets.IndexOf(aSheet);
3971 }
3973 void
3974 nsDocument::AddStyleSheetToStyleSets(nsIStyleSheet* aSheet)
3975 {
3976 nsCOMPtr<nsIPresShell> shell = GetShell();
3977 if (shell) {
3978 shell->StyleSet()->AddDocStyleSheet(aSheet, this);
3979 }
3980 }
3982 #define DO_STYLESHEET_NOTIFICATION(createFunc, concreteInterface, initMethod, type, ...) \
3983 do { \
3984 nsCOMPtr<nsIDOMEvent> event; \
3985 nsresult rv = createFunc(getter_AddRefs(event), this, \
3986 mPresShell ? \
3987 mPresShell->GetPresContext() : nullptr, \
3988 nullptr); \
3989 if (NS_FAILED(rv)) { \
3990 return; \
3991 } \
3992 nsCOMPtr<nsIDOMCSSStyleSheet> cssSheet(do_QueryInterface(aSheet)); \
3993 if (!cssSheet) { \
3994 return; \
3995 } \
3996 nsCOMPtr<concreteInterface> ssEvent(do_QueryInterface(event)); \
3997 MOZ_ASSERT(ssEvent); \
3998 ssEvent->initMethod(NS_LITERAL_STRING(type), true, true, \
3999 cssSheet, __VA_ARGS__); \
4000 event->SetTrusted(true); \
4001 event->SetTarget(this); \
4002 nsRefPtr<AsyncEventDispatcher> asyncDispatcher = \
4003 new AsyncEventDispatcher(this, event); \
4004 asyncDispatcher->mDispatchChromeOnly = true; \
4005 asyncDispatcher->PostDOMEvent(); \
4006 } while (0);
4008 void
4009 nsDocument::NotifyStyleSheetAdded(nsIStyleSheet* aSheet, bool aDocumentSheet)
4010 {
4011 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetAdded, (this, aSheet, aDocumentSheet));
4013 if (StyleSheetChangeEventsEnabled()) {
4014 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent,
4015 nsIDOMStyleSheetChangeEvent,
4016 InitStyleSheetChangeEvent,
4017 "StyleSheetAdded",
4018 aDocumentSheet);
4019 }
4020 }
4022 void
4023 nsDocument::NotifyStyleSheetRemoved(nsIStyleSheet* aSheet, bool aDocumentSheet)
4024 {
4025 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetRemoved, (this, aSheet, aDocumentSheet));
4027 if (StyleSheetChangeEventsEnabled()) {
4028 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetChangeEvent,
4029 nsIDOMStyleSheetChangeEvent,
4030 InitStyleSheetChangeEvent,
4031 "StyleSheetRemoved",
4032 aDocumentSheet);
4033 }
4034 }
4036 void
4037 nsDocument::AddStyleSheet(nsIStyleSheet* aSheet)
4038 {
4039 NS_PRECONDITION(aSheet, "null arg");
4040 mStyleSheets.AppendObject(aSheet);
4041 aSheet->SetOwningDocument(this);
4043 if (aSheet->IsApplicable()) {
4044 AddStyleSheetToStyleSets(aSheet);
4045 }
4047 NotifyStyleSheetAdded(aSheet, true);
4048 }
4050 void
4051 nsDocument::RemoveStyleSheetFromStyleSets(nsIStyleSheet* aSheet)
4052 {
4053 nsCOMPtr<nsIPresShell> shell = GetShell();
4054 if (shell) {
4055 shell->StyleSet()->RemoveDocStyleSheet(aSheet);
4056 }
4057 }
4059 void
4060 nsDocument::RemoveStyleSheet(nsIStyleSheet* aSheet)
4061 {
4062 NS_PRECONDITION(aSheet, "null arg");
4063 nsCOMPtr<nsIStyleSheet> sheet = aSheet; // hold ref so it won't die too soon
4065 if (!mStyleSheets.RemoveObject(aSheet)) {
4066 NS_ASSERTION(mInUnlinkOrDeletion, "stylesheet not found");
4067 return;
4068 }
4070 if (!mIsGoingAway) {
4071 if (aSheet->IsApplicable()) {
4072 RemoveStyleSheetFromStyleSets(aSheet);
4073 }
4075 NotifyStyleSheetRemoved(aSheet, true);
4076 }
4078 aSheet->SetOwningDocument(nullptr);
4079 }
4081 void
4082 nsDocument::UpdateStyleSheets(nsCOMArray<nsIStyleSheet>& aOldSheets,
4083 nsCOMArray<nsIStyleSheet>& aNewSheets)
4084 {
4085 BeginUpdate(UPDATE_STYLE);
4087 // XXX Need to set the sheet on the ownernode, if any
4088 NS_PRECONDITION(aOldSheets.Count() == aNewSheets.Count(),
4089 "The lists must be the same length!");
4090 int32_t count = aOldSheets.Count();
4092 nsCOMPtr<nsIStyleSheet> oldSheet;
4093 int32_t i;
4094 for (i = 0; i < count; ++i) {
4095 oldSheet = aOldSheets[i];
4097 // First remove the old sheet.
4098 NS_ASSERTION(oldSheet, "None of the old sheets should be null");
4099 int32_t oldIndex = mStyleSheets.IndexOf(oldSheet);
4100 RemoveStyleSheet(oldSheet); // This does the right notifications
4102 // Now put the new one in its place. If it's null, just ignore it.
4103 nsIStyleSheet* newSheet = aNewSheets[i];
4104 if (newSheet) {
4105 mStyleSheets.InsertObjectAt(newSheet, oldIndex);
4106 newSheet->SetOwningDocument(this);
4107 if (newSheet->IsApplicable()) {
4108 AddStyleSheetToStyleSets(newSheet);
4109 }
4111 NotifyStyleSheetAdded(newSheet, true);
4112 }
4113 }
4115 EndUpdate(UPDATE_STYLE);
4116 }
4118 void
4119 nsDocument::InsertStyleSheetAt(nsIStyleSheet* aSheet, int32_t aIndex)
4120 {
4121 NS_PRECONDITION(aSheet, "null ptr");
4122 mStyleSheets.InsertObjectAt(aSheet, aIndex);
4124 aSheet->SetOwningDocument(this);
4126 if (aSheet->IsApplicable()) {
4127 AddStyleSheetToStyleSets(aSheet);
4128 }
4130 NotifyStyleSheetAdded(aSheet, true);
4131 }
4134 void
4135 nsDocument::SetStyleSheetApplicableState(nsIStyleSheet* aSheet,
4136 bool aApplicable)
4137 {
4138 NS_PRECONDITION(aSheet, "null arg");
4140 // If we're actually in the document style sheet list
4141 if (-1 != mStyleSheets.IndexOf(aSheet)) {
4142 if (aApplicable) {
4143 AddStyleSheetToStyleSets(aSheet);
4144 } else {
4145 RemoveStyleSheetFromStyleSets(aSheet);
4146 }
4147 }
4149 // We have to always notify, since this will be called for sheets
4150 // that are children of sheets in our style set, as well as some
4151 // sheets for nsHTMLEditor.
4153 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleSheetApplicableStateChanged,
4154 (this, aSheet, aApplicable));
4156 if (StyleSheetChangeEventsEnabled()) {
4157 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleSheetApplicableStateChangeEvent,
4158 nsIDOMStyleSheetApplicableStateChangeEvent,
4159 InitStyleSheetApplicableStateChangeEvent,
4160 "StyleSheetApplicableStateChanged",
4161 aApplicable);
4162 }
4164 if (!mSSApplicableStateNotificationPending) {
4165 nsRefPtr<nsIRunnable> notification = NS_NewRunnableMethod(this,
4166 &nsDocument::NotifyStyleSheetApplicableStateChanged);
4167 mSSApplicableStateNotificationPending =
4168 NS_SUCCEEDED(NS_DispatchToCurrentThread(notification));
4169 }
4170 }
4172 void
4173 nsDocument::NotifyStyleSheetApplicableStateChanged()
4174 {
4175 mSSApplicableStateNotificationPending = false;
4176 nsCOMPtr<nsIObserverService> observerService =
4177 mozilla::services::GetObserverService();
4178 if (observerService) {
4179 observerService->NotifyObservers(static_cast<nsIDocument*>(this),
4180 "style-sheet-applicable-state-changed",
4181 nullptr);
4182 }
4183 }
4185 // These three functions are a lot like the implementation of the
4186 // corresponding API for regular stylesheets.
4188 int32_t
4189 nsDocument::GetNumberOfCatalogStyleSheets() const
4190 {
4191 return mCatalogSheets.Count();
4192 }
4194 nsIStyleSheet*
4195 nsDocument::GetCatalogStyleSheetAt(int32_t aIndex) const
4196 {
4197 NS_ENSURE_TRUE(0 <= aIndex && aIndex < mCatalogSheets.Count(), nullptr);
4198 return mCatalogSheets[aIndex];
4199 }
4201 void
4202 nsDocument::AddCatalogStyleSheet(nsCSSStyleSheet* aSheet)
4203 {
4204 mCatalogSheets.AppendObject(aSheet);
4205 aSheet->SetOwningDocument(this);
4206 aSheet->SetOwningNode(this);
4208 if (aSheet->IsApplicable()) {
4209 // This is like |AddStyleSheetToStyleSets|, but for an agent sheet.
4210 nsCOMPtr<nsIPresShell> shell = GetShell();
4211 if (shell) {
4212 shell->StyleSet()->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
4213 }
4214 }
4216 NotifyStyleSheetAdded(aSheet, false);
4217 }
4219 void
4220 nsDocument::EnsureCatalogStyleSheet(const char *aStyleSheetURI)
4221 {
4222 mozilla::css::Loader* cssLoader = CSSLoader();
4223 if (cssLoader->GetEnabled()) {
4224 int32_t sheetCount = GetNumberOfCatalogStyleSheets();
4225 for (int32_t i = 0; i < sheetCount; i++) {
4226 nsIStyleSheet* sheet = GetCatalogStyleSheetAt(i);
4227 NS_ASSERTION(sheet, "unexpected null stylesheet in the document");
4228 if (sheet) {
4229 nsAutoCString uriStr;
4230 sheet->GetSheetURI()->GetSpec(uriStr);
4231 if (uriStr.Equals(aStyleSheetURI))
4232 return;
4233 }
4234 }
4236 nsCOMPtr<nsIURI> uri;
4237 NS_NewURI(getter_AddRefs(uri), aStyleSheetURI);
4238 if (uri) {
4239 nsRefPtr<nsCSSStyleSheet> sheet;
4240 cssLoader->LoadSheetSync(uri, true, true, getter_AddRefs(sheet));
4241 if (sheet) {
4242 BeginUpdate(UPDATE_STYLE);
4243 AddCatalogStyleSheet(sheet);
4244 EndUpdate(UPDATE_STYLE);
4245 }
4246 }
4247 }
4248 }
4250 static nsStyleSet::sheetType
4251 ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType)
4252 {
4253 switch(aType) {
4254 case nsIDocument::eAgentSheet:
4255 return nsStyleSet::eAgentSheet;
4256 case nsIDocument::eUserSheet:
4257 return nsStyleSet::eUserSheet;
4258 case nsIDocument::eAuthorSheet:
4259 return nsStyleSet::eDocSheet;
4260 default:
4261 NS_ASSERTION(false, "wrong type");
4262 // we must return something although this should never happen
4263 return nsStyleSet::eSheetTypeCount;
4264 }
4265 }
4267 static int32_t
4268 FindSheet(const nsCOMArray<nsIStyleSheet>& aSheets, nsIURI* aSheetURI)
4269 {
4270 for (int32_t i = aSheets.Count() - 1; i >= 0; i-- ) {
4271 bool bEqual;
4272 nsIURI* uri = aSheets[i]->GetSheetURI();
4274 if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
4275 return i;
4276 }
4278 return -1;
4279 }
4281 nsresult
4282 nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4283 {
4284 NS_PRECONDITION(aSheetURI, "null arg");
4286 // Checking if we have loaded this one already.
4287 if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
4288 return NS_ERROR_INVALID_ARG;
4290 // Loading the sheet sync.
4291 nsRefPtr<mozilla::css::Loader> loader = new mozilla::css::Loader();
4293 nsRefPtr<nsCSSStyleSheet> sheet;
4294 nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet,
4295 true, getter_AddRefs(sheet));
4296 NS_ENSURE_SUCCESS(rv, rv);
4298 mAdditionalSheets[aType].AppendObject(sheet);
4299 sheet->SetOwningDocument(this);
4300 MOZ_ASSERT(sheet->IsApplicable());
4302 BeginUpdate(UPDATE_STYLE);
4303 nsCOMPtr<nsIPresShell> shell = GetShell();
4304 if (shell) {
4305 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
4306 shell->StyleSet()->AppendStyleSheet(type, sheet);
4307 }
4309 // Passing false, so documet.styleSheets.length will not be affected by
4310 // these additional sheets.
4311 NotifyStyleSheetAdded(sheet, false);
4312 EndUpdate(UPDATE_STYLE);
4314 return NS_OK;
4315 }
4317 void
4318 nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI)
4319 {
4320 MOZ_ASSERT(aSheetURI);
4322 nsCOMArray<nsIStyleSheet>& sheets = mAdditionalSheets[aType];
4324 int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
4325 if (i >= 0) {
4326 nsCOMPtr<nsIStyleSheet> sheetRef = sheets[i];
4327 sheets.RemoveObjectAt(i);
4329 BeginUpdate(UPDATE_STYLE);
4330 if (!mIsGoingAway) {
4331 MOZ_ASSERT(sheetRef->IsApplicable());
4332 nsCOMPtr<nsIPresShell> shell = GetShell();
4333 if (shell) {
4334 nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType);
4335 shell->StyleSet()->RemoveStyleSheet(type, sheetRef);
4336 }
4337 }
4339 // Passing false, so documet.styleSheets.length will not be affected by
4340 // these additional sheets.
4341 NotifyStyleSheetRemoved(sheetRef, false);
4342 EndUpdate(UPDATE_STYLE);
4344 sheetRef->SetOwningDocument(nullptr);
4345 }
4346 }
4348 nsIStyleSheet*
4349 nsDocument::FirstAdditionalAuthorSheet()
4350 {
4351 return mAdditionalSheets[eAuthorSheet].SafeObjectAt(0);
4352 }
4354 nsIGlobalObject*
4355 nsDocument::GetScopeObject() const
4356 {
4357 nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
4358 return scope;
4359 }
4361 void
4362 nsDocument::SetScopeObject(nsIGlobalObject* aGlobal)
4363 {
4364 mScopeObject = do_GetWeakReference(aGlobal);
4365 if (aGlobal) {
4366 mHasHadScriptHandlingObject = true;
4367 }
4368 }
4370 static void
4371 NotifyActivityChanged(nsIContent *aContent, void *aUnused)
4372 {
4373 nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent));
4374 if (domMediaElem) {
4375 HTMLMediaElement* mediaElem = static_cast<HTMLMediaElement*>(aContent);
4376 mediaElem->NotifyOwnerDocumentActivityChanged();
4377 }
4378 nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aContent));
4379 if (objectLoadingContent) {
4380 nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
4381 olc->NotifyOwnerDocumentActivityChanged();
4382 }
4383 }
4385 void
4386 nsIDocument::SetContainer(nsDocShell* aContainer)
4387 {
4388 if (aContainer) {
4389 mDocumentContainer = aContainer->asWeakPtr();
4390 } else {
4391 mDocumentContainer = WeakPtr<nsDocShell>();
4392 }
4394 EnumerateFreezableElements(NotifyActivityChanged, nullptr);
4395 if (!aContainer) {
4396 return;
4397 }
4399 // Get the Docshell
4400 if (aContainer->ItemType() == nsIDocShellTreeItem::typeContent) {
4401 // check if same type root
4402 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
4403 aContainer->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
4404 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
4406 if (sameTypeRoot == aContainer) {
4407 static_cast<nsDocument*>(this)->SetIsTopLevelContentDocument(true);
4408 }
4409 }
4410 }
4412 nsISupports*
4413 nsIDocument::GetContainer() const
4414 {
4415 return static_cast<nsIDocShell*>(mDocumentContainer);
4416 }
4418 void
4419 nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
4420 {
4421 #ifdef DEBUG
4422 {
4423 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aScriptGlobalObject));
4425 NS_ASSERTION(!win || win->IsInnerWindow(),
4426 "Script global object must be an inner window!");
4427 }
4428 #endif
4429 NS_ABORT_IF_FALSE(aScriptGlobalObject || !mAnimationController ||
4430 mAnimationController->IsPausedByType(
4431 nsSMILTimeContainer::PAUSE_PAGEHIDE |
4432 nsSMILTimeContainer::PAUSE_BEGIN),
4433 "Clearing window pointer while animations are unpaused");
4435 if (mScriptGlobalObject && !aScriptGlobalObject) {
4436 // We're detaching from the window. We need to grab a pointer to
4437 // our layout history state now.
4438 mLayoutHistoryState = GetLayoutHistoryState();
4440 if (mPresShell && !EventHandlingSuppressed()) {
4441 RevokeAnimationFrameNotifications();
4442 }
4444 // Also make sure to remove our onload blocker now if we haven't done it yet
4445 if (mOnloadBlockCount != 0) {
4446 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
4447 if (loadGroup) {
4448 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
4449 }
4450 }
4451 }
4453 mScriptGlobalObject = aScriptGlobalObject;
4455 if (aScriptGlobalObject) {
4456 mHasHadScriptHandlingObject = true;
4457 mHasHadDefaultView = true;
4458 // Go back to using the docshell for the layout history state
4459 mLayoutHistoryState = nullptr;
4460 mScopeObject = do_GetWeakReference(aScriptGlobalObject);
4461 #ifdef DEBUG
4462 if (!mWillReparent) {
4463 // We really shouldn't have a wrapper here but if we do we need to make sure
4464 // it has the correct parent.
4465 JSObject *obj = GetWrapperPreserveColor();
4466 if (obj) {
4467 JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
4468 NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
4469 "Wrong scope, this is really bad!");
4470 }
4471 }
4472 #endif
4474 if (mAllowDNSPrefetch) {
4475 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
4476 if (docShell) {
4477 #ifdef DEBUG
4478 nsCOMPtr<nsIWebNavigation> webNav =
4479 do_GetInterface(aScriptGlobalObject);
4480 NS_ASSERTION(SameCOMIdentity(webNav, docShell),
4481 "Unexpected container or script global?");
4482 #endif
4483 bool allowDNSPrefetch;
4484 docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
4485 mAllowDNSPrefetch = allowDNSPrefetch;
4486 }
4487 }
4489 MaybeRescheduleAnimationFrameNotifications();
4490 mRegistry = new Registry();
4491 }
4493 // Remember the pointer to our window (or lack there of), to avoid
4494 // having to QI every time it's asked for.
4495 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobalObject);
4496 mWindow = window;
4498 // Now that we know what our window is, we can flush the CSP errors to the
4499 // Web Console. We are flushing all messages that occured and were stored
4500 // in the queue prior to this point.
4501 FlushCSPWebConsoleErrorQueue();
4502 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
4503 do_QueryInterface(GetChannel());
4504 if (internalChannel) {
4505 nsCOMArray<nsISecurityConsoleMessage> messages;
4506 internalChannel->TakeAllSecurityMessages(messages);
4507 SendToConsole(messages);
4508 }
4510 // Set our visibility state, but do not fire the event. This is correct
4511 // because either we're coming out of bfcache (in which case IsVisible() will
4512 // still test false at this point and no state change will happen) or we're
4513 // doing the initial document load and don't want to fire the event for this
4514 // change.
4515 mVisibilityState = GetVisibilityState();
4516 }
4518 nsIScriptGlobalObject*
4519 nsDocument::GetScriptHandlingObjectInternal() const
4520 {
4521 MOZ_ASSERT(!mScriptGlobalObject,
4522 "Do not call this when mScriptGlobalObject is set!");
4523 if (mHasHadDefaultView) {
4524 return nullptr;
4525 }
4527 nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
4528 do_QueryReferent(mScopeObject);
4529 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(scriptHandlingObject);
4530 if (win) {
4531 NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!");
4532 nsPIDOMWindow* outer = win->GetOuterWindow();
4533 if (!outer || outer->GetCurrentInnerWindow() != win) {
4534 NS_WARNING("Wrong inner/outer window combination!");
4535 return nullptr;
4536 }
4537 }
4538 return scriptHandlingObject;
4539 }
4540 void
4541 nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject)
4542 {
4543 NS_ASSERTION(!mScriptGlobalObject ||
4544 mScriptGlobalObject == aScriptObject,
4545 "Wrong script object!");
4546 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aScriptObject);
4547 NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!");
4548 if (aScriptObject) {
4549 mScopeObject = do_GetWeakReference(aScriptObject);
4550 mHasHadScriptHandlingObject = true;
4551 mHasHadDefaultView = false;
4552 }
4553 }
4555 bool
4556 nsDocument::IsTopLevelContentDocument()
4557 {
4558 return mIsTopLevelContentDocument;
4559 }
4561 void
4562 nsDocument::SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument)
4563 {
4564 mIsTopLevelContentDocument = aIsTopLevelContentDocument;
4565 }
4567 nsPIDOMWindow *
4568 nsDocument::GetWindowInternal() const
4569 {
4570 MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
4571 // Let's use mScriptGlobalObject. Even if the document is already removed from
4572 // the docshell, the outer window might be still obtainable from the it.
4573 nsCOMPtr<nsPIDOMWindow> win;
4574 if (mRemovedFromDocShell) {
4575 nsCOMPtr<nsIInterfaceRequestor> requestor(mDocumentContainer);
4576 if (requestor) {
4577 // The docshell returns the outer window we are done.
4578 win = do_GetInterface(requestor);
4579 }
4580 } else {
4581 win = do_QueryInterface(mScriptGlobalObject);
4582 if (win) {
4583 // mScriptGlobalObject is always the inner window, let's get the outer.
4584 win = win->GetOuterWindow();
4585 }
4586 }
4588 return win;
4589 }
4591 nsScriptLoader*
4592 nsDocument::ScriptLoader()
4593 {
4594 return mScriptLoader;
4595 }
4597 bool
4598 nsDocument::InternalAllowXULXBL()
4599 {
4600 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
4601 mAllowXULXBL = eTriTrue;
4602 return true;
4603 }
4605 mAllowXULXBL = eTriFalse;
4606 return false;
4607 }
4609 // Note: We don't hold a reference to the document observer; we assume
4610 // that it has a live reference to the document.
4611 void
4612 nsDocument::AddObserver(nsIDocumentObserver* aObserver)
4613 {
4614 NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
4615 "Observer already in the list");
4616 mObservers.AppendElement(aObserver);
4617 AddMutationObserver(aObserver);
4618 }
4620 bool
4621 nsDocument::RemoveObserver(nsIDocumentObserver* aObserver)
4622 {
4623 // If we're in the process of destroying the document (and we're
4624 // informing the observers of the destruction), don't remove the
4625 // observers from the list. This is not a big deal, since we
4626 // don't hold a live reference to the observers.
4627 if (!mInDestructor) {
4628 RemoveMutationObserver(aObserver);
4629 return mObservers.RemoveElement(aObserver);
4630 }
4632 return mObservers.Contains(aObserver);
4633 }
4635 void
4636 nsDocument::MaybeEndOutermostXBLUpdate()
4637 {
4638 // Only call BindingManager()->EndOutermostUpdate() when
4639 // we're not in an update and it is safe to run scripts.
4640 if (mUpdateNestLevel == 0 && mInXBLUpdate) {
4641 if (nsContentUtils::IsSafeToRunScript()) {
4642 mInXBLUpdate = false;
4643 BindingManager()->EndOutermostUpdate();
4644 } else if (!mInDestructor) {
4645 nsContentUtils::AddScriptRunner(
4646 NS_NewRunnableMethod(this, &nsDocument::MaybeEndOutermostXBLUpdate));
4647 }
4648 }
4649 }
4651 void
4652 nsDocument::BeginUpdate(nsUpdateType aUpdateType)
4653 {
4654 if (mUpdateNestLevel == 0 && !mInXBLUpdate) {
4655 mInXBLUpdate = true;
4656 BindingManager()->BeginOutermostUpdate();
4657 }
4659 ++mUpdateNestLevel;
4660 nsContentUtils::AddScriptBlocker();
4661 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this, aUpdateType));
4662 }
4664 void
4665 nsDocument::EndUpdate(nsUpdateType aUpdateType)
4666 {
4667 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this, aUpdateType));
4669 nsContentUtils::RemoveScriptBlocker();
4671 --mUpdateNestLevel;
4673 // This set of updates may have created XBL bindings. Let the
4674 // binding manager know we're done.
4675 MaybeEndOutermostXBLUpdate();
4677 MaybeInitializeFinalizeFrameLoaders();
4678 }
4680 void
4681 nsDocument::BeginLoad()
4682 {
4683 // Block onload here to prevent having to deal with blocking and
4684 // unblocking it while we know the document is loading.
4685 BlockOnload();
4686 mDidFireDOMContentLoaded = false;
4687 BlockDOMContentLoaded();
4689 if (mScriptLoader) {
4690 mScriptLoader->BeginDeferringScripts();
4691 }
4693 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
4694 }
4696 void
4697 nsDocument::ReportEmptyGetElementByIdArg()
4698 {
4699 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
4700 NS_LITERAL_CSTRING("DOM"), this,
4701 nsContentUtils::eDOM_PROPERTIES,
4702 "EmptyGetElementByIdParam");
4703 }
4705 Element*
4706 nsDocument::GetElementById(const nsAString& aElementId)
4707 {
4708 if (!CheckGetElementByIdArg(aElementId)) {
4709 return nullptr;
4710 }
4712 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
4713 return entry ? entry->GetIdElement() : nullptr;
4714 }
4716 const nsSmallVoidArray*
4717 nsDocument::GetAllElementsForId(const nsAString& aElementId) const
4718 {
4719 if (aElementId.IsEmpty()) {
4720 return nullptr;
4721 }
4723 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
4724 return entry ? entry->GetIdElements() : nullptr;
4725 }
4727 NS_IMETHODIMP
4728 nsDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn)
4729 {
4730 Element *content = GetElementById(aId);
4731 if (content) {
4732 return CallQueryInterface(content, aReturn);
4733 }
4735 *aReturn = nullptr;
4737 return NS_OK;
4738 }
4740 Element*
4741 nsDocument::AddIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
4742 void* aData, bool aForImage)
4743 {
4744 nsDependentAtomString id(aID);
4746 if (!CheckGetElementByIdArg(id))
4747 return nullptr;
4749 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(id);
4750 NS_ENSURE_TRUE(entry, nullptr);
4752 entry->AddContentChangeCallback(aObserver, aData, aForImage);
4753 return aForImage ? entry->GetImageIdElement() : entry->GetIdElement();
4754 }
4756 void
4757 nsDocument::RemoveIDTargetObserver(nsIAtom* aID, IDTargetObserver aObserver,
4758 void* aData, bool aForImage)
4759 {
4760 nsDependentAtomString id(aID);
4762 if (!CheckGetElementByIdArg(id))
4763 return;
4765 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(id);
4766 if (!entry) {
4767 return;
4768 }
4770 entry->RemoveContentChangeCallback(aObserver, aData, aForImage);
4771 }
4773 NS_IMETHODIMP
4774 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
4775 nsIDOMElement* aImageElement)
4776 {
4777 nsCOMPtr<Element> el = do_QueryInterface(aImageElement);
4778 MozSetImageElement(aImageElementId, el);
4779 return NS_OK;
4780 }
4782 void
4783 nsDocument::MozSetImageElement(const nsAString& aImageElementId,
4784 Element* aElement)
4785 {
4786 if (aImageElementId.IsEmpty())
4787 return;
4789 // Hold a script blocker while calling SetImageElement since that can call
4790 // out to id-observers
4791 nsAutoScriptBlocker scriptBlocker;
4793 nsIdentifierMapEntry *entry = mIdentifierMap.PutEntry(aImageElementId);
4794 if (entry) {
4795 entry->SetImageElement(aElement);
4796 if (entry->IsEmpty()) {
4797 mIdentifierMap.RemoveEntry(aImageElementId);
4798 }
4799 }
4800 }
4802 Element*
4803 nsDocument::LookupImageElement(const nsAString& aId)
4804 {
4805 if (aId.IsEmpty())
4806 return nullptr;
4808 nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
4809 return entry ? entry->GetImageIdElement() : nullptr;
4810 }
4812 void
4813 nsDocument::DispatchContentLoadedEvents()
4814 {
4815 // If you add early returns from this method, make sure you're
4816 // calling UnblockOnload properly.
4818 // Unpin references to preloaded images
4819 mPreloadingImages.Clear();
4821 if (mTiming) {
4822 mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
4823 }
4825 // Dispatch observer notification to notify observers document is interactive.
4826 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
4827 nsIPrincipal *principal = GetPrincipal();
4828 os->NotifyObservers(static_cast<nsIDocument*>(this),
4829 nsContentUtils::IsSystemPrincipal(principal) ?
4830 "chrome-document-interactive" :
4831 "content-document-interactive",
4832 nullptr);
4834 // Fire a DOM event notifying listeners that this document has been
4835 // loaded (excluding images and other loads initiated by this
4836 // document).
4837 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
4838 NS_LITERAL_STRING("DOMContentLoaded"),
4839 true, true);
4841 if (mTiming) {
4842 mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
4843 }
4845 // If this document is a [i]frame, fire a DOMFrameContentLoaded
4846 // event on all parent documents notifying that the HTML (excluding
4847 // other external files such as images and stylesheets) in a frame
4848 // has finished loading.
4850 // target_frame is the [i]frame element that will be used as the
4851 // target for the event. It's the [i]frame whose content is done
4852 // loading.
4853 nsCOMPtr<EventTarget> target_frame;
4855 if (mParentDocument) {
4856 target_frame = mParentDocument->FindContentForSubDocument(this);
4857 }
4859 if (target_frame) {
4860 nsCOMPtr<nsIDocument> parent = mParentDocument;
4861 do {
4862 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(parent);
4864 nsCOMPtr<nsIDOMEvent> event;
4865 if (domDoc) {
4866 domDoc->CreateEvent(NS_LITERAL_STRING("Events"),
4867 getter_AddRefs(event));
4869 }
4871 if (event) {
4872 event->InitEvent(NS_LITERAL_STRING("DOMFrameContentLoaded"), true,
4873 true);
4875 event->SetTarget(target_frame);
4876 event->SetTrusted(true);
4878 // To dispatch this event we must manually call
4879 // EventDispatcher::Dispatch() on the ancestor document since the
4880 // target is not in the same document, so the event would never reach
4881 // the ancestor document if we used the normal event
4882 // dispatching code.
4884 WidgetEvent* innerEvent = event->GetInternalNSEvent();
4885 if (innerEvent) {
4886 nsEventStatus status = nsEventStatus_eIgnore;
4888 nsIPresShell *shell = parent->GetShell();
4889 if (shell) {
4890 nsRefPtr<nsPresContext> context = shell->GetPresContext();
4892 if (context) {
4893 EventDispatcher::Dispatch(parent, context, innerEvent, event,
4894 &status);
4895 }
4896 }
4897 }
4898 }
4900 parent = parent->GetParentDocument();
4901 } while (parent);
4902 }
4904 // If the document has a manifest attribute, fire a MozApplicationManifest
4905 // event.
4906 Element* root = GetRootElement();
4907 if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
4908 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
4909 NS_LITERAL_STRING("MozApplicationManifest"),
4910 true, true);
4911 }
4913 UnblockOnload(true);
4914 }
4916 void
4917 nsDocument::EndLoad()
4918 {
4919 // Drop the ref to our parser, if any, but keep hold of the sink so that we
4920 // can flush it from FlushPendingNotifications as needed. We might have to
4921 // do that to get a StartLayout() to happen.
4922 if (mParser) {
4923 mWeakSink = do_GetWeakReference(mParser->GetContentSink());
4924 mParser = nullptr;
4925 }
4927 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
4929 UnblockDOMContentLoaded();
4930 }
4932 void
4933 nsDocument::UnblockDOMContentLoaded()
4934 {
4935 MOZ_ASSERT(mBlockDOMContentLoaded);
4936 if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
4937 return;
4938 }
4939 mDidFireDOMContentLoaded = true;
4941 MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
4942 if (!mSynchronousDOMContentLoaded) {
4943 nsRefPtr<nsIRunnable> ev =
4944 NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
4945 NS_DispatchToCurrentThread(ev);
4946 } else {
4947 DispatchContentLoadedEvents();
4948 }
4949 }
4951 void
4952 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
4953 {
4954 NS_PRECONDITION(!nsContentUtils::IsSafeToRunScript(),
4955 "Someone forgot a scriptblocker");
4956 NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
4957 (this, aContent, aStateMask));
4958 }
4960 void
4961 nsDocument::DocumentStatesChanged(EventStates aStateMask)
4962 {
4963 // Invalidate our cached state.
4964 mGotDocumentState &= ~aStateMask;
4965 mDocumentState &= ~aStateMask;
4967 NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
4968 }
4970 void
4971 nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet,
4972 nsIStyleRule* aOldStyleRule,
4973 nsIStyleRule* aNewStyleRule)
4974 {
4975 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged,
4976 (this, aSheet,
4977 aOldStyleRule, aNewStyleRule));
4979 if (StyleSheetChangeEventsEnabled()) {
4980 nsCOMPtr<css::Rule> rule = do_QueryInterface(aNewStyleRule);
4981 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent,
4982 nsIDOMStyleRuleChangeEvent,
4983 InitStyleRuleChangeEvent,
4984 "StyleRuleChanged",
4985 rule ? rule->GetDOMRule() : nullptr);
4986 }
4987 }
4989 void
4990 nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet,
4991 nsIStyleRule* aStyleRule)
4992 {
4993 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded,
4994 (this, aSheet, aStyleRule));
4996 if (StyleSheetChangeEventsEnabled()) {
4997 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
4998 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent,
4999 nsIDOMStyleRuleChangeEvent,
5000 InitStyleRuleChangeEvent,
5001 "StyleRuleAdded",
5002 rule ? rule->GetDOMRule() : nullptr);
5003 }
5004 }
5006 void
5007 nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet,
5008 nsIStyleRule* aStyleRule)
5009 {
5010 NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved,
5011 (this, aSheet, aStyleRule));
5013 if (StyleSheetChangeEventsEnabled()) {
5014 nsCOMPtr<css::Rule> rule = do_QueryInterface(aStyleRule);
5015 DO_STYLESHEET_NOTIFICATION(NS_NewDOMStyleRuleChangeEvent,
5016 nsIDOMStyleRuleChangeEvent,
5017 InitStyleRuleChangeEvent,
5018 "StyleRuleRemoved",
5019 rule ? rule->GetDOMRule() : nullptr);
5020 }
5021 }
5023 #undef DO_STYLESHEET_NOTIFICATION
5026 //
5027 // nsIDOMDocument interface
5028 //
5029 DocumentType*
5030 nsIDocument::GetDoctype() const
5031 {
5032 for (nsIContent* child = GetFirstChild();
5033 child;
5034 child = child->GetNextSibling()) {
5035 if (child->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
5036 return static_cast<DocumentType*>(child);
5037 }
5038 }
5039 return nullptr;
5040 }
5042 NS_IMETHODIMP
5043 nsDocument::GetDoctype(nsIDOMDocumentType** aDoctype)
5044 {
5045 MOZ_ASSERT(aDoctype);
5046 nsCOMPtr<nsIDOMDocumentType> doctype = nsIDocument::GetDoctype();
5047 doctype.forget(aDoctype);
5048 return NS_OK;
5049 }
5051 NS_IMETHODIMP
5052 nsDocument::GetImplementation(nsIDOMDOMImplementation** aImplementation)
5053 {
5054 ErrorResult rv;
5055 *aImplementation = GetImplementation(rv);
5056 if (rv.Failed()) {
5057 MOZ_ASSERT(!*aImplementation);
5058 return rv.ErrorCode();
5059 }
5060 NS_ADDREF(*aImplementation);
5061 return NS_OK;
5062 }
5064 DOMImplementation*
5065 nsDocument::GetImplementation(ErrorResult& rv)
5066 {
5067 if (!mDOMImplementation) {
5068 nsCOMPtr<nsIURI> uri;
5069 NS_NewURI(getter_AddRefs(uri), "about:blank");
5070 if (!uri) {
5071 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5072 return nullptr;
5073 }
5074 bool hasHadScriptObject = true;
5075 nsIScriptGlobalObject* scriptObject =
5076 GetScriptHandlingObject(hasHadScriptObject);
5077 if (!scriptObject && hasHadScriptObject) {
5078 rv.Throw(NS_ERROR_UNEXPECTED);
5079 return nullptr;
5080 }
5081 mDOMImplementation = new DOMImplementation(this,
5082 scriptObject ? scriptObject : GetScopeObject(), uri, uri);
5083 }
5085 return mDOMImplementation;
5086 }
5088 NS_IMETHODIMP
5089 nsDocument::GetDocumentElement(nsIDOMElement** aDocumentElement)
5090 {
5091 NS_ENSURE_ARG_POINTER(aDocumentElement);
5093 Element* root = GetRootElement();
5094 if (root) {
5095 return CallQueryInterface(root, aDocumentElement);
5096 }
5098 *aDocumentElement = nullptr;
5100 return NS_OK;
5101 }
5103 NS_IMETHODIMP
5104 nsDocument::CreateElement(const nsAString& aTagName,
5105 nsIDOMElement** aReturn)
5106 {
5107 *aReturn = nullptr;
5108 ErrorResult rv;
5109 nsCOMPtr<Element> element = nsIDocument::CreateElement(aTagName, rv);
5110 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
5111 return CallQueryInterface(element, aReturn);
5112 }
5114 bool IsLowercaseASCII(const nsAString& aValue)
5115 {
5116 int32_t len = aValue.Length();
5117 for (int32_t i = 0; i < len; ++i) {
5118 char16_t c = aValue[i];
5119 if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
5120 return false;
5121 }
5122 }
5123 return true;
5124 }
5126 already_AddRefed<Element>
5127 nsIDocument::CreateElement(const nsAString& aTagName, ErrorResult& rv)
5128 {
5129 rv = nsContentUtils::CheckQName(aTagName, false);
5130 if (rv.Failed()) {
5131 return nullptr;
5132 }
5134 bool needsLowercase = IsHTML() && !IsLowercaseASCII(aTagName);
5135 nsAutoString lcTagName;
5136 if (needsLowercase) {
5137 nsContentUtils::ASCIIToLower(aTagName, lcTagName);
5138 }
5140 nsCOMPtr<nsIContent> content;
5141 rv = CreateElem(needsLowercase ? lcTagName : aTagName,
5142 nullptr, mDefaultElementType, getter_AddRefs(content));
5143 if (rv.Failed()) {
5144 return nullptr;
5145 }
5146 return dont_AddRef(content.forget().take()->AsElement());
5147 }
5149 void
5150 nsDocument::SwizzleCustomElement(Element* aElement,
5151 const nsAString& aTypeExtension,
5152 uint32_t aNamespaceID,
5153 ErrorResult& rv)
5154 {
5155 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(aTypeExtension));
5156 nsCOMPtr<nsIAtom> tagAtom = aElement->Tag();
5157 if (!mRegistry || tagAtom == typeAtom) {
5158 return;
5159 }
5161 CustomElementDefinition* data;
5162 CustomElementHashKey key(aNamespaceID, typeAtom);
5163 if (!mRegistry->mCustomDefinitions.Get(&key, &data)) {
5164 // The type extension doesn't exist in the registry,
5165 // thus we don't need to swizzle, but it is possibly
5166 // an upgrade candidate.
5167 RegisterUnresolvedElement(aElement, typeAtom);
5168 return;
5169 }
5171 if (data->mLocalName != tagAtom) {
5172 // The element doesn't match the local name for the
5173 // definition, thus the element isn't a custom element
5174 // and we don't need to do anything more.
5175 return;
5176 }
5178 if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) {
5179 // Swizzling in the parser happens after the "is" attribute is added.
5180 aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::is, aTypeExtension, true);
5181 }
5183 // Enqueuing the created callback will set the CustomElementData on the
5184 // element, causing prototype swizzling to occur in Element::WrapObject.
5185 EnqueueLifecycleCallback(nsIDocument::eCreated, aElement, nullptr, data);
5186 }
5188 already_AddRefed<Element>
5189 nsDocument::CreateElement(const nsAString& aTagName,
5190 const nsAString& aTypeExtension,
5191 ErrorResult& rv)
5192 {
5193 nsRefPtr<Element> elem = nsIDocument::CreateElement(aTagName, rv);
5194 if (rv.Failed()) {
5195 return nullptr;
5196 }
5198 SwizzleCustomElement(elem, aTypeExtension,
5199 GetDefaultNamespaceID(), rv);
5200 if (rv.Failed()) {
5201 return nullptr;
5202 }
5204 return elem.forget();
5205 }
5207 NS_IMETHODIMP
5208 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5209 const nsAString& aQualifiedName,
5210 nsIDOMElement** aReturn)
5211 {
5212 *aReturn = nullptr;
5213 ErrorResult rv;
5214 nsCOMPtr<Element> element =
5215 nsIDocument::CreateElementNS(aNamespaceURI, aQualifiedName, rv);
5216 NS_ENSURE_FALSE(rv.Failed(), rv.ErrorCode());
5217 return CallQueryInterface(element, aReturn);
5218 }
5220 already_AddRefed<Element>
5221 nsIDocument::CreateElementNS(const nsAString& aNamespaceURI,
5222 const nsAString& aQualifiedName,
5223 ErrorResult& rv)
5224 {
5225 nsCOMPtr<nsINodeInfo> nodeInfo;
5226 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5227 aQualifiedName,
5228 mNodeInfoManager,
5229 nsIDOMNode::ELEMENT_NODE,
5230 getter_AddRefs(nodeInfo));
5231 if (rv.Failed()) {
5232 return nullptr;
5233 }
5235 nsCOMPtr<Element> element;
5236 rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
5237 NOT_FROM_PARSER);
5238 if (rv.Failed()) {
5239 return nullptr;
5240 }
5241 return element.forget();
5242 }
5244 already_AddRefed<Element>
5245 nsDocument::CreateElementNS(const nsAString& aNamespaceURI,
5246 const nsAString& aQualifiedName,
5247 const nsAString& aTypeExtension,
5248 ErrorResult& rv)
5249 {
5250 nsRefPtr<Element> elem = nsIDocument::CreateElementNS(aNamespaceURI,
5251 aQualifiedName,
5252 rv);
5253 if (rv.Failed()) {
5254 return nullptr;
5255 }
5257 int32_t nameSpaceId = kNameSpaceID_Wildcard;
5258 if (!aNamespaceURI.EqualsLiteral("*")) {
5259 rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
5260 nameSpaceId);
5261 if (rv.Failed()) {
5262 return nullptr;
5263 }
5264 }
5266 SwizzleCustomElement(elem, aTypeExtension, nameSpaceId, rv);
5267 if (rv.Failed()) {
5268 return nullptr;
5269 }
5271 return elem.forget();
5272 }
5274 NS_IMETHODIMP
5275 nsDocument::CreateTextNode(const nsAString& aData, nsIDOMText** aReturn)
5276 {
5277 *aReturn = nsIDocument::CreateTextNode(aData).take();
5278 return NS_OK;
5279 }
5281 already_AddRefed<nsTextNode>
5282 nsIDocument::CreateTextNode(const nsAString& aData) const
5283 {
5284 nsRefPtr<nsTextNode> text = new nsTextNode(mNodeInfoManager);
5285 // Don't notify; this node is still being created.
5286 text->SetText(aData, false);
5287 return text.forget();
5288 }
5290 NS_IMETHODIMP
5291 nsDocument::CreateDocumentFragment(nsIDOMDocumentFragment** aReturn)
5292 {
5293 *aReturn = nsIDocument::CreateDocumentFragment().take();
5294 return NS_OK;
5295 }
5297 already_AddRefed<DocumentFragment>
5298 nsIDocument::CreateDocumentFragment() const
5299 {
5300 nsRefPtr<DocumentFragment> frag = new DocumentFragment(mNodeInfoManager);
5301 return frag.forget();
5302 }
5304 NS_IMETHODIMP
5305 nsDocument::CreateComment(const nsAString& aData, nsIDOMComment** aReturn)
5306 {
5307 *aReturn = nsIDocument::CreateComment(aData).take();
5308 return NS_OK;
5309 }
5311 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
5312 already_AddRefed<dom::Comment>
5313 nsIDocument::CreateComment(const nsAString& aData) const
5314 {
5315 nsRefPtr<dom::Comment> comment = new dom::Comment(mNodeInfoManager);
5317 // Don't notify; this node is still being created.
5318 comment->SetText(aData, false);
5319 return comment.forget();
5320 }
5322 NS_IMETHODIMP
5323 nsDocument::CreateCDATASection(const nsAString& aData,
5324 nsIDOMCDATASection** aReturn)
5325 {
5326 NS_ENSURE_ARG_POINTER(aReturn);
5327 ErrorResult rv;
5328 *aReturn = nsIDocument::CreateCDATASection(aData, rv).take();
5329 return rv.ErrorCode();
5330 }
5332 already_AddRefed<CDATASection>
5333 nsIDocument::CreateCDATASection(const nsAString& aData,
5334 ErrorResult& rv)
5335 {
5336 if (IsHTML()) {
5337 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5338 return nullptr;
5339 }
5341 if (FindInReadable(NS_LITERAL_STRING("]]>"), aData)) {
5342 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5343 return nullptr;
5344 }
5346 nsRefPtr<CDATASection> cdata = new CDATASection(mNodeInfoManager);
5348 // Don't notify; this node is still being created.
5349 cdata->SetText(aData, false);
5351 return cdata.forget();
5352 }
5354 NS_IMETHODIMP
5355 nsDocument::CreateProcessingInstruction(const nsAString& aTarget,
5356 const nsAString& aData,
5357 nsIDOMProcessingInstruction** aReturn)
5358 {
5359 ErrorResult rv;
5360 *aReturn =
5361 nsIDocument::CreateProcessingInstruction(aTarget, aData, rv).take();
5362 return rv.ErrorCode();
5363 }
5365 already_AddRefed<ProcessingInstruction>
5366 nsIDocument::CreateProcessingInstruction(const nsAString& aTarget,
5367 const nsAString& aData,
5368 ErrorResult& rv) const
5369 {
5370 nsresult res = nsContentUtils::CheckQName(aTarget, false);
5371 if (NS_FAILED(res)) {
5372 rv.Throw(res);
5373 return nullptr;
5374 }
5376 if (FindInReadable(NS_LITERAL_STRING("?>"), aData)) {
5377 rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
5378 return nullptr;
5379 }
5381 nsRefPtr<ProcessingInstruction> pi =
5382 NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
5384 return pi.forget();
5385 }
5387 NS_IMETHODIMP
5388 nsDocument::CreateAttribute(const nsAString& aName,
5389 nsIDOMAttr** aReturn)
5390 {
5391 ErrorResult rv;
5392 *aReturn = nsIDocument::CreateAttribute(aName, rv).take();
5393 return rv.ErrorCode();
5394 }
5396 already_AddRefed<Attr>
5397 nsIDocument::CreateAttribute(const nsAString& aName, ErrorResult& rv)
5398 {
5399 WarnOnceAbout(eCreateAttribute);
5401 if (!mNodeInfoManager) {
5402 rv.Throw(NS_ERROR_NOT_INITIALIZED);
5403 return nullptr;
5404 }
5406 nsresult res = nsContentUtils::CheckQName(aName, false);
5407 if (NS_FAILED(res)) {
5408 rv.Throw(res);
5409 return nullptr;
5410 }
5412 nsCOMPtr<nsINodeInfo> nodeInfo;
5413 res = mNodeInfoManager->GetNodeInfo(aName, nullptr, kNameSpaceID_None,
5414 nsIDOMNode::ATTRIBUTE_NODE,
5415 getter_AddRefs(nodeInfo));
5416 if (NS_FAILED(res)) {
5417 rv.Throw(res);
5418 return nullptr;
5419 }
5421 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5422 EmptyString(), false);
5423 return attribute.forget();
5424 }
5426 NS_IMETHODIMP
5427 nsDocument::CreateAttributeNS(const nsAString & aNamespaceURI,
5428 const nsAString & aQualifiedName,
5429 nsIDOMAttr **aResult)
5430 {
5431 ErrorResult rv;
5432 *aResult =
5433 nsIDocument::CreateAttributeNS(aNamespaceURI, aQualifiedName, rv).take();
5434 return rv.ErrorCode();
5435 }
5437 already_AddRefed<Attr>
5438 nsIDocument::CreateAttributeNS(const nsAString& aNamespaceURI,
5439 const nsAString& aQualifiedName,
5440 ErrorResult& rv)
5441 {
5442 WarnOnceAbout(eCreateAttributeNS);
5444 nsCOMPtr<nsINodeInfo> nodeInfo;
5445 rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI,
5446 aQualifiedName,
5447 mNodeInfoManager,
5448 nsIDOMNode::ATTRIBUTE_NODE,
5449 getter_AddRefs(nodeInfo));
5450 if (rv.Failed()) {
5451 return nullptr;
5452 }
5454 nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
5455 EmptyString(), true);
5456 return attribute.forget();
5457 }
5459 bool
5460 nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
5461 {
5462 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
5464 JS::Rooted<JSObject*> global(aCx,
5465 JS_GetGlobalForObject(aCx, &args.callee()));
5466 nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
5467 MOZ_ASSERT(window, "Should have a non-null window");
5469 nsDocument* document = static_cast<nsDocument*>(window->GetDoc());
5471 // Function name is the type of the custom element.
5472 JSString* jsFunName =
5473 JS_GetFunctionId(JS_ValueToFunction(aCx, args.calleev()));
5474 nsDependentJSString elemName;
5475 if (!elemName.init(aCx, jsFunName)) {
5476 return true;
5477 }
5479 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(elemName));
5480 CustomElementHashKey key(kNameSpaceID_Unknown, typeAtom);
5481 CustomElementDefinition* definition;
5482 if (!document->mRegistry ||
5483 !document->mRegistry->mCustomDefinitions.Get(&key, &definition)) {
5484 return true;
5485 }
5487 nsDependentAtomString localName(definition->mLocalName);
5489 nsCOMPtr<nsIContent> newElement;
5490 nsresult rv = document->CreateElem(localName, nullptr,
5491 definition->mNamespaceID,
5492 getter_AddRefs(newElement));
5493 NS_ENSURE_SUCCESS(rv, true);
5495 ErrorResult errorResult;
5496 nsCOMPtr<Element> element = do_QueryInterface(newElement);
5497 document->SwizzleCustomElement(element, elemName, definition->mNamespaceID,
5498 errorResult);
5499 if (errorResult.Failed()) {
5500 return true;
5501 }
5503 rv = nsContentUtils::WrapNative(aCx, newElement, newElement, args.rval());
5504 NS_ENSURE_SUCCESS(rv, true);
5506 return true;
5507 }
5509 bool
5510 nsDocument::IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject)
5511 {
5512 JS::Rooted<JSObject*> obj(aCx, aObject);
5513 return Preferences::GetBool("dom.webcomponents.enabled") ||
5514 IsInCertifiedApp(aCx, obj);
5515 }
5517 nsresult
5518 nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
5519 {
5520 if (!mRegistry) {
5521 return NS_OK;
5522 }
5524 nsINodeInfo* info = aElement->NodeInfo();
5526 // Candidate may be a custom element through extension,
5527 // in which case the custom element type name will not
5528 // match the element tag name. e.g. <button is="x-button">.
5529 nsCOMPtr<nsIAtom> typeName = aTypeName;
5530 if (!typeName) {
5531 typeName = info->NameAtom();
5532 }
5534 CustomElementHashKey key(info->NamespaceID(), typeName);
5535 if (mRegistry->mCustomDefinitions.Get(&key)) {
5536 return NS_OK;
5537 }
5539 nsTArray<nsRefPtr<Element>>* unresolved;
5540 mRegistry->mCandidatesMap.Get(&key, &unresolved);
5541 if (!unresolved) {
5542 unresolved = new nsTArray<nsRefPtr<Element>>();
5543 // Ownership of unresolved is taken by mCandidatesMap.
5544 mRegistry->mCandidatesMap.Put(&key, unresolved);
5545 }
5547 nsRefPtr<Element>* elem = unresolved->AppendElement();
5548 *elem = aElement;
5550 return NS_OK;
5551 }
5553 namespace {
5555 class ProcessStackRunner MOZ_FINAL : public nsIRunnable
5556 {
5557 public:
5558 ProcessStackRunner(bool aIsBaseQueue = false)
5559 : mIsBaseQueue(aIsBaseQueue)
5560 {
5561 }
5562 NS_DECL_ISUPPORTS
5563 NS_IMETHOD Run() MOZ_OVERRIDE
5564 {
5565 nsDocument::ProcessTopElementQueue(mIsBaseQueue);
5566 return NS_OK;
5567 }
5568 bool mIsBaseQueue;
5569 };
5571 NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable);
5573 } // anonymous namespace
5575 void
5576 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
5577 Element* aCustomElement,
5578 LifecycleCallbackArgs* aArgs,
5579 CustomElementDefinition* aDefinition)
5580 {
5581 if (!mRegistry) {
5582 // The element might not belong to a document that
5583 // has a browsing context, and thus no registry.
5584 return;
5585 }
5587 CustomElementData* elementData = aCustomElement->GetCustomElementData();
5589 // Let DEFINITION be ELEMENT's definition
5590 CustomElementDefinition* definition = aDefinition;
5591 if (!definition) {
5592 nsINodeInfo* info = aCustomElement->NodeInfo();
5594 // Make sure we get the correct definition in case the element
5595 // is a extended custom element e.g. <button is="x-button">.
5596 nsCOMPtr<nsIAtom> typeAtom = elementData ?
5597 elementData->mType.get() : info->NameAtom();
5599 CustomElementHashKey key(info->NamespaceID(), typeAtom);
5600 if (!mRegistry->mCustomDefinitions.Get(&key, &definition) ||
5601 definition->mLocalName != info->NameAtom()) {
5602 // Trying to enqueue a callback for an element that is not
5603 // a custom element. We are done, nothing to do.
5604 return;
5605 }
5606 }
5608 if (!elementData) {
5609 // Create the custom element data the first time
5610 // that we try to enqueue a callback.
5611 elementData = new CustomElementData(definition->mType);
5612 // aCustomElement takes ownership of elementData
5613 aCustomElement->SetCustomElementData(elementData);
5614 MOZ_ASSERT(aType == nsIDocument::eCreated,
5615 "First callback should be the created callback");
5616 }
5618 // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
5619 CallbackFunction* func = nullptr;
5620 switch (aType) {
5621 case nsIDocument::eCreated:
5622 if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
5623 func = definition->mCallbacks->mCreatedCallback.Value();
5624 }
5625 break;
5627 case nsIDocument::eAttached:
5628 if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
5629 func = definition->mCallbacks->mAttachedCallback.Value();
5630 }
5631 break;
5633 case nsIDocument::eDetached:
5634 if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
5635 func = definition->mCallbacks->mDetachedCallback.Value();
5636 }
5637 break;
5639 case nsIDocument::eAttributeChanged:
5640 if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
5641 func = definition->mCallbacks->mAttributeChangedCallback.Value();
5642 }
5643 break;
5644 }
5646 // If there is no such callback, stop.
5647 if (!func) {
5648 return;
5649 }
5651 if (aType == nsIDocument::eCreated) {
5652 elementData->mCreatedCallbackInvoked = false;
5653 } else if (!elementData->mCreatedCallbackInvoked) {
5654 // Callbacks other than created callback must not be enqueued
5655 // until after the created callback has been invoked.
5656 return;
5657 }
5659 // Add CALLBACK to ELEMENT's callback queue.
5660 CustomElementCallback* callback = new CustomElementCallback(aCustomElement,
5661 aType,
5662 func,
5663 elementData);
5664 // Ownership of callback is taken by mCallbackQueue.
5665 elementData->mCallbackQueue.AppendElement(callback);
5666 if (aArgs) {
5667 callback->SetArgs(*aArgs);
5668 }
5670 if (!elementData->mElementIsBeingCreated) {
5671 CustomElementData* lastData =
5672 sProcessingStack.ref().SafeLastElement(nullptr);
5674 // A new element queue needs to be pushed if the queue at the
5675 // top of the stack is associated with another microtask level.
5676 // Don't push a queue for the level 0 microtask (base element queue)
5677 // because we don't want to process the queue until the
5678 // microtask checkpoint.
5679 bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 &&
5680 (!lastData || lastData->mAssociatedMicroTask <
5681 static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
5683 // Push a new element queue onto the processing stack when appropriate
5684 // (when we enter a new microtask).
5685 if (shouldPushElementQueue) {
5686 // Push a sentinel value on the processing stack to mark the
5687 // boundary between the element queues.
5688 sProcessingStack.ref().AppendElement((CustomElementData*) nullptr);
5689 }
5691 sProcessingStack.ref().AppendElement(elementData);
5692 elementData->mAssociatedMicroTask =
5693 static_cast<int32_t>(nsContentUtils::MicroTaskLevel());
5695 // Add a script runner to pop and process the element queue at
5696 // the top of the processing stack.
5697 if (shouldPushElementQueue) {
5698 // Lifecycle callbacks enqueued by user agent implementation
5699 // should be invoked prior to returning control back to script.
5700 // Create a script runner to process the top of the processing
5701 // stack as soon as it is safe to run script.
5702 nsContentUtils::AddScriptRunner(new ProcessStackRunner());
5703 }
5704 }
5705 }
5707 // static
5708 void
5709 nsDocument::ProcessBaseElementQueue()
5710 {
5711 // Prevent re-entrance. Also, if a microtask checkpoint is reached
5712 // and there is no processing stack to process, then we are done.
5713 if (sProcessingBaseElementQueue || sProcessingStack.empty()) {
5714 return;
5715 }
5717 MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0);
5718 sProcessingBaseElementQueue = true;
5719 nsContentUtils::AddScriptRunner(new ProcessStackRunner(true));
5720 }
5722 // static
5723 void
5724 nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
5725 {
5726 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
5728 nsTArray<CustomElementData*>& stack = sProcessingStack.ref();
5729 uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
5731 if (aIsBaseQueue && firstQueue != 0) {
5732 return;
5733 }
5735 for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
5736 // Callback queue may have already been processed in an earlier
5737 // element queue or in an element queue that was popped
5738 // off more recently.
5739 if (stack[i]->mAssociatedMicroTask != -1) {
5740 stack[i]->RunCallbackQueue();
5741 stack[i]->mAssociatedMicroTask = -1;
5742 }
5743 }
5745 // If this was actually the base element queue, don't bother trying to pop
5746 // the first "queue" marker (sentinel).
5747 if (firstQueue != 0) {
5748 stack.SetLength(firstQueue);
5749 } else {
5750 // Don't pop sentinel for base element queue.
5751 stack.SetLength(1);
5752 sProcessingBaseElementQueue = false;
5753 }
5754 }
5756 bool
5757 nsDocument::RegisterEnabled()
5758 {
5759 static bool sPrefValue =
5760 Preferences::GetBool("dom.webcomponents.enabled", false);
5761 return sPrefValue;
5762 }
5764 // static
5765 Maybe<nsTArray<mozilla::dom::CustomElementData*>>
5766 nsDocument::sProcessingStack;
5768 // static
5769 bool
5770 nsDocument::sProcessingBaseElementQueue;
5772 void
5773 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
5774 const ElementRegistrationOptions& aOptions,
5775 JS::MutableHandle<JSObject*> aRetval,
5776 ErrorResult& rv)
5777 {
5778 if (!mRegistry) {
5779 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5780 return;
5781 }
5783 Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
5785 // Unconditionally convert TYPE to lowercase.
5786 nsAutoString lcType;
5787 nsContentUtils::ASCIIToLower(aType, lcType);
5789 // Only convert NAME to lowercase in HTML documents. Note that NAME is
5790 // options.extends.
5791 nsAutoString lcName;
5792 if (IsHTML()) {
5793 nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
5794 } else {
5795 lcName.Assign(aOptions.mExtends);
5796 }
5798 nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
5799 if (!nsContentUtils::IsCustomElementName(typeAtom)) {
5800 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5801 return;
5802 }
5804 // If there already exists a definition with the same TYPE, set ERROR to
5805 // DuplicateDefinition and stop.
5806 // Note that we need to find existing custom elements from either namespace.
5807 CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom);
5808 if (definitions.Get(&duplicateFinder)) {
5809 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5810 return;
5811 }
5813 nsIGlobalObject* sgo = GetScopeObject();
5814 if (!sgo) {
5815 rv.Throw(NS_ERROR_UNEXPECTED);
5816 return;
5817 }
5818 JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
5820 JSAutoCompartment ac(aCx, global);
5822 JS::Handle<JSObject*> htmlProto(
5823 HTMLElementBinding::GetProtoObject(aCx, global));
5824 if (!htmlProto) {
5825 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5826 return;
5827 }
5829 int32_t namespaceID = kNameSpaceID_XHTML;
5830 JS::Rooted<JSObject*> protoObject(aCx);
5831 if (!aOptions.mPrototype) {
5832 protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr());
5833 if (!protoObject) {
5834 rv.Throw(NS_ERROR_UNEXPECTED);
5835 return;
5836 }
5837 } else {
5838 // If a prototype is provided, we must check to ensure that it is from the
5839 // same browsing context as us.
5840 protoObject = aOptions.mPrototype;
5841 if (JS_GetGlobalForObject(aCx, protoObject) != global) {
5842 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5843 return;
5844 }
5846 // If PROTOTYPE is already an interface prototype object for any interface
5847 // object or PROTOTYPE has a non-configurable property named constructor,
5848 // throw a NotSupportedError and stop.
5849 const js::Class* clasp = js::GetObjectClass(protoObject);
5850 if (IsDOMIfaceAndProtoClass(clasp)) {
5851 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5852 return;
5853 }
5855 JS::Rooted<JSPropertyDescriptor> descRoot(aCx);
5856 JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot);
5857 if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
5858 rv.Throw(NS_ERROR_UNEXPECTED);
5859 return;
5860 }
5862 // Check if non-configurable
5863 if (desc.isPermanent()) {
5864 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
5865 return;
5866 }
5868 JS::Handle<JSObject*> svgProto(
5869 SVGElementBinding::GetProtoObject(aCx, global));
5870 if (!svgProto) {
5871 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5872 return;
5873 }
5875 JS::Rooted<JSObject*> protoProto(aCx, protoObject);
5877 // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
5878 // Namespace.
5879 while (protoProto) {
5880 if (protoProto == htmlProto) {
5881 break;
5882 }
5884 if (protoProto == svgProto) {
5885 namespaceID = kNameSpaceID_SVG;
5886 break;
5887 }
5889 if (!JS_GetPrototype(aCx, protoProto, &protoProto)) {
5890 rv.Throw(NS_ERROR_UNEXPECTED);
5891 return;
5892 }
5893 }
5894 }
5896 // If name was provided and not null...
5897 nsCOMPtr<nsIAtom> nameAtom;
5898 if (!lcName.IsEmpty()) {
5899 // Let BASE be the element interface for NAME and NAMESPACE.
5900 bool known = false;
5901 nameAtom = do_GetAtom(lcName);
5902 if (namespaceID == kNameSpaceID_XHTML) {
5903 nsIParserService* ps = nsContentUtils::GetParserService();
5904 if (!ps) {
5905 rv.Throw(NS_ERROR_UNEXPECTED);
5906 return;
5907 }
5909 known =
5910 ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined;
5911 } else {
5912 known = SVGElementFactory::Exists(nameAtom);
5913 }
5915 // If BASE does not exist or is an interface for a custom element, set ERROR
5916 // to InvalidName and stop.
5917 // If BASE exists, then it cannot be an interface for a custom element.
5918 if (!known) {
5919 rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
5920 return;
5921 }
5922 } else {
5923 // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
5924 if (namespaceID == kNameSpaceID_SVG) {
5925 rv.Throw(NS_ERROR_UNEXPECTED);
5926 return;
5927 }
5929 nameAtom = typeAtom;
5930 }
5932 nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
5933 JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject));
5934 if (!callbacksHolder->Init(aCx, rootedv)) {
5935 rv.Throw(NS_ERROR_FAILURE);
5936 return;
5937 }
5939 // Associate the definition with the custom element.
5940 CustomElementHashKey key(namespaceID, typeAtom);
5941 LifecycleCallbacks* callbacks = callbacksHolder.forget();
5942 CustomElementDefinition* definition =
5943 new CustomElementDefinition(protoObject,
5944 typeAtom,
5945 nameAtom,
5946 callbacks,
5947 namespaceID,
5948 0 /* TODO dependent on HTML imports. Bug 877072 */);
5949 definitions.Put(&key, definition);
5951 // Do element upgrade.
5952 nsAutoPtr<nsTArray<nsRefPtr<Element>>> candidates;
5953 mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
5954 if (candidates) {
5955 for (size_t i = 0; i < candidates->Length(); ++i) {
5956 Element *elem = candidates->ElementAt(i);
5958 // Make sure that the element name matches the name in the definition.
5959 // (e.g. a definition for x-button extending button should match
5960 // <button is="x-button"> but not <x-button>.
5961 if (elem->NodeInfo()->NameAtom() != nameAtom) {
5962 // Skip over this element because definition does not apply.
5963 continue;
5964 }
5966 nsWrapperCache* cache;
5967 CallQueryInterface(elem, &cache);
5968 MOZ_ASSERT(cache, "Element doesn't support wrapper cache?");
5970 JS::RootedObject wrapper(aCx);
5971 if ((wrapper = cache->GetWrapper())) {
5972 if (!JS_SetPrototype(aCx, wrapper, protoObject)) {
5973 continue;
5974 }
5975 }
5977 EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
5978 if (elem->GetCurrentDoc()) {
5979 // Normally callbacks can not be enqueued until the created
5980 // callback has been invoked, however, the attached callback
5981 // in element upgrade is an exception so pretend the created
5982 // callback has been invoked.
5983 elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
5985 EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition);
5986 }
5987 }
5988 }
5990 // Create constructor to return. Store the name of the custom element as the
5991 // name of the function.
5992 JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
5993 JSFUN_CONSTRUCTOR, JS::NullPtr(),
5994 NS_ConvertUTF16toUTF8(lcType).get());
5995 if (!constructor) {
5996 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
5997 return;
5998 }
6000 aRetval.set(JS_GetFunctionObject(constructor));
6001 }
6003 void
6004 nsDocument::UseRegistryFromDocument(nsIDocument* aDocument)
6005 {
6006 nsDocument* doc = static_cast<nsDocument*>(aDocument);
6007 MOZ_ASSERT(!mRegistry, "There should be no existing registry.");
6008 mRegistry = doc->mRegistry;
6009 }
6011 NS_IMETHODIMP
6012 nsDocument::GetElementsByTagName(const nsAString& aTagname,
6013 nsIDOMNodeList** aReturn)
6014 {
6015 nsRefPtr<nsContentList> list = GetElementsByTagName(aTagname);
6016 NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
6018 // transfer ref to aReturn
6019 list.forget(aReturn);
6020 return NS_OK;
6021 }
6023 already_AddRefed<nsContentList>
6024 nsIDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6025 const nsAString& aLocalName,
6026 ErrorResult& aResult)
6027 {
6028 int32_t nameSpaceId = kNameSpaceID_Wildcard;
6030 if (!aNamespaceURI.EqualsLiteral("*")) {
6031 aResult =
6032 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
6033 nameSpaceId);
6034 if (aResult.Failed()) {
6035 return nullptr;
6036 }
6037 }
6039 NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
6041 return NS_GetContentList(this, nameSpaceId, aLocalName);
6042 }
6044 NS_IMETHODIMP
6045 nsDocument::GetElementsByTagNameNS(const nsAString& aNamespaceURI,
6046 const nsAString& aLocalName,
6047 nsIDOMNodeList** aReturn)
6048 {
6049 ErrorResult rv;
6050 nsRefPtr<nsContentList> list =
6051 nsIDocument::GetElementsByTagNameNS(aNamespaceURI, aLocalName, rv);
6052 if (rv.Failed()) {
6053 return rv.ErrorCode();
6054 }
6056 // transfer ref to aReturn
6057 list.forget(aReturn);
6058 return NS_OK;
6059 }
6061 NS_IMETHODIMP
6062 nsDocument::GetAsync(bool *aAsync)
6063 {
6064 NS_ERROR("nsDocument::GetAsync() should be overriden by subclass!");
6066 return NS_ERROR_NOT_IMPLEMENTED;
6067 }
6069 NS_IMETHODIMP
6070 nsDocument::SetAsync(bool aAsync)
6071 {
6072 NS_ERROR("nsDocument::SetAsync() should be overriden by subclass!");
6074 return NS_ERROR_NOT_IMPLEMENTED;
6075 }
6077 NS_IMETHODIMP
6078 nsDocument::Load(const nsAString& aUrl, bool *aReturn)
6079 {
6080 NS_ERROR("nsDocument::Load() should be overriden by subclass!");
6082 return NS_ERROR_NOT_IMPLEMENTED;
6083 }
6085 NS_IMETHODIMP
6086 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
6087 {
6088 NS_ADDREF(*aStyleSheets = StyleSheets());
6089 return NS_OK;
6090 }
6092 StyleSheetList*
6093 nsDocument::StyleSheets()
6094 {
6095 if (!mDOMStyleSheets) {
6096 mDOMStyleSheets = new nsDOMStyleSheetList(this);
6097 }
6098 return mDOMStyleSheets;
6099 }
6101 NS_IMETHODIMP
6102 nsDocument::GetMozSelectedStyleSheetSet(nsAString& aSheetSet)
6103 {
6104 nsIDocument::GetSelectedStyleSheetSet(aSheetSet);
6105 return NS_OK;
6106 }
6108 void
6109 nsIDocument::GetSelectedStyleSheetSet(nsAString& aSheetSet)
6110 {
6111 aSheetSet.Truncate();
6113 // Look through our sheets, find the selected set title
6114 int32_t count = GetNumberOfStyleSheets();
6115 nsAutoString title;
6116 for (int32_t index = 0; index < count; index++) {
6117 nsIStyleSheet* sheet = GetStyleSheetAt(index);
6118 NS_ASSERTION(sheet, "Null sheet in sheet list!");
6120 nsCOMPtr<nsIDOMStyleSheet> domSheet = do_QueryInterface(sheet);
6121 NS_ASSERTION(domSheet, "Sheet must QI to nsIDOMStyleSheet");
6122 bool disabled;
6123 domSheet->GetDisabled(&disabled);
6124 if (disabled) {
6125 // Disabled sheets don't affect the currently selected set
6126 continue;
6127 }
6129 sheet->GetTitle(title);
6131 if (aSheetSet.IsEmpty()) {
6132 aSheetSet = title;
6133 } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
6134 // Sheets from multiple sets enabled; return null string, per spec.
6135 SetDOMStringToNull(aSheetSet);
6136 return;
6137 }
6138 }
6139 }
6141 NS_IMETHODIMP
6142 nsDocument::SetMozSelectedStyleSheetSet(const nsAString& aSheetSet)
6143 {
6144 SetSelectedStyleSheetSet(aSheetSet);
6145 return NS_OK;
6146 }
6148 void
6149 nsDocument::SetSelectedStyleSheetSet(const nsAString& aSheetSet)
6150 {
6151 if (DOMStringIsNull(aSheetSet)) {
6152 return;
6153 }
6155 // Must update mLastStyleSheetSet before doing anything else with stylesheets
6156 // or CSSLoaders.
6157 mLastStyleSheetSet = aSheetSet;
6158 EnableStyleSheetsForSetInternal(aSheetSet, true);
6159 }
6161 NS_IMETHODIMP
6162 nsDocument::GetLastStyleSheetSet(nsAString& aSheetSet)
6163 {
6164 nsString sheetSet;
6165 GetLastStyleSheetSet(sheetSet);
6166 aSheetSet = sheetSet;
6167 return NS_OK;
6168 }
6170 void
6171 nsDocument::GetLastStyleSheetSet(nsString& aSheetSet)
6172 {
6173 aSheetSet = mLastStyleSheetSet;
6174 }
6176 NS_IMETHODIMP
6177 nsDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6178 {
6179 nsIDocument::GetPreferredStyleSheetSet(aSheetSet);
6180 return NS_OK;
6181 }
6183 void
6184 nsIDocument::GetPreferredStyleSheetSet(nsAString& aSheetSet)
6185 {
6186 GetHeaderData(nsGkAtoms::headerDefaultStyle, aSheetSet);
6187 }
6189 NS_IMETHODIMP
6190 nsDocument::GetStyleSheetSets(nsISupports** aList)
6191 {
6192 NS_ADDREF(*aList = StyleSheetSets());
6193 return NS_OK;
6194 }
6196 DOMStringList*
6197 nsDocument::StyleSheetSets()
6198 {
6199 if (!mStyleSheetSetList) {
6200 mStyleSheetSetList = new nsDOMStyleSheetSetList(this);
6201 }
6202 return mStyleSheetSetList;
6203 }
6205 NS_IMETHODIMP
6206 nsDocument::MozEnableStyleSheetsForSet(const nsAString& aSheetSet)
6207 {
6208 EnableStyleSheetsForSet(aSheetSet);
6209 return NS_OK;
6210 }
6212 void
6213 nsDocument::EnableStyleSheetsForSet(const nsAString& aSheetSet)
6214 {
6215 // Per spec, passing in null is a no-op.
6216 if (!DOMStringIsNull(aSheetSet)) {
6217 // Note: must make sure to not change the CSSLoader's preferred sheet --
6218 // that value should be equal to either our lastStyleSheetSet (if that's
6219 // non-null) or to our preferredStyleSheetSet. And this method doesn't
6220 // change either of those.
6221 EnableStyleSheetsForSetInternal(aSheetSet, false);
6222 }
6223 }
6225 void
6226 nsDocument::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
6227 bool aUpdateCSSLoader)
6228 {
6229 BeginUpdate(UPDATE_STYLE);
6230 int32_t count = GetNumberOfStyleSheets();
6231 nsAutoString title;
6232 for (int32_t index = 0; index < count; index++) {
6233 nsIStyleSheet* sheet = GetStyleSheetAt(index);
6234 NS_ASSERTION(sheet, "Null sheet in sheet list!");
6235 sheet->GetTitle(title);
6236 if (!title.IsEmpty()) {
6237 sheet->SetEnabled(title.Equals(aSheetSet));
6238 }
6239 }
6240 if (aUpdateCSSLoader) {
6241 CSSLoader()->SetPreferredSheet(aSheetSet);
6242 }
6243 EndUpdate(UPDATE_STYLE);
6244 }
6246 NS_IMETHODIMP
6247 nsDocument::GetCharacterSet(nsAString& aCharacterSet)
6248 {
6249 nsIDocument::GetCharacterSet(aCharacterSet);
6250 return NS_OK;
6251 }
6253 void
6254 nsIDocument::GetCharacterSet(nsAString& aCharacterSet) const
6255 {
6256 CopyASCIItoUTF16(GetDocumentCharacterSet(), aCharacterSet);
6257 }
6259 NS_IMETHODIMP
6260 nsDocument::ImportNode(nsIDOMNode* aImportedNode,
6261 bool aDeep,
6262 uint8_t aArgc,
6263 nsIDOMNode** aResult)
6264 {
6265 if (aArgc == 0) {
6266 aDeep = true;
6267 }
6269 *aResult = nullptr;
6271 nsCOMPtr<nsINode> imported = do_QueryInterface(aImportedNode);
6272 NS_ENSURE_TRUE(imported, NS_ERROR_UNEXPECTED);
6274 ErrorResult rv;
6275 nsCOMPtr<nsINode> result = nsIDocument::ImportNode(*imported, aDeep, rv);
6276 if (rv.Failed()) {
6277 return rv.ErrorCode();
6278 }
6280 NS_ADDREF(*aResult = result->AsDOMNode());
6281 return NS_OK;
6282 }
6284 already_AddRefed<nsINode>
6285 nsIDocument::ImportNode(nsINode& aNode, bool aDeep, ErrorResult& rv) const
6286 {
6287 nsINode* imported = &aNode;
6289 switch (imported->NodeType()) {
6290 case nsIDOMNode::ATTRIBUTE_NODE:
6291 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
6292 case nsIDOMNode::ELEMENT_NODE:
6293 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
6294 case nsIDOMNode::TEXT_NODE:
6295 case nsIDOMNode::CDATA_SECTION_NODE:
6296 case nsIDOMNode::COMMENT_NODE:
6297 case nsIDOMNode::DOCUMENT_TYPE_NODE:
6298 {
6299 nsCOMPtr<nsINode> newNode;
6300 nsCOMArray<nsINode> nodesWithProperties;
6301 rv = nsNodeUtils::Clone(imported, aDeep, mNodeInfoManager,
6302 nodesWithProperties, getter_AddRefs(newNode));
6303 if (rv.Failed()) {
6304 return nullptr;
6305 }
6307 nsIDocument *ownerDoc = imported->OwnerDoc();
6308 rv = nsNodeUtils::CallUserDataHandlers(nodesWithProperties, ownerDoc,
6309 nsIDOMUserDataHandler::NODE_IMPORTED,
6310 true);
6311 if (rv.Failed()) {
6312 return nullptr;
6313 }
6315 return newNode.forget();
6316 }
6317 default:
6318 {
6319 NS_WARNING("Don't know how to clone this nodetype for importNode.");
6321 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6322 }
6323 }
6325 return nullptr;
6326 }
6328 NS_IMETHODIMP
6329 nsDocument::LoadBindingDocument(const nsAString& aURI)
6330 {
6331 ErrorResult rv;
6332 nsIDocument::LoadBindingDocument(aURI, rv);
6333 return rv.ErrorCode();
6334 }
6336 void
6337 nsIDocument::LoadBindingDocument(const nsAString& aURI, ErrorResult& rv)
6338 {
6339 nsCOMPtr<nsIURI> uri;
6340 rv = NS_NewURI(getter_AddRefs(uri), aURI,
6341 mCharacterSet.get(),
6342 GetDocBaseURI());
6343 if (rv.Failed()) {
6344 return;
6345 }
6347 // Figure out the right principal to use
6348 nsCOMPtr<nsIPrincipal> subject;
6349 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
6350 if (secMan) {
6351 rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
6352 if (rv.Failed()) {
6353 return;
6354 }
6355 }
6357 if (!subject) {
6358 // Fall back to our principal. Or should we fall back to the null
6359 // principal? The latter would just mean no binding loads....
6360 subject = NodePrincipal();
6361 }
6363 BindingManager()->LoadBindingDocument(this, uri, subject);
6364 }
6366 NS_IMETHODIMP
6367 nsDocument::GetBindingParent(nsIDOMNode* aNode, nsIDOMElement** aResult)
6368 {
6369 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
6370 NS_ENSURE_ARG_POINTER(node);
6372 Element* bindingParent = nsIDocument::GetBindingParent(*node);
6373 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(bindingParent);
6374 retval.forget(aResult);
6375 return NS_OK;
6376 }
6378 Element*
6379 nsIDocument::GetBindingParent(nsINode& aNode)
6380 {
6381 nsCOMPtr<nsIContent> content(do_QueryInterface(&aNode));
6382 if (!content)
6383 return nullptr;
6385 nsIContent* bindingParent = content->GetBindingParent();
6386 return bindingParent ? bindingParent->AsElement() : nullptr;
6387 }
6389 static Element*
6390 GetElementByAttribute(nsIContent* aContent, nsIAtom* aAttrName,
6391 const nsAString& aAttrValue, bool aUniversalMatch)
6392 {
6393 if (aUniversalMatch ? aContent->HasAttr(kNameSpaceID_None, aAttrName) :
6394 aContent->AttrValueIs(kNameSpaceID_None, aAttrName,
6395 aAttrValue, eCaseMatters)) {
6396 return aContent->AsElement();
6397 }
6399 for (nsIContent* child = aContent->GetFirstChild();
6400 child;
6401 child = child->GetNextSibling()) {
6403 Element* matchedElement =
6404 GetElementByAttribute(child, aAttrName, aAttrValue, aUniversalMatch);
6405 if (matchedElement)
6406 return matchedElement;
6407 }
6409 return nullptr;
6410 }
6412 Element*
6413 nsDocument::GetAnonymousElementByAttribute(nsIContent* aElement,
6414 nsIAtom* aAttrName,
6415 const nsAString& aAttrValue) const
6416 {
6417 nsINodeList* nodeList = BindingManager()->GetAnonymousNodesFor(aElement);
6418 if (!nodeList)
6419 return nullptr;
6421 uint32_t length = 0;
6422 nodeList->GetLength(&length);
6424 bool universalMatch = aAttrValue.EqualsLiteral("*");
6426 for (uint32_t i = 0; i < length; ++i) {
6427 nsIContent* current = nodeList->Item(i);
6428 Element* matchedElm =
6429 GetElementByAttribute(current, aAttrName, aAttrValue, universalMatch);
6430 if (matchedElm)
6431 return matchedElm;
6432 }
6434 return nullptr;
6435 }
6437 NS_IMETHODIMP
6438 nsDocument::GetAnonymousElementByAttribute(nsIDOMElement* aElement,
6439 const nsAString& aAttrName,
6440 const nsAString& aAttrValue,
6441 nsIDOMElement** aResult)
6442 {
6443 nsCOMPtr<Element> element = do_QueryInterface(aElement);
6444 NS_ENSURE_ARG_POINTER(element);
6446 Element* anonEl =
6447 nsIDocument::GetAnonymousElementByAttribute(*element, aAttrName,
6448 aAttrValue);
6449 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(anonEl);
6450 retval.forget(aResult);
6451 return NS_OK;
6452 }
6454 Element*
6455 nsIDocument::GetAnonymousElementByAttribute(Element& aElement,
6456 const nsAString& aAttrName,
6457 const nsAString& aAttrValue)
6458 {
6459 nsCOMPtr<nsIAtom> attribute = do_GetAtom(aAttrName);
6461 return GetAnonymousElementByAttribute(&aElement, attribute, aAttrValue);
6462 }
6465 NS_IMETHODIMP
6466 nsDocument::GetAnonymousNodes(nsIDOMElement* aElement,
6467 nsIDOMNodeList** aResult)
6468 {
6469 *aResult = nullptr;
6471 nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
6472 return BindingManager()->GetAnonymousNodesFor(content, aResult);
6473 }
6475 nsINodeList*
6476 nsIDocument::GetAnonymousNodes(Element& aElement)
6477 {
6478 return BindingManager()->GetAnonymousNodesFor(&aElement);
6479 }
6481 NS_IMETHODIMP
6482 nsDocument::CreateRange(nsIDOMRange** aReturn)
6483 {
6484 ErrorResult rv;
6485 *aReturn = nsIDocument::CreateRange(rv).take();
6486 return rv.ErrorCode();
6487 }
6489 already_AddRefed<nsRange>
6490 nsIDocument::CreateRange(ErrorResult& rv)
6491 {
6492 nsRefPtr<nsRange> range = new nsRange(this);
6493 nsresult res = range->Set(this, 0, this, 0);
6494 if (NS_FAILED(res)) {
6495 rv.Throw(res);
6496 return nullptr;
6497 }
6499 return range.forget();
6500 }
6502 NS_IMETHODIMP
6503 nsDocument::CreateNodeIterator(nsIDOMNode *aRoot,
6504 uint32_t aWhatToShow,
6505 nsIDOMNodeFilter *aFilter,
6506 uint8_t aOptionalArgc,
6507 nsIDOMNodeIterator **_retval)
6508 {
6509 *_retval = nullptr;
6511 if (!aOptionalArgc) {
6512 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6513 }
6515 if (!aRoot) {
6516 return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
6517 }
6519 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6520 NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
6522 ErrorResult rv;
6523 NodeFilterHolder holder(aFilter);
6524 *_retval = nsIDocument::CreateNodeIterator(*root, aWhatToShow, holder,
6525 rv).take();
6526 return rv.ErrorCode();
6527 }
6529 already_AddRefed<NodeIterator>
6530 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6531 NodeFilter* aFilter,
6532 ErrorResult& rv) const
6533 {
6534 NodeFilterHolder holder(aFilter);
6535 return CreateNodeIterator(aRoot, aWhatToShow, holder, rv);
6536 }
6538 already_AddRefed<NodeIterator>
6539 nsIDocument::CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,
6540 const NodeFilterHolder& aFilter,
6541 ErrorResult& rv) const
6542 {
6543 nsINode* root = &aRoot;
6544 nsRefPtr<NodeIterator> iterator = new NodeIterator(root, aWhatToShow,
6545 aFilter);
6546 return iterator.forget();
6547 }
6549 NS_IMETHODIMP
6550 nsDocument::CreateTreeWalker(nsIDOMNode *aRoot,
6551 uint32_t aWhatToShow,
6552 nsIDOMNodeFilter *aFilter,
6553 uint8_t aOptionalArgc,
6554 nsIDOMTreeWalker **_retval)
6555 {
6556 *_retval = nullptr;
6558 if (!aOptionalArgc) {
6559 aWhatToShow = nsIDOMNodeFilter::SHOW_ALL;
6560 }
6562 nsCOMPtr<nsINode> root = do_QueryInterface(aRoot);
6563 NS_ENSURE_TRUE(root, NS_ERROR_DOM_NOT_SUPPORTED_ERR);
6565 ErrorResult rv;
6566 NodeFilterHolder holder(aFilter);
6567 *_retval = nsIDocument::CreateTreeWalker(*root, aWhatToShow, holder,
6568 rv).take();
6569 return rv.ErrorCode();
6570 }
6572 already_AddRefed<TreeWalker>
6573 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6574 NodeFilter* aFilter,
6575 ErrorResult& rv) const
6576 {
6577 NodeFilterHolder holder(aFilter);
6578 return CreateTreeWalker(aRoot, aWhatToShow, holder, rv);
6579 }
6581 already_AddRefed<TreeWalker>
6582 nsIDocument::CreateTreeWalker(nsINode& aRoot, uint32_t aWhatToShow,
6583 const NodeFilterHolder& aFilter,
6584 ErrorResult& rv) const
6585 {
6586 nsINode* root = &aRoot;
6587 nsRefPtr<TreeWalker> walker = new TreeWalker(root, aWhatToShow, aFilter);
6588 return walker.forget();
6589 }
6592 NS_IMETHODIMP
6593 nsDocument::GetDefaultView(nsIDOMWindow** aDefaultView)
6594 {
6595 *aDefaultView = nullptr;
6596 nsCOMPtr<nsPIDOMWindow> win = GetWindow();
6597 win.forget(aDefaultView);
6598 return NS_OK;
6599 }
6601 NS_IMETHODIMP
6602 nsDocument::GetLocation(nsIDOMLocation **_retval)
6603 {
6604 *_retval = nsIDocument::GetLocation().take();
6605 return NS_OK;
6606 }
6608 already_AddRefed<nsIDOMLocation>
6609 nsIDocument::GetLocation() const
6610 {
6611 nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject);
6613 if (!w) {
6614 return nullptr;
6615 }
6617 nsCOMPtr<nsIDOMLocation> loc;
6618 w->GetLocation(getter_AddRefs(loc));
6619 return loc.forget();
6620 }
6622 Element*
6623 nsIDocument::GetHtmlElement() const
6624 {
6625 Element* rootElement = GetRootElement();
6626 if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
6627 return rootElement;
6628 return nullptr;
6629 }
6631 Element*
6632 nsIDocument::GetHtmlChildElement(nsIAtom* aTag)
6633 {
6634 Element* html = GetHtmlElement();
6635 if (!html)
6636 return nullptr;
6638 // Look for the element with aTag inside html. This needs to run
6639 // forwards to find the first such element.
6640 for (nsIContent* child = html->GetFirstChild();
6641 child;
6642 child = child->GetNextSibling()) {
6643 if (child->IsHTML(aTag))
6644 return child->AsElement();
6645 }
6646 return nullptr;
6647 }
6649 nsIContent*
6650 nsDocument::GetTitleContent(uint32_t aNamespace)
6651 {
6652 // mMayHaveTitleElement will have been set to true if any HTML or SVG
6653 // <title> element has been bound to this document. So if it's false,
6654 // we know there is nothing to do here. This avoids us having to search
6655 // the whole DOM if someone calls document.title on a large document
6656 // without a title.
6657 if (!mMayHaveTitleElement)
6658 return nullptr;
6660 nsRefPtr<nsContentList> list =
6661 NS_GetContentList(this, aNamespace, NS_LITERAL_STRING("title"));
6663 return list->Item(0, false);
6664 }
6666 void
6667 nsDocument::GetTitleFromElement(uint32_t aNamespace, nsAString& aTitle)
6668 {
6669 nsIContent* title = GetTitleContent(aNamespace);
6670 if (!title)
6671 return;
6672 if(!nsContentUtils::GetNodeTextContent(title, false, aTitle))
6673 NS_RUNTIMEABORT("OOM");
6674 }
6676 NS_IMETHODIMP
6677 nsDocument::GetTitle(nsAString& aTitle)
6678 {
6679 nsString title;
6680 GetTitle(title);
6681 aTitle = title;
6682 return NS_OK;
6683 }
6685 void
6686 nsDocument::GetTitle(nsString& aTitle)
6687 {
6688 aTitle.Truncate();
6690 nsIContent *rootElement = GetRootElement();
6691 if (!rootElement)
6692 return;
6694 nsAutoString tmp;
6696 switch (rootElement->GetNameSpaceID()) {
6697 #ifdef MOZ_XUL
6698 case kNameSpaceID_XUL:
6699 rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
6700 break;
6701 #endif
6702 case kNameSpaceID_SVG:
6703 if (rootElement->Tag() == nsGkAtoms::svg) {
6704 GetTitleFromElement(kNameSpaceID_SVG, tmp);
6705 break;
6706 } // else fall through
6707 default:
6708 GetTitleFromElement(kNameSpaceID_XHTML, tmp);
6709 break;
6710 }
6712 tmp.CompressWhitespace();
6713 aTitle = tmp;
6714 }
6716 NS_IMETHODIMP
6717 nsDocument::SetTitle(const nsAString& aTitle)
6718 {
6719 Element *rootElement = GetRootElement();
6720 if (!rootElement)
6721 return NS_OK;
6723 switch (rootElement->GetNameSpaceID()) {
6724 case kNameSpaceID_SVG:
6725 return NS_OK; // SVG doesn't support setting a title
6726 #ifdef MOZ_XUL
6727 case kNameSpaceID_XUL:
6728 return rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title,
6729 aTitle, true);
6730 #endif
6731 }
6733 // Batch updates so that mutation events don't change "the title
6734 // element" under us
6735 mozAutoDocUpdate updateBatch(this, UPDATE_CONTENT_MODEL, true);
6737 nsIContent* title = GetTitleContent(kNameSpaceID_XHTML);
6738 if (!title) {
6739 Element *head = GetHeadElement();
6740 if (!head)
6741 return NS_OK;
6743 {
6744 nsCOMPtr<nsINodeInfo> titleInfo;
6745 titleInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::title, nullptr,
6746 kNameSpaceID_XHTML,
6747 nsIDOMNode::ELEMENT_NODE);
6748 title = NS_NewHTMLTitleElement(titleInfo.forget());
6749 if (!title)
6750 return NS_OK;
6751 }
6753 head->AppendChildTo(title, true);
6754 }
6756 return nsContentUtils::SetNodeTextContent(title, aTitle, false);
6757 }
6759 void
6760 nsDocument::SetTitle(const nsAString& aTitle, ErrorResult& rv)
6761 {
6762 rv = SetTitle(aTitle);
6763 }
6765 void
6766 nsDocument::NotifyPossibleTitleChange(bool aBoundTitleElement)
6767 {
6768 NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
6769 "Setting a title while unlinking or destroying the element?");
6770 if (mInUnlinkOrDeletion) {
6771 return;
6772 }
6774 if (aBoundTitleElement) {
6775 mMayHaveTitleElement = true;
6776 }
6777 if (mPendingTitleChangeEvent.IsPending())
6778 return;
6780 nsRefPtr<nsRunnableMethod<nsDocument, void, false> > event =
6781 NS_NewNonOwningRunnableMethod(this,
6782 &nsDocument::DoNotifyPossibleTitleChange);
6783 nsresult rv = NS_DispatchToCurrentThread(event);
6784 if (NS_SUCCEEDED(rv)) {
6785 mPendingTitleChangeEvent = event;
6786 }
6787 }
6789 void
6790 nsDocument::DoNotifyPossibleTitleChange()
6791 {
6792 mPendingTitleChangeEvent.Forget();
6793 mHaveFiredTitleChange = true;
6795 nsAutoString title;
6796 GetTitle(title);
6798 nsCOMPtr<nsIPresShell> shell = GetShell();
6799 if (shell) {
6800 nsCOMPtr<nsISupports> container =
6801 shell->GetPresContext()->GetContainerWeak();
6802 if (container) {
6803 nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
6804 if (docShellWin) {
6805 docShellWin->SetTitle(title.get());
6806 }
6807 }
6808 }
6810 // Fire a DOM event for the title change.
6811 nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
6812 NS_LITERAL_STRING("DOMTitleChanged"),
6813 true, true);
6814 }
6816 already_AddRefed<nsIBoxObject>
6817 nsDocument::GetBoxObjectFor(Element* aElement, ErrorResult& aRv)
6818 {
6819 if (!aElement) {
6820 aRv.Throw(NS_ERROR_UNEXPECTED);
6821 return nullptr;
6822 }
6824 nsIDocument* doc = aElement->OwnerDoc();
6825 if (doc != this) {
6826 aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
6827 return nullptr;
6828 }
6830 if (!mHasWarnedAboutBoxObjects && !aElement->IsXUL()) {
6831 mHasWarnedAboutBoxObjects = true;
6832 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
6833 NS_LITERAL_CSTRING("BoxObjects"), this,
6834 nsContentUtils::eDOM_PROPERTIES,
6835 "UseOfGetBoxObjectForWarning");
6836 }
6838 if (!mBoxObjectTable) {
6839 mBoxObjectTable = new nsInterfaceHashtable<nsPtrHashKey<nsIContent>, nsPIBoxObject>(12);
6840 } else {
6841 nsCOMPtr<nsPIBoxObject> boxObject = mBoxObjectTable->Get(aElement);
6842 if (boxObject) {
6843 return boxObject.forget();
6844 }
6845 }
6847 int32_t namespaceID;
6848 nsCOMPtr<nsIAtom> tag = BindingManager()->ResolveTag(aElement, &namespaceID);
6850 nsAutoCString contractID("@mozilla.org/layout/xul-boxobject");
6851 if (namespaceID == kNameSpaceID_XUL) {
6852 if (tag == nsGkAtoms::browser ||
6853 tag == nsGkAtoms::editor ||
6854 tag == nsGkAtoms::iframe)
6855 contractID += "-container";
6856 else if (tag == nsGkAtoms::menu)
6857 contractID += "-menu";
6858 else if (tag == nsGkAtoms::popup ||
6859 tag == nsGkAtoms::menupopup ||
6860 tag == nsGkAtoms::panel ||
6861 tag == nsGkAtoms::tooltip)
6862 contractID += "-popup";
6863 else if (tag == nsGkAtoms::tree)
6864 contractID += "-tree";
6865 else if (tag == nsGkAtoms::listbox)
6866 contractID += "-listbox";
6867 else if (tag == nsGkAtoms::scrollbox)
6868 contractID += "-scrollbox";
6869 }
6870 contractID += ";1";
6872 nsCOMPtr<nsPIBoxObject> boxObject(do_CreateInstance(contractID.get()));
6873 if (!boxObject) {
6874 aRv.Throw(NS_ERROR_FAILURE);
6875 return nullptr;
6876 }
6878 boxObject->Init(aElement);
6880 if (mBoxObjectTable) {
6881 mBoxObjectTable->Put(aElement, boxObject.get());
6882 }
6884 return boxObject.forget();
6885 }
6887 void
6888 nsDocument::ClearBoxObjectFor(nsIContent* aContent)
6889 {
6890 if (mBoxObjectTable) {
6891 nsPIBoxObject *boxObject = mBoxObjectTable->GetWeak(aContent);
6892 if (boxObject) {
6893 boxObject->Clear();
6894 mBoxObjectTable->Remove(aContent);
6895 }
6896 }
6897 }
6899 void
6900 nsDocument::FlushSkinBindings()
6901 {
6902 BindingManager()->FlushSkinBindings();
6903 }
6905 nsresult
6906 nsDocument::InitializeFrameLoader(nsFrameLoader* aLoader)
6907 {
6908 mInitializableFrameLoaders.RemoveElement(aLoader);
6909 // Don't even try to initialize.
6910 if (mInDestructor) {
6911 NS_WARNING("Trying to initialize a frame loader while"
6912 "document is being deleted");
6913 return NS_ERROR_FAILURE;
6914 }
6916 mInitializableFrameLoaders.AppendElement(aLoader);
6917 if (!mFrameLoaderRunner) {
6918 mFrameLoaderRunner =
6919 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6920 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6921 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6922 }
6923 return NS_OK;
6924 }
6926 nsresult
6927 nsDocument::FinalizeFrameLoader(nsFrameLoader* aLoader)
6928 {
6929 mInitializableFrameLoaders.RemoveElement(aLoader);
6930 if (mInDestructor) {
6931 return NS_ERROR_FAILURE;
6932 }
6934 mFinalizableFrameLoaders.AppendElement(aLoader);
6935 if (!mFrameLoaderRunner) {
6936 mFrameLoaderRunner =
6937 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6938 NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
6939 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6940 }
6941 return NS_OK;
6942 }
6944 void
6945 nsDocument::MaybeInitializeFinalizeFrameLoaders()
6946 {
6947 if (mDelayFrameLoaderInitialization || mUpdateNestLevel != 0) {
6948 // This method will be recalled when mUpdateNestLevel drops to 0,
6949 // or when !mDelayFrameLoaderInitialization.
6950 mFrameLoaderRunner = nullptr;
6951 return;
6952 }
6954 // We're not in an update, but it is not safe to run scripts, so
6955 // postpone frameloader initialization and finalization.
6956 if (!nsContentUtils::IsSafeToRunScript()) {
6957 if (!mInDestructor && !mFrameLoaderRunner &&
6958 (mInitializableFrameLoaders.Length() ||
6959 mFinalizableFrameLoaders.Length())) {
6960 mFrameLoaderRunner =
6961 NS_NewRunnableMethod(this, &nsDocument::MaybeInitializeFinalizeFrameLoaders);
6962 nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
6963 }
6964 return;
6965 }
6966 mFrameLoaderRunner = nullptr;
6968 // Don't use a temporary array for mInitializableFrameLoaders, because
6969 // loading a frame may cause some other frameloader to be removed from the
6970 // array. But be careful to keep the loader alive when starting the load!
6971 while (mInitializableFrameLoaders.Length()) {
6972 nsRefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
6973 mInitializableFrameLoaders.RemoveElementAt(0);
6974 NS_ASSERTION(loader, "null frameloader in the array?");
6975 loader->ReallyStartLoading();
6976 }
6978 uint32_t length = mFinalizableFrameLoaders.Length();
6979 if (length > 0) {
6980 nsTArray<nsRefPtr<nsFrameLoader> > loaders;
6981 mFinalizableFrameLoaders.SwapElements(loaders);
6982 for (uint32_t i = 0; i < length; ++i) {
6983 loaders[i]->Finalize();
6984 }
6985 }
6986 }
6988 void
6989 nsDocument::TryCancelFrameLoaderInitialization(nsIDocShell* aShell)
6990 {
6991 uint32_t length = mInitializableFrameLoaders.Length();
6992 for (uint32_t i = 0; i < length; ++i) {
6993 if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
6994 mInitializableFrameLoaders.RemoveElementAt(i);
6995 return;
6996 }
6997 }
6998 }
7000 bool
7001 nsDocument::FrameLoaderScheduledToBeFinalized(nsIDocShell* aShell)
7002 {
7003 if (aShell) {
7004 uint32_t length = mFinalizableFrameLoaders.Length();
7005 for (uint32_t i = 0; i < length; ++i) {
7006 if (mFinalizableFrameLoaders[i]->GetExistingDocShell() == aShell) {
7007 return true;
7008 }
7009 }
7010 }
7011 return false;
7012 }
7014 nsIDocument*
7015 nsDocument::RequestExternalResource(nsIURI* aURI,
7016 nsINode* aRequestingNode,
7017 ExternalResourceLoad** aPendingLoad)
7018 {
7019 NS_PRECONDITION(aURI, "Must have a URI");
7020 NS_PRECONDITION(aRequestingNode, "Must have a node");
7021 if (mDisplayDocument) {
7022 return mDisplayDocument->RequestExternalResource(aURI,
7023 aRequestingNode,
7024 aPendingLoad);
7025 }
7027 return mExternalResourceMap.RequestResource(aURI, aRequestingNode,
7028 this, aPendingLoad);
7029 }
7031 void
7032 nsDocument::EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData)
7033 {
7034 mExternalResourceMap.EnumerateResources(aCallback, aData);
7035 }
7037 nsSMILAnimationController*
7038 nsDocument::GetAnimationController()
7039 {
7040 // We create the animation controller lazily because most documents won't want
7041 // one and only SVG documents and the like will call this
7042 if (mAnimationController)
7043 return mAnimationController;
7044 // Refuse to create an Animation Controller for data documents.
7045 if (mLoadedAsData || mLoadedAsInteractiveData)
7046 return nullptr;
7048 mAnimationController = new nsSMILAnimationController(this);
7050 // If there's a presContext then check the animation mode and pause if
7051 // necessary.
7052 nsIPresShell *shell = GetShell();
7053 if (mAnimationController && shell) {
7054 nsPresContext *context = shell->GetPresContext();
7055 if (context &&
7056 context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
7057 mAnimationController->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
7058 }
7059 }
7061 // If we're hidden (or being hidden), notify the newly-created animation
7062 // controller. (Skip this check for SVG-as-an-image documents, though,
7063 // because they don't get OnPageShow / OnPageHide calls).
7064 if (!mIsShowing && !mIsBeingUsedAsImage) {
7065 mAnimationController->OnPageHide();
7066 }
7068 return mAnimationController;
7069 }
7071 /**
7072 * Retrieve the "direction" property of the document.
7073 *
7074 * @lina 01/09/2001
7075 */
7076 NS_IMETHODIMP
7077 nsDocument::GetDir(nsAString& aDirection)
7078 {
7079 nsIDocument::GetDir(aDirection);
7080 return NS_OK;
7081 }
7083 void
7084 nsIDocument::GetDir(nsAString& aDirection) const
7085 {
7086 aDirection.Truncate();
7087 Element* rootElement = GetHtmlElement();
7088 if (rootElement) {
7089 static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
7090 }
7091 }
7093 /**
7094 * Set the "direction" property of the document.
7095 *
7096 * @lina 01/09/2001
7097 */
7098 NS_IMETHODIMP
7099 nsDocument::SetDir(const nsAString& aDirection)
7100 {
7101 nsIDocument::SetDir(aDirection);
7102 return NS_OK;
7103 }
7105 void
7106 nsIDocument::SetDir(const nsAString& aDirection)
7107 {
7108 Element* rootElement = GetHtmlElement();
7109 if (rootElement) {
7110 rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
7111 aDirection, true);
7112 }
7113 }
7115 NS_IMETHODIMP
7116 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
7117 {
7118 nsIDocument::GetInputEncoding(aInputEncoding);
7119 return NS_OK;
7120 }
7122 void
7123 nsIDocument::GetInputEncoding(nsAString& aInputEncoding)
7124 {
7125 // Not const function, because WarnOnceAbout is not a const method
7126 WarnOnceAbout(eInputEncoding);
7127 if (mHaveInputEncoding) {
7128 return GetCharacterSet(aInputEncoding);
7129 }
7131 SetDOMStringToNull(aInputEncoding);
7132 }
7134 NS_IMETHODIMP
7135 nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument)
7136 {
7137 *aSyntheticDocument = mIsSyntheticDocument;
7138 return NS_OK;
7139 }
7141 NS_IMETHODIMP
7142 nsDocument::GetDocumentURI(nsAString& aDocumentURI)
7143 {
7144 nsString temp;
7145 nsIDocument::GetDocumentURI(temp);
7146 aDocumentURI = temp;
7147 return NS_OK;
7148 }
7150 void
7151 nsIDocument::GetDocumentURI(nsString& aDocumentURI) const
7152 {
7153 if (mDocumentURI) {
7154 nsAutoCString uri;
7155 mDocumentURI->GetSpec(uri);
7156 CopyUTF8toUTF16(uri, aDocumentURI);
7157 } else {
7158 aDocumentURI.Truncate();
7159 }
7160 }
7162 // Alias of above
7163 NS_IMETHODIMP
7164 nsDocument::GetURL(nsAString& aURL)
7165 {
7166 return GetDocumentURI(aURL);
7167 }
7169 void
7170 nsIDocument::GetURL(nsString& aURL) const
7171 {
7172 return GetDocumentURI(aURL);
7173 }
7175 void
7176 nsIDocument::GetDocumentURIFromJS(nsString& aDocumentURI) const
7177 {
7178 if (!mChromeXHRDocURI || !nsContentUtils::IsCallerChrome()) {
7179 return GetDocumentURI(aDocumentURI);
7180 }
7182 nsAutoCString uri;
7183 mChromeXHRDocURI->GetSpec(uri);
7184 CopyUTF8toUTF16(uri, aDocumentURI);
7185 }
7187 nsIURI*
7188 nsIDocument::GetDocumentURIObject() const
7189 {
7190 if (!mChromeXHRDocURI) {
7191 return GetDocumentURI();
7192 }
7194 return mChromeXHRDocURI;
7195 }
7198 // readonly attribute DOMString compatMode;
7199 // Returns "BackCompat" if we are in quirks mode, "CSS1Compat" if we are
7200 // in almost standards or full standards mode. See bug 105640. This was
7201 // implemented to match MSIE's compatMode property.
7202 NS_IMETHODIMP
7203 nsDocument::GetCompatMode(nsAString& aCompatMode)
7204 {
7205 nsString temp;
7206 nsIDocument::GetCompatMode(temp);
7207 aCompatMode = temp;
7208 return NS_OK;
7209 }
7211 void
7212 nsIDocument::GetCompatMode(nsString& aCompatMode) const
7213 {
7214 NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
7215 mCompatMode == eCompatibility_AlmostStandards ||
7216 mCompatMode == eCompatibility_FullStandards,
7217 "mCompatMode is neither quirks nor strict for this document");
7219 if (mCompatMode == eCompatibility_NavQuirks) {
7220 aCompatMode.AssignLiteral("BackCompat");
7221 } else {
7222 aCompatMode.AssignLiteral("CSS1Compat");
7223 }
7224 }
7226 static void BlastSubtreeToPieces(nsINode *aNode);
7228 PLDHashOperator
7229 BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg)
7230 {
7231 nsCOMPtr<nsIAttribute> *attr =
7232 static_cast<nsCOMPtr<nsIAttribute>*>(aUserArg);
7234 *attr = aData;
7236 NS_ASSERTION(attr->get(),
7237 "non-nsIAttribute somehow made it into the hashmap?!");
7239 return PL_DHASH_STOP;
7240 }
7242 static void
7243 BlastSubtreeToPieces(nsINode *aNode)
7244 {
7245 if (aNode->IsElement()) {
7246 Element *element = aNode->AsElement();
7247 const nsDOMAttributeMap *map = element->GetAttributeMap();
7248 if (map) {
7249 nsCOMPtr<nsIAttribute> attr;
7250 while (map->Enumerate(BlastFunc, &attr) > 0) {
7251 BlastSubtreeToPieces(attr);
7253 #ifdef DEBUG
7254 nsresult rv =
7255 #endif
7256 element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
7257 attr->NodeInfo()->NameAtom(),
7258 false);
7260 // XXX Should we abort here?
7261 NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!");
7262 }
7263 }
7264 }
7266 uint32_t count = aNode->GetChildCount();
7267 for (uint32_t i = 0; i < count; ++i) {
7268 BlastSubtreeToPieces(aNode->GetFirstChild());
7269 aNode->RemoveChildAt(0, false);
7270 }
7271 }
7274 class nsUserDataCaller : public nsRunnable
7275 {
7276 public:
7277 nsUserDataCaller(nsCOMArray<nsINode>& aNodesWithProperties,
7278 nsIDocument* aOwnerDoc)
7279 : mNodesWithProperties(aNodesWithProperties),
7280 mOwnerDoc(aOwnerDoc)
7281 {
7282 }
7284 NS_IMETHOD Run()
7285 {
7286 nsNodeUtils::CallUserDataHandlers(mNodesWithProperties, mOwnerDoc,
7287 nsIDOMUserDataHandler::NODE_ADOPTED,
7288 false);
7289 return NS_OK;
7290 }
7292 private:
7293 nsCOMArray<nsINode> mNodesWithProperties;
7294 nsCOMPtr<nsIDocument> mOwnerDoc;
7295 };
7297 NS_IMETHODIMP
7298 nsDocument::AdoptNode(nsIDOMNode *aAdoptedNode, nsIDOMNode **aResult)
7299 {
7300 *aResult = nullptr;
7302 nsCOMPtr<nsINode> adoptedNode = do_QueryInterface(aAdoptedNode);
7303 NS_ENSURE_TRUE(adoptedNode, NS_ERROR_UNEXPECTED);
7305 ErrorResult rv;
7306 nsINode* result = nsIDocument::AdoptNode(*adoptedNode, rv);
7307 if (rv.Failed()) {
7308 return rv.ErrorCode();
7309 }
7311 NS_ADDREF(*aResult = result->AsDOMNode());
7312 return NS_OK;
7313 }
7315 nsINode*
7316 nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv)
7317 {
7318 nsINode* adoptedNode = &aAdoptedNode;
7320 // Scope firing mutation events so that we don't carry any state that
7321 // might be stale
7322 {
7323 nsINode* parent = adoptedNode->GetParentNode();
7324 if (parent) {
7325 nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent,
7326 adoptedNode->OwnerDoc());
7327 }
7328 }
7330 nsAutoScriptBlocker scriptBlocker;
7332 switch (adoptedNode->NodeType()) {
7333 case nsIDOMNode::ATTRIBUTE_NODE:
7334 {
7335 // Remove from ownerElement.
7336 nsRefPtr<Attr> adoptedAttr = static_cast<Attr*>(adoptedNode);
7338 nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
7339 if (rv.Failed()) {
7340 return nullptr;
7341 }
7343 if (ownerElement) {
7344 nsRefPtr<Attr> newAttr =
7345 ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
7346 if (rv.Failed()) {
7347 return nullptr;
7348 }
7350 newAttr.swap(adoptedAttr);
7351 }
7353 break;
7354 }
7355 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
7356 case nsIDOMNode::ELEMENT_NODE:
7357 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
7358 case nsIDOMNode::TEXT_NODE:
7359 case nsIDOMNode::CDATA_SECTION_NODE:
7360 case nsIDOMNode::COMMENT_NODE:
7361 case nsIDOMNode::DOCUMENT_TYPE_NODE:
7362 {
7363 // Don't allow adopting a node's anonymous subtree out from under it.
7364 if (adoptedNode->AsContent()->IsRootOfAnonymousSubtree()) {
7365 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7366 return nullptr;
7367 }
7369 // We don't want to adopt an element into its own contentDocument or into
7370 // a descendant contentDocument, so we check if the frameElement of this
7371 // document or any of its parents is the adopted node or one of its
7372 // descendants.
7373 nsIDocument *doc = this;
7374 do {
7375 nsPIDOMWindow *win = doc->GetWindow();
7376 if (win) {
7377 nsCOMPtr<nsINode> node =
7378 do_QueryInterface(win->GetFrameElementInternal());
7379 if (node &&
7380 nsContentUtils::ContentIsDescendantOf(node, adoptedNode)) {
7381 rv.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
7382 return nullptr;
7383 }
7384 }
7385 } while ((doc = doc->GetParentDocument()));
7387 // Remove from parent.
7388 nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
7389 if (parent) {
7390 int32_t idx = parent->IndexOf(adoptedNode);
7391 MOZ_ASSERT(idx >= 0);
7392 parent->RemoveChildAt(idx, true);
7393 } else {
7394 MOZ_ASSERT(!adoptedNode->IsInDoc());
7396 // If we're adopting a node that's not in a document, it might still
7397 // have a binding applied. Remove the binding from the element now
7398 // that it's getting adopted into a new document.
7399 // TODO Fully tear down the binding.
7400 adoptedNode->AsContent()->SetXBLBinding(nullptr);
7401 }
7403 break;
7404 }
7405 case nsIDOMNode::DOCUMENT_NODE:
7406 {
7407 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7408 return nullptr;
7409 }
7410 default:
7411 {
7412 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
7414 rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
7415 return nullptr;
7416 }
7417 }
7419 nsCOMPtr<nsIDocument> oldDocument = adoptedNode->OwnerDoc();
7420 bool sameDocument = oldDocument == this;
7422 AutoJSContext cx;
7423 JS::Rooted<JSObject*> newScope(cx, nullptr);
7424 if (!sameDocument) {
7425 newScope = GetWrapper();
7426 if (!newScope && GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
7427 // Make sure cx is in a semi-sane compartment before we call WrapNative.
7428 // It's kind of irrelevant, given that we're passing aAllowWrapping =
7429 // false, and documents should always insist on being wrapped in an
7430 // canonical scope. But we try to pass something sane anyway.
7431 JSAutoCompartment ac(cx, GetScopeObject()->GetGlobalJSObject());
7432 JS::Rooted<JS::Value> v(cx);
7433 rv = nsContentUtils::WrapNative(cx, this, this, &v,
7434 /* aAllowWrapping = */ false);
7435 if (rv.Failed())
7436 return nullptr;
7437 newScope = &v.toObject();
7438 }
7439 }
7441 nsCOMArray<nsINode> nodesWithProperties;
7442 rv = nsNodeUtils::Adopt(adoptedNode, sameDocument ? nullptr : mNodeInfoManager,
7443 newScope, nodesWithProperties);
7444 if (rv.Failed()) {
7445 // Disconnect all nodes from their parents, since some have the old document
7446 // as their ownerDocument and some have this as their ownerDocument.
7447 BlastSubtreeToPieces(adoptedNode);
7449 if (!sameDocument && oldDocument) {
7450 uint32_t count = nodesWithProperties.Count();
7451 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7452 for (uint32_t i = 0; i < count; ++i) {
7453 // Remove all properties.
7454 oldDocument->PropertyTable(j)->
7455 DeleteAllPropertiesFor(nodesWithProperties[i]);
7456 }
7457 }
7458 }
7460 return nullptr;
7461 }
7463 uint32_t count = nodesWithProperties.Count();
7464 if (!sameDocument && oldDocument) {
7465 for (uint32_t j = 0; j < oldDocument->GetPropertyTableCount(); ++j) {
7466 nsPropertyTable *oldTable = oldDocument->PropertyTable(j);
7467 nsPropertyTable *newTable = PropertyTable(j);
7468 for (uint32_t i = 0; i < count; ++i) {
7469 rv = oldTable->TransferOrDeleteAllPropertiesFor(nodesWithProperties[i],
7470 newTable);
7471 }
7472 }
7474 if (rv.Failed()) {
7475 // Disconnect all nodes from their parents.
7476 BlastSubtreeToPieces(adoptedNode);
7478 return nullptr;
7479 }
7480 }
7482 if (nodesWithProperties.Count()) {
7483 nsContentUtils::AddScriptRunner(new nsUserDataCaller(nodesWithProperties,
7484 this));
7485 }
7487 NS_ASSERTION(adoptedNode->OwnerDoc() == this,
7488 "Should still be in the document we just got adopted into");
7490 return adoptedNode;
7491 }
7493 nsViewportInfo
7494 nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
7495 {
7496 // In cases where the width of the CSS viewport is less than or equal to the width
7497 // of the display (i.e. width <= device-width) then we disable double-tap-to-zoom
7498 // behaviour. See bug 941995 for details.
7500 switch (mViewportType) {
7501 case DisplayWidthHeight:
7502 return nsViewportInfo(aDisplaySize);
7503 case DisplayWidthHeightNoZoom:
7504 return nsViewportInfo(aDisplaySize, /*allowZoom*/ false, /*allowDoubleTapZoom*/ false);
7505 case Unknown:
7506 {
7507 nsAutoString viewport;
7508 GetHeaderData(nsGkAtoms::viewport, viewport);
7509 if (viewport.IsEmpty()) {
7510 // If the docType specifies that we are on a site optimized for mobile,
7511 // then we want to return specially crafted defaults for the viewport info.
7512 nsCOMPtr<nsIDOMDocumentType> docType;
7513 nsresult rv = GetDoctype(getter_AddRefs(docType));
7514 if (NS_SUCCEEDED(rv) && docType) {
7515 nsAutoString docId;
7516 rv = docType->GetPublicId(docId);
7517 if (NS_SUCCEEDED(rv)) {
7518 if ((docId.Find("WAP") != -1) ||
7519 (docId.Find("Mobile") != -1) ||
7520 (docId.Find("WML") != -1))
7521 {
7522 // We're making an assumption that the docType can't change here
7523 mViewportType = DisplayWidthHeight;
7524 return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
7525 }
7526 }
7527 }
7529 nsAutoString handheldFriendly;
7530 GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
7531 if (handheldFriendly.EqualsLiteral("true")) {
7532 mViewportType = DisplayWidthHeight;
7533 return nsViewportInfo(aDisplaySize, /*allowZoom*/true, /*allowDoubleTapZoom*/false);
7534 }
7536 // Bug 940036. This is bad. When FirefoxOS was built, apps installed
7537 // where not using the AsyncPanZoom code. As a result a lot of apps
7538 // in the marketplace does not use it yet and instead are built to
7539 // render correctly in FirefoxOS only. For a smooth transition the above
7540 // code force installed apps to render as if they have a viewport with
7541 // content="width=device-width, height=device-height, user-scalable=no".
7542 // This could be safely remove once it is known that most apps in the
7543 // marketplace use it and that users does not use an old version of the
7544 // app that does not use it.
7545 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
7546 if (docShell && docShell->GetIsApp()) {
7547 nsString uri;
7548 GetDocumentURI(uri);
7549 if (!uri.EqualsLiteral("about:blank")) {
7550 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
7551 NS_LITERAL_CSTRING("DOM"), this,
7552 nsContentUtils::eDOM_PROPERTIES,
7553 "ImplicitMetaViewportTagFallback");
7554 }
7555 mViewportType = DisplayWidthHeightNoZoom;
7556 return nsViewportInfo(aDisplaySize, /*allowZoom*/false, /*allowDoubleTapZoom*/false);
7557 }
7558 }
7560 nsAutoString minScaleStr;
7561 GetHeaderData(nsGkAtoms::viewport_minimum_scale, minScaleStr);
7563 nsresult errorCode;
7564 mScaleMinFloat = LayoutDeviceToScreenScale(minScaleStr.ToFloat(&errorCode));
7566 if (NS_FAILED(errorCode)) {
7567 mScaleMinFloat = kViewportMinScale;
7568 }
7570 mScaleMinFloat = mozilla::clamped(
7571 mScaleMinFloat, kViewportMinScale, kViewportMaxScale);
7573 nsAutoString maxScaleStr;
7574 GetHeaderData(nsGkAtoms::viewport_maximum_scale, maxScaleStr);
7576 // We define a special error code variable for the scale and max scale,
7577 // because they are used later (see the width calculations).
7578 nsresult scaleMaxErrorCode;
7579 mScaleMaxFloat = LayoutDeviceToScreenScale(maxScaleStr.ToFloat(&scaleMaxErrorCode));
7581 if (NS_FAILED(scaleMaxErrorCode)) {
7582 mScaleMaxFloat = kViewportMaxScale;
7583 }
7585 mScaleMaxFloat = mozilla::clamped(
7586 mScaleMaxFloat, kViewportMinScale, kViewportMaxScale);
7588 nsAutoString scaleStr;
7589 GetHeaderData(nsGkAtoms::viewport_initial_scale, scaleStr);
7591 nsresult scaleErrorCode;
7592 mScaleFloat = LayoutDeviceToScreenScale(scaleStr.ToFloat(&scaleErrorCode));
7594 nsAutoString widthStr, heightStr;
7596 GetHeaderData(nsGkAtoms::viewport_height, heightStr);
7597 GetHeaderData(nsGkAtoms::viewport_width, widthStr);
7599 mAutoSize = false;
7601 if (widthStr.EqualsLiteral("device-width")) {
7602 mAutoSize = true;
7603 }
7605 if (widthStr.IsEmpty() &&
7606 (heightStr.EqualsLiteral("device-height") ||
7607 (mScaleFloat.scale == 1.0)))
7608 {
7609 mAutoSize = true;
7610 }
7612 nsresult widthErrorCode, heightErrorCode;
7613 mViewportSize.width = widthStr.ToInteger(&widthErrorCode);
7614 mViewportSize.height = heightStr.ToInteger(&heightErrorCode);
7616 // If width or height has not been set to a valid number by this point,
7617 // fall back to a default value.
7618 mValidWidth = (!widthStr.IsEmpty() && NS_SUCCEEDED(widthErrorCode) && mViewportSize.width > 0);
7619 mValidHeight = (!heightStr.IsEmpty() && NS_SUCCEEDED(heightErrorCode) && mViewportSize.height > 0);
7621 mAllowZoom = true;
7622 nsAutoString userScalable;
7623 GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
7625 if ((userScalable.EqualsLiteral("0")) ||
7626 (userScalable.EqualsLiteral("no")) ||
7627 (userScalable.EqualsLiteral("false"))) {
7628 mAllowZoom = false;
7629 }
7630 mAllowDoubleTapZoom = mAllowZoom;
7632 mScaleStrEmpty = scaleStr.IsEmpty();
7633 mWidthStrEmpty = widthStr.IsEmpty();
7634 mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
7635 mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
7637 mViewportType = Specified;
7638 }
7639 case Specified:
7640 default:
7641 CSSIntSize size = mViewportSize;
7643 if (!mValidWidth) {
7644 if (mValidHeight && !aDisplaySize.IsEmpty()) {
7645 size.width = int32_t(size.height * aDisplaySize.width / aDisplaySize.height);
7646 } else {
7647 size.width = Preferences::GetInt("browser.viewport.desktopWidth",
7648 kViewportDefaultScreenWidth);
7649 }
7650 }
7652 if (!mValidHeight) {
7653 if (!aDisplaySize.IsEmpty()) {
7654 size.height = int32_t(size.width * aDisplaySize.height / aDisplaySize.width);
7655 } else {
7656 size.height = size.width;
7657 }
7658 }
7659 // Now convert the scale into device pixels per CSS pixel.
7660 nsIWidget *widget = nsContentUtils::WidgetForDocument(this);
7661 CSSToLayoutDeviceScale pixelRatio = widget ? widget->GetDefaultScale()
7662 : CSSToLayoutDeviceScale(1.0f);
7663 CSSToScreenScale scaleFloat = mScaleFloat * pixelRatio;
7664 CSSToScreenScale scaleMinFloat = mScaleMinFloat * pixelRatio;
7665 CSSToScreenScale scaleMaxFloat = mScaleMaxFloat * pixelRatio;
7667 if (mAutoSize) {
7668 // aDisplaySize is in screen pixels; convert them to CSS pixels for the viewport size.
7669 CSSToScreenScale defaultPixelScale = pixelRatio * LayoutDeviceToScreenScale(1.0f);
7670 size = mozilla::gfx::RoundedToInt(ScreenSize(aDisplaySize) / defaultPixelScale);
7671 }
7673 size.width = clamped(size.width, kViewportMinSize.width, kViewportMaxSize.width);
7675 // Also recalculate the default zoom, if it wasn't specified in the metadata,
7676 // and the width is specified.
7677 if (mScaleStrEmpty && !mWidthStrEmpty) {
7678 CSSToScreenScale defaultScale(float(aDisplaySize.width) / float(size.width));
7679 scaleFloat = (scaleFloat > defaultScale) ? scaleFloat : defaultScale;
7680 }
7682 size.height = clamped(size.height, kViewportMinSize.height, kViewportMaxSize.height);
7684 // We need to perform a conversion, but only if the initial or maximum
7685 // scale were set explicitly by the user.
7686 if (mValidScaleFloat) {
7687 CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleFloat);
7688 size.width = std::max(size.width, displaySize.width);
7689 size.height = std::max(size.height, displaySize.height);
7690 } else if (mValidMaxScale) {
7691 CSSIntSize displaySize = RoundedToInt(ScreenSize(aDisplaySize) / scaleMaxFloat);
7692 size.width = std::max(size.width, displaySize.width);
7693 size.height = std::max(size.height, displaySize.height);
7694 }
7696 return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
7697 mAutoSize, mAllowZoom, mAllowDoubleTapZoom);
7698 }
7699 }
7701 EventListenerManager*
7702 nsDocument::GetOrCreateListenerManager()
7703 {
7704 if (!mListenerManager) {
7705 mListenerManager =
7706 new EventListenerManager(static_cast<EventTarget*>(this));
7707 SetFlags(NODE_HAS_LISTENERMANAGER);
7708 }
7710 return mListenerManager;
7711 }
7713 EventListenerManager*
7714 nsDocument::GetExistingListenerManager() const
7715 {
7716 return mListenerManager;
7717 }
7719 nsresult
7720 nsDocument::PreHandleEvent(EventChainPreVisitor& aVisitor)
7721 {
7722 aVisitor.mCanHandle = true;
7723 // FIXME! This is a hack to make middle mouse paste working also in Editor.
7724 // Bug 329119
7725 aVisitor.mForceContentDispatch = true;
7727 // Load events must not propagate to |window| object, see bug 335251.
7728 if (aVisitor.mEvent->message != NS_LOAD) {
7729 nsGlobalWindow* window = static_cast<nsGlobalWindow*>(GetWindow());
7730 aVisitor.mParentTarget =
7731 window ? window->GetTargetForEventTargetChain() : nullptr;
7732 }
7733 return NS_OK;
7734 }
7736 NS_IMETHODIMP
7737 nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
7738 {
7739 NS_ENSURE_ARG_POINTER(aReturn);
7740 ErrorResult rv;
7741 *aReturn = nsIDocument::CreateEvent(aEventType, rv).take();
7742 return rv.ErrorCode();
7743 }
7745 already_AddRefed<Event>
7746 nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
7747 {
7748 nsIPresShell *shell = GetShell();
7750 nsPresContext *presContext = nullptr;
7752 if (shell) {
7753 // Retrieve the context
7754 presContext = shell->GetPresContext();
7755 }
7757 // Create event even without presContext.
7758 nsCOMPtr<nsIDOMEvent> ev;
7759 rv = EventDispatcher::CreateEvent(const_cast<nsIDocument*>(this),
7760 presContext, nullptr, aEventType,
7761 getter_AddRefs(ev));
7762 if (!ev) {
7763 return nullptr;
7764 }
7765 WidgetEvent* e = ev->GetInternalNSEvent();
7766 e->mFlags.mBubbles = false;
7767 e->mFlags.mCancelable = false;
7768 return dont_AddRef(ev.forget().take()->InternalDOMEvent());
7769 }
7771 void
7772 nsDocument::FlushPendingNotifications(mozFlushType aType)
7773 {
7774 nsDocumentOnStack dos(this);
7776 // We need to flush the sink for non-HTML documents (because the XML
7777 // parser still does insertion with deferred notifications). We
7778 // also need to flush the sink if this is a layout-related flush, to
7779 // make sure that layout is started as needed. But we can skip that
7780 // part if we have no presshell or if it's already done an initial
7781 // reflow.
7782 if ((!IsHTML() ||
7783 (aType > Flush_ContentAndNotify && mPresShell &&
7784 !mPresShell->DidInitialize())) &&
7785 (mParser || mWeakSink)) {
7786 nsCOMPtr<nsIContentSink> sink;
7787 if (mParser) {
7788 sink = mParser->GetContentSink();
7789 } else {
7790 sink = do_QueryReferent(mWeakSink);
7791 if (!sink) {
7792 mWeakSink = nullptr;
7793 }
7794 }
7795 // Determine if it is safe to flush the sink notifications
7796 // by determining if it safe to flush all the presshells.
7797 if (sink && (aType == Flush_Content || IsSafeToFlush())) {
7798 sink->FlushPendingNotifications(aType);
7799 }
7800 }
7802 // Should we be flushing pending binding constructors in here?
7804 if (aType <= Flush_ContentAndNotify) {
7805 // Nothing to do here
7806 return;
7807 }
7809 // If we have a parent we must flush the parent too to ensure that our
7810 // container is reflowed if its size was changed. But if it's not safe to
7811 // flush ourselves, then don't flush the parent, since that can cause things
7812 // like resizes of our frame's widget, which we can't handle while flushing
7813 // is unsafe.
7814 // Since media queries mean that a size change of our container can
7815 // affect style, we need to promote a style flush on ourself to a
7816 // layout flush on our parent, since we need our container to be the
7817 // correct size to determine the correct style.
7818 if (mParentDocument && IsSafeToFlush()) {
7819 mozFlushType parentType = aType;
7820 if (aType >= Flush_Style)
7821 parentType = std::max(Flush_Layout, aType);
7822 mParentDocument->FlushPendingNotifications(parentType);
7823 }
7825 // We can optimize away getting our presshell and calling
7826 // FlushPendingNotifications on it if we don't need a flush of the sort we're
7827 // looking at. The one exception is if mInFlush is true, because in that
7828 // case we might have set mNeedStyleFlush and mNeedLayoutFlush to false
7829 // already but the presshell hasn't actually done the corresponding work yet.
7830 // So if mInFlush and reentering this code, we need to flush the presshell.
7831 if (mNeedStyleFlush ||
7832 (mNeedLayoutFlush && aType >= Flush_InterruptibleLayout) ||
7833 aType >= Flush_Display ||
7834 mInFlush) {
7835 nsCOMPtr<nsIPresShell> shell = GetShell();
7836 if (shell) {
7837 mNeedStyleFlush = false;
7838 mNeedLayoutFlush = mNeedLayoutFlush && (aType < Flush_InterruptibleLayout);
7839 // mInFlush is a bitfield, so can't us AutoRestore here. But we
7840 // need to keep track of multi-level reentry correctly, so need
7841 // to restore the old mInFlush value.
7842 bool oldInFlush = mInFlush;
7843 mInFlush = true;
7844 shell->FlushPendingNotifications(aType);
7845 mInFlush = oldInFlush;
7846 }
7847 }
7848 }
7850 static bool
7851 Copy(nsIDocument* aDocument, void* aData)
7852 {
7853 nsTArray<nsCOMPtr<nsIDocument> >* resources =
7854 static_cast<nsTArray<nsCOMPtr<nsIDocument> >* >(aData);
7855 resources->AppendElement(aDocument);
7856 return true;
7857 }
7859 void
7860 nsDocument::FlushExternalResources(mozFlushType aType)
7861 {
7862 NS_ASSERTION(aType >= Flush_Style,
7863 "should only need to flush for style or higher in external resources");
7864 if (GetDisplayDocument()) {
7865 return;
7866 }
7867 nsTArray<nsCOMPtr<nsIDocument> > resources;
7868 EnumerateExternalResources(Copy, &resources);
7870 for (uint32_t i = 0; i < resources.Length(); i++) {
7871 resources[i]->FlushPendingNotifications(aType);
7872 }
7873 }
7875 void
7876 nsDocument::SetXMLDeclaration(const char16_t *aVersion,
7877 const char16_t *aEncoding,
7878 const int32_t aStandalone)
7879 {
7880 if (!aVersion || *aVersion == '\0') {
7881 mXMLDeclarationBits = 0;
7882 return;
7883 }
7885 mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
7887 if (aEncoding && *aEncoding != '\0') {
7888 mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
7889 }
7891 if (aStandalone == 1) {
7892 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
7893 XML_DECLARATION_BITS_STANDALONE_YES;
7894 }
7895 else if (aStandalone == 0) {
7896 mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
7897 }
7898 }
7900 void
7901 nsDocument::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
7902 nsAString& aStandalone)
7903 {
7904 aVersion.Truncate();
7905 aEncoding.Truncate();
7906 aStandalone.Truncate();
7908 if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
7909 return;
7910 }
7912 // always until we start supporting 1.1 etc.
7913 aVersion.AssignLiteral("1.0");
7915 if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
7916 // This is what we have stored, not necessarily what was written
7917 // in the original
7918 GetCharacterSet(aEncoding);
7919 }
7921 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
7922 if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
7923 aStandalone.AssignLiteral("yes");
7924 } else {
7925 aStandalone.AssignLiteral("no");
7926 }
7927 }
7928 }
7930 bool
7931 nsDocument::IsScriptEnabled()
7932 {
7933 // If this document is sandboxed without 'allow-scripts'
7934 // script is not enabled
7935 if (mSandboxFlags & SANDBOXED_SCRIPTS) {
7936 return false;
7937 }
7939 nsCOMPtr<nsIScriptSecurityManager> sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
7940 NS_ENSURE_TRUE(sm, false);
7942 nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(GetInnerWindow());
7943 NS_ENSURE_TRUE(globalObject && globalObject->GetGlobalJSObject(), false);
7945 return sm->ScriptAllowed(globalObject->GetGlobalJSObject());
7946 }
7948 nsRadioGroupStruct*
7949 nsDocument::GetRadioGroupInternal(const nsAString& aName) const
7950 {
7951 #ifdef DEBUG
7952 if (IsHTML()) {
7953 nsAutoString lcName;
7954 ToLowerCase(aName, lcName);
7955 MOZ_ASSERT(aName == lcName);
7956 }
7957 #endif
7959 nsRadioGroupStruct* radioGroup;
7960 if (!mRadioGroups.Get(aName, &radioGroup)) {
7961 return nullptr;
7962 }
7964 return radioGroup;
7965 }
7967 nsRadioGroupStruct*
7968 nsDocument::GetRadioGroup(const nsAString& aName) const
7969 {
7970 nsAutoString tmKey(aName);
7971 if (IsHTML()) {
7972 ToLowerCase(tmKey); //should case-insensitive.
7973 }
7975 return GetRadioGroupInternal(tmKey);
7976 }
7978 nsRadioGroupStruct*
7979 nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
7980 {
7981 nsAutoString tmKey(aName);
7982 if (IsHTML()) {
7983 ToLowerCase(tmKey); //should case-insensitive.
7984 }
7986 if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) {
7987 return radioGroup;
7988 }
7990 nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct());
7991 mRadioGroups.Put(tmKey, newRadioGroup);
7993 return newRadioGroup.forget();
7994 }
7996 void
7997 nsDocument::SetCurrentRadioButton(const nsAString& aName,
7998 HTMLInputElement* aRadio)
7999 {
8000 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8001 radioGroup->mSelectedRadioButton = aRadio;
8002 }
8004 HTMLInputElement*
8005 nsDocument::GetCurrentRadioButton(const nsAString& aName)
8006 {
8007 return GetOrCreateRadioGroup(aName)->mSelectedRadioButton;
8008 }
8010 NS_IMETHODIMP
8011 nsDocument::GetNextRadioButton(const nsAString& aName,
8012 const bool aPrevious,
8013 HTMLInputElement* aFocusedRadio,
8014 HTMLInputElement** aRadioOut)
8015 {
8016 // XXX Can we combine the HTML radio button method impls of
8017 // nsDocument and nsHTMLFormControl?
8018 // XXX Why is HTML radio button stuff in nsDocument, as
8019 // opposed to nsHTMLDocument?
8020 *aRadioOut = nullptr;
8022 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8024 // Return the radio button relative to the focused radio button.
8025 // If no radio is focused, get the radio relative to the selected one.
8026 nsRefPtr<HTMLInputElement> currentRadio;
8027 if (aFocusedRadio) {
8028 currentRadio = aFocusedRadio;
8029 }
8030 else {
8031 currentRadio = radioGroup->mSelectedRadioButton;
8032 if (!currentRadio) {
8033 return NS_ERROR_FAILURE;
8034 }
8035 }
8036 int32_t index = radioGroup->mRadioButtons.IndexOf(currentRadio);
8037 if (index < 0) {
8038 return NS_ERROR_FAILURE;
8039 }
8041 int32_t numRadios = radioGroup->mRadioButtons.Count();
8042 nsRefPtr<HTMLInputElement> radio;
8043 do {
8044 if (aPrevious) {
8045 if (--index < 0) {
8046 index = numRadios -1;
8047 }
8048 }
8049 else if (++index >= numRadios) {
8050 index = 0;
8051 }
8052 NS_ASSERTION(static_cast<nsGenericHTMLFormElement*>(radioGroup->mRadioButtons[index])->IsHTML(nsGkAtoms::input),
8053 "mRadioButtons holding a non-radio button");
8054 radio = static_cast<HTMLInputElement*>(radioGroup->mRadioButtons[index]);
8055 } while (radio->Disabled() && radio != currentRadio);
8057 radio.forget(aRadioOut);
8058 return NS_OK;
8059 }
8061 void
8062 nsDocument::AddToRadioGroup(const nsAString& aName,
8063 nsIFormControl* aRadio)
8064 {
8065 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8066 radioGroup->mRadioButtons.AppendObject(aRadio);
8068 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8069 NS_ASSERTION(element, "radio controls have to be content elements");
8070 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8071 radioGroup->mRequiredRadioCount++;
8072 }
8073 }
8075 void
8076 nsDocument::RemoveFromRadioGroup(const nsAString& aName,
8077 nsIFormControl* aRadio)
8078 {
8079 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8080 radioGroup->mRadioButtons.RemoveObject(aRadio);
8082 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8083 NS_ASSERTION(element, "radio controls have to be content elements");
8084 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8085 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8086 "mRequiredRadioCount about to wrap below 0!");
8087 radioGroup->mRequiredRadioCount--;
8088 }
8089 }
8091 NS_IMETHODIMP
8092 nsDocument::WalkRadioGroup(const nsAString& aName,
8093 nsIRadioVisitor* aVisitor,
8094 bool aFlushContent)
8095 {
8096 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8098 for (int i = 0; i < radioGroup->mRadioButtons.Count(); i++) {
8099 if (!aVisitor->Visit(radioGroup->mRadioButtons[i])) {
8100 return NS_OK;
8101 }
8102 }
8104 return NS_OK;
8105 }
8107 uint32_t
8108 nsDocument::GetRequiredRadioCount(const nsAString& aName) const
8109 {
8110 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8111 return radioGroup ? radioGroup->mRequiredRadioCount : 0;
8112 }
8114 void
8115 nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio)
8116 {
8117 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8119 nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
8120 NS_ASSERTION(element, "radio controls have to be content elements");
8121 if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
8122 radioGroup->mRequiredRadioCount++;
8123 } else {
8124 NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
8125 "mRequiredRadioCount about to wrap below 0!");
8126 radioGroup->mRequiredRadioCount--;
8127 }
8128 }
8130 bool
8131 nsDocument::GetValueMissingState(const nsAString& aName) const
8132 {
8133 nsRadioGroupStruct* radioGroup = GetRadioGroup(aName);
8134 return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
8135 }
8137 void
8138 nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
8139 {
8140 nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
8141 radioGroup->mGroupSuffersFromValueMissing = aValue;
8142 }
8144 void
8145 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
8146 {
8147 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
8148 PRTime modDate = 0;
8149 nsresult rv;
8151 if (httpChannel) {
8152 nsAutoCString tmp;
8153 rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"),
8154 tmp);
8156 if (NS_SUCCEEDED(rv)) {
8157 PRTime time;
8158 PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
8159 if (st == PR_SUCCESS) {
8160 modDate = time;
8161 }
8162 }
8164 // The misspelled key 'referer' is as per the HTTP spec
8165 rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
8166 mReferrer);
8167 if (NS_FAILED(rv)) {
8168 mReferrer.Truncate();
8169 }
8171 static const char *const headers[] = {
8172 "default-style",
8173 "content-style-type",
8174 "content-language",
8175 "content-disposition",
8176 "refresh",
8177 "x-dns-prefetch-control",
8178 "x-frame-options",
8179 // add more http headers if you need
8180 // XXXbz don't add content-location support without reading bug
8181 // 238654 and its dependencies/dups first.
8182 0
8183 };
8185 nsAutoCString headerVal;
8186 const char *const *name = headers;
8187 while (*name) {
8188 rv =
8189 httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
8190 if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
8191 nsCOMPtr<nsIAtom> key = do_GetAtom(*name);
8192 SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
8193 }
8194 ++name;
8195 }
8196 } else {
8197 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
8198 if (fileChannel) {
8199 nsCOMPtr<nsIFile> file;
8200 fileChannel->GetFile(getter_AddRefs(file));
8201 if (file) {
8202 PRTime msecs;
8203 rv = file->GetLastModifiedTime(&msecs);
8205 if (NS_SUCCEEDED(rv)) {
8206 modDate = msecs * int64_t(PR_USEC_PER_MSEC);
8207 }
8208 }
8209 } else {
8210 nsAutoCString contentDisp;
8211 rv = aChannel->GetContentDispositionHeader(contentDisp);
8212 if (NS_SUCCEEDED(rv)) {
8213 SetHeaderData(nsGkAtoms::headerContentDisposition,
8214 NS_ConvertASCIItoUTF16(contentDisp));
8215 }
8216 }
8217 }
8219 if (modDate == 0) {
8220 // We got nothing from our attempt to ask nsIFileChannel and
8221 // nsIHttpChannel for the last modified time. Return the current
8222 // time.
8223 modDate = PR_Now();
8224 }
8226 mLastModified.Truncate();
8227 if (modDate != 0) {
8228 PRExplodedTime prtime;
8229 PR_ExplodeTime(modDate, PR_LocalTimeParameters, &prtime);
8230 // "MM/DD/YYYY hh:mm:ss"
8231 char formatedTime[24];
8232 if (PR_snprintf(formatedTime, sizeof(formatedTime),
8233 "%02ld/%02ld/%04hd %02ld:%02ld:%02ld",
8234 prtime.tm_month + 1, prtime.tm_mday, prtime.tm_year,
8235 prtime.tm_hour , prtime.tm_min, prtime.tm_sec)) {
8236 CopyASCIItoUTF16(nsDependentCString(formatedTime), mLastModified);
8237 }
8238 }
8239 }
8241 nsresult
8242 nsDocument::CreateElem(const nsAString& aName, nsIAtom *aPrefix, int32_t aNamespaceID,
8243 nsIContent **aResult)
8244 {
8245 #ifdef DEBUG
8246 nsAutoString qName;
8247 if (aPrefix) {
8248 aPrefix->ToString(qName);
8249 qName.Append(':');
8250 }
8251 qName.Append(aName);
8253 // Note: "a:b:c" is a valid name in non-namespaces XML, and
8254 // nsDocument::CreateElement can call us with such a name and no prefix,
8255 // which would cause an error if we just used true here.
8256 bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
8257 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
8258 "Don't pass invalid prefixes to nsDocument::CreateElem, "
8259 "check caller.");
8260 #endif
8262 *aResult = nullptr;
8264 nsCOMPtr<nsINodeInfo> nodeInfo;
8265 mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID,
8266 nsIDOMNode::ELEMENT_NODE,
8267 getter_AddRefs(nodeInfo));
8268 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
8270 nsCOMPtr<Element> element;
8271 nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
8272 NOT_FROM_PARSER);
8273 element.forget(aResult);
8274 return rv;
8275 }
8277 bool
8278 nsDocument::IsSafeToFlush() const
8279 {
8280 nsIPresShell* shell = GetShell();
8281 if (!shell)
8282 return true;
8284 return shell->IsSafeToFlush();
8285 }
8287 void
8288 nsDocument::Sanitize()
8289 {
8290 // Sanitize the document by resetting all password fields and any form
8291 // fields with autocomplete=off to their default values. We do this now,
8292 // instead of when the presentation is restored, to offer some protection
8293 // in case there is ever an exploit that allows a cached document to be
8294 // accessed from a different document.
8296 // First locate all input elements, regardless of whether they are
8297 // in a form, and reset the password and autocomplete=off elements.
8299 nsRefPtr<nsContentList> nodes = GetElementsByTagName(NS_LITERAL_STRING("input"));
8301 nsCOMPtr<nsIContent> item;
8302 nsAutoString value;
8304 uint32_t length = nodes->Length(true);
8305 for (uint32_t i = 0; i < length; ++i) {
8306 NS_ASSERTION(nodes->Item(i), "null item in node list!");
8308 nsRefPtr<HTMLInputElement> input = HTMLInputElement::FromContentOrNull(nodes->Item(i));
8309 if (!input)
8310 continue;
8312 bool resetValue = false;
8314 input->GetAttribute(NS_LITERAL_STRING("autocomplete"), value);
8315 if (value.LowerCaseEqualsLiteral("off")) {
8316 resetValue = true;
8317 } else {
8318 input->GetType(value);
8319 if (value.LowerCaseEqualsLiteral("password"))
8320 resetValue = true;
8321 }
8323 if (resetValue) {
8324 input->Reset();
8325 }
8326 }
8328 // Now locate all _form_ elements that have autocomplete=off and reset them
8329 nodes = GetElementsByTagName(NS_LITERAL_STRING("form"));
8331 length = nodes->Length(true);
8332 for (uint32_t i = 0; i < length; ++i) {
8333 NS_ASSERTION(nodes->Item(i), "null item in nodelist");
8335 nsCOMPtr<nsIDOMHTMLFormElement> form = do_QueryInterface(nodes->Item(i));
8336 if (!form)
8337 continue;
8339 nodes->Item(i)->AsElement()->GetAttr(kNameSpaceID_None,
8340 nsGkAtoms::autocomplete, value);
8341 if (value.LowerCaseEqualsLiteral("off"))
8342 form->Reset();
8343 }
8344 }
8346 struct SubDocEnumArgs
8347 {
8348 nsIDocument::nsSubDocEnumFunc callback;
8349 void *data;
8350 };
8352 static PLDHashOperator
8353 SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr,
8354 uint32_t number, void *arg)
8355 {
8356 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
8357 SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg);
8359 nsIDocument *subdoc = entry->mSubDocument;
8360 bool next = subdoc ? args->callback(subdoc, args->data) : true;
8362 return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
8363 }
8365 void
8366 nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
8367 {
8368 if (mSubDocuments) {
8369 SubDocEnumArgs args = { aCallback, aData };
8370 PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args);
8371 }
8372 }
8374 static PLDHashOperator
8375 CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr,
8376 uint32_t number, void *arg)
8377 {
8378 SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
8379 bool *canCacheArg = static_cast<bool*>(arg);
8381 nsIDocument *subdoc = entry->mSubDocument;
8383 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
8384 bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
8385 if (!canCache) {
8386 *canCacheArg = false;
8387 return PL_DHASH_STOP;
8388 }
8390 return PL_DHASH_NEXT;
8391 }
8393 #ifdef DEBUG_bryner
8394 #define DEBUG_PAGE_CACHE
8395 #endif
8397 bool
8398 nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
8399 {
8400 if (EventHandlingSuppressed()) {
8401 return false;
8402 }
8404 nsPIDOMWindow* win = GetInnerWindow();
8405 if (win && win->TimeoutSuspendCount()) {
8406 return false;
8407 }
8409 // Check our event listener manager for unload/beforeunload listeners.
8410 nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
8411 if (piTarget) {
8412 EventListenerManager* manager = piTarget->GetExistingListenerManager();
8413 if (manager && manager->HasUnloadListeners()) {
8414 return false;
8415 }
8416 }
8418 // Check if we have pending network requests
8419 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8420 if (loadGroup) {
8421 nsCOMPtr<nsISimpleEnumerator> requests;
8422 loadGroup->GetRequests(getter_AddRefs(requests));
8424 bool hasMore = false;
8426 // We want to bail out if we have any requests other than aNewRequest (or
8427 // in the case when aNewRequest is a part of a multipart response the base
8428 // channel the multipart response is coming in on).
8429 nsCOMPtr<nsIChannel> baseChannel;
8430 nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
8431 if (part) {
8432 part->GetBaseChannel(getter_AddRefs(baseChannel));
8433 }
8435 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8436 nsCOMPtr<nsISupports> elem;
8437 requests->GetNext(getter_AddRefs(elem));
8439 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8440 if (request && request != aNewRequest && request != baseChannel) {
8441 #ifdef DEBUG_PAGE_CACHE
8442 nsAutoCString requestName, docSpec;
8443 request->GetName(requestName);
8444 if (mDocumentURI)
8445 mDocumentURI->GetSpec(docSpec);
8447 printf("document %s has request %s\n",
8448 docSpec.get(), requestName.get());
8449 #endif
8450 return false;
8451 }
8452 }
8453 }
8455 // Check if we have running offline storage transactions
8456 quota::QuotaManager* quotaManager =
8457 win ? quota::QuotaManager::Get() : nullptr;
8458 if (quotaManager && quotaManager->HasOpenTransactions(win)) {
8459 return false;
8460 }
8462 #ifdef MOZ_MEDIA_NAVIGATOR
8463 // Check if we have active GetUserMedia use
8464 if (MediaManager::Exists() && win &&
8465 MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
8466 return false;
8467 }
8468 #endif // MOZ_MEDIA_NAVIGATOR
8470 #ifdef MOZ_WEBRTC
8471 // Check if we have active PeerConnections
8472 nsCOMPtr<IPeerConnectionManager> pcManager =
8473 do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
8475 if (pcManager && win) {
8476 bool active;
8477 pcManager->HasActivePeerConnection(win->WindowID(), &active);
8478 if (active) {
8479 return false;
8480 }
8481 }
8482 #endif // MOZ_WEBRTC
8484 bool canCache = true;
8485 if (mSubDocuments)
8486 PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache);
8488 return canCache;
8489 }
8491 void
8492 nsDocument::Destroy()
8493 {
8494 // The ContentViewer wants to release the document now. So, tell our content
8495 // to drop any references to the document so that it can be destroyed.
8496 if (mIsGoingAway)
8497 return;
8499 mIsGoingAway = true;
8501 RemovedFromDocShell();
8503 bool oldVal = mInUnlinkOrDeletion;
8504 mInUnlinkOrDeletion = true;
8505 uint32_t i, count = mChildren.ChildCount();
8506 for (i = 0; i < count; ++i) {
8507 mChildren.ChildAt(i)->DestroyContent();
8508 }
8509 mInUnlinkOrDeletion = oldVal;
8511 mLayoutHistoryState = nullptr;
8513 // Shut down our external resource map. We might not need this for
8514 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
8515 // tearing down all those frame trees right now is the right thing to do.
8516 mExternalResourceMap.Shutdown();
8518 mRegistry = nullptr;
8520 // XXX We really should let cycle collection do this, but that currently still
8521 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
8522 ReleaseWrapper(static_cast<nsINode*>(this));
8523 }
8525 void
8526 nsDocument::RemovedFromDocShell()
8527 {
8528 if (mRemovedFromDocShell)
8529 return;
8531 mRemovedFromDocShell = true;
8532 EnumerateFreezableElements(NotifyActivityChanged, nullptr);
8534 uint32_t i, count = mChildren.ChildCount();
8535 for (i = 0; i < count; ++i) {
8536 mChildren.ChildAt(i)->SaveSubtreeState();
8537 }
8538 }
8540 already_AddRefed<nsILayoutHistoryState>
8541 nsDocument::GetLayoutHistoryState() const
8542 {
8543 nsCOMPtr<nsILayoutHistoryState> state;
8544 if (!mScriptGlobalObject) {
8545 state = mLayoutHistoryState;
8546 } else {
8547 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
8548 if (docShell) {
8549 docShell->GetLayoutHistoryState(getter_AddRefs(state));
8550 }
8551 }
8553 return state.forget();
8554 }
8556 void
8557 nsDocument::EnsureOnloadBlocker()
8558 {
8559 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8560 // -- it's not ours.
8561 if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
8562 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8563 if (loadGroup) {
8564 // Check first to see if mOnloadBlocker is in the loadgroup.
8565 nsCOMPtr<nsISimpleEnumerator> requests;
8566 loadGroup->GetRequests(getter_AddRefs(requests));
8568 bool hasMore = false;
8569 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
8570 nsCOMPtr<nsISupports> elem;
8571 requests->GetNext(getter_AddRefs(elem));
8572 nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
8573 if (request && request == mOnloadBlocker) {
8574 return;
8575 }
8576 }
8578 // Not in the loadgroup, so add it.
8579 loadGroup->AddRequest(mOnloadBlocker, nullptr);
8580 }
8581 }
8582 }
8584 void
8585 nsDocument::AsyncBlockOnload()
8586 {
8587 while (mAsyncOnloadBlockCount) {
8588 --mAsyncOnloadBlockCount;
8589 BlockOnload();
8590 }
8591 }
8593 void
8594 nsDocument::BlockOnload()
8595 {
8596 if (mDisplayDocument) {
8597 mDisplayDocument->BlockOnload();
8598 return;
8599 }
8601 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8602 // -- it's not ours.
8603 if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
8604 if (!nsContentUtils::IsSafeToRunScript()) {
8605 // Because AddRequest may lead to OnStateChange calls in chrome,
8606 // block onload only when there are no script blockers.
8607 ++mAsyncOnloadBlockCount;
8608 if (mAsyncOnloadBlockCount == 1) {
8609 bool success = nsContentUtils::AddScriptRunner(
8610 NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
8612 // The script runner shouldn't fail to add. But if somebody broke
8613 // something and it does, we'll thrash at 100% cpu forever. The best
8614 // response is just to ignore the onload blocking request. See bug 579535.
8615 if (!success) {
8616 NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
8617 mAsyncOnloadBlockCount = 0;
8618 }
8619 }
8620 return;
8621 }
8622 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8623 if (loadGroup) {
8624 loadGroup->AddRequest(mOnloadBlocker, nullptr);
8625 }
8626 }
8627 ++mOnloadBlockCount;
8628 }
8630 void
8631 nsDocument::UnblockOnload(bool aFireSync)
8632 {
8633 if (mDisplayDocument) {
8634 mDisplayDocument->UnblockOnload(aFireSync);
8635 return;
8636 }
8638 if (mOnloadBlockCount == 0 && mAsyncOnloadBlockCount == 0) {
8639 NS_NOTREACHED("More UnblockOnload() calls than BlockOnload() calls; dropping call");
8640 return;
8641 }
8643 --mOnloadBlockCount;
8645 if (mOnloadBlockCount == 0) {
8646 if (mScriptGlobalObject) {
8647 // Only manipulate the loadgroup in this case, because if mScriptGlobalObject
8648 // is null, it's not ours.
8649 if (aFireSync && mAsyncOnloadBlockCount == 0) {
8650 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
8651 ++mOnloadBlockCount;
8652 DoUnblockOnload();
8653 } else {
8654 PostUnblockOnloadEvent();
8655 }
8656 } else if (mIsBeingUsedAsImage) {
8657 // To correctly unblock onload for a document that contains an SVG
8658 // image, we need to know when all of the SVG document's resources are
8659 // done loading, in a way comparable to |window.onload|. We fire this
8660 // event to indicate that the SVG should be considered fully loaded.
8661 // Because scripting is disabled on SVG-as-image documents, this event
8662 // is not accessible to content authors. (See bug 837135.)
8663 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
8664 new AsyncEventDispatcher(this,
8665 NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"),
8666 false,
8667 false);
8668 asyncDispatcher->PostDOMEvent();
8669 }
8670 }
8671 }
8673 class nsUnblockOnloadEvent : public nsRunnable {
8674 public:
8675 nsUnblockOnloadEvent(nsDocument *doc) : mDoc(doc) {}
8676 NS_IMETHOD Run() {
8677 mDoc->DoUnblockOnload();
8678 return NS_OK;
8679 }
8680 private:
8681 nsRefPtr<nsDocument> mDoc;
8682 };
8684 void
8685 nsDocument::PostUnblockOnloadEvent()
8686 {
8687 nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
8688 nsresult rv = NS_DispatchToCurrentThread(evt);
8689 if (NS_SUCCEEDED(rv)) {
8690 // Stabilize block count so we don't post more events while this one is up
8691 ++mOnloadBlockCount;
8692 } else {
8693 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
8694 }
8695 }
8697 void
8698 nsDocument::DoUnblockOnload()
8699 {
8700 NS_PRECONDITION(!mDisplayDocument,
8701 "Shouldn't get here for resource document");
8702 NS_PRECONDITION(mOnloadBlockCount != 0,
8703 "Shouldn't have a count of zero here, since we stabilized in "
8704 "PostUnblockOnloadEvent");
8706 --mOnloadBlockCount;
8708 if (mOnloadBlockCount != 0) {
8709 // We blocked again after the last unblock. Nothing to do here. We'll
8710 // post a new event when we unblock again.
8711 return;
8712 }
8714 if (mAsyncOnloadBlockCount != 0) {
8715 // We need to wait until the async onload block has been handled.
8716 PostUnblockOnloadEvent();
8717 }
8719 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
8720 // -- it's not ours.
8721 if (mScriptGlobalObject) {
8722 nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
8723 if (loadGroup) {
8724 loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
8725 }
8726 }
8727 }
8729 nsIContent*
8730 nsDocument::GetContentInThisDocument(nsIFrame* aFrame) const
8731 {
8732 for (nsIFrame* f = aFrame; f;
8733 f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
8734 nsIContent* content = f->GetContent();
8735 if (!content || content->IsInAnonymousSubtree())
8736 continue;
8738 if (content->OwnerDoc() == this) {
8739 return content;
8740 }
8741 // We must be in a subdocument so jump directly to the root frame.
8742 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
8743 // the containing document.
8744 f = f->PresContext()->GetPresShell()->GetRootFrame();
8745 }
8747 return nullptr;
8748 }
8750 void
8751 nsDocument::DispatchPageTransition(EventTarget* aDispatchTarget,
8752 const nsAString& aType,
8753 bool aPersisted)
8754 {
8755 if (aDispatchTarget) {
8756 nsCOMPtr<nsIDOMEvent> event;
8757 CreateEvent(NS_LITERAL_STRING("pagetransition"), getter_AddRefs(event));
8758 nsCOMPtr<nsIDOMPageTransitionEvent> ptEvent = do_QueryInterface(event);
8759 if (ptEvent && NS_SUCCEEDED(ptEvent->InitPageTransitionEvent(aType, true,
8760 true,
8761 aPersisted))) {
8762 event->SetTrusted(true);
8763 event->SetTarget(this);
8764 EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event,
8765 nullptr, nullptr);
8766 }
8767 }
8768 }
8770 static bool
8771 NotifyPageShow(nsIDocument* aDocument, void* aData)
8772 {
8773 const bool* aPersistedPtr = static_cast<const bool*>(aData);
8774 aDocument->OnPageShow(*aPersistedPtr, nullptr);
8775 return true;
8776 }
8778 void
8779 nsDocument::OnPageShow(bool aPersisted,
8780 EventTarget* aDispatchStartTarget)
8781 {
8782 mVisible = true;
8784 EnumerateFreezableElements(NotifyActivityChanged, nullptr);
8785 EnumerateExternalResources(NotifyPageShow, &aPersisted);
8787 Element* root = GetRootElement();
8788 if (aPersisted && root) {
8789 // Send out notifications that our <link> elements are attached.
8790 nsRefPtr<nsContentList> links = NS_GetContentList(root,
8791 kNameSpaceID_XHTML,
8792 NS_LITERAL_STRING("link"));
8794 uint32_t linkCount = links->Length(true);
8795 for (uint32_t i = 0; i < linkCount; ++i) {
8796 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
8797 }
8798 }
8800 // See nsIDocument
8801 if (!aDispatchStartTarget) {
8802 // Set mIsShowing before firing events, in case those event handlers
8803 // move us around.
8804 mIsShowing = true;
8805 }
8807 if (mAnimationController) {
8808 mAnimationController->OnPageShow();
8809 }
8811 if (aPersisted) {
8812 SetImagesNeedAnimating(true);
8813 }
8815 UpdateVisibilityState();
8817 nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8818 if (!target) {
8819 target = do_QueryInterface(GetWindow());
8820 }
8822 // Dispatch observer notification to notify observers page is shown.
8823 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
8824 nsIPrincipal *principal = GetPrincipal();
8825 os->NotifyObservers(static_cast<nsIDocument*>(this),
8826 nsContentUtils::IsSystemPrincipal(principal) ?
8827 "chrome-page-shown" :
8828 "content-page-shown",
8829 nullptr);
8832 DispatchPageTransition(target, NS_LITERAL_STRING("pageshow"), aPersisted);
8833 }
8835 static bool
8836 NotifyPageHide(nsIDocument* aDocument, void* aData)
8837 {
8838 const bool* aPersistedPtr = static_cast<const bool*>(aData);
8839 aDocument->OnPageHide(*aPersistedPtr, nullptr);
8840 return true;
8841 }
8843 static void
8844 DispatchFullScreenChange(nsIDocument* aTarget)
8845 {
8846 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
8847 new AsyncEventDispatcher(aTarget,
8848 NS_LITERAL_STRING("mozfullscreenchange"),
8849 true,
8850 false);
8851 asyncDispatcher->PostDOMEvent();
8852 }
8854 void
8855 nsDocument::OnPageHide(bool aPersisted,
8856 EventTarget* aDispatchStartTarget)
8857 {
8858 // Send out notifications that our <link> elements are detached,
8859 // but only if this is not a full unload.
8860 Element* root = GetRootElement();
8861 if (aPersisted && root) {
8862 nsRefPtr<nsContentList> links = NS_GetContentList(root,
8863 kNameSpaceID_XHTML,
8864 NS_LITERAL_STRING("link"));
8866 uint32_t linkCount = links->Length(true);
8867 for (uint32_t i = 0; i < linkCount; ++i) {
8868 static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
8869 }
8870 }
8872 // See nsIDocument
8873 if (!aDispatchStartTarget) {
8874 // Set mIsShowing before firing events, in case those event handlers
8875 // move us around.
8876 mIsShowing = false;
8877 }
8879 if (mAnimationController) {
8880 mAnimationController->OnPageHide();
8881 }
8883 if (aPersisted) {
8884 SetImagesNeedAnimating(false);
8885 }
8887 MozExitPointerLock();
8889 // Now send out a PageHide event.
8890 nsCOMPtr<EventTarget> target = aDispatchStartTarget;
8891 if (!target) {
8892 target = do_QueryInterface(GetWindow());
8893 }
8895 // Dispatch observer notification to notify observers page is hidden.
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-hidden" :
8901 "content-page-hidden",
8902 nullptr);
8904 DispatchPageTransition(target, NS_LITERAL_STRING("pagehide"), aPersisted);
8906 mVisible = false;
8908 UpdateVisibilityState();
8910 EnumerateExternalResources(NotifyPageHide, &aPersisted);
8911 EnumerateFreezableElements(NotifyActivityChanged, nullptr);
8913 if (IsFullScreenDoc()) {
8914 // If this document was fullscreen, we should exit fullscreen in this
8915 // doctree branch. This ensures that if the user navigates while in
8916 // fullscreen mode we don't leave its still visible ancestor documents
8917 // in fullscreen mode. So exit fullscreen in the document's fullscreen
8918 // root document, as this will exit fullscreen in all the root's
8919 // descendant documents. Note that documents are removed from the
8920 // doctree by the time OnPageHide() is called, so we must store a
8921 // reference to the root (in nsDocument::mFullscreenRoot) since we can't
8922 // just traverse the doctree to get the root.
8923 nsIDocument::ExitFullscreen(this, /* async */ false);
8925 // Since the document is removed from the doctree before OnPageHide() is
8926 // called, ExitFullscreen() can't traverse from the root down to *this*
8927 // document, so we must manually call CleanupFullscreenState() below too.
8928 // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
8929 // so we *must* call it after ExitFullscreen(), not before.
8930 // OnPageHide() is called in every hidden (i.e. descendant) document,
8931 // so calling CleanupFullscreenState() here will ensure all hidden
8932 // documents have their fullscreen state reset.
8933 CleanupFullscreenState();
8935 // If anyone was listening to this document's state, advertizing the state
8936 // change would be the least of the politeness.
8937 DispatchFullScreenChange(this);
8938 }
8939 }
8941 void
8942 nsDocument::WillDispatchMutationEvent(nsINode* aTarget)
8943 {
8944 NS_ASSERTION(mSubtreeModifiedDepth != 0 ||
8945 mSubtreeModifiedTargets.Count() == 0,
8946 "mSubtreeModifiedTargets not cleared after dispatching?");
8947 ++mSubtreeModifiedDepth;
8948 if (aTarget) {
8949 // MayDispatchMutationEvent is often called just before this method,
8950 // so it has already appended the node to mSubtreeModifiedTargets.
8951 int32_t count = mSubtreeModifiedTargets.Count();
8952 if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
8953 mSubtreeModifiedTargets.AppendObject(aTarget);
8954 }
8955 }
8956 }
8958 void
8959 nsDocument::MutationEventDispatched(nsINode* aTarget)
8960 {
8961 --mSubtreeModifiedDepth;
8962 if (mSubtreeModifiedDepth == 0) {
8963 int32_t count = mSubtreeModifiedTargets.Count();
8964 if (!count) {
8965 return;
8966 }
8968 nsCOMPtr<nsPIDOMWindow> window;
8969 window = do_QueryInterface(GetWindow());
8970 if (window &&
8971 !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
8972 mSubtreeModifiedTargets.Clear();
8973 return;
8974 }
8976 nsCOMArray<nsINode> realTargets;
8977 for (int32_t i = 0; i < count; ++i) {
8978 nsINode* possibleTarget = mSubtreeModifiedTargets[i];
8979 nsCOMPtr<nsIContent> content = do_QueryInterface(possibleTarget);
8980 if (content && content->ChromeOnlyAccess()) {
8981 continue;
8982 }
8984 nsINode* commonAncestor = nullptr;
8985 int32_t realTargetCount = realTargets.Count();
8986 for (int32_t j = 0; j < realTargetCount; ++j) {
8987 commonAncestor =
8988 nsContentUtils::GetCommonAncestor(possibleTarget, realTargets[j]);
8989 if (commonAncestor) {
8990 realTargets.ReplaceObjectAt(commonAncestor, j);
8991 break;
8992 }
8993 }
8994 if (!commonAncestor) {
8995 realTargets.AppendObject(possibleTarget);
8996 }
8997 }
8999 mSubtreeModifiedTargets.Clear();
9001 int32_t realTargetCount = realTargets.Count();
9002 for (int32_t k = 0; k < realTargetCount; ++k) {
9003 InternalMutationEvent mutation(true, NS_MUTATION_SUBTREEMODIFIED);
9004 (new AsyncEventDispatcher(realTargets[k], mutation))->
9005 RunDOMEventWhenSafe();
9006 }
9007 }
9008 }
9010 void
9011 nsDocument::AddStyleRelevantLink(Link* aLink)
9012 {
9013 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9014 #ifdef DEBUG
9015 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9016 NS_ASSERTION(!entry, "Document already knows about this Link!");
9017 mStyledLinksCleared = false;
9018 #endif
9019 (void)mStyledLinks.PutEntry(aLink);
9020 }
9022 void
9023 nsDocument::ForgetLink(Link* aLink)
9024 {
9025 NS_ASSERTION(aLink, "Passing in a null link. Expect crashes RSN!");
9026 #ifdef DEBUG
9027 nsPtrHashKey<Link>* entry = mStyledLinks.GetEntry(aLink);
9028 NS_ASSERTION(entry || mStyledLinksCleared,
9029 "Document knows nothing about this Link!");
9030 #endif
9031 (void)mStyledLinks.RemoveEntry(aLink);
9032 }
9034 void
9035 nsDocument::DestroyElementMaps()
9036 {
9037 #ifdef DEBUG
9038 mStyledLinksCleared = true;
9039 #endif
9040 mStyledLinks.Clear();
9041 mIdentifierMap.Clear();
9042 ++mExpandoAndGeneration.generation;
9043 }
9045 static
9046 PLDHashOperator
9047 EnumerateStyledLinks(nsPtrHashKey<Link>* aEntry, void* aArray)
9048 {
9049 LinkArray* array = static_cast<LinkArray*>(aArray);
9050 (void)array->AppendElement(aEntry->GetKey());
9051 return PL_DHASH_NEXT;
9052 }
9054 void
9055 nsDocument::RefreshLinkHrefs()
9056 {
9057 // Get a list of all links we know about. We will reset them, which will
9058 // remove them from the document, so we need a copy of what is in the
9059 // hashtable.
9060 LinkArray linksToNotify(mStyledLinks.Count());
9061 (void)mStyledLinks.EnumerateEntries(EnumerateStyledLinks, &linksToNotify);
9063 // Reset all of our styled links.
9064 nsAutoScriptBlocker scriptBlocker;
9065 for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
9066 linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
9067 }
9068 }
9070 nsresult
9071 nsDocument::CloneDocHelper(nsDocument* clone) const
9072 {
9073 clone->mIsStaticDocument = mCreatingStaticClone;
9075 // Init document
9076 nsresult rv = clone->Init();
9077 NS_ENSURE_SUCCESS(rv, rv);
9079 // Set URI/principal
9080 clone->nsDocument::SetDocumentURI(nsIDocument::GetDocumentURI());
9081 clone->SetChromeXHRDocURI(mChromeXHRDocURI);
9082 // Must set the principal first, since SetBaseURI checks it.
9083 clone->SetPrincipal(NodePrincipal());
9084 clone->mDocumentBaseURI = mDocumentBaseURI;
9085 clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
9087 if (mCreatingStaticClone) {
9088 nsCOMPtr<nsILoadGroup> loadGroup;
9090 // |mDocumentContainer| is the container of the document that is being
9091 // created and not the original container. See CreateStaticClone function().
9092 nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
9093 if (docLoader) {
9094 docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
9095 }
9096 nsCOMPtr<nsIChannel> channel = GetChannel();
9097 if (channel && loadGroup) {
9098 clone->Reset(channel, loadGroup);
9099 } else {
9100 nsIURI* uri = static_cast<const nsIDocument*>(this)->GetDocumentURI();
9101 if (uri) {
9102 clone->ResetToURI(uri, loadGroup, NodePrincipal());
9103 }
9104 }
9105 clone->SetContainer(mDocumentContainer);
9106 }
9108 // Set scripting object
9109 bool hasHadScriptObject = true;
9110 nsIScriptGlobalObject* scriptObject =
9111 GetScriptHandlingObject(hasHadScriptObject);
9112 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
9113 if (scriptObject) {
9114 clone->SetScriptHandlingObject(scriptObject);
9115 } else {
9116 clone->SetScopeObject(GetScopeObject());
9117 }
9118 // Make the clone a data document
9119 clone->SetLoadedAsData(true);
9121 // Misc state
9123 // State from nsIDocument
9124 clone->mCharacterSet = mCharacterSet;
9125 clone->mCharacterSetSource = mCharacterSetSource;
9126 clone->mCompatMode = mCompatMode;
9127 clone->mBidiOptions = mBidiOptions;
9128 clone->mContentLanguage = mContentLanguage;
9129 clone->SetContentTypeInternal(GetContentTypeInternal());
9130 clone->mSecurityInfo = mSecurityInfo;
9132 // State from nsDocument
9133 clone->mIsRegularHTML = mIsRegularHTML;
9134 clone->mXMLDeclarationBits = mXMLDeclarationBits;
9135 clone->mBaseTarget = mBaseTarget;
9136 return NS_OK;
9137 }
9139 void
9140 nsDocument::SetReadyStateInternal(ReadyState rs)
9141 {
9142 mReadyState = rs;
9143 if (rs == READYSTATE_UNINITIALIZED) {
9144 // Transition back to uninitialized happens only to keep assertions happy
9145 // right before readyState transitions to something else. Make this
9146 // transition undetectable by Web content.
9147 return;
9148 }
9149 if (mTiming) {
9150 switch (rs) {
9151 case READYSTATE_LOADING:
9152 mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
9153 break;
9154 case READYSTATE_INTERACTIVE:
9155 mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
9156 break;
9157 case READYSTATE_COMPLETE:
9158 mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
9159 break;
9160 default:
9161 NS_WARNING("Unexpected ReadyState value");
9162 break;
9163 }
9164 }
9165 // At the time of loading start, we don't have timing object, record time.
9166 if (READYSTATE_LOADING == rs) {
9167 mLoadingTimeStamp = mozilla::TimeStamp::Now();
9168 }
9170 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
9171 new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
9172 false, false);
9173 asyncDispatcher->RunDOMEventWhenSafe();
9174 }
9176 NS_IMETHODIMP
9177 nsDocument::GetReadyState(nsAString& aReadyState)
9178 {
9179 nsIDocument::GetReadyState(aReadyState);
9180 return NS_OK;
9181 }
9183 void
9184 nsIDocument::GetReadyState(nsAString& aReadyState) const
9185 {
9186 switch(mReadyState) {
9187 case READYSTATE_LOADING :
9188 aReadyState.Assign(NS_LITERAL_STRING("loading"));
9189 break;
9190 case READYSTATE_INTERACTIVE :
9191 aReadyState.Assign(NS_LITERAL_STRING("interactive"));
9192 break;
9193 case READYSTATE_COMPLETE :
9194 aReadyState.Assign(NS_LITERAL_STRING("complete"));
9195 break;
9196 default:
9197 aReadyState.Assign(NS_LITERAL_STRING("uninitialized"));
9198 }
9199 }
9201 namespace {
9203 struct SuppressArgs
9204 {
9205 nsIDocument::SuppressionType mWhat;
9206 uint32_t mIncrease;
9207 };
9209 }
9211 static bool
9212 SuppressEventHandlingInDocument(nsIDocument* aDocument, void* aData)
9213 {
9214 SuppressArgs* args = static_cast<SuppressArgs*>(aData);
9215 aDocument->SuppressEventHandling(args->mWhat, args->mIncrease);
9216 return true;
9217 }
9219 void
9220 nsDocument::SuppressEventHandling(nsIDocument::SuppressionType aWhat,
9221 uint32_t aIncrease)
9222 {
9223 if (mEventsSuppressed == 0 && mAnimationsPaused == 0 &&
9224 aIncrease != 0 && mPresShell && mScriptGlobalObject) {
9225 RevokeAnimationFrameNotifications();
9226 }
9228 if (aWhat == eAnimationsOnly) {
9229 mAnimationsPaused += aIncrease;
9230 } else {
9231 mEventsSuppressed += aIncrease;
9232 }
9234 SuppressArgs args = { aWhat, aIncrease };
9235 EnumerateSubDocuments(SuppressEventHandlingInDocument, &args);
9236 }
9238 static void
9239 FireOrClearDelayedEvents(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments,
9240 bool aFireEvents)
9241 {
9242 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
9243 if (!fm)
9244 return;
9246 for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
9247 // NB: Don't bother trying to fire delayed events on documents that were
9248 // closed before this event ran.
9249 if (!aDocuments[i]->EventHandlingSuppressed()) {
9250 fm->FireDelayedEvents(aDocuments[i]);
9251 nsCOMPtr<nsIPresShell> shell = aDocuments[i]->GetShell();
9252 if (shell) {
9253 // Only fire events for active documents.
9254 bool fire = aFireEvents &&
9255 aDocuments[i]->GetInnerWindow() &&
9256 aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
9257 shell->FireOrClearDelayedEvents(fire);
9258 }
9259 }
9260 }
9261 }
9263 void
9264 nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr)
9265 {
9266 // Early exit if the img is already present in the img-cache
9267 // which indicates that the "real" load has already started and
9268 // that we shouldn't preload it.
9269 int16_t blockingStatus;
9270 if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
9271 !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
9272 this, NodePrincipal(), &blockingStatus)) {
9273 return;
9274 }
9276 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
9277 switch (Element::StringToCORSMode(aCrossOriginAttr)) {
9278 case CORS_NONE:
9279 // Nothing to do
9280 break;
9281 case CORS_ANONYMOUS:
9282 loadFlags |= imgILoader::LOAD_CORS_ANONYMOUS;
9283 break;
9284 case CORS_USE_CREDENTIALS:
9285 loadFlags |= imgILoader::LOAD_CORS_USE_CREDENTIALS;
9286 break;
9287 default:
9288 MOZ_CRASH("Unknown CORS mode!");
9289 }
9291 // Image not in cache - trigger preload
9292 nsRefPtr<imgRequestProxy> request;
9293 nsresult rv =
9294 nsContentUtils::LoadImage(uri,
9295 this,
9296 NodePrincipal(),
9297 mDocumentURI, // uri of document used as referrer
9298 nullptr, // no observer
9299 loadFlags,
9300 NS_LITERAL_STRING("img"),
9301 getter_AddRefs(request));
9303 // Pin image-reference to avoid evicting it from the img-cache before
9304 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
9305 // unlink
9306 if (NS_SUCCEEDED(rv)) {
9307 mPreloadingImages.AppendObject(request);
9308 }
9309 }
9311 EventStates
9312 nsDocument::GetDocumentState()
9313 {
9314 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
9315 if (IsDocumentRightToLeft()) {
9316 mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9317 }
9318 mGotDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
9319 }
9320 if (!mGotDocumentState.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
9321 nsIPresShell* shell = GetShell();
9322 if (shell && shell->GetPresContext() &&
9323 shell->GetPresContext()->IsTopLevelWindowInactive()) {
9324 mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9325 }
9326 mGotDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
9327 }
9328 return mDocumentState;
9329 }
9331 namespace {
9333 /**
9334 * Stub for LoadSheet(), since all we want is to get the sheet into
9335 * the CSSLoader's style cache
9336 */
9337 class StubCSSLoaderObserver MOZ_FINAL : public nsICSSLoaderObserver {
9338 public:
9339 NS_IMETHOD
9340 StyleSheetLoaded(nsCSSStyleSheet*, bool, nsresult)
9341 {
9342 return NS_OK;
9343 }
9344 NS_DECL_ISUPPORTS
9345 };
9346 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
9348 }
9350 void
9351 nsDocument::PreloadStyle(nsIURI* uri, const nsAString& charset,
9352 const nsAString& aCrossOriginAttr)
9353 {
9354 // The CSSLoader will retain this object after we return.
9355 nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
9357 // Charset names are always ASCII.
9358 CSSLoader()->LoadSheet(uri, NodePrincipal(),
9359 NS_LossyConvertUTF16toASCII(charset),
9360 obs,
9361 Element::StringToCORSMode(aCrossOriginAttr));
9362 }
9364 nsresult
9365 nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet,
9366 nsCSSStyleSheet** sheet)
9367 {
9368 return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet);
9369 }
9371 class nsDelayedEventDispatcher : public nsRunnable
9372 {
9373 public:
9374 nsDelayedEventDispatcher(nsTArray<nsCOMPtr<nsIDocument> >& aDocuments)
9375 {
9376 mDocuments.SwapElements(aDocuments);
9377 }
9378 virtual ~nsDelayedEventDispatcher() {}
9380 NS_IMETHOD Run()
9381 {
9382 FireOrClearDelayedEvents(mDocuments, true);
9383 return NS_OK;
9384 }
9386 private:
9387 nsTArray<nsCOMPtr<nsIDocument> > mDocuments;
9388 };
9390 namespace {
9392 struct UnsuppressArgs
9393 {
9394 UnsuppressArgs(nsIDocument::SuppressionType aWhat)
9395 : mWhat(aWhat)
9396 {
9397 }
9399 nsIDocument::SuppressionType mWhat;
9400 nsTArray<nsCOMPtr<nsIDocument>> mDocs;
9401 };
9403 }
9405 static bool
9406 GetAndUnsuppressSubDocuments(nsIDocument* aDocument,
9407 void* aData)
9408 {
9409 UnsuppressArgs* args = static_cast<UnsuppressArgs*>(aData);
9410 if (args->mWhat != nsIDocument::eAnimationsOnly &&
9411 aDocument->EventHandlingSuppressed() > 0) {
9412 static_cast<nsDocument*>(aDocument)->DecreaseEventSuppression();
9413 } else if (args->mWhat == nsIDocument::eAnimationsOnly &&
9414 aDocument->AnimationsPaused()) {
9415 static_cast<nsDocument*>(aDocument)->ResumeAnimations();
9416 }
9418 if (args->mWhat != nsIDocument::eAnimationsOnly) {
9419 // No need to remember documents if we only care about animation frames.
9420 args->mDocs.AppendElement(aDocument);
9421 }
9423 aDocument->EnumerateSubDocuments(GetAndUnsuppressSubDocuments, aData);
9424 return true;
9425 }
9427 void
9428 nsDocument::UnsuppressEventHandlingAndFireEvents(nsIDocument::SuppressionType aWhat,
9429 bool aFireEvents)
9430 {
9431 UnsuppressArgs args(aWhat);
9432 GetAndUnsuppressSubDocuments(this, &args);
9434 if (aWhat == nsIDocument::eAnimationsOnly) {
9435 // No need to fire events if we only care about animations here.
9436 return;
9437 }
9439 if (aFireEvents) {
9440 NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(args.mDocs));
9441 } else {
9442 FireOrClearDelayedEvents(args.mDocs, false);
9443 }
9444 }
9446 nsISupports*
9447 nsDocument::GetCurrentContentSink()
9448 {
9449 return mParser ? mParser->GetContentSink() : nullptr;
9450 }
9452 nsIDocument*
9453 nsDocument::GetTemplateContentsOwner()
9454 {
9455 if (!mTemplateContentsOwner) {
9456 bool hasHadScriptObject = true;
9457 nsIScriptGlobalObject* scriptObject =
9458 GetScriptHandlingObject(hasHadScriptObject);
9459 NS_ENSURE_TRUE(scriptObject || !hasHadScriptObject, nullptr);
9461 nsCOMPtr<nsIDOMDocument> domDocument;
9462 nsresult rv = NS_NewDOMDocument(getter_AddRefs(domDocument),
9463 EmptyString(), // aNamespaceURI
9464 EmptyString(), // aQualifiedName
9465 nullptr, // aDoctype
9466 nsIDocument::GetDocumentURI(),
9467 nsIDocument::GetDocBaseURI(),
9468 NodePrincipal(),
9469 true, // aLoadedAsData
9470 scriptObject, // aEventObject
9471 DocumentFlavorHTML);
9472 NS_ENSURE_SUCCESS(rv, nullptr);
9474 mTemplateContentsOwner = do_QueryInterface(domDocument);
9475 NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
9477 mTemplateContentsOwner->SetScriptHandlingObject(scriptObject);
9479 // Set |doc| as the template contents owner of itself so that
9480 // |doc| is the template contents owner of template elements created
9481 // by |doc|.
9482 nsDocument* doc = static_cast<nsDocument*>(mTemplateContentsOwner.get());
9483 doc->mTemplateContentsOwner = doc;
9484 }
9486 return mTemplateContentsOwner;
9487 }
9489 void
9490 nsDocument::RegisterHostObjectUri(const nsACString& aUri)
9491 {
9492 mHostObjectURIs.AppendElement(aUri);
9493 }
9495 void
9496 nsDocument::UnregisterHostObjectUri(const nsACString& aUri)
9497 {
9498 mHostObjectURIs.RemoveElement(aUri);
9499 }
9501 void
9502 nsDocument::SetScrollToRef(nsIURI *aDocumentURI)
9503 {
9504 if (!aDocumentURI) {
9505 return;
9506 }
9508 nsAutoCString ref;
9510 // Since all URI's that pass through here aren't URL's we can't
9511 // rely on the nsIURI implementation for providing a way for
9512 // finding the 'ref' part of the URI, we'll haveto revert to
9513 // string routines for finding the data past '#'
9515 aDocumentURI->GetSpec(ref);
9517 nsReadingIterator<char> start, end;
9519 ref.BeginReading(start);
9520 ref.EndReading(end);
9522 if (FindCharInReadable('#', start, end)) {
9523 ++start; // Skip over the '#'
9525 mScrollToRef = Substring(start, end);
9526 }
9527 }
9529 void
9530 nsDocument::ScrollToRef()
9531 {
9532 if (mScrolledToRefAlready) {
9533 nsCOMPtr<nsIPresShell> shell = GetShell();
9534 if (shell) {
9535 shell->ScrollToAnchor();
9536 }
9537 return;
9538 }
9540 if (mScrollToRef.IsEmpty()) {
9541 return;
9542 }
9544 char* tmpstr = ToNewCString(mScrollToRef);
9545 if (!tmpstr) {
9546 return;
9547 }
9549 nsUnescape(tmpstr);
9550 nsAutoCString unescapedRef;
9551 unescapedRef.Assign(tmpstr);
9552 nsMemory::Free(tmpstr);
9554 nsresult rv = NS_ERROR_FAILURE;
9555 // We assume that the bytes are in UTF-8, as it says in the spec:
9556 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
9557 NS_ConvertUTF8toUTF16 ref(unescapedRef);
9559 nsCOMPtr<nsIPresShell> shell = GetShell();
9560 if (shell) {
9561 // Check an empty string which might be caused by the UTF-8 conversion
9562 if (!ref.IsEmpty()) {
9563 // Note that GoToAnchor will handle flushing layout as needed.
9564 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9565 } else {
9566 rv = NS_ERROR_FAILURE;
9567 }
9569 // If UTF-8 URI failed then try to assume the string as a
9570 // document's charset.
9572 if (NS_FAILED(rv)) {
9573 const nsACString &docCharset = GetDocumentCharacterSet();
9575 rv = nsContentUtils::ConvertStringFromEncoding(docCharset,
9576 unescapedRef,
9577 ref);
9579 if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
9580 rv = shell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
9581 }
9582 }
9583 if (NS_SUCCEEDED(rv)) {
9584 mScrolledToRefAlready = true;
9585 }
9586 }
9587 }
9589 void
9590 nsDocument::ResetScrolledToRefAlready()
9591 {
9592 mScrolledToRefAlready = false;
9593 }
9595 void
9596 nsDocument::SetChangeScrollPosWhenScrollingToRef(bool aValue)
9597 {
9598 mChangeScrollPosWhenScrollingToRef = aValue;
9599 }
9601 void
9602 nsIDocument::RegisterFreezableElement(nsIContent* aContent)
9603 {
9604 if (!mFreezableElements) {
9605 mFreezableElements = new nsTHashtable<nsPtrHashKey<nsIContent> >();
9606 if (!mFreezableElements)
9607 return;
9608 }
9609 mFreezableElements->PutEntry(aContent);
9610 }
9612 bool
9613 nsIDocument::UnregisterFreezableElement(nsIContent* aContent)
9614 {
9615 if (!mFreezableElements)
9616 return false;
9617 if (!mFreezableElements->GetEntry(aContent))
9618 return false;
9619 mFreezableElements->RemoveEntry(aContent);
9620 return true;
9621 }
9623 struct EnumerateFreezablesData {
9624 nsIDocument::FreezableElementEnumerator mEnumerator;
9625 void* mData;
9626 };
9628 static PLDHashOperator
9629 EnumerateFreezables(nsPtrHashKey<nsIContent>* aEntry, void* aData)
9630 {
9631 EnumerateFreezablesData* data = static_cast<EnumerateFreezablesData*>(aData);
9632 data->mEnumerator(aEntry->GetKey(), data->mData);
9633 return PL_DHASH_NEXT;
9634 }
9636 void
9637 nsIDocument::EnumerateFreezableElements(FreezableElementEnumerator aEnumerator,
9638 void* aData)
9639 {
9640 if (!mFreezableElements)
9641 return;
9642 EnumerateFreezablesData data = { aEnumerator, aData };
9643 mFreezableElements->EnumerateEntries(EnumerateFreezables, &data);
9644 }
9646 void
9647 nsIDocument::RegisterPendingLinkUpdate(Link* aLink)
9648 {
9649 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9650 mLinksToUpdate.PutEntry(aLink);
9651 mHasLinksToUpdate = true;
9652 }
9654 void
9655 nsIDocument::UnregisterPendingLinkUpdate(Link* aLink)
9656 {
9657 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9658 if (!mHasLinksToUpdate)
9659 return;
9661 mLinksToUpdate.RemoveEntry(aLink);
9662 }
9664 static PLDHashOperator
9665 EnumeratePendingLinkUpdates(nsPtrHashKey<Link>* aEntry, void* aData)
9666 {
9667 aEntry->GetKey()->GetElement()->UpdateLinkState(aEntry->GetKey()->LinkState());
9668 return PL_DHASH_NEXT;
9669 }
9671 void
9672 nsIDocument::FlushPendingLinkUpdates()
9673 {
9674 MOZ_ASSERT(!mIsLinkUpdateRegistrationsForbidden);
9675 if (!mHasLinksToUpdate)
9676 return;
9678 #ifdef DEBUG
9679 AutoRestore<bool> saved(mIsLinkUpdateRegistrationsForbidden);
9680 mIsLinkUpdateRegistrationsForbidden = true;
9681 #endif
9682 mLinksToUpdate.EnumerateEntries(EnumeratePendingLinkUpdates, nullptr);
9683 mLinksToUpdate.Clear();
9684 mHasLinksToUpdate = false;
9685 }
9687 already_AddRefed<nsIDocument>
9688 nsIDocument::CreateStaticClone(nsIDocShell* aCloneContainer)
9689 {
9690 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(this);
9691 NS_ENSURE_TRUE(domDoc, nullptr);
9692 mCreatingStaticClone = true;
9694 // Make document use different container during cloning.
9695 nsRefPtr<nsDocShell> originalShell = mDocumentContainer.get();
9696 SetContainer(static_cast<nsDocShell*>(aCloneContainer));
9697 nsCOMPtr<nsIDOMNode> clonedNode;
9698 nsresult rv = domDoc->CloneNode(true, 1, getter_AddRefs(clonedNode));
9699 SetContainer(originalShell);
9701 nsCOMPtr<nsIDocument> clonedDoc;
9702 if (NS_SUCCEEDED(rv)) {
9703 clonedDoc = do_QueryInterface(clonedNode);
9704 if (clonedDoc) {
9705 if (IsStaticDocument()) {
9706 clonedDoc->mOriginalDocument = mOriginalDocument;
9707 } else {
9708 clonedDoc->mOriginalDocument = this;
9709 }
9710 int32_t sheetsCount = GetNumberOfStyleSheets();
9711 for (int32_t i = 0; i < sheetsCount; ++i) {
9712 nsRefPtr<nsCSSStyleSheet> sheet = do_QueryObject(GetStyleSheetAt(i));
9713 if (sheet) {
9714 if (sheet->IsApplicable()) {
9715 nsRefPtr<nsCSSStyleSheet> clonedSheet =
9716 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9717 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
9718 if (clonedSheet) {
9719 clonedDoc->AddStyleSheet(clonedSheet);
9720 }
9721 }
9722 }
9723 }
9725 sheetsCount = GetNumberOfCatalogStyleSheets();
9726 for (int32_t i = 0; i < sheetsCount; ++i) {
9727 nsRefPtr<nsCSSStyleSheet> sheet =
9728 do_QueryObject(GetCatalogStyleSheetAt(i));
9729 if (sheet) {
9730 if (sheet->IsApplicable()) {
9731 nsRefPtr<nsCSSStyleSheet> clonedSheet =
9732 sheet->Clone(nullptr, nullptr, clonedDoc, nullptr);
9733 NS_WARN_IF_FALSE(clonedSheet, "Cloning a stylesheet didn't work!");
9734 if (clonedSheet) {
9735 clonedDoc->AddCatalogStyleSheet(clonedSheet);
9736 }
9737 }
9738 }
9739 }
9740 }
9741 }
9742 mCreatingStaticClone = false;
9743 return clonedDoc.forget();
9744 }
9746 nsresult
9747 nsIDocument::ScheduleFrameRequestCallback(const FrameRequestCallbackHolder& aCallback,
9748 int32_t *aHandle)
9749 {
9750 if (mFrameRequestCallbackCounter == INT32_MAX) {
9751 // Can't increment without overflowing; bail out
9752 return NS_ERROR_NOT_AVAILABLE;
9753 }
9754 int32_t newHandle = ++mFrameRequestCallbackCounter;
9756 bool alreadyRegistered = !mFrameRequestCallbacks.IsEmpty();
9757 DebugOnly<FrameRequest*> request =
9758 mFrameRequestCallbacks.AppendElement(FrameRequest(aCallback, newHandle));
9759 NS_ASSERTION(request, "This is supposed to be infallible!");
9760 if (!alreadyRegistered && mPresShell && IsEventHandlingEnabled()) {
9761 mPresShell->GetPresContext()->RefreshDriver()->
9762 ScheduleFrameRequestCallbacks(this);
9763 }
9765 *aHandle = newHandle;
9766 return NS_OK;
9767 }
9769 void
9770 nsIDocument::CancelFrameRequestCallback(int32_t aHandle)
9771 {
9772 // mFrameRequestCallbacks is stored sorted by handle
9773 if (mFrameRequestCallbacks.RemoveElementSorted(aHandle) &&
9774 mFrameRequestCallbacks.IsEmpty() &&
9775 mPresShell && IsEventHandlingEnabled()) {
9776 mPresShell->GetPresContext()->RefreshDriver()->
9777 RevokeFrameRequestCallbacks(this);
9778 }
9779 }
9781 nsresult
9782 nsDocument::GetStateObject(nsIVariant** aState)
9783 {
9784 // Get the document's current state object. This is the object backing both
9785 // history.state and popStateEvent.state.
9786 //
9787 // mStateObjectContainer may be null; this just means that there's no
9788 // current state object.
9790 nsCOMPtr<nsIVariant> stateObj;
9791 if (!mStateObjectCached && mStateObjectContainer) {
9792 AutoJSContext cx;
9793 nsIGlobalObject* sgo = GetScopeObject();
9794 NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
9795 JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
9796 NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
9797 JSAutoCompartment ac(cx, global);
9799 mStateObjectContainer->
9800 DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
9801 }
9803 NS_IF_ADDREF(*aState = mStateObjectCached);
9805 return NS_OK;
9806 }
9808 nsDOMNavigationTiming*
9809 nsDocument::GetNavigationTiming() const
9810 {
9811 return mTiming;
9812 }
9814 nsresult
9815 nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
9816 {
9817 mTiming = aTiming;
9818 if (!mLoadingTimeStamp.IsNull() && mTiming) {
9819 mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp);
9820 }
9821 return NS_OK;
9822 }
9824 Element*
9825 nsDocument::FindImageMap(const nsAString& aUseMapValue)
9826 {
9827 if (aUseMapValue.IsEmpty()) {
9828 return nullptr;
9829 }
9831 nsAString::const_iterator start, end;
9832 aUseMapValue.BeginReading(start);
9833 aUseMapValue.EndReading(end);
9835 int32_t hash = aUseMapValue.FindChar('#');
9836 if (hash < 0) {
9837 return nullptr;
9838 }
9839 // aUsemap contains a '#', set start to point right after the '#'
9840 start.advance(hash + 1);
9842 if (start == end) {
9843 return nullptr; // aUsemap == "#"
9844 }
9846 const nsAString& mapName = Substring(start, end);
9848 if (!mImageMaps) {
9849 mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map, nsGkAtoms::map);
9850 }
9852 uint32_t i, n = mImageMaps->Length(true);
9853 nsString name;
9854 for (i = 0; i < n; ++i) {
9855 nsIContent* map = mImageMaps->Item(i);
9856 if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
9857 eCaseMatters) ||
9858 (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) &&
9859 mapName.Equals(name, nsCaseInsensitiveStringComparator()))) {
9860 return map->AsElement();
9861 }
9862 }
9864 return nullptr;
9865 }
9867 #define DEPRECATED_OPERATION(_op) #_op "Warning",
9868 static const char* kWarnings[] = {
9869 #include "nsDeprecatedOperationList.h"
9870 nullptr
9871 };
9872 #undef DEPRECATED_OPERATION
9874 void
9875 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
9876 bool asError /* = false */)
9877 {
9878 static_assert(eDeprecatedOperationCount <= 64,
9879 "Too many deprecated operations");
9880 if (mWarnedAbout & (1ull << aOperation)) {
9881 return;
9882 }
9883 mWarnedAbout |= (1ull << aOperation);
9884 uint32_t flags = asError ? nsIScriptError::errorFlag
9885 : nsIScriptError::warningFlag;
9886 nsContentUtils::ReportToConsole(flags,
9887 NS_LITERAL_CSTRING("DOM Core"), this,
9888 nsContentUtils::eDOM_PROPERTIES,
9889 kWarnings[aOperation]);
9890 }
9892 nsresult
9893 nsDocument::AddImage(imgIRequest* aImage)
9894 {
9895 NS_ENSURE_ARG_POINTER(aImage);
9897 // See if the image is already in the hashtable. If it is, get the old count.
9898 uint32_t oldCount = 0;
9899 mImageTracker.Get(aImage, &oldCount);
9901 // Put the image in the hashtable, with the proper count.
9902 mImageTracker.Put(aImage, oldCount + 1);
9904 nsresult rv = NS_OK;
9906 // If this is the first insertion and we're locking images, lock this image
9907 // too.
9908 if (oldCount == 0) {
9909 if (mLockingImages)
9910 rv = aImage->LockImage();
9911 if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit ||
9912 mImageTracker.Count() < sOnloadDecodeLimit))
9913 rv = aImage->StartDecoding();
9914 }
9916 // If this is the first insertion and we're animating images, request
9917 // that this image be animated too.
9918 if (oldCount == 0 && mAnimatingImages) {
9919 nsresult rv2 = aImage->IncrementAnimationConsumers();
9920 rv = NS_SUCCEEDED(rv) ? rv2 : rv;
9921 }
9923 return rv;
9924 }
9926 nsresult
9927 nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags)
9928 {
9929 NS_ENSURE_ARG_POINTER(aImage);
9931 // Get the old count. It should exist and be > 0.
9932 uint32_t count = 0;
9933 DebugOnly<bool> found = mImageTracker.Get(aImage, &count);
9934 NS_ABORT_IF_FALSE(found, "Removing image that wasn't in the tracker!");
9935 NS_ABORT_IF_FALSE(count > 0, "Entry in the cache tracker with count 0!");
9937 // We're removing, so decrement the count.
9938 count--;
9940 // If the count is now zero, remove from the tracker.
9941 // Otherwise, set the new value.
9942 if (count != 0) {
9943 mImageTracker.Put(aImage, count);
9944 return NS_OK;
9945 }
9947 mImageTracker.Remove(aImage);
9949 nsresult rv = NS_OK;
9951 // Now that we're no longer tracking this image, unlock it if we'd
9952 // previously locked it.
9953 if (mLockingImages) {
9954 rv = aImage->UnlockImage();
9955 }
9957 // If we're animating images, remove our request to animate this one.
9958 if (mAnimatingImages) {
9959 nsresult rv2 = aImage->DecrementAnimationConsumers();
9960 rv = NS_SUCCEEDED(rv) ? rv2 : rv;
9961 }
9963 if (aFlags & REQUEST_DISCARD) {
9964 // Request that the image be discarded if nobody else holds a lock on it.
9965 // Do this even if !mLockingImages, because even if we didn't just unlock
9966 // this image, it might still be a candidate for discarding.
9967 aImage->RequestDiscard();
9968 }
9970 return rv;
9971 }
9973 nsresult
9974 nsDocument::AddPlugin(nsIObjectLoadingContent* aPlugin)
9975 {
9976 MOZ_ASSERT(aPlugin);
9977 if (!mPlugins.PutEntry(aPlugin)) {
9978 return NS_ERROR_OUT_OF_MEMORY;
9979 }
9980 return NS_OK;
9981 }
9983 void
9984 nsDocument::RemovePlugin(nsIObjectLoadingContent* aPlugin)
9985 {
9986 MOZ_ASSERT(aPlugin);
9987 mPlugins.RemoveEntry(aPlugin);
9988 }
9990 static bool
9991 AllSubDocumentPluginEnum(nsIDocument* aDocument, void* userArg)
9992 {
9993 nsTArray<nsIObjectLoadingContent*>* plugins =
9994 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
9995 MOZ_ASSERT(plugins);
9996 aDocument->GetPlugins(*plugins);
9997 return true;
9998 }
10000 static PLDHashOperator
10001 AllPluginEnum(nsPtrHashKey<nsIObjectLoadingContent>* aPlugin, void* userArg)
10002 {
10003 nsTArray<nsIObjectLoadingContent*>* allPlugins =
10004 reinterpret_cast< nsTArray<nsIObjectLoadingContent*>* >(userArg);
10005 MOZ_ASSERT(allPlugins);
10006 allPlugins->AppendElement(aPlugin->GetKey());
10007 return PL_DHASH_NEXT;
10008 }
10010 void
10011 nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
10012 {
10013 aPlugins.SetCapacity(aPlugins.Length() + mPlugins.Count());
10014 mPlugins.EnumerateEntries(AllPluginEnum, &aPlugins);
10015 EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
10016 }
10018 PLDHashOperator LockEnumerator(imgIRequest* aKey,
10019 uint32_t aData,
10020 void* userArg)
10021 {
10022 aKey->LockImage();
10023 aKey->RequestDecode();
10024 return PL_DHASH_NEXT;
10025 }
10027 PLDHashOperator UnlockEnumerator(imgIRequest* aKey,
10028 uint32_t aData,
10029 void* userArg)
10030 {
10031 aKey->UnlockImage();
10032 return PL_DHASH_NEXT;
10033 }
10036 nsresult
10037 nsDocument::SetImageLockingState(bool aLocked)
10038 {
10039 if (XRE_GetProcessType() == GeckoProcessType_Content &&
10040 !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) {
10041 return NS_OK;
10042 }
10044 // If there's no change, there's nothing to do.
10045 if (mLockingImages == aLocked)
10046 return NS_OK;
10048 // Otherwise, iterate over our images and perform the appropriate action.
10049 mImageTracker.EnumerateRead(aLocked ? LockEnumerator
10050 : UnlockEnumerator,
10051 nullptr);
10053 // Update state.
10054 mLockingImages = aLocked;
10056 return NS_OK;
10057 }
10059 PLDHashOperator IncrementAnimationEnumerator(imgIRequest* aKey,
10060 uint32_t aData,
10061 void* userArg)
10062 {
10063 aKey->IncrementAnimationConsumers();
10064 return PL_DHASH_NEXT;
10065 }
10067 PLDHashOperator DecrementAnimationEnumerator(imgIRequest* aKey,
10068 uint32_t aData,
10069 void* userArg)
10070 {
10071 aKey->DecrementAnimationConsumers();
10072 return PL_DHASH_NEXT;
10073 }
10075 void
10076 nsDocument::SetImagesNeedAnimating(bool aAnimating)
10077 {
10078 // If there's no change, there's nothing to do.
10079 if (mAnimatingImages == aAnimating)
10080 return;
10082 // Otherwise, iterate over our images and perform the appropriate action.
10083 mImageTracker.EnumerateRead(aAnimating ? IncrementAnimationEnumerator
10084 : DecrementAnimationEnumerator,
10085 nullptr);
10087 // Update state.
10088 mAnimatingImages = aAnimating;
10089 }
10091 already_AddRefed<Touch>
10092 nsIDocument::CreateTouch(nsIDOMWindow* aView,
10093 EventTarget* aTarget,
10094 int32_t aIdentifier,
10095 int32_t aPageX, int32_t aPageY,
10096 int32_t aScreenX, int32_t aScreenY,
10097 int32_t aClientX, int32_t aClientY,
10098 int32_t aRadiusX, int32_t aRadiusY,
10099 float aRotationAngle,
10100 float aForce)
10101 {
10102 nsRefPtr<Touch> touch = new Touch(aTarget,
10103 aIdentifier,
10104 aPageX, aPageY,
10105 aScreenX, aScreenY,
10106 aClientX, aClientY,
10107 aRadiusX, aRadiusY,
10108 aRotationAngle,
10109 aForce);
10110 return touch.forget();
10111 }
10113 already_AddRefed<TouchList>
10114 nsIDocument::CreateTouchList()
10115 {
10116 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10117 return retval.forget();
10118 }
10120 already_AddRefed<TouchList>
10121 nsIDocument::CreateTouchList(Touch& aTouch,
10122 const Sequence<OwningNonNull<Touch> >& aTouches)
10123 {
10124 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10125 retval->Append(&aTouch);
10126 for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10127 retval->Append(aTouches[i].get());
10128 }
10129 return retval.forget();
10130 }
10132 already_AddRefed<TouchList>
10133 nsIDocument::CreateTouchList(const Sequence<OwningNonNull<Touch> >& aTouches)
10134 {
10135 nsRefPtr<TouchList> retval = new TouchList(ToSupports(this));
10136 for (uint32_t i = 0; i < aTouches.Length(); ++i) {
10137 retval->Append(aTouches[i].get());
10138 }
10139 return retval.forget();
10140 }
10142 already_AddRefed<nsDOMCaretPosition>
10143 nsIDocument::CaretPositionFromPoint(float aX, float aY)
10144 {
10145 nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
10146 nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
10147 nsPoint pt(x, y);
10149 FlushPendingNotifications(Flush_Layout);
10151 nsIPresShell *ps = GetShell();
10152 if (!ps) {
10153 return nullptr;
10154 }
10156 nsIFrame *rootFrame = ps->GetRootFrame();
10158 // XUL docs, unlike HTML, have no frame tree until everything's done loading
10159 if (!rootFrame) {
10160 return nullptr;
10161 }
10163 nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, pt,
10164 nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
10165 if (!ptFrame) {
10166 return nullptr;
10167 }
10169 // GetContentOffsetsFromPoint requires frame-relative coordinates, so we need
10170 // to adjust to frame-relative coordinates before we can perform this call.
10171 // It should also not take into account the padding of the frame.
10172 nsPoint adjustedPoint = pt - ptFrame->GetOffsetTo(rootFrame);
10174 nsFrame::ContentOffsets offsets =
10175 ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
10177 nsCOMPtr<nsIContent> node = offsets.content;
10178 uint32_t offset = offsets.offset;
10179 nsCOMPtr<nsIContent> anonNode = node;
10180 bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
10181 if (nodeIsAnonymous) {
10182 node = ptFrame->GetContent();
10183 nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
10184 nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(nonanon);
10185 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(nonanon);
10186 bool isText;
10187 if (textArea || (input &&
10188 NS_SUCCEEDED(input->MozIsTextField(false, &isText)) &&
10189 isText)) {
10190 // If the anonymous content node has a child, then we need to make sure
10191 // that we get the appropriate child, as otherwise the offset may not be
10192 // correct when we construct a range for it.
10193 nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
10194 if (firstChild) {
10195 anonNode = firstChild;
10196 }
10198 if (textArea) {
10199 offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
10200 }
10202 node = nonanon;
10203 } else {
10204 node = nullptr;
10205 offset = 0;
10206 }
10207 }
10209 nsRefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
10210 if (nodeIsAnonymous) {
10211 aCaretPos->SetAnonymousContentNode(anonNode);
10212 }
10213 return aCaretPos.forget();
10214 }
10216 NS_IMETHODIMP
10217 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
10218 {
10219 NS_ENSURE_ARG_POINTER(aCaretPos);
10220 *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).take();
10221 return NS_OK;
10222 }
10224 void
10225 nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
10226 {
10227 nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
10228 if (NS_FAILED(res)) {
10229 rv.Throw(res);
10230 }
10231 }
10233 void
10234 nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
10235 {
10236 nsCOMPtr<nsIURI> uri;
10237 nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
10238 if (NS_FAILED(res)) {
10239 rv.Throw(res);
10240 return;
10241 }
10242 res = CSSLoader()->ObsoleteSheet(uri);
10243 if (NS_FAILED(res)) {
10244 rv.Throw(res);
10245 }
10246 }
10248 nsIHTMLCollection*
10249 nsIDocument::Children()
10250 {
10251 if (!mChildrenCollection) {
10252 mChildrenCollection = new nsContentList(this, kNameSpaceID_Wildcard,
10253 nsGkAtoms::_asterix,
10254 nsGkAtoms::_asterix,
10255 false);
10256 }
10258 return mChildrenCollection;
10259 }
10261 uint32_t
10262 nsIDocument::ChildElementCount()
10263 {
10264 return Children()->Length();
10265 }
10267 namespace mozilla {
10269 // Singleton class to manage the list of fullscreen documents which are the
10270 // root of a branch which contains fullscreen documents. We maintain this list
10271 // so that we can easily exit all windows from fullscreen when the user
10272 // presses the escape key.
10273 class FullscreenRoots {
10274 public:
10275 // Adds a root to the manager. Adding a root multiple times does not result
10276 // in duplicate entries for that item, only one.
10277 static void Add(nsIDocument* aRoot);
10279 // Iterates over every root in the root list, and calls aFunction, passing
10280 // each root once to aFunction. It is safe to call Add() and Remove() while
10281 // iterating over the list (i.e. in aFunction). Documents that are removed
10282 // from the manager during traversal are not traversed, and documents that
10283 // are added to the manager during traversal are also not traversed.
10284 static void ForEach(void(*aFunction)(nsIDocument* aDoc));
10286 // Removes a specific root from the manager.
10287 static void Remove(nsIDocument* aRoot);
10289 // Returns true if all roots added to the list have been removed.
10290 static bool IsEmpty();
10292 private:
10294 FullscreenRoots() {
10295 MOZ_COUNT_CTOR(FullscreenRoots);
10296 }
10297 ~FullscreenRoots() {
10298 MOZ_COUNT_DTOR(FullscreenRoots);
10299 }
10301 enum {
10302 NotFound = uint32_t(-1)
10303 };
10304 // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
10305 static uint32_t Find(nsIDocument* aRoot);
10307 // Returns true if aRoot is in the list of fullscreen roots.
10308 static bool Contains(nsIDocument* aRoot);
10310 // Singleton instance of the FullscreenRoots. This is instantiated when a
10311 // root is added, and it is deleted when the last root is removed.
10312 static FullscreenRoots* sInstance;
10314 // List of weak pointers to roots.
10315 nsTArray<nsWeakPtr> mRoots;
10316 };
10318 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
10320 /* static */
10321 void
10322 FullscreenRoots::ForEach(void(*aFunction)(nsIDocument* aDoc))
10323 {
10324 if (!sInstance) {
10325 return;
10326 }
10327 // Create a copy of the roots array, and iterate over the copy. This is so
10328 // that if an element is removed from mRoots we don't mess up our iteration.
10329 nsTArray<nsWeakPtr> roots(sInstance->mRoots);
10330 // Call aFunction on all entries.
10331 for (uint32_t i = 0; i < roots.Length(); i++) {
10332 nsCOMPtr<nsIDocument> root = do_QueryReferent(roots[i]);
10333 // Check that the root isn't in the manager. This is so that new additions
10334 // while we were running don't get traversed.
10335 if (root && FullscreenRoots::Contains(root)) {
10336 aFunction(root);
10337 }
10338 }
10339 }
10341 /* static */
10342 bool
10343 FullscreenRoots::Contains(nsIDocument* aRoot)
10344 {
10345 return FullscreenRoots::Find(aRoot) != NotFound;
10346 }
10348 /* static */
10349 void
10350 FullscreenRoots::Add(nsIDocument* aRoot)
10351 {
10352 if (!FullscreenRoots::Contains(aRoot)) {
10353 if (!sInstance) {
10354 sInstance = new FullscreenRoots();
10355 }
10356 nsWeakPtr weakRoot = do_GetWeakReference(aRoot);
10357 sInstance->mRoots.AppendElement(weakRoot);
10358 }
10359 }
10361 /* static */
10362 uint32_t
10363 FullscreenRoots::Find(nsIDocument* aRoot)
10364 {
10365 if (!sInstance) {
10366 return NotFound;
10367 }
10368 nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
10369 for (uint32_t i = 0; i < roots.Length(); i++) {
10370 nsCOMPtr<nsIDocument> otherRoot(do_QueryReferent(roots[i]));
10371 if (otherRoot == aRoot) {
10372 return i;
10373 }
10374 }
10375 return NotFound;
10376 }
10378 /* static */
10379 void
10380 FullscreenRoots::Remove(nsIDocument* aRoot)
10381 {
10382 uint32_t index = Find(aRoot);
10383 NS_ASSERTION(index != NotFound,
10384 "Should only try to remove roots which are still added!");
10385 if (index == NotFound || !sInstance) {
10386 return;
10387 }
10388 sInstance->mRoots.RemoveElementAt(index);
10389 if (sInstance->mRoots.IsEmpty()) {
10390 delete sInstance;
10391 sInstance = nullptr;
10392 }
10393 }
10395 /* static */
10396 bool
10397 FullscreenRoots::IsEmpty()
10398 {
10399 return !sInstance;
10400 }
10402 } // end namespace mozilla.
10403 using mozilla::FullscreenRoots;
10405 nsIDocument*
10406 nsDocument::GetFullscreenRoot()
10407 {
10408 nsCOMPtr<nsIDocument> root = do_QueryReferent(mFullscreenRoot);
10409 return root;
10410 }
10412 void
10413 nsDocument::SetFullscreenRoot(nsIDocument* aRoot)
10414 {
10415 mFullscreenRoot = do_GetWeakReference(aRoot);
10416 }
10418 NS_IMETHODIMP
10419 nsDocument::MozCancelFullScreen()
10420 {
10421 nsIDocument::MozCancelFullScreen();
10422 return NS_OK;
10423 }
10425 void
10426 nsIDocument::MozCancelFullScreen()
10427 {
10428 RestorePreviousFullScreenState();
10429 }
10431 // Runnable to set window full-screen mode. Used as a script runner
10432 // to ensure we only call nsGlobalWindow::SetFullScreen() when it's safe to
10433 // run script. nsGlobalWindow::SetFullScreen() dispatches a synchronous event
10434 // (handled in chome code) which is unsafe to run if this is called in
10435 // Element::UnbindFromTree().
10436 class nsSetWindowFullScreen : public nsRunnable {
10437 public:
10438 nsSetWindowFullScreen(nsIDocument* aDoc, bool aValue)
10439 : mDoc(aDoc), mValue(aValue) {}
10441 NS_IMETHOD Run()
10442 {
10443 if (mDoc->GetWindow()) {
10444 mDoc->GetWindow()->SetFullScreenInternal(mValue, false);
10445 }
10446 return NS_OK;
10447 }
10449 private:
10450 nsCOMPtr<nsIDocument> mDoc;
10451 bool mValue;
10452 };
10454 static nsIDocument*
10455 GetFullscreenRootDocument(nsIDocument* aDoc)
10456 {
10457 if (!aDoc) {
10458 return nullptr;
10459 }
10460 nsIDocument* doc = aDoc;
10461 nsIDocument* parent;
10462 while ((parent = doc->GetParentDocument()) &&
10463 (!nsContentUtils::IsFullscreenApiContentOnly() ||
10464 !nsContentUtils::IsChromeDoc(parent))) {
10465 doc = parent;
10466 }
10467 return doc;
10468 }
10470 static void
10471 SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
10472 {
10473 // Maintain list of fullscreen root documents.
10474 nsCOMPtr<nsIDocument> root = GetFullscreenRootDocument(aDoc);
10475 if (aValue) {
10476 FullscreenRoots::Add(root);
10477 } else {
10478 FullscreenRoots::Remove(root);
10479 }
10480 if (!nsContentUtils::IsFullscreenApiContentOnly()) {
10481 nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
10482 }
10483 }
10485 class nsCallExitFullscreen : public nsRunnable {
10486 public:
10487 nsCallExitFullscreen(nsIDocument* aDoc)
10488 : mDoc(aDoc) {}
10489 NS_IMETHOD Run()
10490 {
10491 nsDocument::ExitFullscreen(mDoc);
10492 return NS_OK;
10493 }
10494 private:
10495 nsCOMPtr<nsIDocument> mDoc;
10496 };
10498 /* static */
10499 void
10500 nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
10501 {
10502 if (aDoc && !aDoc->IsFullScreenDoc()) {
10503 return;
10504 }
10505 if (aRunAsync) {
10506 NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
10507 return;
10508 }
10509 nsDocument::ExitFullscreen(aDoc);
10510 }
10512 // Returns true if the document is a direct child of a cross process parent
10513 // mozbrowser iframe. This is the case when the document has a null parent,
10514 // and its DocShell reports that it is a browser frame.
10515 static bool
10516 HasCrossProcessParent(nsIDocument* aDocument)
10517 {
10518 if (XRE_GetProcessType() != GeckoProcessType_Content) {
10519 return false;
10520 }
10521 if (aDocument->GetParentDocument() != nullptr) {
10522 return false;
10523 }
10524 nsPIDOMWindow* win = aDocument->GetWindow();
10525 if (!win) {
10526 return false;
10527 }
10528 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
10529 if (!docShell) {
10530 return false;
10531 }
10532 return docShell->GetIsBrowserOrApp();
10533 }
10535 static bool
10536 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
10537 {
10538 if (aDoc->IsFullScreenDoc()) {
10539 uint32_t* count = static_cast<uint32_t*>(aData);
10540 (*count)++;
10541 }
10542 return true;
10543 }
10545 static uint32_t
10546 CountFullscreenSubDocuments(nsIDocument* aDoc)
10547 {
10548 uint32_t count = 0;
10549 aDoc->EnumerateSubDocuments(CountFullscreenSubDocuments, &count);
10550 return count;
10551 }
10553 bool
10554 nsDocument::IsFullscreenLeaf()
10555 {
10556 // A fullscreen leaf document is fullscreen, and has no fullscreen
10557 // subdocuments.
10558 if (!IsFullScreenDoc()) {
10559 return false;
10560 }
10561 return CountFullscreenSubDocuments(this) == 0;
10562 }
10564 static bool
10565 ResetFullScreen(nsIDocument* aDocument, void* aData)
10566 {
10567 if (aDocument->IsFullScreenDoc()) {
10568 NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
10569 "Should have at most 1 fullscreen subdocument.");
10570 static_cast<nsDocument*>(aDocument)->CleanupFullscreenState();
10571 NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
10572 nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
10573 changed->AppendElement(aDocument);
10575 if (HasCrossProcessParent(aDocument)) {
10576 // We're at the top of the content-process side doc tree. Ask the parent
10577 // process to exit fullscreen.
10578 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10579 os->NotifyObservers(aDocument, "ask-parent-to-exit-fullscreen", nullptr);
10580 }
10582 // Dispatch a notification so that if this document has any
10583 // cross-process subdocuments, they'll be notified to exit fullscreen.
10584 // The BrowserElementParent listens for this event and performs the
10585 // cross process notification if it has a remote child process.
10586 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10587 os->NotifyObservers(aDocument, "ask-children-to-exit-fullscreen", nullptr);
10589 aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
10590 }
10591 return true;
10592 }
10594 static void
10595 ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
10596 {
10597 MOZ_ASSERT(aMaybeNotARootDoc);
10598 nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
10599 NS_ASSERTION(root, "Should have root when in fullscreen!");
10600 if (!root) {
10601 return;
10602 }
10603 NS_ASSERTION(root->IsFullScreenDoc(),
10604 "Fullscreen root should be a fullscreen doc...");
10606 // Stores a list of documents to which we must dispatch "mozfullscreenchange".
10607 // We're required by the spec to dispatch the events in leaf-to-root
10608 // order when exiting fullscreen, but we traverse the doctree in a
10609 // root-to-leaf order, so we save references to the documents we must
10610 // dispatch to so that we dispatch in the specified order.
10611 nsAutoTArray<nsIDocument*, 8> changed;
10613 // Walk the tree of fullscreen documents, and reset their fullscreen state.
10614 ResetFullScreen(root, static_cast<void*>(&changed));
10616 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
10617 // order so that the events for the leaf document arrives before the root
10618 // document, as required by the spec.
10619 for (uint32_t i = 0; i < changed.Length(); ++i) {
10620 DispatchFullScreenChange(changed[changed.Length() - i - 1]);
10621 }
10623 NS_ASSERTION(!root->IsFullScreenDoc(),
10624 "Fullscreen root should no longer be a fullscreen doc...");
10626 // Move the top-level window out of fullscreen mode.
10627 SetWindowFullScreen(root, false);
10628 }
10630 /* static */
10631 void
10632 nsDocument::ExitFullscreen(nsIDocument* aDoc)
10633 {
10634 // Unlock the pointer, if it's locked.
10635 nsCOMPtr<Element> pointerLockedElement =
10636 do_QueryReferent(EventStateManager::sPointerLockedElement);
10637 if (pointerLockedElement) {
10638 UnlockPointer();
10639 }
10641 if (aDoc) {
10642 ExitFullscreenInDocTree(aDoc);
10643 return;
10644 }
10646 // Clear fullscreen stacks in all fullscreen roots' descendant documents.
10647 FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
10648 NS_ASSERTION(FullscreenRoots::IsEmpty(),
10649 "Should have exited all fullscreen roots from fullscreen");
10650 }
10652 bool
10653 GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
10654 {
10655 if (aDoc->IsFullscreenLeaf()) {
10656 nsIDocument** result = static_cast<nsIDocument**>(aData);
10657 *result = aDoc;
10658 return false;
10659 } else if (aDoc->IsFullScreenDoc()) {
10660 aDoc->EnumerateSubDocuments(GetFullscreenLeaf, aData);
10661 }
10662 return true;
10663 }
10665 static nsIDocument*
10666 GetFullscreenLeaf(nsIDocument* aDoc)
10667 {
10668 nsIDocument* leaf = nullptr;
10669 GetFullscreenLeaf(aDoc, &leaf);
10670 if (leaf) {
10671 return leaf;
10672 }
10673 // Otherwise we could be either in a non-fullscreen doc tree, or we're
10674 // below the fullscreen doc. Start the search from the root.
10675 nsIDocument* root = GetFullscreenRootDocument(aDoc);
10676 // Check that the root is actually fullscreen so we don't waste time walking
10677 // around its descendants.
10678 if (!root->IsFullScreenDoc()) {
10679 return nullptr;
10680 }
10681 GetFullscreenLeaf(root, &leaf);
10682 return leaf;
10683 }
10685 void
10686 nsDocument::RestorePreviousFullScreenState()
10687 {
10688 NS_ASSERTION(!IsFullScreenDoc() || !FullscreenRoots::IsEmpty(),
10689 "Should have at least 1 fullscreen root when fullscreen!");
10690 NS_ASSERTION(!nsContentUtils::IsFullscreenApiContentOnly() ||
10691 !nsContentUtils::IsChromeDoc(this),
10692 "Should not run RestorePreviousFullScreenState() on "
10693 "chrome document when fullscreen is content only");
10695 if (!IsFullScreenDoc() || !GetWindow() || FullscreenRoots::IsEmpty()) {
10696 return;
10697 }
10699 // If fullscreen mode is updated the pointer should be unlocked
10700 nsCOMPtr<Element> pointerLockedElement =
10701 do_QueryReferent(EventStateManager::sPointerLockedElement);
10702 if (pointerLockedElement) {
10703 UnlockPointer();
10704 }
10706 nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
10708 // The fullscreen document may contain a <iframe mozbrowser> element which
10709 // has a cross process child. So send a notification so that its browser
10710 // parent will send a message to its child process to also exit fullscreen.
10711 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10712 os->NotifyObservers(fullScreenDoc, "ask-children-to-exit-fullscreen", nullptr);
10714 // Clear full-screen stacks in all descendant in process documents, bottom up.
10715 nsIDocument* doc = fullScreenDoc;
10716 while (doc != this) {
10717 NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc");
10718 static_cast<nsDocument*>(doc)->CleanupFullscreenState();
10719 UnlockPointer();
10720 DispatchFullScreenChange(doc);
10721 doc = doc->GetParentDocument();
10722 }
10724 // Roll-back full-screen state to previous full-screen element.
10725 NS_ASSERTION(doc == this, "Must have reached this doc.");
10726 while (doc != nullptr) {
10727 static_cast<nsDocument*>(doc)->FullScreenStackPop();
10728 UnlockPointer();
10729 DispatchFullScreenChange(doc);
10730 if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) {
10731 if (HasCrossProcessParent(doc)) {
10732 // Send notification to the parent process to tell it to rollback to
10733 // the previous fullscreen elements in its fullscreen element stacks.
10734 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10735 os->NotifyObservers(doc, "ask-parent-to-rollback-fullscreen", nullptr);
10736 }
10737 // Full-screen stack in document is empty. Go back up to the parent
10738 // document. We'll pop the containing element off its stack, and use
10739 // its next full-screen element as the full-screen element.
10740 static_cast<nsDocument*>(doc)->CleanupFullscreenState();
10741 doc = doc->GetParentDocument();
10742 } else {
10743 // Else we popped the top of the stack, and there's still another
10744 // element in there, so that will become the full-screen element.
10745 if (fullScreenDoc != doc) {
10746 // We've popped so enough off the stack that we've rolled back to
10747 // a fullscreen element in a parent document. If this document isn't
10748 // approved for fullscreen, or if it's cross origin, dispatch an
10749 // event to chrome so it knows to show the authorization/warning UI.
10750 if (!nsContentUtils::HaveEqualPrincipals(fullScreenDoc, doc) ||
10751 (!nsContentUtils::IsSitePermAllow(doc->NodePrincipal(), "fullscreen") &&
10752 !static_cast<nsDocument*>(doc)->mIsApprovedForFullscreen)) {
10753 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
10754 new AsyncEventDispatcher(doc,
10755 NS_LITERAL_STRING("MozEnteredDomFullscreen"),
10756 true,
10757 true);
10758 asyncDispatcher->PostDOMEvent();
10759 }
10760 }
10762 if (!nsContentUtils::HaveEqualPrincipals(doc, fullScreenDoc)) {
10763 // The origin which is fullscreen changed. Send a notification to
10764 // the root process so that a warning or approval UI can be shown
10765 // as necessary.
10766 nsAutoString origin;
10767 nsContentUtils::GetUTFOrigin(doc->NodePrincipal(), origin);
10768 nsIDocument* root = GetFullscreenRootDocument(doc);
10769 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10770 os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
10771 }
10773 break;
10774 }
10775 }
10777 if (doc == nullptr) {
10778 // We moved all documents in this doctree out of fullscreen mode,
10779 // move the top-level window out of fullscreen mode.
10780 NS_ASSERTION(!GetFullscreenRootDocument(this)->IsFullScreenDoc(),
10781 "Should have cleared all docs' stacks");
10782 SetWindowFullScreen(this, false);
10783 }
10784 }
10786 bool
10787 nsDocument::IsFullScreenDoc()
10788 {
10789 return GetFullScreenElement() != nullptr;
10790 }
10792 class nsCallRequestFullScreen : public nsRunnable
10793 {
10794 public:
10795 nsCallRequestFullScreen(Element* aElement)
10796 : mElement(aElement),
10797 mDoc(aElement->OwnerDoc()),
10798 mWasCallerChrome(nsContentUtils::IsCallerChrome()),
10799 mHadRequestPending(static_cast<nsDocument*>(mDoc.get())->
10800 mAsyncFullscreenPending)
10801 {
10802 static_cast<nsDocument*>(mDoc.get())->
10803 mAsyncFullscreenPending = true;
10804 }
10806 NS_IMETHOD Run()
10807 {
10808 static_cast<nsDocument*>(mDoc.get())->
10809 mAsyncFullscreenPending = mHadRequestPending;
10810 nsDocument* doc = static_cast<nsDocument*>(mDoc.get());
10811 doc->RequestFullScreen(mElement,
10812 mWasCallerChrome,
10813 /* aNotifyOnOriginChange */ true);
10814 return NS_OK;
10815 }
10817 nsRefPtr<Element> mElement;
10818 nsCOMPtr<nsIDocument> mDoc;
10819 bool mWasCallerChrome;
10820 bool mHadRequestPending;
10821 };
10823 void
10824 nsDocument::AsyncRequestFullScreen(Element* aElement)
10825 {
10826 NS_ASSERTION(aElement,
10827 "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
10828 if (!aElement) {
10829 return;
10830 }
10831 // Request full-screen asynchronously.
10832 nsCOMPtr<nsIRunnable> event(new nsCallRequestFullScreen(aElement));
10833 NS_DispatchToCurrentThread(event);
10834 }
10836 static void
10837 LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc)
10838 {
10839 if (!aLogFailure) {
10840 return;
10841 }
10842 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
10843 new AsyncEventDispatcher(aDoc,
10844 NS_LITERAL_STRING("mozfullscreenerror"),
10845 true,
10846 false);
10847 asyncDispatcher->PostDOMEvent();
10848 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
10849 NS_LITERAL_CSTRING("DOM"), aDoc,
10850 nsContentUtils::eDOM_PROPERTIES,
10851 aMessage);
10852 }
10854 nsresult
10855 nsDocument::AddFullscreenApprovedObserver()
10856 {
10857 if (mHasFullscreenApprovedObserver ||
10858 !Preferences::GetBool("full-screen-api.approval-required")) {
10859 return NS_OK;
10860 }
10862 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10863 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
10865 nsresult res = os->AddObserver(this, "fullscreen-approved", true);
10866 NS_ENSURE_SUCCESS(res, res);
10868 mHasFullscreenApprovedObserver = true;
10870 return NS_OK;
10871 }
10873 nsresult
10874 nsDocument::RemoveFullscreenApprovedObserver()
10875 {
10876 if (!mHasFullscreenApprovedObserver) {
10877 return NS_OK;
10878 }
10879 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
10880 NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
10882 nsresult res = os->RemoveObserver(this, "fullscreen-approved");
10883 NS_ENSURE_SUCCESS(res, res);
10885 mHasFullscreenApprovedObserver = false;
10887 return NS_OK;
10888 }
10890 void
10891 nsDocument::CleanupFullscreenState()
10892 {
10893 if (!mFullScreenStack.IsEmpty()) {
10894 // The top element in the full-screen stack will have full-screen
10895 // style bits set on it and its ancestors. Remove the style bits.
10896 // Note the non-top elements won't have the style bits set.
10897 Element* top = FullScreenStackTop();
10898 NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
10899 if (top) {
10900 EventStateManager::SetFullScreenState(top, false);
10901 }
10902 mFullScreenStack.Clear();
10903 }
10904 SetApprovedForFullscreen(false);
10905 RemoveFullscreenApprovedObserver();
10906 mFullscreenRoot = nullptr;
10907 }
10909 bool
10910 nsDocument::FullScreenStackPush(Element* aElement)
10911 {
10912 NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
10913 Element* top = FullScreenStackTop();
10914 if (top == aElement || !aElement) {
10915 return false;
10916 }
10917 if (top) {
10918 // We're pushing a new element onto the full-screen stack, so we must
10919 // remove the ancestor and full-screen styles from the former top of the
10920 // stack.
10921 EventStateManager::SetFullScreenState(top, false);
10922 }
10923 EventStateManager::SetFullScreenState(aElement, true);
10924 nsWeakPtr weakElement = do_GetWeakReference(aElement);
10925 mFullScreenStack.AppendElement(weakElement);
10926 NS_ASSERTION(GetFullScreenElement() == aElement, "Should match");
10927 return true;
10928 }
10930 void
10931 nsDocument::FullScreenStackPop()
10932 {
10933 if (mFullScreenStack.IsEmpty()) {
10934 return;
10935 }
10937 // Remove styles from existing top element.
10938 Element* top = FullScreenStackTop();
10939 EventStateManager::SetFullScreenState(top, false);
10941 // Remove top element. Note the remaining top element in the stack
10942 // will not have full-screen style bits set, so we will need to restore
10943 // them on the new top element before returning.
10944 uint32_t last = mFullScreenStack.Length() - 1;
10945 mFullScreenStack.RemoveElementAt(last);
10947 // Pop from the stack null elements (references to elements which have
10948 // been GC'd since they were added to the stack) and elements which are
10949 // no longer in this document.
10950 while (!mFullScreenStack.IsEmpty()) {
10951 Element* element = FullScreenStackTop();
10952 if (!element || !element->IsInDoc() || element->OwnerDoc() != this) {
10953 NS_ASSERTION(!element->IsFullScreenAncestor(),
10954 "Should have already removed full-screen styles");
10955 uint32_t last = mFullScreenStack.Length() - 1;
10956 mFullScreenStack.RemoveElementAt(last);
10957 } else {
10958 // The top element of the stack is now an in-doc element. Apply the
10959 // full-screen styles and return.
10960 EventStateManager::SetFullScreenState(element, true);
10961 break;
10962 }
10963 }
10964 }
10966 Element*
10967 nsDocument::FullScreenStackTop()
10968 {
10969 if (mFullScreenStack.IsEmpty()) {
10970 return nullptr;
10971 }
10972 uint32_t last = mFullScreenStack.Length() - 1;
10973 nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
10974 NS_ASSERTION(element, "Should have full-screen element!");
10975 NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc");
10976 NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
10977 return element;
10978 }
10980 // Returns true if aDoc is in the focused tab in the active window.
10981 static bool
10982 IsInActiveTab(nsIDocument* aDoc)
10983 {
10984 nsCOMPtr<nsIDocShell> docshell = aDoc->GetDocShell();
10985 if (!docshell) {
10986 return false;
10987 }
10989 bool isActive = false;
10990 docshell->GetIsActive(&isActive);
10991 if (!isActive) {
10992 return false;
10993 }
10995 nsCOMPtr<nsIDocShellTreeItem> rootItem;
10996 docshell->GetRootTreeItem(getter_AddRefs(rootItem));
10997 if (!rootItem) {
10998 return false;
10999 }
11000 nsCOMPtr<nsIDOMWindow> rootWin = do_GetInterface(rootItem);
11001 if (!rootWin) {
11002 return false;
11003 }
11005 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11006 if (!fm) {
11007 return false;
11008 }
11010 nsCOMPtr<nsIDOMWindow> activeWindow;
11011 fm->GetActiveWindow(getter_AddRefs(activeWindow));
11012 if (!activeWindow) {
11013 return false;
11014 }
11016 return activeWindow == rootWin;
11017 }
11019 nsresult nsDocument::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement,
11020 const nsAString& aOrigin)
11021 {
11022 // Ensure the frame element is the fullscreen element in this document.
11023 // If the frame element is already the fullscreen element in this document,
11024 // this has no effect.
11025 nsCOMPtr<nsIContent> content(do_QueryInterface(aFrameElement));
11026 RequestFullScreen(content->AsElement(),
11027 /* aWasCallerChrome */ false,
11028 /* aNotifyOnOriginChange */ false);
11030 // Origin changed in child process, send notifiction, so that chrome can
11031 // update the UI to reflect the fullscreen origin change if necessary.
11032 // The BrowserElementChild listens on this, and forwards it over its
11033 // parent process, where it is redispatched. Chrome (in the root process,
11034 // which could be *this* process) listens for this notification so that
11035 // it can show a warning or approval UI.
11036 if (!aOrigin.IsEmpty()) {
11037 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11038 os->NotifyObservers(GetFullscreenRootDocument(this),
11039 "fullscreen-origin-change",
11040 PromiseFlatString(aOrigin).get());
11041 }
11043 return NS_OK;
11044 }
11046 nsresult nsDocument::RemoteFrameFullscreenReverted()
11047 {
11048 RestorePreviousFullScreenState();
11049 return NS_OK;
11050 }
11052 void
11053 nsDocument::RequestFullScreen(Element* aElement,
11054 bool aWasCallerChrome,
11055 bool aNotifyOnOriginChange)
11056 {
11057 NS_ASSERTION(aElement,
11058 "Must pass non-null element to nsDocument::RequestFullScreen");
11059 if (!aElement || aElement == GetFullScreenElement()) {
11060 return;
11061 }
11062 if (!aElement->IsInDoc()) {
11063 LogFullScreenDenied(true, "FullScreenDeniedNotInDocument", this);
11064 return;
11065 }
11066 if (aElement->OwnerDoc() != this) {
11067 LogFullScreenDenied(true, "FullScreenDeniedMovedDocument", this);
11068 return;
11069 }
11070 if (!GetWindow()) {
11071 LogFullScreenDenied(true, "FullScreenDeniedLostWindow", this);
11072 return;
11073 }
11074 if (nsContentUtils::IsFullscreenApiContentOnly() &&
11075 nsContentUtils::IsChromeDoc(this)) {
11076 // Block fullscreen requests in the chrome document when the fullscreen API
11077 // is configured for content only.
11078 LogFullScreenDenied(true, "FullScreenDeniedContentOnly", this);
11079 return;
11080 }
11081 if (!IsFullScreenEnabled(aWasCallerChrome, true)) {
11082 // IsFullScreenEnabled calls LogFullScreenDenied, no need to log.
11083 return;
11084 }
11085 if (GetFullScreenElement() &&
11086 !nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) {
11087 // If this document is full-screen, only grant full-screen requests from
11088 // a descendant of the current full-screen element.
11089 LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this);
11090 return;
11091 }
11092 if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
11093 LogFullScreenDenied(true, "FullScreenDeniedNotFocusedTab", this);
11094 return;
11095 }
11096 // Deny requests when a windowed plugin is focused.
11097 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
11098 if (!fm) {
11099 NS_WARNING("Failed to retrieve focus manager in full-screen request.");
11100 return;
11101 }
11102 nsCOMPtr<nsIDOMElement> focusedElement;
11103 fm->GetFocusedElement(getter_AddRefs(focusedElement));
11104 if (focusedElement) {
11105 nsCOMPtr<nsIContent> content = do_QueryInterface(focusedElement);
11106 if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(content)) {
11107 LogFullScreenDenied(true, "FullScreenDeniedFocusedPlugin", this);
11108 return;
11109 }
11110 }
11112 // Stash a reference to any existing fullscreen doc, we'll use this later
11113 // to detect if the origin which is fullscreen has changed.
11114 nsCOMPtr<nsIDocument> previousFullscreenDoc = GetFullscreenLeaf(this);
11116 AddFullscreenApprovedObserver();
11118 // Stores a list of documents which we must dispatch "mozfullscreenchange"
11119 // too. We're required by the spec to dispatch the events in root-to-leaf
11120 // order, but we traverse the doctree in a leaf-to-root order, so we save
11121 // references to the documents we must dispatch to so that we get the order
11122 // as specified.
11123 nsAutoTArray<nsIDocument*, 8> changed;
11125 // Remember the root document, so that if a full-screen document is hidden
11126 // we can reset full-screen state in the remaining visible full-screen documents.
11127 nsIDocument* fullScreenRootDoc = GetFullscreenRootDocument(this);
11128 if (fullScreenRootDoc->IsFullScreenDoc()) {
11129 // A document is already in fullscreen, unlock the mouse pointer
11130 // before setting a new document to fullscreen
11131 UnlockPointer();
11132 }
11134 // If a document is already in fullscreen, then unlock the mouse pointer
11135 // before setting a new document to fullscreen
11136 nsCOMPtr<Element> pointerLockedElement =
11137 do_QueryReferent(EventStateManager::sPointerLockedElement);
11138 if (pointerLockedElement) {
11139 UnlockPointer();
11140 }
11142 // Set the full-screen element. This sets the full-screen style on the
11143 // element, and the full-screen-ancestor styles on ancestors of the element
11144 // in this document.
11145 DebugOnly<bool> x = FullScreenStackPush(aElement);
11146 NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
11147 changed.AppendElement(this);
11149 // Propagate up the document hierarchy, setting the full-screen element as
11150 // the element's container in ancestor documents. This also sets the
11151 // appropriate css styles as well. Note we don't propagate down the
11152 // document hierarchy, the full-screen element (or its container) is not
11153 // visible there. Stop when we reach the root document.
11154 nsIDocument* child = this;
11155 while (true) {
11156 child->SetFullscreenRoot(fullScreenRootDoc);
11157 NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
11158 "Fullscreen root should be set!");
11159 if (child == fullScreenRootDoc) {
11160 break;
11161 }
11162 nsIDocument* parent = child->GetParentDocument();
11163 Element* element = parent->FindContentForSubDocument(child)->AsElement();
11164 if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
11165 changed.AppendElement(parent);
11166 child = parent;
11167 } else {
11168 // We've reached either the root, or a point in the doctree where the
11169 // new full-screen element container is the same as the previous
11170 // full-screen element's container. No more changes need to be made
11171 // to the full-screen stacks of documents further up the tree.
11172 break;
11173 }
11174 }
11176 // Dispatch "mozfullscreenchange" events. Note this loop is in reverse
11177 // order so that the events for the root document arrives before the leaf
11178 // document, as required by the spec.
11179 for (uint32_t i = 0; i < changed.Length(); ++i) {
11180 DispatchFullScreenChange(changed[changed.Length() - i - 1]);
11181 }
11183 // If this document hasn't already been approved in this session,
11184 // check to see if the user has granted the fullscreen access
11185 // to the document's principal's host, if it has one. Note that documents
11186 // in web apps which are the same origin as the web app are considered
11187 // trusted and so are automatically approved.
11188 if (!mIsApprovedForFullscreen) {
11189 mIsApprovedForFullscreen =
11190 !Preferences::GetBool("full-screen-api.approval-required") ||
11191 NodePrincipal()->GetAppStatus() >= nsIPrincipal::APP_STATUS_INSTALLED ||
11192 nsContentUtils::IsSitePermAllow(NodePrincipal(), "fullscreen");
11193 }
11195 // If this document, or a document with the same principal has not
11196 // already been approved for fullscreen this fullscreen-session, dispatch
11197 // an event so that chrome knows to pop up a warning/approval UI.
11198 // Note previousFullscreenDoc=nullptr upon first entry, so we always
11199 // take this path on the first time we enter fullscreen in a fullscreen
11200 // session.
11201 if (!mIsApprovedForFullscreen ||
11202 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11203 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11204 new AsyncEventDispatcher(this,
11205 NS_LITERAL_STRING("MozEnteredDomFullscreen"),
11206 true,
11207 true);
11208 asyncDispatcher->PostDOMEvent();
11209 }
11211 #ifdef DEBUG
11212 // Note assertions must run before SetWindowFullScreen() as that does
11213 // synchronous event dispatch which can run script which exits full-screen!
11214 NS_ASSERTION(GetFullScreenElement() == aElement,
11215 "Full-screen element should be the requested element!");
11216 NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
11217 nsCOMPtr<nsIDOMElement> fse;
11218 GetMozFullScreenElement(getter_AddRefs(fse));
11219 nsCOMPtr<nsIContent> c(do_QueryInterface(fse));
11220 NS_ASSERTION(c->AsElement() == aElement,
11221 "GetMozFullScreenElement should match GetFullScreenElement()");
11222 #endif
11224 // The origin which is fullscreen changed, send a notifiction so that the
11225 // root document knows the origin of the document which requested fullscreen.
11226 // This is used for the fullscreen approval UI. If we're in a child
11227 // process, the root BrowserElementChild listens for this notification,
11228 // and forwards it across to its BrowserElementParent, which
11229 // re-broadcasts the message for the root document in its process.
11230 if (aNotifyOnOriginChange &&
11231 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
11232 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11233 nsIDocument* root = GetFullscreenRootDocument(this);
11234 nsAutoString origin;
11235 nsContentUtils::GetUTFOrigin(NodePrincipal(), origin);
11236 os->NotifyObservers(root, "fullscreen-origin-change", origin.get());
11237 }
11239 // Make the window full-screen. Note we must make the state changes above
11240 // before making the window full-screen, as then the document reports as
11241 // being in full-screen mode when the chrome "fullscreen" event fires,
11242 // enabling chrome to distinguish between browser and dom full-screen
11243 // modes. Also note that nsGlobalWindow::SetFullScreen() (which
11244 // SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
11245 // and does not operate on the a per-nsIDOMWindow basis.
11246 SetWindowFullScreen(this, true);
11247 }
11249 NS_IMETHODIMP
11250 nsDocument::GetMozFullScreenElement(nsIDOMElement **aFullScreenElement)
11251 {
11252 ErrorResult rv;
11253 Element* el = GetMozFullScreenElement(rv);
11254 if (rv.Failed()) {
11255 return rv.ErrorCode();
11256 }
11257 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
11258 retval.forget(aFullScreenElement);
11259 return NS_OK;
11260 }
11262 Element*
11263 nsDocument::GetMozFullScreenElement(ErrorResult& rv)
11264 {
11265 if (IsFullScreenDoc()) {
11266 // Must have a full-screen element while in full-screen mode.
11267 Element* el = GetFullScreenElement();
11268 if (!el) {
11269 rv.Throw(NS_ERROR_UNEXPECTED);
11270 }
11271 return el;
11272 }
11273 return nullptr;
11274 }
11276 Element*
11277 nsDocument::GetFullScreenElement()
11278 {
11279 Element* element = FullScreenStackTop();
11280 NS_ASSERTION(!element ||
11281 element->IsFullScreenAncestor(),
11282 "Fullscreen element should have fullscreen styles applied");
11283 return element;
11284 }
11286 NS_IMETHODIMP
11287 nsDocument::GetMozFullScreen(bool *aFullScreen)
11288 {
11289 *aFullScreen = MozFullScreen();
11290 return NS_OK;
11291 }
11293 NS_IMETHODIMP
11294 nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
11295 {
11296 NS_ENSURE_ARG_POINTER(aFullScreen);
11297 *aFullScreen = MozFullScreenEnabled();
11298 return NS_OK;
11299 }
11301 bool
11302 nsDocument::MozFullScreenEnabled()
11303 {
11304 return IsFullScreenEnabled(nsContentUtils::IsCallerChrome(), false);
11305 }
11307 static bool
11308 HasFullScreenSubDocument(nsIDocument* aDoc)
11309 {
11310 uint32_t count = CountFullscreenSubDocuments(aDoc);
11311 NS_ASSERTION(count <= 1, "Fullscreen docs should have at most 1 fullscreen child!");
11312 return count >= 1;
11313 }
11315 bool
11316 nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
11317 {
11318 if (nsContentUtils::IsFullScreenApiEnabled() && aCallerIsChrome) {
11319 // Chrome code can always use the full-screen API, provided it's not
11320 // explicitly disabled. Note IsCallerChrome() returns true when running
11321 // in an nsRunnable, so don't use GetMozFullScreenEnabled() from an
11322 // nsRunnable!
11323 return true;
11324 }
11326 if (!nsContentUtils::IsFullScreenApiEnabled()) {
11327 LogFullScreenDenied(aLogFailure, "FullScreenDeniedDisabled", this);
11328 return false;
11329 }
11330 if (!IsVisible()) {
11331 LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this);
11332 return false;
11333 }
11334 if (HasFullScreenSubDocument(this)) {
11335 LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this);
11336 return false;
11337 }
11339 // Ensure that all ancestor <iframe> elements have the allowfullscreen
11340 // boolean attribute set.
11341 nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
11342 bool allowed = false;
11343 if (docShell) {
11344 docShell->GetFullscreenAllowed(&allowed);
11345 }
11346 if (!allowed) {
11347 LogFullScreenDenied(aLogFailure, "FullScreenDeniedIframeNotAllowed", this);
11348 }
11350 return allowed;
11351 }
11353 static void
11354 DispatchPointerLockChange(nsIDocument* aTarget)
11355 {
11356 if (!aTarget) {
11357 return;
11358 }
11360 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11361 new AsyncEventDispatcher(aTarget,
11362 NS_LITERAL_STRING("mozpointerlockchange"),
11363 true,
11364 false);
11365 asyncDispatcher->PostDOMEvent();
11366 }
11368 static void
11369 DispatchPointerLockError(nsIDocument* aTarget)
11370 {
11371 if (!aTarget) {
11372 return;
11373 }
11375 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
11376 new AsyncEventDispatcher(aTarget,
11377 NS_LITERAL_STRING("mozpointerlockerror"),
11378 true,
11379 false);
11380 asyncDispatcher->PostDOMEvent();
11381 }
11383 mozilla::StaticRefPtr<nsPointerLockPermissionRequest> gPendingPointerLockRequest;
11385 class nsPointerLockPermissionRequest : public nsRunnable,
11386 public nsIContentPermissionRequest
11387 {
11388 public:
11389 nsPointerLockPermissionRequest(Element* aElement, bool aUserInputOrChromeCaller)
11390 : mElement(do_GetWeakReference(aElement)),
11391 mDocument(do_GetWeakReference(aElement->OwnerDoc())),
11392 mUserInputOrChromeCaller(aUserInputOrChromeCaller) {}
11394 virtual ~nsPointerLockPermissionRequest() {}
11396 NS_DECL_ISUPPORTS
11397 NS_DECL_NSICONTENTPERMISSIONREQUEST
11399 NS_IMETHOD Run()
11400 {
11401 nsCOMPtr<Element> e = do_QueryReferent(mElement);
11402 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11403 if (!e || !d || gPendingPointerLockRequest != this ||
11404 e->GetCurrentDoc() != d) {
11405 Handled();
11406 DispatchPointerLockError(d);
11407 return NS_OK;
11408 }
11410 // We're about to enter fullscreen mode.
11411 nsDocument* doc = static_cast<nsDocument*>(d.get());
11412 if (doc->mAsyncFullscreenPending ||
11413 (doc->mHasFullscreenApprovedObserver && !doc->mIsApprovedForFullscreen)) {
11414 // We're still waiting for approval.
11415 return NS_OK;
11416 }
11418 if (doc->mIsApprovedForFullscreen || doc->mAllowRelocking) {
11419 Allow(JS::UndefinedHandleValue);
11420 return NS_OK;
11421 }
11423 // In non-fullscreen mode user input (or chrome caller) is required!
11424 // Also, don't let the page to try to get the permission too many times.
11425 if (!mUserInputOrChromeCaller ||
11426 doc->mCancelledPointerLockRequests > 2) {
11427 Handled();
11428 DispatchPointerLockError(d);
11429 return NS_OK;
11430 }
11432 // Handling a request from user input in non-fullscreen mode.
11433 // Do a normal permission check.
11434 nsCOMPtr<nsIContentPermissionPrompt> prompt =
11435 do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
11436 if (prompt) {
11437 prompt->Prompt(this);
11438 }
11440 return NS_OK;
11441 }
11443 void Handled()
11444 {
11445 mElement = nullptr;
11446 mDocument = nullptr;
11447 if (gPendingPointerLockRequest == this) {
11448 gPendingPointerLockRequest = nullptr;
11449 }
11450 }
11452 nsWeakPtr mElement;
11453 nsWeakPtr mDocument;
11454 bool mUserInputOrChromeCaller;
11455 };
11457 NS_IMPL_ISUPPORTS_INHERITED(nsPointerLockPermissionRequest,
11458 nsRunnable,
11459 nsIContentPermissionRequest)
11461 NS_IMETHODIMP
11462 nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
11463 {
11464 nsTArray<nsString> emptyOptions;
11465 return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
11466 NS_LITERAL_CSTRING("unused"),
11467 emptyOptions,
11468 aTypes);
11469 }
11471 NS_IMETHODIMP
11472 nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
11473 {
11474 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11475 if (d) {
11476 NS_ADDREF(*aPrincipal = d->NodePrincipal());
11477 }
11478 return NS_OK;
11479 }
11481 NS_IMETHODIMP
11482 nsPointerLockPermissionRequest::GetWindow(nsIDOMWindow** aWindow)
11483 {
11484 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11485 if (d) {
11486 NS_IF_ADDREF(*aWindow = d->GetInnerWindow());
11487 }
11488 return NS_OK;
11489 }
11491 NS_IMETHODIMP
11492 nsPointerLockPermissionRequest::GetElement(nsIDOMElement** aElement)
11493 {
11494 // It is enough to implement GetWindow.
11495 *aElement = nullptr;
11496 return NS_OK;
11497 }
11499 NS_IMETHODIMP
11500 nsPointerLockPermissionRequest::Cancel()
11501 {
11502 nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument);
11503 Handled();
11504 if (d) {
11505 static_cast<nsDocument*>(d.get())->mCancelledPointerLockRequests++;
11506 DispatchPointerLockError(d);
11507 }
11508 return NS_OK;
11509 }
11511 NS_IMETHODIMP
11512 nsPointerLockPermissionRequest::Allow(JS::HandleValue aChoices)
11513 {
11514 MOZ_ASSERT(aChoices.isUndefined());
11516 nsCOMPtr<Element> e = do_QueryReferent(mElement);
11517 nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument);
11518 nsDocument* d = static_cast<nsDocument*>(doc.get());
11519 if (!e || !d || gPendingPointerLockRequest != this ||
11520 e->GetCurrentDoc() != d ||
11521 (!mUserInputOrChromeCaller && !d->mIsApprovedForFullscreen)) {
11522 Handled();
11523 DispatchPointerLockError(d);
11524 return NS_OK;
11525 }
11527 // Mark handled here so that we don't need to call it everywhere below.
11528 Handled();
11530 nsCOMPtr<Element> pointerLockedElement =
11531 do_QueryReferent(EventStateManager::sPointerLockedElement);
11532 if (e == pointerLockedElement) {
11533 DispatchPointerLockChange(d);
11534 return NS_OK;
11535 }
11537 // Note, we must bypass focus change, so pass true as the last parameter!
11538 if (!d->ShouldLockPointer(e, pointerLockedElement, true)) {
11539 DispatchPointerLockError(d);
11540 return NS_OK;
11541 }
11543 if (!d->SetPointerLock(e, NS_STYLE_CURSOR_NONE)) {
11544 DispatchPointerLockError(d);
11545 return NS_OK;
11546 }
11548 d->mCancelledPointerLockRequests = 0;
11549 e->SetPointerLock();
11550 EventStateManager::sPointerLockedElement = do_GetWeakReference(e);
11551 EventStateManager::sPointerLockedDoc = do_GetWeakReference(doc);
11552 NS_ASSERTION(EventStateManager::sPointerLockedElement &&
11553 EventStateManager::sPointerLockedDoc,
11554 "aElement and this should support weak references!");
11556 DispatchPointerLockChange(d);
11557 return NS_OK;
11558 }
11560 void
11561 nsDocument::SetApprovedForFullscreen(bool aIsApproved)
11562 {
11563 mIsApprovedForFullscreen = aIsApproved;
11564 }
11566 nsresult
11567 nsDocument::Observe(nsISupports *aSubject,
11568 const char *aTopic,
11569 const char16_t *aData)
11570 {
11571 if (strcmp("fullscreen-approved", aTopic) == 0) {
11572 nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
11573 if (subject != this) {
11574 return NS_OK;
11575 }
11576 SetApprovedForFullscreen(true);
11577 if (gPendingPointerLockRequest) {
11578 // We have a request pending. Create a clone of it and re-dispatch so that
11579 // Run() method gets called again.
11580 nsCOMPtr<Element> el =
11581 do_QueryReferent(gPendingPointerLockRequest->mElement);
11582 nsCOMPtr<nsIDocument> doc =
11583 do_QueryReferent(gPendingPointerLockRequest->mDocument);
11584 bool userInputOrChromeCaller =
11585 gPendingPointerLockRequest->mUserInputOrChromeCaller;
11586 gPendingPointerLockRequest->Handled();
11587 if (doc == this && el && el->GetCurrentDoc() == doc) {
11588 nsPointerLockPermissionRequest* clone =
11589 new nsPointerLockPermissionRequest(el, userInputOrChromeCaller);
11590 gPendingPointerLockRequest = clone;
11591 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
11592 NS_DispatchToMainThread(r);
11593 }
11594 }
11595 }
11596 return NS_OK;
11597 }
11599 void
11600 nsDocument::RequestPointerLock(Element* aElement)
11601 {
11602 NS_ASSERTION(aElement,
11603 "Must pass non-null element to nsDocument::RequestPointerLock");
11605 nsCOMPtr<Element> pointerLockedElement =
11606 do_QueryReferent(EventStateManager::sPointerLockedElement);
11607 if (aElement == pointerLockedElement) {
11608 DispatchPointerLockChange(this);
11609 return;
11610 }
11612 if (!ShouldLockPointer(aElement, pointerLockedElement)) {
11613 DispatchPointerLockError(this);
11614 return;
11615 }
11617 bool userInputOrChromeCaller = EventStateManager::IsHandlingUserInput() ||
11618 nsContentUtils::IsCallerChrome();
11620 gPendingPointerLockRequest =
11621 new nsPointerLockPermissionRequest(aElement, userInputOrChromeCaller);
11622 nsCOMPtr<nsIRunnable> r = gPendingPointerLockRequest.get();
11623 NS_DispatchToMainThread(r);
11624 }
11626 bool
11627 nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock,
11628 bool aNoFocusCheck)
11629 {
11630 // Check if pointer lock pref is enabled
11631 if (!Preferences::GetBool("full-screen-api.pointer-lock.enabled")) {
11632 NS_WARNING("ShouldLockPointer(): Pointer Lock pref not enabled");
11633 return false;
11634 }
11636 if (aCurrentLock && aCurrentLock->OwnerDoc() != aElement->OwnerDoc()) {
11637 NS_WARNING("ShouldLockPointer(): Existing pointer lock element in a different document");
11638 return false;
11639 }
11641 if (!aElement->IsInDoc()) {
11642 NS_WARNING("ShouldLockPointer(): Element without Document");
11643 return false;
11644 }
11646 if (mSandboxFlags & SANDBOXED_POINTER_LOCK) {
11647 NS_WARNING("ShouldLockPointer(): Document is sandboxed and doesn't allow pointer-lock");
11648 return false;
11649 }
11651 // Check if the element is in a document with a docshell.
11652 nsCOMPtr<nsIDocument> ownerDoc = aElement->OwnerDoc();
11653 if (!ownerDoc->GetContainer()) {
11654 return false;
11655 }
11656 nsCOMPtr<nsPIDOMWindow> ownerWindow = ownerDoc->GetWindow();
11657 if (!ownerWindow) {
11658 return false;
11659 }
11660 nsCOMPtr<nsPIDOMWindow> ownerInnerWindow = ownerDoc->GetInnerWindow();
11661 if (!ownerInnerWindow) {
11662 return false;
11663 }
11664 if (ownerWindow->GetCurrentInnerWindow() != ownerInnerWindow) {
11665 return false;
11666 }
11668 nsCOMPtr<nsIDOMWindow> top;
11669 ownerWindow->GetScriptableTop(getter_AddRefs(top));
11670 nsCOMPtr<nsPIDOMWindow> piTop = do_QueryInterface(top);
11671 if (!piTop || !piTop->GetExtantDoc() ||
11672 piTop->GetExtantDoc()->Hidden()) {
11673 NS_WARNING("ShouldLockPointer(): Top document isn't visible.");
11674 return false;
11675 }
11677 if (!aNoFocusCheck) {
11678 mozilla::ErrorResult rv;
11679 if (!piTop->GetExtantDoc()->HasFocus(rv)) {
11680 NS_WARNING("ShouldLockPointer(): Top document isn't focused.");
11681 return false;
11682 }
11683 }
11685 return true;
11686 }
11688 bool
11689 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
11690 {
11691 // NOTE: aElement will be nullptr when unlocking.
11692 nsCOMPtr<nsPIDOMWindow> window = GetWindow();
11693 if (!window) {
11694 NS_WARNING("SetPointerLock(): No Window");
11695 return false;
11696 }
11698 nsIDocShell *docShell = window->GetDocShell();
11699 if (!docShell) {
11700 NS_WARNING("SetPointerLock(): No DocShell (window already closed?)");
11701 return false;
11702 }
11704 nsRefPtr<nsPresContext> presContext;
11705 docShell->GetPresContext(getter_AddRefs(presContext));
11706 if (!presContext) {
11707 NS_WARNING("SetPointerLock(): Unable to get presContext in \
11708 domWindow->GetDocShell()->GetPresContext()");
11709 return false;
11710 }
11712 nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
11713 if (!shell) {
11714 NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()");
11715 return false;
11716 }
11718 nsIFrame* rootFrame = shell->GetRootFrame();
11719 if (!rootFrame) {
11720 NS_WARNING("SetPointerLock(): Unable to get root frame");
11721 return false;
11722 }
11724 nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
11725 if (!widget) {
11726 NS_WARNING("SetPointerLock(): Unable to find widget in \
11727 shell->GetRootFrame()->GetNearestWidget();");
11728 return false;
11729 }
11731 if (aElement && (aElement->OwnerDoc() != this)) {
11732 NS_WARNING("SetPointerLock(): Element not in this document.");
11733 return false;
11734 }
11736 // Hide the cursor and set pointer lock for future mouse events
11737 nsRefPtr<EventStateManager> esm = presContext->EventStateManager();
11738 esm->SetCursor(aCursorStyle, nullptr, false,
11739 0.0f, 0.0f, widget, true);
11740 esm->SetPointerLock(widget, aElement);
11742 return true;
11743 }
11745 void
11746 nsDocument::UnlockPointer(nsIDocument* aDoc)
11747 {
11748 if (!EventStateManager::sIsPointerLocked) {
11749 return;
11750 }
11752 nsCOMPtr<nsIDocument> pointerLockedDoc =
11753 do_QueryReferent(EventStateManager::sPointerLockedDoc);
11754 if (!pointerLockedDoc || (aDoc && aDoc != pointerLockedDoc)) {
11755 return;
11756 }
11757 nsDocument* doc = static_cast<nsDocument*>(pointerLockedDoc.get());
11758 if (!doc->SetPointerLock(nullptr, NS_STYLE_CURSOR_AUTO)) {
11759 return;
11760 }
11762 nsCOMPtr<Element> pointerLockedElement =
11763 do_QueryReferent(EventStateManager::sPointerLockedElement);
11764 if (pointerLockedElement) {
11765 pointerLockedElement->ClearPointerLock();
11766 }
11768 EventStateManager::sPointerLockedElement = nullptr;
11769 EventStateManager::sPointerLockedDoc = nullptr;
11770 static_cast<nsDocument*>(pointerLockedDoc.get())->mAllowRelocking = !!aDoc;
11771 gPendingPointerLockRequest = nullptr;
11772 DispatchPointerLockChange(pointerLockedDoc);
11773 }
11775 void
11776 nsIDocument::UnlockPointer(nsIDocument* aDoc)
11777 {
11778 nsDocument::UnlockPointer(aDoc);
11779 }
11781 NS_IMETHODIMP
11782 nsDocument::MozExitPointerLock()
11783 {
11784 nsIDocument::MozExitPointerLock();
11785 return NS_OK;
11786 }
11788 NS_IMETHODIMP
11789 nsDocument::GetMozPointerLockElement(nsIDOMElement** aPointerLockedElement)
11790 {
11791 Element* el = nsIDocument::GetMozPointerLockElement();
11792 nsCOMPtr<nsIDOMElement> retval = do_QueryInterface(el);
11793 retval.forget(aPointerLockedElement);
11794 return NS_OK;
11795 }
11797 Element*
11798 nsIDocument::GetMozPointerLockElement()
11799 {
11800 nsCOMPtr<Element> pointerLockedElement =
11801 do_QueryReferent(EventStateManager::sPointerLockedElement);
11802 if (!pointerLockedElement) {
11803 return nullptr;
11804 }
11806 // Make sure pointer locked element is in the same document.
11807 nsCOMPtr<nsIDocument> pointerLockedDoc =
11808 do_QueryReferent(EventStateManager::sPointerLockedDoc);
11809 if (pointerLockedDoc != this) {
11810 return nullptr;
11811 }
11813 return pointerLockedElement;
11814 }
11816 void
11817 nsDocument::XPCOMShutdown()
11818 {
11819 gPendingPointerLockRequest = nullptr;
11820 sProcessingStack.destroyIfConstructed();
11821 }
11823 void
11824 nsDocument::UpdateVisibilityState()
11825 {
11826 dom::VisibilityState oldState = mVisibilityState;
11827 mVisibilityState = GetVisibilityState();
11828 if (oldState != mVisibilityState) {
11829 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
11830 NS_LITERAL_STRING("visibilitychange"),
11831 /* bubbles = */ true,
11832 /* cancelable = */ false);
11833 nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
11834 NS_LITERAL_STRING("mozvisibilitychange"),
11835 /* bubbles = */ true,
11836 /* cancelable = */ false);
11838 EnumerateFreezableElements(NotifyActivityChanged, nullptr);
11839 }
11840 }
11842 VisibilityState
11843 nsDocument::GetVisibilityState() const
11844 {
11845 // We have to check a few pieces of information here:
11846 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
11847 // 2) Do we have an outer window? If not, we're hidden. Note that we don't
11848 // want to use GetWindow here because it does weird groveling for windows
11849 // in some cases.
11850 // 3) Is our outer window background? If so, we're hidden.
11851 // Otherwise, we're visible.
11852 if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
11853 mWindow->GetOuterWindow()->IsBackground()) {
11854 return dom::VisibilityState::Hidden;
11855 }
11857 return dom::VisibilityState::Visible;
11858 }
11860 /* virtual */ void
11861 nsDocument::PostVisibilityUpdateEvent()
11862 {
11863 nsCOMPtr<nsIRunnable> event =
11864 NS_NewRunnableMethod(this, &nsDocument::UpdateVisibilityState);
11865 NS_DispatchToMainThread(event);
11866 }
11868 NS_IMETHODIMP
11869 nsDocument::GetMozHidden(bool* aHidden)
11870 {
11871 *aHidden = MozHidden();
11872 return NS_OK;
11873 }
11875 NS_IMETHODIMP
11876 nsDocument::GetHidden(bool* aHidden)
11877 {
11878 *aHidden = Hidden();
11879 return NS_OK;
11880 }
11882 NS_IMETHODIMP
11883 nsDocument::GetMozVisibilityState(nsAString& aState)
11884 {
11885 WarnOnceAbout(ePrefixedVisibilityAPI);
11886 return GetVisibilityState(aState);
11887 }
11889 NS_IMETHODIMP
11890 nsDocument::GetVisibilityState(nsAString& aState)
11891 {
11892 const EnumEntry& entry =
11893 VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
11894 aState.AssignASCII(entry.value, entry.length);
11895 return NS_OK;
11896 }
11898 /* virtual */ void
11899 nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
11900 {
11901 aWindowSizes->mDOMOtherSize +=
11902 nsINode::SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
11904 if (mPresShell) {
11905 mPresShell->AddSizeOfIncludingThis(aWindowSizes->mMallocSizeOf,
11906 &aWindowSizes->mArenaStats,
11907 &aWindowSizes->mLayoutPresShellSize,
11908 &aWindowSizes->mLayoutStyleSetsSize,
11909 &aWindowSizes->mLayoutTextRunsSize,
11910 &aWindowSizes->mLayoutPresContextSize);
11911 }
11913 aWindowSizes->mPropertyTablesSize +=
11914 mPropertyTable.SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
11915 for (uint32_t i = 0, count = mExtraPropertyTables.Length();
11916 i < count; ++i) {
11917 aWindowSizes->mPropertyTablesSize +=
11918 mExtraPropertyTables[i]->SizeOfExcludingThis(aWindowSizes->mMallocSizeOf);
11919 }
11921 if (EventListenerManager* elm = GetExistingListenerManager()) {
11922 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
11923 }
11925 // Measurement of the following members may be added later if DMD finds it
11926 // is worthwhile:
11927 // - many!
11928 }
11930 void
11931 nsIDocument::DocAddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
11932 {
11933 aWindowSizes->mDOMOtherSize += aWindowSizes->mMallocSizeOf(this);
11934 DocAddSizeOfExcludingThis(aWindowSizes);
11935 }
11937 static size_t
11938 SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet,
11939 MallocSizeOf aMallocSizeOf,
11940 void* aData)
11941 {
11942 return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf);
11943 }
11945 size_t
11946 nsDocument::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
11947 {
11948 // This SizeOfExcludingThis() overrides the one from nsINode. But
11949 // nsDocuments can only appear at the top of the DOM tree, and we use the
11950 // specialized DocAddSizeOfExcludingThis() in that case. So this should never
11951 // be called.
11952 MOZ_CRASH();
11953 }
11955 void
11956 nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const
11957 {
11958 nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);
11960 for (nsIContent* node = nsINode::GetFirstChild();
11961 node;
11962 node = node->GetNextNode(this))
11963 {
11964 size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
11965 size_t* p;
11967 switch (node->NodeType()) {
11968 case nsIDOMNode::ELEMENT_NODE:
11969 p = &aWindowSizes->mDOMElementNodesSize;
11970 break;
11971 case nsIDOMNode::TEXT_NODE:
11972 p = &aWindowSizes->mDOMTextNodesSize;
11973 break;
11974 case nsIDOMNode::CDATA_SECTION_NODE:
11975 p = &aWindowSizes->mDOMCDATANodesSize;
11976 break;
11977 case nsIDOMNode::COMMENT_NODE:
11978 p = &aWindowSizes->mDOMCommentNodesSize;
11979 break;
11980 default:
11981 p = &aWindowSizes->mDOMOtherSize;
11982 break;
11983 }
11985 *p += nodeSize;
11987 if (EventListenerManager* elm = node->GetExistingListenerManager()) {
11988 aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
11989 }
11990 }
11992 aWindowSizes->mStyleSheetsSize +=
11993 mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
11994 aWindowSizes->mMallocSizeOf);
11995 aWindowSizes->mStyleSheetsSize +=
11996 mCatalogSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
11997 aWindowSizes->mMallocSizeOf);
11998 aWindowSizes->mStyleSheetsSize +=
11999 mAdditionalSheets[eAgentSheet].
12000 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12001 aWindowSizes->mMallocSizeOf);
12002 aWindowSizes->mStyleSheetsSize +=
12003 mAdditionalSheets[eUserSheet].
12004 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12005 aWindowSizes->mMallocSizeOf);
12006 aWindowSizes->mStyleSheetsSize +=
12007 mAdditionalSheets[eAuthorSheet].
12008 SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis,
12009 aWindowSizes->mMallocSizeOf);
12010 // Lumping in the loader with the style-sheets size is not ideal,
12011 // but most of the things in there are in fact stylesheets, so it
12012 // doesn't seem worthwhile to separate it out.
12013 aWindowSizes->mStyleSheetsSize +=
12014 CSSLoader()->SizeOfIncludingThis(aWindowSizes->mMallocSizeOf);
12016 aWindowSizes->mDOMOtherSize +=
12017 mAttrStyleSheet ?
12018 mAttrStyleSheet->DOMSizeOfIncludingThis(aWindowSizes->mMallocSizeOf) :
12019 0;
12021 aWindowSizes->mDOMOtherSize +=
12022 mStyledLinks.SizeOfExcludingThis(nullptr, aWindowSizes->mMallocSizeOf);
12024 aWindowSizes->mDOMOtherSize +=
12025 mIdentifierMap.SizeOfExcludingThis(nsIdentifierMapEntry::SizeOfExcludingThis,
12026 aWindowSizes->mMallocSizeOf);
12028 // Measurement of the following members may be added later if DMD finds it
12029 // is worthwhile:
12030 // - many!
12031 }
12033 NS_IMETHODIMP
12034 nsDocument::QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn)
12035 {
12036 return nsINode::QuerySelector(aSelector, aReturn);
12037 }
12039 NS_IMETHODIMP
12040 nsDocument::QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn)
12041 {
12042 return nsINode::QuerySelectorAll(aSelector, aReturn);
12043 }
12045 already_AddRefed<nsIDocument>
12046 nsIDocument::Constructor(const GlobalObject& aGlobal,
12047 ErrorResult& rv)
12048 {
12049 nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
12050 if (!global) {
12051 rv.Throw(NS_ERROR_UNEXPECTED);
12052 return nullptr;
12053 }
12055 nsCOMPtr<nsIScriptObjectPrincipal> prin = do_QueryInterface(aGlobal.GetAsSupports());
12056 if (!prin) {
12057 rv.Throw(NS_ERROR_UNEXPECTED);
12058 return nullptr;
12059 }
12061 nsCOMPtr<nsIURI> uri;
12062 NS_NewURI(getter_AddRefs(uri), "about:blank");
12063 if (!uri) {
12064 rv.Throw(NS_ERROR_OUT_OF_MEMORY);
12065 return nullptr;
12066 }
12068 nsCOMPtr<nsIDOMDocument> document;
12069 nsresult res =
12070 NS_NewDOMDocument(getter_AddRefs(document),
12071 NullString(),
12072 EmptyString(),
12073 nullptr,
12074 uri,
12075 uri,
12076 prin->GetPrincipal(),
12077 true,
12078 global,
12079 DocumentFlavorLegacyGuess);
12080 if (NS_FAILED(res)) {
12081 rv.Throw(res);
12082 return nullptr;
12083 }
12085 nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
12086 doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
12088 return doc.forget();
12089 }
12091 already_AddRefed<nsIDOMXPathExpression>
12092 nsIDocument::CreateExpression(const nsAString& aExpression,
12093 nsIDOMXPathNSResolver* aResolver,
12094 ErrorResult& rv)
12095 {
12096 return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
12097 }
12099 already_AddRefed<nsIDOMXPathNSResolver>
12100 nsIDocument::CreateNSResolver(nsINode* aNodeResolver,
12101 ErrorResult& rv)
12102 {
12103 return XPathEvaluator()->CreateNSResolver(aNodeResolver, rv);
12104 }
12106 already_AddRefed<nsISupports>
12107 nsIDocument::Evaluate(const nsAString& aExpression, nsINode* aContextNode,
12108 nsIDOMXPathNSResolver* aResolver, uint16_t aType,
12109 nsISupports* aResult, ErrorResult& rv)
12110 {
12111 return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
12112 aResult, rv);
12113 }
12115 NS_IMETHODIMP
12116 nsDocument::CreateExpression(const nsAString& aExpression,
12117 nsIDOMXPathNSResolver* aResolver,
12118 nsIDOMXPathExpression** aResult)
12119 {
12120 return XPathEvaluator()->CreateExpression(aExpression, aResolver, aResult);
12121 }
12123 NS_IMETHODIMP
12124 nsDocument::CreateNSResolver(nsIDOMNode* aNodeResolver,
12125 nsIDOMXPathNSResolver** aResult)
12126 {
12127 return XPathEvaluator()->CreateNSResolver(aNodeResolver, aResult);
12128 }
12130 NS_IMETHODIMP
12131 nsDocument::Evaluate(const nsAString& aExpression, nsIDOMNode* aContextNode,
12132 nsIDOMXPathNSResolver* aResolver, uint16_t aType,
12133 nsISupports* aInResult, nsISupports** aResult)
12134 {
12135 return XPathEvaluator()->Evaluate(aExpression, aContextNode, aResolver, aType,
12136 aInResult, aResult);
12137 }
12139 // This is just a hack around the fact that window.document is not
12140 // [Unforgeable] yet.
12141 JSObject*
12142 nsIDocument::WrapObject(JSContext *aCx)
12143 {
12144 MOZ_ASSERT(IsDOMBinding());
12146 JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx));
12147 if (!obj) {
12148 return nullptr;
12149 }
12151 nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(GetInnerWindow());
12152 if (!win) {
12153 // No window, nothing else to do here
12154 return obj;
12155 }
12157 if (this != win->GetExtantDoc()) {
12158 // We're not the current document; we're also done here
12159 return obj;
12160 }
12162 JSAutoCompartment ac(aCx, obj);
12164 JS::Rooted<JS::Value> winVal(aCx);
12165 nsresult rv = nsContentUtils::WrapNative(aCx, win, &NS_GET_IID(nsIDOMWindow),
12166 &winVal,
12167 false);
12168 if (NS_FAILED(rv)) {
12169 Throw(aCx, rv);
12170 return nullptr;
12171 }
12173 NS_NAMED_LITERAL_STRING(doc_str, "document");
12175 if (!JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(winVal), doc_str.get(),
12176 doc_str.Length(), JS::ObjectValue(*obj),
12177 JS_PropertyStub, JS_StrictPropertyStub,
12178 JSPROP_READONLY | JSPROP_ENUMERATE)) {
12179 return nullptr;
12180 }
12182 return obj;
12183 }
12185 XPathEvaluator*
12186 nsIDocument::XPathEvaluator()
12187 {
12188 if (!mXPathEvaluator) {
12189 mXPathEvaluator = new dom::XPathEvaluator(this);
12190 }
12191 return mXPathEvaluator;
12192 }
12194 already_AddRefed<nsIDocumentEncoder>
12195 nsIDocument::GetCachedEncoder()
12196 {
12197 return mCachedEncoder.forget();
12198 }
12200 void
12201 nsIDocument::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)
12202 {
12203 mCachedEncoder = aEncoder;
12204 }
12206 void
12207 nsIDocument::SetContentTypeInternal(const nsACString& aType)
12208 {
12209 mCachedEncoder = nullptr;
12210 mContentType = aType;
12211 }
12213 nsILoadContext*
12214 nsIDocument::GetLoadContext() const
12215 {
12216 return mDocumentContainer;
12217 }
12219 nsIDocShell*
12220 nsIDocument::GetDocShell() const
12221 {
12222 return mDocumentContainer;
12223 }
12225 void
12226 nsIDocument::SetStateObject(nsIStructuredCloneContainer *scContainer)
12227 {
12228 mStateObjectContainer = scContainer;
12229 mStateObjectCached = nullptr;
12230 }
12232 already_AddRefed<Element>
12233 nsIDocument::CreateHTMLElement(nsIAtom* aTag)
12234 {
12235 nsCOMPtr<nsINodeInfo> nodeInfo;
12236 nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
12237 nsIDOMNode::ELEMENT_NODE);
12238 MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
12240 nsCOMPtr<Element> element;
12241 DebugOnly<nsresult> rv = NS_NewHTMLElement(getter_AddRefs(element),
12242 nodeInfo.forget(),
12243 mozilla::dom::NOT_FROM_PARSER);
12245 MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
12246 return element.forget();
12247 }
12249 bool
12250 MarkDocumentTreeToBeInSyncOperation(nsIDocument* aDoc, void* aData)
12251 {
12252 nsCOMArray<nsIDocument>* documents =
12253 static_cast<nsCOMArray<nsIDocument>*>(aData);
12254 if (aDoc) {
12255 aDoc->SetIsInSyncOperation(true);
12256 documents->AppendObject(aDoc);
12257 aDoc->EnumerateSubDocuments(MarkDocumentTreeToBeInSyncOperation, aData);
12258 }
12259 return true;
12260 }
12262 nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc)
12263 {
12264 mMicroTaskLevel = nsContentUtils::MicroTaskLevel();
12265 nsContentUtils::SetMicroTaskLevel(0);
12266 if (aDoc) {
12267 nsPIDOMWindow* win = aDoc->GetWindow();
12268 if (win) {
12269 nsCOMPtr<nsIDOMWindow> topWindow;
12270 win->GetTop(getter_AddRefs(topWindow));
12271 nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
12272 if (top) {
12273 nsCOMPtr<nsIDocument> doc = top->GetExtantDoc();
12274 MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments);
12275 }
12276 }
12277 }
12278 }
12280 nsAutoSyncOperation::~nsAutoSyncOperation()
12281 {
12282 for (int32_t i = 0; i < mDocuments.Count(); ++i) {
12283 mDocuments[i]->SetIsInSyncOperation(false);
12284 }
12285 nsContentUtils::SetMicroTaskLevel(mMicroTaskLevel);
12286 }